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;
1850 player->effective_action = 0;
1851 player->programmed_action = 0;
1854 player->score_final = 0;
1856 player->gems_still_needed = level.gems_needed;
1857 player->sokobanfields_still_needed = 0;
1858 player->lights_still_needed = 0;
1859 player->friends_still_needed = 0;
1861 for (j = 0; j < MAX_NUM_KEYS; j++)
1862 player->key[j] = FALSE;
1864 player->dynabomb_count = 0;
1865 player->dynabomb_size = 1;
1866 player->dynabombs_left = 0;
1867 player->dynabomb_xl = FALSE;
1869 player->MovDir = MV_NONE;
1872 player->GfxDir = MV_NONE;
1873 player->GfxAction = ACTION_DEFAULT;
1875 player->StepFrame = 0;
1877 player->use_murphy = FALSE;
1878 player->artwork_element =
1879 (level.use_artwork_element[i] ? level.artwork_element[i] :
1880 player->element_nr);
1882 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1883 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1885 player->gravity = level.initial_player_gravity[i];
1887 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1889 player->actual_frame_counter = 0;
1891 player->step_counter = 0;
1893 player->last_move_dir = MV_NONE;
1895 player->is_active = FALSE;
1897 player->is_waiting = FALSE;
1898 player->is_moving = FALSE;
1899 player->is_auto_moving = FALSE;
1900 player->is_digging = FALSE;
1901 player->is_snapping = FALSE;
1902 player->is_collecting = FALSE;
1903 player->is_pushing = FALSE;
1904 player->is_switching = FALSE;
1905 player->is_dropping = FALSE;
1906 player->is_dropping_pressed = FALSE;
1908 player->is_bored = FALSE;
1909 player->is_sleeping = FALSE;
1911 player->frame_counter_bored = -1;
1912 player->frame_counter_sleeping = -1;
1914 player->anim_delay_counter = 0;
1915 player->post_delay_counter = 0;
1917 player->dir_waiting = MV_NONE;
1918 player->action_waiting = ACTION_DEFAULT;
1919 player->last_action_waiting = ACTION_DEFAULT;
1920 player->special_action_bored = ACTION_DEFAULT;
1921 player->special_action_sleeping = ACTION_DEFAULT;
1923 player->switch_x = -1;
1924 player->switch_y = -1;
1926 player->drop_x = -1;
1927 player->drop_y = -1;
1929 player->show_envelope = 0;
1931 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
1933 player->push_delay = -1; /* initialized when pushing starts */
1934 player->push_delay_value = game.initial_push_delay_value;
1936 player->drop_delay = 0;
1937 player->drop_pressed_delay = 0;
1939 player->last_jx = -1;
1940 player->last_jy = -1;
1944 player->shield_normal_time_left = 0;
1945 player->shield_deadly_time_left = 0;
1947 player->inventory_infinite_element = EL_UNDEFINED;
1948 player->inventory_size = 0;
1950 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1951 SnapField(player, 0, 0);
1953 player->LevelSolved = FALSE;
1954 player->GameOver = FALSE;
1956 player->LevelSolved_GameEnd = FALSE;
1957 player->LevelSolved_SaveTape = FALSE;
1958 player->LevelSolved_SaveScore = FALSE;
1961 network_player_action_received = FALSE;
1963 #if defined(NETWORK_AVALIABLE)
1964 /* initial null action */
1965 if (network_playing)
1966 SendToServer_MovePlayer(MV_NONE);
1975 TimeLeft = level.time;
1978 ScreenMovDir = MV_NONE;
1982 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1984 AllPlayersGone = FALSE;
1986 game.yamyam_content_nr = 0;
1987 game.magic_wall_active = FALSE;
1988 game.magic_wall_time_left = 0;
1989 game.light_time_left = 0;
1990 game.timegate_time_left = 0;
1991 game.switchgate_pos = 0;
1992 game.wind_direction = level.wind_direction_initial;
1994 #if !USE_PLAYER_GRAVITY
1995 game.gravity = FALSE;
1996 game.explosions_delayed = TRUE;
1999 game.lenses_time_left = 0;
2000 game.magnify_time_left = 0;
2002 game.ball_state = level.ball_state_initial;
2003 game.ball_content_nr = 0;
2005 game.envelope_active = FALSE;
2007 /* set focus to local player for network games, else to all players */
2008 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2009 game.centered_player_nr_next = game.centered_player_nr;
2010 game.set_centered_player = FALSE;
2012 if (network_playing && tape.recording)
2014 /* store client dependent player focus when recording network games */
2015 tape.centered_player_nr_next = game.centered_player_nr_next;
2016 tape.set_centered_player = TRUE;
2019 for (i = 0; i < NUM_BELTS; i++)
2021 game.belt_dir[i] = MV_NONE;
2022 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2025 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2026 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2028 SCAN_PLAYFIELD(x, y)
2030 Feld[x][y] = level.field[x][y];
2031 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2032 ChangeDelay[x][y] = 0;
2033 ChangePage[x][y] = -1;
2034 #if USE_NEW_CUSTOM_VALUE
2035 CustomValue[x][y] = 0; /* initialized in InitField() */
2037 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2039 WasJustMoving[x][y] = 0;
2040 WasJustFalling[x][y] = 0;
2041 CheckCollision[x][y] = 0;
2042 CheckImpact[x][y] = 0;
2044 Pushed[x][y] = FALSE;
2046 ChangeCount[x][y] = 0;
2047 ChangeEvent[x][y] = -1;
2049 ExplodePhase[x][y] = 0;
2050 ExplodeDelay[x][y] = 0;
2051 ExplodeField[x][y] = EX_TYPE_NONE;
2053 RunnerVisit[x][y] = 0;
2054 PlayerVisit[x][y] = 0;
2057 GfxRandom[x][y] = INIT_GFX_RANDOM();
2058 GfxElement[x][y] = EL_UNDEFINED;
2059 GfxAction[x][y] = ACTION_DEFAULT;
2060 GfxDir[x][y] = MV_NONE;
2063 SCAN_PLAYFIELD(x, y)
2065 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2067 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2069 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2072 InitField(x, y, TRUE);
2077 for (i = 0; i < MAX_PLAYERS; i++)
2079 struct PlayerInfo *player = &stored_player[i];
2081 /* set number of special actions for bored and sleeping animation */
2082 player->num_special_action_bored =
2083 get_num_special_action(player->artwork_element,
2084 ACTION_BORING_1, ACTION_BORING_LAST);
2085 player->num_special_action_sleeping =
2086 get_num_special_action(player->artwork_element,
2087 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2090 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2091 emulate_sb ? EMU_SOKOBAN :
2092 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2094 #if USE_NEW_ALL_SLIPPERY
2095 /* initialize type of slippery elements */
2096 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2098 if (!IS_CUSTOM_ELEMENT(i))
2100 /* default: elements slip down either to the left or right randomly */
2101 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2103 /* SP style elements prefer to slip down on the left side */
2104 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2105 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2107 /* BD style elements prefer to slip down on the left side */
2108 if (game.emulation == EMU_BOULDERDASH)
2109 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2114 /* initialize explosion and ignition delay */
2115 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2117 if (!IS_CUSTOM_ELEMENT(i))
2120 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2121 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2122 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2123 int last_phase = (num_phase + 1) * delay;
2124 int half_phase = (num_phase / 2) * delay;
2126 element_info[i].explosion_delay = last_phase - 1;
2127 element_info[i].ignition_delay = half_phase;
2129 if (i == EL_BLACK_ORB)
2130 element_info[i].ignition_delay = 1;
2134 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2135 element_info[i].explosion_delay = 1;
2137 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2138 element_info[i].ignition_delay = 1;
2142 /* correct non-moving belts to start moving left */
2143 for (i = 0; i < NUM_BELTS; i++)
2144 if (game.belt_dir[i] == MV_NONE)
2145 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2147 /* check if any connected player was not found in playfield */
2148 for (i = 0; i < MAX_PLAYERS; i++)
2150 struct PlayerInfo *player = &stored_player[i];
2152 if (player->connected && !player->present)
2154 for (j = 0; j < MAX_PLAYERS; j++)
2156 struct PlayerInfo *some_player = &stored_player[j];
2157 int jx = some_player->jx, jy = some_player->jy;
2159 /* assign first free player found that is present in the playfield */
2160 if (some_player->present && !some_player->connected)
2162 player->present = TRUE;
2163 player->active = TRUE;
2165 some_player->present = FALSE;
2166 some_player->active = FALSE;
2168 player->artwork_element = some_player->artwork_element;
2170 player->block_last_field = some_player->block_last_field;
2171 player->block_delay_adjustment = some_player->block_delay_adjustment;
2173 StorePlayer[jx][jy] = player->element_nr;
2174 player->jx = player->last_jx = jx;
2175 player->jy = player->last_jy = jy;
2185 /* when playing a tape, eliminate all players who do not participate */
2187 for (i = 0; i < MAX_PLAYERS; i++)
2189 if (stored_player[i].active && !tape.player_participates[i])
2191 struct PlayerInfo *player = &stored_player[i];
2192 int jx = player->jx, jy = player->jy;
2194 player->active = FALSE;
2195 StorePlayer[jx][jy] = 0;
2196 Feld[jx][jy] = EL_EMPTY;
2200 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2202 /* when in single player mode, eliminate all but the first active player */
2204 for (i = 0; i < MAX_PLAYERS; i++)
2206 if (stored_player[i].active)
2208 for (j = i + 1; j < MAX_PLAYERS; j++)
2210 if (stored_player[j].active)
2212 struct PlayerInfo *player = &stored_player[j];
2213 int jx = player->jx, jy = player->jy;
2215 player->active = FALSE;
2216 player->present = FALSE;
2218 StorePlayer[jx][jy] = 0;
2219 Feld[jx][jy] = EL_EMPTY;
2226 /* when recording the game, store which players take part in the game */
2229 for (i = 0; i < MAX_PLAYERS; i++)
2230 if (stored_player[i].active)
2231 tape.player_participates[i] = TRUE;
2236 for (i = 0; i < MAX_PLAYERS; i++)
2238 struct PlayerInfo *player = &stored_player[i];
2240 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2245 if (local_player == player)
2246 printf("Player %d is local player.\n", i+1);
2250 if (BorderElement == EL_EMPTY)
2253 SBX_Right = lev_fieldx - SCR_FIELDX;
2255 SBY_Lower = lev_fieldy - SCR_FIELDY;
2260 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2262 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2265 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2266 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2268 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2269 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2271 /* if local player not found, look for custom element that might create
2272 the player (make some assumptions about the right custom element) */
2273 if (!local_player->present)
2275 int start_x = 0, start_y = 0;
2276 int found_rating = 0;
2277 int found_element = EL_UNDEFINED;
2278 int player_nr = local_player->index_nr;
2280 SCAN_PLAYFIELD(x, y)
2282 int element = Feld[x][y];
2287 if (level.use_start_element[player_nr] &&
2288 level.start_element[player_nr] == element &&
2295 found_element = element;
2298 if (!IS_CUSTOM_ELEMENT(element))
2301 if (CAN_CHANGE(element))
2303 for (i = 0; i < element_info[element].num_change_pages; i++)
2305 /* check for player created from custom element as single target */
2306 content = element_info[element].change_page[i].target_element;
2307 is_player = ELEM_IS_PLAYER(content);
2309 if (is_player && (found_rating < 3 || element < found_element))
2315 found_element = element;
2320 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2322 /* check for player created from custom element as explosion content */
2323 content = element_info[element].content.e[xx][yy];
2324 is_player = ELEM_IS_PLAYER(content);
2326 if (is_player && (found_rating < 2 || element < found_element))
2328 start_x = x + xx - 1;
2329 start_y = y + yy - 1;
2332 found_element = element;
2335 if (!CAN_CHANGE(element))
2338 for (i = 0; i < element_info[element].num_change_pages; i++)
2340 /* check for player created from custom element as extended target */
2342 element_info[element].change_page[i].target_content.e[xx][yy];
2344 is_player = ELEM_IS_PLAYER(content);
2346 if (is_player && (found_rating < 1 || element < found_element))
2348 start_x = x + xx - 1;
2349 start_y = y + yy - 1;
2352 found_element = element;
2358 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2359 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2362 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2363 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2368 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2369 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2370 local_player->jx - MIDPOSX);
2372 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2373 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2374 local_player->jy - MIDPOSY);
2379 if (!game.restart_level)
2380 CloseDoor(DOOR_CLOSE_1);
2383 FadeOut(REDRAW_FIELD);
2385 /* !!! FIX THIS (START) !!! */
2386 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2388 InitGameEngine_EM();
2390 /* blit playfield from scroll buffer to normal back buffer for fading in */
2391 BlitScreenToBitmap_EM(backbuffer);
2398 /* after drawing the level, correct some elements */
2399 if (game.timegate_time_left == 0)
2400 CloseAllOpenTimegates();
2402 /* blit playfield from scroll buffer to normal back buffer for fading in */
2403 if (setup.soft_scrolling)
2404 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2406 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2408 /* !!! FIX THIS (END) !!! */
2411 FadeIn(REDRAW_FIELD);
2415 if (!game.restart_level)
2417 /* copy default game door content to main double buffer */
2418 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2419 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2422 SetPanelBackground();
2423 SetDrawBackgroundMask(REDRAW_DOOR_1);
2425 DrawGameDoorValues();
2427 if (!game.restart_level)
2431 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2432 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2433 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2437 /* copy actual game door content to door double buffer for OpenDoor() */
2438 BlitBitmap(drawto, bitmap_db_door,
2439 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2441 OpenDoor(DOOR_OPEN_ALL);
2443 PlaySound(SND_GAME_STARTING);
2445 if (setup.sound_music)
2448 KeyboardAutoRepeatOffUnlessAutoplay();
2452 for (i = 0; i < MAX_PLAYERS; i++)
2453 printf("Player %d %sactive.\n",
2454 i + 1, (stored_player[i].active ? "" : "not "));
2465 game.restart_level = FALSE;
2468 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2470 /* this is used for non-R'n'D game engines to update certain engine values */
2472 /* needed to determine if sounds are played within the visible screen area */
2473 scroll_x = actual_scroll_x;
2474 scroll_y = actual_scroll_y;
2477 void InitMovDir(int x, int y)
2479 int i, element = Feld[x][y];
2480 static int xy[4][2] =
2487 static int direction[3][4] =
2489 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2490 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2491 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2500 Feld[x][y] = EL_BUG;
2501 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2504 case EL_SPACESHIP_RIGHT:
2505 case EL_SPACESHIP_UP:
2506 case EL_SPACESHIP_LEFT:
2507 case EL_SPACESHIP_DOWN:
2508 Feld[x][y] = EL_SPACESHIP;
2509 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2512 case EL_BD_BUTTERFLY_RIGHT:
2513 case EL_BD_BUTTERFLY_UP:
2514 case EL_BD_BUTTERFLY_LEFT:
2515 case EL_BD_BUTTERFLY_DOWN:
2516 Feld[x][y] = EL_BD_BUTTERFLY;
2517 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2520 case EL_BD_FIREFLY_RIGHT:
2521 case EL_BD_FIREFLY_UP:
2522 case EL_BD_FIREFLY_LEFT:
2523 case EL_BD_FIREFLY_DOWN:
2524 Feld[x][y] = EL_BD_FIREFLY;
2525 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2528 case EL_PACMAN_RIGHT:
2530 case EL_PACMAN_LEFT:
2531 case EL_PACMAN_DOWN:
2532 Feld[x][y] = EL_PACMAN;
2533 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2536 case EL_YAMYAM_LEFT:
2537 case EL_YAMYAM_RIGHT:
2539 case EL_YAMYAM_DOWN:
2540 Feld[x][y] = EL_YAMYAM;
2541 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2544 case EL_SP_SNIKSNAK:
2545 MovDir[x][y] = MV_UP;
2548 case EL_SP_ELECTRON:
2549 MovDir[x][y] = MV_LEFT;
2556 Feld[x][y] = EL_MOLE;
2557 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2561 if (IS_CUSTOM_ELEMENT(element))
2563 struct ElementInfo *ei = &element_info[element];
2564 int move_direction_initial = ei->move_direction_initial;
2565 int move_pattern = ei->move_pattern;
2567 if (move_direction_initial == MV_START_PREVIOUS)
2569 if (MovDir[x][y] != MV_NONE)
2572 move_direction_initial = MV_START_AUTOMATIC;
2575 if (move_direction_initial == MV_START_RANDOM)
2576 MovDir[x][y] = 1 << RND(4);
2577 else if (move_direction_initial & MV_ANY_DIRECTION)
2578 MovDir[x][y] = move_direction_initial;
2579 else if (move_pattern == MV_ALL_DIRECTIONS ||
2580 move_pattern == MV_TURNING_LEFT ||
2581 move_pattern == MV_TURNING_RIGHT ||
2582 move_pattern == MV_TURNING_LEFT_RIGHT ||
2583 move_pattern == MV_TURNING_RIGHT_LEFT ||
2584 move_pattern == MV_TURNING_RANDOM)
2585 MovDir[x][y] = 1 << RND(4);
2586 else if (move_pattern == MV_HORIZONTAL)
2587 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2588 else if (move_pattern == MV_VERTICAL)
2589 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2590 else if (move_pattern & MV_ANY_DIRECTION)
2591 MovDir[x][y] = element_info[element].move_pattern;
2592 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2593 move_pattern == MV_ALONG_RIGHT_SIDE)
2595 /* use random direction as default start direction */
2596 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2597 MovDir[x][y] = 1 << RND(4);
2599 for (i = 0; i < NUM_DIRECTIONS; i++)
2601 int x1 = x + xy[i][0];
2602 int y1 = y + xy[i][1];
2604 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2606 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2607 MovDir[x][y] = direction[0][i];
2609 MovDir[x][y] = direction[1][i];
2618 MovDir[x][y] = 1 << RND(4);
2620 if (element != EL_BUG &&
2621 element != EL_SPACESHIP &&
2622 element != EL_BD_BUTTERFLY &&
2623 element != EL_BD_FIREFLY)
2626 for (i = 0; i < NUM_DIRECTIONS; i++)
2628 int x1 = x + xy[i][0];
2629 int y1 = y + xy[i][1];
2631 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2633 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2635 MovDir[x][y] = direction[0][i];
2638 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2639 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2641 MovDir[x][y] = direction[1][i];
2650 GfxDir[x][y] = MovDir[x][y];
2653 void InitAmoebaNr(int x, int y)
2656 int group_nr = AmoebeNachbarNr(x, y);
2660 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2662 if (AmoebaCnt[i] == 0)
2670 AmoebaNr[x][y] = group_nr;
2671 AmoebaCnt[group_nr]++;
2672 AmoebaCnt2[group_nr]++;
2675 static void PlayerWins(struct PlayerInfo *player)
2677 player->LevelSolved = TRUE;
2678 player->GameOver = TRUE;
2680 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2681 level.native_em_level->lev->score : player->score);
2686 static int time, time_final;
2687 static int score, score_final;
2688 static int game_over_delay = 0;
2689 int game_over_delay_value = 50;
2691 if (!local_player->LevelSolved_GameEnd)
2695 /* do not start end game actions before the player stops moving (to exit) */
2696 if (local_player->MovPos)
2699 local_player->LevelSolved_GameEnd = TRUE;
2700 local_player->LevelSolved_SaveTape = tape.recording;
2701 local_player->LevelSolved_SaveScore = !tape.playing;
2703 if (tape.auto_play) /* tape might already be stopped here */
2704 tape.auto_play_level_solved = TRUE;
2710 game_over_delay = game_over_delay_value;
2712 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
2713 score = score_final = local_player->score_final;
2718 score_final += TimeLeft * level.score[SC_TIME_BONUS];
2720 else if (level.time == 0 && TimePlayed < 999)
2723 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
2726 local_player->score_final = score_final;
2728 if (level_editor_test_game)
2731 score = score_final;
2733 DrawGameValue_Time(time);
2734 DrawGameValue_Score(score);
2737 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
2739 /* close exit door after last player */
2740 if (AllPlayersGone &&
2741 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2742 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2744 int element = Feld[ExitX][ExitY];
2746 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2747 EL_SP_EXIT_CLOSING);
2749 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2752 /* player disappears */
2753 DrawLevelField(ExitX, ExitY);
2756 for (i = 0; i < MAX_PLAYERS; i++)
2758 struct PlayerInfo *player = &stored_player[i];
2760 if (player->present)
2762 RemovePlayer(player);
2764 /* player disappears */
2765 DrawLevelField(player->jx, player->jy);
2769 PlaySound(SND_GAME_WINNING);
2772 if (game_over_delay > 0)
2779 if (time != time_final)
2781 int time_to_go = ABS(time_final - time);
2782 int time_count_dir = (time < time_final ? +1 : -1);
2783 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
2785 time += time_count_steps * time_count_dir;
2786 score += time_count_steps * level.score[SC_TIME_BONUS];
2788 DrawGameValue_Time(time);
2789 DrawGameValue_Score(score);
2791 if (time == time_final)
2792 StopSound(SND_GAME_LEVELTIME_BONUS);
2793 else if (setup.sound_loops)
2794 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
2796 PlaySound(SND_GAME_LEVELTIME_BONUS);
2803 boolean raise_level = FALSE;
2805 CloseDoor(DOOR_CLOSE_1);
2807 if (local_player->LevelSolved_SaveTape)
2814 SaveTapeChecked(tape.level_nr); /* ask to save tape */
2816 SaveTape(tape.level_nr); /* ask to save tape */
2820 if (level_editor_test_game)
2822 game_status = GAME_MODE_MAIN;
2829 if (!local_player->LevelSolved_SaveScore)
2831 FadeOut(REDRAW_FIELD);
2833 game_status = GAME_MODE_MAIN;
2835 DrawAndFadeInMainMenu(REDRAW_FIELD);
2840 if (level_nr == leveldir_current->handicap_level)
2842 leveldir_current->handicap_level++;
2843 SaveLevelSetup_SeriesInfo();
2846 if (level_nr < leveldir_current->last_level)
2847 raise_level = TRUE; /* advance to next level */
2849 if ((hi_pos = NewHiScore()) >= 0)
2851 game_status = GAME_MODE_SCORES;
2853 DrawHallOfFame(hi_pos);
2863 FadeOut(REDRAW_FIELD);
2865 game_status = GAME_MODE_MAIN;
2873 DrawAndFadeInMainMenu(REDRAW_FIELD);
2882 LoadScore(level_nr);
2884 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2885 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
2888 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2890 if (local_player->score_final > highscore[k].Score)
2892 /* player has made it to the hall of fame */
2894 if (k < MAX_SCORE_ENTRIES - 1)
2896 int m = MAX_SCORE_ENTRIES - 1;
2899 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2900 if (strEqual(setup.player_name, highscore[l].Name))
2902 if (m == k) /* player's new highscore overwrites his old one */
2906 for (l = m; l > k; l--)
2908 strcpy(highscore[l].Name, highscore[l - 1].Name);
2909 highscore[l].Score = highscore[l - 1].Score;
2916 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2917 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2918 highscore[k].Score = local_player->score_final;
2924 else if (!strncmp(setup.player_name, highscore[k].Name,
2925 MAX_PLAYER_NAME_LEN))
2926 break; /* player already there with a higher score */
2932 SaveScore(level_nr);
2937 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
2939 int element = Feld[x][y];
2940 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2941 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2942 int horiz_move = (dx != 0);
2943 int sign = (horiz_move ? dx : dy);
2944 int step = sign * element_info[element].move_stepsize;
2946 /* special values for move stepsize for spring and things on conveyor belt */
2949 if (CAN_FALL(element) &&
2950 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2951 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2952 else if (element == EL_SPRING)
2953 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2959 inline static int getElementMoveStepsize(int x, int y)
2961 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
2964 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2966 if (player->GfxAction != action || player->GfxDir != dir)
2969 printf("Player frame reset! (%d => %d, %d => %d)\n",
2970 player->GfxAction, action, player->GfxDir, dir);
2973 player->GfxAction = action;
2974 player->GfxDir = dir;
2976 player->StepFrame = 0;
2980 #if USE_GFX_RESET_GFX_ANIMATION
2981 static void ResetGfxFrame(int x, int y, boolean redraw)
2983 int element = Feld[x][y];
2984 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2985 int last_gfx_frame = GfxFrame[x][y];
2987 if (graphic_info[graphic].anim_global_sync)
2988 GfxFrame[x][y] = FrameCounter;
2989 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2990 GfxFrame[x][y] = CustomValue[x][y];
2991 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2992 GfxFrame[x][y] = element_info[element].collect_score;
2993 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
2994 GfxFrame[x][y] = ChangeDelay[x][y];
2996 if (redraw && GfxFrame[x][y] != last_gfx_frame)
2997 DrawLevelGraphicAnimation(x, y, graphic);
3001 static void ResetGfxAnimation(int x, int y)
3003 GfxAction[x][y] = ACTION_DEFAULT;
3004 GfxDir[x][y] = MovDir[x][y];
3007 #if USE_GFX_RESET_GFX_ANIMATION
3008 ResetGfxFrame(x, y, FALSE);
3012 static void ResetRandomAnimationValue(int x, int y)
3014 GfxRandom[x][y] = INIT_GFX_RANDOM();
3017 void InitMovingField(int x, int y, int direction)
3019 int element = Feld[x][y];
3020 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3021 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3024 boolean is_moving_before, is_moving_after;
3026 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3029 /* check if element was/is moving or being moved before/after mode change */
3031 is_moving_before = WasJustMoving[x][y];
3033 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3035 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3037 /* reset animation only for moving elements which change direction of moving
3038 or which just started or stopped moving
3039 (else CEs with property "can move" / "not moving" are reset each frame) */
3040 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3042 if (is_moving_before != is_moving_after ||
3043 direction != MovDir[x][y])
3044 ResetGfxAnimation(x, y);
3046 if ((is_moving_before || is_moving_after) && !continues_moving)
3047 ResetGfxAnimation(x, y);
3050 if (!continues_moving)
3051 ResetGfxAnimation(x, y);
3054 MovDir[x][y] = direction;
3055 GfxDir[x][y] = direction;
3057 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3058 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3059 direction == MV_DOWN && CAN_FALL(element) ?
3060 ACTION_FALLING : ACTION_MOVING);
3062 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3063 ACTION_FALLING : ACTION_MOVING);
3066 /* this is needed for CEs with property "can move" / "not moving" */
3068 if (is_moving_after)
3070 if (Feld[newx][newy] == EL_EMPTY)
3071 Feld[newx][newy] = EL_BLOCKED;
3073 MovDir[newx][newy] = MovDir[x][y];
3075 #if USE_NEW_CUSTOM_VALUE
3076 CustomValue[newx][newy] = CustomValue[x][y];
3079 GfxFrame[newx][newy] = GfxFrame[x][y];
3080 GfxRandom[newx][newy] = GfxRandom[x][y];
3081 GfxAction[newx][newy] = GfxAction[x][y];
3082 GfxDir[newx][newy] = GfxDir[x][y];
3086 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3088 int direction = MovDir[x][y];
3089 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3090 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3096 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3098 int oldx = x, oldy = y;
3099 int direction = MovDir[x][y];
3101 if (direction == MV_LEFT)
3103 else if (direction == MV_RIGHT)
3105 else if (direction == MV_UP)
3107 else if (direction == MV_DOWN)
3110 *comes_from_x = oldx;
3111 *comes_from_y = oldy;
3114 int MovingOrBlocked2Element(int x, int y)
3116 int element = Feld[x][y];
3118 if (element == EL_BLOCKED)
3122 Blocked2Moving(x, y, &oldx, &oldy);
3123 return Feld[oldx][oldy];
3129 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3131 /* like MovingOrBlocked2Element(), but if element is moving
3132 and (x,y) is the field the moving element is just leaving,
3133 return EL_BLOCKED instead of the element value */
3134 int element = Feld[x][y];
3136 if (IS_MOVING(x, y))
3138 if (element == EL_BLOCKED)
3142 Blocked2Moving(x, y, &oldx, &oldy);
3143 return Feld[oldx][oldy];
3152 static void RemoveField(int x, int y)
3154 Feld[x][y] = EL_EMPTY;
3160 #if USE_NEW_CUSTOM_VALUE
3161 CustomValue[x][y] = 0;
3165 ChangeDelay[x][y] = 0;
3166 ChangePage[x][y] = -1;
3167 Pushed[x][y] = FALSE;
3170 ExplodeField[x][y] = EX_TYPE_NONE;
3173 GfxElement[x][y] = EL_UNDEFINED;
3174 GfxAction[x][y] = ACTION_DEFAULT;
3175 GfxDir[x][y] = MV_NONE;
3178 void RemoveMovingField(int x, int y)
3180 int oldx = x, oldy = y, newx = x, newy = y;
3181 int element = Feld[x][y];
3182 int next_element = EL_UNDEFINED;
3184 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3187 if (IS_MOVING(x, y))
3189 Moving2Blocked(x, y, &newx, &newy);
3191 if (Feld[newx][newy] != EL_BLOCKED)
3193 /* element is moving, but target field is not free (blocked), but
3194 already occupied by something different (example: acid pool);
3195 in this case, only remove the moving field, but not the target */
3197 RemoveField(oldx, oldy);
3199 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3201 DrawLevelField(oldx, oldy);
3206 else if (element == EL_BLOCKED)
3208 Blocked2Moving(x, y, &oldx, &oldy);
3209 if (!IS_MOVING(oldx, oldy))
3213 if (element == EL_BLOCKED &&
3214 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3215 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3216 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3217 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3218 next_element = get_next_element(Feld[oldx][oldy]);
3220 RemoveField(oldx, oldy);
3221 RemoveField(newx, newy);
3223 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3225 if (next_element != EL_UNDEFINED)
3226 Feld[oldx][oldy] = next_element;
3228 DrawLevelField(oldx, oldy);
3229 DrawLevelField(newx, newy);
3232 void DrawDynamite(int x, int y)
3234 int sx = SCREENX(x), sy = SCREENY(y);
3235 int graphic = el2img(Feld[x][y]);
3238 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3241 if (IS_WALKABLE_INSIDE(Back[x][y]))
3245 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3246 else if (Store[x][y])
3247 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3249 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3251 if (Back[x][y] || Store[x][y])
3252 DrawGraphicThruMask(sx, sy, graphic, frame);
3254 DrawGraphic(sx, sy, graphic, frame);
3257 void CheckDynamite(int x, int y)
3259 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3263 if (MovDelay[x][y] != 0)
3266 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3272 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3277 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3279 boolean num_checked_players = 0;
3282 for (i = 0; i < MAX_PLAYERS; i++)
3284 if (stored_player[i].active)
3286 int sx = stored_player[i].jx;
3287 int sy = stored_player[i].jy;
3289 if (num_checked_players == 0)
3296 *sx1 = MIN(*sx1, sx);
3297 *sy1 = MIN(*sy1, sy);
3298 *sx2 = MAX(*sx2, sx);
3299 *sy2 = MAX(*sy2, sy);
3302 num_checked_players++;
3307 static boolean checkIfAllPlayersFitToScreen_RND()
3309 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3311 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3313 return (sx2 - sx1 < SCR_FIELDX &&
3314 sy2 - sy1 < SCR_FIELDY);
3317 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3319 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3321 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3323 *sx = (sx1 + sx2) / 2;
3324 *sy = (sy1 + sy2) / 2;
3327 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3328 boolean quick_relocation)
3330 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3331 boolean no_delay = (tape.warp_forward);
3332 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3333 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3335 if (quick_relocation)
3337 int offset = (setup.scroll_delay ? 3 : 0);
3339 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3341 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3342 x > SBX_Right + MIDPOSX ? SBX_Right :
3345 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3346 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3351 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3352 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3353 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3355 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3356 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3357 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3359 /* don't scroll over playfield boundaries */
3360 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3361 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3363 /* don't scroll over playfield boundaries */
3364 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3365 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3368 RedrawPlayfield(TRUE, 0,0,0,0);
3372 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3373 x > SBX_Right + MIDPOSX ? SBX_Right :
3376 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3377 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3380 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3382 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3385 int fx = FX, fy = FY;
3387 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3388 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3390 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3396 fx += dx * TILEX / 2;
3397 fy += dy * TILEY / 2;
3399 ScrollLevel(dx, dy);
3402 /* scroll in two steps of half tile size to make things smoother */
3403 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3405 Delay(wait_delay_value);
3407 /* scroll second step to align at full tile size */
3409 Delay(wait_delay_value);
3414 Delay(wait_delay_value);
3418 void RelocatePlayer(int jx, int jy, int el_player_raw)
3420 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3421 int player_nr = GET_PLAYER_NR(el_player);
3422 struct PlayerInfo *player = &stored_player[player_nr];
3423 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3424 boolean no_delay = (tape.warp_forward);
3425 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3426 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3427 int old_jx = player->jx;
3428 int old_jy = player->jy;
3429 int old_element = Feld[old_jx][old_jy];
3430 int element = Feld[jx][jy];
3431 boolean player_relocated = (old_jx != jx || old_jy != jy);
3433 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3434 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3435 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3436 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3437 int leave_side_horiz = move_dir_horiz;
3438 int leave_side_vert = move_dir_vert;
3439 int enter_side = enter_side_horiz | enter_side_vert;
3440 int leave_side = leave_side_horiz | leave_side_vert;
3442 if (player->GameOver) /* do not reanimate dead player */
3445 if (!player_relocated) /* no need to relocate the player */
3448 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3450 RemoveField(jx, jy); /* temporarily remove newly placed player */
3451 DrawLevelField(jx, jy);
3454 if (player->present)
3456 while (player->MovPos)
3458 ScrollPlayer(player, SCROLL_GO_ON);
3459 ScrollScreen(NULL, SCROLL_GO_ON);
3461 AdvanceFrameAndPlayerCounters(player->index_nr);
3466 Delay(wait_delay_value);
3469 DrawPlayer(player); /* needed here only to cleanup last field */
3470 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3472 player->is_moving = FALSE;
3475 if (IS_CUSTOM_ELEMENT(old_element))
3476 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3478 player->index_bit, leave_side);
3480 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3482 player->index_bit, leave_side);
3484 Feld[jx][jy] = el_player;
3485 InitPlayerField(jx, jy, el_player, TRUE);
3487 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3489 Feld[jx][jy] = element;
3490 InitField(jx, jy, FALSE);
3493 /* only visually relocate centered player */
3494 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3495 level.instant_relocation);
3497 TestIfPlayerTouchesBadThing(jx, jy);
3498 TestIfPlayerTouchesCustomElement(jx, jy);
3500 if (IS_CUSTOM_ELEMENT(element))
3501 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3502 player->index_bit, enter_side);
3504 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3505 player->index_bit, enter_side);
3508 void Explode(int ex, int ey, int phase, int mode)
3514 /* !!! eliminate this variable !!! */
3515 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3517 if (game.explosions_delayed)
3519 ExplodeField[ex][ey] = mode;
3523 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3525 int center_element = Feld[ex][ey];
3526 int artwork_element, explosion_element; /* set these values later */
3529 /* --- This is only really needed (and now handled) in "Impact()". --- */
3530 /* do not explode moving elements that left the explode field in time */
3531 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3532 center_element == EL_EMPTY &&
3533 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3538 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3539 if (mode == EX_TYPE_NORMAL ||
3540 mode == EX_TYPE_CENTER ||
3541 mode == EX_TYPE_CROSS)
3542 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3545 /* remove things displayed in background while burning dynamite */
3546 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3549 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3551 /* put moving element to center field (and let it explode there) */
3552 center_element = MovingOrBlocked2Element(ex, ey);
3553 RemoveMovingField(ex, ey);
3554 Feld[ex][ey] = center_element;
3557 /* now "center_element" is finally determined -- set related values now */
3558 artwork_element = center_element; /* for custom player artwork */
3559 explosion_element = center_element; /* for custom player artwork */
3561 if (IS_PLAYER(ex, ey))
3563 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3565 artwork_element = stored_player[player_nr].artwork_element;
3567 if (level.use_explosion_element[player_nr])
3569 explosion_element = level.explosion_element[player_nr];
3570 artwork_element = explosion_element;
3575 if (mode == EX_TYPE_NORMAL ||
3576 mode == EX_TYPE_CENTER ||
3577 mode == EX_TYPE_CROSS)
3578 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3581 last_phase = element_info[explosion_element].explosion_delay + 1;
3583 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3585 int xx = x - ex + 1;
3586 int yy = y - ey + 1;
3589 if (!IN_LEV_FIELD(x, y) ||
3590 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3591 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3594 element = Feld[x][y];
3596 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3598 element = MovingOrBlocked2Element(x, y);
3600 if (!IS_EXPLOSION_PROOF(element))
3601 RemoveMovingField(x, y);
3604 /* indestructible elements can only explode in center (but not flames) */
3605 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3606 mode == EX_TYPE_BORDER)) ||
3607 element == EL_FLAMES)
3610 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3611 behaviour, for example when touching a yamyam that explodes to rocks
3612 with active deadly shield, a rock is created under the player !!! */
3613 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3615 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3616 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3617 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3619 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3622 if (IS_ACTIVE_BOMB(element))
3624 /* re-activate things under the bomb like gate or penguin */
3625 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3632 /* save walkable background elements while explosion on same tile */
3633 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3634 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3635 Back[x][y] = element;
3637 /* ignite explodable elements reached by other explosion */
3638 if (element == EL_EXPLOSION)
3639 element = Store2[x][y];
3641 if (AmoebaNr[x][y] &&
3642 (element == EL_AMOEBA_FULL ||
3643 element == EL_BD_AMOEBA ||
3644 element == EL_AMOEBA_GROWING))
3646 AmoebaCnt[AmoebaNr[x][y]]--;
3647 AmoebaCnt2[AmoebaNr[x][y]]--;
3652 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3654 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3656 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3658 if (PLAYERINFO(ex, ey)->use_murphy)
3659 Store[x][y] = EL_EMPTY;
3662 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3663 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3664 else if (ELEM_IS_PLAYER(center_element))
3665 Store[x][y] = EL_EMPTY;
3666 else if (center_element == EL_YAMYAM)
3667 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3668 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3669 Store[x][y] = element_info[center_element].content.e[xx][yy];
3671 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3672 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3673 otherwise) -- FIX THIS !!! */
3674 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3675 Store[x][y] = element_info[element].content.e[1][1];
3677 else if (!CAN_EXPLODE(element))
3678 Store[x][y] = element_info[element].content.e[1][1];
3681 Store[x][y] = EL_EMPTY;
3683 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3684 center_element == EL_AMOEBA_TO_DIAMOND)
3685 Store2[x][y] = element;
3687 Feld[x][y] = EL_EXPLOSION;
3688 GfxElement[x][y] = artwork_element;
3690 ExplodePhase[x][y] = 1;
3691 ExplodeDelay[x][y] = last_phase;
3696 if (center_element == EL_YAMYAM)
3697 game.yamyam_content_nr =
3698 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3710 GfxFrame[x][y] = 0; /* restart explosion animation */
3712 last_phase = ExplodeDelay[x][y];
3714 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3718 /* activate this even in non-DEBUG version until cause for crash in
3719 getGraphicAnimationFrame() (see below) is found and eliminated */
3725 /* this can happen if the player leaves an explosion just in time */
3726 if (GfxElement[x][y] == EL_UNDEFINED)
3727 GfxElement[x][y] = EL_EMPTY;
3729 if (GfxElement[x][y] == EL_UNDEFINED)
3732 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3733 printf("Explode(): This should never happen!\n");
3736 GfxElement[x][y] = EL_EMPTY;
3742 border_element = Store2[x][y];
3743 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3744 border_element = StorePlayer[x][y];
3746 if (phase == element_info[border_element].ignition_delay ||
3747 phase == last_phase)
3749 boolean border_explosion = FALSE;
3751 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3752 !PLAYER_EXPLOSION_PROTECTED(x, y))
3754 KillPlayerUnlessExplosionProtected(x, y);
3755 border_explosion = TRUE;
3757 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3759 Feld[x][y] = Store2[x][y];
3762 border_explosion = TRUE;
3764 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3766 AmoebeUmwandeln(x, y);
3768 border_explosion = TRUE;
3771 /* if an element just explodes due to another explosion (chain-reaction),
3772 do not immediately end the new explosion when it was the last frame of
3773 the explosion (as it would be done in the following "if"-statement!) */
3774 if (border_explosion && phase == last_phase)
3778 if (phase == last_phase)
3782 element = Feld[x][y] = Store[x][y];
3783 Store[x][y] = Store2[x][y] = 0;
3784 GfxElement[x][y] = EL_UNDEFINED;
3786 /* player can escape from explosions and might therefore be still alive */
3787 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3788 element <= EL_PLAYER_IS_EXPLODING_4)
3790 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3791 int explosion_element = EL_PLAYER_1 + player_nr;
3792 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3793 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3795 if (level.use_explosion_element[player_nr])
3796 explosion_element = level.explosion_element[player_nr];
3798 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3799 element_info[explosion_element].content.e[xx][yy]);
3802 /* restore probably existing indestructible background element */
3803 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3804 element = Feld[x][y] = Back[x][y];
3807 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3808 GfxDir[x][y] = MV_NONE;
3809 ChangeDelay[x][y] = 0;
3810 ChangePage[x][y] = -1;
3812 #if USE_NEW_CUSTOM_VALUE
3813 CustomValue[x][y] = 0;
3816 InitField_WithBug2(x, y, FALSE);
3818 DrawLevelField(x, y);
3820 TestIfElementTouchesCustomElement(x, y);
3822 if (GFX_CRUMBLED(element))
3823 DrawLevelFieldCrumbledSandNeighbours(x, y);
3825 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3826 StorePlayer[x][y] = 0;
3828 if (ELEM_IS_PLAYER(element))
3829 RelocatePlayer(x, y, element);
3831 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3833 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3834 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3837 DrawLevelFieldCrumbledSand(x, y);
3839 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3841 DrawLevelElement(x, y, Back[x][y]);
3842 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3844 else if (IS_WALKABLE_UNDER(Back[x][y]))
3846 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3847 DrawLevelElementThruMask(x, y, Back[x][y]);
3849 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3850 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3854 void DynaExplode(int ex, int ey)
3857 int dynabomb_element = Feld[ex][ey];
3858 int dynabomb_size = 1;
3859 boolean dynabomb_xl = FALSE;
3860 struct PlayerInfo *player;
3861 static int xy[4][2] =
3869 if (IS_ACTIVE_BOMB(dynabomb_element))
3871 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3872 dynabomb_size = player->dynabomb_size;
3873 dynabomb_xl = player->dynabomb_xl;
3874 player->dynabombs_left++;
3877 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3879 for (i = 0; i < NUM_DIRECTIONS; i++)
3881 for (j = 1; j <= dynabomb_size; j++)
3883 int x = ex + j * xy[i][0];
3884 int y = ey + j * xy[i][1];
3887 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3890 element = Feld[x][y];
3892 /* do not restart explosions of fields with active bombs */
3893 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3896 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3898 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3899 !IS_DIGGABLE(element) && !dynabomb_xl)
3905 void Bang(int x, int y)
3907 int element = MovingOrBlocked2Element(x, y);
3908 int explosion_type = EX_TYPE_NORMAL;
3910 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3912 struct PlayerInfo *player = PLAYERINFO(x, y);
3914 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3915 player->element_nr);
3917 if (level.use_explosion_element[player->index_nr])
3919 int explosion_element = level.explosion_element[player->index_nr];
3921 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3922 explosion_type = EX_TYPE_CROSS;
3923 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3924 explosion_type = EX_TYPE_CENTER;
3932 case EL_BD_BUTTERFLY:
3935 case EL_DARK_YAMYAM:
3939 RaiseScoreElement(element);
3942 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3943 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3944 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3945 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3946 case EL_DYNABOMB_INCREASE_NUMBER:
3947 case EL_DYNABOMB_INCREASE_SIZE:
3948 case EL_DYNABOMB_INCREASE_POWER:
3949 explosion_type = EX_TYPE_DYNA;
3954 case EL_LAMP_ACTIVE:
3955 case EL_AMOEBA_TO_DIAMOND:
3956 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3957 explosion_type = EX_TYPE_CENTER;
3961 if (element_info[element].explosion_type == EXPLODES_CROSS)
3962 explosion_type = EX_TYPE_CROSS;
3963 else if (element_info[element].explosion_type == EXPLODES_1X1)
3964 explosion_type = EX_TYPE_CENTER;
3968 if (explosion_type == EX_TYPE_DYNA)
3971 Explode(x, y, EX_PHASE_START, explosion_type);
3973 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3976 void SplashAcid(int x, int y)
3978 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3979 (!IN_LEV_FIELD(x - 1, y - 2) ||
3980 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3981 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3983 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3984 (!IN_LEV_FIELD(x + 1, y - 2) ||
3985 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3986 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3988 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3991 static void InitBeltMovement()
3993 static int belt_base_element[4] =
3995 EL_CONVEYOR_BELT_1_LEFT,
3996 EL_CONVEYOR_BELT_2_LEFT,
3997 EL_CONVEYOR_BELT_3_LEFT,
3998 EL_CONVEYOR_BELT_4_LEFT
4000 static int belt_base_active_element[4] =
4002 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4003 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4004 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4005 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4010 /* set frame order for belt animation graphic according to belt direction */
4011 for (i = 0; i < NUM_BELTS; i++)
4015 for (j = 0; j < NUM_BELT_PARTS; j++)
4017 int element = belt_base_active_element[belt_nr] + j;
4018 int graphic = el2img(element);
4020 if (game.belt_dir[i] == MV_LEFT)
4021 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4023 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4027 SCAN_PLAYFIELD(x, y)
4029 int element = Feld[x][y];
4031 for (i = 0; i < NUM_BELTS; i++)
4033 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4035 int e_belt_nr = getBeltNrFromBeltElement(element);
4038 if (e_belt_nr == belt_nr)
4040 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4042 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4049 static void ToggleBeltSwitch(int x, int y)
4051 static int belt_base_element[4] =
4053 EL_CONVEYOR_BELT_1_LEFT,
4054 EL_CONVEYOR_BELT_2_LEFT,
4055 EL_CONVEYOR_BELT_3_LEFT,
4056 EL_CONVEYOR_BELT_4_LEFT
4058 static int belt_base_active_element[4] =
4060 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4061 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4062 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4063 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4065 static int belt_base_switch_element[4] =
4067 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4068 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4069 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4070 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4072 static int belt_move_dir[4] =
4080 int element = Feld[x][y];
4081 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4082 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4083 int belt_dir = belt_move_dir[belt_dir_nr];
4086 if (!IS_BELT_SWITCH(element))
4089 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4090 game.belt_dir[belt_nr] = belt_dir;
4092 if (belt_dir_nr == 3)
4095 /* set frame order for belt animation graphic according to belt direction */
4096 for (i = 0; i < NUM_BELT_PARTS; i++)
4098 int element = belt_base_active_element[belt_nr] + i;
4099 int graphic = el2img(element);
4101 if (belt_dir == MV_LEFT)
4102 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4104 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4107 SCAN_PLAYFIELD(xx, yy)
4109 int element = Feld[xx][yy];
4111 if (IS_BELT_SWITCH(element))
4113 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4115 if (e_belt_nr == belt_nr)
4117 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4118 DrawLevelField(xx, yy);
4121 else if (IS_BELT(element) && belt_dir != MV_NONE)
4123 int e_belt_nr = getBeltNrFromBeltElement(element);
4125 if (e_belt_nr == belt_nr)
4127 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4129 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4130 DrawLevelField(xx, yy);
4133 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4135 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4137 if (e_belt_nr == belt_nr)
4139 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4141 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4142 DrawLevelField(xx, yy);
4148 static void ToggleSwitchgateSwitch(int x, int y)
4152 game.switchgate_pos = !game.switchgate_pos;
4154 SCAN_PLAYFIELD(xx, yy)
4156 int element = Feld[xx][yy];
4158 #if !USE_BOTH_SWITCHGATE_SWITCHES
4159 if (element == EL_SWITCHGATE_SWITCH_UP ||
4160 element == EL_SWITCHGATE_SWITCH_DOWN)
4162 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4163 DrawLevelField(xx, yy);
4166 if (element == EL_SWITCHGATE_SWITCH_UP)
4168 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4169 DrawLevelField(xx, yy);
4171 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4173 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4174 DrawLevelField(xx, yy);
4177 else if (element == EL_SWITCHGATE_OPEN ||
4178 element == EL_SWITCHGATE_OPENING)
4180 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4182 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4184 else if (element == EL_SWITCHGATE_CLOSED ||
4185 element == EL_SWITCHGATE_CLOSING)
4187 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4189 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4194 static int getInvisibleActiveFromInvisibleElement(int element)
4196 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4197 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4198 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4202 static int getInvisibleFromInvisibleActiveElement(int element)
4204 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4205 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4206 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4210 static void RedrawAllLightSwitchesAndInvisibleElements()
4214 SCAN_PLAYFIELD(x, y)
4216 int element = Feld[x][y];
4218 if (element == EL_LIGHT_SWITCH &&
4219 game.light_time_left > 0)
4221 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4222 DrawLevelField(x, y);
4224 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4225 game.light_time_left == 0)
4227 Feld[x][y] = EL_LIGHT_SWITCH;
4228 DrawLevelField(x, y);
4230 else if (element == EL_EMC_DRIPPER &&
4231 game.light_time_left > 0)
4233 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4234 DrawLevelField(x, y);
4236 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4237 game.light_time_left == 0)
4239 Feld[x][y] = EL_EMC_DRIPPER;
4240 DrawLevelField(x, y);
4242 else if (element == EL_INVISIBLE_STEELWALL ||
4243 element == EL_INVISIBLE_WALL ||
4244 element == EL_INVISIBLE_SAND)
4246 if (game.light_time_left > 0)
4247 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4249 DrawLevelField(x, y);
4251 /* uncrumble neighbour fields, if needed */
4252 if (element == EL_INVISIBLE_SAND)
4253 DrawLevelFieldCrumbledSandNeighbours(x, y);
4255 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4256 element == EL_INVISIBLE_WALL_ACTIVE ||
4257 element == EL_INVISIBLE_SAND_ACTIVE)
4259 if (game.light_time_left == 0)
4260 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4262 DrawLevelField(x, y);
4264 /* re-crumble neighbour fields, if needed */
4265 if (element == EL_INVISIBLE_SAND)
4266 DrawLevelFieldCrumbledSandNeighbours(x, y);
4271 static void RedrawAllInvisibleElementsForLenses()
4275 SCAN_PLAYFIELD(x, y)
4277 int element = Feld[x][y];
4279 if (element == EL_EMC_DRIPPER &&
4280 game.lenses_time_left > 0)
4282 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4283 DrawLevelField(x, y);
4285 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4286 game.lenses_time_left == 0)
4288 Feld[x][y] = EL_EMC_DRIPPER;
4289 DrawLevelField(x, y);
4291 else if (element == EL_INVISIBLE_STEELWALL ||
4292 element == EL_INVISIBLE_WALL ||
4293 element == EL_INVISIBLE_SAND)
4295 if (game.lenses_time_left > 0)
4296 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4298 DrawLevelField(x, y);
4300 /* uncrumble neighbour fields, if needed */
4301 if (element == EL_INVISIBLE_SAND)
4302 DrawLevelFieldCrumbledSandNeighbours(x, y);
4304 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4305 element == EL_INVISIBLE_WALL_ACTIVE ||
4306 element == EL_INVISIBLE_SAND_ACTIVE)
4308 if (game.lenses_time_left == 0)
4309 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4311 DrawLevelField(x, y);
4313 /* re-crumble neighbour fields, if needed */
4314 if (element == EL_INVISIBLE_SAND)
4315 DrawLevelFieldCrumbledSandNeighbours(x, y);
4320 static void RedrawAllInvisibleElementsForMagnifier()
4324 SCAN_PLAYFIELD(x, y)
4326 int element = Feld[x][y];
4328 if (element == EL_EMC_FAKE_GRASS &&
4329 game.magnify_time_left > 0)
4331 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4332 DrawLevelField(x, y);
4334 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4335 game.magnify_time_left == 0)
4337 Feld[x][y] = EL_EMC_FAKE_GRASS;
4338 DrawLevelField(x, y);
4340 else if (IS_GATE_GRAY(element) &&
4341 game.magnify_time_left > 0)
4343 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4344 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4345 IS_EM_GATE_GRAY(element) ?
4346 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4347 IS_EMC_GATE_GRAY(element) ?
4348 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4350 DrawLevelField(x, y);
4352 else if (IS_GATE_GRAY_ACTIVE(element) &&
4353 game.magnify_time_left == 0)
4355 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4356 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4357 IS_EM_GATE_GRAY_ACTIVE(element) ?
4358 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4359 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4360 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4362 DrawLevelField(x, y);
4367 static void ToggleLightSwitch(int x, int y)
4369 int element = Feld[x][y];
4371 game.light_time_left =
4372 (element == EL_LIGHT_SWITCH ?
4373 level.time_light * FRAMES_PER_SECOND : 0);
4375 RedrawAllLightSwitchesAndInvisibleElements();
4378 static void ActivateTimegateSwitch(int x, int y)
4382 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4384 SCAN_PLAYFIELD(xx, yy)
4386 int element = Feld[xx][yy];
4388 if (element == EL_TIMEGATE_CLOSED ||
4389 element == EL_TIMEGATE_CLOSING)
4391 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4392 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4396 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4398 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4399 DrawLevelField(xx, yy);
4405 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4408 void Impact(int x, int y)
4410 boolean last_line = (y == lev_fieldy - 1);
4411 boolean object_hit = FALSE;
4412 boolean impact = (last_line || object_hit);
4413 int element = Feld[x][y];
4414 int smashed = EL_STEELWALL;
4416 if (!last_line) /* check if element below was hit */
4418 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4421 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4422 MovDir[x][y + 1] != MV_DOWN ||
4423 MovPos[x][y + 1] <= TILEY / 2));
4425 /* do not smash moving elements that left the smashed field in time */
4426 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4427 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4430 #if USE_QUICKSAND_IMPACT_BUGFIX
4431 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4433 RemoveMovingField(x, y + 1);
4434 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4435 Feld[x][y + 2] = EL_ROCK;
4436 DrawLevelField(x, y + 2);
4443 smashed = MovingOrBlocked2Element(x, y + 1);
4445 impact = (last_line || object_hit);
4448 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4450 SplashAcid(x, y + 1);
4454 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4455 /* only reset graphic animation if graphic really changes after impact */
4457 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4459 ResetGfxAnimation(x, y);
4460 DrawLevelField(x, y);
4463 if (impact && CAN_EXPLODE_IMPACT(element))
4468 else if (impact && element == EL_PEARL)
4470 ResetGfxAnimation(x, y);
4472 Feld[x][y] = EL_PEARL_BREAKING;
4473 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4476 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4478 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4483 if (impact && element == EL_AMOEBA_DROP)
4485 if (object_hit && IS_PLAYER(x, y + 1))
4486 KillPlayerUnlessEnemyProtected(x, y + 1);
4487 else if (object_hit && smashed == EL_PENGUIN)
4491 Feld[x][y] = EL_AMOEBA_GROWING;
4492 Store[x][y] = EL_AMOEBA_WET;
4494 ResetRandomAnimationValue(x, y);
4499 if (object_hit) /* check which object was hit */
4501 if (CAN_PASS_MAGIC_WALL(element) &&
4502 (smashed == EL_MAGIC_WALL ||
4503 smashed == EL_BD_MAGIC_WALL))
4506 int activated_magic_wall =
4507 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4508 EL_BD_MAGIC_WALL_ACTIVE);
4510 /* activate magic wall / mill */
4511 SCAN_PLAYFIELD(xx, yy)
4512 if (Feld[xx][yy] == smashed)
4513 Feld[xx][yy] = activated_magic_wall;
4515 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4516 game.magic_wall_active = TRUE;
4518 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4519 SND_MAGIC_WALL_ACTIVATING :
4520 SND_BD_MAGIC_WALL_ACTIVATING));
4523 if (IS_PLAYER(x, y + 1))
4525 if (CAN_SMASH_PLAYER(element))
4527 KillPlayerUnlessEnemyProtected(x, y + 1);
4531 else if (smashed == EL_PENGUIN)
4533 if (CAN_SMASH_PLAYER(element))
4539 else if (element == EL_BD_DIAMOND)
4541 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4547 else if (((element == EL_SP_INFOTRON ||
4548 element == EL_SP_ZONK) &&
4549 (smashed == EL_SP_SNIKSNAK ||
4550 smashed == EL_SP_ELECTRON ||
4551 smashed == EL_SP_DISK_ORANGE)) ||
4552 (element == EL_SP_INFOTRON &&
4553 smashed == EL_SP_DISK_YELLOW))
4558 else if (CAN_SMASH_EVERYTHING(element))
4560 if (IS_CLASSIC_ENEMY(smashed) ||
4561 CAN_EXPLODE_SMASHED(smashed))
4566 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4568 if (smashed == EL_LAMP ||
4569 smashed == EL_LAMP_ACTIVE)
4574 else if (smashed == EL_NUT)
4576 Feld[x][y + 1] = EL_NUT_BREAKING;
4577 PlayLevelSound(x, y, SND_NUT_BREAKING);
4578 RaiseScoreElement(EL_NUT);
4581 else if (smashed == EL_PEARL)
4583 ResetGfxAnimation(x, y);
4585 Feld[x][y + 1] = EL_PEARL_BREAKING;
4586 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4589 else if (smashed == EL_DIAMOND)
4591 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4592 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4595 else if (IS_BELT_SWITCH(smashed))
4597 ToggleBeltSwitch(x, y + 1);
4599 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4600 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4602 ToggleSwitchgateSwitch(x, y + 1);
4604 else if (smashed == EL_LIGHT_SWITCH ||
4605 smashed == EL_LIGHT_SWITCH_ACTIVE)
4607 ToggleLightSwitch(x, y + 1);
4612 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4615 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4617 CheckElementChangeBySide(x, y + 1, smashed, element,
4618 CE_SWITCHED, CH_SIDE_TOP);
4619 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4625 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4630 /* play sound of magic wall / mill */
4632 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4633 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4635 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4636 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4637 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4638 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4643 /* play sound of object that hits the ground */
4644 if (last_line || object_hit)
4645 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4648 inline static void TurnRoundExt(int x, int y)
4660 { 0, 0 }, { 0, 0 }, { 0, 0 },
4665 int left, right, back;
4669 { MV_DOWN, MV_UP, MV_RIGHT },
4670 { MV_UP, MV_DOWN, MV_LEFT },
4672 { MV_LEFT, MV_RIGHT, MV_DOWN },
4676 { MV_RIGHT, MV_LEFT, MV_UP }
4679 int element = Feld[x][y];
4680 int move_pattern = element_info[element].move_pattern;
4682 int old_move_dir = MovDir[x][y];
4683 int left_dir = turn[old_move_dir].left;
4684 int right_dir = turn[old_move_dir].right;
4685 int back_dir = turn[old_move_dir].back;
4687 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4688 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4689 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4690 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4692 int left_x = x + left_dx, left_y = y + left_dy;
4693 int right_x = x + right_dx, right_y = y + right_dy;
4694 int move_x = x + move_dx, move_y = y + move_dy;
4698 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4700 TestIfBadThingTouchesOtherBadThing(x, y);
4702 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4703 MovDir[x][y] = right_dir;
4704 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4705 MovDir[x][y] = left_dir;
4707 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4709 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4712 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4714 TestIfBadThingTouchesOtherBadThing(x, y);
4716 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4717 MovDir[x][y] = left_dir;
4718 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4719 MovDir[x][y] = right_dir;
4721 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4723 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4726 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4728 TestIfBadThingTouchesOtherBadThing(x, y);
4730 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4731 MovDir[x][y] = left_dir;
4732 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4733 MovDir[x][y] = right_dir;
4735 if (MovDir[x][y] != old_move_dir)
4738 else if (element == EL_YAMYAM)
4740 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4741 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4743 if (can_turn_left && can_turn_right)
4744 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4745 else if (can_turn_left)
4746 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4747 else if (can_turn_right)
4748 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4750 MovDir[x][y] = back_dir;
4752 MovDelay[x][y] = 16 + 16 * RND(3);
4754 else if (element == EL_DARK_YAMYAM)
4756 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4758 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4761 if (can_turn_left && can_turn_right)
4762 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4763 else if (can_turn_left)
4764 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4765 else if (can_turn_right)
4766 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4768 MovDir[x][y] = back_dir;
4770 MovDelay[x][y] = 16 + 16 * RND(3);
4772 else if (element == EL_PACMAN)
4774 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4775 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4777 if (can_turn_left && can_turn_right)
4778 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4779 else if (can_turn_left)
4780 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4781 else if (can_turn_right)
4782 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4784 MovDir[x][y] = back_dir;
4786 MovDelay[x][y] = 6 + RND(40);
4788 else if (element == EL_PIG)
4790 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4791 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4792 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4793 boolean should_turn_left, should_turn_right, should_move_on;
4795 int rnd = RND(rnd_value);
4797 should_turn_left = (can_turn_left &&
4799 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4800 y + back_dy + left_dy)));
4801 should_turn_right = (can_turn_right &&
4803 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4804 y + back_dy + right_dy)));
4805 should_move_on = (can_move_on &&
4808 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4809 y + move_dy + left_dy) ||
4810 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4811 y + move_dy + right_dy)));
4813 if (should_turn_left || should_turn_right || should_move_on)
4815 if (should_turn_left && should_turn_right && should_move_on)
4816 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4817 rnd < 2 * rnd_value / 3 ? right_dir :
4819 else if (should_turn_left && should_turn_right)
4820 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4821 else if (should_turn_left && should_move_on)
4822 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4823 else if (should_turn_right && should_move_on)
4824 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4825 else if (should_turn_left)
4826 MovDir[x][y] = left_dir;
4827 else if (should_turn_right)
4828 MovDir[x][y] = right_dir;
4829 else if (should_move_on)
4830 MovDir[x][y] = old_move_dir;
4832 else if (can_move_on && rnd > rnd_value / 8)
4833 MovDir[x][y] = old_move_dir;
4834 else if (can_turn_left && can_turn_right)
4835 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4836 else if (can_turn_left && rnd > rnd_value / 8)
4837 MovDir[x][y] = left_dir;
4838 else if (can_turn_right && rnd > rnd_value/8)
4839 MovDir[x][y] = right_dir;
4841 MovDir[x][y] = back_dir;
4843 xx = x + move_xy[MovDir[x][y]].dx;
4844 yy = y + move_xy[MovDir[x][y]].dy;
4846 if (!IN_LEV_FIELD(xx, yy) ||
4847 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4848 MovDir[x][y] = old_move_dir;
4852 else if (element == EL_DRAGON)
4854 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4855 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4856 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4858 int rnd = RND(rnd_value);
4860 if (can_move_on && rnd > rnd_value / 8)
4861 MovDir[x][y] = old_move_dir;
4862 else if (can_turn_left && can_turn_right)
4863 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4864 else if (can_turn_left && rnd > rnd_value / 8)
4865 MovDir[x][y] = left_dir;
4866 else if (can_turn_right && rnd > rnd_value / 8)
4867 MovDir[x][y] = right_dir;
4869 MovDir[x][y] = back_dir;
4871 xx = x + move_xy[MovDir[x][y]].dx;
4872 yy = y + move_xy[MovDir[x][y]].dy;
4874 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4875 MovDir[x][y] = old_move_dir;
4879 else if (element == EL_MOLE)
4881 boolean can_move_on =
4882 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4883 IS_AMOEBOID(Feld[move_x][move_y]) ||
4884 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4887 boolean can_turn_left =
4888 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4889 IS_AMOEBOID(Feld[left_x][left_y])));
4891 boolean can_turn_right =
4892 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4893 IS_AMOEBOID(Feld[right_x][right_y])));
4895 if (can_turn_left && can_turn_right)
4896 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4897 else if (can_turn_left)
4898 MovDir[x][y] = left_dir;
4900 MovDir[x][y] = right_dir;
4903 if (MovDir[x][y] != old_move_dir)
4906 else if (element == EL_BALLOON)
4908 MovDir[x][y] = game.wind_direction;
4911 else if (element == EL_SPRING)
4913 #if USE_NEW_SPRING_BUMPER
4914 if (MovDir[x][y] & MV_HORIZONTAL)
4916 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4917 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4919 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4920 ResetGfxAnimation(move_x, move_y);
4921 DrawLevelField(move_x, move_y);
4923 MovDir[x][y] = back_dir;
4925 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4926 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4927 MovDir[x][y] = MV_NONE;
4930 if (MovDir[x][y] & MV_HORIZONTAL &&
4931 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4932 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4933 MovDir[x][y] = MV_NONE;
4938 else if (element == EL_ROBOT ||
4939 element == EL_SATELLITE ||
4940 element == EL_PENGUIN ||
4941 element == EL_EMC_ANDROID)
4943 int attr_x = -1, attr_y = -1;
4954 for (i = 0; i < MAX_PLAYERS; i++)
4956 struct PlayerInfo *player = &stored_player[i];
4957 int jx = player->jx, jy = player->jy;
4959 if (!player->active)
4963 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4971 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4972 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4973 game.engine_version < VERSION_IDENT(3,1,0,0)))
4979 if (element == EL_PENGUIN)
4982 static int xy[4][2] =
4990 for (i = 0; i < NUM_DIRECTIONS; i++)
4992 int ex = x + xy[i][0];
4993 int ey = y + xy[i][1];
4995 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5004 MovDir[x][y] = MV_NONE;
5006 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5007 else if (attr_x > x)
5008 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5010 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5011 else if (attr_y > y)
5012 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5014 if (element == EL_ROBOT)
5018 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5019 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5020 Moving2Blocked(x, y, &newx, &newy);
5022 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5023 MovDelay[x][y] = 8 + 8 * !RND(3);
5025 MovDelay[x][y] = 16;
5027 else if (element == EL_PENGUIN)
5033 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5035 boolean first_horiz = RND(2);
5036 int new_move_dir = MovDir[x][y];
5039 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5040 Moving2Blocked(x, y, &newx, &newy);
5042 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5046 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5047 Moving2Blocked(x, y, &newx, &newy);
5049 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5052 MovDir[x][y] = old_move_dir;
5056 else if (element == EL_SATELLITE)
5062 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5064 boolean first_horiz = RND(2);
5065 int new_move_dir = MovDir[x][y];
5068 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5069 Moving2Blocked(x, y, &newx, &newy);
5071 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5075 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5076 Moving2Blocked(x, y, &newx, &newy);
5078 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5081 MovDir[x][y] = old_move_dir;
5085 else if (element == EL_EMC_ANDROID)
5087 static int check_pos[16] =
5089 -1, /* 0 => (invalid) */
5090 7, /* 1 => MV_LEFT */
5091 3, /* 2 => MV_RIGHT */
5092 -1, /* 3 => (invalid) */
5094 0, /* 5 => MV_LEFT | MV_UP */
5095 2, /* 6 => MV_RIGHT | MV_UP */
5096 -1, /* 7 => (invalid) */
5097 5, /* 8 => MV_DOWN */
5098 6, /* 9 => MV_LEFT | MV_DOWN */
5099 4, /* 10 => MV_RIGHT | MV_DOWN */
5100 -1, /* 11 => (invalid) */
5101 -1, /* 12 => (invalid) */
5102 -1, /* 13 => (invalid) */
5103 -1, /* 14 => (invalid) */
5104 -1, /* 15 => (invalid) */
5112 { -1, -1, MV_LEFT | MV_UP },
5114 { +1, -1, MV_RIGHT | MV_UP },
5115 { +1, 0, MV_RIGHT },
5116 { +1, +1, MV_RIGHT | MV_DOWN },
5118 { -1, +1, MV_LEFT | MV_DOWN },
5121 int start_pos, check_order;
5122 boolean can_clone = FALSE;
5125 /* check if there is any free field around current position */
5126 for (i = 0; i < 8; i++)
5128 int newx = x + check_xy[i].dx;
5129 int newy = y + check_xy[i].dy;
5131 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5139 if (can_clone) /* randomly find an element to clone */
5143 start_pos = check_pos[RND(8)];
5144 check_order = (RND(2) ? -1 : +1);
5146 for (i = 0; i < 8; i++)
5148 int pos_raw = start_pos + i * check_order;
5149 int pos = (pos_raw + 8) % 8;
5150 int newx = x + check_xy[pos].dx;
5151 int newy = y + check_xy[pos].dy;
5153 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5155 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5156 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5158 Store[x][y] = Feld[newx][newy];
5167 if (can_clone) /* randomly find a direction to move */
5171 start_pos = check_pos[RND(8)];
5172 check_order = (RND(2) ? -1 : +1);
5174 for (i = 0; i < 8; i++)
5176 int pos_raw = start_pos + i * check_order;
5177 int pos = (pos_raw + 8) % 8;
5178 int newx = x + check_xy[pos].dx;
5179 int newy = y + check_xy[pos].dy;
5180 int new_move_dir = check_xy[pos].dir;
5182 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5184 MovDir[x][y] = new_move_dir;
5185 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5194 if (can_clone) /* cloning and moving successful */
5197 /* cannot clone -- try to move towards player */
5199 start_pos = check_pos[MovDir[x][y] & 0x0f];
5200 check_order = (RND(2) ? -1 : +1);
5202 for (i = 0; i < 3; i++)
5204 /* first check start_pos, then previous/next or (next/previous) pos */
5205 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5206 int pos = (pos_raw + 8) % 8;
5207 int newx = x + check_xy[pos].dx;
5208 int newy = y + check_xy[pos].dy;
5209 int new_move_dir = check_xy[pos].dir;
5211 if (IS_PLAYER(newx, newy))
5214 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5216 MovDir[x][y] = new_move_dir;
5217 MovDelay[x][y] = level.android_move_time * 8 + 1;
5224 else if (move_pattern == MV_TURNING_LEFT ||
5225 move_pattern == MV_TURNING_RIGHT ||
5226 move_pattern == MV_TURNING_LEFT_RIGHT ||
5227 move_pattern == MV_TURNING_RIGHT_LEFT ||
5228 move_pattern == MV_TURNING_RANDOM ||
5229 move_pattern == MV_ALL_DIRECTIONS)
5231 boolean can_turn_left =
5232 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5233 boolean can_turn_right =
5234 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5236 if (element_info[element].move_stepsize == 0) /* "not moving" */
5239 if (move_pattern == MV_TURNING_LEFT)
5240 MovDir[x][y] = left_dir;
5241 else if (move_pattern == MV_TURNING_RIGHT)
5242 MovDir[x][y] = right_dir;
5243 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5244 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5245 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5246 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5247 else if (move_pattern == MV_TURNING_RANDOM)
5248 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5249 can_turn_right && !can_turn_left ? right_dir :
5250 RND(2) ? left_dir : right_dir);
5251 else if (can_turn_left && can_turn_right)
5252 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5253 else if (can_turn_left)
5254 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5255 else if (can_turn_right)
5256 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5258 MovDir[x][y] = back_dir;
5260 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5262 else if (move_pattern == MV_HORIZONTAL ||
5263 move_pattern == MV_VERTICAL)
5265 if (move_pattern & old_move_dir)
5266 MovDir[x][y] = back_dir;
5267 else if (move_pattern == MV_HORIZONTAL)
5268 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5269 else if (move_pattern == MV_VERTICAL)
5270 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5272 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5274 else if (move_pattern & MV_ANY_DIRECTION)
5276 MovDir[x][y] = move_pattern;
5277 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5279 else if (move_pattern & MV_WIND_DIRECTION)
5281 MovDir[x][y] = game.wind_direction;
5282 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5284 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5286 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5287 MovDir[x][y] = left_dir;
5288 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5289 MovDir[x][y] = right_dir;
5291 if (MovDir[x][y] != old_move_dir)
5292 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5294 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5296 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5297 MovDir[x][y] = right_dir;
5298 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5299 MovDir[x][y] = left_dir;
5301 if (MovDir[x][y] != old_move_dir)
5302 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5304 else if (move_pattern == MV_TOWARDS_PLAYER ||
5305 move_pattern == MV_AWAY_FROM_PLAYER)
5307 int attr_x = -1, attr_y = -1;
5309 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5320 for (i = 0; i < MAX_PLAYERS; i++)
5322 struct PlayerInfo *player = &stored_player[i];
5323 int jx = player->jx, jy = player->jy;
5325 if (!player->active)
5329 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5337 MovDir[x][y] = MV_NONE;
5339 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5340 else if (attr_x > x)
5341 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5343 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5344 else if (attr_y > y)
5345 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5347 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5349 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5351 boolean first_horiz = RND(2);
5352 int new_move_dir = MovDir[x][y];
5354 if (element_info[element].move_stepsize == 0) /* "not moving" */
5356 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5357 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5363 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5364 Moving2Blocked(x, y, &newx, &newy);
5366 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5370 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5371 Moving2Blocked(x, y, &newx, &newy);
5373 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5376 MovDir[x][y] = old_move_dir;
5379 else if (move_pattern == MV_WHEN_PUSHED ||
5380 move_pattern == MV_WHEN_DROPPED)
5382 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5383 MovDir[x][y] = MV_NONE;
5387 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5389 static int test_xy[7][2] =
5399 static int test_dir[7] =
5409 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5410 int move_preference = -1000000; /* start with very low preference */
5411 int new_move_dir = MV_NONE;
5412 int start_test = RND(4);
5415 for (i = 0; i < NUM_DIRECTIONS; i++)
5417 int move_dir = test_dir[start_test + i];
5418 int move_dir_preference;
5420 xx = x + test_xy[start_test + i][0];
5421 yy = y + test_xy[start_test + i][1];
5423 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5424 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5426 new_move_dir = move_dir;
5431 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5434 move_dir_preference = -1 * RunnerVisit[xx][yy];
5435 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5436 move_dir_preference = PlayerVisit[xx][yy];
5438 if (move_dir_preference > move_preference)
5440 /* prefer field that has not been visited for the longest time */
5441 move_preference = move_dir_preference;
5442 new_move_dir = move_dir;
5444 else if (move_dir_preference == move_preference &&
5445 move_dir == old_move_dir)
5447 /* prefer last direction when all directions are preferred equally */
5448 move_preference = move_dir_preference;
5449 new_move_dir = move_dir;
5453 MovDir[x][y] = new_move_dir;
5454 if (old_move_dir != new_move_dir)
5455 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5459 static void TurnRound(int x, int y)
5461 int direction = MovDir[x][y];
5465 GfxDir[x][y] = MovDir[x][y];
5467 if (direction != MovDir[x][y])
5471 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5473 ResetGfxFrame(x, y, FALSE);
5476 static boolean JustBeingPushed(int x, int y)
5480 for (i = 0; i < MAX_PLAYERS; i++)
5482 struct PlayerInfo *player = &stored_player[i];
5484 if (player->active && player->is_pushing && player->MovPos)
5486 int next_jx = player->jx + (player->jx - player->last_jx);
5487 int next_jy = player->jy + (player->jy - player->last_jy);
5489 if (x == next_jx && y == next_jy)
5497 void StartMoving(int x, int y)
5499 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5500 int element = Feld[x][y];
5505 if (MovDelay[x][y] == 0)
5506 GfxAction[x][y] = ACTION_DEFAULT;
5508 if (CAN_FALL(element) && y < lev_fieldy - 1)
5510 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5511 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5512 if (JustBeingPushed(x, y))
5515 if (element == EL_QUICKSAND_FULL)
5517 if (IS_FREE(x, y + 1))
5519 InitMovingField(x, y, MV_DOWN);
5520 started_moving = TRUE;
5522 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5523 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5524 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5525 Store[x][y] = EL_ROCK;
5527 Store[x][y] = EL_ROCK;
5530 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5532 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5534 if (!MovDelay[x][y])
5535 MovDelay[x][y] = TILEY + 1;
5544 Feld[x][y] = EL_QUICKSAND_EMPTY;
5545 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5546 Store[x][y + 1] = Store[x][y];
5549 PlayLevelSoundAction(x, y, ACTION_FILLING);
5552 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5553 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5555 InitMovingField(x, y, MV_DOWN);
5556 started_moving = TRUE;
5558 Feld[x][y] = EL_QUICKSAND_FILLING;
5559 Store[x][y] = element;
5561 PlayLevelSoundAction(x, y, ACTION_FILLING);
5563 else if (element == EL_MAGIC_WALL_FULL)
5565 if (IS_FREE(x, y + 1))
5567 InitMovingField(x, y, MV_DOWN);
5568 started_moving = TRUE;
5570 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5571 Store[x][y] = EL_CHANGED(Store[x][y]);
5573 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5575 if (!MovDelay[x][y])
5576 MovDelay[x][y] = TILEY/4 + 1;
5585 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5586 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5587 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5591 else if (element == EL_BD_MAGIC_WALL_FULL)
5593 if (IS_FREE(x, y + 1))
5595 InitMovingField(x, y, MV_DOWN);
5596 started_moving = TRUE;
5598 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5599 Store[x][y] = EL_CHANGED2(Store[x][y]);
5601 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5603 if (!MovDelay[x][y])
5604 MovDelay[x][y] = TILEY/4 + 1;
5613 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5614 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5615 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5619 else if (CAN_PASS_MAGIC_WALL(element) &&
5620 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5621 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5623 InitMovingField(x, y, MV_DOWN);
5624 started_moving = TRUE;
5627 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5628 EL_BD_MAGIC_WALL_FILLING);
5629 Store[x][y] = element;
5631 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5633 SplashAcid(x, y + 1);
5635 InitMovingField(x, y, MV_DOWN);
5636 started_moving = TRUE;
5638 Store[x][y] = EL_ACID;
5641 #if USE_FIX_IMPACT_COLLISION
5642 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5643 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
5645 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5646 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5648 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5649 CAN_FALL(element) && WasJustFalling[x][y] &&
5650 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5652 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5653 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5654 (Feld[x][y + 1] == EL_BLOCKED)))
5656 /* this is needed for a special case not covered by calling "Impact()"
5657 from "ContinueMoving()": if an element moves to a tile directly below
5658 another element which was just falling on that tile (which was empty
5659 in the previous frame), the falling element above would just stop
5660 instead of smashing the element below (in previous version, the above
5661 element was just checked for "moving" instead of "falling", resulting
5662 in incorrect smashes caused by horizontal movement of the above
5663 element; also, the case of the player being the element to smash was
5664 simply not covered here... :-/ ) */
5666 CheckCollision[x][y] = 0;
5667 CheckImpact[x][y] = 0;
5671 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5673 if (MovDir[x][y] == MV_NONE)
5675 InitMovingField(x, y, MV_DOWN);
5676 started_moving = TRUE;
5679 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5681 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5682 MovDir[x][y] = MV_DOWN;
5684 InitMovingField(x, y, MV_DOWN);
5685 started_moving = TRUE;
5687 else if (element == EL_AMOEBA_DROP)
5689 Feld[x][y] = EL_AMOEBA_GROWING;
5690 Store[x][y] = EL_AMOEBA_WET;
5692 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5693 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5694 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5695 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5697 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5698 (IS_FREE(x - 1, y + 1) ||
5699 Feld[x - 1][y + 1] == EL_ACID));
5700 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5701 (IS_FREE(x + 1, y + 1) ||
5702 Feld[x + 1][y + 1] == EL_ACID));
5703 boolean can_fall_any = (can_fall_left || can_fall_right);
5704 boolean can_fall_both = (can_fall_left && can_fall_right);
5705 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5707 #if USE_NEW_ALL_SLIPPERY
5708 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5710 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5711 can_fall_right = FALSE;
5712 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5713 can_fall_left = FALSE;
5714 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5715 can_fall_right = FALSE;
5716 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5717 can_fall_left = FALSE;
5719 can_fall_any = (can_fall_left || can_fall_right);
5720 can_fall_both = FALSE;
5723 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5725 if (slippery_type == SLIPPERY_ONLY_LEFT)
5726 can_fall_right = FALSE;
5727 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5728 can_fall_left = FALSE;
5729 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5730 can_fall_right = FALSE;
5731 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5732 can_fall_left = FALSE;
5734 can_fall_any = (can_fall_left || can_fall_right);
5735 can_fall_both = (can_fall_left && can_fall_right);
5739 #if USE_NEW_ALL_SLIPPERY
5741 #if USE_NEW_SP_SLIPPERY
5742 /* !!! better use the same properties as for custom elements here !!! */
5743 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5744 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5746 can_fall_right = FALSE; /* slip down on left side */
5747 can_fall_both = FALSE;
5752 #if USE_NEW_ALL_SLIPPERY
5755 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5756 can_fall_right = FALSE; /* slip down on left side */
5758 can_fall_left = !(can_fall_right = RND(2));
5760 can_fall_both = FALSE;
5765 if (game.emulation == EMU_BOULDERDASH ||
5766 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5767 can_fall_right = FALSE; /* slip down on left side */
5769 can_fall_left = !(can_fall_right = RND(2));
5771 can_fall_both = FALSE;
5777 /* if not determined otherwise, prefer left side for slipping down */
5778 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5779 started_moving = TRUE;
5783 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5785 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5788 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5789 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5790 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5791 int belt_dir = game.belt_dir[belt_nr];
5793 if ((belt_dir == MV_LEFT && left_is_free) ||
5794 (belt_dir == MV_RIGHT && right_is_free))
5796 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5798 InitMovingField(x, y, belt_dir);
5799 started_moving = TRUE;
5801 Pushed[x][y] = TRUE;
5802 Pushed[nextx][y] = TRUE;
5804 GfxAction[x][y] = ACTION_DEFAULT;
5808 MovDir[x][y] = 0; /* if element was moving, stop it */
5813 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5815 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5817 if (CAN_MOVE(element) && !started_moving)
5820 int move_pattern = element_info[element].move_pattern;
5825 if (MovDir[x][y] == MV_NONE)
5827 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5828 x, y, element, element_info[element].token_name);
5829 printf("StartMoving(): This should never happen!\n");
5834 Moving2Blocked(x, y, &newx, &newy);
5836 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5839 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5840 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5842 WasJustMoving[x][y] = 0;
5843 CheckCollision[x][y] = 0;
5845 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5847 if (Feld[x][y] != element) /* element has changed */
5851 if (!MovDelay[x][y]) /* start new movement phase */
5853 /* all objects that can change their move direction after each step
5854 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5856 if (element != EL_YAMYAM &&
5857 element != EL_DARK_YAMYAM &&
5858 element != EL_PACMAN &&
5859 !(move_pattern & MV_ANY_DIRECTION) &&
5860 move_pattern != MV_TURNING_LEFT &&
5861 move_pattern != MV_TURNING_RIGHT &&
5862 move_pattern != MV_TURNING_LEFT_RIGHT &&
5863 move_pattern != MV_TURNING_RIGHT_LEFT &&
5864 move_pattern != MV_TURNING_RANDOM)
5868 if (MovDelay[x][y] && (element == EL_BUG ||
5869 element == EL_SPACESHIP ||
5870 element == EL_SP_SNIKSNAK ||
5871 element == EL_SP_ELECTRON ||
5872 element == EL_MOLE))
5873 DrawLevelField(x, y);
5877 if (MovDelay[x][y]) /* wait some time before next movement */
5881 if (element == EL_ROBOT ||
5882 element == EL_YAMYAM ||
5883 element == EL_DARK_YAMYAM)
5885 DrawLevelElementAnimationIfNeeded(x, y, element);
5886 PlayLevelSoundAction(x, y, ACTION_WAITING);
5888 else if (element == EL_SP_ELECTRON)
5889 DrawLevelElementAnimationIfNeeded(x, y, element);
5890 else if (element == EL_DRAGON)
5893 int dir = MovDir[x][y];
5894 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5895 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5896 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5897 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5898 dir == MV_UP ? IMG_FLAMES_1_UP :
5899 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5900 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5902 GfxAction[x][y] = ACTION_ATTACKING;
5904 if (IS_PLAYER(x, y))
5905 DrawPlayerField(x, y);
5907 DrawLevelField(x, y);
5909 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5911 for (i = 1; i <= 3; i++)
5913 int xx = x + i * dx;
5914 int yy = y + i * dy;
5915 int sx = SCREENX(xx);
5916 int sy = SCREENY(yy);
5917 int flame_graphic = graphic + (i - 1);
5919 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5924 int flamed = MovingOrBlocked2Element(xx, yy);
5928 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5930 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5931 RemoveMovingField(xx, yy);
5933 RemoveField(xx, yy);
5935 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5938 RemoveMovingField(xx, yy);
5941 ChangeDelay[xx][yy] = 0;
5943 Feld[xx][yy] = EL_FLAMES;
5945 if (IN_SCR_FIELD(sx, sy))
5947 DrawLevelFieldCrumbledSand(xx, yy);
5948 DrawGraphic(sx, sy, flame_graphic, frame);
5953 if (Feld[xx][yy] == EL_FLAMES)
5954 Feld[xx][yy] = EL_EMPTY;
5955 DrawLevelField(xx, yy);
5960 if (MovDelay[x][y]) /* element still has to wait some time */
5962 PlayLevelSoundAction(x, y, ACTION_WAITING);
5968 /* now make next step */
5970 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5972 if (DONT_COLLIDE_WITH(element) &&
5973 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5974 !PLAYER_ENEMY_PROTECTED(newx, newy))
5976 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5981 else if (CAN_MOVE_INTO_ACID(element) &&
5982 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5983 !IS_MV_DIAGONAL(MovDir[x][y]) &&
5984 (MovDir[x][y] == MV_DOWN ||
5985 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5987 SplashAcid(newx, newy);
5988 Store[x][y] = EL_ACID;
5990 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5992 if (Feld[newx][newy] == EL_EXIT_OPEN)
5995 DrawLevelField(x, y);
5997 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5998 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5999 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6001 local_player->friends_still_needed--;
6002 if (!local_player->friends_still_needed &&
6003 !local_player->GameOver && AllPlayersGone)
6004 PlayerWins(local_player);
6008 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6010 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6011 DrawLevelField(newx, newy);
6013 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6015 else if (!IS_FREE(newx, newy))
6017 GfxAction[x][y] = ACTION_WAITING;
6019 if (IS_PLAYER(x, y))
6020 DrawPlayerField(x, y);
6022 DrawLevelField(x, y);
6027 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6029 if (IS_FOOD_PIG(Feld[newx][newy]))
6031 if (IS_MOVING(newx, newy))
6032 RemoveMovingField(newx, newy);
6035 Feld[newx][newy] = EL_EMPTY;
6036 DrawLevelField(newx, newy);
6039 PlayLevelSound(x, y, SND_PIG_DIGGING);
6041 else if (!IS_FREE(newx, newy))
6043 if (IS_PLAYER(x, y))
6044 DrawPlayerField(x, y);
6046 DrawLevelField(x, y);
6051 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6053 if (Store[x][y] != EL_EMPTY)
6055 boolean can_clone = FALSE;
6058 /* check if element to clone is still there */
6059 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6061 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6069 /* cannot clone or target field not free anymore -- do not clone */
6070 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6071 Store[x][y] = EL_EMPTY;
6074 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6076 if (IS_MV_DIAGONAL(MovDir[x][y]))
6078 int diagonal_move_dir = MovDir[x][y];
6079 int stored = Store[x][y];
6080 int change_delay = 8;
6083 /* android is moving diagonally */
6085 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6087 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6088 GfxElement[x][y] = EL_EMC_ANDROID;
6089 GfxAction[x][y] = ACTION_SHRINKING;
6090 GfxDir[x][y] = diagonal_move_dir;
6091 ChangeDelay[x][y] = change_delay;
6093 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6096 DrawLevelGraphicAnimation(x, y, graphic);
6097 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6099 if (Feld[newx][newy] == EL_ACID)
6101 SplashAcid(newx, newy);
6106 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6108 Store[newx][newy] = EL_EMC_ANDROID;
6109 GfxElement[newx][newy] = EL_EMC_ANDROID;
6110 GfxAction[newx][newy] = ACTION_GROWING;
6111 GfxDir[newx][newy] = diagonal_move_dir;
6112 ChangeDelay[newx][newy] = change_delay;
6114 graphic = el_act_dir2img(GfxElement[newx][newy],
6115 GfxAction[newx][newy], GfxDir[newx][newy]);
6117 DrawLevelGraphicAnimation(newx, newy, graphic);
6118 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6124 Feld[newx][newy] = EL_EMPTY;
6125 DrawLevelField(newx, newy);
6127 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6130 else if (!IS_FREE(newx, newy))
6133 if (IS_PLAYER(x, y))
6134 DrawPlayerField(x, y);
6136 DrawLevelField(x, y);
6142 else if (IS_CUSTOM_ELEMENT(element) &&
6143 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6145 int new_element = Feld[newx][newy];
6147 if (!IS_FREE(newx, newy))
6149 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6150 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6153 /* no element can dig solid indestructible elements */
6154 if (IS_INDESTRUCTIBLE(new_element) &&
6155 !IS_DIGGABLE(new_element) &&
6156 !IS_COLLECTIBLE(new_element))
6159 if (AmoebaNr[newx][newy] &&
6160 (new_element == EL_AMOEBA_FULL ||
6161 new_element == EL_BD_AMOEBA ||
6162 new_element == EL_AMOEBA_GROWING))
6164 AmoebaCnt[AmoebaNr[newx][newy]]--;
6165 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6168 if (IS_MOVING(newx, newy))
6169 RemoveMovingField(newx, newy);
6172 RemoveField(newx, newy);
6173 DrawLevelField(newx, newy);
6176 /* if digged element was about to explode, prevent the explosion */
6177 ExplodeField[newx][newy] = EX_TYPE_NONE;
6179 PlayLevelSoundAction(x, y, action);
6182 Store[newx][newy] = EL_EMPTY;
6184 /* this makes it possible to leave the removed element again */
6185 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6186 Store[newx][newy] = new_element;
6188 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6190 int move_leave_element = element_info[element].move_leave_element;
6192 /* this makes it possible to leave the removed element again */
6193 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6194 new_element : move_leave_element);
6198 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6200 RunnerVisit[x][y] = FrameCounter;
6201 PlayerVisit[x][y] /= 8; /* expire player visit path */
6204 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6206 if (!IS_FREE(newx, newy))
6208 if (IS_PLAYER(x, y))
6209 DrawPlayerField(x, y);
6211 DrawLevelField(x, y);
6217 boolean wanna_flame = !RND(10);
6218 int dx = newx - x, dy = newy - y;
6219 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6220 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6221 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6222 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6223 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6224 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6227 IS_CLASSIC_ENEMY(element1) ||
6228 IS_CLASSIC_ENEMY(element2)) &&
6229 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6230 element1 != EL_FLAMES && element2 != EL_FLAMES)
6232 ResetGfxAnimation(x, y);
6233 GfxAction[x][y] = ACTION_ATTACKING;
6235 if (IS_PLAYER(x, y))
6236 DrawPlayerField(x, y);
6238 DrawLevelField(x, y);
6240 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6242 MovDelay[x][y] = 50;
6246 RemoveField(newx, newy);
6248 Feld[newx][newy] = EL_FLAMES;
6249 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6252 RemoveField(newx1, newy1);
6254 Feld[newx1][newy1] = EL_FLAMES;
6256 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6259 RemoveField(newx2, newy2);
6261 Feld[newx2][newy2] = EL_FLAMES;
6268 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6269 Feld[newx][newy] == EL_DIAMOND)
6271 if (IS_MOVING(newx, newy))
6272 RemoveMovingField(newx, newy);
6275 Feld[newx][newy] = EL_EMPTY;
6276 DrawLevelField(newx, newy);
6279 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6281 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6282 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6284 if (AmoebaNr[newx][newy])
6286 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6287 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6288 Feld[newx][newy] == EL_BD_AMOEBA)
6289 AmoebaCnt[AmoebaNr[newx][newy]]--;
6294 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6296 RemoveMovingField(newx, newy);
6299 if (IS_MOVING(newx, newy))
6301 RemoveMovingField(newx, newy);
6306 Feld[newx][newy] = EL_EMPTY;
6307 DrawLevelField(newx, newy);
6310 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6312 else if ((element == EL_PACMAN || element == EL_MOLE)
6313 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6315 if (AmoebaNr[newx][newy])
6317 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6318 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6319 Feld[newx][newy] == EL_BD_AMOEBA)
6320 AmoebaCnt[AmoebaNr[newx][newy]]--;
6323 if (element == EL_MOLE)
6325 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6326 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6328 ResetGfxAnimation(x, y);
6329 GfxAction[x][y] = ACTION_DIGGING;
6330 DrawLevelField(x, y);
6332 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6334 return; /* wait for shrinking amoeba */
6336 else /* element == EL_PACMAN */
6338 Feld[newx][newy] = EL_EMPTY;
6339 DrawLevelField(newx, newy);
6340 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6343 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6344 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6345 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6347 /* wait for shrinking amoeba to completely disappear */
6350 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6352 /* object was running against a wall */
6357 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6358 if (move_pattern & MV_ANY_DIRECTION &&
6359 move_pattern == MovDir[x][y])
6361 int blocking_element =
6362 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6364 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6367 element = Feld[x][y]; /* element might have changed */
6371 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6372 DrawLevelElementAnimation(x, y, element);
6374 if (DONT_TOUCH(element))
6375 TestIfBadThingTouchesPlayer(x, y);
6380 InitMovingField(x, y, MovDir[x][y]);
6382 PlayLevelSoundAction(x, y, ACTION_MOVING);
6386 ContinueMoving(x, y);
6389 void ContinueMoving(int x, int y)
6391 int element = Feld[x][y];
6392 struct ElementInfo *ei = &element_info[element];
6393 int direction = MovDir[x][y];
6394 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6395 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6396 int newx = x + dx, newy = y + dy;
6397 int stored = Store[x][y];
6398 int stored_new = Store[newx][newy];
6399 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6400 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6401 boolean last_line = (newy == lev_fieldy - 1);
6403 MovPos[x][y] += getElementMoveStepsize(x, y);
6405 if (pushed_by_player) /* special case: moving object pushed by player */
6406 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6408 if (ABS(MovPos[x][y]) < TILEX)
6410 DrawLevelField(x, y);
6412 return; /* element is still moving */
6415 /* element reached destination field */
6417 Feld[x][y] = EL_EMPTY;
6418 Feld[newx][newy] = element;
6419 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6421 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6423 element = Feld[newx][newy] = EL_ACID;
6425 else if (element == EL_MOLE)
6427 Feld[x][y] = EL_SAND;
6429 DrawLevelFieldCrumbledSandNeighbours(x, y);
6431 else if (element == EL_QUICKSAND_FILLING)
6433 element = Feld[newx][newy] = get_next_element(element);
6434 Store[newx][newy] = Store[x][y];
6436 else if (element == EL_QUICKSAND_EMPTYING)
6438 Feld[x][y] = get_next_element(element);
6439 element = Feld[newx][newy] = Store[x][y];
6441 else if (element == EL_MAGIC_WALL_FILLING)
6443 element = Feld[newx][newy] = get_next_element(element);
6444 if (!game.magic_wall_active)
6445 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6446 Store[newx][newy] = Store[x][y];
6448 else if (element == EL_MAGIC_WALL_EMPTYING)
6450 Feld[x][y] = get_next_element(element);
6451 if (!game.magic_wall_active)
6452 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6453 element = Feld[newx][newy] = Store[x][y];
6455 #if USE_NEW_CUSTOM_VALUE
6456 InitField(newx, newy, FALSE);
6459 else if (element == EL_BD_MAGIC_WALL_FILLING)
6461 element = Feld[newx][newy] = get_next_element(element);
6462 if (!game.magic_wall_active)
6463 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6464 Store[newx][newy] = Store[x][y];
6466 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6468 Feld[x][y] = get_next_element(element);
6469 if (!game.magic_wall_active)
6470 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6471 element = Feld[newx][newy] = Store[x][y];
6473 #if USE_NEW_CUSTOM_VALUE
6474 InitField(newx, newy, FALSE);
6477 else if (element == EL_AMOEBA_DROPPING)
6479 Feld[x][y] = get_next_element(element);
6480 element = Feld[newx][newy] = Store[x][y];
6482 else if (element == EL_SOKOBAN_OBJECT)
6485 Feld[x][y] = Back[x][y];
6487 if (Back[newx][newy])
6488 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6490 Back[x][y] = Back[newx][newy] = 0;
6493 Store[x][y] = EL_EMPTY;
6498 MovDelay[newx][newy] = 0;
6500 if (CAN_CHANGE_OR_HAS_ACTION(element))
6502 /* copy element change control values to new field */
6503 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6504 ChangePage[newx][newy] = ChangePage[x][y];
6505 ChangeCount[newx][newy] = ChangeCount[x][y];
6506 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6509 #if USE_NEW_CUSTOM_VALUE
6510 CustomValue[newx][newy] = CustomValue[x][y];
6513 ChangeDelay[x][y] = 0;
6514 ChangePage[x][y] = -1;
6515 ChangeCount[x][y] = 0;
6516 ChangeEvent[x][y] = -1;
6518 #if USE_NEW_CUSTOM_VALUE
6519 CustomValue[x][y] = 0;
6522 /* copy animation control values to new field */
6523 GfxFrame[newx][newy] = GfxFrame[x][y];
6524 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6525 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6526 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6528 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6530 /* some elements can leave other elements behind after moving */
6532 if (ei->move_leave_element != EL_EMPTY &&
6533 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6534 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6536 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6537 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6538 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6541 int move_leave_element = ei->move_leave_element;
6545 /* this makes it possible to leave the removed element again */
6546 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6547 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6549 /* this makes it possible to leave the removed element again */
6550 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6551 move_leave_element = stored;
6554 /* this makes it possible to leave the removed element again */
6555 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6556 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6557 move_leave_element = stored;
6560 Feld[x][y] = move_leave_element;
6562 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6563 MovDir[x][y] = direction;
6565 InitField(x, y, FALSE);
6567 if (GFX_CRUMBLED(Feld[x][y]))
6568 DrawLevelFieldCrumbledSandNeighbours(x, y);
6570 if (ELEM_IS_PLAYER(move_leave_element))
6571 RelocatePlayer(x, y, move_leave_element);
6574 /* do this after checking for left-behind element */
6575 ResetGfxAnimation(x, y); /* reset animation values for old field */
6577 if (!CAN_MOVE(element) ||
6578 (CAN_FALL(element) && direction == MV_DOWN &&
6579 (element == EL_SPRING ||
6580 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6581 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6582 GfxDir[x][y] = MovDir[newx][newy] = 0;
6584 DrawLevelField(x, y);
6585 DrawLevelField(newx, newy);
6587 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6589 /* prevent pushed element from moving on in pushed direction */
6590 if (pushed_by_player && CAN_MOVE(element) &&
6591 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6592 !(element_info[element].move_pattern & direction))
6593 TurnRound(newx, newy);
6595 /* prevent elements on conveyor belt from moving on in last direction */
6596 if (pushed_by_conveyor && CAN_FALL(element) &&
6597 direction & MV_HORIZONTAL)
6598 MovDir[newx][newy] = 0;
6600 if (!pushed_by_player)
6602 int nextx = newx + dx, nexty = newy + dy;
6603 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6605 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6607 if (CAN_FALL(element) && direction == MV_DOWN)
6608 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6610 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6611 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6613 #if USE_FIX_IMPACT_COLLISION
6614 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
6615 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
6619 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6621 TestIfBadThingTouchesPlayer(newx, newy);
6622 TestIfBadThingTouchesFriend(newx, newy);
6624 if (!IS_CUSTOM_ELEMENT(element))
6625 TestIfBadThingTouchesOtherBadThing(newx, newy);
6627 else if (element == EL_PENGUIN)
6628 TestIfFriendTouchesBadThing(newx, newy);
6630 /* give the player one last chance (one more frame) to move away */
6631 if (CAN_FALL(element) && direction == MV_DOWN &&
6632 (last_line || (!IS_FREE(x, newy + 1) &&
6633 (!IS_PLAYER(x, newy + 1) ||
6634 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6637 if (pushed_by_player && !game.use_change_when_pushing_bug)
6639 int push_side = MV_DIR_OPPOSITE(direction);
6640 struct PlayerInfo *player = PLAYERINFO(x, y);
6642 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6643 player->index_bit, push_side);
6644 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6645 player->index_bit, push_side);
6648 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6649 MovDelay[newx][newy] = 1;
6651 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6653 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6656 if (ChangePage[newx][newy] != -1) /* delayed change */
6658 int page = ChangePage[newx][newy];
6659 struct ElementChangeInfo *change = &ei->change_page[page];
6661 ChangePage[newx][newy] = -1;
6663 if (change->can_change)
6665 if (ChangeElement(newx, newy, element, page))
6667 if (change->post_change_function)
6668 change->post_change_function(newx, newy);
6672 if (change->has_action)
6673 ExecuteCustomElementAction(newx, newy, element, page);
6677 TestIfElementHitsCustomElement(newx, newy, direction);
6678 TestIfPlayerTouchesCustomElement(newx, newy);
6679 TestIfElementTouchesCustomElement(newx, newy);
6681 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6682 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6683 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6684 MV_DIR_OPPOSITE(direction));
6687 int AmoebeNachbarNr(int ax, int ay)
6690 int element = Feld[ax][ay];
6692 static int xy[4][2] =
6700 for (i = 0; i < NUM_DIRECTIONS; i++)
6702 int x = ax + xy[i][0];
6703 int y = ay + xy[i][1];
6705 if (!IN_LEV_FIELD(x, y))
6708 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6709 group_nr = AmoebaNr[x][y];
6715 void AmoebenVereinigen(int ax, int ay)
6717 int i, x, y, xx, yy;
6718 int new_group_nr = AmoebaNr[ax][ay];
6719 static int xy[4][2] =
6727 if (new_group_nr == 0)
6730 for (i = 0; i < NUM_DIRECTIONS; i++)
6735 if (!IN_LEV_FIELD(x, y))
6738 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6739 Feld[x][y] == EL_BD_AMOEBA ||
6740 Feld[x][y] == EL_AMOEBA_DEAD) &&
6741 AmoebaNr[x][y] != new_group_nr)
6743 int old_group_nr = AmoebaNr[x][y];
6745 if (old_group_nr == 0)
6748 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6749 AmoebaCnt[old_group_nr] = 0;
6750 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6751 AmoebaCnt2[old_group_nr] = 0;
6753 SCAN_PLAYFIELD(xx, yy)
6755 if (AmoebaNr[xx][yy] == old_group_nr)
6756 AmoebaNr[xx][yy] = new_group_nr;
6762 void AmoebeUmwandeln(int ax, int ay)
6766 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6768 int group_nr = AmoebaNr[ax][ay];
6773 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6774 printf("AmoebeUmwandeln(): This should never happen!\n");
6779 SCAN_PLAYFIELD(x, y)
6781 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6784 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6788 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6789 SND_AMOEBA_TURNING_TO_GEM :
6790 SND_AMOEBA_TURNING_TO_ROCK));
6795 static int xy[4][2] =
6803 for (i = 0; i < NUM_DIRECTIONS; i++)
6808 if (!IN_LEV_FIELD(x, y))
6811 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6813 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6814 SND_AMOEBA_TURNING_TO_GEM :
6815 SND_AMOEBA_TURNING_TO_ROCK));
6822 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6825 int group_nr = AmoebaNr[ax][ay];
6826 boolean done = FALSE;
6831 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6832 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6837 SCAN_PLAYFIELD(x, y)
6839 if (AmoebaNr[x][y] == group_nr &&
6840 (Feld[x][y] == EL_AMOEBA_DEAD ||
6841 Feld[x][y] == EL_BD_AMOEBA ||
6842 Feld[x][y] == EL_AMOEBA_GROWING))
6845 Feld[x][y] = new_element;
6846 InitField(x, y, FALSE);
6847 DrawLevelField(x, y);
6853 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6854 SND_BD_AMOEBA_TURNING_TO_ROCK :
6855 SND_BD_AMOEBA_TURNING_TO_GEM));
6858 void AmoebeWaechst(int x, int y)
6860 static unsigned long sound_delay = 0;
6861 static unsigned long sound_delay_value = 0;
6863 if (!MovDelay[x][y]) /* start new growing cycle */
6867 if (DelayReached(&sound_delay, sound_delay_value))
6869 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6870 sound_delay_value = 30;
6874 if (MovDelay[x][y]) /* wait some time before growing bigger */
6877 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6879 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6880 6 - MovDelay[x][y]);
6882 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6885 if (!MovDelay[x][y])
6887 Feld[x][y] = Store[x][y];
6889 DrawLevelField(x, y);
6894 void AmoebaDisappearing(int x, int y)
6896 static unsigned long sound_delay = 0;
6897 static unsigned long sound_delay_value = 0;
6899 if (!MovDelay[x][y]) /* start new shrinking cycle */
6903 if (DelayReached(&sound_delay, sound_delay_value))
6904 sound_delay_value = 30;
6907 if (MovDelay[x][y]) /* wait some time before shrinking */
6910 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6912 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6913 6 - MovDelay[x][y]);
6915 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6918 if (!MovDelay[x][y])
6920 Feld[x][y] = EL_EMPTY;
6921 DrawLevelField(x, y);
6923 /* don't let mole enter this field in this cycle;
6924 (give priority to objects falling to this field from above) */
6930 void AmoebeAbleger(int ax, int ay)
6933 int element = Feld[ax][ay];
6934 int graphic = el2img(element);
6935 int newax = ax, neway = ay;
6936 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6937 static int xy[4][2] =
6945 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6947 Feld[ax][ay] = EL_AMOEBA_DEAD;
6948 DrawLevelField(ax, ay);
6952 if (IS_ANIMATED(graphic))
6953 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6955 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6956 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6958 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6961 if (MovDelay[ax][ay])
6965 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6968 int x = ax + xy[start][0];
6969 int y = ay + xy[start][1];
6971 if (!IN_LEV_FIELD(x, y))
6974 if (IS_FREE(x, y) ||
6975 CAN_GROW_INTO(Feld[x][y]) ||
6976 Feld[x][y] == EL_QUICKSAND_EMPTY)
6982 if (newax == ax && neway == ay)
6985 else /* normal or "filled" (BD style) amoeba */
6988 boolean waiting_for_player = FALSE;
6990 for (i = 0; i < NUM_DIRECTIONS; i++)
6992 int j = (start + i) % 4;
6993 int x = ax + xy[j][0];
6994 int y = ay + xy[j][1];
6996 if (!IN_LEV_FIELD(x, y))
6999 if (IS_FREE(x, y) ||
7000 CAN_GROW_INTO(Feld[x][y]) ||
7001 Feld[x][y] == EL_QUICKSAND_EMPTY)
7007 else if (IS_PLAYER(x, y))
7008 waiting_for_player = TRUE;
7011 if (newax == ax && neway == ay) /* amoeba cannot grow */
7013 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7015 Feld[ax][ay] = EL_AMOEBA_DEAD;
7016 DrawLevelField(ax, ay);
7017 AmoebaCnt[AmoebaNr[ax][ay]]--;
7019 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7021 if (element == EL_AMOEBA_FULL)
7022 AmoebeUmwandeln(ax, ay);
7023 else if (element == EL_BD_AMOEBA)
7024 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7029 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7031 /* amoeba gets larger by growing in some direction */
7033 int new_group_nr = AmoebaNr[ax][ay];
7036 if (new_group_nr == 0)
7038 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7039 printf("AmoebeAbleger(): This should never happen!\n");
7044 AmoebaNr[newax][neway] = new_group_nr;
7045 AmoebaCnt[new_group_nr]++;
7046 AmoebaCnt2[new_group_nr]++;
7048 /* if amoeba touches other amoeba(s) after growing, unify them */
7049 AmoebenVereinigen(newax, neway);
7051 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7053 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7059 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7060 (neway == lev_fieldy - 1 && newax != ax))
7062 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7063 Store[newax][neway] = element;
7065 else if (neway == ay || element == EL_EMC_DRIPPER)
7067 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7069 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7073 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7074 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7075 Store[ax][ay] = EL_AMOEBA_DROP;
7076 ContinueMoving(ax, ay);
7080 DrawLevelField(newax, neway);
7083 void Life(int ax, int ay)
7087 int element = Feld[ax][ay];
7088 int graphic = el2img(element);
7089 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7091 boolean changed = FALSE;
7093 if (IS_ANIMATED(graphic))
7094 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7099 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7100 MovDelay[ax][ay] = life_time;
7102 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7105 if (MovDelay[ax][ay])
7109 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7111 int xx = ax+x1, yy = ay+y1;
7114 if (!IN_LEV_FIELD(xx, yy))
7117 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7119 int x = xx+x2, y = yy+y2;
7121 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7124 if (((Feld[x][y] == element ||
7125 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7127 (IS_FREE(x, y) && Stop[x][y]))
7131 if (xx == ax && yy == ay) /* field in the middle */
7133 if (nachbarn < life_parameter[0] ||
7134 nachbarn > life_parameter[1])
7136 Feld[xx][yy] = EL_EMPTY;
7138 DrawLevelField(xx, yy);
7139 Stop[xx][yy] = TRUE;
7143 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7144 { /* free border field */
7145 if (nachbarn >= life_parameter[2] &&
7146 nachbarn <= life_parameter[3])
7148 Feld[xx][yy] = element;
7149 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7151 DrawLevelField(xx, yy);
7152 Stop[xx][yy] = TRUE;
7159 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7160 SND_GAME_OF_LIFE_GROWING);
7163 static void InitRobotWheel(int x, int y)
7165 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7168 static void RunRobotWheel(int x, int y)
7170 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7173 static void StopRobotWheel(int x, int y)
7175 if (ZX == x && ZY == y)
7179 static void InitTimegateWheel(int x, int y)
7181 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7184 static void RunTimegateWheel(int x, int y)
7186 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7189 static void InitMagicBallDelay(int x, int y)
7192 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7194 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7198 static void ActivateMagicBall(int bx, int by)
7202 if (level.ball_random)
7204 int pos_border = RND(8); /* select one of the eight border elements */
7205 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7206 int xx = pos_content % 3;
7207 int yy = pos_content / 3;
7212 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7213 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7217 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7219 int xx = x - bx + 1;
7220 int yy = y - by + 1;
7222 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7223 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7227 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7230 void CheckExit(int x, int y)
7232 if (local_player->gems_still_needed > 0 ||
7233 local_player->sokobanfields_still_needed > 0 ||
7234 local_player->lights_still_needed > 0)
7236 int element = Feld[x][y];
7237 int graphic = el2img(element);
7239 if (IS_ANIMATED(graphic))
7240 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7245 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7248 Feld[x][y] = EL_EXIT_OPENING;
7250 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7253 void CheckExitSP(int x, int y)
7255 if (local_player->gems_still_needed > 0)
7257 int element = Feld[x][y];
7258 int graphic = el2img(element);
7260 if (IS_ANIMATED(graphic))
7261 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7266 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7269 Feld[x][y] = EL_SP_EXIT_OPENING;
7271 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7274 static void CloseAllOpenTimegates()
7278 SCAN_PLAYFIELD(x, y)
7280 int element = Feld[x][y];
7282 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7284 Feld[x][y] = EL_TIMEGATE_CLOSING;
7286 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7291 void DrawTwinkleOnField(int x, int y)
7293 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7296 if (Feld[x][y] == EL_BD_DIAMOND)
7299 if (MovDelay[x][y] == 0) /* next animation frame */
7300 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7302 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7306 if (setup.direct_draw && MovDelay[x][y])
7307 SetDrawtoField(DRAW_BUFFERED);
7309 DrawLevelElementAnimation(x, y, Feld[x][y]);
7311 if (MovDelay[x][y] != 0)
7313 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7314 10 - MovDelay[x][y]);
7316 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7318 if (setup.direct_draw)
7322 dest_x = FX + SCREENX(x) * TILEX;
7323 dest_y = FY + SCREENY(y) * TILEY;
7325 BlitBitmap(drawto_field, window,
7326 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7327 SetDrawtoField(DRAW_DIRECT);
7333 void MauerWaechst(int x, int y)
7337 if (!MovDelay[x][y]) /* next animation frame */
7338 MovDelay[x][y] = 3 * delay;
7340 if (MovDelay[x][y]) /* wait some time before next frame */
7344 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7346 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7347 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7349 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7352 if (!MovDelay[x][y])
7354 if (MovDir[x][y] == MV_LEFT)
7356 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7357 DrawLevelField(x - 1, y);
7359 else if (MovDir[x][y] == MV_RIGHT)
7361 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7362 DrawLevelField(x + 1, y);
7364 else if (MovDir[x][y] == MV_UP)
7366 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7367 DrawLevelField(x, y - 1);
7371 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7372 DrawLevelField(x, y + 1);
7375 Feld[x][y] = Store[x][y];
7377 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7378 DrawLevelField(x, y);
7383 void MauerAbleger(int ax, int ay)
7385 int element = Feld[ax][ay];
7386 int graphic = el2img(element);
7387 boolean oben_frei = FALSE, unten_frei = FALSE;
7388 boolean links_frei = FALSE, rechts_frei = FALSE;
7389 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7390 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7391 boolean new_wall = FALSE;
7393 if (IS_ANIMATED(graphic))
7394 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7396 if (!MovDelay[ax][ay]) /* start building new wall */
7397 MovDelay[ax][ay] = 6;
7399 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7402 if (MovDelay[ax][ay])
7406 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7408 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7410 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7412 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7415 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7416 element == EL_EXPANDABLE_WALL_ANY)
7420 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7421 Store[ax][ay-1] = element;
7422 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7423 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7424 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7425 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7430 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7431 Store[ax][ay+1] = element;
7432 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7433 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7434 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7435 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7440 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7441 element == EL_EXPANDABLE_WALL_ANY ||
7442 element == EL_EXPANDABLE_WALL ||
7443 element == EL_BD_EXPANDABLE_WALL)
7447 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7448 Store[ax-1][ay] = element;
7449 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7450 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7451 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7452 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7458 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7459 Store[ax+1][ay] = element;
7460 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7461 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7462 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7463 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7468 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7469 DrawLevelField(ax, ay);
7471 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7473 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7474 unten_massiv = TRUE;
7475 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7476 links_massiv = TRUE;
7477 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7478 rechts_massiv = TRUE;
7480 if (((oben_massiv && unten_massiv) ||
7481 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7482 element == EL_EXPANDABLE_WALL) &&
7483 ((links_massiv && rechts_massiv) ||
7484 element == EL_EXPANDABLE_WALL_VERTICAL))
7485 Feld[ax][ay] = EL_WALL;
7488 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7491 void CheckForDragon(int x, int y)
7494 boolean dragon_found = FALSE;
7495 static int xy[4][2] =
7503 for (i = 0; i < NUM_DIRECTIONS; i++)
7505 for (j = 0; j < 4; j++)
7507 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7509 if (IN_LEV_FIELD(xx, yy) &&
7510 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7512 if (Feld[xx][yy] == EL_DRAGON)
7513 dragon_found = TRUE;
7522 for (i = 0; i < NUM_DIRECTIONS; i++)
7524 for (j = 0; j < 3; j++)
7526 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7528 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7530 Feld[xx][yy] = EL_EMPTY;
7531 DrawLevelField(xx, yy);
7540 static void InitBuggyBase(int x, int y)
7542 int element = Feld[x][y];
7543 int activating_delay = FRAMES_PER_SECOND / 4;
7546 (element == EL_SP_BUGGY_BASE ?
7547 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7548 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7550 element == EL_SP_BUGGY_BASE_ACTIVE ?
7551 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7554 static void WarnBuggyBase(int x, int y)
7557 static int xy[4][2] =
7565 for (i = 0; i < NUM_DIRECTIONS; i++)
7567 int xx = x + xy[i][0];
7568 int yy = y + xy[i][1];
7570 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7572 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7579 static void InitTrap(int x, int y)
7581 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7584 static void ActivateTrap(int x, int y)
7586 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7589 static void ChangeActiveTrap(int x, int y)
7591 int graphic = IMG_TRAP_ACTIVE;
7593 /* if new animation frame was drawn, correct crumbled sand border */
7594 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7595 DrawLevelFieldCrumbledSand(x, y);
7598 static int getSpecialActionElement(int element, int number, int base_element)
7600 return (element != EL_EMPTY ? element :
7601 number != -1 ? base_element + number - 1 :
7605 static int getModifiedActionNumber(int value_old, int operator, int operand,
7606 int value_min, int value_max)
7608 int value_new = (operator == CA_MODE_SET ? operand :
7609 operator == CA_MODE_ADD ? value_old + operand :
7610 operator == CA_MODE_SUBTRACT ? value_old - operand :
7611 operator == CA_MODE_MULTIPLY ? value_old * operand :
7612 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7613 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7616 return (value_new < value_min ? value_min :
7617 value_new > value_max ? value_max :
7621 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7623 struct ElementInfo *ei = &element_info[element];
7624 struct ElementChangeInfo *change = &ei->change_page[page];
7625 int target_element = change->target_element;
7626 int action_type = change->action_type;
7627 int action_mode = change->action_mode;
7628 int action_arg = change->action_arg;
7631 if (!change->has_action)
7634 /* ---------- determine action paramater values -------------------------- */
7636 int level_time_value =
7637 (level.time > 0 ? TimeLeft :
7640 int action_arg_element =
7641 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7642 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7643 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7646 int action_arg_direction =
7647 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7648 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7649 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7650 change->actual_trigger_side :
7651 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7652 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7655 int action_arg_number_min =
7656 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7659 int action_arg_number_max =
7660 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7661 action_type == CA_SET_LEVEL_GEMS ? 999 :
7662 action_type == CA_SET_LEVEL_TIME ? 9999 :
7663 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7664 action_type == CA_SET_CE_VALUE ? 9999 :
7665 action_type == CA_SET_CE_SCORE ? 9999 :
7668 int action_arg_number_reset =
7669 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
7670 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7671 action_type == CA_SET_LEVEL_TIME ? level.time :
7672 action_type == CA_SET_LEVEL_SCORE ? 0 :
7673 #if USE_NEW_CUSTOM_VALUE
7674 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
7676 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7678 action_type == CA_SET_CE_SCORE ? 0 :
7681 int action_arg_number =
7682 (action_arg <= CA_ARG_MAX ? action_arg :
7683 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7684 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7685 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7686 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7687 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7688 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7689 #if USE_NEW_CUSTOM_VALUE
7690 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7692 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7694 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7695 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7696 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7697 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7698 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7699 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
7700 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7701 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
7702 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
7703 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7704 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7707 int action_arg_number_old =
7708 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7709 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7710 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7711 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7712 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7715 int action_arg_number_new =
7716 getModifiedActionNumber(action_arg_number_old,
7717 action_mode, action_arg_number,
7718 action_arg_number_min, action_arg_number_max);
7720 int trigger_player_bits =
7721 (change->actual_trigger_player >= EL_PLAYER_1 &&
7722 change->actual_trigger_player <= EL_PLAYER_4 ?
7723 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7726 int action_arg_player_bits =
7727 (action_arg >= CA_ARG_PLAYER_1 &&
7728 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7729 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7732 /* ---------- execute action -------------------------------------------- */
7734 switch (action_type)
7741 /* ---------- level actions ------------------------------------------- */
7743 case CA_RESTART_LEVEL:
7745 game.restart_level = TRUE;
7750 case CA_SHOW_ENVELOPE:
7752 int element = getSpecialActionElement(action_arg_element,
7753 action_arg_number, EL_ENVELOPE_1);
7755 if (IS_ENVELOPE(element))
7756 local_player->show_envelope = element;
7761 case CA_SET_LEVEL_TIME:
7763 if (level.time > 0) /* only modify limited time value */
7765 TimeLeft = action_arg_number_new;
7767 DrawGameValue_Time(TimeLeft);
7769 if (!TimeLeft && setup.time_limit)
7770 for (i = 0; i < MAX_PLAYERS; i++)
7771 KillPlayer(&stored_player[i]);
7777 case CA_SET_LEVEL_SCORE:
7779 local_player->score = action_arg_number_new;
7781 DrawGameValue_Score(local_player->score);
7786 case CA_SET_LEVEL_GEMS:
7788 local_player->gems_still_needed = action_arg_number_new;
7790 DrawGameValue_Emeralds(local_player->gems_still_needed);
7795 #if !USE_PLAYER_GRAVITY
7796 case CA_SET_LEVEL_GRAVITY:
7798 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7799 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7800 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7806 case CA_SET_LEVEL_WIND:
7808 game.wind_direction = action_arg_direction;
7813 /* ---------- player actions ------------------------------------------ */
7815 case CA_MOVE_PLAYER:
7817 /* automatically move to the next field in specified direction */
7818 for (i = 0; i < MAX_PLAYERS; i++)
7819 if (trigger_player_bits & (1 << i))
7820 stored_player[i].programmed_action = action_arg_direction;
7825 case CA_EXIT_PLAYER:
7827 for (i = 0; i < MAX_PLAYERS; i++)
7828 if (action_arg_player_bits & (1 << i))
7829 PlayerWins(&stored_player[i]);
7834 case CA_KILL_PLAYER:
7836 for (i = 0; i < MAX_PLAYERS; i++)
7837 if (action_arg_player_bits & (1 << i))
7838 KillPlayer(&stored_player[i]);
7843 case CA_SET_PLAYER_KEYS:
7845 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7846 int element = getSpecialActionElement(action_arg_element,
7847 action_arg_number, EL_KEY_1);
7849 if (IS_KEY(element))
7851 for (i = 0; i < MAX_PLAYERS; i++)
7853 if (trigger_player_bits & (1 << i))
7855 stored_player[i].key[KEY_NR(element)] = key_state;
7857 DrawGameDoorValues();
7865 case CA_SET_PLAYER_SPEED:
7867 for (i = 0; i < MAX_PLAYERS; i++)
7869 if (trigger_player_bits & (1 << i))
7871 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7873 if (action_arg == CA_ARG_SPEED_FASTER &&
7874 stored_player[i].cannot_move)
7876 action_arg_number = STEPSIZE_VERY_SLOW;
7878 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7879 action_arg == CA_ARG_SPEED_FASTER)
7881 action_arg_number = 2;
7882 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7885 else if (action_arg == CA_ARG_NUMBER_RESET)
7887 action_arg_number = level.initial_player_stepsize[i];
7891 getModifiedActionNumber(move_stepsize,
7894 action_arg_number_min,
7895 action_arg_number_max);
7897 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7904 case CA_SET_PLAYER_SHIELD:
7906 for (i = 0; i < MAX_PLAYERS; i++)
7908 if (trigger_player_bits & (1 << i))
7910 if (action_arg == CA_ARG_SHIELD_OFF)
7912 stored_player[i].shield_normal_time_left = 0;
7913 stored_player[i].shield_deadly_time_left = 0;
7915 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7917 stored_player[i].shield_normal_time_left = 999999;
7919 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7921 stored_player[i].shield_normal_time_left = 999999;
7922 stored_player[i].shield_deadly_time_left = 999999;
7930 #if USE_PLAYER_GRAVITY
7931 case CA_SET_PLAYER_GRAVITY:
7933 for (i = 0; i < MAX_PLAYERS; i++)
7935 if (trigger_player_bits & (1 << i))
7937 stored_player[i].gravity =
7938 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7939 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7940 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
7941 stored_player[i].gravity);
7949 case CA_SET_PLAYER_ARTWORK:
7951 for (i = 0; i < MAX_PLAYERS; i++)
7953 if (trigger_player_bits & (1 << i))
7955 int artwork_element = action_arg_element;
7957 if (action_arg == CA_ARG_ELEMENT_RESET)
7959 (level.use_artwork_element[i] ? level.artwork_element[i] :
7960 stored_player[i].element_nr);
7962 #if USE_GFX_RESET_PLAYER_ARTWORK
7963 if (stored_player[i].artwork_element != artwork_element)
7964 stored_player[i].Frame = 0;
7967 stored_player[i].artwork_element = artwork_element;
7969 SetPlayerWaiting(&stored_player[i], FALSE);
7971 /* set number of special actions for bored and sleeping animation */
7972 stored_player[i].num_special_action_bored =
7973 get_num_special_action(artwork_element,
7974 ACTION_BORING_1, ACTION_BORING_LAST);
7975 stored_player[i].num_special_action_sleeping =
7976 get_num_special_action(artwork_element,
7977 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7984 /* ---------- CE actions ---------------------------------------------- */
7986 case CA_SET_CE_VALUE:
7988 #if USE_NEW_CUSTOM_VALUE
7989 int last_ce_value = CustomValue[x][y];
7991 CustomValue[x][y] = action_arg_number_new;
7993 if (CustomValue[x][y] != last_ce_value)
7995 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
7996 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
7998 if (CustomValue[x][y] == 0)
8000 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8001 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8009 case CA_SET_CE_SCORE:
8011 #if USE_NEW_CUSTOM_VALUE
8012 int last_ce_score = ei->collect_score;
8014 ei->collect_score = action_arg_number_new;
8016 if (ei->collect_score != last_ce_score)
8018 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8019 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8021 if (ei->collect_score == 0)
8025 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8026 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8029 This is a very special case that seems to be a mixture between
8030 CheckElementChange() and CheckTriggeredElementChange(): while
8031 the first one only affects single elements that are triggered
8032 directly, the second one affects multiple elements in the playfield
8033 that are triggered indirectly by another element. This is a third
8034 case: Changing the CE score always affects multiple identical CEs,
8035 so every affected CE must be checked, not only the single CE for
8036 which the CE score was changed in the first place (as every instance
8037 of that CE shares the same CE score, and therefore also can change)!
8039 SCAN_PLAYFIELD(xx, yy)
8041 if (Feld[xx][yy] == element)
8042 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8043 CE_SCORE_GETS_ZERO);
8052 /* ---------- engine actions ------------------------------------------ */
8054 case CA_SET_ENGINE_SCAN_MODE:
8056 InitPlayfieldScanMode(action_arg);
8066 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8068 int old_element = Feld[x][y];
8069 int new_element = get_element_from_group_element(element);
8070 int previous_move_direction = MovDir[x][y];
8071 #if USE_NEW_CUSTOM_VALUE
8072 int last_ce_value = CustomValue[x][y];
8074 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8075 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8076 boolean add_player_onto_element = (new_element_is_player &&
8077 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8078 /* this breaks SnakeBite when a snake is
8079 halfway through a door that closes */
8080 /* NOW FIXED AT LEVEL INIT IN files.c */
8081 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8083 IS_WALKABLE(old_element));
8086 /* check if element under the player changes from accessible to unaccessible
8087 (needed for special case of dropping element which then changes) */
8088 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8089 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8097 if (!add_player_onto_element)
8099 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8100 RemoveMovingField(x, y);
8104 Feld[x][y] = new_element;
8106 #if !USE_GFX_RESET_GFX_ANIMATION
8107 ResetGfxAnimation(x, y);
8108 ResetRandomAnimationValue(x, y);
8111 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8112 MovDir[x][y] = previous_move_direction;
8114 #if USE_NEW_CUSTOM_VALUE
8115 if (element_info[new_element].use_last_ce_value)
8116 CustomValue[x][y] = last_ce_value;
8119 InitField_WithBug1(x, y, FALSE);
8121 new_element = Feld[x][y]; /* element may have changed */
8123 #if USE_GFX_RESET_GFX_ANIMATION
8124 ResetGfxAnimation(x, y);
8125 ResetRandomAnimationValue(x, y);
8128 DrawLevelField(x, y);
8130 if (GFX_CRUMBLED(new_element))
8131 DrawLevelFieldCrumbledSandNeighbours(x, y);
8135 /* check if element under the player changes from accessible to unaccessible
8136 (needed for special case of dropping element which then changes) */
8137 /* (must be checked after creating new element for walkable group elements) */
8138 #if USE_FIX_KILLED_BY_NON_WALKABLE
8139 if (IS_PLAYER(x, y) && !player_explosion_protected &&
8140 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8147 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8148 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8157 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8158 if (new_element_is_player)
8159 RelocatePlayer(x, y, new_element);
8162 ChangeCount[x][y]++; /* count number of changes in the same frame */
8164 TestIfBadThingTouchesPlayer(x, y);
8165 TestIfPlayerTouchesCustomElement(x, y);
8166 TestIfElementTouchesCustomElement(x, y);
8169 static void CreateField(int x, int y, int element)
8171 CreateFieldExt(x, y, element, FALSE);
8174 static void CreateElementFromChange(int x, int y, int element)
8176 element = GET_VALID_RUNTIME_ELEMENT(element);
8178 #if USE_STOP_CHANGED_ELEMENTS
8179 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8181 int old_element = Feld[x][y];
8183 /* prevent changed element from moving in same engine frame
8184 unless both old and new element can either fall or move */
8185 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8186 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8191 CreateFieldExt(x, y, element, TRUE);
8194 static boolean ChangeElement(int x, int y, int element, int page)
8196 struct ElementInfo *ei = &element_info[element];
8197 struct ElementChangeInfo *change = &ei->change_page[page];
8198 int ce_value = CustomValue[x][y];
8199 int ce_score = ei->collect_score;
8201 int old_element = Feld[x][y];
8203 /* always use default change event to prevent running into a loop */
8204 if (ChangeEvent[x][y] == -1)
8205 ChangeEvent[x][y] = CE_DELAY;
8207 if (ChangeEvent[x][y] == CE_DELAY)
8209 /* reset actual trigger element, trigger player and action element */
8210 change->actual_trigger_element = EL_EMPTY;
8211 change->actual_trigger_player = EL_PLAYER_1;
8212 change->actual_trigger_side = CH_SIDE_NONE;
8213 change->actual_trigger_ce_value = 0;
8214 change->actual_trigger_ce_score = 0;
8217 /* do not change elements more than a specified maximum number of changes */
8218 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8221 ChangeCount[x][y]++; /* count number of changes in the same frame */
8223 if (change->explode)
8230 if (change->use_target_content)
8232 boolean complete_replace = TRUE;
8233 boolean can_replace[3][3];
8236 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8239 boolean is_walkable;
8240 boolean is_diggable;
8241 boolean is_collectible;
8242 boolean is_removable;
8243 boolean is_destructible;
8244 int ex = x + xx - 1;
8245 int ey = y + yy - 1;
8246 int content_element = change->target_content.e[xx][yy];
8249 can_replace[xx][yy] = TRUE;
8251 if (ex == x && ey == y) /* do not check changing element itself */
8254 if (content_element == EL_EMPTY_SPACE)
8256 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8261 if (!IN_LEV_FIELD(ex, ey))
8263 can_replace[xx][yy] = FALSE;
8264 complete_replace = FALSE;
8271 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8272 e = MovingOrBlocked2Element(ex, ey);
8274 is_empty = (IS_FREE(ex, ey) ||
8275 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8277 is_walkable = (is_empty || IS_WALKABLE(e));
8278 is_diggable = (is_empty || IS_DIGGABLE(e));
8279 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8280 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8281 is_removable = (is_diggable || is_collectible);
8283 can_replace[xx][yy] =
8284 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8285 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8286 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8287 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8288 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8289 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8290 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8292 if (!can_replace[xx][yy])
8293 complete_replace = FALSE;
8296 if (!change->only_if_complete || complete_replace)
8298 boolean something_has_changed = FALSE;
8300 if (change->only_if_complete && change->use_random_replace &&
8301 RND(100) < change->random_percentage)
8304 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8306 int ex = x + xx - 1;
8307 int ey = y + yy - 1;
8308 int content_element;
8310 if (can_replace[xx][yy] && (!change->use_random_replace ||
8311 RND(100) < change->random_percentage))
8313 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8314 RemoveMovingField(ex, ey);
8316 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8318 content_element = change->target_content.e[xx][yy];
8319 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8320 ce_value, ce_score);
8322 CreateElementFromChange(ex, ey, target_element);
8324 something_has_changed = TRUE;
8326 /* for symmetry reasons, freeze newly created border elements */
8327 if (ex != x || ey != y)
8328 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8332 if (something_has_changed)
8334 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8335 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8341 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
8342 ce_value, ce_score);
8344 if (element == EL_DIAGONAL_GROWING ||
8345 element == EL_DIAGONAL_SHRINKING)
8347 target_element = Store[x][y];
8349 Store[x][y] = EL_EMPTY;
8352 CreateElementFromChange(x, y, target_element);
8354 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8355 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8358 /* this uses direct change before indirect change */
8359 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8364 #if USE_NEW_DELAYED_ACTION
8366 static void HandleElementChange(int x, int y, int page)
8368 int element = MovingOrBlocked2Element(x, y);
8369 struct ElementInfo *ei = &element_info[element];
8370 struct ElementChangeInfo *change = &ei->change_page[page];
8373 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8374 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8377 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8378 x, y, element, element_info[element].token_name);
8379 printf("HandleElementChange(): This should never happen!\n");
8384 /* this can happen with classic bombs on walkable, changing elements */
8385 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8388 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8389 ChangeDelay[x][y] = 0;
8395 if (ChangeDelay[x][y] == 0) /* initialize element change */
8397 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8399 if (change->can_change)
8401 ResetGfxAnimation(x, y);
8402 ResetRandomAnimationValue(x, y);
8404 if (change->pre_change_function)
8405 change->pre_change_function(x, y);
8409 ChangeDelay[x][y]--;
8411 if (ChangeDelay[x][y] != 0) /* continue element change */
8413 if (change->can_change)
8415 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8417 if (IS_ANIMATED(graphic))
8418 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8420 if (change->change_function)
8421 change->change_function(x, y);
8424 else /* finish element change */
8426 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8428 page = ChangePage[x][y];
8429 ChangePage[x][y] = -1;
8431 change = &ei->change_page[page];
8434 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8436 ChangeDelay[x][y] = 1; /* try change after next move step */
8437 ChangePage[x][y] = page; /* remember page to use for change */
8442 if (change->can_change)
8444 if (ChangeElement(x, y, element, page))
8446 if (change->post_change_function)
8447 change->post_change_function(x, y);
8451 if (change->has_action)
8452 ExecuteCustomElementAction(x, y, element, page);
8458 static void HandleElementChange(int x, int y, int page)
8460 int element = MovingOrBlocked2Element(x, y);
8461 struct ElementInfo *ei = &element_info[element];
8462 struct ElementChangeInfo *change = &ei->change_page[page];
8465 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8468 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8469 x, y, element, element_info[element].token_name);
8470 printf("HandleElementChange(): This should never happen!\n");
8475 /* this can happen with classic bombs on walkable, changing elements */
8476 if (!CAN_CHANGE(element))
8479 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8480 ChangeDelay[x][y] = 0;
8486 if (ChangeDelay[x][y] == 0) /* initialize element change */
8488 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8490 ResetGfxAnimation(x, y);
8491 ResetRandomAnimationValue(x, y);
8493 if (change->pre_change_function)
8494 change->pre_change_function(x, y);
8497 ChangeDelay[x][y]--;
8499 if (ChangeDelay[x][y] != 0) /* continue element change */
8501 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8503 if (IS_ANIMATED(graphic))
8504 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8506 if (change->change_function)
8507 change->change_function(x, y);
8509 else /* finish element change */
8511 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8513 page = ChangePage[x][y];
8514 ChangePage[x][y] = -1;
8516 change = &ei->change_page[page];
8519 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8521 ChangeDelay[x][y] = 1; /* try change after next move step */
8522 ChangePage[x][y] = page; /* remember page to use for change */
8527 if (ChangeElement(x, y, element, page))
8529 if (change->post_change_function)
8530 change->post_change_function(x, y);
8537 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8538 int trigger_element,
8544 boolean change_done_any = FALSE;
8545 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8548 if (!(trigger_events[trigger_element][trigger_event]))
8551 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8553 int element = EL_CUSTOM_START + i;
8554 boolean change_done = FALSE;
8557 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8558 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8561 for (p = 0; p < element_info[element].num_change_pages; p++)
8563 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8565 if (change->can_change_or_has_action &&
8566 change->has_event[trigger_event] &&
8567 change->trigger_side & trigger_side &&
8568 change->trigger_player & trigger_player &&
8569 change->trigger_page & trigger_page_bits &&
8570 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8572 change->actual_trigger_element = trigger_element;
8573 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8574 change->actual_trigger_side = trigger_side;
8575 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8576 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8578 if ((change->can_change && !change_done) || change->has_action)
8582 SCAN_PLAYFIELD(x, y)
8584 if (Feld[x][y] == element)
8586 if (change->can_change && !change_done)
8588 ChangeDelay[x][y] = 1;
8589 ChangeEvent[x][y] = trigger_event;
8591 HandleElementChange(x, y, p);
8593 #if USE_NEW_DELAYED_ACTION
8594 else if (change->has_action)
8596 ExecuteCustomElementAction(x, y, element, p);
8597 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8600 if (change->has_action)
8602 ExecuteCustomElementAction(x, y, element, p);
8603 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8609 if (change->can_change)
8612 change_done_any = TRUE;
8619 return change_done_any;
8622 static boolean CheckElementChangeExt(int x, int y,
8624 int trigger_element,
8629 boolean change_done = FALSE;
8632 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8633 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8636 if (Feld[x][y] == EL_BLOCKED)
8638 Blocked2Moving(x, y, &x, &y);
8639 element = Feld[x][y];
8643 /* check if element has already changed */
8644 if (Feld[x][y] != element)
8647 /* check if element has already changed or is about to change after moving */
8648 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8649 Feld[x][y] != element) ||
8651 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8652 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8653 ChangePage[x][y] != -1)))
8657 for (p = 0; p < element_info[element].num_change_pages; p++)
8659 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8661 /* check trigger element for all events where the element that is checked
8662 for changing interacts with a directly adjacent element -- this is
8663 different to element changes that affect other elements to change on the
8664 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
8665 boolean check_trigger_element =
8666 (trigger_event == CE_TOUCHING_X ||
8667 trigger_event == CE_HITTING_X ||
8668 trigger_event == CE_HIT_BY_X ||
8670 /* this one was forgotten until 3.2.3 */
8671 trigger_event == CE_DIGGING_X);
8674 if (change->can_change_or_has_action &&
8675 change->has_event[trigger_event] &&
8676 change->trigger_side & trigger_side &&
8677 change->trigger_player & trigger_player &&
8678 (!check_trigger_element ||
8679 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8681 change->actual_trigger_element = trigger_element;
8682 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8683 change->actual_trigger_side = trigger_side;
8684 change->actual_trigger_ce_value = CustomValue[x][y];
8685 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8687 /* special case: trigger element not at (x,y) position for some events */
8688 if (check_trigger_element)
8700 { 0, 0 }, { 0, 0 }, { 0, 0 },
8704 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8705 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8707 change->actual_trigger_ce_value = CustomValue[xx][yy];
8708 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8711 if (change->can_change && !change_done)
8713 ChangeDelay[x][y] = 1;
8714 ChangeEvent[x][y] = trigger_event;
8716 HandleElementChange(x, y, p);
8720 #if USE_NEW_DELAYED_ACTION
8721 else if (change->has_action)
8723 ExecuteCustomElementAction(x, y, element, p);
8724 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8727 if (change->has_action)
8729 ExecuteCustomElementAction(x, y, element, p);
8730 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8739 static void PlayPlayerSound(struct PlayerInfo *player)
8741 int jx = player->jx, jy = player->jy;
8742 int sound_element = player->artwork_element;
8743 int last_action = player->last_action_waiting;
8744 int action = player->action_waiting;
8746 if (player->is_waiting)
8748 if (action != last_action)
8749 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8751 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8755 if (action != last_action)
8756 StopSound(element_info[sound_element].sound[last_action]);
8758 if (last_action == ACTION_SLEEPING)
8759 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8763 static void PlayAllPlayersSound()
8767 for (i = 0; i < MAX_PLAYERS; i++)
8768 if (stored_player[i].active)
8769 PlayPlayerSound(&stored_player[i]);
8772 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8774 boolean last_waiting = player->is_waiting;
8775 int move_dir = player->MovDir;
8777 player->dir_waiting = move_dir;
8778 player->last_action_waiting = player->action_waiting;
8782 if (!last_waiting) /* not waiting -> waiting */
8784 player->is_waiting = TRUE;
8786 player->frame_counter_bored =
8788 game.player_boring_delay_fixed +
8789 GetSimpleRandom(game.player_boring_delay_random);
8790 player->frame_counter_sleeping =
8792 game.player_sleeping_delay_fixed +
8793 GetSimpleRandom(game.player_sleeping_delay_random);
8795 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
8798 if (game.player_sleeping_delay_fixed +
8799 game.player_sleeping_delay_random > 0 &&
8800 player->anim_delay_counter == 0 &&
8801 player->post_delay_counter == 0 &&
8802 FrameCounter >= player->frame_counter_sleeping)
8803 player->is_sleeping = TRUE;
8804 else if (game.player_boring_delay_fixed +
8805 game.player_boring_delay_random > 0 &&
8806 FrameCounter >= player->frame_counter_bored)
8807 player->is_bored = TRUE;
8809 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8810 player->is_bored ? ACTION_BORING :
8813 if (player->is_sleeping && player->use_murphy)
8815 /* special case for sleeping Murphy when leaning against non-free tile */
8817 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
8818 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
8819 !IS_MOVING(player->jx - 1, player->jy)))
8821 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
8822 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
8823 !IS_MOVING(player->jx + 1, player->jy)))
8824 move_dir = MV_RIGHT;
8826 player->is_sleeping = FALSE;
8828 player->dir_waiting = move_dir;
8831 if (player->is_sleeping)
8833 if (player->num_special_action_sleeping > 0)
8835 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8837 int last_special_action = player->special_action_sleeping;
8838 int num_special_action = player->num_special_action_sleeping;
8839 int special_action =
8840 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8841 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8842 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8843 last_special_action + 1 : ACTION_SLEEPING);
8844 int special_graphic =
8845 el_act_dir2img(player->artwork_element, special_action, move_dir);
8847 player->anim_delay_counter =
8848 graphic_info[special_graphic].anim_delay_fixed +
8849 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8850 player->post_delay_counter =
8851 graphic_info[special_graphic].post_delay_fixed +
8852 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8854 player->special_action_sleeping = special_action;
8857 if (player->anim_delay_counter > 0)
8859 player->action_waiting = player->special_action_sleeping;
8860 player->anim_delay_counter--;
8862 else if (player->post_delay_counter > 0)
8864 player->post_delay_counter--;
8868 else if (player->is_bored)
8870 if (player->num_special_action_bored > 0)
8872 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8874 int special_action =
8875 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
8876 int special_graphic =
8877 el_act_dir2img(player->artwork_element, special_action, move_dir);
8879 player->anim_delay_counter =
8880 graphic_info[special_graphic].anim_delay_fixed +
8881 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8882 player->post_delay_counter =
8883 graphic_info[special_graphic].post_delay_fixed +
8884 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8886 player->special_action_bored = special_action;
8889 if (player->anim_delay_counter > 0)
8891 player->action_waiting = player->special_action_bored;
8892 player->anim_delay_counter--;
8894 else if (player->post_delay_counter > 0)
8896 player->post_delay_counter--;
8901 else if (last_waiting) /* waiting -> not waiting */
8903 player->is_waiting = FALSE;
8904 player->is_bored = FALSE;
8905 player->is_sleeping = FALSE;
8907 player->frame_counter_bored = -1;
8908 player->frame_counter_sleeping = -1;
8910 player->anim_delay_counter = 0;
8911 player->post_delay_counter = 0;
8913 player->dir_waiting = player->MovDir;
8914 player->action_waiting = ACTION_DEFAULT;
8916 player->special_action_bored = ACTION_DEFAULT;
8917 player->special_action_sleeping = ACTION_DEFAULT;
8921 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8923 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8924 int left = player_action & JOY_LEFT;
8925 int right = player_action & JOY_RIGHT;
8926 int up = player_action & JOY_UP;
8927 int down = player_action & JOY_DOWN;
8928 int button1 = player_action & JOY_BUTTON_1;
8929 int button2 = player_action & JOY_BUTTON_2;
8930 int dx = (left ? -1 : right ? 1 : 0);
8931 int dy = (up ? -1 : down ? 1 : 0);
8933 if (!player->active || tape.pausing)
8939 snapped = SnapField(player, dx, dy);
8943 dropped = DropElement(player);
8945 moved = MovePlayer(player, dx, dy);
8948 if (tape.single_step && tape.recording && !tape.pausing)
8950 if (button1 || (dropped && !moved))
8952 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8953 SnapField(player, 0, 0); /* stop snapping */
8957 SetPlayerWaiting(player, FALSE);
8959 return player_action;
8963 /* no actions for this player (no input at player's configured device) */
8965 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8966 SnapField(player, 0, 0);
8967 CheckGravityMovementWhenNotMoving(player);
8969 if (player->MovPos == 0)
8970 SetPlayerWaiting(player, TRUE);
8972 if (player->MovPos == 0) /* needed for tape.playing */
8973 player->is_moving = FALSE;
8975 player->is_dropping = FALSE;
8976 player->is_dropping_pressed = FALSE;
8977 player->drop_pressed_delay = 0;
8983 static void CheckLevelTime()
8987 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8989 if (level.native_em_level->lev->home == 0) /* all players at home */
8991 PlayerWins(local_player);
8993 AllPlayersGone = TRUE;
8995 level.native_em_level->lev->home = -1;
8998 if (level.native_em_level->ply[0]->alive == 0 &&
8999 level.native_em_level->ply[1]->alive == 0 &&
9000 level.native_em_level->ply[2]->alive == 0 &&
9001 level.native_em_level->ply[3]->alive == 0) /* all dead */
9002 AllPlayersGone = TRUE;
9005 if (TimeFrames >= FRAMES_PER_SECOND)
9010 for (i = 0; i < MAX_PLAYERS; i++)
9012 struct PlayerInfo *player = &stored_player[i];
9014 if (SHIELD_ON(player))
9016 player->shield_normal_time_left--;
9018 if (player->shield_deadly_time_left > 0)
9019 player->shield_deadly_time_left--;
9023 if (!local_player->LevelSolved && !level.use_step_counter)
9031 if (TimeLeft <= 10 && setup.time_limit)
9032 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9034 DrawGameValue_Time(TimeLeft);
9036 if (!TimeLeft && setup.time_limit)
9038 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9039 level.native_em_level->lev->killed_out_of_time = TRUE;
9041 for (i = 0; i < MAX_PLAYERS; i++)
9042 KillPlayer(&stored_player[i]);
9045 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9046 DrawGameValue_Time(TimePlayed);
9048 level.native_em_level->lev->time =
9049 (level.time == 0 ? TimePlayed : TimeLeft);
9052 if (tape.recording || tape.playing)
9053 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9057 void AdvanceFrameAndPlayerCounters(int player_nr)
9061 /* advance frame counters (global frame counter and time frame counter) */
9065 /* advance player counters (counters for move delay, move animation etc.) */
9066 for (i = 0; i < MAX_PLAYERS; i++)
9068 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9069 int move_delay_value = stored_player[i].move_delay_value;
9070 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9072 if (!advance_player_counters) /* not all players may be affected */
9075 #if USE_NEW_PLAYER_ANIM
9076 if (move_frames == 0) /* less than one move per game frame */
9078 int stepsize = TILEX / move_delay_value;
9079 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9080 int count = (stored_player[i].is_moving ?
9081 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9083 if (count % delay == 0)
9088 stored_player[i].Frame += move_frames;
9090 if (stored_player[i].MovPos != 0)
9091 stored_player[i].StepFrame += move_frames;
9093 if (stored_player[i].move_delay > 0)
9094 stored_player[i].move_delay--;
9096 /* due to bugs in previous versions, counter must count up, not down */
9097 if (stored_player[i].push_delay != -1)
9098 stored_player[i].push_delay++;
9100 if (stored_player[i].drop_delay > 0)
9101 stored_player[i].drop_delay--;
9103 if (stored_player[i].is_dropping_pressed)
9104 stored_player[i].drop_pressed_delay++;
9108 void StartGameActions(boolean init_network_game, boolean record_tape,
9111 unsigned long new_random_seed = InitRND(random_seed);
9114 TapeStartRecording(new_random_seed);
9116 #if defined(NETWORK_AVALIABLE)
9117 if (init_network_game)
9119 SendToServer_StartPlaying();
9130 static unsigned long game_frame_delay = 0;
9131 unsigned long game_frame_delay_value;
9132 byte *recorded_player_action;
9133 byte summarized_player_action = 0;
9134 byte tape_action[MAX_PLAYERS];
9137 if (game.restart_level)
9138 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9140 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9142 if (level.native_em_level->lev->home == 0) /* all players at home */
9144 PlayerWins(local_player);
9146 AllPlayersGone = TRUE;
9148 level.native_em_level->lev->home = -1;
9151 if (level.native_em_level->ply[0]->alive == 0 &&
9152 level.native_em_level->ply[1]->alive == 0 &&
9153 level.native_em_level->ply[2]->alive == 0 &&
9154 level.native_em_level->ply[3]->alive == 0) /* all dead */
9155 AllPlayersGone = TRUE;
9158 if (local_player->LevelSolved)
9161 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9164 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9167 game_frame_delay_value =
9168 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9170 if (tape.playing && tape.warp_forward && !tape.pausing)
9171 game_frame_delay_value = 0;
9173 /* ---------- main game synchronization point ---------- */
9175 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9177 if (network_playing && !network_player_action_received)
9179 /* try to get network player actions in time */
9181 #if defined(NETWORK_AVALIABLE)
9182 /* last chance to get network player actions without main loop delay */
9186 /* game was quit by network peer */
9187 if (game_status != GAME_MODE_PLAYING)
9190 if (!network_player_action_received)
9191 return; /* failed to get network player actions in time */
9193 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9199 /* at this point we know that we really continue executing the game */
9201 network_player_action_received = FALSE;
9203 /* when playing tape, read previously recorded player input from tape data */
9204 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9207 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9212 if (tape.set_centered_player)
9214 game.centered_player_nr_next = tape.centered_player_nr_next;
9215 game.set_centered_player = TRUE;
9218 for (i = 0; i < MAX_PLAYERS; i++)
9220 summarized_player_action |= stored_player[i].action;
9222 if (!network_playing)
9223 stored_player[i].effective_action = stored_player[i].action;
9226 #if defined(NETWORK_AVALIABLE)
9227 if (network_playing)
9228 SendToServer_MovePlayer(summarized_player_action);
9231 if (!options.network && !setup.team_mode)
9232 local_player->effective_action = summarized_player_action;
9234 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9236 for (i = 0; i < MAX_PLAYERS; i++)
9237 stored_player[i].effective_action =
9238 (i == game.centered_player_nr ? summarized_player_action : 0);
9241 if (recorded_player_action != NULL)
9242 for (i = 0; i < MAX_PLAYERS; i++)
9243 stored_player[i].effective_action = recorded_player_action[i];
9245 for (i = 0; i < MAX_PLAYERS; i++)
9247 tape_action[i] = stored_player[i].effective_action;
9249 /* (this can only happen in the R'n'D game engine) */
9250 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9251 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9254 /* only record actions from input devices, but not programmed actions */
9256 TapeRecordAction(tape_action);
9258 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9260 GameActions_EM_Main();
9268 void GameActions_EM_Main()
9270 byte effective_action[MAX_PLAYERS];
9271 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9274 for (i = 0; i < MAX_PLAYERS; i++)
9275 effective_action[i] = stored_player[i].effective_action;
9277 GameActions_EM(effective_action, warp_mode);
9281 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9284 void GameActions_RND()
9286 int magic_wall_x = 0, magic_wall_y = 0;
9287 int i, x, y, element, graphic;
9289 InitPlayfieldScanModeVars();
9291 #if USE_ONE_MORE_CHANGE_PER_FRAME
9292 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9294 SCAN_PLAYFIELD(x, y)
9296 ChangeCount[x][y] = 0;
9297 ChangeEvent[x][y] = -1;
9302 if (game.set_centered_player)
9304 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9306 /* switching to "all players" only possible if all players fit to screen */
9307 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9309 game.centered_player_nr_next = game.centered_player_nr;
9310 game.set_centered_player = FALSE;
9313 /* do not switch focus to non-existing (or non-active) player */
9314 if (game.centered_player_nr_next >= 0 &&
9315 !stored_player[game.centered_player_nr_next].active)
9317 game.centered_player_nr_next = game.centered_player_nr;
9318 game.set_centered_player = FALSE;
9322 if (game.set_centered_player &&
9323 ScreenMovPos == 0) /* screen currently aligned at tile position */
9327 if (game.centered_player_nr_next == -1)
9329 setScreenCenteredToAllPlayers(&sx, &sy);
9333 sx = stored_player[game.centered_player_nr_next].jx;
9334 sy = stored_player[game.centered_player_nr_next].jy;
9337 game.centered_player_nr = game.centered_player_nr_next;
9338 game.set_centered_player = FALSE;
9340 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9341 DrawGameDoorValues();
9344 for (i = 0; i < MAX_PLAYERS; i++)
9346 int actual_player_action = stored_player[i].effective_action;
9349 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9350 - rnd_equinox_tetrachloride 048
9351 - rnd_equinox_tetrachloride_ii 096
9352 - rnd_emanuel_schmieg 002
9353 - doctor_sloan_ww 001, 020
9355 if (stored_player[i].MovPos == 0)
9356 CheckGravityMovement(&stored_player[i]);
9359 /* overwrite programmed action with tape action */
9360 if (stored_player[i].programmed_action)
9361 actual_player_action = stored_player[i].programmed_action;
9363 PlayerActions(&stored_player[i], actual_player_action);
9365 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9368 ScrollScreen(NULL, SCROLL_GO_ON);
9370 /* for backwards compatibility, the following code emulates a fixed bug that
9371 occured when pushing elements (causing elements that just made their last
9372 pushing step to already (if possible) make their first falling step in the
9373 same game frame, which is bad); this code is also needed to use the famous
9374 "spring push bug" which is used in older levels and might be wanted to be
9375 used also in newer levels, but in this case the buggy pushing code is only
9376 affecting the "spring" element and no other elements */
9378 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9380 for (i = 0; i < MAX_PLAYERS; i++)
9382 struct PlayerInfo *player = &stored_player[i];
9386 if (player->active && player->is_pushing && player->is_moving &&
9388 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9389 Feld[x][y] == EL_SPRING))
9391 ContinueMoving(x, y);
9393 /* continue moving after pushing (this is actually a bug) */
9394 if (!IS_MOVING(x, y))
9402 SCAN_PLAYFIELD(x, y)
9404 ChangeCount[x][y] = 0;
9405 ChangeEvent[x][y] = -1;
9407 /* this must be handled before main playfield loop */
9408 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9411 if (MovDelay[x][y] <= 0)
9415 #if USE_NEW_SNAP_DELAY
9416 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9419 if (MovDelay[x][y] <= 0)
9422 DrawLevelField(x, y);
9424 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9430 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9432 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9433 printf("GameActions(): This should never happen!\n");
9435 ChangePage[x][y] = -1;
9440 if (WasJustMoving[x][y] > 0)
9441 WasJustMoving[x][y]--;
9442 if (WasJustFalling[x][y] > 0)
9443 WasJustFalling[x][y]--;
9444 if (CheckCollision[x][y] > 0)
9445 CheckCollision[x][y]--;
9446 if (CheckImpact[x][y] > 0)
9447 CheckImpact[x][y]--;
9451 /* reset finished pushing action (not done in ContinueMoving() to allow
9452 continuous pushing animation for elements with zero push delay) */
9453 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9455 ResetGfxAnimation(x, y);
9456 DrawLevelField(x, y);
9460 if (IS_BLOCKED(x, y))
9464 Blocked2Moving(x, y, &oldx, &oldy);
9465 if (!IS_MOVING(oldx, oldy))
9467 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9468 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9469 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9470 printf("GameActions(): This should never happen!\n");
9476 SCAN_PLAYFIELD(x, y)
9478 element = Feld[x][y];
9479 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9481 ResetGfxFrame(x, y, TRUE);
9483 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9484 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9485 ResetRandomAnimationValue(x, y);
9487 SetRandomAnimationValue(x, y);
9489 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9491 if (IS_INACTIVE(element))
9493 if (IS_ANIMATED(graphic))
9494 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9499 /* this may take place after moving, so 'element' may have changed */
9500 if (IS_CHANGING(x, y) &&
9501 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9503 int page = element_info[element].event_page_nr[CE_DELAY];
9506 HandleElementChange(x, y, page);
9508 if (CAN_CHANGE(element))
9509 HandleElementChange(x, y, page);
9511 if (HAS_ACTION(element))
9512 ExecuteCustomElementAction(x, y, element, page);
9515 element = Feld[x][y];
9516 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9519 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9523 element = Feld[x][y];
9524 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9526 if (IS_ANIMATED(graphic) &&
9529 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9531 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9532 DrawTwinkleOnField(x, y);
9534 else if ((element == EL_ACID ||
9535 element == EL_EXIT_OPEN ||
9536 element == EL_SP_EXIT_OPEN ||
9537 element == EL_SP_TERMINAL ||
9538 element == EL_SP_TERMINAL_ACTIVE ||
9539 element == EL_EXTRA_TIME ||
9540 element == EL_SHIELD_NORMAL ||
9541 element == EL_SHIELD_DEADLY) &&
9542 IS_ANIMATED(graphic))
9543 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9544 else if (IS_MOVING(x, y))
9545 ContinueMoving(x, y);
9546 else if (IS_ACTIVE_BOMB(element))
9547 CheckDynamite(x, y);
9548 else if (element == EL_AMOEBA_GROWING)
9549 AmoebeWaechst(x, y);
9550 else if (element == EL_AMOEBA_SHRINKING)
9551 AmoebaDisappearing(x, y);
9553 #if !USE_NEW_AMOEBA_CODE
9554 else if (IS_AMOEBALIVE(element))
9555 AmoebeAbleger(x, y);
9558 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9560 else if (element == EL_EXIT_CLOSED)
9562 else if (element == EL_SP_EXIT_CLOSED)
9564 else if (element == EL_EXPANDABLE_WALL_GROWING)
9566 else if (element == EL_EXPANDABLE_WALL ||
9567 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9568 element == EL_EXPANDABLE_WALL_VERTICAL ||
9569 element == EL_EXPANDABLE_WALL_ANY ||
9570 element == EL_BD_EXPANDABLE_WALL)
9572 else if (element == EL_FLAMES)
9573 CheckForDragon(x, y);
9574 else if (element == EL_EXPLOSION)
9575 ; /* drawing of correct explosion animation is handled separately */
9576 else if (element == EL_ELEMENT_SNAPPING ||
9577 element == EL_DIAGONAL_SHRINKING ||
9578 element == EL_DIAGONAL_GROWING)
9580 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9582 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9584 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9585 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9587 if (IS_BELT_ACTIVE(element))
9588 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9590 if (game.magic_wall_active)
9592 int jx = local_player->jx, jy = local_player->jy;
9594 /* play the element sound at the position nearest to the player */
9595 if ((element == EL_MAGIC_WALL_FULL ||
9596 element == EL_MAGIC_WALL_ACTIVE ||
9597 element == EL_MAGIC_WALL_EMPTYING ||
9598 element == EL_BD_MAGIC_WALL_FULL ||
9599 element == EL_BD_MAGIC_WALL_ACTIVE ||
9600 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9601 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9609 #if USE_NEW_AMOEBA_CODE
9610 /* new experimental amoeba growth stuff */
9611 if (!(FrameCounter % 8))
9613 static unsigned long random = 1684108901;
9615 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9617 x = RND(lev_fieldx);
9618 y = RND(lev_fieldy);
9619 element = Feld[x][y];
9621 if (!IS_PLAYER(x,y) &&
9622 (element == EL_EMPTY ||
9623 CAN_GROW_INTO(element) ||
9624 element == EL_QUICKSAND_EMPTY ||
9625 element == EL_ACID_SPLASH_LEFT ||
9626 element == EL_ACID_SPLASH_RIGHT))
9628 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9629 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9630 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9631 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9632 Feld[x][y] = EL_AMOEBA_DROP;
9635 random = random * 129 + 1;
9641 if (game.explosions_delayed)
9644 game.explosions_delayed = FALSE;
9646 SCAN_PLAYFIELD(x, y)
9648 element = Feld[x][y];
9650 if (ExplodeField[x][y])
9651 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9652 else if (element == EL_EXPLOSION)
9653 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9655 ExplodeField[x][y] = EX_TYPE_NONE;
9658 game.explosions_delayed = TRUE;
9661 if (game.magic_wall_active)
9663 if (!(game.magic_wall_time_left % 4))
9665 int element = Feld[magic_wall_x][magic_wall_y];
9667 if (element == EL_BD_MAGIC_WALL_FULL ||
9668 element == EL_BD_MAGIC_WALL_ACTIVE ||
9669 element == EL_BD_MAGIC_WALL_EMPTYING)
9670 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9672 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9675 if (game.magic_wall_time_left > 0)
9677 game.magic_wall_time_left--;
9678 if (!game.magic_wall_time_left)
9680 SCAN_PLAYFIELD(x, y)
9682 element = Feld[x][y];
9684 if (element == EL_MAGIC_WALL_ACTIVE ||
9685 element == EL_MAGIC_WALL_FULL)
9687 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9688 DrawLevelField(x, y);
9690 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9691 element == EL_BD_MAGIC_WALL_FULL)
9693 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9694 DrawLevelField(x, y);
9698 game.magic_wall_active = FALSE;
9703 if (game.light_time_left > 0)
9705 game.light_time_left--;
9707 if (game.light_time_left == 0)
9708 RedrawAllLightSwitchesAndInvisibleElements();
9711 if (game.timegate_time_left > 0)
9713 game.timegate_time_left--;
9715 if (game.timegate_time_left == 0)
9716 CloseAllOpenTimegates();
9719 if (game.lenses_time_left > 0)
9721 game.lenses_time_left--;
9723 if (game.lenses_time_left == 0)
9724 RedrawAllInvisibleElementsForLenses();
9727 if (game.magnify_time_left > 0)
9729 game.magnify_time_left--;
9731 if (game.magnify_time_left == 0)
9732 RedrawAllInvisibleElementsForMagnifier();
9735 for (i = 0; i < MAX_PLAYERS; i++)
9737 struct PlayerInfo *player = &stored_player[i];
9739 if (SHIELD_ON(player))
9741 if (player->shield_deadly_time_left)
9742 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9743 else if (player->shield_normal_time_left)
9744 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9751 PlayAllPlayersSound();
9753 if (options.debug) /* calculate frames per second */
9755 static unsigned long fps_counter = 0;
9756 static int fps_frames = 0;
9757 unsigned long fps_delay_ms = Counter() - fps_counter;
9761 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9763 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9766 fps_counter = Counter();
9769 redraw_mask |= REDRAW_FPS;
9772 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9774 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9776 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9778 local_player->show_envelope = 0;
9781 /* use random number generator in every frame to make it less predictable */
9782 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9786 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9788 int min_x = x, min_y = y, max_x = x, max_y = y;
9791 for (i = 0; i < MAX_PLAYERS; i++)
9793 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9795 if (!stored_player[i].active || &stored_player[i] == player)
9798 min_x = MIN(min_x, jx);
9799 min_y = MIN(min_y, jy);
9800 max_x = MAX(max_x, jx);
9801 max_y = MAX(max_y, jy);
9804 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9807 static boolean AllPlayersInVisibleScreen()
9811 for (i = 0; i < MAX_PLAYERS; i++)
9813 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9815 if (!stored_player[i].active)
9818 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9825 void ScrollLevel(int dx, int dy)
9827 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9830 BlitBitmap(drawto_field, drawto_field,
9831 FX + TILEX * (dx == -1) - softscroll_offset,
9832 FY + TILEY * (dy == -1) - softscroll_offset,
9833 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9834 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9835 FX + TILEX * (dx == 1) - softscroll_offset,
9836 FY + TILEY * (dy == 1) - softscroll_offset);
9840 x = (dx == 1 ? BX1 : BX2);
9841 for (y = BY1; y <= BY2; y++)
9842 DrawScreenField(x, y);
9847 y = (dy == 1 ? BY1 : BY2);
9848 for (x = BX1; x <= BX2; x++)
9849 DrawScreenField(x, y);
9852 redraw_mask |= REDRAW_FIELD;
9855 static boolean canFallDown(struct PlayerInfo *player)
9857 int jx = player->jx, jy = player->jy;
9859 return (IN_LEV_FIELD(jx, jy + 1) &&
9860 (IS_FREE(jx, jy + 1) ||
9861 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9862 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9863 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9866 static boolean canPassField(int x, int y, int move_dir)
9868 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9869 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9870 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9873 int element = Feld[x][y];
9875 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9876 !CAN_MOVE(element) &&
9877 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9878 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9879 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9882 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9884 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9885 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9886 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9890 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9891 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9892 (IS_DIGGABLE(Feld[newx][newy]) ||
9893 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9894 canPassField(newx, newy, move_dir)));
9897 static void CheckGravityMovement(struct PlayerInfo *player)
9899 #if USE_PLAYER_GRAVITY
9900 if (player->gravity && !player->programmed_action)
9902 if (game.gravity && !player->programmed_action)
9905 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9906 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9907 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
9908 int jx = player->jx, jy = player->jy;
9909 boolean player_is_moving_to_valid_field =
9910 (!player_is_snapping &&
9911 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9912 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9913 boolean player_can_fall_down = canFallDown(player);
9915 if (player_can_fall_down &&
9916 !player_is_moving_to_valid_field)
9917 player->programmed_action = MV_DOWN;
9921 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9923 return CheckGravityMovement(player);
9925 #if USE_PLAYER_GRAVITY
9926 if (player->gravity && !player->programmed_action)
9928 if (game.gravity && !player->programmed_action)
9931 int jx = player->jx, jy = player->jy;
9932 boolean field_under_player_is_free =
9933 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9934 boolean player_is_standing_on_valid_field =
9935 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9936 (IS_WALKABLE(Feld[jx][jy]) &&
9937 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9939 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9940 player->programmed_action = MV_DOWN;
9946 -----------------------------------------------------------------------------
9947 dx, dy: direction (non-diagonal) to try to move the player to
9948 real_dx, real_dy: direction as read from input device (can be diagonal)
9951 boolean MovePlayerOneStep(struct PlayerInfo *player,
9952 int dx, int dy, int real_dx, int real_dy)
9954 int jx = player->jx, jy = player->jy;
9955 int new_jx = jx + dx, new_jy = jy + dy;
9956 #if !USE_FIXED_DONT_RUN_INTO
9960 boolean player_can_move = !player->cannot_move;
9962 if (!player->active || (!dx && !dy))
9963 return MP_NO_ACTION;
9965 player->MovDir = (dx < 0 ? MV_LEFT :
9968 dy > 0 ? MV_DOWN : MV_NONE);
9970 if (!IN_LEV_FIELD(new_jx, new_jy))
9971 return MP_NO_ACTION;
9973 if (!player_can_move)
9975 if (player->MovPos == 0)
9977 player->is_moving = FALSE;
9978 player->is_digging = FALSE;
9979 player->is_collecting = FALSE;
9980 player->is_snapping = FALSE;
9981 player->is_pushing = FALSE;
9986 if (!options.network && game.centered_player_nr == -1 &&
9987 !AllPlayersInSight(player, new_jx, new_jy))
9988 return MP_NO_ACTION;
9990 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9991 return MP_NO_ACTION;
9994 #if !USE_FIXED_DONT_RUN_INTO
9995 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9997 /* (moved to DigField()) */
9998 if (player_can_move && DONT_RUN_INTO(element))
10000 if (element == EL_ACID && dx == 0 && dy == 1)
10002 SplashAcid(new_jx, new_jy);
10003 Feld[jx][jy] = EL_PLAYER_1;
10004 InitMovingField(jx, jy, MV_DOWN);
10005 Store[jx][jy] = EL_ACID;
10006 ContinueMoving(jx, jy);
10007 BuryPlayer(player);
10010 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10016 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10017 if (can_move != MP_MOVING)
10020 /* check if DigField() has caused relocation of the player */
10021 if (player->jx != jx || player->jy != jy)
10022 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10024 StorePlayer[jx][jy] = 0;
10025 player->last_jx = jx;
10026 player->last_jy = jy;
10027 player->jx = new_jx;
10028 player->jy = new_jy;
10029 StorePlayer[new_jx][new_jy] = player->element_nr;
10031 if (player->move_delay_value_next != -1)
10033 player->move_delay_value = player->move_delay_value_next;
10034 player->move_delay_value_next = -1;
10038 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10040 player->step_counter++;
10042 PlayerVisit[jx][jy] = FrameCounter;
10044 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10045 player->is_moving = TRUE;
10049 /* should better be called in MovePlayer(), but this breaks some tapes */
10050 ScrollPlayer(player, SCROLL_INIT);
10056 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10058 int jx = player->jx, jy = player->jy;
10059 int old_jx = jx, old_jy = jy;
10060 int moved = MP_NO_ACTION;
10062 if (!player->active)
10067 if (player->MovPos == 0)
10069 player->is_moving = FALSE;
10070 player->is_digging = FALSE;
10071 player->is_collecting = FALSE;
10072 player->is_snapping = FALSE;
10073 player->is_pushing = FALSE;
10079 if (player->move_delay > 0)
10082 player->move_delay = -1; /* set to "uninitialized" value */
10084 /* store if player is automatically moved to next field */
10085 player->is_auto_moving = (player->programmed_action != MV_NONE);
10087 /* remove the last programmed player action */
10088 player->programmed_action = 0;
10090 if (player->MovPos)
10092 /* should only happen if pre-1.2 tape recordings are played */
10093 /* this is only for backward compatibility */
10095 int original_move_delay_value = player->move_delay_value;
10098 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10102 /* scroll remaining steps with finest movement resolution */
10103 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10105 while (player->MovPos)
10107 ScrollPlayer(player, SCROLL_GO_ON);
10108 ScrollScreen(NULL, SCROLL_GO_ON);
10110 AdvanceFrameAndPlayerCounters(player->index_nr);
10116 player->move_delay_value = original_move_delay_value;
10119 player->is_active = FALSE;
10121 if (player->last_move_dir & MV_HORIZONTAL)
10123 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10124 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10128 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10129 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10132 #if USE_FIXED_BORDER_RUNNING_GFX
10133 if (!moved && !player->is_active)
10135 player->is_moving = FALSE;
10136 player->is_digging = FALSE;
10137 player->is_collecting = FALSE;
10138 player->is_snapping = FALSE;
10139 player->is_pushing = FALSE;
10147 if (moved & MP_MOVING && !ScreenMovPos &&
10148 (player->index_nr == game.centered_player_nr ||
10149 game.centered_player_nr == -1))
10151 if (moved & MP_MOVING && !ScreenMovPos &&
10152 (player == local_player || !options.network))
10155 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10156 int offset = (setup.scroll_delay ? 3 : 0);
10158 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10160 /* actual player has left the screen -- scroll in that direction */
10161 if (jx != old_jx) /* player has moved horizontally */
10162 scroll_x += (jx - old_jx);
10163 else /* player has moved vertically */
10164 scroll_y += (jy - old_jy);
10168 if (jx != old_jx) /* player has moved horizontally */
10170 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10171 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10172 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10174 /* don't scroll over playfield boundaries */
10175 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10176 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10178 /* don't scroll more than one field at a time */
10179 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10181 /* don't scroll against the player's moving direction */
10182 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10183 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10184 scroll_x = old_scroll_x;
10186 else /* player has moved vertically */
10188 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10189 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10190 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10192 /* don't scroll over playfield boundaries */
10193 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10194 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10196 /* don't scroll more than one field at a time */
10197 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10199 /* don't scroll against the player's moving direction */
10200 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10201 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10202 scroll_y = old_scroll_y;
10206 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10209 if (!options.network && game.centered_player_nr == -1 &&
10210 !AllPlayersInVisibleScreen())
10212 scroll_x = old_scroll_x;
10213 scroll_y = old_scroll_y;
10217 if (!options.network && !AllPlayersInVisibleScreen())
10219 scroll_x = old_scroll_x;
10220 scroll_y = old_scroll_y;
10225 ScrollScreen(player, SCROLL_INIT);
10226 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10231 player->StepFrame = 0;
10233 if (moved & MP_MOVING)
10235 if (old_jx != jx && old_jy == jy)
10236 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10237 else if (old_jx == jx && old_jy != jy)
10238 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10240 DrawLevelField(jx, jy); /* for "crumbled sand" */
10242 player->last_move_dir = player->MovDir;
10243 player->is_moving = TRUE;
10244 player->is_snapping = FALSE;
10245 player->is_switching = FALSE;
10246 player->is_dropping = FALSE;
10247 player->is_dropping_pressed = FALSE;
10248 player->drop_pressed_delay = 0;
10251 /* should better be called here than above, but this breaks some tapes */
10252 ScrollPlayer(player, SCROLL_INIT);
10257 CheckGravityMovementWhenNotMoving(player);
10259 player->is_moving = FALSE;
10261 /* at this point, the player is allowed to move, but cannot move right now
10262 (e.g. because of something blocking the way) -- ensure that the player
10263 is also allowed to move in the next frame (in old versions before 3.1.1,
10264 the player was forced to wait again for eight frames before next try) */
10266 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10267 player->move_delay = 0; /* allow direct movement in the next frame */
10270 if (player->move_delay == -1) /* not yet initialized by DigField() */
10271 player->move_delay = player->move_delay_value;
10273 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10275 TestIfPlayerTouchesBadThing(jx, jy);
10276 TestIfPlayerTouchesCustomElement(jx, jy);
10279 if (!player->active)
10280 RemovePlayer(player);
10285 void ScrollPlayer(struct PlayerInfo *player, int mode)
10287 int jx = player->jx, jy = player->jy;
10288 int last_jx = player->last_jx, last_jy = player->last_jy;
10289 int move_stepsize = TILEX / player->move_delay_value;
10291 #if USE_NEW_PLAYER_SPEED
10292 if (!player->active)
10295 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10298 if (!player->active || player->MovPos == 0)
10302 if (mode == SCROLL_INIT)
10304 player->actual_frame_counter = FrameCounter;
10305 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10307 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10308 Feld[last_jx][last_jy] == EL_EMPTY)
10310 int last_field_block_delay = 0; /* start with no blocking at all */
10311 int block_delay_adjustment = player->block_delay_adjustment;
10313 /* if player blocks last field, add delay for exactly one move */
10314 if (player->block_last_field)
10316 last_field_block_delay += player->move_delay_value;
10318 /* when blocking enabled, prevent moving up despite gravity */
10319 #if USE_PLAYER_GRAVITY
10320 if (player->gravity && player->MovDir == MV_UP)
10321 block_delay_adjustment = -1;
10323 if (game.gravity && player->MovDir == MV_UP)
10324 block_delay_adjustment = -1;
10328 /* add block delay adjustment (also possible when not blocking) */
10329 last_field_block_delay += block_delay_adjustment;
10331 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10332 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10335 #if USE_NEW_PLAYER_SPEED
10336 if (player->MovPos != 0) /* player has not yet reached destination */
10342 else if (!FrameReached(&player->actual_frame_counter, 1))
10345 #if USE_NEW_PLAYER_SPEED
10346 if (player->MovPos != 0)
10348 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10349 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10351 /* before DrawPlayer() to draw correct player graphic for this case */
10352 if (player->MovPos == 0)
10353 CheckGravityMovement(player);
10356 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10357 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10359 /* before DrawPlayer() to draw correct player graphic for this case */
10360 if (player->MovPos == 0)
10361 CheckGravityMovement(player);
10364 if (player->MovPos == 0) /* player reached destination field */
10366 if (player->move_delay_reset_counter > 0)
10368 player->move_delay_reset_counter--;
10370 if (player->move_delay_reset_counter == 0)
10372 /* continue with normal speed after quickly moving through gate */
10373 HALVE_PLAYER_SPEED(player);
10375 /* be able to make the next move without delay */
10376 player->move_delay = 0;
10380 player->last_jx = jx;
10381 player->last_jy = jy;
10383 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10384 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10385 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10387 DrawPlayer(player); /* needed here only to cleanup last field */
10388 RemovePlayer(player);
10390 if (local_player->friends_still_needed == 0 ||
10391 IS_SP_ELEMENT(Feld[jx][jy]))
10392 PlayerWins(player);
10395 /* this breaks one level: "machine", level 000 */
10397 int move_direction = player->MovDir;
10398 int enter_side = MV_DIR_OPPOSITE(move_direction);
10399 int leave_side = move_direction;
10400 int old_jx = last_jx;
10401 int old_jy = last_jy;
10402 int old_element = Feld[old_jx][old_jy];
10403 int new_element = Feld[jx][jy];
10405 if (IS_CUSTOM_ELEMENT(old_element))
10406 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10408 player->index_bit, leave_side);
10410 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10411 CE_PLAYER_LEAVES_X,
10412 player->index_bit, leave_side);
10414 if (IS_CUSTOM_ELEMENT(new_element))
10415 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10416 player->index_bit, enter_side);
10418 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10419 CE_PLAYER_ENTERS_X,
10420 player->index_bit, enter_side);
10422 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10423 CE_MOVE_OF_X, move_direction);
10426 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10428 TestIfPlayerTouchesBadThing(jx, jy);
10429 TestIfPlayerTouchesCustomElement(jx, jy);
10431 /* needed because pushed element has not yet reached its destination,
10432 so it would trigger a change event at its previous field location */
10433 if (!player->is_pushing)
10434 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10436 if (!player->active)
10437 RemovePlayer(player);
10440 if (!local_player->LevelSolved && level.use_step_counter)
10450 if (TimeLeft <= 10 && setup.time_limit)
10451 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10453 DrawGameValue_Time(TimeLeft);
10455 if (!TimeLeft && setup.time_limit)
10456 for (i = 0; i < MAX_PLAYERS; i++)
10457 KillPlayer(&stored_player[i]);
10459 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10460 DrawGameValue_Time(TimePlayed);
10463 if (tape.single_step && tape.recording && !tape.pausing &&
10464 !player->programmed_action)
10465 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10469 void ScrollScreen(struct PlayerInfo *player, int mode)
10471 static unsigned long screen_frame_counter = 0;
10473 if (mode == SCROLL_INIT)
10475 /* set scrolling step size according to actual player's moving speed */
10476 ScrollStepSize = TILEX / player->move_delay_value;
10478 screen_frame_counter = FrameCounter;
10479 ScreenMovDir = player->MovDir;
10480 ScreenMovPos = player->MovPos;
10481 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10484 else if (!FrameReached(&screen_frame_counter, 1))
10489 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10490 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10491 redraw_mask |= REDRAW_FIELD;
10494 ScreenMovDir = MV_NONE;
10497 void TestIfPlayerTouchesCustomElement(int x, int y)
10499 static int xy[4][2] =
10506 static int trigger_sides[4][2] =
10508 /* center side border side */
10509 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10510 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10511 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10512 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10514 static int touch_dir[4] =
10516 MV_LEFT | MV_RIGHT,
10521 int center_element = Feld[x][y]; /* should always be non-moving! */
10524 for (i = 0; i < NUM_DIRECTIONS; i++)
10526 int xx = x + xy[i][0];
10527 int yy = y + xy[i][1];
10528 int center_side = trigger_sides[i][0];
10529 int border_side = trigger_sides[i][1];
10530 int border_element;
10532 if (!IN_LEV_FIELD(xx, yy))
10535 if (IS_PLAYER(x, y))
10537 struct PlayerInfo *player = PLAYERINFO(x, y);
10539 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10540 border_element = Feld[xx][yy]; /* may be moving! */
10541 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10542 border_element = Feld[xx][yy];
10543 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10544 border_element = MovingOrBlocked2Element(xx, yy);
10546 continue; /* center and border element do not touch */
10548 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10549 player->index_bit, border_side);
10550 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10551 CE_PLAYER_TOUCHES_X,
10552 player->index_bit, border_side);
10554 else if (IS_PLAYER(xx, yy))
10556 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10558 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10560 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10561 continue; /* center and border element do not touch */
10564 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10565 player->index_bit, center_side);
10566 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10567 CE_PLAYER_TOUCHES_X,
10568 player->index_bit, center_side);
10574 #if USE_ELEMENT_TOUCHING_BUGFIX
10576 void TestIfElementTouchesCustomElement(int x, int y)
10578 static int xy[4][2] =
10585 static int trigger_sides[4][2] =
10587 /* center side border side */
10588 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10589 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10590 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10591 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10593 static int touch_dir[4] =
10595 MV_LEFT | MV_RIGHT,
10600 boolean change_center_element = FALSE;
10601 int center_element = Feld[x][y]; /* should always be non-moving! */
10602 int border_element_old[NUM_DIRECTIONS];
10605 for (i = 0; i < NUM_DIRECTIONS; i++)
10607 int xx = x + xy[i][0];
10608 int yy = y + xy[i][1];
10609 int border_element;
10611 border_element_old[i] = -1;
10613 if (!IN_LEV_FIELD(xx, yy))
10616 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10617 border_element = Feld[xx][yy]; /* may be moving! */
10618 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10619 border_element = Feld[xx][yy];
10620 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10621 border_element = MovingOrBlocked2Element(xx, yy);
10623 continue; /* center and border element do not touch */
10625 border_element_old[i] = border_element;
10628 for (i = 0; i < NUM_DIRECTIONS; i++)
10630 int xx = x + xy[i][0];
10631 int yy = y + xy[i][1];
10632 int center_side = trigger_sides[i][0];
10633 int border_element = border_element_old[i];
10635 if (border_element == -1)
10638 /* check for change of border element */
10639 CheckElementChangeBySide(xx, yy, border_element, center_element,
10640 CE_TOUCHING_X, center_side);
10643 for (i = 0; i < NUM_DIRECTIONS; i++)
10645 int border_side = trigger_sides[i][1];
10646 int border_element = border_element_old[i];
10648 if (border_element == -1)
10651 /* check for change of center element (but change it only once) */
10652 if (!change_center_element)
10653 change_center_element =
10654 CheckElementChangeBySide(x, y, center_element, border_element,
10655 CE_TOUCHING_X, border_side);
10661 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10663 static int xy[4][2] =
10670 static int trigger_sides[4][2] =
10672 /* center side border side */
10673 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10674 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10675 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10676 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10678 static int touch_dir[4] =
10680 MV_LEFT | MV_RIGHT,
10685 boolean change_center_element = FALSE;
10686 int center_element = Feld[x][y]; /* should always be non-moving! */
10689 for (i = 0; i < NUM_DIRECTIONS; i++)
10691 int xx = x + xy[i][0];
10692 int yy = y + xy[i][1];
10693 int center_side = trigger_sides[i][0];
10694 int border_side = trigger_sides[i][1];
10695 int border_element;
10697 if (!IN_LEV_FIELD(xx, yy))
10700 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10701 border_element = Feld[xx][yy]; /* may be moving! */
10702 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10703 border_element = Feld[xx][yy];
10704 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10705 border_element = MovingOrBlocked2Element(xx, yy);
10707 continue; /* center and border element do not touch */
10709 /* check for change of center element (but change it only once) */
10710 if (!change_center_element)
10711 change_center_element =
10712 CheckElementChangeBySide(x, y, center_element, border_element,
10713 CE_TOUCHING_X, border_side);
10715 /* check for change of border element */
10716 CheckElementChangeBySide(xx, yy, border_element, center_element,
10717 CE_TOUCHING_X, center_side);
10723 void TestIfElementHitsCustomElement(int x, int y, int direction)
10725 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10726 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10727 int hitx = x + dx, hity = y + dy;
10728 int hitting_element = Feld[x][y];
10729 int touched_element;
10731 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10734 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10735 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10737 if (IN_LEV_FIELD(hitx, hity))
10739 int opposite_direction = MV_DIR_OPPOSITE(direction);
10740 int hitting_side = direction;
10741 int touched_side = opposite_direction;
10742 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10743 MovDir[hitx][hity] != direction ||
10744 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10750 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10751 CE_HITTING_X, touched_side);
10753 CheckElementChangeBySide(hitx, hity, touched_element,
10754 hitting_element, CE_HIT_BY_X, hitting_side);
10756 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10757 CE_HIT_BY_SOMETHING, opposite_direction);
10761 /* "hitting something" is also true when hitting the playfield border */
10762 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10763 CE_HITTING_SOMETHING, direction);
10767 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10769 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10770 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10771 int hitx = x + dx, hity = y + dy;
10772 int hitting_element = Feld[x][y];
10773 int touched_element;
10775 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10776 !IS_FREE(hitx, hity) &&
10777 (!IS_MOVING(hitx, hity) ||
10778 MovDir[hitx][hity] != direction ||
10779 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10782 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10786 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10790 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10791 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10793 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10794 EP_CAN_SMASH_EVERYTHING, direction);
10796 if (IN_LEV_FIELD(hitx, hity))
10798 int opposite_direction = MV_DIR_OPPOSITE(direction);
10799 int hitting_side = direction;
10800 int touched_side = opposite_direction;
10802 int touched_element = MovingOrBlocked2Element(hitx, hity);
10805 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10806 MovDir[hitx][hity] != direction ||
10807 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10816 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10817 CE_SMASHED_BY_SOMETHING, opposite_direction);
10819 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10820 CE_OTHER_IS_SMASHING, touched_side);
10822 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10823 CE_OTHER_GETS_SMASHED, hitting_side);
10829 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10831 int i, kill_x = -1, kill_y = -1;
10833 int bad_element = -1;
10834 static int test_xy[4][2] =
10841 static int test_dir[4] =
10849 for (i = 0; i < NUM_DIRECTIONS; i++)
10851 int test_x, test_y, test_move_dir, test_element;
10853 test_x = good_x + test_xy[i][0];
10854 test_y = good_y + test_xy[i][1];
10856 if (!IN_LEV_FIELD(test_x, test_y))
10860 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10862 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10864 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10865 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10867 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10868 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10872 bad_element = test_element;
10878 if (kill_x != -1 || kill_y != -1)
10880 if (IS_PLAYER(good_x, good_y))
10882 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10884 if (player->shield_deadly_time_left > 0 &&
10885 !IS_INDESTRUCTIBLE(bad_element))
10886 Bang(kill_x, kill_y);
10887 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10888 KillPlayer(player);
10891 Bang(good_x, good_y);
10895 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10897 int i, kill_x = -1, kill_y = -1;
10898 int bad_element = Feld[bad_x][bad_y];
10899 static int test_xy[4][2] =
10906 static int touch_dir[4] =
10908 MV_LEFT | MV_RIGHT,
10913 static int test_dir[4] =
10921 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10924 for (i = 0; i < NUM_DIRECTIONS; i++)
10926 int test_x, test_y, test_move_dir, test_element;
10928 test_x = bad_x + test_xy[i][0];
10929 test_y = bad_y + test_xy[i][1];
10930 if (!IN_LEV_FIELD(test_x, test_y))
10934 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10936 test_element = Feld[test_x][test_y];
10938 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10939 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10941 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10942 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10944 /* good thing is player or penguin that does not move away */
10945 if (IS_PLAYER(test_x, test_y))
10947 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10949 if (bad_element == EL_ROBOT && player->is_moving)
10950 continue; /* robot does not kill player if he is moving */
10952 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10954 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10955 continue; /* center and border element do not touch */
10962 else if (test_element == EL_PENGUIN)
10971 if (kill_x != -1 || kill_y != -1)
10973 if (IS_PLAYER(kill_x, kill_y))
10975 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10977 if (player->shield_deadly_time_left > 0 &&
10978 !IS_INDESTRUCTIBLE(bad_element))
10979 Bang(bad_x, bad_y);
10980 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10981 KillPlayer(player);
10984 Bang(kill_x, kill_y);
10988 void TestIfPlayerTouchesBadThing(int x, int y)
10990 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10993 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10995 TestIfGoodThingHitsBadThing(x, y, move_dir);
10998 void TestIfBadThingTouchesPlayer(int x, int y)
11000 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11003 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11005 TestIfBadThingHitsGoodThing(x, y, move_dir);
11008 void TestIfFriendTouchesBadThing(int x, int y)
11010 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11013 void TestIfBadThingTouchesFriend(int x, int y)
11015 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11018 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11020 int i, kill_x = bad_x, kill_y = bad_y;
11021 static int xy[4][2] =
11029 for (i = 0; i < NUM_DIRECTIONS; i++)
11033 x = bad_x + xy[i][0];
11034 y = bad_y + xy[i][1];
11035 if (!IN_LEV_FIELD(x, y))
11038 element = Feld[x][y];
11039 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11040 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11048 if (kill_x != bad_x || kill_y != bad_y)
11049 Bang(bad_x, bad_y);
11052 void KillPlayer(struct PlayerInfo *player)
11054 int jx = player->jx, jy = player->jy;
11056 if (!player->active)
11059 /* remove accessible field at the player's position */
11060 Feld[jx][jy] = EL_EMPTY;
11062 /* deactivate shield (else Bang()/Explode() would not work right) */
11063 player->shield_normal_time_left = 0;
11064 player->shield_deadly_time_left = 0;
11067 BuryPlayer(player);
11070 static void KillPlayerUnlessEnemyProtected(int x, int y)
11072 if (!PLAYER_ENEMY_PROTECTED(x, y))
11073 KillPlayer(PLAYERINFO(x, y));
11076 static void KillPlayerUnlessExplosionProtected(int x, int y)
11078 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11079 KillPlayer(PLAYERINFO(x, y));
11082 void BuryPlayer(struct PlayerInfo *player)
11084 int jx = player->jx, jy = player->jy;
11086 if (!player->active)
11089 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11090 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11092 player->GameOver = TRUE;
11093 RemovePlayer(player);
11096 void RemovePlayer(struct PlayerInfo *player)
11098 int jx = player->jx, jy = player->jy;
11099 int i, found = FALSE;
11101 player->present = FALSE;
11102 player->active = FALSE;
11104 if (!ExplodeField[jx][jy])
11105 StorePlayer[jx][jy] = 0;
11107 if (player->is_moving)
11108 DrawLevelField(player->last_jx, player->last_jy);
11110 for (i = 0; i < MAX_PLAYERS; i++)
11111 if (stored_player[i].active)
11115 AllPlayersGone = TRUE;
11121 #if USE_NEW_SNAP_DELAY
11122 static void setFieldForSnapping(int x, int y, int element, int direction)
11124 struct ElementInfo *ei = &element_info[element];
11125 int direction_bit = MV_DIR_TO_BIT(direction);
11126 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11127 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11128 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11130 Feld[x][y] = EL_ELEMENT_SNAPPING;
11131 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11133 ResetGfxAnimation(x, y);
11135 GfxElement[x][y] = element;
11136 GfxAction[x][y] = action;
11137 GfxDir[x][y] = direction;
11138 GfxFrame[x][y] = -1;
11143 =============================================================================
11144 checkDiagonalPushing()
11145 -----------------------------------------------------------------------------
11146 check if diagonal input device direction results in pushing of object
11147 (by checking if the alternative direction is walkable, diggable, ...)
11148 =============================================================================
11151 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11152 int x, int y, int real_dx, int real_dy)
11154 int jx, jy, dx, dy, xx, yy;
11156 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11159 /* diagonal direction: check alternative direction */
11164 xx = jx + (dx == 0 ? real_dx : 0);
11165 yy = jy + (dy == 0 ? real_dy : 0);
11167 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11171 =============================================================================
11173 -----------------------------------------------------------------------------
11174 x, y: field next to player (non-diagonal) to try to dig to
11175 real_dx, real_dy: direction as read from input device (can be diagonal)
11176 =============================================================================
11179 int DigField(struct PlayerInfo *player,
11180 int oldx, int oldy, int x, int y,
11181 int real_dx, int real_dy, int mode)
11183 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11184 boolean player_was_pushing = player->is_pushing;
11185 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11186 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11187 int jx = oldx, jy = oldy;
11188 int dx = x - jx, dy = y - jy;
11189 int nextx = x + dx, nexty = y + dy;
11190 int move_direction = (dx == -1 ? MV_LEFT :
11191 dx == +1 ? MV_RIGHT :
11193 dy == +1 ? MV_DOWN : MV_NONE);
11194 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11195 int dig_side = MV_DIR_OPPOSITE(move_direction);
11196 int old_element = Feld[jx][jy];
11197 #if USE_FIXED_DONT_RUN_INTO
11198 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11204 if (is_player) /* function can also be called by EL_PENGUIN */
11206 if (player->MovPos == 0)
11208 player->is_digging = FALSE;
11209 player->is_collecting = FALSE;
11212 if (player->MovPos == 0) /* last pushing move finished */
11213 player->is_pushing = FALSE;
11215 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11217 player->is_switching = FALSE;
11218 player->push_delay = -1;
11220 return MP_NO_ACTION;
11224 #if !USE_FIXED_DONT_RUN_INTO
11225 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11226 return MP_NO_ACTION;
11229 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11230 old_element = Back[jx][jy];
11232 /* in case of element dropped at player position, check background */
11233 else if (Back[jx][jy] != EL_EMPTY &&
11234 game.engine_version >= VERSION_IDENT(2,2,0,0))
11235 old_element = Back[jx][jy];
11237 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11238 return MP_NO_ACTION; /* field has no opening in this direction */
11240 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11241 return MP_NO_ACTION; /* field has no opening in this direction */
11243 #if USE_FIXED_DONT_RUN_INTO
11244 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11248 Feld[jx][jy] = player->artwork_element;
11249 InitMovingField(jx, jy, MV_DOWN);
11250 Store[jx][jy] = EL_ACID;
11251 ContinueMoving(jx, jy);
11252 BuryPlayer(player);
11254 return MP_DONT_RUN_INTO;
11258 #if USE_FIXED_DONT_RUN_INTO
11259 if (player_can_move && DONT_RUN_INTO(element))
11261 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11263 return MP_DONT_RUN_INTO;
11267 #if USE_FIXED_DONT_RUN_INTO
11268 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11269 return MP_NO_ACTION;
11272 #if !USE_FIXED_DONT_RUN_INTO
11273 element = Feld[x][y];
11276 collect_count = element_info[element].collect_count_initial;
11278 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11279 return MP_NO_ACTION;
11281 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11282 player_can_move = player_can_move_or_snap;
11284 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11285 game.engine_version >= VERSION_IDENT(2,2,0,0))
11287 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11288 player->index_bit, dig_side);
11289 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11290 player->index_bit, dig_side);
11292 if (Feld[x][y] != element) /* field changed by snapping */
11295 return MP_NO_ACTION;
11298 #if USE_PLAYER_GRAVITY
11299 if (player->gravity && is_player && !player->is_auto_moving &&
11300 canFallDown(player) && move_direction != MV_DOWN &&
11301 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11302 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11304 if (game.gravity && is_player && !player->is_auto_moving &&
11305 canFallDown(player) && move_direction != MV_DOWN &&
11306 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11307 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11310 if (player_can_move &&
11311 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11313 int sound_element = SND_ELEMENT(element);
11314 int sound_action = ACTION_WALKING;
11316 if (IS_RND_GATE(element))
11318 if (!player->key[RND_GATE_NR(element)])
11319 return MP_NO_ACTION;
11321 else if (IS_RND_GATE_GRAY(element))
11323 if (!player->key[RND_GATE_GRAY_NR(element)])
11324 return MP_NO_ACTION;
11326 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11328 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11329 return MP_NO_ACTION;
11331 else if (element == EL_EXIT_OPEN ||
11332 element == EL_SP_EXIT_OPEN ||
11333 element == EL_SP_EXIT_OPENING)
11335 sound_action = ACTION_PASSING; /* player is passing exit */
11337 else if (element == EL_EMPTY)
11339 sound_action = ACTION_MOVING; /* nothing to walk on */
11342 /* play sound from background or player, whatever is available */
11343 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11344 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11346 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11348 else if (player_can_move &&
11349 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11351 if (!ACCESS_FROM(element, opposite_direction))
11352 return MP_NO_ACTION; /* field not accessible from this direction */
11354 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11355 return MP_NO_ACTION;
11357 if (IS_EM_GATE(element))
11359 if (!player->key[EM_GATE_NR(element)])
11360 return MP_NO_ACTION;
11362 else if (IS_EM_GATE_GRAY(element))
11364 if (!player->key[EM_GATE_GRAY_NR(element)])
11365 return MP_NO_ACTION;
11367 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11369 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11370 return MP_NO_ACTION;
11372 else if (IS_EMC_GATE(element))
11374 if (!player->key[EMC_GATE_NR(element)])
11375 return MP_NO_ACTION;
11377 else if (IS_EMC_GATE_GRAY(element))
11379 if (!player->key[EMC_GATE_GRAY_NR(element)])
11380 return MP_NO_ACTION;
11382 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11384 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11385 return MP_NO_ACTION;
11387 else if (IS_SP_PORT(element))
11389 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11390 element == EL_SP_GRAVITY_PORT_RIGHT ||
11391 element == EL_SP_GRAVITY_PORT_UP ||
11392 element == EL_SP_GRAVITY_PORT_DOWN)
11393 #if USE_PLAYER_GRAVITY
11394 player->gravity = !player->gravity;
11396 game.gravity = !game.gravity;
11398 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11399 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11400 element == EL_SP_GRAVITY_ON_PORT_UP ||
11401 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11402 #if USE_PLAYER_GRAVITY
11403 player->gravity = TRUE;
11405 game.gravity = TRUE;
11407 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11408 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11409 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11410 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11411 #if USE_PLAYER_GRAVITY
11412 player->gravity = FALSE;
11414 game.gravity = FALSE;
11418 /* automatically move to the next field with double speed */
11419 player->programmed_action = move_direction;
11421 if (player->move_delay_reset_counter == 0)
11423 player->move_delay_reset_counter = 2; /* two double speed steps */
11425 DOUBLE_PLAYER_SPEED(player);
11428 PlayLevelSoundAction(x, y, ACTION_PASSING);
11430 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11434 if (mode != DF_SNAP)
11436 GfxElement[x][y] = GFX_ELEMENT(element);
11437 player->is_digging = TRUE;
11440 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11442 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11443 player->index_bit, dig_side);
11445 if (mode == DF_SNAP)
11447 #if USE_NEW_SNAP_DELAY
11448 if (level.block_snap_field)
11449 setFieldForSnapping(x, y, element, move_direction);
11451 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11453 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11456 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11457 player->index_bit, dig_side);
11460 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11464 if (is_player && mode != DF_SNAP)
11466 GfxElement[x][y] = element;
11467 player->is_collecting = TRUE;
11470 if (element == EL_SPEED_PILL)
11472 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11474 else if (element == EL_EXTRA_TIME && level.time > 0)
11476 TimeLeft += level.extra_time;
11477 DrawGameValue_Time(TimeLeft);
11479 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11481 player->shield_normal_time_left += level.shield_normal_time;
11482 if (element == EL_SHIELD_DEADLY)
11483 player->shield_deadly_time_left += level.shield_deadly_time;
11485 else if (element == EL_DYNAMITE ||
11486 element == EL_EM_DYNAMITE ||
11487 element == EL_SP_DISK_RED)
11489 if (player->inventory_size < MAX_INVENTORY_SIZE)
11490 player->inventory_element[player->inventory_size++] = element;
11492 DrawGameDoorValues();
11494 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11496 player->dynabomb_count++;
11497 player->dynabombs_left++;
11499 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11501 player->dynabomb_size++;
11503 else if (element == EL_DYNABOMB_INCREASE_POWER)
11505 player->dynabomb_xl = TRUE;
11507 else if (IS_KEY(element))
11509 player->key[KEY_NR(element)] = TRUE;
11511 DrawGameDoorValues();
11513 else if (IS_ENVELOPE(element))
11515 player->show_envelope = element;
11517 else if (element == EL_EMC_LENSES)
11519 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11521 RedrawAllInvisibleElementsForLenses();
11523 else if (element == EL_EMC_MAGNIFIER)
11525 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11527 RedrawAllInvisibleElementsForMagnifier();
11529 else if (IS_DROPPABLE(element) ||
11530 IS_THROWABLE(element)) /* can be collected and dropped */
11534 if (collect_count == 0)
11535 player->inventory_infinite_element = element;
11537 for (i = 0; i < collect_count; i++)
11538 if (player->inventory_size < MAX_INVENTORY_SIZE)
11539 player->inventory_element[player->inventory_size++] = element;
11541 DrawGameDoorValues();
11543 else if (collect_count > 0)
11545 local_player->gems_still_needed -= collect_count;
11546 if (local_player->gems_still_needed < 0)
11547 local_player->gems_still_needed = 0;
11549 DrawGameValue_Emeralds(local_player->gems_still_needed);
11552 RaiseScoreElement(element);
11553 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11556 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11557 player->index_bit, dig_side);
11559 if (mode == DF_SNAP)
11561 #if USE_NEW_SNAP_DELAY
11562 if (level.block_snap_field)
11563 setFieldForSnapping(x, y, element, move_direction);
11565 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11567 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11570 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11571 player->index_bit, dig_side);
11574 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11576 if (mode == DF_SNAP && element != EL_BD_ROCK)
11577 return MP_NO_ACTION;
11579 if (CAN_FALL(element) && dy)
11580 return MP_NO_ACTION;
11582 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11583 !(element == EL_SPRING && level.use_spring_bug))
11584 return MP_NO_ACTION;
11586 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11587 ((move_direction & MV_VERTICAL &&
11588 ((element_info[element].move_pattern & MV_LEFT &&
11589 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11590 (element_info[element].move_pattern & MV_RIGHT &&
11591 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11592 (move_direction & MV_HORIZONTAL &&
11593 ((element_info[element].move_pattern & MV_UP &&
11594 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11595 (element_info[element].move_pattern & MV_DOWN &&
11596 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11597 return MP_NO_ACTION;
11599 /* do not push elements already moving away faster than player */
11600 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11601 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11602 return MP_NO_ACTION;
11604 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11606 if (player->push_delay_value == -1 || !player_was_pushing)
11607 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11609 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11611 if (player->push_delay_value == -1)
11612 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11614 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11616 if (!player->is_pushing)
11617 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11620 player->is_pushing = TRUE;
11621 player->is_active = TRUE;
11623 if (!(IN_LEV_FIELD(nextx, nexty) &&
11624 (IS_FREE(nextx, nexty) ||
11625 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11626 IS_SB_ELEMENT(element)))))
11627 return MP_NO_ACTION;
11629 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11630 return MP_NO_ACTION;
11632 if (player->push_delay == -1) /* new pushing; restart delay */
11633 player->push_delay = 0;
11635 if (player->push_delay < player->push_delay_value &&
11636 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11637 element != EL_SPRING && element != EL_BALLOON)
11639 /* make sure that there is no move delay before next try to push */
11640 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11641 player->move_delay = 0;
11643 return MP_NO_ACTION;
11646 if (IS_SB_ELEMENT(element))
11648 if (element == EL_SOKOBAN_FIELD_FULL)
11650 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11651 local_player->sokobanfields_still_needed++;
11654 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11656 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11657 local_player->sokobanfields_still_needed--;
11660 Feld[x][y] = EL_SOKOBAN_OBJECT;
11662 if (Back[x][y] == Back[nextx][nexty])
11663 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11664 else if (Back[x][y] != 0)
11665 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11668 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11671 if (local_player->sokobanfields_still_needed == 0 &&
11672 game.emulation == EMU_SOKOBAN)
11674 PlayerWins(player);
11676 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11680 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11682 InitMovingField(x, y, move_direction);
11683 GfxAction[x][y] = ACTION_PUSHING;
11685 if (mode == DF_SNAP)
11686 ContinueMoving(x, y);
11688 MovPos[x][y] = (dx != 0 ? dx : dy);
11690 Pushed[x][y] = TRUE;
11691 Pushed[nextx][nexty] = TRUE;
11693 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11694 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11696 player->push_delay_value = -1; /* get new value later */
11698 /* check for element change _after_ element has been pushed */
11699 if (game.use_change_when_pushing_bug)
11701 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11702 player->index_bit, dig_side);
11703 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11704 player->index_bit, dig_side);
11707 else if (IS_SWITCHABLE(element))
11709 if (PLAYER_SWITCHING(player, x, y))
11711 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11712 player->index_bit, dig_side);
11717 player->is_switching = TRUE;
11718 player->switch_x = x;
11719 player->switch_y = y;
11721 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11723 if (element == EL_ROBOT_WHEEL)
11725 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11729 DrawLevelField(x, y);
11731 else if (element == EL_SP_TERMINAL)
11735 SCAN_PLAYFIELD(xx, yy)
11737 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11739 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11740 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11743 else if (IS_BELT_SWITCH(element))
11745 ToggleBeltSwitch(x, y);
11747 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11748 element == EL_SWITCHGATE_SWITCH_DOWN)
11750 ToggleSwitchgateSwitch(x, y);
11752 else if (element == EL_LIGHT_SWITCH ||
11753 element == EL_LIGHT_SWITCH_ACTIVE)
11755 ToggleLightSwitch(x, y);
11757 else if (element == EL_TIMEGATE_SWITCH)
11759 ActivateTimegateSwitch(x, y);
11761 else if (element == EL_BALLOON_SWITCH_LEFT ||
11762 element == EL_BALLOON_SWITCH_RIGHT ||
11763 element == EL_BALLOON_SWITCH_UP ||
11764 element == EL_BALLOON_SWITCH_DOWN ||
11765 element == EL_BALLOON_SWITCH_NONE ||
11766 element == EL_BALLOON_SWITCH_ANY)
11768 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11769 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11770 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11771 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11772 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11775 else if (element == EL_LAMP)
11777 Feld[x][y] = EL_LAMP_ACTIVE;
11778 local_player->lights_still_needed--;
11780 ResetGfxAnimation(x, y);
11781 DrawLevelField(x, y);
11783 else if (element == EL_TIME_ORB_FULL)
11785 Feld[x][y] = EL_TIME_ORB_EMPTY;
11787 if (level.time > 0 || level.use_time_orb_bug)
11789 TimeLeft += level.time_orb_time;
11790 DrawGameValue_Time(TimeLeft);
11793 ResetGfxAnimation(x, y);
11794 DrawLevelField(x, y);
11796 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11797 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11801 game.ball_state = !game.ball_state;
11803 SCAN_PLAYFIELD(xx, yy)
11805 int e = Feld[xx][yy];
11807 if (game.ball_state)
11809 if (e == EL_EMC_MAGIC_BALL)
11810 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11811 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11812 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11816 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11817 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11818 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11819 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11824 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11825 player->index_bit, dig_side);
11827 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11828 player->index_bit, dig_side);
11830 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11831 player->index_bit, dig_side);
11837 if (!PLAYER_SWITCHING(player, x, y))
11839 player->is_switching = TRUE;
11840 player->switch_x = x;
11841 player->switch_y = y;
11843 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11844 player->index_bit, dig_side);
11845 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11846 player->index_bit, dig_side);
11848 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11849 player->index_bit, dig_side);
11850 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11851 player->index_bit, dig_side);
11854 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11855 player->index_bit, dig_side);
11856 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11857 player->index_bit, dig_side);
11859 return MP_NO_ACTION;
11862 player->push_delay = -1;
11864 if (is_player) /* function can also be called by EL_PENGUIN */
11866 if (Feld[x][y] != element) /* really digged/collected something */
11868 player->is_collecting = !player->is_digging;
11869 player->is_active = TRUE;
11876 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11878 int jx = player->jx, jy = player->jy;
11879 int x = jx + dx, y = jy + dy;
11880 int snap_direction = (dx == -1 ? MV_LEFT :
11881 dx == +1 ? MV_RIGHT :
11883 dy == +1 ? MV_DOWN : MV_NONE);
11884 boolean can_continue_snapping = (level.continuous_snapping &&
11885 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
11887 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11890 if (!player->active || !IN_LEV_FIELD(x, y))
11898 if (player->MovPos == 0)
11899 player->is_pushing = FALSE;
11901 player->is_snapping = FALSE;
11903 if (player->MovPos == 0)
11905 player->is_moving = FALSE;
11906 player->is_digging = FALSE;
11907 player->is_collecting = FALSE;
11913 #if USE_NEW_CONTINUOUS_SNAPPING
11914 /* prevent snapping with already pressed snap key when not allowed */
11915 if (player->is_snapping && !can_continue_snapping)
11918 if (player->is_snapping)
11922 player->MovDir = snap_direction;
11924 if (player->MovPos == 0)
11926 player->is_moving = FALSE;
11927 player->is_digging = FALSE;
11928 player->is_collecting = FALSE;
11931 player->is_dropping = FALSE;
11932 player->is_dropping_pressed = FALSE;
11933 player->drop_pressed_delay = 0;
11935 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11938 player->is_snapping = TRUE;
11939 player->is_active = TRUE;
11941 if (player->MovPos == 0)
11943 player->is_moving = FALSE;
11944 player->is_digging = FALSE;
11945 player->is_collecting = FALSE;
11948 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11949 DrawLevelField(player->last_jx, player->last_jy);
11951 DrawLevelField(x, y);
11956 boolean DropElement(struct PlayerInfo *player)
11958 int old_element, new_element;
11959 int dropx = player->jx, dropy = player->jy;
11960 int drop_direction = player->MovDir;
11961 int drop_side = drop_direction;
11962 int drop_element = (player->inventory_size > 0 ?
11963 player->inventory_element[player->inventory_size - 1] :
11964 player->inventory_infinite_element != EL_UNDEFINED ?
11965 player->inventory_infinite_element :
11966 player->dynabombs_left > 0 ?
11967 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11970 player->is_dropping_pressed = TRUE;
11972 /* do not drop an element on top of another element; when holding drop key
11973 pressed without moving, dropped element must move away before the next
11974 element can be dropped (this is especially important if the next element
11975 is dynamite, which can be placed on background for historical reasons) */
11976 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11979 if (IS_THROWABLE(drop_element))
11981 dropx += GET_DX_FROM_DIR(drop_direction);
11982 dropy += GET_DY_FROM_DIR(drop_direction);
11984 if (!IN_LEV_FIELD(dropx, dropy))
11988 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11989 new_element = drop_element; /* default: no change when dropping */
11991 /* check if player is active, not moving and ready to drop */
11992 if (!player->active || player->MovPos || player->drop_delay > 0)
11995 /* check if player has anything that can be dropped */
11996 if (new_element == EL_UNDEFINED)
11999 /* check if drop key was pressed long enough for EM style dynamite */
12000 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12003 /* check if anything can be dropped at the current position */
12004 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12007 /* collected custom elements can only be dropped on empty fields */
12008 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12011 if (old_element != EL_EMPTY)
12012 Back[dropx][dropy] = old_element; /* store old element on this field */
12014 ResetGfxAnimation(dropx, dropy);
12015 ResetRandomAnimationValue(dropx, dropy);
12017 if (player->inventory_size > 0 ||
12018 player->inventory_infinite_element != EL_UNDEFINED)
12020 if (player->inventory_size > 0)
12022 player->inventory_size--;
12024 DrawGameDoorValues();
12026 if (new_element == EL_DYNAMITE)
12027 new_element = EL_DYNAMITE_ACTIVE;
12028 else if (new_element == EL_EM_DYNAMITE)
12029 new_element = EL_EM_DYNAMITE_ACTIVE;
12030 else if (new_element == EL_SP_DISK_RED)
12031 new_element = EL_SP_DISK_RED_ACTIVE;
12034 Feld[dropx][dropy] = new_element;
12036 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12037 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12038 el2img(Feld[dropx][dropy]), 0);
12040 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12042 /* needed if previous element just changed to "empty" in the last frame */
12043 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12045 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12046 player->index_bit, drop_side);
12047 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12049 player->index_bit, drop_side);
12051 TestIfElementTouchesCustomElement(dropx, dropy);
12053 else /* player is dropping a dyna bomb */
12055 player->dynabombs_left--;
12057 Feld[dropx][dropy] = new_element;
12059 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12060 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12061 el2img(Feld[dropx][dropy]), 0);
12063 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12066 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12067 InitField_WithBug1(dropx, dropy, FALSE);
12069 new_element = Feld[dropx][dropy]; /* element might have changed */
12071 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12072 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12074 int move_direction, nextx, nexty;
12076 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12077 MovDir[dropx][dropy] = drop_direction;
12079 move_direction = MovDir[dropx][dropy];
12080 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12081 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12083 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12085 #if USE_FIX_IMPACT_COLLISION
12086 /* do not cause impact style collision by dropping elements that can fall */
12087 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12089 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12093 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12094 player->is_dropping = TRUE;
12096 player->drop_pressed_delay = 0;
12097 player->is_dropping_pressed = FALSE;
12099 player->drop_x = dropx;
12100 player->drop_y = dropy;
12105 /* ------------------------------------------------------------------------- */
12106 /* game sound playing functions */
12107 /* ------------------------------------------------------------------------- */
12109 static int *loop_sound_frame = NULL;
12110 static int *loop_sound_volume = NULL;
12112 void InitPlayLevelSound()
12114 int num_sounds = getSoundListSize();
12116 checked_free(loop_sound_frame);
12117 checked_free(loop_sound_volume);
12119 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12120 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12123 static void PlayLevelSound(int x, int y, int nr)
12125 int sx = SCREENX(x), sy = SCREENY(y);
12126 int volume, stereo_position;
12127 int max_distance = 8;
12128 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12130 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12131 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12134 if (!IN_LEV_FIELD(x, y) ||
12135 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12136 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12139 volume = SOUND_MAX_VOLUME;
12141 if (!IN_SCR_FIELD(sx, sy))
12143 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12144 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12146 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12149 stereo_position = (SOUND_MAX_LEFT +
12150 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12151 (SCR_FIELDX + 2 * max_distance));
12153 if (IS_LOOP_SOUND(nr))
12155 /* This assures that quieter loop sounds do not overwrite louder ones,
12156 while restarting sound volume comparison with each new game frame. */
12158 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12161 loop_sound_volume[nr] = volume;
12162 loop_sound_frame[nr] = FrameCounter;
12165 PlaySoundExt(nr, volume, stereo_position, type);
12168 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12170 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12171 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12172 y < LEVELY(BY1) ? LEVELY(BY1) :
12173 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12177 static void PlayLevelSoundAction(int x, int y, int action)
12179 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12182 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12184 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12186 if (sound_effect != SND_UNDEFINED)
12187 PlayLevelSound(x, y, sound_effect);
12190 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12193 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12195 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12196 PlayLevelSound(x, y, sound_effect);
12199 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12201 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12203 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12204 PlayLevelSound(x, y, sound_effect);
12207 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12209 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12211 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12212 StopSound(sound_effect);
12215 static void PlayLevelMusic()
12217 if (levelset.music[level_nr] != MUS_UNDEFINED)
12218 PlayMusic(levelset.music[level_nr]); /* from config file */
12220 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12223 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
12225 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12226 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
12227 int x = xx - 1 - offset;
12228 int y = yy - 1 - offset;
12233 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12237 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12241 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12245 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12249 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12253 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12257 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12260 case SAMPLE_android_clone:
12261 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12264 case SAMPLE_android_move:
12265 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12268 case SAMPLE_spring:
12269 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12273 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12277 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12280 case SAMPLE_eater_eat:
12281 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12285 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12288 case SAMPLE_collect:
12289 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12292 case SAMPLE_diamond:
12293 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12296 case SAMPLE_squash:
12297 /* !!! CHECK THIS !!! */
12299 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12301 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12305 case SAMPLE_wonderfall:
12306 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12310 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12314 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12318 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12322 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12326 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12330 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12333 case SAMPLE_wonder:
12334 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12338 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12341 case SAMPLE_exit_open:
12342 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12345 case SAMPLE_exit_leave:
12346 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12349 case SAMPLE_dynamite:
12350 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12354 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12358 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12362 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12366 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12370 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12374 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12378 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12384 void ChangeTime(int value)
12386 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
12390 /* EMC game engine uses value from time counter of RND game engine */
12391 level.native_em_level->lev->time = *time;
12393 DrawGameValue_Time(*time);
12396 void RaiseScore(int value)
12398 /* EMC game engine and RND game engine have separate score counters */
12399 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
12400 &level.native_em_level->lev->score : &local_player->score);
12404 DrawGameValue_Score(*score);
12408 void RaiseScore(int value)
12410 local_player->score += value;
12412 DrawGameValue_Score(local_player->score);
12415 void RaiseScoreElement(int element)
12420 case EL_BD_DIAMOND:
12421 case EL_EMERALD_YELLOW:
12422 case EL_EMERALD_RED:
12423 case EL_EMERALD_PURPLE:
12424 case EL_SP_INFOTRON:
12425 RaiseScore(level.score[SC_EMERALD]);
12428 RaiseScore(level.score[SC_DIAMOND]);
12431 RaiseScore(level.score[SC_CRYSTAL]);
12434 RaiseScore(level.score[SC_PEARL]);
12437 case EL_BD_BUTTERFLY:
12438 case EL_SP_ELECTRON:
12439 RaiseScore(level.score[SC_BUG]);
12442 case EL_BD_FIREFLY:
12443 case EL_SP_SNIKSNAK:
12444 RaiseScore(level.score[SC_SPACESHIP]);
12447 case EL_DARK_YAMYAM:
12448 RaiseScore(level.score[SC_YAMYAM]);
12451 RaiseScore(level.score[SC_ROBOT]);
12454 RaiseScore(level.score[SC_PACMAN]);
12457 RaiseScore(level.score[SC_NUT]);
12460 case EL_EM_DYNAMITE:
12461 case EL_SP_DISK_RED:
12462 case EL_DYNABOMB_INCREASE_NUMBER:
12463 case EL_DYNABOMB_INCREASE_SIZE:
12464 case EL_DYNABOMB_INCREASE_POWER:
12465 RaiseScore(level.score[SC_DYNAMITE]);
12467 case EL_SHIELD_NORMAL:
12468 case EL_SHIELD_DEADLY:
12469 RaiseScore(level.score[SC_SHIELD]);
12471 case EL_EXTRA_TIME:
12472 RaiseScore(level.extra_time_score);
12486 RaiseScore(level.score[SC_KEY]);
12489 RaiseScore(element_info[element].collect_score);
12494 void RequestQuitGame(boolean ask_if_really_quit)
12496 if (AllPlayersGone ||
12497 !ask_if_really_quit ||
12498 level_editor_test_game ||
12499 Request("Do you really want to quit the game ?",
12500 REQ_ASK | REQ_STAY_CLOSED))
12502 #if defined(NETWORK_AVALIABLE)
12503 if (options.network)
12504 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12508 if (!ask_if_really_quit || level_editor_test_game)
12510 game_status = GAME_MODE_MAIN;
12516 FadeOut(REDRAW_FIELD);
12518 game_status = GAME_MODE_MAIN;
12520 DrawAndFadeInMainMenu(REDRAW_FIELD);
12526 if (tape.playing && tape.deactivate_display)
12527 TapeDeactivateDisplayOff(TRUE);
12529 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12531 if (tape.playing && tape.deactivate_display)
12532 TapeDeactivateDisplayOn();
12537 /* ------------------------------------------------------------------------- */
12538 /* random generator functions */
12539 /* ------------------------------------------------------------------------- */
12541 unsigned int InitEngineRandom_RND(long seed)
12543 game.num_random_calls = 0;
12546 unsigned int rnd_seed = InitEngineRandom(seed);
12548 printf("::: START RND: %d\n", rnd_seed);
12553 return InitEngineRandom(seed);
12559 unsigned int RND(int max)
12563 game.num_random_calls++;
12565 return GetEngineRandom(max);
12572 /* ------------------------------------------------------------------------- */
12573 /* game engine snapshot handling functions */
12574 /* ------------------------------------------------------------------------- */
12576 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
12578 struct EngineSnapshotInfo
12580 /* runtime values for custom element collect score */
12581 int collect_score[NUM_CUSTOM_ELEMENTS];
12583 /* runtime values for group element choice position */
12584 int choice_pos[NUM_GROUP_ELEMENTS];
12586 /* runtime values for belt position animations */
12587 int belt_graphic[4 * NUM_BELT_PARTS];
12588 int belt_anim_mode[4 * NUM_BELT_PARTS];
12591 struct EngineSnapshotNodeInfo
12598 static struct EngineSnapshotInfo engine_snapshot_rnd;
12599 static ListNode *engine_snapshot_list = NULL;
12600 static char *snapshot_level_identifier = NULL;
12601 static int snapshot_level_nr = -1;
12603 void FreeEngineSnapshot()
12605 while (engine_snapshot_list != NULL)
12606 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
12609 setString(&snapshot_level_identifier, NULL);
12610 snapshot_level_nr = -1;
12613 static void SaveEngineSnapshotValues_RND()
12615 static int belt_base_active_element[4] =
12617 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
12618 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
12619 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
12620 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
12624 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12626 int element = EL_CUSTOM_START + i;
12628 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
12631 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12633 int element = EL_GROUP_START + i;
12635 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
12638 for (i = 0; i < 4; i++)
12640 for (j = 0; j < NUM_BELT_PARTS; j++)
12642 int element = belt_base_active_element[i] + j;
12643 int graphic = el2img(element);
12644 int anim_mode = graphic_info[graphic].anim_mode;
12646 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
12647 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
12652 static void LoadEngineSnapshotValues_RND()
12654 unsigned long num_random_calls = game.num_random_calls;
12657 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12659 int element = EL_CUSTOM_START + i;
12661 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
12664 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12666 int element = EL_GROUP_START + i;
12668 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
12671 for (i = 0; i < 4; i++)
12673 for (j = 0; j < NUM_BELT_PARTS; j++)
12675 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
12676 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
12678 graphic_info[graphic].anim_mode = anim_mode;
12682 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
12684 InitRND(tape.random_seed);
12685 for (i = 0; i < num_random_calls; i++)
12689 if (game.num_random_calls != num_random_calls)
12691 Error(ERR_RETURN, "number of random calls out of sync");
12692 Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
12693 Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
12694 Error(ERR_EXIT, "this should not happen -- please debug");
12698 static void SaveEngineSnapshotBuffer(void *buffer, int size)
12700 struct EngineSnapshotNodeInfo *bi =
12701 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
12703 bi->buffer_orig = buffer;
12704 bi->buffer_copy = checked_malloc(size);
12707 memcpy(bi->buffer_copy, buffer, size);
12709 addNodeToList(&engine_snapshot_list, NULL, bi);
12712 void SaveEngineSnapshot()
12714 FreeEngineSnapshot(); /* free previous snapshot, if needed */
12716 if (level_editor_test_game) /* do not save snapshots from editor */
12719 /* copy some special values to a structure better suited for the snapshot */
12721 SaveEngineSnapshotValues_RND();
12722 SaveEngineSnapshotValues_EM();
12724 /* save values stored in special snapshot structure */
12726 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
12727 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
12729 /* save further RND engine values */
12731 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
12732 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
12733 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
12735 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
12736 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
12737 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
12738 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
12740 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
12741 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
12742 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
12743 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
12744 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
12746 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
12747 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
12748 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
12750 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
12752 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
12754 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
12755 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
12757 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
12758 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
12759 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
12760 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
12761 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
12762 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
12763 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
12764 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
12765 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
12766 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
12767 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
12768 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
12769 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
12770 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
12771 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
12772 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
12773 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
12774 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
12776 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
12777 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
12779 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
12780 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
12781 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
12783 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
12784 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
12786 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
12787 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
12788 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
12789 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
12790 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
12792 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
12793 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
12795 /* save level identification information */
12797 setString(&snapshot_level_identifier, leveldir_current->identifier);
12798 snapshot_level_nr = level_nr;
12801 ListNode *node = engine_snapshot_list;
12804 while (node != NULL)
12806 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
12811 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
12815 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
12817 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
12820 void LoadEngineSnapshot()
12822 ListNode *node = engine_snapshot_list;
12824 if (engine_snapshot_list == NULL)
12827 while (node != NULL)
12829 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
12834 /* restore special values from snapshot structure */
12836 LoadEngineSnapshotValues_RND();
12837 LoadEngineSnapshotValues_EM();
12840 boolean CheckEngineSnapshot()
12842 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
12843 snapshot_level_nr == level_nr);
12847 /* ---------- new game button stuff ---------------------------------------- */
12849 /* graphic position values for game buttons */
12850 #define GAME_BUTTON_XSIZE 30
12851 #define GAME_BUTTON_YSIZE 30
12852 #define GAME_BUTTON_XPOS 5
12853 #define GAME_BUTTON_YPOS 215
12854 #define SOUND_BUTTON_XPOS 5
12855 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12857 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12858 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12859 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12860 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12861 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12862 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12869 } gamebutton_info[NUM_GAME_BUTTONS] =
12872 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12877 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12878 GAME_CTRL_ID_PAUSE,
12882 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12887 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12888 SOUND_CTRL_ID_MUSIC,
12889 "background music on/off"
12892 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12893 SOUND_CTRL_ID_LOOPS,
12894 "sound loops on/off"
12897 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12898 SOUND_CTRL_ID_SIMPLE,
12899 "normal sounds on/off"
12903 void CreateGameButtons()
12907 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12909 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12910 struct GadgetInfo *gi;
12913 unsigned long event_mask;
12914 int gd_xoffset, gd_yoffset;
12915 int gd_x1, gd_x2, gd_y1, gd_y2;
12918 gd_xoffset = gamebutton_info[i].x;
12919 gd_yoffset = gamebutton_info[i].y;
12920 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12921 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12923 if (id == GAME_CTRL_ID_STOP ||
12924 id == GAME_CTRL_ID_PAUSE ||
12925 id == GAME_CTRL_ID_PLAY)
12927 button_type = GD_TYPE_NORMAL_BUTTON;
12929 event_mask = GD_EVENT_RELEASED;
12930 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12931 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12935 button_type = GD_TYPE_CHECK_BUTTON;
12937 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12938 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12939 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12940 event_mask = GD_EVENT_PRESSED;
12941 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12942 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12945 gi = CreateGadget(GDI_CUSTOM_ID, id,
12946 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12947 GDI_X, DX + gd_xoffset,
12948 GDI_Y, DY + gd_yoffset,
12949 GDI_WIDTH, GAME_BUTTON_XSIZE,
12950 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12951 GDI_TYPE, button_type,
12952 GDI_STATE, GD_BUTTON_UNPRESSED,
12953 GDI_CHECKED, checked,
12954 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12955 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12956 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12957 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12958 GDI_EVENT_MASK, event_mask,
12959 GDI_CALLBACK_ACTION, HandleGameButtons,
12963 Error(ERR_EXIT, "cannot create gadget");
12965 game_gadget[id] = gi;
12969 void FreeGameButtons()
12973 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12974 FreeGadget(game_gadget[i]);
12977 static void MapGameButtons()
12981 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12982 MapGadget(game_gadget[i]);
12985 void UnmapGameButtons()
12989 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12990 UnmapGadget(game_gadget[i]);
12993 static void HandleGameButtons(struct GadgetInfo *gi)
12995 int id = gi->custom_id;
12997 if (game_status != GAME_MODE_PLAYING)
13002 case GAME_CTRL_ID_STOP:
13006 RequestQuitGame(TRUE);
13009 case GAME_CTRL_ID_PAUSE:
13010 if (options.network)
13012 #if defined(NETWORK_AVALIABLE)
13014 SendToServer_ContinuePlaying();
13016 SendToServer_PausePlaying();
13020 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13023 case GAME_CTRL_ID_PLAY:
13026 #if defined(NETWORK_AVALIABLE)
13027 if (options.network)
13028 SendToServer_ContinuePlaying();
13032 tape.pausing = FALSE;
13033 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13038 case SOUND_CTRL_ID_MUSIC:
13039 if (setup.sound_music)
13041 setup.sound_music = FALSE;
13044 else if (audio.music_available)
13046 setup.sound = setup.sound_music = TRUE;
13048 SetAudioMode(setup.sound);
13054 case SOUND_CTRL_ID_LOOPS:
13055 if (setup.sound_loops)
13056 setup.sound_loops = FALSE;
13057 else if (audio.loops_available)
13059 setup.sound = setup.sound_loops = TRUE;
13060 SetAudioMode(setup.sound);
13064 case SOUND_CTRL_ID_SIMPLE:
13065 if (setup.sound_simple)
13066 setup.sound_simple = FALSE;
13067 else if (audio.sound_available)
13069 setup.sound = setup.sound_simple = TRUE;
13070 SetAudioMode(setup.sound);