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 (level.game_engine_type == GAME_ENGINE_TYPE_RND)
2739 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
2741 /* close exit door after last player */
2742 if (AllPlayersGone &&
2743 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2744 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2746 int element = Feld[ExitX][ExitY];
2748 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2749 EL_SP_EXIT_CLOSING);
2751 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2754 /* player disappears */
2755 DrawLevelField(ExitX, ExitY);
2758 for (i = 0; i < MAX_PLAYERS; i++)
2760 struct PlayerInfo *player = &stored_player[i];
2762 if (player->present)
2764 RemovePlayer(player);
2766 /* player disappears */
2767 DrawLevelField(player->jx, player->jy);
2772 PlaySound(SND_GAME_WINNING);
2775 if (game_over_delay > 0)
2782 if (time != time_final)
2784 int time_to_go = ABS(time_final - time);
2785 int time_count_dir = (time < time_final ? +1 : -1);
2786 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
2788 time += time_count_steps * time_count_dir;
2789 score += time_count_steps * level.score[SC_TIME_BONUS];
2791 DrawGameValue_Time(time);
2792 DrawGameValue_Score(score);
2794 if (time == time_final)
2795 StopSound(SND_GAME_LEVELTIME_BONUS);
2796 else if (setup.sound_loops)
2797 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
2799 PlaySound(SND_GAME_LEVELTIME_BONUS);
2806 boolean raise_level = FALSE;
2808 CloseDoor(DOOR_CLOSE_1);
2810 if (local_player->LevelSolved_SaveTape)
2817 SaveTapeChecked(tape.level_nr); /* ask to save tape */
2819 SaveTape(tape.level_nr); /* ask to save tape */
2823 if (level_editor_test_game)
2825 game_status = GAME_MODE_MAIN;
2832 if (!local_player->LevelSolved_SaveScore)
2834 FadeOut(REDRAW_FIELD);
2836 game_status = GAME_MODE_MAIN;
2838 DrawAndFadeInMainMenu(REDRAW_FIELD);
2843 if (level_nr == leveldir_current->handicap_level)
2845 leveldir_current->handicap_level++;
2846 SaveLevelSetup_SeriesInfo();
2849 if (level_nr < leveldir_current->last_level)
2850 raise_level = TRUE; /* advance to next level */
2852 if ((hi_pos = NewHiScore()) >= 0)
2854 game_status = GAME_MODE_SCORES;
2856 DrawHallOfFame(hi_pos);
2866 FadeOut(REDRAW_FIELD);
2868 game_status = GAME_MODE_MAIN;
2876 DrawAndFadeInMainMenu(REDRAW_FIELD);
2885 LoadScore(level_nr);
2887 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2888 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
2891 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2893 if (local_player->score_final > highscore[k].Score)
2895 /* player has made it to the hall of fame */
2897 if (k < MAX_SCORE_ENTRIES - 1)
2899 int m = MAX_SCORE_ENTRIES - 1;
2902 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2903 if (strEqual(setup.player_name, highscore[l].Name))
2905 if (m == k) /* player's new highscore overwrites his old one */
2909 for (l = m; l > k; l--)
2911 strcpy(highscore[l].Name, highscore[l - 1].Name);
2912 highscore[l].Score = highscore[l - 1].Score;
2919 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2920 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2921 highscore[k].Score = local_player->score_final;
2927 else if (!strncmp(setup.player_name, highscore[k].Name,
2928 MAX_PLAYER_NAME_LEN))
2929 break; /* player already there with a higher score */
2935 SaveScore(level_nr);
2940 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
2942 int element = Feld[x][y];
2943 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2944 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2945 int horiz_move = (dx != 0);
2946 int sign = (horiz_move ? dx : dy);
2947 int step = sign * element_info[element].move_stepsize;
2949 /* special values for move stepsize for spring and things on conveyor belt */
2952 if (CAN_FALL(element) &&
2953 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2954 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2955 else if (element == EL_SPRING)
2956 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2962 inline static int getElementMoveStepsize(int x, int y)
2964 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
2967 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2969 if (player->GfxAction != action || player->GfxDir != dir)
2972 printf("Player frame reset! (%d => %d, %d => %d)\n",
2973 player->GfxAction, action, player->GfxDir, dir);
2976 player->GfxAction = action;
2977 player->GfxDir = dir;
2979 player->StepFrame = 0;
2983 #if USE_GFX_RESET_GFX_ANIMATION
2984 static void ResetGfxFrame(int x, int y, boolean redraw)
2986 int element = Feld[x][y];
2987 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2988 int last_gfx_frame = GfxFrame[x][y];
2990 if (graphic_info[graphic].anim_global_sync)
2991 GfxFrame[x][y] = FrameCounter;
2992 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2993 GfxFrame[x][y] = CustomValue[x][y];
2994 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2995 GfxFrame[x][y] = element_info[element].collect_score;
2996 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
2997 GfxFrame[x][y] = ChangeDelay[x][y];
2999 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3000 DrawLevelGraphicAnimation(x, y, graphic);
3004 static void ResetGfxAnimation(int x, int y)
3006 GfxAction[x][y] = ACTION_DEFAULT;
3007 GfxDir[x][y] = MovDir[x][y];
3010 #if USE_GFX_RESET_GFX_ANIMATION
3011 ResetGfxFrame(x, y, FALSE);
3015 static void ResetRandomAnimationValue(int x, int y)
3017 GfxRandom[x][y] = INIT_GFX_RANDOM();
3020 void InitMovingField(int x, int y, int direction)
3022 int element = Feld[x][y];
3023 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3024 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3027 boolean is_moving_before, is_moving_after;
3029 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3032 /* check if element was/is moving or being moved before/after mode change */
3034 is_moving_before = WasJustMoving[x][y];
3036 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3038 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3040 /* reset animation only for moving elements which change direction of moving
3041 or which just started or stopped moving
3042 (else CEs with property "can move" / "not moving" are reset each frame) */
3043 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3045 if (is_moving_before != is_moving_after ||
3046 direction != MovDir[x][y])
3047 ResetGfxAnimation(x, y);
3049 if ((is_moving_before || is_moving_after) && !continues_moving)
3050 ResetGfxAnimation(x, y);
3053 if (!continues_moving)
3054 ResetGfxAnimation(x, y);
3057 MovDir[x][y] = direction;
3058 GfxDir[x][y] = direction;
3060 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3061 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3062 direction == MV_DOWN && CAN_FALL(element) ?
3063 ACTION_FALLING : ACTION_MOVING);
3065 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3066 ACTION_FALLING : ACTION_MOVING);
3069 /* this is needed for CEs with property "can move" / "not moving" */
3071 if (is_moving_after)
3073 if (Feld[newx][newy] == EL_EMPTY)
3074 Feld[newx][newy] = EL_BLOCKED;
3076 MovDir[newx][newy] = MovDir[x][y];
3078 #if USE_NEW_CUSTOM_VALUE
3079 CustomValue[newx][newy] = CustomValue[x][y];
3082 GfxFrame[newx][newy] = GfxFrame[x][y];
3083 GfxRandom[newx][newy] = GfxRandom[x][y];
3084 GfxAction[newx][newy] = GfxAction[x][y];
3085 GfxDir[newx][newy] = GfxDir[x][y];
3089 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3091 int direction = MovDir[x][y];
3092 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3093 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3099 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3101 int oldx = x, oldy = y;
3102 int direction = MovDir[x][y];
3104 if (direction == MV_LEFT)
3106 else if (direction == MV_RIGHT)
3108 else if (direction == MV_UP)
3110 else if (direction == MV_DOWN)
3113 *comes_from_x = oldx;
3114 *comes_from_y = oldy;
3117 int MovingOrBlocked2Element(int x, int y)
3119 int element = Feld[x][y];
3121 if (element == EL_BLOCKED)
3125 Blocked2Moving(x, y, &oldx, &oldy);
3126 return Feld[oldx][oldy];
3132 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3134 /* like MovingOrBlocked2Element(), but if element is moving
3135 and (x,y) is the field the moving element is just leaving,
3136 return EL_BLOCKED instead of the element value */
3137 int element = Feld[x][y];
3139 if (IS_MOVING(x, y))
3141 if (element == EL_BLOCKED)
3145 Blocked2Moving(x, y, &oldx, &oldy);
3146 return Feld[oldx][oldy];
3155 static void RemoveField(int x, int y)
3157 Feld[x][y] = EL_EMPTY;
3163 #if USE_NEW_CUSTOM_VALUE
3164 CustomValue[x][y] = 0;
3168 ChangeDelay[x][y] = 0;
3169 ChangePage[x][y] = -1;
3170 Pushed[x][y] = FALSE;
3173 ExplodeField[x][y] = EX_TYPE_NONE;
3176 GfxElement[x][y] = EL_UNDEFINED;
3177 GfxAction[x][y] = ACTION_DEFAULT;
3178 GfxDir[x][y] = MV_NONE;
3181 void RemoveMovingField(int x, int y)
3183 int oldx = x, oldy = y, newx = x, newy = y;
3184 int element = Feld[x][y];
3185 int next_element = EL_UNDEFINED;
3187 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3190 if (IS_MOVING(x, y))
3192 Moving2Blocked(x, y, &newx, &newy);
3194 if (Feld[newx][newy] != EL_BLOCKED)
3196 /* element is moving, but target field is not free (blocked), but
3197 already occupied by something different (example: acid pool);
3198 in this case, only remove the moving field, but not the target */
3200 RemoveField(oldx, oldy);
3202 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3204 DrawLevelField(oldx, oldy);
3209 else if (element == EL_BLOCKED)
3211 Blocked2Moving(x, y, &oldx, &oldy);
3212 if (!IS_MOVING(oldx, oldy))
3216 if (element == EL_BLOCKED &&
3217 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3218 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3219 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3220 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3221 next_element = get_next_element(Feld[oldx][oldy]);
3223 RemoveField(oldx, oldy);
3224 RemoveField(newx, newy);
3226 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3228 if (next_element != EL_UNDEFINED)
3229 Feld[oldx][oldy] = next_element;
3231 DrawLevelField(oldx, oldy);
3232 DrawLevelField(newx, newy);
3235 void DrawDynamite(int x, int y)
3237 int sx = SCREENX(x), sy = SCREENY(y);
3238 int graphic = el2img(Feld[x][y]);
3241 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3244 if (IS_WALKABLE_INSIDE(Back[x][y]))
3248 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3249 else if (Store[x][y])
3250 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3252 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3254 if (Back[x][y] || Store[x][y])
3255 DrawGraphicThruMask(sx, sy, graphic, frame);
3257 DrawGraphic(sx, sy, graphic, frame);
3260 void CheckDynamite(int x, int y)
3262 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3266 if (MovDelay[x][y] != 0)
3269 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3275 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3280 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3282 boolean num_checked_players = 0;
3285 for (i = 0; i < MAX_PLAYERS; i++)
3287 if (stored_player[i].active)
3289 int sx = stored_player[i].jx;
3290 int sy = stored_player[i].jy;
3292 if (num_checked_players == 0)
3299 *sx1 = MIN(*sx1, sx);
3300 *sy1 = MIN(*sy1, sy);
3301 *sx2 = MAX(*sx2, sx);
3302 *sy2 = MAX(*sy2, sy);
3305 num_checked_players++;
3310 static boolean checkIfAllPlayersFitToScreen_RND()
3312 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3314 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3316 return (sx2 - sx1 < SCR_FIELDX &&
3317 sy2 - sy1 < SCR_FIELDY);
3320 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3322 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3324 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3326 *sx = (sx1 + sx2) / 2;
3327 *sy = (sy1 + sy2) / 2;
3330 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3331 boolean quick_relocation)
3333 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3334 boolean no_delay = (tape.warp_forward);
3335 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3336 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3338 if (quick_relocation)
3340 int offset = (setup.scroll_delay ? 3 : 0);
3342 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3344 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3345 x > SBX_Right + MIDPOSX ? SBX_Right :
3348 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3349 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3354 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3355 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3356 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3358 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3359 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3360 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3362 /* don't scroll over playfield boundaries */
3363 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3364 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3366 /* don't scroll over playfield boundaries */
3367 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3368 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3371 RedrawPlayfield(TRUE, 0,0,0,0);
3375 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3376 x > SBX_Right + MIDPOSX ? SBX_Right :
3379 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3380 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3383 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3385 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3388 int fx = FX, fy = FY;
3390 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3391 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3393 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3399 fx += dx * TILEX / 2;
3400 fy += dy * TILEY / 2;
3402 ScrollLevel(dx, dy);
3405 /* scroll in two steps of half tile size to make things smoother */
3406 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3408 Delay(wait_delay_value);
3410 /* scroll second step to align at full tile size */
3412 Delay(wait_delay_value);
3417 Delay(wait_delay_value);
3421 void RelocatePlayer(int jx, int jy, int el_player_raw)
3423 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3424 int player_nr = GET_PLAYER_NR(el_player);
3425 struct PlayerInfo *player = &stored_player[player_nr];
3426 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3427 boolean no_delay = (tape.warp_forward);
3428 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3429 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3430 int old_jx = player->jx;
3431 int old_jy = player->jy;
3432 int old_element = Feld[old_jx][old_jy];
3433 int element = Feld[jx][jy];
3434 boolean player_relocated = (old_jx != jx || old_jy != jy);
3436 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3437 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3438 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3439 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3440 int leave_side_horiz = move_dir_horiz;
3441 int leave_side_vert = move_dir_vert;
3442 int enter_side = enter_side_horiz | enter_side_vert;
3443 int leave_side = leave_side_horiz | leave_side_vert;
3445 if (player->GameOver) /* do not reanimate dead player */
3448 if (!player_relocated) /* no need to relocate the player */
3451 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3453 RemoveField(jx, jy); /* temporarily remove newly placed player */
3454 DrawLevelField(jx, jy);
3457 if (player->present)
3459 while (player->MovPos)
3461 ScrollPlayer(player, SCROLL_GO_ON);
3462 ScrollScreen(NULL, SCROLL_GO_ON);
3464 AdvanceFrameAndPlayerCounters(player->index_nr);
3469 Delay(wait_delay_value);
3472 DrawPlayer(player); /* needed here only to cleanup last field */
3473 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3475 player->is_moving = FALSE;
3478 if (IS_CUSTOM_ELEMENT(old_element))
3479 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3481 player->index_bit, leave_side);
3483 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3485 player->index_bit, leave_side);
3487 Feld[jx][jy] = el_player;
3488 InitPlayerField(jx, jy, el_player, TRUE);
3490 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3492 Feld[jx][jy] = element;
3493 InitField(jx, jy, FALSE);
3496 /* only visually relocate centered player */
3497 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3498 level.instant_relocation);
3500 TestIfPlayerTouchesBadThing(jx, jy);
3501 TestIfPlayerTouchesCustomElement(jx, jy);
3503 if (IS_CUSTOM_ELEMENT(element))
3504 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3505 player->index_bit, enter_side);
3507 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3508 player->index_bit, enter_side);
3511 void Explode(int ex, int ey, int phase, int mode)
3517 /* !!! eliminate this variable !!! */
3518 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3520 if (game.explosions_delayed)
3522 ExplodeField[ex][ey] = mode;
3526 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3528 int center_element = Feld[ex][ey];
3529 int artwork_element, explosion_element; /* set these values later */
3532 /* --- This is only really needed (and now handled) in "Impact()". --- */
3533 /* do not explode moving elements that left the explode field in time */
3534 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3535 center_element == EL_EMPTY &&
3536 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3541 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3542 if (mode == EX_TYPE_NORMAL ||
3543 mode == EX_TYPE_CENTER ||
3544 mode == EX_TYPE_CROSS)
3545 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3548 /* remove things displayed in background while burning dynamite */
3549 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3552 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3554 /* put moving element to center field (and let it explode there) */
3555 center_element = MovingOrBlocked2Element(ex, ey);
3556 RemoveMovingField(ex, ey);
3557 Feld[ex][ey] = center_element;
3560 /* now "center_element" is finally determined -- set related values now */
3561 artwork_element = center_element; /* for custom player artwork */
3562 explosion_element = center_element; /* for custom player artwork */
3564 if (IS_PLAYER(ex, ey))
3566 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3568 artwork_element = stored_player[player_nr].artwork_element;
3570 if (level.use_explosion_element[player_nr])
3572 explosion_element = level.explosion_element[player_nr];
3573 artwork_element = explosion_element;
3578 if (mode == EX_TYPE_NORMAL ||
3579 mode == EX_TYPE_CENTER ||
3580 mode == EX_TYPE_CROSS)
3581 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3584 last_phase = element_info[explosion_element].explosion_delay + 1;
3586 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3588 int xx = x - ex + 1;
3589 int yy = y - ey + 1;
3592 if (!IN_LEV_FIELD(x, y) ||
3593 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3594 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3597 element = Feld[x][y];
3599 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3601 element = MovingOrBlocked2Element(x, y);
3603 if (!IS_EXPLOSION_PROOF(element))
3604 RemoveMovingField(x, y);
3607 /* indestructible elements can only explode in center (but not flames) */
3608 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3609 mode == EX_TYPE_BORDER)) ||
3610 element == EL_FLAMES)
3613 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3614 behaviour, for example when touching a yamyam that explodes to rocks
3615 with active deadly shield, a rock is created under the player !!! */
3616 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3618 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3619 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3620 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3622 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3625 if (IS_ACTIVE_BOMB(element))
3627 /* re-activate things under the bomb like gate or penguin */
3628 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3635 /* save walkable background elements while explosion on same tile */
3636 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3637 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3638 Back[x][y] = element;
3640 /* ignite explodable elements reached by other explosion */
3641 if (element == EL_EXPLOSION)
3642 element = Store2[x][y];
3644 if (AmoebaNr[x][y] &&
3645 (element == EL_AMOEBA_FULL ||
3646 element == EL_BD_AMOEBA ||
3647 element == EL_AMOEBA_GROWING))
3649 AmoebaCnt[AmoebaNr[x][y]]--;
3650 AmoebaCnt2[AmoebaNr[x][y]]--;
3655 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3657 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3659 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3661 if (PLAYERINFO(ex, ey)->use_murphy)
3662 Store[x][y] = EL_EMPTY;
3665 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3666 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3667 else if (ELEM_IS_PLAYER(center_element))
3668 Store[x][y] = EL_EMPTY;
3669 else if (center_element == EL_YAMYAM)
3670 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3671 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3672 Store[x][y] = element_info[center_element].content.e[xx][yy];
3674 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3675 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3676 otherwise) -- FIX THIS !!! */
3677 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3678 Store[x][y] = element_info[element].content.e[1][1];
3680 else if (!CAN_EXPLODE(element))
3681 Store[x][y] = element_info[element].content.e[1][1];
3684 Store[x][y] = EL_EMPTY;
3686 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3687 center_element == EL_AMOEBA_TO_DIAMOND)
3688 Store2[x][y] = element;
3690 Feld[x][y] = EL_EXPLOSION;
3691 GfxElement[x][y] = artwork_element;
3693 ExplodePhase[x][y] = 1;
3694 ExplodeDelay[x][y] = last_phase;
3699 if (center_element == EL_YAMYAM)
3700 game.yamyam_content_nr =
3701 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3713 GfxFrame[x][y] = 0; /* restart explosion animation */
3715 last_phase = ExplodeDelay[x][y];
3717 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3721 /* activate this even in non-DEBUG version until cause for crash in
3722 getGraphicAnimationFrame() (see below) is found and eliminated */
3728 /* this can happen if the player leaves an explosion just in time */
3729 if (GfxElement[x][y] == EL_UNDEFINED)
3730 GfxElement[x][y] = EL_EMPTY;
3732 if (GfxElement[x][y] == EL_UNDEFINED)
3735 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3736 printf("Explode(): This should never happen!\n");
3739 GfxElement[x][y] = EL_EMPTY;
3745 border_element = Store2[x][y];
3746 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3747 border_element = StorePlayer[x][y];
3749 if (phase == element_info[border_element].ignition_delay ||
3750 phase == last_phase)
3752 boolean border_explosion = FALSE;
3754 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3755 !PLAYER_EXPLOSION_PROTECTED(x, y))
3757 KillPlayerUnlessExplosionProtected(x, y);
3758 border_explosion = TRUE;
3760 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3762 Feld[x][y] = Store2[x][y];
3765 border_explosion = TRUE;
3767 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3769 AmoebeUmwandeln(x, y);
3771 border_explosion = TRUE;
3774 /* if an element just explodes due to another explosion (chain-reaction),
3775 do not immediately end the new explosion when it was the last frame of
3776 the explosion (as it would be done in the following "if"-statement!) */
3777 if (border_explosion && phase == last_phase)
3781 if (phase == last_phase)
3785 element = Feld[x][y] = Store[x][y];
3786 Store[x][y] = Store2[x][y] = 0;
3787 GfxElement[x][y] = EL_UNDEFINED;
3789 /* player can escape from explosions and might therefore be still alive */
3790 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3791 element <= EL_PLAYER_IS_EXPLODING_4)
3793 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3794 int explosion_element = EL_PLAYER_1 + player_nr;
3795 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3796 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3798 if (level.use_explosion_element[player_nr])
3799 explosion_element = level.explosion_element[player_nr];
3801 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3802 element_info[explosion_element].content.e[xx][yy]);
3805 /* restore probably existing indestructible background element */
3806 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3807 element = Feld[x][y] = Back[x][y];
3810 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3811 GfxDir[x][y] = MV_NONE;
3812 ChangeDelay[x][y] = 0;
3813 ChangePage[x][y] = -1;
3815 #if USE_NEW_CUSTOM_VALUE
3816 CustomValue[x][y] = 0;
3819 InitField_WithBug2(x, y, FALSE);
3821 DrawLevelField(x, y);
3823 TestIfElementTouchesCustomElement(x, y);
3825 if (GFX_CRUMBLED(element))
3826 DrawLevelFieldCrumbledSandNeighbours(x, y);
3828 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3829 StorePlayer[x][y] = 0;
3831 if (ELEM_IS_PLAYER(element))
3832 RelocatePlayer(x, y, element);
3834 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3836 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3837 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3840 DrawLevelFieldCrumbledSand(x, y);
3842 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3844 DrawLevelElement(x, y, Back[x][y]);
3845 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3847 else if (IS_WALKABLE_UNDER(Back[x][y]))
3849 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3850 DrawLevelElementThruMask(x, y, Back[x][y]);
3852 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3853 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3857 void DynaExplode(int ex, int ey)
3860 int dynabomb_element = Feld[ex][ey];
3861 int dynabomb_size = 1;
3862 boolean dynabomb_xl = FALSE;
3863 struct PlayerInfo *player;
3864 static int xy[4][2] =
3872 if (IS_ACTIVE_BOMB(dynabomb_element))
3874 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3875 dynabomb_size = player->dynabomb_size;
3876 dynabomb_xl = player->dynabomb_xl;
3877 player->dynabombs_left++;
3880 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3882 for (i = 0; i < NUM_DIRECTIONS; i++)
3884 for (j = 1; j <= dynabomb_size; j++)
3886 int x = ex + j * xy[i][0];
3887 int y = ey + j * xy[i][1];
3890 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3893 element = Feld[x][y];
3895 /* do not restart explosions of fields with active bombs */
3896 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3899 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3901 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3902 !IS_DIGGABLE(element) && !dynabomb_xl)
3908 void Bang(int x, int y)
3910 int element = MovingOrBlocked2Element(x, y);
3911 int explosion_type = EX_TYPE_NORMAL;
3913 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3915 struct PlayerInfo *player = PLAYERINFO(x, y);
3917 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3918 player->element_nr);
3920 if (level.use_explosion_element[player->index_nr])
3922 int explosion_element = level.explosion_element[player->index_nr];
3924 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3925 explosion_type = EX_TYPE_CROSS;
3926 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3927 explosion_type = EX_TYPE_CENTER;
3935 case EL_BD_BUTTERFLY:
3938 case EL_DARK_YAMYAM:
3942 RaiseScoreElement(element);
3945 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3946 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3947 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3948 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3949 case EL_DYNABOMB_INCREASE_NUMBER:
3950 case EL_DYNABOMB_INCREASE_SIZE:
3951 case EL_DYNABOMB_INCREASE_POWER:
3952 explosion_type = EX_TYPE_DYNA;
3957 case EL_LAMP_ACTIVE:
3958 case EL_AMOEBA_TO_DIAMOND:
3959 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3960 explosion_type = EX_TYPE_CENTER;
3964 if (element_info[element].explosion_type == EXPLODES_CROSS)
3965 explosion_type = EX_TYPE_CROSS;
3966 else if (element_info[element].explosion_type == EXPLODES_1X1)
3967 explosion_type = EX_TYPE_CENTER;
3971 if (explosion_type == EX_TYPE_DYNA)
3974 Explode(x, y, EX_PHASE_START, explosion_type);
3976 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3979 void SplashAcid(int x, int y)
3981 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3982 (!IN_LEV_FIELD(x - 1, y - 2) ||
3983 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3984 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3986 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3987 (!IN_LEV_FIELD(x + 1, y - 2) ||
3988 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3989 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3991 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3994 static void InitBeltMovement()
3996 static int belt_base_element[4] =
3998 EL_CONVEYOR_BELT_1_LEFT,
3999 EL_CONVEYOR_BELT_2_LEFT,
4000 EL_CONVEYOR_BELT_3_LEFT,
4001 EL_CONVEYOR_BELT_4_LEFT
4003 static int belt_base_active_element[4] =
4005 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4006 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4007 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4008 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4013 /* set frame order for belt animation graphic according to belt direction */
4014 for (i = 0; i < NUM_BELTS; i++)
4018 for (j = 0; j < NUM_BELT_PARTS; j++)
4020 int element = belt_base_active_element[belt_nr] + j;
4021 int graphic = el2img(element);
4023 if (game.belt_dir[i] == MV_LEFT)
4024 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4026 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4030 SCAN_PLAYFIELD(x, y)
4032 int element = Feld[x][y];
4034 for (i = 0; i < NUM_BELTS; i++)
4036 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4038 int e_belt_nr = getBeltNrFromBeltElement(element);
4041 if (e_belt_nr == belt_nr)
4043 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4045 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4052 static void ToggleBeltSwitch(int x, int y)
4054 static int belt_base_element[4] =
4056 EL_CONVEYOR_BELT_1_LEFT,
4057 EL_CONVEYOR_BELT_2_LEFT,
4058 EL_CONVEYOR_BELT_3_LEFT,
4059 EL_CONVEYOR_BELT_4_LEFT
4061 static int belt_base_active_element[4] =
4063 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4064 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4065 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4066 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4068 static int belt_base_switch_element[4] =
4070 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4071 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4072 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4073 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4075 static int belt_move_dir[4] =
4083 int element = Feld[x][y];
4084 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4085 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4086 int belt_dir = belt_move_dir[belt_dir_nr];
4089 if (!IS_BELT_SWITCH(element))
4092 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4093 game.belt_dir[belt_nr] = belt_dir;
4095 if (belt_dir_nr == 3)
4098 /* set frame order for belt animation graphic according to belt direction */
4099 for (i = 0; i < NUM_BELT_PARTS; i++)
4101 int element = belt_base_active_element[belt_nr] + i;
4102 int graphic = el2img(element);
4104 if (belt_dir == MV_LEFT)
4105 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4107 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4110 SCAN_PLAYFIELD(xx, yy)
4112 int element = Feld[xx][yy];
4114 if (IS_BELT_SWITCH(element))
4116 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4118 if (e_belt_nr == belt_nr)
4120 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4121 DrawLevelField(xx, yy);
4124 else if (IS_BELT(element) && belt_dir != MV_NONE)
4126 int e_belt_nr = getBeltNrFromBeltElement(element);
4128 if (e_belt_nr == belt_nr)
4130 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4132 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4133 DrawLevelField(xx, yy);
4136 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4138 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4140 if (e_belt_nr == belt_nr)
4142 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4144 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4145 DrawLevelField(xx, yy);
4151 static void ToggleSwitchgateSwitch(int x, int y)
4155 game.switchgate_pos = !game.switchgate_pos;
4157 SCAN_PLAYFIELD(xx, yy)
4159 int element = Feld[xx][yy];
4161 #if !USE_BOTH_SWITCHGATE_SWITCHES
4162 if (element == EL_SWITCHGATE_SWITCH_UP ||
4163 element == EL_SWITCHGATE_SWITCH_DOWN)
4165 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4166 DrawLevelField(xx, yy);
4169 if (element == EL_SWITCHGATE_SWITCH_UP)
4171 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4172 DrawLevelField(xx, yy);
4174 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4176 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4177 DrawLevelField(xx, yy);
4180 else if (element == EL_SWITCHGATE_OPEN ||
4181 element == EL_SWITCHGATE_OPENING)
4183 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4185 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4187 else if (element == EL_SWITCHGATE_CLOSED ||
4188 element == EL_SWITCHGATE_CLOSING)
4190 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4192 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4197 static int getInvisibleActiveFromInvisibleElement(int element)
4199 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4200 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4201 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4205 static int getInvisibleFromInvisibleActiveElement(int element)
4207 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4208 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4209 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4213 static void RedrawAllLightSwitchesAndInvisibleElements()
4217 SCAN_PLAYFIELD(x, y)
4219 int element = Feld[x][y];
4221 if (element == EL_LIGHT_SWITCH &&
4222 game.light_time_left > 0)
4224 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4225 DrawLevelField(x, y);
4227 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4228 game.light_time_left == 0)
4230 Feld[x][y] = EL_LIGHT_SWITCH;
4231 DrawLevelField(x, y);
4233 else if (element == EL_EMC_DRIPPER &&
4234 game.light_time_left > 0)
4236 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4237 DrawLevelField(x, y);
4239 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4240 game.light_time_left == 0)
4242 Feld[x][y] = EL_EMC_DRIPPER;
4243 DrawLevelField(x, y);
4245 else if (element == EL_INVISIBLE_STEELWALL ||
4246 element == EL_INVISIBLE_WALL ||
4247 element == EL_INVISIBLE_SAND)
4249 if (game.light_time_left > 0)
4250 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4252 DrawLevelField(x, y);
4254 /* uncrumble neighbour fields, if needed */
4255 if (element == EL_INVISIBLE_SAND)
4256 DrawLevelFieldCrumbledSandNeighbours(x, y);
4258 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4259 element == EL_INVISIBLE_WALL_ACTIVE ||
4260 element == EL_INVISIBLE_SAND_ACTIVE)
4262 if (game.light_time_left == 0)
4263 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4265 DrawLevelField(x, y);
4267 /* re-crumble neighbour fields, if needed */
4268 if (element == EL_INVISIBLE_SAND)
4269 DrawLevelFieldCrumbledSandNeighbours(x, y);
4274 static void RedrawAllInvisibleElementsForLenses()
4278 SCAN_PLAYFIELD(x, y)
4280 int element = Feld[x][y];
4282 if (element == EL_EMC_DRIPPER &&
4283 game.lenses_time_left > 0)
4285 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4286 DrawLevelField(x, y);
4288 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4289 game.lenses_time_left == 0)
4291 Feld[x][y] = EL_EMC_DRIPPER;
4292 DrawLevelField(x, y);
4294 else if (element == EL_INVISIBLE_STEELWALL ||
4295 element == EL_INVISIBLE_WALL ||
4296 element == EL_INVISIBLE_SAND)
4298 if (game.lenses_time_left > 0)
4299 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4301 DrawLevelField(x, y);
4303 /* uncrumble neighbour fields, if needed */
4304 if (element == EL_INVISIBLE_SAND)
4305 DrawLevelFieldCrumbledSandNeighbours(x, y);
4307 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4308 element == EL_INVISIBLE_WALL_ACTIVE ||
4309 element == EL_INVISIBLE_SAND_ACTIVE)
4311 if (game.lenses_time_left == 0)
4312 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4314 DrawLevelField(x, y);
4316 /* re-crumble neighbour fields, if needed */
4317 if (element == EL_INVISIBLE_SAND)
4318 DrawLevelFieldCrumbledSandNeighbours(x, y);
4323 static void RedrawAllInvisibleElementsForMagnifier()
4327 SCAN_PLAYFIELD(x, y)
4329 int element = Feld[x][y];
4331 if (element == EL_EMC_FAKE_GRASS &&
4332 game.magnify_time_left > 0)
4334 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4335 DrawLevelField(x, y);
4337 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4338 game.magnify_time_left == 0)
4340 Feld[x][y] = EL_EMC_FAKE_GRASS;
4341 DrawLevelField(x, y);
4343 else if (IS_GATE_GRAY(element) &&
4344 game.magnify_time_left > 0)
4346 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4347 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4348 IS_EM_GATE_GRAY(element) ?
4349 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4350 IS_EMC_GATE_GRAY(element) ?
4351 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4353 DrawLevelField(x, y);
4355 else if (IS_GATE_GRAY_ACTIVE(element) &&
4356 game.magnify_time_left == 0)
4358 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4359 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4360 IS_EM_GATE_GRAY_ACTIVE(element) ?
4361 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4362 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4363 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4365 DrawLevelField(x, y);
4370 static void ToggleLightSwitch(int x, int y)
4372 int element = Feld[x][y];
4374 game.light_time_left =
4375 (element == EL_LIGHT_SWITCH ?
4376 level.time_light * FRAMES_PER_SECOND : 0);
4378 RedrawAllLightSwitchesAndInvisibleElements();
4381 static void ActivateTimegateSwitch(int x, int y)
4385 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4387 SCAN_PLAYFIELD(xx, yy)
4389 int element = Feld[xx][yy];
4391 if (element == EL_TIMEGATE_CLOSED ||
4392 element == EL_TIMEGATE_CLOSING)
4394 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4395 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4399 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4401 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4402 DrawLevelField(xx, yy);
4408 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4411 void Impact(int x, int y)
4413 boolean last_line = (y == lev_fieldy - 1);
4414 boolean object_hit = FALSE;
4415 boolean impact = (last_line || object_hit);
4416 int element = Feld[x][y];
4417 int smashed = EL_STEELWALL;
4419 if (!last_line) /* check if element below was hit */
4421 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4424 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4425 MovDir[x][y + 1] != MV_DOWN ||
4426 MovPos[x][y + 1] <= TILEY / 2));
4428 /* do not smash moving elements that left the smashed field in time */
4429 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4430 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4433 #if USE_QUICKSAND_IMPACT_BUGFIX
4434 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4436 RemoveMovingField(x, y + 1);
4437 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4438 Feld[x][y + 2] = EL_ROCK;
4439 DrawLevelField(x, y + 2);
4446 smashed = MovingOrBlocked2Element(x, y + 1);
4448 impact = (last_line || object_hit);
4451 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4453 SplashAcid(x, y + 1);
4457 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4458 /* only reset graphic animation if graphic really changes after impact */
4460 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4462 ResetGfxAnimation(x, y);
4463 DrawLevelField(x, y);
4466 if (impact && CAN_EXPLODE_IMPACT(element))
4471 else if (impact && element == EL_PEARL)
4473 ResetGfxAnimation(x, y);
4475 Feld[x][y] = EL_PEARL_BREAKING;
4476 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4479 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4481 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4486 if (impact && element == EL_AMOEBA_DROP)
4488 if (object_hit && IS_PLAYER(x, y + 1))
4489 KillPlayerUnlessEnemyProtected(x, y + 1);
4490 else if (object_hit && smashed == EL_PENGUIN)
4494 Feld[x][y] = EL_AMOEBA_GROWING;
4495 Store[x][y] = EL_AMOEBA_WET;
4497 ResetRandomAnimationValue(x, y);
4502 if (object_hit) /* check which object was hit */
4504 if (CAN_PASS_MAGIC_WALL(element) &&
4505 (smashed == EL_MAGIC_WALL ||
4506 smashed == EL_BD_MAGIC_WALL))
4509 int activated_magic_wall =
4510 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4511 EL_BD_MAGIC_WALL_ACTIVE);
4513 /* activate magic wall / mill */
4514 SCAN_PLAYFIELD(xx, yy)
4515 if (Feld[xx][yy] == smashed)
4516 Feld[xx][yy] = activated_magic_wall;
4518 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4519 game.magic_wall_active = TRUE;
4521 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4522 SND_MAGIC_WALL_ACTIVATING :
4523 SND_BD_MAGIC_WALL_ACTIVATING));
4526 if (IS_PLAYER(x, y + 1))
4528 if (CAN_SMASH_PLAYER(element))
4530 KillPlayerUnlessEnemyProtected(x, y + 1);
4534 else if (smashed == EL_PENGUIN)
4536 if (CAN_SMASH_PLAYER(element))
4542 else if (element == EL_BD_DIAMOND)
4544 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4550 else if (((element == EL_SP_INFOTRON ||
4551 element == EL_SP_ZONK) &&
4552 (smashed == EL_SP_SNIKSNAK ||
4553 smashed == EL_SP_ELECTRON ||
4554 smashed == EL_SP_DISK_ORANGE)) ||
4555 (element == EL_SP_INFOTRON &&
4556 smashed == EL_SP_DISK_YELLOW))
4561 else if (CAN_SMASH_EVERYTHING(element))
4563 if (IS_CLASSIC_ENEMY(smashed) ||
4564 CAN_EXPLODE_SMASHED(smashed))
4569 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4571 if (smashed == EL_LAMP ||
4572 smashed == EL_LAMP_ACTIVE)
4577 else if (smashed == EL_NUT)
4579 Feld[x][y + 1] = EL_NUT_BREAKING;
4580 PlayLevelSound(x, y, SND_NUT_BREAKING);
4581 RaiseScoreElement(EL_NUT);
4584 else if (smashed == EL_PEARL)
4586 ResetGfxAnimation(x, y);
4588 Feld[x][y + 1] = EL_PEARL_BREAKING;
4589 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4592 else if (smashed == EL_DIAMOND)
4594 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4595 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4598 else if (IS_BELT_SWITCH(smashed))
4600 ToggleBeltSwitch(x, y + 1);
4602 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4603 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4605 ToggleSwitchgateSwitch(x, y + 1);
4607 else if (smashed == EL_LIGHT_SWITCH ||
4608 smashed == EL_LIGHT_SWITCH_ACTIVE)
4610 ToggleLightSwitch(x, y + 1);
4615 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4618 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4620 CheckElementChangeBySide(x, y + 1, smashed, element,
4621 CE_SWITCHED, CH_SIDE_TOP);
4622 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4628 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4633 /* play sound of magic wall / mill */
4635 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4636 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4638 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4639 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4640 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4641 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4646 /* play sound of object that hits the ground */
4647 if (last_line || object_hit)
4648 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4651 inline static void TurnRoundExt(int x, int y)
4663 { 0, 0 }, { 0, 0 }, { 0, 0 },
4668 int left, right, back;
4672 { MV_DOWN, MV_UP, MV_RIGHT },
4673 { MV_UP, MV_DOWN, MV_LEFT },
4675 { MV_LEFT, MV_RIGHT, MV_DOWN },
4679 { MV_RIGHT, MV_LEFT, MV_UP }
4682 int element = Feld[x][y];
4683 int move_pattern = element_info[element].move_pattern;
4685 int old_move_dir = MovDir[x][y];
4686 int left_dir = turn[old_move_dir].left;
4687 int right_dir = turn[old_move_dir].right;
4688 int back_dir = turn[old_move_dir].back;
4690 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4691 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4692 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4693 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4695 int left_x = x + left_dx, left_y = y + left_dy;
4696 int right_x = x + right_dx, right_y = y + right_dy;
4697 int move_x = x + move_dx, move_y = y + move_dy;
4701 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4703 TestIfBadThingTouchesOtherBadThing(x, y);
4705 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4706 MovDir[x][y] = right_dir;
4707 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4708 MovDir[x][y] = left_dir;
4710 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4712 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4715 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4717 TestIfBadThingTouchesOtherBadThing(x, y);
4719 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4720 MovDir[x][y] = left_dir;
4721 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4722 MovDir[x][y] = right_dir;
4724 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4726 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4729 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4731 TestIfBadThingTouchesOtherBadThing(x, y);
4733 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4734 MovDir[x][y] = left_dir;
4735 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4736 MovDir[x][y] = right_dir;
4738 if (MovDir[x][y] != old_move_dir)
4741 else if (element == EL_YAMYAM)
4743 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4744 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4746 if (can_turn_left && can_turn_right)
4747 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4748 else if (can_turn_left)
4749 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4750 else if (can_turn_right)
4751 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4753 MovDir[x][y] = back_dir;
4755 MovDelay[x][y] = 16 + 16 * RND(3);
4757 else if (element == EL_DARK_YAMYAM)
4759 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4761 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4764 if (can_turn_left && can_turn_right)
4765 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4766 else if (can_turn_left)
4767 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4768 else if (can_turn_right)
4769 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4771 MovDir[x][y] = back_dir;
4773 MovDelay[x][y] = 16 + 16 * RND(3);
4775 else if (element == EL_PACMAN)
4777 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4778 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4780 if (can_turn_left && can_turn_right)
4781 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4782 else if (can_turn_left)
4783 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4784 else if (can_turn_right)
4785 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4787 MovDir[x][y] = back_dir;
4789 MovDelay[x][y] = 6 + RND(40);
4791 else if (element == EL_PIG)
4793 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4794 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4795 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4796 boolean should_turn_left, should_turn_right, should_move_on;
4798 int rnd = RND(rnd_value);
4800 should_turn_left = (can_turn_left &&
4802 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4803 y + back_dy + left_dy)));
4804 should_turn_right = (can_turn_right &&
4806 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4807 y + back_dy + right_dy)));
4808 should_move_on = (can_move_on &&
4811 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4812 y + move_dy + left_dy) ||
4813 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4814 y + move_dy + right_dy)));
4816 if (should_turn_left || should_turn_right || should_move_on)
4818 if (should_turn_left && should_turn_right && should_move_on)
4819 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4820 rnd < 2 * rnd_value / 3 ? right_dir :
4822 else if (should_turn_left && should_turn_right)
4823 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4824 else if (should_turn_left && should_move_on)
4825 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4826 else if (should_turn_right && should_move_on)
4827 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4828 else if (should_turn_left)
4829 MovDir[x][y] = left_dir;
4830 else if (should_turn_right)
4831 MovDir[x][y] = right_dir;
4832 else if (should_move_on)
4833 MovDir[x][y] = old_move_dir;
4835 else if (can_move_on && rnd > rnd_value / 8)
4836 MovDir[x][y] = old_move_dir;
4837 else if (can_turn_left && can_turn_right)
4838 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4839 else if (can_turn_left && rnd > rnd_value / 8)
4840 MovDir[x][y] = left_dir;
4841 else if (can_turn_right && rnd > rnd_value/8)
4842 MovDir[x][y] = right_dir;
4844 MovDir[x][y] = back_dir;
4846 xx = x + move_xy[MovDir[x][y]].dx;
4847 yy = y + move_xy[MovDir[x][y]].dy;
4849 if (!IN_LEV_FIELD(xx, yy) ||
4850 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4851 MovDir[x][y] = old_move_dir;
4855 else if (element == EL_DRAGON)
4857 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4858 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4859 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4861 int rnd = RND(rnd_value);
4863 if (can_move_on && rnd > rnd_value / 8)
4864 MovDir[x][y] = old_move_dir;
4865 else if (can_turn_left && can_turn_right)
4866 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4867 else if (can_turn_left && rnd > rnd_value / 8)
4868 MovDir[x][y] = left_dir;
4869 else if (can_turn_right && rnd > rnd_value / 8)
4870 MovDir[x][y] = right_dir;
4872 MovDir[x][y] = back_dir;
4874 xx = x + move_xy[MovDir[x][y]].dx;
4875 yy = y + move_xy[MovDir[x][y]].dy;
4877 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4878 MovDir[x][y] = old_move_dir;
4882 else if (element == EL_MOLE)
4884 boolean can_move_on =
4885 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4886 IS_AMOEBOID(Feld[move_x][move_y]) ||
4887 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4890 boolean can_turn_left =
4891 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4892 IS_AMOEBOID(Feld[left_x][left_y])));
4894 boolean can_turn_right =
4895 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4896 IS_AMOEBOID(Feld[right_x][right_y])));
4898 if (can_turn_left && can_turn_right)
4899 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4900 else if (can_turn_left)
4901 MovDir[x][y] = left_dir;
4903 MovDir[x][y] = right_dir;
4906 if (MovDir[x][y] != old_move_dir)
4909 else if (element == EL_BALLOON)
4911 MovDir[x][y] = game.wind_direction;
4914 else if (element == EL_SPRING)
4916 #if USE_NEW_SPRING_BUMPER
4917 if (MovDir[x][y] & MV_HORIZONTAL)
4919 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4920 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4922 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4923 ResetGfxAnimation(move_x, move_y);
4924 DrawLevelField(move_x, move_y);
4926 MovDir[x][y] = back_dir;
4928 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4929 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4930 MovDir[x][y] = MV_NONE;
4933 if (MovDir[x][y] & MV_HORIZONTAL &&
4934 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4935 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4936 MovDir[x][y] = MV_NONE;
4941 else if (element == EL_ROBOT ||
4942 element == EL_SATELLITE ||
4943 element == EL_PENGUIN ||
4944 element == EL_EMC_ANDROID)
4946 int attr_x = -1, attr_y = -1;
4957 for (i = 0; i < MAX_PLAYERS; i++)
4959 struct PlayerInfo *player = &stored_player[i];
4960 int jx = player->jx, jy = player->jy;
4962 if (!player->active)
4966 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4974 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4975 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4976 game.engine_version < VERSION_IDENT(3,1,0,0)))
4982 if (element == EL_PENGUIN)
4985 static int xy[4][2] =
4993 for (i = 0; i < NUM_DIRECTIONS; i++)
4995 int ex = x + xy[i][0];
4996 int ey = y + xy[i][1];
4998 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5007 MovDir[x][y] = MV_NONE;
5009 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5010 else if (attr_x > x)
5011 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5013 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5014 else if (attr_y > y)
5015 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5017 if (element == EL_ROBOT)
5021 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5022 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5023 Moving2Blocked(x, y, &newx, &newy);
5025 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5026 MovDelay[x][y] = 8 + 8 * !RND(3);
5028 MovDelay[x][y] = 16;
5030 else if (element == EL_PENGUIN)
5036 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5038 boolean first_horiz = RND(2);
5039 int new_move_dir = MovDir[x][y];
5042 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5043 Moving2Blocked(x, y, &newx, &newy);
5045 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5049 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5050 Moving2Blocked(x, y, &newx, &newy);
5052 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5055 MovDir[x][y] = old_move_dir;
5059 else if (element == EL_SATELLITE)
5065 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5067 boolean first_horiz = RND(2);
5068 int new_move_dir = MovDir[x][y];
5071 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5072 Moving2Blocked(x, y, &newx, &newy);
5074 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5078 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5079 Moving2Blocked(x, y, &newx, &newy);
5081 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5084 MovDir[x][y] = old_move_dir;
5088 else if (element == EL_EMC_ANDROID)
5090 static int check_pos[16] =
5092 -1, /* 0 => (invalid) */
5093 7, /* 1 => MV_LEFT */
5094 3, /* 2 => MV_RIGHT */
5095 -1, /* 3 => (invalid) */
5097 0, /* 5 => MV_LEFT | MV_UP */
5098 2, /* 6 => MV_RIGHT | MV_UP */
5099 -1, /* 7 => (invalid) */
5100 5, /* 8 => MV_DOWN */
5101 6, /* 9 => MV_LEFT | MV_DOWN */
5102 4, /* 10 => MV_RIGHT | MV_DOWN */
5103 -1, /* 11 => (invalid) */
5104 -1, /* 12 => (invalid) */
5105 -1, /* 13 => (invalid) */
5106 -1, /* 14 => (invalid) */
5107 -1, /* 15 => (invalid) */
5115 { -1, -1, MV_LEFT | MV_UP },
5117 { +1, -1, MV_RIGHT | MV_UP },
5118 { +1, 0, MV_RIGHT },
5119 { +1, +1, MV_RIGHT | MV_DOWN },
5121 { -1, +1, MV_LEFT | MV_DOWN },
5124 int start_pos, check_order;
5125 boolean can_clone = FALSE;
5128 /* check if there is any free field around current position */
5129 for (i = 0; i < 8; i++)
5131 int newx = x + check_xy[i].dx;
5132 int newy = y + check_xy[i].dy;
5134 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5142 if (can_clone) /* randomly find an element to clone */
5146 start_pos = check_pos[RND(8)];
5147 check_order = (RND(2) ? -1 : +1);
5149 for (i = 0; i < 8; i++)
5151 int pos_raw = start_pos + i * check_order;
5152 int pos = (pos_raw + 8) % 8;
5153 int newx = x + check_xy[pos].dx;
5154 int newy = y + check_xy[pos].dy;
5156 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5158 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5159 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5161 Store[x][y] = Feld[newx][newy];
5170 if (can_clone) /* randomly find a direction to move */
5174 start_pos = check_pos[RND(8)];
5175 check_order = (RND(2) ? -1 : +1);
5177 for (i = 0; i < 8; i++)
5179 int pos_raw = start_pos + i * check_order;
5180 int pos = (pos_raw + 8) % 8;
5181 int newx = x + check_xy[pos].dx;
5182 int newy = y + check_xy[pos].dy;
5183 int new_move_dir = check_xy[pos].dir;
5185 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5187 MovDir[x][y] = new_move_dir;
5188 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5197 if (can_clone) /* cloning and moving successful */
5200 /* cannot clone -- try to move towards player */
5202 start_pos = check_pos[MovDir[x][y] & 0x0f];
5203 check_order = (RND(2) ? -1 : +1);
5205 for (i = 0; i < 3; i++)
5207 /* first check start_pos, then previous/next or (next/previous) pos */
5208 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5209 int pos = (pos_raw + 8) % 8;
5210 int newx = x + check_xy[pos].dx;
5211 int newy = y + check_xy[pos].dy;
5212 int new_move_dir = check_xy[pos].dir;
5214 if (IS_PLAYER(newx, newy))
5217 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5219 MovDir[x][y] = new_move_dir;
5220 MovDelay[x][y] = level.android_move_time * 8 + 1;
5227 else if (move_pattern == MV_TURNING_LEFT ||
5228 move_pattern == MV_TURNING_RIGHT ||
5229 move_pattern == MV_TURNING_LEFT_RIGHT ||
5230 move_pattern == MV_TURNING_RIGHT_LEFT ||
5231 move_pattern == MV_TURNING_RANDOM ||
5232 move_pattern == MV_ALL_DIRECTIONS)
5234 boolean can_turn_left =
5235 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5236 boolean can_turn_right =
5237 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5239 if (element_info[element].move_stepsize == 0) /* "not moving" */
5242 if (move_pattern == MV_TURNING_LEFT)
5243 MovDir[x][y] = left_dir;
5244 else if (move_pattern == MV_TURNING_RIGHT)
5245 MovDir[x][y] = right_dir;
5246 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5247 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5248 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5249 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5250 else if (move_pattern == MV_TURNING_RANDOM)
5251 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5252 can_turn_right && !can_turn_left ? right_dir :
5253 RND(2) ? left_dir : right_dir);
5254 else if (can_turn_left && can_turn_right)
5255 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5256 else if (can_turn_left)
5257 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5258 else if (can_turn_right)
5259 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5261 MovDir[x][y] = back_dir;
5263 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5265 else if (move_pattern == MV_HORIZONTAL ||
5266 move_pattern == MV_VERTICAL)
5268 if (move_pattern & old_move_dir)
5269 MovDir[x][y] = back_dir;
5270 else if (move_pattern == MV_HORIZONTAL)
5271 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5272 else if (move_pattern == MV_VERTICAL)
5273 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5275 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5277 else if (move_pattern & MV_ANY_DIRECTION)
5279 MovDir[x][y] = move_pattern;
5280 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5282 else if (move_pattern & MV_WIND_DIRECTION)
5284 MovDir[x][y] = game.wind_direction;
5285 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5287 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5289 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5290 MovDir[x][y] = left_dir;
5291 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5292 MovDir[x][y] = right_dir;
5294 if (MovDir[x][y] != old_move_dir)
5295 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5297 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5299 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5300 MovDir[x][y] = right_dir;
5301 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5302 MovDir[x][y] = left_dir;
5304 if (MovDir[x][y] != old_move_dir)
5305 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5307 else if (move_pattern == MV_TOWARDS_PLAYER ||
5308 move_pattern == MV_AWAY_FROM_PLAYER)
5310 int attr_x = -1, attr_y = -1;
5312 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5323 for (i = 0; i < MAX_PLAYERS; i++)
5325 struct PlayerInfo *player = &stored_player[i];
5326 int jx = player->jx, jy = player->jy;
5328 if (!player->active)
5332 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5340 MovDir[x][y] = MV_NONE;
5342 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5343 else if (attr_x > x)
5344 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5346 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5347 else if (attr_y > y)
5348 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5350 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5352 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5354 boolean first_horiz = RND(2);
5355 int new_move_dir = MovDir[x][y];
5357 if (element_info[element].move_stepsize == 0) /* "not moving" */
5359 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5360 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5366 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5367 Moving2Blocked(x, y, &newx, &newy);
5369 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5373 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5374 Moving2Blocked(x, y, &newx, &newy);
5376 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5379 MovDir[x][y] = old_move_dir;
5382 else if (move_pattern == MV_WHEN_PUSHED ||
5383 move_pattern == MV_WHEN_DROPPED)
5385 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5386 MovDir[x][y] = MV_NONE;
5390 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5392 static int test_xy[7][2] =
5402 static int test_dir[7] =
5412 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5413 int move_preference = -1000000; /* start with very low preference */
5414 int new_move_dir = MV_NONE;
5415 int start_test = RND(4);
5418 for (i = 0; i < NUM_DIRECTIONS; i++)
5420 int move_dir = test_dir[start_test + i];
5421 int move_dir_preference;
5423 xx = x + test_xy[start_test + i][0];
5424 yy = y + test_xy[start_test + i][1];
5426 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5427 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5429 new_move_dir = move_dir;
5434 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5437 move_dir_preference = -1 * RunnerVisit[xx][yy];
5438 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5439 move_dir_preference = PlayerVisit[xx][yy];
5441 if (move_dir_preference > move_preference)
5443 /* prefer field that has not been visited for the longest time */
5444 move_preference = move_dir_preference;
5445 new_move_dir = move_dir;
5447 else if (move_dir_preference == move_preference &&
5448 move_dir == old_move_dir)
5450 /* prefer last direction when all directions are preferred equally */
5451 move_preference = move_dir_preference;
5452 new_move_dir = move_dir;
5456 MovDir[x][y] = new_move_dir;
5457 if (old_move_dir != new_move_dir)
5458 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5462 static void TurnRound(int x, int y)
5464 int direction = MovDir[x][y];
5468 GfxDir[x][y] = MovDir[x][y];
5470 if (direction != MovDir[x][y])
5474 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5476 ResetGfxFrame(x, y, FALSE);
5479 static boolean JustBeingPushed(int x, int y)
5483 for (i = 0; i < MAX_PLAYERS; i++)
5485 struct PlayerInfo *player = &stored_player[i];
5487 if (player->active && player->is_pushing && player->MovPos)
5489 int next_jx = player->jx + (player->jx - player->last_jx);
5490 int next_jy = player->jy + (player->jy - player->last_jy);
5492 if (x == next_jx && y == next_jy)
5500 void StartMoving(int x, int y)
5502 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5503 int element = Feld[x][y];
5508 if (MovDelay[x][y] == 0)
5509 GfxAction[x][y] = ACTION_DEFAULT;
5511 if (CAN_FALL(element) && y < lev_fieldy - 1)
5513 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5514 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5515 if (JustBeingPushed(x, y))
5518 if (element == EL_QUICKSAND_FULL)
5520 if (IS_FREE(x, y + 1))
5522 InitMovingField(x, y, MV_DOWN);
5523 started_moving = TRUE;
5525 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5526 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5527 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5528 Store[x][y] = EL_ROCK;
5530 Store[x][y] = EL_ROCK;
5533 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5535 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5537 if (!MovDelay[x][y])
5538 MovDelay[x][y] = TILEY + 1;
5547 Feld[x][y] = EL_QUICKSAND_EMPTY;
5548 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5549 Store[x][y + 1] = Store[x][y];
5552 PlayLevelSoundAction(x, y, ACTION_FILLING);
5555 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5556 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5558 InitMovingField(x, y, MV_DOWN);
5559 started_moving = TRUE;
5561 Feld[x][y] = EL_QUICKSAND_FILLING;
5562 Store[x][y] = element;
5564 PlayLevelSoundAction(x, y, ACTION_FILLING);
5566 else if (element == EL_MAGIC_WALL_FULL)
5568 if (IS_FREE(x, y + 1))
5570 InitMovingField(x, y, MV_DOWN);
5571 started_moving = TRUE;
5573 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5574 Store[x][y] = EL_CHANGED(Store[x][y]);
5576 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5578 if (!MovDelay[x][y])
5579 MovDelay[x][y] = TILEY/4 + 1;
5588 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5589 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5590 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5594 else if (element == EL_BD_MAGIC_WALL_FULL)
5596 if (IS_FREE(x, y + 1))
5598 InitMovingField(x, y, MV_DOWN);
5599 started_moving = TRUE;
5601 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5602 Store[x][y] = EL_CHANGED2(Store[x][y]);
5604 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5606 if (!MovDelay[x][y])
5607 MovDelay[x][y] = TILEY/4 + 1;
5616 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5617 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5618 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5622 else if (CAN_PASS_MAGIC_WALL(element) &&
5623 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5624 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5626 InitMovingField(x, y, MV_DOWN);
5627 started_moving = TRUE;
5630 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5631 EL_BD_MAGIC_WALL_FILLING);
5632 Store[x][y] = element;
5634 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5636 SplashAcid(x, y + 1);
5638 InitMovingField(x, y, MV_DOWN);
5639 started_moving = TRUE;
5641 Store[x][y] = EL_ACID;
5644 #if USE_FIX_IMPACT_COLLISION
5645 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5646 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
5648 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5649 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5651 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5652 CAN_FALL(element) && WasJustFalling[x][y] &&
5653 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5655 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5656 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5657 (Feld[x][y + 1] == EL_BLOCKED)))
5659 /* this is needed for a special case not covered by calling "Impact()"
5660 from "ContinueMoving()": if an element moves to a tile directly below
5661 another element which was just falling on that tile (which was empty
5662 in the previous frame), the falling element above would just stop
5663 instead of smashing the element below (in previous version, the above
5664 element was just checked for "moving" instead of "falling", resulting
5665 in incorrect smashes caused by horizontal movement of the above
5666 element; also, the case of the player being the element to smash was
5667 simply not covered here... :-/ ) */
5669 CheckCollision[x][y] = 0;
5670 CheckImpact[x][y] = 0;
5674 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5676 if (MovDir[x][y] == MV_NONE)
5678 InitMovingField(x, y, MV_DOWN);
5679 started_moving = TRUE;
5682 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5684 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5685 MovDir[x][y] = MV_DOWN;
5687 InitMovingField(x, y, MV_DOWN);
5688 started_moving = TRUE;
5690 else if (element == EL_AMOEBA_DROP)
5692 Feld[x][y] = EL_AMOEBA_GROWING;
5693 Store[x][y] = EL_AMOEBA_WET;
5695 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5696 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5697 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5698 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5700 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5701 (IS_FREE(x - 1, y + 1) ||
5702 Feld[x - 1][y + 1] == EL_ACID));
5703 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5704 (IS_FREE(x + 1, y + 1) ||
5705 Feld[x + 1][y + 1] == EL_ACID));
5706 boolean can_fall_any = (can_fall_left || can_fall_right);
5707 boolean can_fall_both = (can_fall_left && can_fall_right);
5708 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5710 #if USE_NEW_ALL_SLIPPERY
5711 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5713 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5714 can_fall_right = FALSE;
5715 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5716 can_fall_left = FALSE;
5717 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5718 can_fall_right = FALSE;
5719 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5720 can_fall_left = FALSE;
5722 can_fall_any = (can_fall_left || can_fall_right);
5723 can_fall_both = FALSE;
5726 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5728 if (slippery_type == SLIPPERY_ONLY_LEFT)
5729 can_fall_right = FALSE;
5730 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5731 can_fall_left = FALSE;
5732 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5733 can_fall_right = FALSE;
5734 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5735 can_fall_left = FALSE;
5737 can_fall_any = (can_fall_left || can_fall_right);
5738 can_fall_both = (can_fall_left && can_fall_right);
5742 #if USE_NEW_ALL_SLIPPERY
5744 #if USE_NEW_SP_SLIPPERY
5745 /* !!! better use the same properties as for custom elements here !!! */
5746 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5747 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5749 can_fall_right = FALSE; /* slip down on left side */
5750 can_fall_both = FALSE;
5755 #if USE_NEW_ALL_SLIPPERY
5758 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5759 can_fall_right = FALSE; /* slip down on left side */
5761 can_fall_left = !(can_fall_right = RND(2));
5763 can_fall_both = FALSE;
5768 if (game.emulation == EMU_BOULDERDASH ||
5769 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5770 can_fall_right = FALSE; /* slip down on left side */
5772 can_fall_left = !(can_fall_right = RND(2));
5774 can_fall_both = FALSE;
5780 /* if not determined otherwise, prefer left side for slipping down */
5781 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5782 started_moving = TRUE;
5786 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5788 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5791 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5792 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5793 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5794 int belt_dir = game.belt_dir[belt_nr];
5796 if ((belt_dir == MV_LEFT && left_is_free) ||
5797 (belt_dir == MV_RIGHT && right_is_free))
5799 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5801 InitMovingField(x, y, belt_dir);
5802 started_moving = TRUE;
5804 Pushed[x][y] = TRUE;
5805 Pushed[nextx][y] = TRUE;
5807 GfxAction[x][y] = ACTION_DEFAULT;
5811 MovDir[x][y] = 0; /* if element was moving, stop it */
5816 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5818 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5820 if (CAN_MOVE(element) && !started_moving)
5823 int move_pattern = element_info[element].move_pattern;
5828 if (MovDir[x][y] == MV_NONE)
5830 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5831 x, y, element, element_info[element].token_name);
5832 printf("StartMoving(): This should never happen!\n");
5837 Moving2Blocked(x, y, &newx, &newy);
5839 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5842 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5843 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5845 WasJustMoving[x][y] = 0;
5846 CheckCollision[x][y] = 0;
5848 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5850 if (Feld[x][y] != element) /* element has changed */
5854 if (!MovDelay[x][y]) /* start new movement phase */
5856 /* all objects that can change their move direction after each step
5857 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5859 if (element != EL_YAMYAM &&
5860 element != EL_DARK_YAMYAM &&
5861 element != EL_PACMAN &&
5862 !(move_pattern & MV_ANY_DIRECTION) &&
5863 move_pattern != MV_TURNING_LEFT &&
5864 move_pattern != MV_TURNING_RIGHT &&
5865 move_pattern != MV_TURNING_LEFT_RIGHT &&
5866 move_pattern != MV_TURNING_RIGHT_LEFT &&
5867 move_pattern != MV_TURNING_RANDOM)
5871 if (MovDelay[x][y] && (element == EL_BUG ||
5872 element == EL_SPACESHIP ||
5873 element == EL_SP_SNIKSNAK ||
5874 element == EL_SP_ELECTRON ||
5875 element == EL_MOLE))
5876 DrawLevelField(x, y);
5880 if (MovDelay[x][y]) /* wait some time before next movement */
5884 if (element == EL_ROBOT ||
5885 element == EL_YAMYAM ||
5886 element == EL_DARK_YAMYAM)
5888 DrawLevelElementAnimationIfNeeded(x, y, element);
5889 PlayLevelSoundAction(x, y, ACTION_WAITING);
5891 else if (element == EL_SP_ELECTRON)
5892 DrawLevelElementAnimationIfNeeded(x, y, element);
5893 else if (element == EL_DRAGON)
5896 int dir = MovDir[x][y];
5897 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5898 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5899 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5900 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5901 dir == MV_UP ? IMG_FLAMES_1_UP :
5902 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5903 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5905 GfxAction[x][y] = ACTION_ATTACKING;
5907 if (IS_PLAYER(x, y))
5908 DrawPlayerField(x, y);
5910 DrawLevelField(x, y);
5912 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5914 for (i = 1; i <= 3; i++)
5916 int xx = x + i * dx;
5917 int yy = y + i * dy;
5918 int sx = SCREENX(xx);
5919 int sy = SCREENY(yy);
5920 int flame_graphic = graphic + (i - 1);
5922 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5927 int flamed = MovingOrBlocked2Element(xx, yy);
5931 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5933 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5934 RemoveMovingField(xx, yy);
5936 RemoveField(xx, yy);
5938 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5941 RemoveMovingField(xx, yy);
5944 ChangeDelay[xx][yy] = 0;
5946 Feld[xx][yy] = EL_FLAMES;
5948 if (IN_SCR_FIELD(sx, sy))
5950 DrawLevelFieldCrumbledSand(xx, yy);
5951 DrawGraphic(sx, sy, flame_graphic, frame);
5956 if (Feld[xx][yy] == EL_FLAMES)
5957 Feld[xx][yy] = EL_EMPTY;
5958 DrawLevelField(xx, yy);
5963 if (MovDelay[x][y]) /* element still has to wait some time */
5965 PlayLevelSoundAction(x, y, ACTION_WAITING);
5971 /* now make next step */
5973 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5975 if (DONT_COLLIDE_WITH(element) &&
5976 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5977 !PLAYER_ENEMY_PROTECTED(newx, newy))
5979 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5984 else if (CAN_MOVE_INTO_ACID(element) &&
5985 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5986 !IS_MV_DIAGONAL(MovDir[x][y]) &&
5987 (MovDir[x][y] == MV_DOWN ||
5988 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5990 SplashAcid(newx, newy);
5991 Store[x][y] = EL_ACID;
5993 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5995 if (Feld[newx][newy] == EL_EXIT_OPEN)
5998 DrawLevelField(x, y);
6000 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6001 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6002 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6004 local_player->friends_still_needed--;
6005 if (!local_player->friends_still_needed &&
6006 !local_player->GameOver && AllPlayersGone)
6007 PlayerWins(local_player);
6011 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6013 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6014 DrawLevelField(newx, newy);
6016 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6018 else if (!IS_FREE(newx, newy))
6020 GfxAction[x][y] = ACTION_WAITING;
6022 if (IS_PLAYER(x, y))
6023 DrawPlayerField(x, y);
6025 DrawLevelField(x, y);
6030 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6032 if (IS_FOOD_PIG(Feld[newx][newy]))
6034 if (IS_MOVING(newx, newy))
6035 RemoveMovingField(newx, newy);
6038 Feld[newx][newy] = EL_EMPTY;
6039 DrawLevelField(newx, newy);
6042 PlayLevelSound(x, y, SND_PIG_DIGGING);
6044 else if (!IS_FREE(newx, newy))
6046 if (IS_PLAYER(x, y))
6047 DrawPlayerField(x, y);
6049 DrawLevelField(x, y);
6054 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6056 if (Store[x][y] != EL_EMPTY)
6058 boolean can_clone = FALSE;
6061 /* check if element to clone is still there */
6062 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6064 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6072 /* cannot clone or target field not free anymore -- do not clone */
6073 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6074 Store[x][y] = EL_EMPTY;
6077 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6079 if (IS_MV_DIAGONAL(MovDir[x][y]))
6081 int diagonal_move_dir = MovDir[x][y];
6082 int stored = Store[x][y];
6083 int change_delay = 8;
6086 /* android is moving diagonally */
6088 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6090 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6091 GfxElement[x][y] = EL_EMC_ANDROID;
6092 GfxAction[x][y] = ACTION_SHRINKING;
6093 GfxDir[x][y] = diagonal_move_dir;
6094 ChangeDelay[x][y] = change_delay;
6096 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6099 DrawLevelGraphicAnimation(x, y, graphic);
6100 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6102 if (Feld[newx][newy] == EL_ACID)
6104 SplashAcid(newx, newy);
6109 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6111 Store[newx][newy] = EL_EMC_ANDROID;
6112 GfxElement[newx][newy] = EL_EMC_ANDROID;
6113 GfxAction[newx][newy] = ACTION_GROWING;
6114 GfxDir[newx][newy] = diagonal_move_dir;
6115 ChangeDelay[newx][newy] = change_delay;
6117 graphic = el_act_dir2img(GfxElement[newx][newy],
6118 GfxAction[newx][newy], GfxDir[newx][newy]);
6120 DrawLevelGraphicAnimation(newx, newy, graphic);
6121 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6127 Feld[newx][newy] = EL_EMPTY;
6128 DrawLevelField(newx, newy);
6130 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6133 else if (!IS_FREE(newx, newy))
6136 if (IS_PLAYER(x, y))
6137 DrawPlayerField(x, y);
6139 DrawLevelField(x, y);
6145 else if (IS_CUSTOM_ELEMENT(element) &&
6146 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6148 int new_element = Feld[newx][newy];
6150 if (!IS_FREE(newx, newy))
6152 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6153 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6156 /* no element can dig solid indestructible elements */
6157 if (IS_INDESTRUCTIBLE(new_element) &&
6158 !IS_DIGGABLE(new_element) &&
6159 !IS_COLLECTIBLE(new_element))
6162 if (AmoebaNr[newx][newy] &&
6163 (new_element == EL_AMOEBA_FULL ||
6164 new_element == EL_BD_AMOEBA ||
6165 new_element == EL_AMOEBA_GROWING))
6167 AmoebaCnt[AmoebaNr[newx][newy]]--;
6168 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6171 if (IS_MOVING(newx, newy))
6172 RemoveMovingField(newx, newy);
6175 RemoveField(newx, newy);
6176 DrawLevelField(newx, newy);
6179 /* if digged element was about to explode, prevent the explosion */
6180 ExplodeField[newx][newy] = EX_TYPE_NONE;
6182 PlayLevelSoundAction(x, y, action);
6185 Store[newx][newy] = EL_EMPTY;
6187 /* this makes it possible to leave the removed element again */
6188 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6189 Store[newx][newy] = new_element;
6191 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6193 int move_leave_element = element_info[element].move_leave_element;
6195 /* this makes it possible to leave the removed element again */
6196 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6197 new_element : move_leave_element);
6201 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6203 RunnerVisit[x][y] = FrameCounter;
6204 PlayerVisit[x][y] /= 8; /* expire player visit path */
6207 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6209 if (!IS_FREE(newx, newy))
6211 if (IS_PLAYER(x, y))
6212 DrawPlayerField(x, y);
6214 DrawLevelField(x, y);
6220 boolean wanna_flame = !RND(10);
6221 int dx = newx - x, dy = newy - y;
6222 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6223 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6224 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6225 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6226 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6227 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6230 IS_CLASSIC_ENEMY(element1) ||
6231 IS_CLASSIC_ENEMY(element2)) &&
6232 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6233 element1 != EL_FLAMES && element2 != EL_FLAMES)
6235 ResetGfxAnimation(x, y);
6236 GfxAction[x][y] = ACTION_ATTACKING;
6238 if (IS_PLAYER(x, y))
6239 DrawPlayerField(x, y);
6241 DrawLevelField(x, y);
6243 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6245 MovDelay[x][y] = 50;
6249 RemoveField(newx, newy);
6251 Feld[newx][newy] = EL_FLAMES;
6252 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6255 RemoveField(newx1, newy1);
6257 Feld[newx1][newy1] = EL_FLAMES;
6259 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6262 RemoveField(newx2, newy2);
6264 Feld[newx2][newy2] = EL_FLAMES;
6271 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6272 Feld[newx][newy] == EL_DIAMOND)
6274 if (IS_MOVING(newx, newy))
6275 RemoveMovingField(newx, newy);
6278 Feld[newx][newy] = EL_EMPTY;
6279 DrawLevelField(newx, newy);
6282 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6284 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6285 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6287 if (AmoebaNr[newx][newy])
6289 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6290 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6291 Feld[newx][newy] == EL_BD_AMOEBA)
6292 AmoebaCnt[AmoebaNr[newx][newy]]--;
6297 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6299 RemoveMovingField(newx, newy);
6302 if (IS_MOVING(newx, newy))
6304 RemoveMovingField(newx, newy);
6309 Feld[newx][newy] = EL_EMPTY;
6310 DrawLevelField(newx, newy);
6313 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6315 else if ((element == EL_PACMAN || element == EL_MOLE)
6316 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6318 if (AmoebaNr[newx][newy])
6320 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6321 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6322 Feld[newx][newy] == EL_BD_AMOEBA)
6323 AmoebaCnt[AmoebaNr[newx][newy]]--;
6326 if (element == EL_MOLE)
6328 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6329 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6331 ResetGfxAnimation(x, y);
6332 GfxAction[x][y] = ACTION_DIGGING;
6333 DrawLevelField(x, y);
6335 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6337 return; /* wait for shrinking amoeba */
6339 else /* element == EL_PACMAN */
6341 Feld[newx][newy] = EL_EMPTY;
6342 DrawLevelField(newx, newy);
6343 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6346 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6347 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6348 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6350 /* wait for shrinking amoeba to completely disappear */
6353 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6355 /* object was running against a wall */
6360 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6361 if (move_pattern & MV_ANY_DIRECTION &&
6362 move_pattern == MovDir[x][y])
6364 int blocking_element =
6365 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6367 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6370 element = Feld[x][y]; /* element might have changed */
6374 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6375 DrawLevelElementAnimation(x, y, element);
6377 if (DONT_TOUCH(element))
6378 TestIfBadThingTouchesPlayer(x, y);
6383 InitMovingField(x, y, MovDir[x][y]);
6385 PlayLevelSoundAction(x, y, ACTION_MOVING);
6389 ContinueMoving(x, y);
6392 void ContinueMoving(int x, int y)
6394 int element = Feld[x][y];
6395 struct ElementInfo *ei = &element_info[element];
6396 int direction = MovDir[x][y];
6397 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6398 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6399 int newx = x + dx, newy = y + dy;
6400 int stored = Store[x][y];
6401 int stored_new = Store[newx][newy];
6402 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6403 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6404 boolean last_line = (newy == lev_fieldy - 1);
6406 MovPos[x][y] += getElementMoveStepsize(x, y);
6408 if (pushed_by_player) /* special case: moving object pushed by player */
6409 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6411 if (ABS(MovPos[x][y]) < TILEX)
6413 DrawLevelField(x, y);
6415 return; /* element is still moving */
6418 /* element reached destination field */
6420 Feld[x][y] = EL_EMPTY;
6421 Feld[newx][newy] = element;
6422 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6424 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6426 element = Feld[newx][newy] = EL_ACID;
6428 else if (element == EL_MOLE)
6430 Feld[x][y] = EL_SAND;
6432 DrawLevelFieldCrumbledSandNeighbours(x, y);
6434 else if (element == EL_QUICKSAND_FILLING)
6436 element = Feld[newx][newy] = get_next_element(element);
6437 Store[newx][newy] = Store[x][y];
6439 else if (element == EL_QUICKSAND_EMPTYING)
6441 Feld[x][y] = get_next_element(element);
6442 element = Feld[newx][newy] = Store[x][y];
6444 else if (element == EL_MAGIC_WALL_FILLING)
6446 element = Feld[newx][newy] = get_next_element(element);
6447 if (!game.magic_wall_active)
6448 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6449 Store[newx][newy] = Store[x][y];
6451 else if (element == EL_MAGIC_WALL_EMPTYING)
6453 Feld[x][y] = get_next_element(element);
6454 if (!game.magic_wall_active)
6455 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6456 element = Feld[newx][newy] = Store[x][y];
6458 #if USE_NEW_CUSTOM_VALUE
6459 InitField(newx, newy, FALSE);
6462 else if (element == EL_BD_MAGIC_WALL_FILLING)
6464 element = Feld[newx][newy] = get_next_element(element);
6465 if (!game.magic_wall_active)
6466 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6467 Store[newx][newy] = Store[x][y];
6469 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6471 Feld[x][y] = get_next_element(element);
6472 if (!game.magic_wall_active)
6473 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6474 element = Feld[newx][newy] = Store[x][y];
6476 #if USE_NEW_CUSTOM_VALUE
6477 InitField(newx, newy, FALSE);
6480 else if (element == EL_AMOEBA_DROPPING)
6482 Feld[x][y] = get_next_element(element);
6483 element = Feld[newx][newy] = Store[x][y];
6485 else if (element == EL_SOKOBAN_OBJECT)
6488 Feld[x][y] = Back[x][y];
6490 if (Back[newx][newy])
6491 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6493 Back[x][y] = Back[newx][newy] = 0;
6496 Store[x][y] = EL_EMPTY;
6501 MovDelay[newx][newy] = 0;
6503 if (CAN_CHANGE_OR_HAS_ACTION(element))
6505 /* copy element change control values to new field */
6506 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6507 ChangePage[newx][newy] = ChangePage[x][y];
6508 ChangeCount[newx][newy] = ChangeCount[x][y];
6509 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6512 #if USE_NEW_CUSTOM_VALUE
6513 CustomValue[newx][newy] = CustomValue[x][y];
6516 ChangeDelay[x][y] = 0;
6517 ChangePage[x][y] = -1;
6518 ChangeCount[x][y] = 0;
6519 ChangeEvent[x][y] = -1;
6521 #if USE_NEW_CUSTOM_VALUE
6522 CustomValue[x][y] = 0;
6525 /* copy animation control values to new field */
6526 GfxFrame[newx][newy] = GfxFrame[x][y];
6527 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6528 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6529 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6531 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6533 /* some elements can leave other elements behind after moving */
6535 if (ei->move_leave_element != EL_EMPTY &&
6536 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6537 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6539 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6540 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6541 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6544 int move_leave_element = ei->move_leave_element;
6548 /* this makes it possible to leave the removed element again */
6549 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6550 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6552 /* this makes it possible to leave the removed element again */
6553 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6554 move_leave_element = stored;
6557 /* this makes it possible to leave the removed element again */
6558 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6559 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6560 move_leave_element = stored;
6563 Feld[x][y] = move_leave_element;
6565 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6566 MovDir[x][y] = direction;
6568 InitField(x, y, FALSE);
6570 if (GFX_CRUMBLED(Feld[x][y]))
6571 DrawLevelFieldCrumbledSandNeighbours(x, y);
6573 if (ELEM_IS_PLAYER(move_leave_element))
6574 RelocatePlayer(x, y, move_leave_element);
6577 /* do this after checking for left-behind element */
6578 ResetGfxAnimation(x, y); /* reset animation values for old field */
6580 if (!CAN_MOVE(element) ||
6581 (CAN_FALL(element) && direction == MV_DOWN &&
6582 (element == EL_SPRING ||
6583 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6584 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6585 GfxDir[x][y] = MovDir[newx][newy] = 0;
6587 DrawLevelField(x, y);
6588 DrawLevelField(newx, newy);
6590 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6592 /* prevent pushed element from moving on in pushed direction */
6593 if (pushed_by_player && CAN_MOVE(element) &&
6594 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6595 !(element_info[element].move_pattern & direction))
6596 TurnRound(newx, newy);
6598 /* prevent elements on conveyor belt from moving on in last direction */
6599 if (pushed_by_conveyor && CAN_FALL(element) &&
6600 direction & MV_HORIZONTAL)
6601 MovDir[newx][newy] = 0;
6603 if (!pushed_by_player)
6605 int nextx = newx + dx, nexty = newy + dy;
6606 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6608 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6610 if (CAN_FALL(element) && direction == MV_DOWN)
6611 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6613 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6614 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6616 #if USE_FIX_IMPACT_COLLISION
6617 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
6618 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
6622 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6624 TestIfBadThingTouchesPlayer(newx, newy);
6625 TestIfBadThingTouchesFriend(newx, newy);
6627 if (!IS_CUSTOM_ELEMENT(element))
6628 TestIfBadThingTouchesOtherBadThing(newx, newy);
6630 else if (element == EL_PENGUIN)
6631 TestIfFriendTouchesBadThing(newx, newy);
6633 /* give the player one last chance (one more frame) to move away */
6634 if (CAN_FALL(element) && direction == MV_DOWN &&
6635 (last_line || (!IS_FREE(x, newy + 1) &&
6636 (!IS_PLAYER(x, newy + 1) ||
6637 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6640 if (pushed_by_player && !game.use_change_when_pushing_bug)
6642 int push_side = MV_DIR_OPPOSITE(direction);
6643 struct PlayerInfo *player = PLAYERINFO(x, y);
6645 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6646 player->index_bit, push_side);
6647 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6648 player->index_bit, push_side);
6651 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6652 MovDelay[newx][newy] = 1;
6654 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6656 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6659 if (ChangePage[newx][newy] != -1) /* delayed change */
6661 int page = ChangePage[newx][newy];
6662 struct ElementChangeInfo *change = &ei->change_page[page];
6664 ChangePage[newx][newy] = -1;
6666 if (change->can_change)
6668 if (ChangeElement(newx, newy, element, page))
6670 if (change->post_change_function)
6671 change->post_change_function(newx, newy);
6675 if (change->has_action)
6676 ExecuteCustomElementAction(newx, newy, element, page);
6680 TestIfElementHitsCustomElement(newx, newy, direction);
6681 TestIfPlayerTouchesCustomElement(newx, newy);
6682 TestIfElementTouchesCustomElement(newx, newy);
6684 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6685 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6686 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6687 MV_DIR_OPPOSITE(direction));
6690 int AmoebeNachbarNr(int ax, int ay)
6693 int element = Feld[ax][ay];
6695 static int xy[4][2] =
6703 for (i = 0; i < NUM_DIRECTIONS; i++)
6705 int x = ax + xy[i][0];
6706 int y = ay + xy[i][1];
6708 if (!IN_LEV_FIELD(x, y))
6711 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6712 group_nr = AmoebaNr[x][y];
6718 void AmoebenVereinigen(int ax, int ay)
6720 int i, x, y, xx, yy;
6721 int new_group_nr = AmoebaNr[ax][ay];
6722 static int xy[4][2] =
6730 if (new_group_nr == 0)
6733 for (i = 0; i < NUM_DIRECTIONS; i++)
6738 if (!IN_LEV_FIELD(x, y))
6741 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6742 Feld[x][y] == EL_BD_AMOEBA ||
6743 Feld[x][y] == EL_AMOEBA_DEAD) &&
6744 AmoebaNr[x][y] != new_group_nr)
6746 int old_group_nr = AmoebaNr[x][y];
6748 if (old_group_nr == 0)
6751 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6752 AmoebaCnt[old_group_nr] = 0;
6753 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6754 AmoebaCnt2[old_group_nr] = 0;
6756 SCAN_PLAYFIELD(xx, yy)
6758 if (AmoebaNr[xx][yy] == old_group_nr)
6759 AmoebaNr[xx][yy] = new_group_nr;
6765 void AmoebeUmwandeln(int ax, int ay)
6769 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6771 int group_nr = AmoebaNr[ax][ay];
6776 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6777 printf("AmoebeUmwandeln(): This should never happen!\n");
6782 SCAN_PLAYFIELD(x, y)
6784 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6787 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6791 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6792 SND_AMOEBA_TURNING_TO_GEM :
6793 SND_AMOEBA_TURNING_TO_ROCK));
6798 static int xy[4][2] =
6806 for (i = 0; i < NUM_DIRECTIONS; i++)
6811 if (!IN_LEV_FIELD(x, y))
6814 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6816 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6817 SND_AMOEBA_TURNING_TO_GEM :
6818 SND_AMOEBA_TURNING_TO_ROCK));
6825 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6828 int group_nr = AmoebaNr[ax][ay];
6829 boolean done = FALSE;
6834 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6835 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6840 SCAN_PLAYFIELD(x, y)
6842 if (AmoebaNr[x][y] == group_nr &&
6843 (Feld[x][y] == EL_AMOEBA_DEAD ||
6844 Feld[x][y] == EL_BD_AMOEBA ||
6845 Feld[x][y] == EL_AMOEBA_GROWING))
6848 Feld[x][y] = new_element;
6849 InitField(x, y, FALSE);
6850 DrawLevelField(x, y);
6856 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6857 SND_BD_AMOEBA_TURNING_TO_ROCK :
6858 SND_BD_AMOEBA_TURNING_TO_GEM));
6861 void AmoebeWaechst(int x, int y)
6863 static unsigned long sound_delay = 0;
6864 static unsigned long sound_delay_value = 0;
6866 if (!MovDelay[x][y]) /* start new growing cycle */
6870 if (DelayReached(&sound_delay, sound_delay_value))
6872 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6873 sound_delay_value = 30;
6877 if (MovDelay[x][y]) /* wait some time before growing bigger */
6880 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6882 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6883 6 - MovDelay[x][y]);
6885 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6888 if (!MovDelay[x][y])
6890 Feld[x][y] = Store[x][y];
6892 DrawLevelField(x, y);
6897 void AmoebaDisappearing(int x, int y)
6899 static unsigned long sound_delay = 0;
6900 static unsigned long sound_delay_value = 0;
6902 if (!MovDelay[x][y]) /* start new shrinking cycle */
6906 if (DelayReached(&sound_delay, sound_delay_value))
6907 sound_delay_value = 30;
6910 if (MovDelay[x][y]) /* wait some time before shrinking */
6913 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6915 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6916 6 - MovDelay[x][y]);
6918 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6921 if (!MovDelay[x][y])
6923 Feld[x][y] = EL_EMPTY;
6924 DrawLevelField(x, y);
6926 /* don't let mole enter this field in this cycle;
6927 (give priority to objects falling to this field from above) */
6933 void AmoebeAbleger(int ax, int ay)
6936 int element = Feld[ax][ay];
6937 int graphic = el2img(element);
6938 int newax = ax, neway = ay;
6939 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6940 static int xy[4][2] =
6948 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6950 Feld[ax][ay] = EL_AMOEBA_DEAD;
6951 DrawLevelField(ax, ay);
6955 if (IS_ANIMATED(graphic))
6956 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6958 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6959 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6961 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6964 if (MovDelay[ax][ay])
6968 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6971 int x = ax + xy[start][0];
6972 int y = ay + xy[start][1];
6974 if (!IN_LEV_FIELD(x, y))
6977 if (IS_FREE(x, y) ||
6978 CAN_GROW_INTO(Feld[x][y]) ||
6979 Feld[x][y] == EL_QUICKSAND_EMPTY)
6985 if (newax == ax && neway == ay)
6988 else /* normal or "filled" (BD style) amoeba */
6991 boolean waiting_for_player = FALSE;
6993 for (i = 0; i < NUM_DIRECTIONS; i++)
6995 int j = (start + i) % 4;
6996 int x = ax + xy[j][0];
6997 int y = ay + xy[j][1];
6999 if (!IN_LEV_FIELD(x, y))
7002 if (IS_FREE(x, y) ||
7003 CAN_GROW_INTO(Feld[x][y]) ||
7004 Feld[x][y] == EL_QUICKSAND_EMPTY)
7010 else if (IS_PLAYER(x, y))
7011 waiting_for_player = TRUE;
7014 if (newax == ax && neway == ay) /* amoeba cannot grow */
7016 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7018 Feld[ax][ay] = EL_AMOEBA_DEAD;
7019 DrawLevelField(ax, ay);
7020 AmoebaCnt[AmoebaNr[ax][ay]]--;
7022 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7024 if (element == EL_AMOEBA_FULL)
7025 AmoebeUmwandeln(ax, ay);
7026 else if (element == EL_BD_AMOEBA)
7027 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7032 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7034 /* amoeba gets larger by growing in some direction */
7036 int new_group_nr = AmoebaNr[ax][ay];
7039 if (new_group_nr == 0)
7041 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7042 printf("AmoebeAbleger(): This should never happen!\n");
7047 AmoebaNr[newax][neway] = new_group_nr;
7048 AmoebaCnt[new_group_nr]++;
7049 AmoebaCnt2[new_group_nr]++;
7051 /* if amoeba touches other amoeba(s) after growing, unify them */
7052 AmoebenVereinigen(newax, neway);
7054 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7056 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7062 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7063 (neway == lev_fieldy - 1 && newax != ax))
7065 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7066 Store[newax][neway] = element;
7068 else if (neway == ay || element == EL_EMC_DRIPPER)
7070 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7072 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7076 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7077 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7078 Store[ax][ay] = EL_AMOEBA_DROP;
7079 ContinueMoving(ax, ay);
7083 DrawLevelField(newax, neway);
7086 void Life(int ax, int ay)
7090 int element = Feld[ax][ay];
7091 int graphic = el2img(element);
7092 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7094 boolean changed = FALSE;
7096 if (IS_ANIMATED(graphic))
7097 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7102 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7103 MovDelay[ax][ay] = life_time;
7105 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7108 if (MovDelay[ax][ay])
7112 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7114 int xx = ax+x1, yy = ay+y1;
7117 if (!IN_LEV_FIELD(xx, yy))
7120 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7122 int x = xx+x2, y = yy+y2;
7124 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7127 if (((Feld[x][y] == element ||
7128 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7130 (IS_FREE(x, y) && Stop[x][y]))
7134 if (xx == ax && yy == ay) /* field in the middle */
7136 if (nachbarn < life_parameter[0] ||
7137 nachbarn > life_parameter[1])
7139 Feld[xx][yy] = EL_EMPTY;
7141 DrawLevelField(xx, yy);
7142 Stop[xx][yy] = TRUE;
7146 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7147 { /* free border field */
7148 if (nachbarn >= life_parameter[2] &&
7149 nachbarn <= life_parameter[3])
7151 Feld[xx][yy] = element;
7152 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7154 DrawLevelField(xx, yy);
7155 Stop[xx][yy] = TRUE;
7162 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7163 SND_GAME_OF_LIFE_GROWING);
7166 static void InitRobotWheel(int x, int y)
7168 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7171 static void RunRobotWheel(int x, int y)
7173 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7176 static void StopRobotWheel(int x, int y)
7178 if (ZX == x && ZY == y)
7182 static void InitTimegateWheel(int x, int y)
7184 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7187 static void RunTimegateWheel(int x, int y)
7189 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7192 static void InitMagicBallDelay(int x, int y)
7195 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7197 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7201 static void ActivateMagicBall(int bx, int by)
7205 if (level.ball_random)
7207 int pos_border = RND(8); /* select one of the eight border elements */
7208 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7209 int xx = pos_content % 3;
7210 int yy = pos_content / 3;
7215 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7216 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7220 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7222 int xx = x - bx + 1;
7223 int yy = y - by + 1;
7225 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7226 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7230 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7233 void CheckExit(int x, int y)
7235 if (local_player->gems_still_needed > 0 ||
7236 local_player->sokobanfields_still_needed > 0 ||
7237 local_player->lights_still_needed > 0)
7239 int element = Feld[x][y];
7240 int graphic = el2img(element);
7242 if (IS_ANIMATED(graphic))
7243 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7248 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7251 Feld[x][y] = EL_EXIT_OPENING;
7253 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7256 void CheckExitSP(int x, int y)
7258 if (local_player->gems_still_needed > 0)
7260 int element = Feld[x][y];
7261 int graphic = el2img(element);
7263 if (IS_ANIMATED(graphic))
7264 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7269 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7272 Feld[x][y] = EL_SP_EXIT_OPENING;
7274 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7277 static void CloseAllOpenTimegates()
7281 SCAN_PLAYFIELD(x, y)
7283 int element = Feld[x][y];
7285 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7287 Feld[x][y] = EL_TIMEGATE_CLOSING;
7289 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7294 void DrawTwinkleOnField(int x, int y)
7296 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7299 if (Feld[x][y] == EL_BD_DIAMOND)
7302 if (MovDelay[x][y] == 0) /* next animation frame */
7303 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7305 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7309 if (setup.direct_draw && MovDelay[x][y])
7310 SetDrawtoField(DRAW_BUFFERED);
7312 DrawLevelElementAnimation(x, y, Feld[x][y]);
7314 if (MovDelay[x][y] != 0)
7316 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7317 10 - MovDelay[x][y]);
7319 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7321 if (setup.direct_draw)
7325 dest_x = FX + SCREENX(x) * TILEX;
7326 dest_y = FY + SCREENY(y) * TILEY;
7328 BlitBitmap(drawto_field, window,
7329 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7330 SetDrawtoField(DRAW_DIRECT);
7336 void MauerWaechst(int x, int y)
7340 if (!MovDelay[x][y]) /* next animation frame */
7341 MovDelay[x][y] = 3 * delay;
7343 if (MovDelay[x][y]) /* wait some time before next frame */
7347 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7349 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7350 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7352 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7355 if (!MovDelay[x][y])
7357 if (MovDir[x][y] == MV_LEFT)
7359 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7360 DrawLevelField(x - 1, y);
7362 else if (MovDir[x][y] == MV_RIGHT)
7364 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7365 DrawLevelField(x + 1, y);
7367 else if (MovDir[x][y] == MV_UP)
7369 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7370 DrawLevelField(x, y - 1);
7374 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7375 DrawLevelField(x, y + 1);
7378 Feld[x][y] = Store[x][y];
7380 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7381 DrawLevelField(x, y);
7386 void MauerAbleger(int ax, int ay)
7388 int element = Feld[ax][ay];
7389 int graphic = el2img(element);
7390 boolean oben_frei = FALSE, unten_frei = FALSE;
7391 boolean links_frei = FALSE, rechts_frei = FALSE;
7392 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7393 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7394 boolean new_wall = FALSE;
7396 if (IS_ANIMATED(graphic))
7397 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7399 if (!MovDelay[ax][ay]) /* start building new wall */
7400 MovDelay[ax][ay] = 6;
7402 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7405 if (MovDelay[ax][ay])
7409 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7411 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7413 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7415 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7418 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7419 element == EL_EXPANDABLE_WALL_ANY)
7423 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7424 Store[ax][ay-1] = element;
7425 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7426 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7427 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7428 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7433 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7434 Store[ax][ay+1] = element;
7435 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7436 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7437 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7438 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7443 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7444 element == EL_EXPANDABLE_WALL_ANY ||
7445 element == EL_EXPANDABLE_WALL ||
7446 element == EL_BD_EXPANDABLE_WALL)
7450 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7451 Store[ax-1][ay] = element;
7452 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7453 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7454 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7455 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7461 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7462 Store[ax+1][ay] = element;
7463 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7464 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7465 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7466 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7471 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7472 DrawLevelField(ax, ay);
7474 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7476 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7477 unten_massiv = TRUE;
7478 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7479 links_massiv = TRUE;
7480 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7481 rechts_massiv = TRUE;
7483 if (((oben_massiv && unten_massiv) ||
7484 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7485 element == EL_EXPANDABLE_WALL) &&
7486 ((links_massiv && rechts_massiv) ||
7487 element == EL_EXPANDABLE_WALL_VERTICAL))
7488 Feld[ax][ay] = EL_WALL;
7491 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7494 void CheckForDragon(int x, int y)
7497 boolean dragon_found = FALSE;
7498 static int xy[4][2] =
7506 for (i = 0; i < NUM_DIRECTIONS; i++)
7508 for (j = 0; j < 4; j++)
7510 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7512 if (IN_LEV_FIELD(xx, yy) &&
7513 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7515 if (Feld[xx][yy] == EL_DRAGON)
7516 dragon_found = TRUE;
7525 for (i = 0; i < NUM_DIRECTIONS; i++)
7527 for (j = 0; j < 3; j++)
7529 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7531 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7533 Feld[xx][yy] = EL_EMPTY;
7534 DrawLevelField(xx, yy);
7543 static void InitBuggyBase(int x, int y)
7545 int element = Feld[x][y];
7546 int activating_delay = FRAMES_PER_SECOND / 4;
7549 (element == EL_SP_BUGGY_BASE ?
7550 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7551 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7553 element == EL_SP_BUGGY_BASE_ACTIVE ?
7554 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7557 static void WarnBuggyBase(int x, int y)
7560 static int xy[4][2] =
7568 for (i = 0; i < NUM_DIRECTIONS; i++)
7570 int xx = x + xy[i][0];
7571 int yy = y + xy[i][1];
7573 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7575 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7582 static void InitTrap(int x, int y)
7584 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7587 static void ActivateTrap(int x, int y)
7589 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7592 static void ChangeActiveTrap(int x, int y)
7594 int graphic = IMG_TRAP_ACTIVE;
7596 /* if new animation frame was drawn, correct crumbled sand border */
7597 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7598 DrawLevelFieldCrumbledSand(x, y);
7601 static int getSpecialActionElement(int element, int number, int base_element)
7603 return (element != EL_EMPTY ? element :
7604 number != -1 ? base_element + number - 1 :
7608 static int getModifiedActionNumber(int value_old, int operator, int operand,
7609 int value_min, int value_max)
7611 int value_new = (operator == CA_MODE_SET ? operand :
7612 operator == CA_MODE_ADD ? value_old + operand :
7613 operator == CA_MODE_SUBTRACT ? value_old - operand :
7614 operator == CA_MODE_MULTIPLY ? value_old * operand :
7615 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7616 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7619 return (value_new < value_min ? value_min :
7620 value_new > value_max ? value_max :
7624 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7626 struct ElementInfo *ei = &element_info[element];
7627 struct ElementChangeInfo *change = &ei->change_page[page];
7628 int target_element = change->target_element;
7629 int action_type = change->action_type;
7630 int action_mode = change->action_mode;
7631 int action_arg = change->action_arg;
7634 if (!change->has_action)
7637 /* ---------- determine action paramater values -------------------------- */
7639 int level_time_value =
7640 (level.time > 0 ? TimeLeft :
7643 int action_arg_element =
7644 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7645 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7646 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7649 int action_arg_direction =
7650 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7651 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7652 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7653 change->actual_trigger_side :
7654 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7655 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7658 int action_arg_number_min =
7659 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7662 int action_arg_number_max =
7663 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7664 action_type == CA_SET_LEVEL_GEMS ? 999 :
7665 action_type == CA_SET_LEVEL_TIME ? 9999 :
7666 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7667 action_type == CA_SET_CE_VALUE ? 9999 :
7668 action_type == CA_SET_CE_SCORE ? 9999 :
7671 int action_arg_number_reset =
7672 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
7673 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7674 action_type == CA_SET_LEVEL_TIME ? level.time :
7675 action_type == CA_SET_LEVEL_SCORE ? 0 :
7676 #if USE_NEW_CUSTOM_VALUE
7677 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
7679 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7681 action_type == CA_SET_CE_SCORE ? 0 :
7684 int action_arg_number =
7685 (action_arg <= CA_ARG_MAX ? action_arg :
7686 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7687 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7688 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7689 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7690 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7691 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7692 #if USE_NEW_CUSTOM_VALUE
7693 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7695 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7697 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7698 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7699 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7700 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7701 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7702 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
7703 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7704 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
7705 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
7706 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7707 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7710 int action_arg_number_old =
7711 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7712 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7713 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7714 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7715 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7718 int action_arg_number_new =
7719 getModifiedActionNumber(action_arg_number_old,
7720 action_mode, action_arg_number,
7721 action_arg_number_min, action_arg_number_max);
7723 int trigger_player_bits =
7724 (change->actual_trigger_player >= EL_PLAYER_1 &&
7725 change->actual_trigger_player <= EL_PLAYER_4 ?
7726 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7729 int action_arg_player_bits =
7730 (action_arg >= CA_ARG_PLAYER_1 &&
7731 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7732 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7735 /* ---------- execute action -------------------------------------------- */
7737 switch (action_type)
7744 /* ---------- level actions ------------------------------------------- */
7746 case CA_RESTART_LEVEL:
7748 game.restart_level = TRUE;
7753 case CA_SHOW_ENVELOPE:
7755 int element = getSpecialActionElement(action_arg_element,
7756 action_arg_number, EL_ENVELOPE_1);
7758 if (IS_ENVELOPE(element))
7759 local_player->show_envelope = element;
7764 case CA_SET_LEVEL_TIME:
7766 if (level.time > 0) /* only modify limited time value */
7768 TimeLeft = action_arg_number_new;
7770 DrawGameValue_Time(TimeLeft);
7772 if (!TimeLeft && setup.time_limit)
7773 for (i = 0; i < MAX_PLAYERS; i++)
7774 KillPlayer(&stored_player[i]);
7780 case CA_SET_LEVEL_SCORE:
7782 local_player->score = action_arg_number_new;
7784 DrawGameValue_Score(local_player->score);
7789 case CA_SET_LEVEL_GEMS:
7791 local_player->gems_still_needed = action_arg_number_new;
7793 DrawGameValue_Emeralds(local_player->gems_still_needed);
7798 #if !USE_PLAYER_GRAVITY
7799 case CA_SET_LEVEL_GRAVITY:
7801 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7802 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7803 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7809 case CA_SET_LEVEL_WIND:
7811 game.wind_direction = action_arg_direction;
7816 /* ---------- player actions ------------------------------------------ */
7818 case CA_MOVE_PLAYER:
7820 /* automatically move to the next field in specified direction */
7821 for (i = 0; i < MAX_PLAYERS; i++)
7822 if (trigger_player_bits & (1 << i))
7823 stored_player[i].programmed_action = action_arg_direction;
7828 case CA_EXIT_PLAYER:
7830 for (i = 0; i < MAX_PLAYERS; i++)
7831 if (action_arg_player_bits & (1 << i))
7832 PlayerWins(&stored_player[i]);
7837 case CA_KILL_PLAYER:
7839 for (i = 0; i < MAX_PLAYERS; i++)
7840 if (action_arg_player_bits & (1 << i))
7841 KillPlayer(&stored_player[i]);
7846 case CA_SET_PLAYER_KEYS:
7848 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7849 int element = getSpecialActionElement(action_arg_element,
7850 action_arg_number, EL_KEY_1);
7852 if (IS_KEY(element))
7854 for (i = 0; i < MAX_PLAYERS; i++)
7856 if (trigger_player_bits & (1 << i))
7858 stored_player[i].key[KEY_NR(element)] = key_state;
7860 DrawGameDoorValues();
7868 case CA_SET_PLAYER_SPEED:
7870 for (i = 0; i < MAX_PLAYERS; i++)
7872 if (trigger_player_bits & (1 << i))
7874 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7876 if (action_arg == CA_ARG_SPEED_FASTER &&
7877 stored_player[i].cannot_move)
7879 action_arg_number = STEPSIZE_VERY_SLOW;
7881 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7882 action_arg == CA_ARG_SPEED_FASTER)
7884 action_arg_number = 2;
7885 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7888 else if (action_arg == CA_ARG_NUMBER_RESET)
7890 action_arg_number = level.initial_player_stepsize[i];
7894 getModifiedActionNumber(move_stepsize,
7897 action_arg_number_min,
7898 action_arg_number_max);
7900 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7907 case CA_SET_PLAYER_SHIELD:
7909 for (i = 0; i < MAX_PLAYERS; i++)
7911 if (trigger_player_bits & (1 << i))
7913 if (action_arg == CA_ARG_SHIELD_OFF)
7915 stored_player[i].shield_normal_time_left = 0;
7916 stored_player[i].shield_deadly_time_left = 0;
7918 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7920 stored_player[i].shield_normal_time_left = 999999;
7922 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7924 stored_player[i].shield_normal_time_left = 999999;
7925 stored_player[i].shield_deadly_time_left = 999999;
7933 #if USE_PLAYER_GRAVITY
7934 case CA_SET_PLAYER_GRAVITY:
7936 for (i = 0; i < MAX_PLAYERS; i++)
7938 if (trigger_player_bits & (1 << i))
7940 stored_player[i].gravity =
7941 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7942 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7943 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
7944 stored_player[i].gravity);
7952 case CA_SET_PLAYER_ARTWORK:
7954 for (i = 0; i < MAX_PLAYERS; i++)
7956 if (trigger_player_bits & (1 << i))
7958 int artwork_element = action_arg_element;
7960 if (action_arg == CA_ARG_ELEMENT_RESET)
7962 (level.use_artwork_element[i] ? level.artwork_element[i] :
7963 stored_player[i].element_nr);
7965 #if USE_GFX_RESET_PLAYER_ARTWORK
7966 if (stored_player[i].artwork_element != artwork_element)
7967 stored_player[i].Frame = 0;
7970 stored_player[i].artwork_element = artwork_element;
7972 SetPlayerWaiting(&stored_player[i], FALSE);
7974 /* set number of special actions for bored and sleeping animation */
7975 stored_player[i].num_special_action_bored =
7976 get_num_special_action(artwork_element,
7977 ACTION_BORING_1, ACTION_BORING_LAST);
7978 stored_player[i].num_special_action_sleeping =
7979 get_num_special_action(artwork_element,
7980 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7987 /* ---------- CE actions ---------------------------------------------- */
7989 case CA_SET_CE_VALUE:
7991 #if USE_NEW_CUSTOM_VALUE
7992 int last_ce_value = CustomValue[x][y];
7994 CustomValue[x][y] = action_arg_number_new;
7996 if (CustomValue[x][y] != last_ce_value)
7998 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
7999 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8001 if (CustomValue[x][y] == 0)
8003 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8004 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8012 case CA_SET_CE_SCORE:
8014 #if USE_NEW_CUSTOM_VALUE
8015 int last_ce_score = ei->collect_score;
8017 ei->collect_score = action_arg_number_new;
8019 if (ei->collect_score != last_ce_score)
8021 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8022 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8024 if (ei->collect_score == 0)
8028 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8029 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8032 This is a very special case that seems to be a mixture between
8033 CheckElementChange() and CheckTriggeredElementChange(): while
8034 the first one only affects single elements that are triggered
8035 directly, the second one affects multiple elements in the playfield
8036 that are triggered indirectly by another element. This is a third
8037 case: Changing the CE score always affects multiple identical CEs,
8038 so every affected CE must be checked, not only the single CE for
8039 which the CE score was changed in the first place (as every instance
8040 of that CE shares the same CE score, and therefore also can change)!
8042 SCAN_PLAYFIELD(xx, yy)
8044 if (Feld[xx][yy] == element)
8045 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8046 CE_SCORE_GETS_ZERO);
8055 /* ---------- engine actions ------------------------------------------ */
8057 case CA_SET_ENGINE_SCAN_MODE:
8059 InitPlayfieldScanMode(action_arg);
8069 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8071 int old_element = Feld[x][y];
8072 int new_element = get_element_from_group_element(element);
8073 int previous_move_direction = MovDir[x][y];
8074 #if USE_NEW_CUSTOM_VALUE
8075 int last_ce_value = CustomValue[x][y];
8077 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8078 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8079 boolean add_player_onto_element = (new_element_is_player &&
8080 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8081 /* this breaks SnakeBite when a snake is
8082 halfway through a door that closes */
8083 /* NOW FIXED AT LEVEL INIT IN files.c */
8084 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8086 IS_WALKABLE(old_element));
8089 /* check if element under the player changes from accessible to unaccessible
8090 (needed for special case of dropping element which then changes) */
8091 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8092 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8100 if (!add_player_onto_element)
8102 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8103 RemoveMovingField(x, y);
8107 Feld[x][y] = new_element;
8109 #if !USE_GFX_RESET_GFX_ANIMATION
8110 ResetGfxAnimation(x, y);
8111 ResetRandomAnimationValue(x, y);
8114 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8115 MovDir[x][y] = previous_move_direction;
8117 #if USE_NEW_CUSTOM_VALUE
8118 if (element_info[new_element].use_last_ce_value)
8119 CustomValue[x][y] = last_ce_value;
8122 InitField_WithBug1(x, y, FALSE);
8124 new_element = Feld[x][y]; /* element may have changed */
8126 #if USE_GFX_RESET_GFX_ANIMATION
8127 ResetGfxAnimation(x, y);
8128 ResetRandomAnimationValue(x, y);
8131 DrawLevelField(x, y);
8133 if (GFX_CRUMBLED(new_element))
8134 DrawLevelFieldCrumbledSandNeighbours(x, y);
8138 /* check if element under the player changes from accessible to unaccessible
8139 (needed for special case of dropping element which then changes) */
8140 /* (must be checked after creating new element for walkable group elements) */
8141 #if USE_FIX_KILLED_BY_NON_WALKABLE
8142 if (IS_PLAYER(x, y) && !player_explosion_protected &&
8143 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8150 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8151 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8160 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8161 if (new_element_is_player)
8162 RelocatePlayer(x, y, new_element);
8165 ChangeCount[x][y]++; /* count number of changes in the same frame */
8167 TestIfBadThingTouchesPlayer(x, y);
8168 TestIfPlayerTouchesCustomElement(x, y);
8169 TestIfElementTouchesCustomElement(x, y);
8172 static void CreateField(int x, int y, int element)
8174 CreateFieldExt(x, y, element, FALSE);
8177 static void CreateElementFromChange(int x, int y, int element)
8179 element = GET_VALID_RUNTIME_ELEMENT(element);
8181 #if USE_STOP_CHANGED_ELEMENTS
8182 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8184 int old_element = Feld[x][y];
8186 /* prevent changed element from moving in same engine frame
8187 unless both old and new element can either fall or move */
8188 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8189 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8194 CreateFieldExt(x, y, element, TRUE);
8197 static boolean ChangeElement(int x, int y, int element, int page)
8199 struct ElementInfo *ei = &element_info[element];
8200 struct ElementChangeInfo *change = &ei->change_page[page];
8201 int ce_value = CustomValue[x][y];
8202 int ce_score = ei->collect_score;
8204 int old_element = Feld[x][y];
8206 /* always use default change event to prevent running into a loop */
8207 if (ChangeEvent[x][y] == -1)
8208 ChangeEvent[x][y] = CE_DELAY;
8210 if (ChangeEvent[x][y] == CE_DELAY)
8212 /* reset actual trigger element, trigger player and action element */
8213 change->actual_trigger_element = EL_EMPTY;
8214 change->actual_trigger_player = EL_PLAYER_1;
8215 change->actual_trigger_side = CH_SIDE_NONE;
8216 change->actual_trigger_ce_value = 0;
8217 change->actual_trigger_ce_score = 0;
8220 /* do not change elements more than a specified maximum number of changes */
8221 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8224 ChangeCount[x][y]++; /* count number of changes in the same frame */
8226 if (change->explode)
8233 if (change->use_target_content)
8235 boolean complete_replace = TRUE;
8236 boolean can_replace[3][3];
8239 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8242 boolean is_walkable;
8243 boolean is_diggable;
8244 boolean is_collectible;
8245 boolean is_removable;
8246 boolean is_destructible;
8247 int ex = x + xx - 1;
8248 int ey = y + yy - 1;
8249 int content_element = change->target_content.e[xx][yy];
8252 can_replace[xx][yy] = TRUE;
8254 if (ex == x && ey == y) /* do not check changing element itself */
8257 if (content_element == EL_EMPTY_SPACE)
8259 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8264 if (!IN_LEV_FIELD(ex, ey))
8266 can_replace[xx][yy] = FALSE;
8267 complete_replace = FALSE;
8274 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8275 e = MovingOrBlocked2Element(ex, ey);
8277 is_empty = (IS_FREE(ex, ey) ||
8278 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8280 is_walkable = (is_empty || IS_WALKABLE(e));
8281 is_diggable = (is_empty || IS_DIGGABLE(e));
8282 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8283 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8284 is_removable = (is_diggable || is_collectible);
8286 can_replace[xx][yy] =
8287 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8288 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8289 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8290 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8291 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8292 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8293 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8295 if (!can_replace[xx][yy])
8296 complete_replace = FALSE;
8299 if (!change->only_if_complete || complete_replace)
8301 boolean something_has_changed = FALSE;
8303 if (change->only_if_complete && change->use_random_replace &&
8304 RND(100) < change->random_percentage)
8307 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8309 int ex = x + xx - 1;
8310 int ey = y + yy - 1;
8311 int content_element;
8313 if (can_replace[xx][yy] && (!change->use_random_replace ||
8314 RND(100) < change->random_percentage))
8316 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8317 RemoveMovingField(ex, ey);
8319 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8321 content_element = change->target_content.e[xx][yy];
8322 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8323 ce_value, ce_score);
8325 CreateElementFromChange(ex, ey, target_element);
8327 something_has_changed = TRUE;
8329 /* for symmetry reasons, freeze newly created border elements */
8330 if (ex != x || ey != y)
8331 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8335 if (something_has_changed)
8337 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8338 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8344 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
8345 ce_value, ce_score);
8347 if (element == EL_DIAGONAL_GROWING ||
8348 element == EL_DIAGONAL_SHRINKING)
8350 target_element = Store[x][y];
8352 Store[x][y] = EL_EMPTY;
8355 CreateElementFromChange(x, y, target_element);
8357 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8358 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8361 /* this uses direct change before indirect change */
8362 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8367 #if USE_NEW_DELAYED_ACTION
8369 static void HandleElementChange(int x, int y, int page)
8371 int element = MovingOrBlocked2Element(x, y);
8372 struct ElementInfo *ei = &element_info[element];
8373 struct ElementChangeInfo *change = &ei->change_page[page];
8376 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8377 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8380 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8381 x, y, element, element_info[element].token_name);
8382 printf("HandleElementChange(): This should never happen!\n");
8387 /* this can happen with classic bombs on walkable, changing elements */
8388 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8391 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8392 ChangeDelay[x][y] = 0;
8398 if (ChangeDelay[x][y] == 0) /* initialize element change */
8400 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8402 if (change->can_change)
8404 ResetGfxAnimation(x, y);
8405 ResetRandomAnimationValue(x, y);
8407 if (change->pre_change_function)
8408 change->pre_change_function(x, y);
8412 ChangeDelay[x][y]--;
8414 if (ChangeDelay[x][y] != 0) /* continue element change */
8416 if (change->can_change)
8418 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8420 if (IS_ANIMATED(graphic))
8421 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8423 if (change->change_function)
8424 change->change_function(x, y);
8427 else /* finish element change */
8429 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8431 page = ChangePage[x][y];
8432 ChangePage[x][y] = -1;
8434 change = &ei->change_page[page];
8437 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8439 ChangeDelay[x][y] = 1; /* try change after next move step */
8440 ChangePage[x][y] = page; /* remember page to use for change */
8445 if (change->can_change)
8447 if (ChangeElement(x, y, element, page))
8449 if (change->post_change_function)
8450 change->post_change_function(x, y);
8454 if (change->has_action)
8455 ExecuteCustomElementAction(x, y, element, page);
8461 static void HandleElementChange(int x, int y, int page)
8463 int element = MovingOrBlocked2Element(x, y);
8464 struct ElementInfo *ei = &element_info[element];
8465 struct ElementChangeInfo *change = &ei->change_page[page];
8468 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8471 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8472 x, y, element, element_info[element].token_name);
8473 printf("HandleElementChange(): This should never happen!\n");
8478 /* this can happen with classic bombs on walkable, changing elements */
8479 if (!CAN_CHANGE(element))
8482 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8483 ChangeDelay[x][y] = 0;
8489 if (ChangeDelay[x][y] == 0) /* initialize element change */
8491 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8493 ResetGfxAnimation(x, y);
8494 ResetRandomAnimationValue(x, y);
8496 if (change->pre_change_function)
8497 change->pre_change_function(x, y);
8500 ChangeDelay[x][y]--;
8502 if (ChangeDelay[x][y] != 0) /* continue element change */
8504 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8506 if (IS_ANIMATED(graphic))
8507 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8509 if (change->change_function)
8510 change->change_function(x, y);
8512 else /* finish element change */
8514 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8516 page = ChangePage[x][y];
8517 ChangePage[x][y] = -1;
8519 change = &ei->change_page[page];
8522 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8524 ChangeDelay[x][y] = 1; /* try change after next move step */
8525 ChangePage[x][y] = page; /* remember page to use for change */
8530 if (ChangeElement(x, y, element, page))
8532 if (change->post_change_function)
8533 change->post_change_function(x, y);
8540 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8541 int trigger_element,
8547 boolean change_done_any = FALSE;
8548 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8551 if (!(trigger_events[trigger_element][trigger_event]))
8554 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8556 int element = EL_CUSTOM_START + i;
8557 boolean change_done = FALSE;
8560 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8561 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8564 for (p = 0; p < element_info[element].num_change_pages; p++)
8566 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8568 if (change->can_change_or_has_action &&
8569 change->has_event[trigger_event] &&
8570 change->trigger_side & trigger_side &&
8571 change->trigger_player & trigger_player &&
8572 change->trigger_page & trigger_page_bits &&
8573 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8575 change->actual_trigger_element = trigger_element;
8576 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8577 change->actual_trigger_side = trigger_side;
8578 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8579 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8581 if ((change->can_change && !change_done) || change->has_action)
8585 SCAN_PLAYFIELD(x, y)
8587 if (Feld[x][y] == element)
8589 if (change->can_change && !change_done)
8591 ChangeDelay[x][y] = 1;
8592 ChangeEvent[x][y] = trigger_event;
8594 HandleElementChange(x, y, p);
8596 #if USE_NEW_DELAYED_ACTION
8597 else if (change->has_action)
8599 ExecuteCustomElementAction(x, y, element, p);
8600 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8603 if (change->has_action)
8605 ExecuteCustomElementAction(x, y, element, p);
8606 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8612 if (change->can_change)
8615 change_done_any = TRUE;
8622 return change_done_any;
8625 static boolean CheckElementChangeExt(int x, int y,
8627 int trigger_element,
8632 boolean change_done = FALSE;
8635 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8636 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8639 if (Feld[x][y] == EL_BLOCKED)
8641 Blocked2Moving(x, y, &x, &y);
8642 element = Feld[x][y];
8646 /* check if element has already changed */
8647 if (Feld[x][y] != element)
8650 /* check if element has already changed or is about to change after moving */
8651 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8652 Feld[x][y] != element) ||
8654 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8655 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8656 ChangePage[x][y] != -1)))
8660 for (p = 0; p < element_info[element].num_change_pages; p++)
8662 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8664 /* check trigger element for all events where the element that is checked
8665 for changing interacts with a directly adjacent element -- this is
8666 different to element changes that affect other elements to change on the
8667 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
8668 boolean check_trigger_element =
8669 (trigger_event == CE_TOUCHING_X ||
8670 trigger_event == CE_HITTING_X ||
8671 trigger_event == CE_HIT_BY_X ||
8673 /* this one was forgotten until 3.2.3 */
8674 trigger_event == CE_DIGGING_X);
8677 if (change->can_change_or_has_action &&
8678 change->has_event[trigger_event] &&
8679 change->trigger_side & trigger_side &&
8680 change->trigger_player & trigger_player &&
8681 (!check_trigger_element ||
8682 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8684 change->actual_trigger_element = trigger_element;
8685 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8686 change->actual_trigger_side = trigger_side;
8687 change->actual_trigger_ce_value = CustomValue[x][y];
8688 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8690 /* special case: trigger element not at (x,y) position for some events */
8691 if (check_trigger_element)
8703 { 0, 0 }, { 0, 0 }, { 0, 0 },
8707 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8708 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8710 change->actual_trigger_ce_value = CustomValue[xx][yy];
8711 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8714 if (change->can_change && !change_done)
8716 ChangeDelay[x][y] = 1;
8717 ChangeEvent[x][y] = trigger_event;
8719 HandleElementChange(x, y, p);
8723 #if USE_NEW_DELAYED_ACTION
8724 else if (change->has_action)
8726 ExecuteCustomElementAction(x, y, element, p);
8727 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8730 if (change->has_action)
8732 ExecuteCustomElementAction(x, y, element, p);
8733 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8742 static void PlayPlayerSound(struct PlayerInfo *player)
8744 int jx = player->jx, jy = player->jy;
8745 int sound_element = player->artwork_element;
8746 int last_action = player->last_action_waiting;
8747 int action = player->action_waiting;
8749 if (player->is_waiting)
8751 if (action != last_action)
8752 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8754 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8758 if (action != last_action)
8759 StopSound(element_info[sound_element].sound[last_action]);
8761 if (last_action == ACTION_SLEEPING)
8762 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8766 static void PlayAllPlayersSound()
8770 for (i = 0; i < MAX_PLAYERS; i++)
8771 if (stored_player[i].active)
8772 PlayPlayerSound(&stored_player[i]);
8775 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8777 boolean last_waiting = player->is_waiting;
8778 int move_dir = player->MovDir;
8780 player->dir_waiting = move_dir;
8781 player->last_action_waiting = player->action_waiting;
8785 if (!last_waiting) /* not waiting -> waiting */
8787 player->is_waiting = TRUE;
8789 player->frame_counter_bored =
8791 game.player_boring_delay_fixed +
8792 GetSimpleRandom(game.player_boring_delay_random);
8793 player->frame_counter_sleeping =
8795 game.player_sleeping_delay_fixed +
8796 GetSimpleRandom(game.player_sleeping_delay_random);
8798 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
8801 if (game.player_sleeping_delay_fixed +
8802 game.player_sleeping_delay_random > 0 &&
8803 player->anim_delay_counter == 0 &&
8804 player->post_delay_counter == 0 &&
8805 FrameCounter >= player->frame_counter_sleeping)
8806 player->is_sleeping = TRUE;
8807 else if (game.player_boring_delay_fixed +
8808 game.player_boring_delay_random > 0 &&
8809 FrameCounter >= player->frame_counter_bored)
8810 player->is_bored = TRUE;
8812 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8813 player->is_bored ? ACTION_BORING :
8816 if (player->is_sleeping && player->use_murphy)
8818 /* special case for sleeping Murphy when leaning against non-free tile */
8820 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
8821 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
8822 !IS_MOVING(player->jx - 1, player->jy)))
8824 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
8825 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
8826 !IS_MOVING(player->jx + 1, player->jy)))
8827 move_dir = MV_RIGHT;
8829 player->is_sleeping = FALSE;
8831 player->dir_waiting = move_dir;
8834 if (player->is_sleeping)
8836 if (player->num_special_action_sleeping > 0)
8838 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8840 int last_special_action = player->special_action_sleeping;
8841 int num_special_action = player->num_special_action_sleeping;
8842 int special_action =
8843 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8844 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8845 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8846 last_special_action + 1 : ACTION_SLEEPING);
8847 int special_graphic =
8848 el_act_dir2img(player->artwork_element, special_action, move_dir);
8850 player->anim_delay_counter =
8851 graphic_info[special_graphic].anim_delay_fixed +
8852 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8853 player->post_delay_counter =
8854 graphic_info[special_graphic].post_delay_fixed +
8855 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8857 player->special_action_sleeping = special_action;
8860 if (player->anim_delay_counter > 0)
8862 player->action_waiting = player->special_action_sleeping;
8863 player->anim_delay_counter--;
8865 else if (player->post_delay_counter > 0)
8867 player->post_delay_counter--;
8871 else if (player->is_bored)
8873 if (player->num_special_action_bored > 0)
8875 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8877 int special_action =
8878 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
8879 int special_graphic =
8880 el_act_dir2img(player->artwork_element, special_action, move_dir);
8882 player->anim_delay_counter =
8883 graphic_info[special_graphic].anim_delay_fixed +
8884 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8885 player->post_delay_counter =
8886 graphic_info[special_graphic].post_delay_fixed +
8887 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8889 player->special_action_bored = special_action;
8892 if (player->anim_delay_counter > 0)
8894 player->action_waiting = player->special_action_bored;
8895 player->anim_delay_counter--;
8897 else if (player->post_delay_counter > 0)
8899 player->post_delay_counter--;
8904 else if (last_waiting) /* waiting -> not waiting */
8906 player->is_waiting = FALSE;
8907 player->is_bored = FALSE;
8908 player->is_sleeping = FALSE;
8910 player->frame_counter_bored = -1;
8911 player->frame_counter_sleeping = -1;
8913 player->anim_delay_counter = 0;
8914 player->post_delay_counter = 0;
8916 player->dir_waiting = player->MovDir;
8917 player->action_waiting = ACTION_DEFAULT;
8919 player->special_action_bored = ACTION_DEFAULT;
8920 player->special_action_sleeping = ACTION_DEFAULT;
8924 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8926 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8927 int left = player_action & JOY_LEFT;
8928 int right = player_action & JOY_RIGHT;
8929 int up = player_action & JOY_UP;
8930 int down = player_action & JOY_DOWN;
8931 int button1 = player_action & JOY_BUTTON_1;
8932 int button2 = player_action & JOY_BUTTON_2;
8933 int dx = (left ? -1 : right ? 1 : 0);
8934 int dy = (up ? -1 : down ? 1 : 0);
8936 if (!player->active || tape.pausing)
8942 snapped = SnapField(player, dx, dy);
8946 dropped = DropElement(player);
8948 moved = MovePlayer(player, dx, dy);
8951 if (tape.single_step && tape.recording && !tape.pausing)
8953 if (button1 || (dropped && !moved))
8955 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8956 SnapField(player, 0, 0); /* stop snapping */
8960 SetPlayerWaiting(player, FALSE);
8962 return player_action;
8966 /* no actions for this player (no input at player's configured device) */
8968 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8969 SnapField(player, 0, 0);
8970 CheckGravityMovementWhenNotMoving(player);
8972 if (player->MovPos == 0)
8973 SetPlayerWaiting(player, TRUE);
8975 if (player->MovPos == 0) /* needed for tape.playing */
8976 player->is_moving = FALSE;
8978 player->is_dropping = FALSE;
8979 player->is_dropping_pressed = FALSE;
8980 player->drop_pressed_delay = 0;
8986 static void CheckLevelTime()
8990 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8992 if (level.native_em_level->lev->home == 0) /* all players at home */
8994 PlayerWins(local_player);
8996 AllPlayersGone = TRUE;
8998 level.native_em_level->lev->home = -1;
9001 if (level.native_em_level->ply[0]->alive == 0 &&
9002 level.native_em_level->ply[1]->alive == 0 &&
9003 level.native_em_level->ply[2]->alive == 0 &&
9004 level.native_em_level->ply[3]->alive == 0) /* all dead */
9005 AllPlayersGone = TRUE;
9008 if (TimeFrames >= FRAMES_PER_SECOND)
9013 for (i = 0; i < MAX_PLAYERS; i++)
9015 struct PlayerInfo *player = &stored_player[i];
9017 if (SHIELD_ON(player))
9019 player->shield_normal_time_left--;
9021 if (player->shield_deadly_time_left > 0)
9022 player->shield_deadly_time_left--;
9026 if (!local_player->LevelSolved && !level.use_step_counter)
9034 if (TimeLeft <= 10 && setup.time_limit)
9035 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9037 DrawGameValue_Time(TimeLeft);
9039 if (!TimeLeft && setup.time_limit)
9041 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9042 level.native_em_level->lev->killed_out_of_time = TRUE;
9044 for (i = 0; i < MAX_PLAYERS; i++)
9045 KillPlayer(&stored_player[i]);
9048 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9049 DrawGameValue_Time(TimePlayed);
9051 level.native_em_level->lev->time =
9052 (level.time == 0 ? TimePlayed : TimeLeft);
9055 if (tape.recording || tape.playing)
9056 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9060 void AdvanceFrameAndPlayerCounters(int player_nr)
9064 /* advance frame counters (global frame counter and time frame counter) */
9068 /* advance player counters (counters for move delay, move animation etc.) */
9069 for (i = 0; i < MAX_PLAYERS; i++)
9071 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9072 int move_delay_value = stored_player[i].move_delay_value;
9073 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9075 if (!advance_player_counters) /* not all players may be affected */
9078 #if USE_NEW_PLAYER_ANIM
9079 if (move_frames == 0) /* less than one move per game frame */
9081 int stepsize = TILEX / move_delay_value;
9082 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9083 int count = (stored_player[i].is_moving ?
9084 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9086 if (count % delay == 0)
9091 stored_player[i].Frame += move_frames;
9093 if (stored_player[i].MovPos != 0)
9094 stored_player[i].StepFrame += move_frames;
9096 if (stored_player[i].move_delay > 0)
9097 stored_player[i].move_delay--;
9099 /* due to bugs in previous versions, counter must count up, not down */
9100 if (stored_player[i].push_delay != -1)
9101 stored_player[i].push_delay++;
9103 if (stored_player[i].drop_delay > 0)
9104 stored_player[i].drop_delay--;
9106 if (stored_player[i].is_dropping_pressed)
9107 stored_player[i].drop_pressed_delay++;
9111 void StartGameActions(boolean init_network_game, boolean record_tape,
9114 unsigned long new_random_seed = InitRND(random_seed);
9117 TapeStartRecording(new_random_seed);
9119 #if defined(NETWORK_AVALIABLE)
9120 if (init_network_game)
9122 SendToServer_StartPlaying();
9133 static unsigned long game_frame_delay = 0;
9134 unsigned long game_frame_delay_value;
9135 byte *recorded_player_action;
9136 byte summarized_player_action = 0;
9137 byte tape_action[MAX_PLAYERS];
9140 if (game.restart_level)
9141 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9143 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9145 if (level.native_em_level->lev->home == 0) /* all players at home */
9147 PlayerWins(local_player);
9149 AllPlayersGone = TRUE;
9151 level.native_em_level->lev->home = -1;
9154 if (level.native_em_level->ply[0]->alive == 0 &&
9155 level.native_em_level->ply[1]->alive == 0 &&
9156 level.native_em_level->ply[2]->alive == 0 &&
9157 level.native_em_level->ply[3]->alive == 0) /* all dead */
9158 AllPlayersGone = TRUE;
9161 if (local_player->LevelSolved)
9164 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9167 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9170 game_frame_delay_value =
9171 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9173 if (tape.playing && tape.warp_forward && !tape.pausing)
9174 game_frame_delay_value = 0;
9176 /* ---------- main game synchronization point ---------- */
9178 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9180 if (network_playing && !network_player_action_received)
9182 /* try to get network player actions in time */
9184 #if defined(NETWORK_AVALIABLE)
9185 /* last chance to get network player actions without main loop delay */
9189 /* game was quit by network peer */
9190 if (game_status != GAME_MODE_PLAYING)
9193 if (!network_player_action_received)
9194 return; /* failed to get network player actions in time */
9196 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9202 /* at this point we know that we really continue executing the game */
9204 network_player_action_received = FALSE;
9206 /* when playing tape, read previously recorded player input from tape data */
9207 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9210 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9215 if (tape.set_centered_player)
9217 game.centered_player_nr_next = tape.centered_player_nr_next;
9218 game.set_centered_player = TRUE;
9221 for (i = 0; i < MAX_PLAYERS; i++)
9223 summarized_player_action |= stored_player[i].action;
9225 if (!network_playing)
9226 stored_player[i].effective_action = stored_player[i].action;
9229 #if defined(NETWORK_AVALIABLE)
9230 if (network_playing)
9231 SendToServer_MovePlayer(summarized_player_action);
9234 if (!options.network && !setup.team_mode)
9235 local_player->effective_action = summarized_player_action;
9237 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9239 for (i = 0; i < MAX_PLAYERS; i++)
9240 stored_player[i].effective_action =
9241 (i == game.centered_player_nr ? summarized_player_action : 0);
9244 if (recorded_player_action != NULL)
9245 for (i = 0; i < MAX_PLAYERS; i++)
9246 stored_player[i].effective_action = recorded_player_action[i];
9248 for (i = 0; i < MAX_PLAYERS; i++)
9250 tape_action[i] = stored_player[i].effective_action;
9252 /* (this can only happen in the R'n'D game engine) */
9253 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9254 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9257 /* only record actions from input devices, but not programmed actions */
9259 TapeRecordAction(tape_action);
9261 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9263 GameActions_EM_Main();
9271 void GameActions_EM_Main()
9273 byte effective_action[MAX_PLAYERS];
9274 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9277 for (i = 0; i < MAX_PLAYERS; i++)
9278 effective_action[i] = stored_player[i].effective_action;
9280 GameActions_EM(effective_action, warp_mode);
9284 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9287 void GameActions_RND()
9289 int magic_wall_x = 0, magic_wall_y = 0;
9290 int i, x, y, element, graphic;
9292 InitPlayfieldScanModeVars();
9294 #if USE_ONE_MORE_CHANGE_PER_FRAME
9295 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9297 SCAN_PLAYFIELD(x, y)
9299 ChangeCount[x][y] = 0;
9300 ChangeEvent[x][y] = -1;
9305 if (game.set_centered_player)
9307 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9309 /* switching to "all players" only possible if all players fit to screen */
9310 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9312 game.centered_player_nr_next = game.centered_player_nr;
9313 game.set_centered_player = FALSE;
9316 /* do not switch focus to non-existing (or non-active) player */
9317 if (game.centered_player_nr_next >= 0 &&
9318 !stored_player[game.centered_player_nr_next].active)
9320 game.centered_player_nr_next = game.centered_player_nr;
9321 game.set_centered_player = FALSE;
9325 if (game.set_centered_player &&
9326 ScreenMovPos == 0) /* screen currently aligned at tile position */
9330 if (game.centered_player_nr_next == -1)
9332 setScreenCenteredToAllPlayers(&sx, &sy);
9336 sx = stored_player[game.centered_player_nr_next].jx;
9337 sy = stored_player[game.centered_player_nr_next].jy;
9340 game.centered_player_nr = game.centered_player_nr_next;
9341 game.set_centered_player = FALSE;
9343 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9344 DrawGameDoorValues();
9347 for (i = 0; i < MAX_PLAYERS; i++)
9349 int actual_player_action = stored_player[i].effective_action;
9352 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9353 - rnd_equinox_tetrachloride 048
9354 - rnd_equinox_tetrachloride_ii 096
9355 - rnd_emanuel_schmieg 002
9356 - doctor_sloan_ww 001, 020
9358 if (stored_player[i].MovPos == 0)
9359 CheckGravityMovement(&stored_player[i]);
9362 /* overwrite programmed action with tape action */
9363 if (stored_player[i].programmed_action)
9364 actual_player_action = stored_player[i].programmed_action;
9366 PlayerActions(&stored_player[i], actual_player_action);
9368 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9371 ScrollScreen(NULL, SCROLL_GO_ON);
9373 /* for backwards compatibility, the following code emulates a fixed bug that
9374 occured when pushing elements (causing elements that just made their last
9375 pushing step to already (if possible) make their first falling step in the
9376 same game frame, which is bad); this code is also needed to use the famous
9377 "spring push bug" which is used in older levels and might be wanted to be
9378 used also in newer levels, but in this case the buggy pushing code is only
9379 affecting the "spring" element and no other elements */
9381 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9383 for (i = 0; i < MAX_PLAYERS; i++)
9385 struct PlayerInfo *player = &stored_player[i];
9389 if (player->active && player->is_pushing && player->is_moving &&
9391 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9392 Feld[x][y] == EL_SPRING))
9394 ContinueMoving(x, y);
9396 /* continue moving after pushing (this is actually a bug) */
9397 if (!IS_MOVING(x, y))
9405 SCAN_PLAYFIELD(x, y)
9407 ChangeCount[x][y] = 0;
9408 ChangeEvent[x][y] = -1;
9410 /* this must be handled before main playfield loop */
9411 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9414 if (MovDelay[x][y] <= 0)
9418 #if USE_NEW_SNAP_DELAY
9419 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9422 if (MovDelay[x][y] <= 0)
9425 DrawLevelField(x, y);
9427 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9433 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9435 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9436 printf("GameActions(): This should never happen!\n");
9438 ChangePage[x][y] = -1;
9443 if (WasJustMoving[x][y] > 0)
9444 WasJustMoving[x][y]--;
9445 if (WasJustFalling[x][y] > 0)
9446 WasJustFalling[x][y]--;
9447 if (CheckCollision[x][y] > 0)
9448 CheckCollision[x][y]--;
9449 if (CheckImpact[x][y] > 0)
9450 CheckImpact[x][y]--;
9454 /* reset finished pushing action (not done in ContinueMoving() to allow
9455 continuous pushing animation for elements with zero push delay) */
9456 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9458 ResetGfxAnimation(x, y);
9459 DrawLevelField(x, y);
9463 if (IS_BLOCKED(x, y))
9467 Blocked2Moving(x, y, &oldx, &oldy);
9468 if (!IS_MOVING(oldx, oldy))
9470 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9471 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9472 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9473 printf("GameActions(): This should never happen!\n");
9479 SCAN_PLAYFIELD(x, y)
9481 element = Feld[x][y];
9482 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9484 ResetGfxFrame(x, y, TRUE);
9486 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9487 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9488 ResetRandomAnimationValue(x, y);
9490 SetRandomAnimationValue(x, y);
9492 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9494 if (IS_INACTIVE(element))
9496 if (IS_ANIMATED(graphic))
9497 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9502 /* this may take place after moving, so 'element' may have changed */
9503 if (IS_CHANGING(x, y) &&
9504 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9506 int page = element_info[element].event_page_nr[CE_DELAY];
9509 HandleElementChange(x, y, page);
9511 if (CAN_CHANGE(element))
9512 HandleElementChange(x, y, page);
9514 if (HAS_ACTION(element))
9515 ExecuteCustomElementAction(x, y, element, page);
9518 element = Feld[x][y];
9519 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9522 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9526 element = Feld[x][y];
9527 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9529 if (IS_ANIMATED(graphic) &&
9532 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9534 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9535 DrawTwinkleOnField(x, y);
9537 else if ((element == EL_ACID ||
9538 element == EL_EXIT_OPEN ||
9539 element == EL_SP_EXIT_OPEN ||
9540 element == EL_SP_TERMINAL ||
9541 element == EL_SP_TERMINAL_ACTIVE ||
9542 element == EL_EXTRA_TIME ||
9543 element == EL_SHIELD_NORMAL ||
9544 element == EL_SHIELD_DEADLY) &&
9545 IS_ANIMATED(graphic))
9546 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9547 else if (IS_MOVING(x, y))
9548 ContinueMoving(x, y);
9549 else if (IS_ACTIVE_BOMB(element))
9550 CheckDynamite(x, y);
9551 else if (element == EL_AMOEBA_GROWING)
9552 AmoebeWaechst(x, y);
9553 else if (element == EL_AMOEBA_SHRINKING)
9554 AmoebaDisappearing(x, y);
9556 #if !USE_NEW_AMOEBA_CODE
9557 else if (IS_AMOEBALIVE(element))
9558 AmoebeAbleger(x, y);
9561 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9563 else if (element == EL_EXIT_CLOSED)
9565 else if (element == EL_SP_EXIT_CLOSED)
9567 else if (element == EL_EXPANDABLE_WALL_GROWING)
9569 else if (element == EL_EXPANDABLE_WALL ||
9570 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9571 element == EL_EXPANDABLE_WALL_VERTICAL ||
9572 element == EL_EXPANDABLE_WALL_ANY ||
9573 element == EL_BD_EXPANDABLE_WALL)
9575 else if (element == EL_FLAMES)
9576 CheckForDragon(x, y);
9577 else if (element == EL_EXPLOSION)
9578 ; /* drawing of correct explosion animation is handled separately */
9579 else if (element == EL_ELEMENT_SNAPPING ||
9580 element == EL_DIAGONAL_SHRINKING ||
9581 element == EL_DIAGONAL_GROWING)
9583 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9585 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9587 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9588 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9590 if (IS_BELT_ACTIVE(element))
9591 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9593 if (game.magic_wall_active)
9595 int jx = local_player->jx, jy = local_player->jy;
9597 /* play the element sound at the position nearest to the player */
9598 if ((element == EL_MAGIC_WALL_FULL ||
9599 element == EL_MAGIC_WALL_ACTIVE ||
9600 element == EL_MAGIC_WALL_EMPTYING ||
9601 element == EL_BD_MAGIC_WALL_FULL ||
9602 element == EL_BD_MAGIC_WALL_ACTIVE ||
9603 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9604 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9612 #if USE_NEW_AMOEBA_CODE
9613 /* new experimental amoeba growth stuff */
9614 if (!(FrameCounter % 8))
9616 static unsigned long random = 1684108901;
9618 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9620 x = RND(lev_fieldx);
9621 y = RND(lev_fieldy);
9622 element = Feld[x][y];
9624 if (!IS_PLAYER(x,y) &&
9625 (element == EL_EMPTY ||
9626 CAN_GROW_INTO(element) ||
9627 element == EL_QUICKSAND_EMPTY ||
9628 element == EL_ACID_SPLASH_LEFT ||
9629 element == EL_ACID_SPLASH_RIGHT))
9631 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9632 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9633 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9634 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9635 Feld[x][y] = EL_AMOEBA_DROP;
9638 random = random * 129 + 1;
9644 if (game.explosions_delayed)
9647 game.explosions_delayed = FALSE;
9649 SCAN_PLAYFIELD(x, y)
9651 element = Feld[x][y];
9653 if (ExplodeField[x][y])
9654 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9655 else if (element == EL_EXPLOSION)
9656 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9658 ExplodeField[x][y] = EX_TYPE_NONE;
9661 game.explosions_delayed = TRUE;
9664 if (game.magic_wall_active)
9666 if (!(game.magic_wall_time_left % 4))
9668 int element = Feld[magic_wall_x][magic_wall_y];
9670 if (element == EL_BD_MAGIC_WALL_FULL ||
9671 element == EL_BD_MAGIC_WALL_ACTIVE ||
9672 element == EL_BD_MAGIC_WALL_EMPTYING)
9673 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9675 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9678 if (game.magic_wall_time_left > 0)
9680 game.magic_wall_time_left--;
9681 if (!game.magic_wall_time_left)
9683 SCAN_PLAYFIELD(x, y)
9685 element = Feld[x][y];
9687 if (element == EL_MAGIC_WALL_ACTIVE ||
9688 element == EL_MAGIC_WALL_FULL)
9690 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9691 DrawLevelField(x, y);
9693 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9694 element == EL_BD_MAGIC_WALL_FULL)
9696 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9697 DrawLevelField(x, y);
9701 game.magic_wall_active = FALSE;
9706 if (game.light_time_left > 0)
9708 game.light_time_left--;
9710 if (game.light_time_left == 0)
9711 RedrawAllLightSwitchesAndInvisibleElements();
9714 if (game.timegate_time_left > 0)
9716 game.timegate_time_left--;
9718 if (game.timegate_time_left == 0)
9719 CloseAllOpenTimegates();
9722 if (game.lenses_time_left > 0)
9724 game.lenses_time_left--;
9726 if (game.lenses_time_left == 0)
9727 RedrawAllInvisibleElementsForLenses();
9730 if (game.magnify_time_left > 0)
9732 game.magnify_time_left--;
9734 if (game.magnify_time_left == 0)
9735 RedrawAllInvisibleElementsForMagnifier();
9738 for (i = 0; i < MAX_PLAYERS; i++)
9740 struct PlayerInfo *player = &stored_player[i];
9742 if (SHIELD_ON(player))
9744 if (player->shield_deadly_time_left)
9745 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9746 else if (player->shield_normal_time_left)
9747 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9754 PlayAllPlayersSound();
9756 if (options.debug) /* calculate frames per second */
9758 static unsigned long fps_counter = 0;
9759 static int fps_frames = 0;
9760 unsigned long fps_delay_ms = Counter() - fps_counter;
9764 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9766 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9769 fps_counter = Counter();
9772 redraw_mask |= REDRAW_FPS;
9775 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9777 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9779 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9781 local_player->show_envelope = 0;
9784 /* use random number generator in every frame to make it less predictable */
9785 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9789 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9791 int min_x = x, min_y = y, max_x = x, max_y = y;
9794 for (i = 0; i < MAX_PLAYERS; i++)
9796 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9798 if (!stored_player[i].active || &stored_player[i] == player)
9801 min_x = MIN(min_x, jx);
9802 min_y = MIN(min_y, jy);
9803 max_x = MAX(max_x, jx);
9804 max_y = MAX(max_y, jy);
9807 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9810 static boolean AllPlayersInVisibleScreen()
9814 for (i = 0; i < MAX_PLAYERS; i++)
9816 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9818 if (!stored_player[i].active)
9821 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9828 void ScrollLevel(int dx, int dy)
9830 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9833 BlitBitmap(drawto_field, drawto_field,
9834 FX + TILEX * (dx == -1) - softscroll_offset,
9835 FY + TILEY * (dy == -1) - softscroll_offset,
9836 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9837 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9838 FX + TILEX * (dx == 1) - softscroll_offset,
9839 FY + TILEY * (dy == 1) - softscroll_offset);
9843 x = (dx == 1 ? BX1 : BX2);
9844 for (y = BY1; y <= BY2; y++)
9845 DrawScreenField(x, y);
9850 y = (dy == 1 ? BY1 : BY2);
9851 for (x = BX1; x <= BX2; x++)
9852 DrawScreenField(x, y);
9855 redraw_mask |= REDRAW_FIELD;
9858 static boolean canFallDown(struct PlayerInfo *player)
9860 int jx = player->jx, jy = player->jy;
9862 return (IN_LEV_FIELD(jx, jy + 1) &&
9863 (IS_FREE(jx, jy + 1) ||
9864 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9865 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9866 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9869 static boolean canPassField(int x, int y, int move_dir)
9871 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9872 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9873 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9876 int element = Feld[x][y];
9878 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9879 !CAN_MOVE(element) &&
9880 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9881 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9882 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9885 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9887 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9888 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9889 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9893 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9894 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9895 (IS_DIGGABLE(Feld[newx][newy]) ||
9896 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9897 canPassField(newx, newy, move_dir)));
9900 static void CheckGravityMovement(struct PlayerInfo *player)
9902 #if USE_PLAYER_GRAVITY
9903 if (player->gravity && !player->programmed_action)
9905 if (game.gravity && !player->programmed_action)
9908 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9909 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9910 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
9911 int jx = player->jx, jy = player->jy;
9912 boolean player_is_moving_to_valid_field =
9913 (!player_is_snapping &&
9914 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9915 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9916 boolean player_can_fall_down = canFallDown(player);
9918 if (player_can_fall_down &&
9919 !player_is_moving_to_valid_field)
9920 player->programmed_action = MV_DOWN;
9924 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9926 return CheckGravityMovement(player);
9928 #if USE_PLAYER_GRAVITY
9929 if (player->gravity && !player->programmed_action)
9931 if (game.gravity && !player->programmed_action)
9934 int jx = player->jx, jy = player->jy;
9935 boolean field_under_player_is_free =
9936 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9937 boolean player_is_standing_on_valid_field =
9938 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9939 (IS_WALKABLE(Feld[jx][jy]) &&
9940 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9942 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9943 player->programmed_action = MV_DOWN;
9949 -----------------------------------------------------------------------------
9950 dx, dy: direction (non-diagonal) to try to move the player to
9951 real_dx, real_dy: direction as read from input device (can be diagonal)
9954 boolean MovePlayerOneStep(struct PlayerInfo *player,
9955 int dx, int dy, int real_dx, int real_dy)
9957 int jx = player->jx, jy = player->jy;
9958 int new_jx = jx + dx, new_jy = jy + dy;
9959 #if !USE_FIXED_DONT_RUN_INTO
9963 boolean player_can_move = !player->cannot_move;
9965 if (!player->active || (!dx && !dy))
9966 return MP_NO_ACTION;
9968 player->MovDir = (dx < 0 ? MV_LEFT :
9971 dy > 0 ? MV_DOWN : MV_NONE);
9973 if (!IN_LEV_FIELD(new_jx, new_jy))
9974 return MP_NO_ACTION;
9976 if (!player_can_move)
9978 if (player->MovPos == 0)
9980 player->is_moving = FALSE;
9981 player->is_digging = FALSE;
9982 player->is_collecting = FALSE;
9983 player->is_snapping = FALSE;
9984 player->is_pushing = FALSE;
9989 if (!options.network && game.centered_player_nr == -1 &&
9990 !AllPlayersInSight(player, new_jx, new_jy))
9991 return MP_NO_ACTION;
9993 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9994 return MP_NO_ACTION;
9997 #if !USE_FIXED_DONT_RUN_INTO
9998 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10000 /* (moved to DigField()) */
10001 if (player_can_move && DONT_RUN_INTO(element))
10003 if (element == EL_ACID && dx == 0 && dy == 1)
10005 SplashAcid(new_jx, new_jy);
10006 Feld[jx][jy] = EL_PLAYER_1;
10007 InitMovingField(jx, jy, MV_DOWN);
10008 Store[jx][jy] = EL_ACID;
10009 ContinueMoving(jx, jy);
10010 BuryPlayer(player);
10013 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10019 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10020 if (can_move != MP_MOVING)
10023 /* check if DigField() has caused relocation of the player */
10024 if (player->jx != jx || player->jy != jy)
10025 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10027 StorePlayer[jx][jy] = 0;
10028 player->last_jx = jx;
10029 player->last_jy = jy;
10030 player->jx = new_jx;
10031 player->jy = new_jy;
10032 StorePlayer[new_jx][new_jy] = player->element_nr;
10034 if (player->move_delay_value_next != -1)
10036 player->move_delay_value = player->move_delay_value_next;
10037 player->move_delay_value_next = -1;
10041 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10043 player->step_counter++;
10045 PlayerVisit[jx][jy] = FrameCounter;
10047 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10048 player->is_moving = TRUE;
10052 /* should better be called in MovePlayer(), but this breaks some tapes */
10053 ScrollPlayer(player, SCROLL_INIT);
10059 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10061 int jx = player->jx, jy = player->jy;
10062 int old_jx = jx, old_jy = jy;
10063 int moved = MP_NO_ACTION;
10065 if (!player->active)
10070 if (player->MovPos == 0)
10072 player->is_moving = FALSE;
10073 player->is_digging = FALSE;
10074 player->is_collecting = FALSE;
10075 player->is_snapping = FALSE;
10076 player->is_pushing = FALSE;
10082 if (player->move_delay > 0)
10085 player->move_delay = -1; /* set to "uninitialized" value */
10087 /* store if player is automatically moved to next field */
10088 player->is_auto_moving = (player->programmed_action != MV_NONE);
10090 /* remove the last programmed player action */
10091 player->programmed_action = 0;
10093 if (player->MovPos)
10095 /* should only happen if pre-1.2 tape recordings are played */
10096 /* this is only for backward compatibility */
10098 int original_move_delay_value = player->move_delay_value;
10101 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10105 /* scroll remaining steps with finest movement resolution */
10106 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10108 while (player->MovPos)
10110 ScrollPlayer(player, SCROLL_GO_ON);
10111 ScrollScreen(NULL, SCROLL_GO_ON);
10113 AdvanceFrameAndPlayerCounters(player->index_nr);
10119 player->move_delay_value = original_move_delay_value;
10122 player->is_active = FALSE;
10124 if (player->last_move_dir & MV_HORIZONTAL)
10126 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10127 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10131 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10132 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10135 #if USE_FIXED_BORDER_RUNNING_GFX
10136 if (!moved && !player->is_active)
10138 player->is_moving = FALSE;
10139 player->is_digging = FALSE;
10140 player->is_collecting = FALSE;
10141 player->is_snapping = FALSE;
10142 player->is_pushing = FALSE;
10150 if (moved & MP_MOVING && !ScreenMovPos &&
10151 (player->index_nr == game.centered_player_nr ||
10152 game.centered_player_nr == -1))
10154 if (moved & MP_MOVING && !ScreenMovPos &&
10155 (player == local_player || !options.network))
10158 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10159 int offset = (setup.scroll_delay ? 3 : 0);
10161 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10163 /* actual player has left the screen -- scroll in that direction */
10164 if (jx != old_jx) /* player has moved horizontally */
10165 scroll_x += (jx - old_jx);
10166 else /* player has moved vertically */
10167 scroll_y += (jy - old_jy);
10171 if (jx != old_jx) /* player has moved horizontally */
10173 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10174 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10175 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10177 /* don't scroll over playfield boundaries */
10178 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10179 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10181 /* don't scroll more than one field at a time */
10182 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10184 /* don't scroll against the player's moving direction */
10185 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10186 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10187 scroll_x = old_scroll_x;
10189 else /* player has moved vertically */
10191 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10192 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10193 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10195 /* don't scroll over playfield boundaries */
10196 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10197 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10199 /* don't scroll more than one field at a time */
10200 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10202 /* don't scroll against the player's moving direction */
10203 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10204 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10205 scroll_y = old_scroll_y;
10209 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10212 if (!options.network && game.centered_player_nr == -1 &&
10213 !AllPlayersInVisibleScreen())
10215 scroll_x = old_scroll_x;
10216 scroll_y = old_scroll_y;
10220 if (!options.network && !AllPlayersInVisibleScreen())
10222 scroll_x = old_scroll_x;
10223 scroll_y = old_scroll_y;
10228 ScrollScreen(player, SCROLL_INIT);
10229 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10234 player->StepFrame = 0;
10236 if (moved & MP_MOVING)
10238 if (old_jx != jx && old_jy == jy)
10239 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10240 else if (old_jx == jx && old_jy != jy)
10241 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10243 DrawLevelField(jx, jy); /* for "crumbled sand" */
10245 player->last_move_dir = player->MovDir;
10246 player->is_moving = TRUE;
10247 player->is_snapping = FALSE;
10248 player->is_switching = FALSE;
10249 player->is_dropping = FALSE;
10250 player->is_dropping_pressed = FALSE;
10251 player->drop_pressed_delay = 0;
10254 /* should better be called here than above, but this breaks some tapes */
10255 ScrollPlayer(player, SCROLL_INIT);
10260 CheckGravityMovementWhenNotMoving(player);
10262 player->is_moving = FALSE;
10264 /* at this point, the player is allowed to move, but cannot move right now
10265 (e.g. because of something blocking the way) -- ensure that the player
10266 is also allowed to move in the next frame (in old versions before 3.1.1,
10267 the player was forced to wait again for eight frames before next try) */
10269 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10270 player->move_delay = 0; /* allow direct movement in the next frame */
10273 if (player->move_delay == -1) /* not yet initialized by DigField() */
10274 player->move_delay = player->move_delay_value;
10276 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10278 TestIfPlayerTouchesBadThing(jx, jy);
10279 TestIfPlayerTouchesCustomElement(jx, jy);
10282 if (!player->active)
10283 RemovePlayer(player);
10288 void ScrollPlayer(struct PlayerInfo *player, int mode)
10290 int jx = player->jx, jy = player->jy;
10291 int last_jx = player->last_jx, last_jy = player->last_jy;
10292 int move_stepsize = TILEX / player->move_delay_value;
10294 #if USE_NEW_PLAYER_SPEED
10295 if (!player->active)
10298 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10301 if (!player->active || player->MovPos == 0)
10305 if (mode == SCROLL_INIT)
10307 player->actual_frame_counter = FrameCounter;
10308 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10310 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10311 Feld[last_jx][last_jy] == EL_EMPTY)
10313 int last_field_block_delay = 0; /* start with no blocking at all */
10314 int block_delay_adjustment = player->block_delay_adjustment;
10316 /* if player blocks last field, add delay for exactly one move */
10317 if (player->block_last_field)
10319 last_field_block_delay += player->move_delay_value;
10321 /* when blocking enabled, prevent moving up despite gravity */
10322 #if USE_PLAYER_GRAVITY
10323 if (player->gravity && player->MovDir == MV_UP)
10324 block_delay_adjustment = -1;
10326 if (game.gravity && player->MovDir == MV_UP)
10327 block_delay_adjustment = -1;
10331 /* add block delay adjustment (also possible when not blocking) */
10332 last_field_block_delay += block_delay_adjustment;
10334 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10335 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10338 #if USE_NEW_PLAYER_SPEED
10339 if (player->MovPos != 0) /* player has not yet reached destination */
10345 else if (!FrameReached(&player->actual_frame_counter, 1))
10348 #if USE_NEW_PLAYER_SPEED
10349 if (player->MovPos != 0)
10351 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10352 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10354 /* before DrawPlayer() to draw correct player graphic for this case */
10355 if (player->MovPos == 0)
10356 CheckGravityMovement(player);
10359 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10360 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10362 /* before DrawPlayer() to draw correct player graphic for this case */
10363 if (player->MovPos == 0)
10364 CheckGravityMovement(player);
10367 if (player->MovPos == 0) /* player reached destination field */
10369 if (player->move_delay_reset_counter > 0)
10371 player->move_delay_reset_counter--;
10373 if (player->move_delay_reset_counter == 0)
10375 /* continue with normal speed after quickly moving through gate */
10376 HALVE_PLAYER_SPEED(player);
10378 /* be able to make the next move without delay */
10379 player->move_delay = 0;
10383 player->last_jx = jx;
10384 player->last_jy = jy;
10386 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10387 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10388 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10390 DrawPlayer(player); /* needed here only to cleanup last field */
10391 RemovePlayer(player);
10393 if (local_player->friends_still_needed == 0 ||
10394 IS_SP_ELEMENT(Feld[jx][jy]))
10395 PlayerWins(player);
10398 /* this breaks one level: "machine", level 000 */
10400 int move_direction = player->MovDir;
10401 int enter_side = MV_DIR_OPPOSITE(move_direction);
10402 int leave_side = move_direction;
10403 int old_jx = last_jx;
10404 int old_jy = last_jy;
10405 int old_element = Feld[old_jx][old_jy];
10406 int new_element = Feld[jx][jy];
10408 if (IS_CUSTOM_ELEMENT(old_element))
10409 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10411 player->index_bit, leave_side);
10413 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10414 CE_PLAYER_LEAVES_X,
10415 player->index_bit, leave_side);
10417 if (IS_CUSTOM_ELEMENT(new_element))
10418 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10419 player->index_bit, enter_side);
10421 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10422 CE_PLAYER_ENTERS_X,
10423 player->index_bit, enter_side);
10425 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10426 CE_MOVE_OF_X, move_direction);
10429 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10431 TestIfPlayerTouchesBadThing(jx, jy);
10432 TestIfPlayerTouchesCustomElement(jx, jy);
10434 /* needed because pushed element has not yet reached its destination,
10435 so it would trigger a change event at its previous field location */
10436 if (!player->is_pushing)
10437 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10439 if (!player->active)
10440 RemovePlayer(player);
10443 if (!local_player->LevelSolved && level.use_step_counter)
10453 if (TimeLeft <= 10 && setup.time_limit)
10454 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10456 DrawGameValue_Time(TimeLeft);
10458 if (!TimeLeft && setup.time_limit)
10459 for (i = 0; i < MAX_PLAYERS; i++)
10460 KillPlayer(&stored_player[i]);
10462 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10463 DrawGameValue_Time(TimePlayed);
10466 if (tape.single_step && tape.recording && !tape.pausing &&
10467 !player->programmed_action)
10468 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10472 void ScrollScreen(struct PlayerInfo *player, int mode)
10474 static unsigned long screen_frame_counter = 0;
10476 if (mode == SCROLL_INIT)
10478 /* set scrolling step size according to actual player's moving speed */
10479 ScrollStepSize = TILEX / player->move_delay_value;
10481 screen_frame_counter = FrameCounter;
10482 ScreenMovDir = player->MovDir;
10483 ScreenMovPos = player->MovPos;
10484 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10487 else if (!FrameReached(&screen_frame_counter, 1))
10492 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10493 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10494 redraw_mask |= REDRAW_FIELD;
10497 ScreenMovDir = MV_NONE;
10500 void TestIfPlayerTouchesCustomElement(int x, int y)
10502 static int xy[4][2] =
10509 static int trigger_sides[4][2] =
10511 /* center side border side */
10512 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10513 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10514 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10515 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10517 static int touch_dir[4] =
10519 MV_LEFT | MV_RIGHT,
10524 int center_element = Feld[x][y]; /* should always be non-moving! */
10527 for (i = 0; i < NUM_DIRECTIONS; i++)
10529 int xx = x + xy[i][0];
10530 int yy = y + xy[i][1];
10531 int center_side = trigger_sides[i][0];
10532 int border_side = trigger_sides[i][1];
10533 int border_element;
10535 if (!IN_LEV_FIELD(xx, yy))
10538 if (IS_PLAYER(x, y))
10540 struct PlayerInfo *player = PLAYERINFO(x, y);
10542 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10543 border_element = Feld[xx][yy]; /* may be moving! */
10544 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10545 border_element = Feld[xx][yy];
10546 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10547 border_element = MovingOrBlocked2Element(xx, yy);
10549 continue; /* center and border element do not touch */
10551 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10552 player->index_bit, border_side);
10553 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10554 CE_PLAYER_TOUCHES_X,
10555 player->index_bit, border_side);
10557 else if (IS_PLAYER(xx, yy))
10559 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10561 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10563 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10564 continue; /* center and border element do not touch */
10567 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10568 player->index_bit, center_side);
10569 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10570 CE_PLAYER_TOUCHES_X,
10571 player->index_bit, center_side);
10577 #if USE_ELEMENT_TOUCHING_BUGFIX
10579 void TestIfElementTouchesCustomElement(int x, int y)
10581 static int xy[4][2] =
10588 static int trigger_sides[4][2] =
10590 /* center side border side */
10591 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10592 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10593 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10594 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10596 static int touch_dir[4] =
10598 MV_LEFT | MV_RIGHT,
10603 boolean change_center_element = FALSE;
10604 int center_element = Feld[x][y]; /* should always be non-moving! */
10605 int border_element_old[NUM_DIRECTIONS];
10608 for (i = 0; i < NUM_DIRECTIONS; i++)
10610 int xx = x + xy[i][0];
10611 int yy = y + xy[i][1];
10612 int border_element;
10614 border_element_old[i] = -1;
10616 if (!IN_LEV_FIELD(xx, yy))
10619 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10620 border_element = Feld[xx][yy]; /* may be moving! */
10621 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10622 border_element = Feld[xx][yy];
10623 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10624 border_element = MovingOrBlocked2Element(xx, yy);
10626 continue; /* center and border element do not touch */
10628 border_element_old[i] = border_element;
10631 for (i = 0; i < NUM_DIRECTIONS; i++)
10633 int xx = x + xy[i][0];
10634 int yy = y + xy[i][1];
10635 int center_side = trigger_sides[i][0];
10636 int border_element = border_element_old[i];
10638 if (border_element == -1)
10641 /* check for change of border element */
10642 CheckElementChangeBySide(xx, yy, border_element, center_element,
10643 CE_TOUCHING_X, center_side);
10646 for (i = 0; i < NUM_DIRECTIONS; i++)
10648 int border_side = trigger_sides[i][1];
10649 int border_element = border_element_old[i];
10651 if (border_element == -1)
10654 /* check for change of center element (but change it only once) */
10655 if (!change_center_element)
10656 change_center_element =
10657 CheckElementChangeBySide(x, y, center_element, border_element,
10658 CE_TOUCHING_X, border_side);
10664 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10666 static int xy[4][2] =
10673 static int trigger_sides[4][2] =
10675 /* center side border side */
10676 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10677 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10678 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10679 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10681 static int touch_dir[4] =
10683 MV_LEFT | MV_RIGHT,
10688 boolean change_center_element = FALSE;
10689 int center_element = Feld[x][y]; /* should always be non-moving! */
10692 for (i = 0; i < NUM_DIRECTIONS; i++)
10694 int xx = x + xy[i][0];
10695 int yy = y + xy[i][1];
10696 int center_side = trigger_sides[i][0];
10697 int border_side = trigger_sides[i][1];
10698 int border_element;
10700 if (!IN_LEV_FIELD(xx, yy))
10703 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10704 border_element = Feld[xx][yy]; /* may be moving! */
10705 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10706 border_element = Feld[xx][yy];
10707 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10708 border_element = MovingOrBlocked2Element(xx, yy);
10710 continue; /* center and border element do not touch */
10712 /* check for change of center element (but change it only once) */
10713 if (!change_center_element)
10714 change_center_element =
10715 CheckElementChangeBySide(x, y, center_element, border_element,
10716 CE_TOUCHING_X, border_side);
10718 /* check for change of border element */
10719 CheckElementChangeBySide(xx, yy, border_element, center_element,
10720 CE_TOUCHING_X, center_side);
10726 void TestIfElementHitsCustomElement(int x, int y, int direction)
10728 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10729 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10730 int hitx = x + dx, hity = y + dy;
10731 int hitting_element = Feld[x][y];
10732 int touched_element;
10734 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10737 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10738 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10740 if (IN_LEV_FIELD(hitx, hity))
10742 int opposite_direction = MV_DIR_OPPOSITE(direction);
10743 int hitting_side = direction;
10744 int touched_side = opposite_direction;
10745 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10746 MovDir[hitx][hity] != direction ||
10747 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10753 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10754 CE_HITTING_X, touched_side);
10756 CheckElementChangeBySide(hitx, hity, touched_element,
10757 hitting_element, CE_HIT_BY_X, hitting_side);
10759 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10760 CE_HIT_BY_SOMETHING, opposite_direction);
10764 /* "hitting something" is also true when hitting the playfield border */
10765 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10766 CE_HITTING_SOMETHING, direction);
10770 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10772 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10773 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10774 int hitx = x + dx, hity = y + dy;
10775 int hitting_element = Feld[x][y];
10776 int touched_element;
10778 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10779 !IS_FREE(hitx, hity) &&
10780 (!IS_MOVING(hitx, hity) ||
10781 MovDir[hitx][hity] != direction ||
10782 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10785 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10789 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10793 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10794 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10796 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10797 EP_CAN_SMASH_EVERYTHING, direction);
10799 if (IN_LEV_FIELD(hitx, hity))
10801 int opposite_direction = MV_DIR_OPPOSITE(direction);
10802 int hitting_side = direction;
10803 int touched_side = opposite_direction;
10805 int touched_element = MovingOrBlocked2Element(hitx, hity);
10808 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10809 MovDir[hitx][hity] != direction ||
10810 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10819 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10820 CE_SMASHED_BY_SOMETHING, opposite_direction);
10822 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10823 CE_OTHER_IS_SMASHING, touched_side);
10825 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10826 CE_OTHER_GETS_SMASHED, hitting_side);
10832 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10834 int i, kill_x = -1, kill_y = -1;
10836 int bad_element = -1;
10837 static int test_xy[4][2] =
10844 static int test_dir[4] =
10852 for (i = 0; i < NUM_DIRECTIONS; i++)
10854 int test_x, test_y, test_move_dir, test_element;
10856 test_x = good_x + test_xy[i][0];
10857 test_y = good_y + test_xy[i][1];
10859 if (!IN_LEV_FIELD(test_x, test_y))
10863 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10865 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10867 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10868 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10870 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10871 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10875 bad_element = test_element;
10881 if (kill_x != -1 || kill_y != -1)
10883 if (IS_PLAYER(good_x, good_y))
10885 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10887 if (player->shield_deadly_time_left > 0 &&
10888 !IS_INDESTRUCTIBLE(bad_element))
10889 Bang(kill_x, kill_y);
10890 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10891 KillPlayer(player);
10894 Bang(good_x, good_y);
10898 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10900 int i, kill_x = -1, kill_y = -1;
10901 int bad_element = Feld[bad_x][bad_y];
10902 static int test_xy[4][2] =
10909 static int touch_dir[4] =
10911 MV_LEFT | MV_RIGHT,
10916 static int test_dir[4] =
10924 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10927 for (i = 0; i < NUM_DIRECTIONS; i++)
10929 int test_x, test_y, test_move_dir, test_element;
10931 test_x = bad_x + test_xy[i][0];
10932 test_y = bad_y + test_xy[i][1];
10933 if (!IN_LEV_FIELD(test_x, test_y))
10937 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10939 test_element = Feld[test_x][test_y];
10941 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10942 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10944 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10945 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10947 /* good thing is player or penguin that does not move away */
10948 if (IS_PLAYER(test_x, test_y))
10950 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10952 if (bad_element == EL_ROBOT && player->is_moving)
10953 continue; /* robot does not kill player if he is moving */
10955 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10957 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10958 continue; /* center and border element do not touch */
10965 else if (test_element == EL_PENGUIN)
10974 if (kill_x != -1 || kill_y != -1)
10976 if (IS_PLAYER(kill_x, kill_y))
10978 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10980 if (player->shield_deadly_time_left > 0 &&
10981 !IS_INDESTRUCTIBLE(bad_element))
10982 Bang(bad_x, bad_y);
10983 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10984 KillPlayer(player);
10987 Bang(kill_x, kill_y);
10991 void TestIfPlayerTouchesBadThing(int x, int y)
10993 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10996 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10998 TestIfGoodThingHitsBadThing(x, y, move_dir);
11001 void TestIfBadThingTouchesPlayer(int x, int y)
11003 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11006 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11008 TestIfBadThingHitsGoodThing(x, y, move_dir);
11011 void TestIfFriendTouchesBadThing(int x, int y)
11013 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11016 void TestIfBadThingTouchesFriend(int x, int y)
11018 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11021 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11023 int i, kill_x = bad_x, kill_y = bad_y;
11024 static int xy[4][2] =
11032 for (i = 0; i < NUM_DIRECTIONS; i++)
11036 x = bad_x + xy[i][0];
11037 y = bad_y + xy[i][1];
11038 if (!IN_LEV_FIELD(x, y))
11041 element = Feld[x][y];
11042 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11043 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11051 if (kill_x != bad_x || kill_y != bad_y)
11052 Bang(bad_x, bad_y);
11055 void KillPlayer(struct PlayerInfo *player)
11057 int jx = player->jx, jy = player->jy;
11059 if (!player->active)
11062 /* remove accessible field at the player's position */
11063 Feld[jx][jy] = EL_EMPTY;
11065 /* deactivate shield (else Bang()/Explode() would not work right) */
11066 player->shield_normal_time_left = 0;
11067 player->shield_deadly_time_left = 0;
11070 BuryPlayer(player);
11073 static void KillPlayerUnlessEnemyProtected(int x, int y)
11075 if (!PLAYER_ENEMY_PROTECTED(x, y))
11076 KillPlayer(PLAYERINFO(x, y));
11079 static void KillPlayerUnlessExplosionProtected(int x, int y)
11081 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11082 KillPlayer(PLAYERINFO(x, y));
11085 void BuryPlayer(struct PlayerInfo *player)
11087 int jx = player->jx, jy = player->jy;
11089 if (!player->active)
11092 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11093 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11095 player->GameOver = TRUE;
11096 RemovePlayer(player);
11099 void RemovePlayer(struct PlayerInfo *player)
11101 int jx = player->jx, jy = player->jy;
11102 int i, found = FALSE;
11104 player->present = FALSE;
11105 player->active = FALSE;
11107 if (!ExplodeField[jx][jy])
11108 StorePlayer[jx][jy] = 0;
11110 if (player->is_moving)
11111 DrawLevelField(player->last_jx, player->last_jy);
11113 for (i = 0; i < MAX_PLAYERS; i++)
11114 if (stored_player[i].active)
11118 AllPlayersGone = TRUE;
11124 #if USE_NEW_SNAP_DELAY
11125 static void setFieldForSnapping(int x, int y, int element, int direction)
11127 struct ElementInfo *ei = &element_info[element];
11128 int direction_bit = MV_DIR_TO_BIT(direction);
11129 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11130 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11131 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11133 Feld[x][y] = EL_ELEMENT_SNAPPING;
11134 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11136 ResetGfxAnimation(x, y);
11138 GfxElement[x][y] = element;
11139 GfxAction[x][y] = action;
11140 GfxDir[x][y] = direction;
11141 GfxFrame[x][y] = -1;
11146 =============================================================================
11147 checkDiagonalPushing()
11148 -----------------------------------------------------------------------------
11149 check if diagonal input device direction results in pushing of object
11150 (by checking if the alternative direction is walkable, diggable, ...)
11151 =============================================================================
11154 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11155 int x, int y, int real_dx, int real_dy)
11157 int jx, jy, dx, dy, xx, yy;
11159 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11162 /* diagonal direction: check alternative direction */
11167 xx = jx + (dx == 0 ? real_dx : 0);
11168 yy = jy + (dy == 0 ? real_dy : 0);
11170 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11174 =============================================================================
11176 -----------------------------------------------------------------------------
11177 x, y: field next to player (non-diagonal) to try to dig to
11178 real_dx, real_dy: direction as read from input device (can be diagonal)
11179 =============================================================================
11182 int DigField(struct PlayerInfo *player,
11183 int oldx, int oldy, int x, int y,
11184 int real_dx, int real_dy, int mode)
11186 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11187 boolean player_was_pushing = player->is_pushing;
11188 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11189 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11190 int jx = oldx, jy = oldy;
11191 int dx = x - jx, dy = y - jy;
11192 int nextx = x + dx, nexty = y + dy;
11193 int move_direction = (dx == -1 ? MV_LEFT :
11194 dx == +1 ? MV_RIGHT :
11196 dy == +1 ? MV_DOWN : MV_NONE);
11197 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11198 int dig_side = MV_DIR_OPPOSITE(move_direction);
11199 int old_element = Feld[jx][jy];
11200 #if USE_FIXED_DONT_RUN_INTO
11201 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11207 if (is_player) /* function can also be called by EL_PENGUIN */
11209 if (player->MovPos == 0)
11211 player->is_digging = FALSE;
11212 player->is_collecting = FALSE;
11215 if (player->MovPos == 0) /* last pushing move finished */
11216 player->is_pushing = FALSE;
11218 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11220 player->is_switching = FALSE;
11221 player->push_delay = -1;
11223 return MP_NO_ACTION;
11227 #if !USE_FIXED_DONT_RUN_INTO
11228 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11229 return MP_NO_ACTION;
11232 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11233 old_element = Back[jx][jy];
11235 /* in case of element dropped at player position, check background */
11236 else if (Back[jx][jy] != EL_EMPTY &&
11237 game.engine_version >= VERSION_IDENT(2,2,0,0))
11238 old_element = Back[jx][jy];
11240 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11241 return MP_NO_ACTION; /* field has no opening in this direction */
11243 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11244 return MP_NO_ACTION; /* field has no opening in this direction */
11246 #if USE_FIXED_DONT_RUN_INTO
11247 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11251 Feld[jx][jy] = player->artwork_element;
11252 InitMovingField(jx, jy, MV_DOWN);
11253 Store[jx][jy] = EL_ACID;
11254 ContinueMoving(jx, jy);
11255 BuryPlayer(player);
11257 return MP_DONT_RUN_INTO;
11261 #if USE_FIXED_DONT_RUN_INTO
11262 if (player_can_move && DONT_RUN_INTO(element))
11264 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11266 return MP_DONT_RUN_INTO;
11270 #if USE_FIXED_DONT_RUN_INTO
11271 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11272 return MP_NO_ACTION;
11275 #if !USE_FIXED_DONT_RUN_INTO
11276 element = Feld[x][y];
11279 collect_count = element_info[element].collect_count_initial;
11281 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11282 return MP_NO_ACTION;
11284 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11285 player_can_move = player_can_move_or_snap;
11287 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11288 game.engine_version >= VERSION_IDENT(2,2,0,0))
11290 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11291 player->index_bit, dig_side);
11292 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11293 player->index_bit, dig_side);
11295 if (Feld[x][y] != element) /* field changed by snapping */
11298 return MP_NO_ACTION;
11301 #if USE_PLAYER_GRAVITY
11302 if (player->gravity && is_player && !player->is_auto_moving &&
11303 canFallDown(player) && move_direction != MV_DOWN &&
11304 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11305 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11307 if (game.gravity && is_player && !player->is_auto_moving &&
11308 canFallDown(player) && move_direction != MV_DOWN &&
11309 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11310 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11313 if (player_can_move &&
11314 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11316 int sound_element = SND_ELEMENT(element);
11317 int sound_action = ACTION_WALKING;
11319 if (IS_RND_GATE(element))
11321 if (!player->key[RND_GATE_NR(element)])
11322 return MP_NO_ACTION;
11324 else if (IS_RND_GATE_GRAY(element))
11326 if (!player->key[RND_GATE_GRAY_NR(element)])
11327 return MP_NO_ACTION;
11329 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11331 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11332 return MP_NO_ACTION;
11334 else if (element == EL_EXIT_OPEN ||
11335 element == EL_SP_EXIT_OPEN ||
11336 element == EL_SP_EXIT_OPENING)
11338 sound_action = ACTION_PASSING; /* player is passing exit */
11340 else if (element == EL_EMPTY)
11342 sound_action = ACTION_MOVING; /* nothing to walk on */
11345 /* play sound from background or player, whatever is available */
11346 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11347 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11349 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11351 else if (player_can_move &&
11352 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11354 if (!ACCESS_FROM(element, opposite_direction))
11355 return MP_NO_ACTION; /* field not accessible from this direction */
11357 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11358 return MP_NO_ACTION;
11360 if (IS_EM_GATE(element))
11362 if (!player->key[EM_GATE_NR(element)])
11363 return MP_NO_ACTION;
11365 else if (IS_EM_GATE_GRAY(element))
11367 if (!player->key[EM_GATE_GRAY_NR(element)])
11368 return MP_NO_ACTION;
11370 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11372 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11373 return MP_NO_ACTION;
11375 else if (IS_EMC_GATE(element))
11377 if (!player->key[EMC_GATE_NR(element)])
11378 return MP_NO_ACTION;
11380 else if (IS_EMC_GATE_GRAY(element))
11382 if (!player->key[EMC_GATE_GRAY_NR(element)])
11383 return MP_NO_ACTION;
11385 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11387 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11388 return MP_NO_ACTION;
11390 else if (IS_SP_PORT(element))
11392 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11393 element == EL_SP_GRAVITY_PORT_RIGHT ||
11394 element == EL_SP_GRAVITY_PORT_UP ||
11395 element == EL_SP_GRAVITY_PORT_DOWN)
11396 #if USE_PLAYER_GRAVITY
11397 player->gravity = !player->gravity;
11399 game.gravity = !game.gravity;
11401 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11402 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11403 element == EL_SP_GRAVITY_ON_PORT_UP ||
11404 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11405 #if USE_PLAYER_GRAVITY
11406 player->gravity = TRUE;
11408 game.gravity = TRUE;
11410 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11411 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11412 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11413 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11414 #if USE_PLAYER_GRAVITY
11415 player->gravity = FALSE;
11417 game.gravity = FALSE;
11421 /* automatically move to the next field with double speed */
11422 player->programmed_action = move_direction;
11424 if (player->move_delay_reset_counter == 0)
11426 player->move_delay_reset_counter = 2; /* two double speed steps */
11428 DOUBLE_PLAYER_SPEED(player);
11431 PlayLevelSoundAction(x, y, ACTION_PASSING);
11433 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11437 if (mode != DF_SNAP)
11439 GfxElement[x][y] = GFX_ELEMENT(element);
11440 player->is_digging = TRUE;
11443 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11445 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11446 player->index_bit, dig_side);
11448 if (mode == DF_SNAP)
11450 #if USE_NEW_SNAP_DELAY
11451 if (level.block_snap_field)
11452 setFieldForSnapping(x, y, element, move_direction);
11454 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11456 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11459 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11460 player->index_bit, dig_side);
11463 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11467 if (is_player && mode != DF_SNAP)
11469 GfxElement[x][y] = element;
11470 player->is_collecting = TRUE;
11473 if (element == EL_SPEED_PILL)
11475 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11477 else if (element == EL_EXTRA_TIME && level.time > 0)
11479 TimeLeft += level.extra_time;
11480 DrawGameValue_Time(TimeLeft);
11482 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11484 player->shield_normal_time_left += level.shield_normal_time;
11485 if (element == EL_SHIELD_DEADLY)
11486 player->shield_deadly_time_left += level.shield_deadly_time;
11488 else if (element == EL_DYNAMITE ||
11489 element == EL_EM_DYNAMITE ||
11490 element == EL_SP_DISK_RED)
11492 if (player->inventory_size < MAX_INVENTORY_SIZE)
11493 player->inventory_element[player->inventory_size++] = element;
11495 DrawGameDoorValues();
11497 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11499 player->dynabomb_count++;
11500 player->dynabombs_left++;
11502 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11504 player->dynabomb_size++;
11506 else if (element == EL_DYNABOMB_INCREASE_POWER)
11508 player->dynabomb_xl = TRUE;
11510 else if (IS_KEY(element))
11512 player->key[KEY_NR(element)] = TRUE;
11514 DrawGameDoorValues();
11516 else if (IS_ENVELOPE(element))
11518 player->show_envelope = element;
11520 else if (element == EL_EMC_LENSES)
11522 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11524 RedrawAllInvisibleElementsForLenses();
11526 else if (element == EL_EMC_MAGNIFIER)
11528 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11530 RedrawAllInvisibleElementsForMagnifier();
11532 else if (IS_DROPPABLE(element) ||
11533 IS_THROWABLE(element)) /* can be collected and dropped */
11537 if (collect_count == 0)
11538 player->inventory_infinite_element = element;
11540 for (i = 0; i < collect_count; i++)
11541 if (player->inventory_size < MAX_INVENTORY_SIZE)
11542 player->inventory_element[player->inventory_size++] = element;
11544 DrawGameDoorValues();
11546 else if (collect_count > 0)
11548 local_player->gems_still_needed -= collect_count;
11549 if (local_player->gems_still_needed < 0)
11550 local_player->gems_still_needed = 0;
11552 DrawGameValue_Emeralds(local_player->gems_still_needed);
11555 RaiseScoreElement(element);
11556 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11559 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11560 player->index_bit, dig_side);
11562 if (mode == DF_SNAP)
11564 #if USE_NEW_SNAP_DELAY
11565 if (level.block_snap_field)
11566 setFieldForSnapping(x, y, element, move_direction);
11568 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11570 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11573 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11574 player->index_bit, dig_side);
11577 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11579 if (mode == DF_SNAP && element != EL_BD_ROCK)
11580 return MP_NO_ACTION;
11582 if (CAN_FALL(element) && dy)
11583 return MP_NO_ACTION;
11585 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11586 !(element == EL_SPRING && level.use_spring_bug))
11587 return MP_NO_ACTION;
11589 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11590 ((move_direction & MV_VERTICAL &&
11591 ((element_info[element].move_pattern & MV_LEFT &&
11592 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11593 (element_info[element].move_pattern & MV_RIGHT &&
11594 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11595 (move_direction & MV_HORIZONTAL &&
11596 ((element_info[element].move_pattern & MV_UP &&
11597 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11598 (element_info[element].move_pattern & MV_DOWN &&
11599 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11600 return MP_NO_ACTION;
11602 /* do not push elements already moving away faster than player */
11603 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11604 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11605 return MP_NO_ACTION;
11607 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11609 if (player->push_delay_value == -1 || !player_was_pushing)
11610 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11612 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11614 if (player->push_delay_value == -1)
11615 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11617 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11619 if (!player->is_pushing)
11620 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11623 player->is_pushing = TRUE;
11624 player->is_active = TRUE;
11626 if (!(IN_LEV_FIELD(nextx, nexty) &&
11627 (IS_FREE(nextx, nexty) ||
11628 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11629 IS_SB_ELEMENT(element)))))
11630 return MP_NO_ACTION;
11632 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11633 return MP_NO_ACTION;
11635 if (player->push_delay == -1) /* new pushing; restart delay */
11636 player->push_delay = 0;
11638 if (player->push_delay < player->push_delay_value &&
11639 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11640 element != EL_SPRING && element != EL_BALLOON)
11642 /* make sure that there is no move delay before next try to push */
11643 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11644 player->move_delay = 0;
11646 return MP_NO_ACTION;
11649 if (IS_SB_ELEMENT(element))
11651 if (element == EL_SOKOBAN_FIELD_FULL)
11653 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11654 local_player->sokobanfields_still_needed++;
11657 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11659 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11660 local_player->sokobanfields_still_needed--;
11663 Feld[x][y] = EL_SOKOBAN_OBJECT;
11665 if (Back[x][y] == Back[nextx][nexty])
11666 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11667 else if (Back[x][y] != 0)
11668 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11671 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11674 if (local_player->sokobanfields_still_needed == 0 &&
11675 game.emulation == EMU_SOKOBAN)
11677 PlayerWins(player);
11679 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11683 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11685 InitMovingField(x, y, move_direction);
11686 GfxAction[x][y] = ACTION_PUSHING;
11688 if (mode == DF_SNAP)
11689 ContinueMoving(x, y);
11691 MovPos[x][y] = (dx != 0 ? dx : dy);
11693 Pushed[x][y] = TRUE;
11694 Pushed[nextx][nexty] = TRUE;
11696 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11697 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11699 player->push_delay_value = -1; /* get new value later */
11701 /* check for element change _after_ element has been pushed */
11702 if (game.use_change_when_pushing_bug)
11704 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11705 player->index_bit, dig_side);
11706 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11707 player->index_bit, dig_side);
11710 else if (IS_SWITCHABLE(element))
11712 if (PLAYER_SWITCHING(player, x, y))
11714 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11715 player->index_bit, dig_side);
11720 player->is_switching = TRUE;
11721 player->switch_x = x;
11722 player->switch_y = y;
11724 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11726 if (element == EL_ROBOT_WHEEL)
11728 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11732 DrawLevelField(x, y);
11734 else if (element == EL_SP_TERMINAL)
11738 SCAN_PLAYFIELD(xx, yy)
11740 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11742 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11743 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11746 else if (IS_BELT_SWITCH(element))
11748 ToggleBeltSwitch(x, y);
11750 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11751 element == EL_SWITCHGATE_SWITCH_DOWN)
11753 ToggleSwitchgateSwitch(x, y);
11755 else if (element == EL_LIGHT_SWITCH ||
11756 element == EL_LIGHT_SWITCH_ACTIVE)
11758 ToggleLightSwitch(x, y);
11760 else if (element == EL_TIMEGATE_SWITCH)
11762 ActivateTimegateSwitch(x, y);
11764 else if (element == EL_BALLOON_SWITCH_LEFT ||
11765 element == EL_BALLOON_SWITCH_RIGHT ||
11766 element == EL_BALLOON_SWITCH_UP ||
11767 element == EL_BALLOON_SWITCH_DOWN ||
11768 element == EL_BALLOON_SWITCH_NONE ||
11769 element == EL_BALLOON_SWITCH_ANY)
11771 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11772 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11773 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11774 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11775 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11778 else if (element == EL_LAMP)
11780 Feld[x][y] = EL_LAMP_ACTIVE;
11781 local_player->lights_still_needed--;
11783 ResetGfxAnimation(x, y);
11784 DrawLevelField(x, y);
11786 else if (element == EL_TIME_ORB_FULL)
11788 Feld[x][y] = EL_TIME_ORB_EMPTY;
11790 if (level.time > 0 || level.use_time_orb_bug)
11792 TimeLeft += level.time_orb_time;
11793 DrawGameValue_Time(TimeLeft);
11796 ResetGfxAnimation(x, y);
11797 DrawLevelField(x, y);
11799 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11800 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11804 game.ball_state = !game.ball_state;
11806 SCAN_PLAYFIELD(xx, yy)
11808 int e = Feld[xx][yy];
11810 if (game.ball_state)
11812 if (e == EL_EMC_MAGIC_BALL)
11813 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11814 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11815 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11819 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11820 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11821 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11822 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11827 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11828 player->index_bit, dig_side);
11830 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11831 player->index_bit, dig_side);
11833 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11834 player->index_bit, dig_side);
11840 if (!PLAYER_SWITCHING(player, x, y))
11842 player->is_switching = TRUE;
11843 player->switch_x = x;
11844 player->switch_y = y;
11846 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11847 player->index_bit, dig_side);
11848 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11849 player->index_bit, dig_side);
11851 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11852 player->index_bit, dig_side);
11853 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11854 player->index_bit, dig_side);
11857 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11858 player->index_bit, dig_side);
11859 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11860 player->index_bit, dig_side);
11862 return MP_NO_ACTION;
11865 player->push_delay = -1;
11867 if (is_player) /* function can also be called by EL_PENGUIN */
11869 if (Feld[x][y] != element) /* really digged/collected something */
11871 player->is_collecting = !player->is_digging;
11872 player->is_active = TRUE;
11879 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11881 int jx = player->jx, jy = player->jy;
11882 int x = jx + dx, y = jy + dy;
11883 int snap_direction = (dx == -1 ? MV_LEFT :
11884 dx == +1 ? MV_RIGHT :
11886 dy == +1 ? MV_DOWN : MV_NONE);
11887 boolean can_continue_snapping = (level.continuous_snapping &&
11888 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
11890 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11893 if (!player->active || !IN_LEV_FIELD(x, y))
11901 if (player->MovPos == 0)
11902 player->is_pushing = FALSE;
11904 player->is_snapping = FALSE;
11906 if (player->MovPos == 0)
11908 player->is_moving = FALSE;
11909 player->is_digging = FALSE;
11910 player->is_collecting = FALSE;
11916 #if USE_NEW_CONTINUOUS_SNAPPING
11917 /* prevent snapping with already pressed snap key when not allowed */
11918 if (player->is_snapping && !can_continue_snapping)
11921 if (player->is_snapping)
11925 player->MovDir = snap_direction;
11927 if (player->MovPos == 0)
11929 player->is_moving = FALSE;
11930 player->is_digging = FALSE;
11931 player->is_collecting = FALSE;
11934 player->is_dropping = FALSE;
11935 player->is_dropping_pressed = FALSE;
11936 player->drop_pressed_delay = 0;
11938 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11941 player->is_snapping = TRUE;
11942 player->is_active = TRUE;
11944 if (player->MovPos == 0)
11946 player->is_moving = FALSE;
11947 player->is_digging = FALSE;
11948 player->is_collecting = FALSE;
11951 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11952 DrawLevelField(player->last_jx, player->last_jy);
11954 DrawLevelField(x, y);
11959 boolean DropElement(struct PlayerInfo *player)
11961 int old_element, new_element;
11962 int dropx = player->jx, dropy = player->jy;
11963 int drop_direction = player->MovDir;
11964 int drop_side = drop_direction;
11965 int drop_element = (player->inventory_size > 0 ?
11966 player->inventory_element[player->inventory_size - 1] :
11967 player->inventory_infinite_element != EL_UNDEFINED ?
11968 player->inventory_infinite_element :
11969 player->dynabombs_left > 0 ?
11970 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11973 player->is_dropping_pressed = TRUE;
11975 /* do not drop an element on top of another element; when holding drop key
11976 pressed without moving, dropped element must move away before the next
11977 element can be dropped (this is especially important if the next element
11978 is dynamite, which can be placed on background for historical reasons) */
11979 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11982 if (IS_THROWABLE(drop_element))
11984 dropx += GET_DX_FROM_DIR(drop_direction);
11985 dropy += GET_DY_FROM_DIR(drop_direction);
11987 if (!IN_LEV_FIELD(dropx, dropy))
11991 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11992 new_element = drop_element; /* default: no change when dropping */
11994 /* check if player is active, not moving and ready to drop */
11995 if (!player->active || player->MovPos || player->drop_delay > 0)
11998 /* check if player has anything that can be dropped */
11999 if (new_element == EL_UNDEFINED)
12002 /* check if drop key was pressed long enough for EM style dynamite */
12003 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12006 /* check if anything can be dropped at the current position */
12007 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12010 /* collected custom elements can only be dropped on empty fields */
12011 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12014 if (old_element != EL_EMPTY)
12015 Back[dropx][dropy] = old_element; /* store old element on this field */
12017 ResetGfxAnimation(dropx, dropy);
12018 ResetRandomAnimationValue(dropx, dropy);
12020 if (player->inventory_size > 0 ||
12021 player->inventory_infinite_element != EL_UNDEFINED)
12023 if (player->inventory_size > 0)
12025 player->inventory_size--;
12027 DrawGameDoorValues();
12029 if (new_element == EL_DYNAMITE)
12030 new_element = EL_DYNAMITE_ACTIVE;
12031 else if (new_element == EL_EM_DYNAMITE)
12032 new_element = EL_EM_DYNAMITE_ACTIVE;
12033 else if (new_element == EL_SP_DISK_RED)
12034 new_element = EL_SP_DISK_RED_ACTIVE;
12037 Feld[dropx][dropy] = new_element;
12039 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12040 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12041 el2img(Feld[dropx][dropy]), 0);
12043 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12045 /* needed if previous element just changed to "empty" in the last frame */
12046 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12048 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12049 player->index_bit, drop_side);
12050 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12052 player->index_bit, drop_side);
12054 TestIfElementTouchesCustomElement(dropx, dropy);
12056 else /* player is dropping a dyna bomb */
12058 player->dynabombs_left--;
12060 Feld[dropx][dropy] = new_element;
12062 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12063 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12064 el2img(Feld[dropx][dropy]), 0);
12066 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12069 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12070 InitField_WithBug1(dropx, dropy, FALSE);
12072 new_element = Feld[dropx][dropy]; /* element might have changed */
12074 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12075 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12077 int move_direction, nextx, nexty;
12079 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12080 MovDir[dropx][dropy] = drop_direction;
12082 move_direction = MovDir[dropx][dropy];
12083 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12084 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12086 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12088 #if USE_FIX_IMPACT_COLLISION
12089 /* do not cause impact style collision by dropping elements that can fall */
12090 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12092 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12096 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12097 player->is_dropping = TRUE;
12099 player->drop_pressed_delay = 0;
12100 player->is_dropping_pressed = FALSE;
12102 player->drop_x = dropx;
12103 player->drop_y = dropy;
12108 /* ------------------------------------------------------------------------- */
12109 /* game sound playing functions */
12110 /* ------------------------------------------------------------------------- */
12112 static int *loop_sound_frame = NULL;
12113 static int *loop_sound_volume = NULL;
12115 void InitPlayLevelSound()
12117 int num_sounds = getSoundListSize();
12119 checked_free(loop_sound_frame);
12120 checked_free(loop_sound_volume);
12122 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12123 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12126 static void PlayLevelSound(int x, int y, int nr)
12128 int sx = SCREENX(x), sy = SCREENY(y);
12129 int volume, stereo_position;
12130 int max_distance = 8;
12131 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12133 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12134 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12137 if (!IN_LEV_FIELD(x, y) ||
12138 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12139 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12142 volume = SOUND_MAX_VOLUME;
12144 if (!IN_SCR_FIELD(sx, sy))
12146 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12147 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12149 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12152 stereo_position = (SOUND_MAX_LEFT +
12153 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12154 (SCR_FIELDX + 2 * max_distance));
12156 if (IS_LOOP_SOUND(nr))
12158 /* This assures that quieter loop sounds do not overwrite louder ones,
12159 while restarting sound volume comparison with each new game frame. */
12161 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12164 loop_sound_volume[nr] = volume;
12165 loop_sound_frame[nr] = FrameCounter;
12168 PlaySoundExt(nr, volume, stereo_position, type);
12171 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12173 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12174 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12175 y < LEVELY(BY1) ? LEVELY(BY1) :
12176 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12180 static void PlayLevelSoundAction(int x, int y, int action)
12182 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12185 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12187 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12189 if (sound_effect != SND_UNDEFINED)
12190 PlayLevelSound(x, y, sound_effect);
12193 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12196 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12198 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12199 PlayLevelSound(x, y, sound_effect);
12202 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12204 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12206 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12207 PlayLevelSound(x, y, sound_effect);
12210 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12212 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12214 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12215 StopSound(sound_effect);
12218 static void PlayLevelMusic()
12220 if (levelset.music[level_nr] != MUS_UNDEFINED)
12221 PlayMusic(levelset.music[level_nr]); /* from config file */
12223 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12226 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
12228 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12229 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
12230 int x = xx - 1 - offset;
12231 int y = yy - 1 - offset;
12236 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12240 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12244 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12248 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12252 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12256 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12260 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12263 case SAMPLE_android_clone:
12264 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12267 case SAMPLE_android_move:
12268 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12271 case SAMPLE_spring:
12272 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12276 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12280 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12283 case SAMPLE_eater_eat:
12284 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12288 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12291 case SAMPLE_collect:
12292 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12295 case SAMPLE_diamond:
12296 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12299 case SAMPLE_squash:
12300 /* !!! CHECK THIS !!! */
12302 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12304 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12308 case SAMPLE_wonderfall:
12309 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12313 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12317 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12321 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12325 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12329 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12333 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12336 case SAMPLE_wonder:
12337 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12341 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12344 case SAMPLE_exit_open:
12345 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12348 case SAMPLE_exit_leave:
12349 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12352 case SAMPLE_dynamite:
12353 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12357 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12361 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12365 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12369 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12373 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12377 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12381 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12387 void ChangeTime(int value)
12389 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
12393 /* EMC game engine uses value from time counter of RND game engine */
12394 level.native_em_level->lev->time = *time;
12396 DrawGameValue_Time(*time);
12399 void RaiseScore(int value)
12401 /* EMC game engine and RND game engine have separate score counters */
12402 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
12403 &level.native_em_level->lev->score : &local_player->score);
12407 DrawGameValue_Score(*score);
12411 void RaiseScore(int value)
12413 local_player->score += value;
12415 DrawGameValue_Score(local_player->score);
12418 void RaiseScoreElement(int element)
12423 case EL_BD_DIAMOND:
12424 case EL_EMERALD_YELLOW:
12425 case EL_EMERALD_RED:
12426 case EL_EMERALD_PURPLE:
12427 case EL_SP_INFOTRON:
12428 RaiseScore(level.score[SC_EMERALD]);
12431 RaiseScore(level.score[SC_DIAMOND]);
12434 RaiseScore(level.score[SC_CRYSTAL]);
12437 RaiseScore(level.score[SC_PEARL]);
12440 case EL_BD_BUTTERFLY:
12441 case EL_SP_ELECTRON:
12442 RaiseScore(level.score[SC_BUG]);
12445 case EL_BD_FIREFLY:
12446 case EL_SP_SNIKSNAK:
12447 RaiseScore(level.score[SC_SPACESHIP]);
12450 case EL_DARK_YAMYAM:
12451 RaiseScore(level.score[SC_YAMYAM]);
12454 RaiseScore(level.score[SC_ROBOT]);
12457 RaiseScore(level.score[SC_PACMAN]);
12460 RaiseScore(level.score[SC_NUT]);
12463 case EL_EM_DYNAMITE:
12464 case EL_SP_DISK_RED:
12465 case EL_DYNABOMB_INCREASE_NUMBER:
12466 case EL_DYNABOMB_INCREASE_SIZE:
12467 case EL_DYNABOMB_INCREASE_POWER:
12468 RaiseScore(level.score[SC_DYNAMITE]);
12470 case EL_SHIELD_NORMAL:
12471 case EL_SHIELD_DEADLY:
12472 RaiseScore(level.score[SC_SHIELD]);
12474 case EL_EXTRA_TIME:
12475 RaiseScore(level.extra_time_score);
12489 RaiseScore(level.score[SC_KEY]);
12492 RaiseScore(element_info[element].collect_score);
12497 void RequestQuitGame(boolean ask_if_really_quit)
12499 if (AllPlayersGone ||
12500 !ask_if_really_quit ||
12501 level_editor_test_game ||
12502 Request("Do you really want to quit the game ?",
12503 REQ_ASK | REQ_STAY_CLOSED))
12505 #if defined(NETWORK_AVALIABLE)
12506 if (options.network)
12507 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12511 if (!ask_if_really_quit || level_editor_test_game)
12513 game_status = GAME_MODE_MAIN;
12519 FadeOut(REDRAW_FIELD);
12521 game_status = GAME_MODE_MAIN;
12523 DrawAndFadeInMainMenu(REDRAW_FIELD);
12529 if (tape.playing && tape.deactivate_display)
12530 TapeDeactivateDisplayOff(TRUE);
12532 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12534 if (tape.playing && tape.deactivate_display)
12535 TapeDeactivateDisplayOn();
12540 /* ------------------------------------------------------------------------- */
12541 /* random generator functions */
12542 /* ------------------------------------------------------------------------- */
12544 unsigned int InitEngineRandom_RND(long seed)
12546 game.num_random_calls = 0;
12549 unsigned int rnd_seed = InitEngineRandom(seed);
12551 printf("::: START RND: %d\n", rnd_seed);
12556 return InitEngineRandom(seed);
12562 unsigned int RND(int max)
12566 game.num_random_calls++;
12568 return GetEngineRandom(max);
12575 /* ------------------------------------------------------------------------- */
12576 /* game engine snapshot handling functions */
12577 /* ------------------------------------------------------------------------- */
12579 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
12581 struct EngineSnapshotInfo
12583 /* runtime values for custom element collect score */
12584 int collect_score[NUM_CUSTOM_ELEMENTS];
12586 /* runtime values for group element choice position */
12587 int choice_pos[NUM_GROUP_ELEMENTS];
12589 /* runtime values for belt position animations */
12590 int belt_graphic[4 * NUM_BELT_PARTS];
12591 int belt_anim_mode[4 * NUM_BELT_PARTS];
12594 struct EngineSnapshotNodeInfo
12601 static struct EngineSnapshotInfo engine_snapshot_rnd;
12602 static ListNode *engine_snapshot_list = NULL;
12603 static char *snapshot_level_identifier = NULL;
12604 static int snapshot_level_nr = -1;
12606 void FreeEngineSnapshot()
12608 while (engine_snapshot_list != NULL)
12609 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
12612 setString(&snapshot_level_identifier, NULL);
12613 snapshot_level_nr = -1;
12616 static void SaveEngineSnapshotValues_RND()
12618 static int belt_base_active_element[4] =
12620 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
12621 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
12622 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
12623 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
12627 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12629 int element = EL_CUSTOM_START + i;
12631 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
12634 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12636 int element = EL_GROUP_START + i;
12638 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
12641 for (i = 0; i < 4; i++)
12643 for (j = 0; j < NUM_BELT_PARTS; j++)
12645 int element = belt_base_active_element[i] + j;
12646 int graphic = el2img(element);
12647 int anim_mode = graphic_info[graphic].anim_mode;
12649 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
12650 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
12655 static void LoadEngineSnapshotValues_RND()
12657 unsigned long num_random_calls = game.num_random_calls;
12660 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12662 int element = EL_CUSTOM_START + i;
12664 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
12667 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12669 int element = EL_GROUP_START + i;
12671 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
12674 for (i = 0; i < 4; i++)
12676 for (j = 0; j < NUM_BELT_PARTS; j++)
12678 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
12679 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
12681 graphic_info[graphic].anim_mode = anim_mode;
12685 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
12687 InitRND(tape.random_seed);
12688 for (i = 0; i < num_random_calls; i++)
12692 if (game.num_random_calls != num_random_calls)
12694 Error(ERR_RETURN, "number of random calls out of sync");
12695 Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
12696 Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
12697 Error(ERR_EXIT, "this should not happen -- please debug");
12701 static void SaveEngineSnapshotBuffer(void *buffer, int size)
12703 struct EngineSnapshotNodeInfo *bi =
12704 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
12706 bi->buffer_orig = buffer;
12707 bi->buffer_copy = checked_malloc(size);
12710 memcpy(bi->buffer_copy, buffer, size);
12712 addNodeToList(&engine_snapshot_list, NULL, bi);
12715 void SaveEngineSnapshot()
12717 FreeEngineSnapshot(); /* free previous snapshot, if needed */
12719 if (level_editor_test_game) /* do not save snapshots from editor */
12722 /* copy some special values to a structure better suited for the snapshot */
12724 SaveEngineSnapshotValues_RND();
12725 SaveEngineSnapshotValues_EM();
12727 /* save values stored in special snapshot structure */
12729 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
12730 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
12732 /* save further RND engine values */
12734 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
12735 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
12736 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
12738 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
12739 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
12740 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
12741 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
12743 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
12744 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
12745 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
12746 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
12747 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
12749 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
12750 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
12751 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
12753 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
12755 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
12757 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
12758 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
12760 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
12761 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
12762 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
12763 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
12764 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
12765 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
12766 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
12767 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
12768 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
12769 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
12770 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
12771 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
12772 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
12773 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
12774 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
12775 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
12776 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
12777 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
12779 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
12780 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
12782 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
12783 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
12784 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
12786 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
12787 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
12789 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
12790 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
12791 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
12792 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
12793 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
12795 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
12796 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
12798 /* save level identification information */
12800 setString(&snapshot_level_identifier, leveldir_current->identifier);
12801 snapshot_level_nr = level_nr;
12804 ListNode *node = engine_snapshot_list;
12807 while (node != NULL)
12809 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
12814 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
12818 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
12820 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
12823 void LoadEngineSnapshot()
12825 ListNode *node = engine_snapshot_list;
12827 if (engine_snapshot_list == NULL)
12830 while (node != NULL)
12832 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
12837 /* restore special values from snapshot structure */
12839 LoadEngineSnapshotValues_RND();
12840 LoadEngineSnapshotValues_EM();
12843 boolean CheckEngineSnapshot()
12845 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
12846 snapshot_level_nr == level_nr);
12850 /* ---------- new game button stuff ---------------------------------------- */
12852 /* graphic position values for game buttons */
12853 #define GAME_BUTTON_XSIZE 30
12854 #define GAME_BUTTON_YSIZE 30
12855 #define GAME_BUTTON_XPOS 5
12856 #define GAME_BUTTON_YPOS 215
12857 #define SOUND_BUTTON_XPOS 5
12858 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12860 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12861 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12862 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12863 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12864 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12865 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12872 } gamebutton_info[NUM_GAME_BUTTONS] =
12875 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12880 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12881 GAME_CTRL_ID_PAUSE,
12885 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12890 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12891 SOUND_CTRL_ID_MUSIC,
12892 "background music on/off"
12895 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12896 SOUND_CTRL_ID_LOOPS,
12897 "sound loops on/off"
12900 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12901 SOUND_CTRL_ID_SIMPLE,
12902 "normal sounds on/off"
12906 void CreateGameButtons()
12910 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12912 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12913 struct GadgetInfo *gi;
12916 unsigned long event_mask;
12917 int gd_xoffset, gd_yoffset;
12918 int gd_x1, gd_x2, gd_y1, gd_y2;
12921 gd_xoffset = gamebutton_info[i].x;
12922 gd_yoffset = gamebutton_info[i].y;
12923 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12924 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12926 if (id == GAME_CTRL_ID_STOP ||
12927 id == GAME_CTRL_ID_PAUSE ||
12928 id == GAME_CTRL_ID_PLAY)
12930 button_type = GD_TYPE_NORMAL_BUTTON;
12932 event_mask = GD_EVENT_RELEASED;
12933 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12934 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12938 button_type = GD_TYPE_CHECK_BUTTON;
12940 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12941 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12942 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12943 event_mask = GD_EVENT_PRESSED;
12944 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12945 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12948 gi = CreateGadget(GDI_CUSTOM_ID, id,
12949 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12950 GDI_X, DX + gd_xoffset,
12951 GDI_Y, DY + gd_yoffset,
12952 GDI_WIDTH, GAME_BUTTON_XSIZE,
12953 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12954 GDI_TYPE, button_type,
12955 GDI_STATE, GD_BUTTON_UNPRESSED,
12956 GDI_CHECKED, checked,
12957 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12958 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12959 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12960 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12961 GDI_EVENT_MASK, event_mask,
12962 GDI_CALLBACK_ACTION, HandleGameButtons,
12966 Error(ERR_EXIT, "cannot create gadget");
12968 game_gadget[id] = gi;
12972 void FreeGameButtons()
12976 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12977 FreeGadget(game_gadget[i]);
12980 static void MapGameButtons()
12984 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12985 MapGadget(game_gadget[i]);
12988 void UnmapGameButtons()
12992 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12993 UnmapGadget(game_gadget[i]);
12996 static void HandleGameButtons(struct GadgetInfo *gi)
12998 int id = gi->custom_id;
13000 if (game_status != GAME_MODE_PLAYING)
13005 case GAME_CTRL_ID_STOP:
13009 RequestQuitGame(TRUE);
13012 case GAME_CTRL_ID_PAUSE:
13013 if (options.network)
13015 #if defined(NETWORK_AVALIABLE)
13017 SendToServer_ContinuePlaying();
13019 SendToServer_PausePlaying();
13023 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13026 case GAME_CTRL_ID_PLAY:
13029 #if defined(NETWORK_AVALIABLE)
13030 if (options.network)
13031 SendToServer_ContinuePlaying();
13035 tape.pausing = FALSE;
13036 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13041 case SOUND_CTRL_ID_MUSIC:
13042 if (setup.sound_music)
13044 setup.sound_music = FALSE;
13047 else if (audio.music_available)
13049 setup.sound = setup.sound_music = TRUE;
13051 SetAudioMode(setup.sound);
13057 case SOUND_CTRL_ID_LOOPS:
13058 if (setup.sound_loops)
13059 setup.sound_loops = FALSE;
13060 else if (audio.loops_available)
13062 setup.sound = setup.sound_loops = TRUE;
13063 SetAudioMode(setup.sound);
13067 case SOUND_CTRL_ID_SIMPLE:
13068 if (setup.sound_simple)
13069 setup.sound_simple = FALSE;
13070 else if (audio.sound_available)
13072 setup.sound = setup.sound_simple = TRUE;
13073 SetAudioMode(setup.sound);