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)
67 /* for MovePlayer() */
68 #define MP_NO_ACTION 0
71 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
73 /* for ScrollPlayer() */
75 #define SCROLL_GO_ON 1
77 /* for Bang()/Explode() */
78 #define EX_PHASE_START 0
79 #define EX_TYPE_NONE 0
80 #define EX_TYPE_NORMAL (1 << 0)
81 #define EX_TYPE_CENTER (1 << 1)
82 #define EX_TYPE_BORDER (1 << 2)
83 #define EX_TYPE_CROSS (1 << 3)
84 #define EX_TYPE_DYNA (1 << 4)
85 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
87 #define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0)
89 /* special positions in the game control window (relative to control window) */
90 #define XX_LEVEL1 (game.panel.level.x)
91 #define XX_LEVEL2 (game.panel.level.x - 1)
92 #define YY_LEVEL (game.panel.level.y)
93 #define XX_EMERALDS (game.panel.gems.x)
94 #define YY_EMERALDS (game.panel.gems.y)
95 #define XX_DYNAMITE (game.panel.inventory.x)
96 #define YY_DYNAMITE (game.panel.inventory.y)
97 #define XX_KEYS (game.panel.keys.x)
98 #define YY_KEYS (game.panel.keys.y)
99 #define XX_SCORE (game.panel.score.x)
100 #define YY_SCORE (game.panel.score.y)
101 #define XX_TIME1 (game.panel.time.x)
102 #define XX_TIME2 (game.panel.time.x + 1)
103 #define YY_TIME (game.panel.time.y)
105 /* special positions in the game control window (relative to main window) */
106 #define DX_LEVEL1 (DX + XX_LEVEL1)
107 #define DX_LEVEL2 (DX + XX_LEVEL2)
108 #define DY_LEVEL (DY + YY_LEVEL)
109 #define DX_EMERALDS (DX + XX_EMERALDS)
110 #define DY_EMERALDS (DY + YY_EMERALDS)
111 #define DX_DYNAMITE (DX + XX_DYNAMITE)
112 #define DY_DYNAMITE (DY + YY_DYNAMITE)
113 #define DX_KEYS (DX + XX_KEYS)
114 #define DY_KEYS (DY + YY_KEYS)
115 #define DX_SCORE (DX + XX_SCORE)
116 #define DY_SCORE (DY + YY_SCORE)
117 #define DX_TIME1 (DX + XX_TIME1)
118 #define DX_TIME2 (DX + XX_TIME2)
119 #define DY_TIME (DY + YY_TIME)
121 /* values for delayed check of falling and moving elements and for collision */
122 #define CHECK_DELAY_MOVING 3
123 #define CHECK_DELAY_FALLING 3
124 #define CHECK_DELAY_COLLISION 2
126 /* values for initial player move delay (initial delay counter value) */
127 #define INITIAL_MOVE_DELAY_OFF -1
128 #define INITIAL_MOVE_DELAY_ON 0
130 /* values for player movement speed (which is in fact a delay value) */
131 #define MOVE_DELAY_MIN_SPEED 32
132 #define MOVE_DELAY_NORMAL_SPEED 8
133 #define MOVE_DELAY_HIGH_SPEED 4
134 #define MOVE_DELAY_MAX_SPEED 1
136 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
137 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
139 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
140 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
142 /* values for other actions */
143 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
144 #define MOVE_STEPSIZE_MIN (1)
145 #define MOVE_STEPSIZE_MAX (TILEX)
147 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
148 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
150 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
152 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
153 RND(element_info[e].push_delay_random))
154 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
155 RND(element_info[e].drop_delay_random))
156 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
157 RND(element_info[e].move_delay_random))
158 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
159 (element_info[e].move_delay_random))
160 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
161 RND(element_info[e].ce_value_random_initial))
162 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
163 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
164 RND((c)->delay_random * (c)->delay_frames))
165 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
166 RND((c)->delay_random))
169 #define GET_VALID_RUNTIME_ELEMENT(e) \
170 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
172 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
173 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
174 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
175 (be) + (e) - EL_SELF)
177 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
178 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
179 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
180 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
181 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
182 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
183 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
184 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
185 RESOLVED_REFERENCE_ELEMENT(be, e) : \
188 #define CAN_GROW_INTO(e) \
189 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
191 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
192 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
195 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
196 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
197 (CAN_MOVE_INTO_ACID(e) && \
198 Feld[x][y] == EL_ACID) || \
201 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
202 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
203 (CAN_MOVE_INTO_ACID(e) && \
204 Feld[x][y] == EL_ACID) || \
207 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
208 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
210 (CAN_MOVE_INTO_ACID(e) && \
211 Feld[x][y] == EL_ACID) || \
212 (DONT_COLLIDE_WITH(e) && \
214 !PLAYER_ENEMY_PROTECTED(x, y))))
216 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
217 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
219 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
220 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
222 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
223 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
225 #define ANDROID_CAN_CLONE_FIELD(x, y) \
226 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
227 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
229 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
230 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
232 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
233 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
235 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
236 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
238 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
239 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
241 #define PIG_CAN_ENTER_FIELD(e, x, y) \
242 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
244 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
245 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
246 IS_FOOD_PENGUIN(Feld[x][y])))
247 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
248 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
250 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
251 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
253 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
254 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
256 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
257 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
258 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
260 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
262 #define CE_ENTER_FIELD_COND(e, x, y) \
263 (!IS_PLAYER(x, y) && \
264 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
266 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
267 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
269 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
270 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
272 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
273 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
274 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
275 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
277 /* game button identifiers */
278 #define GAME_CTRL_ID_STOP 0
279 #define GAME_CTRL_ID_PAUSE 1
280 #define GAME_CTRL_ID_PLAY 2
281 #define SOUND_CTRL_ID_MUSIC 3
282 #define SOUND_CTRL_ID_LOOPS 4
283 #define SOUND_CTRL_ID_SIMPLE 5
285 #define NUM_GAME_BUTTONS 6
288 /* forward declaration for internal use */
290 static void CreateField(int, int, int);
292 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
293 static void AdvanceFrameAndPlayerCounters(int);
295 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
296 static boolean MovePlayer(struct PlayerInfo *, int, int);
297 static void ScrollPlayer(struct PlayerInfo *, int);
298 static void ScrollScreen(struct PlayerInfo *, int);
300 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
302 static void InitBeltMovement(void);
303 static void CloseAllOpenTimegates(void);
304 static void CheckGravityMovement(struct PlayerInfo *);
305 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
306 static void KillPlayerUnlessEnemyProtected(int, int);
307 static void KillPlayerUnlessExplosionProtected(int, int);
309 static void TestIfPlayerTouchesCustomElement(int, int);
310 static void TestIfElementTouchesCustomElement(int, int);
311 static void TestIfElementHitsCustomElement(int, int, int);
313 static void TestIfElementSmashesCustomElement(int, int, int);
316 static void HandleElementChange(int, int, int);
317 static void ExecuteCustomElementAction(int, int, int, int);
318 static boolean ChangeElement(int, int, int, int);
320 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
321 #define CheckTriggeredElementChange(x, y, e, ev) \
322 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
323 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
324 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
325 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
326 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
327 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
328 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
330 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
331 #define CheckElementChange(x, y, e, te, ev) \
332 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
333 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
334 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
335 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
336 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
338 static void PlayLevelSound(int, int, int);
339 static void PlayLevelSoundNearest(int, int, int);
340 static void PlayLevelSoundAction(int, int, int);
341 static void PlayLevelSoundElementAction(int, int, int, int);
342 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
343 static void PlayLevelSoundActionIfLoop(int, int, int);
344 static void StopLevelSoundActionIfLoop(int, int, int);
345 static void PlayLevelMusic();
347 static void MapGameButtons();
348 static void HandleGameButtons(struct GadgetInfo *);
350 int AmoebeNachbarNr(int, int);
351 void AmoebeUmwandeln(int, int);
352 void ContinueMoving(int, int);
354 void InitMovDir(int, int);
355 void InitAmoebaNr(int, int);
356 int NewHiScore(void);
358 void TestIfGoodThingHitsBadThing(int, int, int);
359 void TestIfBadThingHitsGoodThing(int, int, int);
360 void TestIfPlayerTouchesBadThing(int, int);
361 void TestIfPlayerRunsIntoBadThing(int, int, int);
362 void TestIfBadThingTouchesPlayer(int, int);
363 void TestIfBadThingRunsIntoPlayer(int, int, int);
364 void TestIfFriendTouchesBadThing(int, int);
365 void TestIfBadThingTouchesFriend(int, int);
366 void TestIfBadThingTouchesOtherBadThing(int, int);
368 void KillPlayer(struct PlayerInfo *);
369 void BuryPlayer(struct PlayerInfo *);
370 void RemovePlayer(struct PlayerInfo *);
372 boolean SnapField(struct PlayerInfo *, int, int);
373 boolean DropElement(struct PlayerInfo *);
375 static int getInvisibleActiveFromInvisibleElement(int);
376 static int getInvisibleFromInvisibleActiveElement(int);
378 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
381 /* ------------------------------------------------------------------------- */
382 /* definition of elements that automatically change to other elements after */
383 /* a specified time, eventually calling a function when changing */
384 /* ------------------------------------------------------------------------- */
386 /* forward declaration for changer functions */
387 static void InitBuggyBase(int, int);
388 static void WarnBuggyBase(int, int);
390 static void InitTrap(int, int);
391 static void ActivateTrap(int, int);
392 static void ChangeActiveTrap(int, int);
394 static void InitRobotWheel(int, int);
395 static void RunRobotWheel(int, int);
396 static void StopRobotWheel(int, int);
398 static void InitTimegateWheel(int, int);
399 static void RunTimegateWheel(int, int);
401 static void InitMagicBallDelay(int, int);
402 static void ActivateMagicBall(int, int);
404 struct ChangingElementInfo
409 void (*pre_change_function)(int x, int y);
410 void (*change_function)(int x, int y);
411 void (*post_change_function)(int x, int y);
414 static struct ChangingElementInfo change_delay_list[] =
465 EL_SWITCHGATE_OPENING,
473 EL_SWITCHGATE_CLOSING,
474 EL_SWITCHGATE_CLOSED,
506 EL_ACID_SPLASH_RIGHT,
515 EL_SP_BUGGY_BASE_ACTIVATING,
522 EL_SP_BUGGY_BASE_ACTIVATING,
523 EL_SP_BUGGY_BASE_ACTIVE,
530 EL_SP_BUGGY_BASE_ACTIVE,
554 EL_ROBOT_WHEEL_ACTIVE,
562 EL_TIMEGATE_SWITCH_ACTIVE,
570 EL_EMC_MAGIC_BALL_ACTIVE,
571 EL_EMC_MAGIC_BALL_ACTIVE,
578 EL_EMC_SPRING_BUMPER_ACTIVE,
579 EL_EMC_SPRING_BUMPER,
586 EL_DIAGONAL_SHRINKING,
615 int push_delay_fixed, push_delay_random;
620 { EL_BALLOON, 0, 0 },
622 { EL_SOKOBAN_OBJECT, 2, 0 },
623 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
624 { EL_SATELLITE, 2, 0 },
625 { EL_SP_DISK_YELLOW, 2, 0 },
627 { EL_UNDEFINED, 0, 0 },
635 move_stepsize_list[] =
637 { EL_AMOEBA_DROP, 2 },
638 { EL_AMOEBA_DROPPING, 2 },
639 { EL_QUICKSAND_FILLING, 1 },
640 { EL_QUICKSAND_EMPTYING, 1 },
641 { EL_MAGIC_WALL_FILLING, 2 },
642 { EL_BD_MAGIC_WALL_FILLING, 2 },
643 { EL_MAGIC_WALL_EMPTYING, 2 },
644 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
654 collect_count_list[] =
657 { EL_BD_DIAMOND, 1 },
658 { EL_EMERALD_YELLOW, 1 },
659 { EL_EMERALD_RED, 1 },
660 { EL_EMERALD_PURPLE, 1 },
662 { EL_SP_INFOTRON, 1 },
674 access_direction_list[] =
676 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
677 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
678 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
679 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
680 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
681 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
682 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
683 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
684 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
685 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
686 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
688 { EL_SP_PORT_LEFT, MV_RIGHT },
689 { EL_SP_PORT_RIGHT, MV_LEFT },
690 { EL_SP_PORT_UP, MV_DOWN },
691 { EL_SP_PORT_DOWN, MV_UP },
692 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
693 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
694 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
695 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
696 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
697 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
698 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
699 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
700 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
701 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
702 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
703 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
704 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
705 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
706 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
708 { EL_UNDEFINED, MV_NONE }
711 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
713 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
714 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
715 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
716 IS_JUST_CHANGING(x, y))
718 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
720 /* static variables for playfield scan mode (scanning forward or backward) */
721 static int playfield_scan_start_x = 0;
722 static int playfield_scan_start_y = 0;
723 static int playfield_scan_delta_x = 1;
724 static int playfield_scan_delta_y = 1;
726 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
727 (y) >= 0 && (y) <= lev_fieldy - 1; \
728 (y) += playfield_scan_delta_y) \
729 for ((x) = playfield_scan_start_x; \
730 (x) >= 0 && (x) <= lev_fieldx - 1; \
731 (x) += playfield_scan_delta_x) \
734 void DEBUG_SetMaximumDynamite()
738 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
739 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
740 local_player->inventory_element[local_player->inventory_size++] =
745 static void InitPlayfieldScanModeVars()
747 if (game.use_reverse_scan_direction)
749 playfield_scan_start_x = lev_fieldx - 1;
750 playfield_scan_start_y = lev_fieldy - 1;
752 playfield_scan_delta_x = -1;
753 playfield_scan_delta_y = -1;
757 playfield_scan_start_x = 0;
758 playfield_scan_start_y = 0;
760 playfield_scan_delta_x = 1;
761 playfield_scan_delta_y = 1;
765 static void InitPlayfieldScanMode(int mode)
767 game.use_reverse_scan_direction =
768 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
770 InitPlayfieldScanModeVars();
773 static int get_move_delay_from_stepsize(int move_stepsize)
776 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
778 /* make sure that stepsize value is always a power of 2 */
779 move_stepsize = (1 << log_2(move_stepsize));
781 return TILEX / move_stepsize;
784 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
787 int player_nr = player->index_nr;
788 int move_delay = get_move_delay_from_stepsize(move_stepsize);
789 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
791 /* do no immediately change move delay -- the player might just be moving */
792 player->move_delay_value_next = move_delay;
794 /* information if player can move must be set separately */
795 player->cannot_move = cannot_move;
799 player->move_delay = game.initial_move_delay[player_nr];
800 player->move_delay_value = game.initial_move_delay_value[player_nr];
802 player->move_delay_value_next = -1;
804 player->move_delay_reset_counter = 0;
808 void GetPlayerConfig()
810 if (!audio.sound_available)
811 setup.sound_simple = FALSE;
813 if (!audio.loops_available)
814 setup.sound_loops = FALSE;
816 if (!audio.music_available)
817 setup.sound_music = FALSE;
819 if (!video.fullscreen_available)
820 setup.fullscreen = FALSE;
822 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
824 SetAudioMode(setup.sound);
828 static int getBeltNrFromBeltElement(int element)
830 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
831 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
832 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
835 static int getBeltNrFromBeltActiveElement(int element)
837 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
838 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
839 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
842 static int getBeltNrFromBeltSwitchElement(int element)
844 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
845 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
846 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
849 static int getBeltDirNrFromBeltSwitchElement(int element)
851 static int belt_base_element[4] =
853 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
854 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
855 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
856 EL_CONVEYOR_BELT_4_SWITCH_LEFT
859 int belt_nr = getBeltNrFromBeltSwitchElement(element);
860 int belt_dir_nr = element - belt_base_element[belt_nr];
862 return (belt_dir_nr % 3);
865 static int getBeltDirFromBeltSwitchElement(int element)
867 static int belt_move_dir[3] =
874 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
876 return belt_move_dir[belt_dir_nr];
879 static int get_element_from_group_element(int element)
881 if (IS_GROUP_ELEMENT(element))
883 struct ElementGroupInfo *group = element_info[element].group;
884 int last_anim_random_frame = gfx.anim_random_frame;
887 if (group->choice_mode == ANIM_RANDOM)
888 gfx.anim_random_frame = RND(group->num_elements_resolved);
890 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
891 group->choice_mode, 0,
894 if (group->choice_mode == ANIM_RANDOM)
895 gfx.anim_random_frame = last_anim_random_frame;
899 element = group->element_resolved[element_pos];
905 static void InitPlayerField(int x, int y, int element, boolean init_game)
907 if (element == EL_SP_MURPHY)
911 if (stored_player[0].present)
913 Feld[x][y] = EL_SP_MURPHY_CLONE;
919 stored_player[0].use_murphy = TRUE;
921 if (!level.use_artwork_element[0])
922 stored_player[0].artwork_element = EL_SP_MURPHY;
925 Feld[x][y] = EL_PLAYER_1;
931 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
932 int jx = player->jx, jy = player->jy;
934 player->present = TRUE;
936 player->block_last_field = (element == EL_SP_MURPHY ?
937 level.sp_block_last_field :
938 level.block_last_field);
940 /* ---------- initialize player's last field block delay --------------- */
942 /* always start with reliable default value (no adjustment needed) */
943 player->block_delay_adjustment = 0;
945 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
946 if (player->block_last_field && element == EL_SP_MURPHY)
947 player->block_delay_adjustment = 1;
949 /* special case 2: in game engines before 3.1.1, blocking was different */
950 if (game.use_block_last_field_bug)
951 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
953 if (!options.network || player->connected)
955 player->active = TRUE;
957 /* remove potentially duplicate players */
958 if (StorePlayer[jx][jy] == Feld[x][y])
959 StorePlayer[jx][jy] = 0;
961 StorePlayer[x][y] = Feld[x][y];
965 printf("Player %d activated.\n", player->element_nr);
966 printf("[Local player is %d and currently %s.]\n",
967 local_player->element_nr,
968 local_player->active ? "active" : "not active");
972 Feld[x][y] = EL_EMPTY;
974 player->jx = player->last_jx = x;
975 player->jy = player->last_jy = y;
979 static void InitField(int x, int y, boolean init_game)
981 int element = Feld[x][y];
990 InitPlayerField(x, y, element, init_game);
993 case EL_SOKOBAN_FIELD_PLAYER:
994 element = Feld[x][y] = EL_PLAYER_1;
995 InitField(x, y, init_game);
997 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
998 InitField(x, y, init_game);
1001 case EL_SOKOBAN_FIELD_EMPTY:
1002 local_player->sokobanfields_still_needed++;
1006 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1007 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1008 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1009 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1010 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1011 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1012 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1013 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1014 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1015 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1024 case EL_SPACESHIP_RIGHT:
1025 case EL_SPACESHIP_UP:
1026 case EL_SPACESHIP_LEFT:
1027 case EL_SPACESHIP_DOWN:
1028 case EL_BD_BUTTERFLY:
1029 case EL_BD_BUTTERFLY_RIGHT:
1030 case EL_BD_BUTTERFLY_UP:
1031 case EL_BD_BUTTERFLY_LEFT:
1032 case EL_BD_BUTTERFLY_DOWN:
1034 case EL_BD_FIREFLY_RIGHT:
1035 case EL_BD_FIREFLY_UP:
1036 case EL_BD_FIREFLY_LEFT:
1037 case EL_BD_FIREFLY_DOWN:
1038 case EL_PACMAN_RIGHT:
1040 case EL_PACMAN_LEFT:
1041 case EL_PACMAN_DOWN:
1043 case EL_YAMYAM_LEFT:
1044 case EL_YAMYAM_RIGHT:
1046 case EL_YAMYAM_DOWN:
1047 case EL_DARK_YAMYAM:
1050 case EL_SP_SNIKSNAK:
1051 case EL_SP_ELECTRON:
1060 case EL_AMOEBA_FULL:
1065 case EL_AMOEBA_DROP:
1066 if (y == lev_fieldy - 1)
1068 Feld[x][y] = EL_AMOEBA_GROWING;
1069 Store[x][y] = EL_AMOEBA_WET;
1073 case EL_DYNAMITE_ACTIVE:
1074 case EL_SP_DISK_RED_ACTIVE:
1075 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1076 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1077 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1078 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1079 MovDelay[x][y] = 96;
1082 case EL_EM_DYNAMITE_ACTIVE:
1083 MovDelay[x][y] = 32;
1087 local_player->lights_still_needed++;
1091 local_player->friends_still_needed++;
1096 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1099 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1100 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1101 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1102 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1103 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1104 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1105 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1106 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1107 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1108 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1109 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1110 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1113 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1114 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1115 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1117 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1119 game.belt_dir[belt_nr] = belt_dir;
1120 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1122 else /* more than one switch -- set it like the first switch */
1124 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1129 #if !USE_BOTH_SWITCHGATE_SWITCHES
1130 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1132 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1136 case EL_LIGHT_SWITCH_ACTIVE:
1138 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1141 case EL_INVISIBLE_STEELWALL:
1142 case EL_INVISIBLE_WALL:
1143 case EL_INVISIBLE_SAND:
1144 if (game.light_time_left > 0 ||
1145 game.lenses_time_left > 0)
1146 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1149 case EL_EMC_MAGIC_BALL:
1150 if (game.ball_state)
1151 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1154 case EL_EMC_MAGIC_BALL_SWITCH:
1155 if (game.ball_state)
1156 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1160 if (IS_CUSTOM_ELEMENT(element))
1162 if (CAN_MOVE(element))
1165 #if USE_NEW_CUSTOM_VALUE
1166 if (!element_info[element].use_last_ce_value || init_game)
1167 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1170 else if (IS_GROUP_ELEMENT(element))
1172 Feld[x][y] = get_element_from_group_element(element);
1174 InitField(x, y, init_game);
1181 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1184 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1186 InitField(x, y, init_game);
1188 /* not needed to call InitMovDir() -- already done by InitField()! */
1189 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1190 CAN_MOVE(Feld[x][y]))
1194 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1196 int old_element = Feld[x][y];
1198 InitField(x, y, init_game);
1200 /* not needed to call InitMovDir() -- already done by InitField()! */
1201 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1202 CAN_MOVE(old_element) &&
1203 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1206 /* this case is in fact a combination of not less than three bugs:
1207 first, it calls InitMovDir() for elements that can move, although this is
1208 already done by InitField(); then, it checks the element that was at this
1209 field _before_ the call to InitField() (which can change it); lastly, it
1210 was not called for "mole with direction" elements, which were treated as
1211 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1215 inline void DrawGameValue_Emeralds(int value)
1217 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1219 if (PANEL_DEACTIVATED(game.panel.gems))
1222 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1225 inline void DrawGameValue_Dynamite(int value)
1227 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1229 if (PANEL_DEACTIVATED(game.panel.inventory))
1232 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1235 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1237 int base_key_graphic = EL_KEY_1;
1240 if (PANEL_DEACTIVATED(game.panel.keys))
1243 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1244 base_key_graphic = EL_EM_KEY_1;
1246 /* currently only 4 of 8 possible keys are displayed */
1247 for (i = 0; i < STD_NUM_KEYS; i++)
1249 int x = XX_KEYS + i * MINI_TILEX;
1253 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1255 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1256 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1260 inline void DrawGameValue_Score(int value)
1262 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1264 if (PANEL_DEACTIVATED(game.panel.score))
1267 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1270 inline void DrawGameValue_Time(int value)
1272 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1273 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1275 if (PANEL_DEACTIVATED(game.panel.time))
1278 /* clear background if value just changed its size */
1279 if (value == 999 || value == 1000)
1280 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1283 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1285 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1288 inline void DrawGameValue_Level(int value)
1290 if (PANEL_DEACTIVATED(game.panel.level))
1294 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1296 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
1299 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1302 int key[MAX_NUM_KEYS];
1305 /* prevent EM engine from updating time/score values parallel to GameWon() */
1306 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1307 local_player->LevelSolved)
1310 for (i = 0; i < MAX_NUM_KEYS; i++)
1311 key[i] = key_bits & (1 << i);
1313 DrawGameValue_Level(level_nr);
1315 DrawGameValue_Emeralds(emeralds);
1316 DrawGameValue_Dynamite(dynamite);
1317 DrawGameValue_Score(score);
1318 DrawGameValue_Time(time);
1320 DrawGameValue_Keys(key);
1323 void DrawGameDoorValues()
1325 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1326 int dynamite_value = 0;
1327 int score_value = (local_player->LevelSolved ? local_player->score_final :
1328 local_player->score);
1329 int gems_value = local_player->gems_still_needed;
1333 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1335 DrawGameDoorValues_EM();
1340 if (game.centered_player_nr == -1)
1342 for (i = 0; i < MAX_PLAYERS; i++)
1344 for (j = 0; j < MAX_NUM_KEYS; j++)
1345 if (stored_player[i].key[j])
1346 key_bits |= (1 << j);
1348 dynamite_value += stored_player[i].inventory_size;
1353 int player_nr = game.centered_player_nr;
1355 for (i = 0; i < MAX_NUM_KEYS; i++)
1356 if (stored_player[player_nr].key[i])
1357 key_bits |= (1 << i);
1359 dynamite_value = stored_player[player_nr].inventory_size;
1362 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1368 =============================================================================
1370 -----------------------------------------------------------------------------
1371 initialize game engine due to level / tape version number
1372 =============================================================================
1375 static void InitGameEngine()
1377 int i, j, k, l, x, y;
1379 /* set game engine from tape file when re-playing, else from level file */
1380 game.engine_version = (tape.playing ? tape.engine_version :
1381 level.game_version);
1383 /* ---------------------------------------------------------------------- */
1384 /* set flags for bugs and changes according to active game engine version */
1385 /* ---------------------------------------------------------------------- */
1388 Summary of bugfix/change:
1389 Fixed handling for custom elements that change when pushed by the player.
1391 Fixed/changed in version:
1395 Before 3.1.0, custom elements that "change when pushing" changed directly
1396 after the player started pushing them (until then handled in "DigField()").
1397 Since 3.1.0, these custom elements are not changed until the "pushing"
1398 move of the element is finished (now handled in "ContinueMoving()").
1400 Affected levels/tapes:
1401 The first condition is generally needed for all levels/tapes before version
1402 3.1.0, which might use the old behaviour before it was changed; known tapes
1403 that are affected are some tapes from the level set "Walpurgis Gardens" by
1405 The second condition is an exception from the above case and is needed for
1406 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1407 above (including some development versions of 3.1.0), but before it was
1408 known that this change would break tapes like the above and was fixed in
1409 3.1.1, so that the changed behaviour was active although the engine version
1410 while recording maybe was before 3.1.0. There is at least one tape that is
1411 affected by this exception, which is the tape for the one-level set "Bug
1412 Machine" by Juergen Bonhagen.
1415 game.use_change_when_pushing_bug =
1416 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1418 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1419 tape.game_version < VERSION_IDENT(3,1,1,0)));
1422 Summary of bugfix/change:
1423 Fixed handling for blocking the field the player leaves when moving.
1425 Fixed/changed in version:
1429 Before 3.1.1, when "block last field when moving" was enabled, the field
1430 the player is leaving when moving was blocked for the time of the move,
1431 and was directly unblocked afterwards. This resulted in the last field
1432 being blocked for exactly one less than the number of frames of one player
1433 move. Additionally, even when blocking was disabled, the last field was
1434 blocked for exactly one frame.
1435 Since 3.1.1, due to changes in player movement handling, the last field
1436 is not blocked at all when blocking is disabled. When blocking is enabled,
1437 the last field is blocked for exactly the number of frames of one player
1438 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1439 last field is blocked for exactly one more than the number of frames of
1442 Affected levels/tapes:
1443 (!!! yet to be determined -- probably many !!!)
1446 game.use_block_last_field_bug =
1447 (game.engine_version < VERSION_IDENT(3,1,1,0));
1450 Summary of bugfix/change:
1451 Changed behaviour of CE changes with multiple changes per single frame.
1453 Fixed/changed in version:
1457 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1458 This resulted in race conditions where CEs seem to behave strange in some
1459 situations (where triggered CE changes were just skipped because there was
1460 already a CE change on that tile in the playfield in that engine frame).
1461 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1462 (The number of changes per frame must be limited in any case, because else
1463 it is easily possible to define CE changes that would result in an infinite
1464 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1465 should be set large enough so that it would only be reached in cases where
1466 the corresponding CE change conditions run into a loop. Therefore, it seems
1467 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1468 maximal number of change pages for custom elements.)
1470 Affected levels/tapes:
1474 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1475 game.max_num_changes_per_frame = 1;
1477 game.max_num_changes_per_frame =
1478 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1481 /* ---------------------------------------------------------------------- */
1483 /* default scan direction: scan playfield from top/left to bottom/right */
1484 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1486 /* dynamically adjust element properties according to game engine version */
1487 InitElementPropertiesEngine(game.engine_version);
1490 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1491 printf(" tape version == %06d [%s] [file: %06d]\n",
1492 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1494 printf(" => game.engine_version == %06d\n", game.engine_version);
1497 /* ---------- initialize player's initial move delay --------------------- */
1499 /* dynamically adjust player properties according to level information */
1500 for (i = 0; i < MAX_PLAYERS; i++)
1501 game.initial_move_delay_value[i] =
1502 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1504 /* dynamically adjust player properties according to game engine version */
1505 for (i = 0; i < MAX_PLAYERS; i++)
1506 game.initial_move_delay[i] =
1507 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1508 game.initial_move_delay_value[i] : 0);
1510 /* ---------- initialize player's initial push delay --------------------- */
1512 /* dynamically adjust player properties according to game engine version */
1513 game.initial_push_delay_value =
1514 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1516 /* ---------- initialize changing elements ------------------------------- */
1518 /* initialize changing elements information */
1519 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1521 struct ElementInfo *ei = &element_info[i];
1523 /* this pointer might have been changed in the level editor */
1524 ei->change = &ei->change_page[0];
1526 if (!IS_CUSTOM_ELEMENT(i))
1528 ei->change->target_element = EL_EMPTY_SPACE;
1529 ei->change->delay_fixed = 0;
1530 ei->change->delay_random = 0;
1531 ei->change->delay_frames = 1;
1534 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1536 ei->has_change_event[j] = FALSE;
1538 ei->event_page_nr[j] = 0;
1539 ei->event_page[j] = &ei->change_page[0];
1543 /* add changing elements from pre-defined list */
1544 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1546 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1547 struct ElementInfo *ei = &element_info[ch_delay->element];
1549 ei->change->target_element = ch_delay->target_element;
1550 ei->change->delay_fixed = ch_delay->change_delay;
1552 ei->change->pre_change_function = ch_delay->pre_change_function;
1553 ei->change->change_function = ch_delay->change_function;
1554 ei->change->post_change_function = ch_delay->post_change_function;
1556 ei->change->can_change = TRUE;
1557 ei->change->can_change_or_has_action = TRUE;
1559 ei->has_change_event[CE_DELAY] = TRUE;
1561 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1562 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1565 /* ---------- initialize internal run-time variables ------------- */
1567 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1569 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1571 for (j = 0; j < ei->num_change_pages; j++)
1573 ei->change_page[j].can_change_or_has_action =
1574 (ei->change_page[j].can_change |
1575 ei->change_page[j].has_action);
1579 /* add change events from custom element configuration */
1580 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1582 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1584 for (j = 0; j < ei->num_change_pages; j++)
1586 if (!ei->change_page[j].can_change_or_has_action)
1589 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1591 /* only add event page for the first page found with this event */
1592 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1594 ei->has_change_event[k] = TRUE;
1596 ei->event_page_nr[k] = j;
1597 ei->event_page[k] = &ei->change_page[j];
1603 /* ---------- initialize run-time trigger player and element ------------- */
1605 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1607 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1609 for (j = 0; j < ei->num_change_pages; j++)
1611 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1612 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1613 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1614 ei->change_page[j].actual_trigger_ce_value = 0;
1615 ei->change_page[j].actual_trigger_ce_score = 0;
1619 /* ---------- initialize trigger events ---------------------------------- */
1621 /* initialize trigger events information */
1622 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1623 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1624 trigger_events[i][j] = FALSE;
1626 /* add trigger events from element change event properties */
1627 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1629 struct ElementInfo *ei = &element_info[i];
1631 for (j = 0; j < ei->num_change_pages; j++)
1633 if (!ei->change_page[j].can_change_or_has_action)
1636 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1638 int trigger_element = ei->change_page[j].trigger_element;
1640 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1642 if (ei->change_page[j].has_event[k])
1644 if (IS_GROUP_ELEMENT(trigger_element))
1646 struct ElementGroupInfo *group =
1647 element_info[trigger_element].group;
1649 for (l = 0; l < group->num_elements_resolved; l++)
1650 trigger_events[group->element_resolved[l]][k] = TRUE;
1652 else if (trigger_element == EL_ANY_ELEMENT)
1653 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1654 trigger_events[l][k] = TRUE;
1656 trigger_events[trigger_element][k] = TRUE;
1663 /* ---------- initialize push delay -------------------------------------- */
1665 /* initialize push delay values to default */
1666 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1668 if (!IS_CUSTOM_ELEMENT(i))
1670 /* set default push delay values (corrected since version 3.0.7-1) */
1671 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1673 element_info[i].push_delay_fixed = 2;
1674 element_info[i].push_delay_random = 8;
1678 element_info[i].push_delay_fixed = 8;
1679 element_info[i].push_delay_random = 8;
1684 /* set push delay value for certain elements from pre-defined list */
1685 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1687 int e = push_delay_list[i].element;
1689 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1690 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1693 /* set push delay value for Supaplex elements for newer engine versions */
1694 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1696 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1698 if (IS_SP_ELEMENT(i))
1700 /* set SP push delay to just enough to push under a falling zonk */
1701 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1703 element_info[i].push_delay_fixed = delay;
1704 element_info[i].push_delay_random = 0;
1709 /* ---------- initialize move stepsize ----------------------------------- */
1711 /* initialize move stepsize values to default */
1712 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1713 if (!IS_CUSTOM_ELEMENT(i))
1714 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1716 /* set move stepsize value for certain elements from pre-defined list */
1717 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1719 int e = move_stepsize_list[i].element;
1721 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1724 /* ---------- initialize collect score ----------------------------------- */
1726 /* initialize collect score values for custom elements from initial value */
1727 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1728 if (IS_CUSTOM_ELEMENT(i))
1729 element_info[i].collect_score = element_info[i].collect_score_initial;
1731 /* ---------- initialize collect count ----------------------------------- */
1733 /* initialize collect count values for non-custom elements */
1734 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1735 if (!IS_CUSTOM_ELEMENT(i))
1736 element_info[i].collect_count_initial = 0;
1738 /* add collect count values for all elements from pre-defined list */
1739 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1740 element_info[collect_count_list[i].element].collect_count_initial =
1741 collect_count_list[i].count;
1743 /* ---------- initialize access direction -------------------------------- */
1745 /* initialize access direction values to default (access from every side) */
1746 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1747 if (!IS_CUSTOM_ELEMENT(i))
1748 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1750 /* set access direction value for certain elements from pre-defined list */
1751 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1752 element_info[access_direction_list[i].element].access_direction =
1753 access_direction_list[i].direction;
1755 /* ---------- initialize explosion content ------------------------------- */
1756 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1758 if (IS_CUSTOM_ELEMENT(i))
1761 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1763 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1765 element_info[i].content.e[x][y] =
1766 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1767 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1768 i == EL_PLAYER_3 ? EL_EMERALD :
1769 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1770 i == EL_MOLE ? EL_EMERALD_RED :
1771 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1772 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1773 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1774 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1775 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1776 i == EL_WALL_EMERALD ? EL_EMERALD :
1777 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1778 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1779 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1780 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1781 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1782 i == EL_WALL_PEARL ? EL_PEARL :
1783 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1789 int get_num_special_action(int element, int action_first, int action_last)
1791 int num_special_action = 0;
1794 for (i = action_first; i <= action_last; i++)
1796 boolean found = FALSE;
1798 for (j = 0; j < NUM_DIRECTIONS; j++)
1799 if (el_act_dir2img(element, i, j) !=
1800 el_act_dir2img(element, ACTION_DEFAULT, j))
1804 num_special_action++;
1809 return num_special_action;
1814 =============================================================================
1816 -----------------------------------------------------------------------------
1817 initialize and start new game
1818 =============================================================================
1823 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1824 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1825 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1826 boolean do_fading = (game_status == GAME_MODE_MAIN);
1829 game_status = GAME_MODE_PLAYING;
1833 /* don't play tapes over network */
1834 network_playing = (options.network && !tape.playing);
1836 for (i = 0; i < MAX_PLAYERS; i++)
1838 struct PlayerInfo *player = &stored_player[i];
1840 player->index_nr = i;
1841 player->index_bit = (1 << i);
1842 player->element_nr = EL_PLAYER_1 + i;
1844 player->present = FALSE;
1845 player->active = FALSE;
1848 player->effective_action = 0;
1849 player->programmed_action = 0;
1852 player->score_final = 0;
1854 player->gems_still_needed = level.gems_needed;
1855 player->sokobanfields_still_needed = 0;
1856 player->lights_still_needed = 0;
1857 player->friends_still_needed = 0;
1859 for (j = 0; j < MAX_NUM_KEYS; j++)
1860 player->key[j] = FALSE;
1862 player->dynabomb_count = 0;
1863 player->dynabomb_size = 1;
1864 player->dynabombs_left = 0;
1865 player->dynabomb_xl = FALSE;
1867 player->MovDir = MV_NONE;
1870 player->GfxDir = MV_NONE;
1871 player->GfxAction = ACTION_DEFAULT;
1873 player->StepFrame = 0;
1875 player->use_murphy = FALSE;
1876 player->artwork_element =
1877 (level.use_artwork_element[i] ? level.artwork_element[i] :
1878 player->element_nr);
1880 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1881 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1883 player->gravity = level.initial_player_gravity[i];
1885 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1887 player->actual_frame_counter = 0;
1889 player->step_counter = 0;
1891 player->last_move_dir = MV_NONE;
1893 player->is_active = FALSE;
1895 player->is_waiting = FALSE;
1896 player->is_moving = FALSE;
1897 player->is_auto_moving = FALSE;
1898 player->is_digging = FALSE;
1899 player->is_snapping = FALSE;
1900 player->is_collecting = FALSE;
1901 player->is_pushing = FALSE;
1902 player->is_switching = FALSE;
1903 player->is_dropping = FALSE;
1904 player->is_dropping_pressed = FALSE;
1906 player->is_bored = FALSE;
1907 player->is_sleeping = FALSE;
1909 player->frame_counter_bored = -1;
1910 player->frame_counter_sleeping = -1;
1912 player->anim_delay_counter = 0;
1913 player->post_delay_counter = 0;
1915 player->dir_waiting = MV_NONE;
1916 player->action_waiting = ACTION_DEFAULT;
1917 player->last_action_waiting = ACTION_DEFAULT;
1918 player->special_action_bored = ACTION_DEFAULT;
1919 player->special_action_sleeping = ACTION_DEFAULT;
1921 player->switch_x = -1;
1922 player->switch_y = -1;
1924 player->drop_x = -1;
1925 player->drop_y = -1;
1927 player->show_envelope = 0;
1929 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
1931 player->push_delay = -1; /* initialized when pushing starts */
1932 player->push_delay_value = game.initial_push_delay_value;
1934 player->drop_delay = 0;
1935 player->drop_pressed_delay = 0;
1937 player->last_jx = -1;
1938 player->last_jy = -1;
1942 player->shield_normal_time_left = 0;
1943 player->shield_deadly_time_left = 0;
1945 player->inventory_infinite_element = EL_UNDEFINED;
1946 player->inventory_size = 0;
1948 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1949 SnapField(player, 0, 0);
1951 player->LevelSolved = FALSE;
1952 player->GameOver = FALSE;
1954 player->LevelSolved_GameEnd = FALSE;
1955 player->LevelSolved_SaveTape = FALSE;
1956 player->LevelSolved_SaveScore = FALSE;
1959 network_player_action_received = FALSE;
1961 #if defined(NETWORK_AVALIABLE)
1962 /* initial null action */
1963 if (network_playing)
1964 SendToServer_MovePlayer(MV_NONE);
1973 TimeLeft = level.time;
1976 ScreenMovDir = MV_NONE;
1980 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1982 AllPlayersGone = FALSE;
1984 game.yamyam_content_nr = 0;
1985 game.magic_wall_active = FALSE;
1986 game.magic_wall_time_left = 0;
1987 game.light_time_left = 0;
1988 game.timegate_time_left = 0;
1989 game.switchgate_pos = 0;
1990 game.wind_direction = level.wind_direction_initial;
1992 #if !USE_PLAYER_GRAVITY
1993 game.gravity = FALSE;
1994 game.explosions_delayed = TRUE;
1997 game.lenses_time_left = 0;
1998 game.magnify_time_left = 0;
2000 game.ball_state = level.ball_state_initial;
2001 game.ball_content_nr = 0;
2003 game.envelope_active = FALSE;
2005 /* set focus to local player for network games, else to all players */
2006 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2007 game.centered_player_nr_next = game.centered_player_nr;
2008 game.set_centered_player = FALSE;
2010 if (network_playing && tape.recording)
2012 /* store client dependent player focus when recording network games */
2013 tape.centered_player_nr_next = game.centered_player_nr_next;
2014 tape.set_centered_player = TRUE;
2017 for (i = 0; i < NUM_BELTS; i++)
2019 game.belt_dir[i] = MV_NONE;
2020 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2023 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2024 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2026 SCAN_PLAYFIELD(x, y)
2028 Feld[x][y] = level.field[x][y];
2029 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2030 ChangeDelay[x][y] = 0;
2031 ChangePage[x][y] = -1;
2032 #if USE_NEW_CUSTOM_VALUE
2033 CustomValue[x][y] = 0; /* initialized in InitField() */
2035 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2037 WasJustMoving[x][y] = 0;
2038 WasJustFalling[x][y] = 0;
2039 CheckCollision[x][y] = 0;
2041 Pushed[x][y] = FALSE;
2043 ChangeCount[x][y] = 0;
2044 ChangeEvent[x][y] = -1;
2046 ExplodePhase[x][y] = 0;
2047 ExplodeDelay[x][y] = 0;
2048 ExplodeField[x][y] = EX_TYPE_NONE;
2050 RunnerVisit[x][y] = 0;
2051 PlayerVisit[x][y] = 0;
2054 GfxRandom[x][y] = INIT_GFX_RANDOM();
2055 GfxElement[x][y] = EL_UNDEFINED;
2056 GfxAction[x][y] = ACTION_DEFAULT;
2057 GfxDir[x][y] = MV_NONE;
2060 SCAN_PLAYFIELD(x, y)
2062 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2064 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2066 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2069 InitField(x, y, TRUE);
2074 for (i = 0; i < MAX_PLAYERS; i++)
2076 struct PlayerInfo *player = &stored_player[i];
2078 /* set number of special actions for bored and sleeping animation */
2079 player->num_special_action_bored =
2080 get_num_special_action(player->artwork_element,
2081 ACTION_BORING_1, ACTION_BORING_LAST);
2082 player->num_special_action_sleeping =
2083 get_num_special_action(player->artwork_element,
2084 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2087 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2088 emulate_sb ? EMU_SOKOBAN :
2089 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2091 #if USE_NEW_ALL_SLIPPERY
2092 /* initialize type of slippery elements */
2093 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2095 if (!IS_CUSTOM_ELEMENT(i))
2097 /* default: elements slip down either to the left or right randomly */
2098 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2100 /* SP style elements prefer to slip down on the left side */
2101 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2102 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2104 /* BD style elements prefer to slip down on the left side */
2105 if (game.emulation == EMU_BOULDERDASH)
2106 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2111 /* initialize explosion and ignition delay */
2112 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2114 if (!IS_CUSTOM_ELEMENT(i))
2117 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2118 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2119 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2120 int last_phase = (num_phase + 1) * delay;
2121 int half_phase = (num_phase / 2) * delay;
2123 element_info[i].explosion_delay = last_phase - 1;
2124 element_info[i].ignition_delay = half_phase;
2126 if (i == EL_BLACK_ORB)
2127 element_info[i].ignition_delay = 1;
2131 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2132 element_info[i].explosion_delay = 1;
2134 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2135 element_info[i].ignition_delay = 1;
2139 /* correct non-moving belts to start moving left */
2140 for (i = 0; i < NUM_BELTS; i++)
2141 if (game.belt_dir[i] == MV_NONE)
2142 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2144 /* check if any connected player was not found in playfield */
2145 for (i = 0; i < MAX_PLAYERS; i++)
2147 struct PlayerInfo *player = &stored_player[i];
2149 if (player->connected && !player->present)
2151 for (j = 0; j < MAX_PLAYERS; j++)
2153 struct PlayerInfo *some_player = &stored_player[j];
2154 int jx = some_player->jx, jy = some_player->jy;
2156 /* assign first free player found that is present in the playfield */
2157 if (some_player->present && !some_player->connected)
2159 player->present = TRUE;
2160 player->active = TRUE;
2162 some_player->present = FALSE;
2163 some_player->active = FALSE;
2165 player->artwork_element = some_player->artwork_element;
2167 player->block_last_field = some_player->block_last_field;
2168 player->block_delay_adjustment = some_player->block_delay_adjustment;
2170 StorePlayer[jx][jy] = player->element_nr;
2171 player->jx = player->last_jx = jx;
2172 player->jy = player->last_jy = jy;
2182 /* when playing a tape, eliminate all players who do not participate */
2184 for (i = 0; i < MAX_PLAYERS; i++)
2186 if (stored_player[i].active && !tape.player_participates[i])
2188 struct PlayerInfo *player = &stored_player[i];
2189 int jx = player->jx, jy = player->jy;
2191 player->active = FALSE;
2192 StorePlayer[jx][jy] = 0;
2193 Feld[jx][jy] = EL_EMPTY;
2197 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2199 /* when in single player mode, eliminate all but the first active player */
2201 for (i = 0; i < MAX_PLAYERS; i++)
2203 if (stored_player[i].active)
2205 for (j = i + 1; j < MAX_PLAYERS; j++)
2207 if (stored_player[j].active)
2209 struct PlayerInfo *player = &stored_player[j];
2210 int jx = player->jx, jy = player->jy;
2212 player->active = FALSE;
2213 player->present = FALSE;
2215 StorePlayer[jx][jy] = 0;
2216 Feld[jx][jy] = EL_EMPTY;
2223 /* when recording the game, store which players take part in the game */
2226 for (i = 0; i < MAX_PLAYERS; i++)
2227 if (stored_player[i].active)
2228 tape.player_participates[i] = TRUE;
2233 for (i = 0; i < MAX_PLAYERS; i++)
2235 struct PlayerInfo *player = &stored_player[i];
2237 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2242 if (local_player == player)
2243 printf("Player %d is local player.\n", i+1);
2247 if (BorderElement == EL_EMPTY)
2250 SBX_Right = lev_fieldx - SCR_FIELDX;
2252 SBY_Lower = lev_fieldy - SCR_FIELDY;
2257 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2259 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2262 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2263 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2265 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2266 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2268 /* if local player not found, look for custom element that might create
2269 the player (make some assumptions about the right custom element) */
2270 if (!local_player->present)
2272 int start_x = 0, start_y = 0;
2273 int found_rating = 0;
2274 int found_element = EL_UNDEFINED;
2275 int player_nr = local_player->index_nr;
2277 SCAN_PLAYFIELD(x, y)
2279 int element = Feld[x][y];
2284 if (level.use_start_element[player_nr] &&
2285 level.start_element[player_nr] == element &&
2292 found_element = element;
2295 if (!IS_CUSTOM_ELEMENT(element))
2298 if (CAN_CHANGE(element))
2300 for (i = 0; i < element_info[element].num_change_pages; i++)
2302 /* check for player created from custom element as single target */
2303 content = element_info[element].change_page[i].target_element;
2304 is_player = ELEM_IS_PLAYER(content);
2306 if (is_player && (found_rating < 3 || element < found_element))
2312 found_element = element;
2317 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2319 /* check for player created from custom element as explosion content */
2320 content = element_info[element].content.e[xx][yy];
2321 is_player = ELEM_IS_PLAYER(content);
2323 if (is_player && (found_rating < 2 || element < found_element))
2325 start_x = x + xx - 1;
2326 start_y = y + yy - 1;
2329 found_element = element;
2332 if (!CAN_CHANGE(element))
2335 for (i = 0; i < element_info[element].num_change_pages; i++)
2337 /* check for player created from custom element as extended target */
2339 element_info[element].change_page[i].target_content.e[xx][yy];
2341 is_player = ELEM_IS_PLAYER(content);
2343 if (is_player && (found_rating < 1 || element < found_element))
2345 start_x = x + xx - 1;
2346 start_y = y + yy - 1;
2349 found_element = element;
2355 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2356 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2359 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2360 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2365 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2366 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2367 local_player->jx - MIDPOSX);
2369 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2370 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2371 local_player->jy - MIDPOSY);
2376 if (!game.restart_level)
2377 CloseDoor(DOOR_CLOSE_1);
2380 FadeOut(REDRAW_FIELD);
2382 /* !!! FIX THIS (START) !!! */
2383 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2385 InitGameEngine_EM();
2387 /* blit playfield from scroll buffer to normal back buffer for fading in */
2388 BlitScreenToBitmap_EM(backbuffer);
2395 /* after drawing the level, correct some elements */
2396 if (game.timegate_time_left == 0)
2397 CloseAllOpenTimegates();
2399 /* blit playfield from scroll buffer to normal back buffer for fading in */
2400 if (setup.soft_scrolling)
2401 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2403 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2405 /* !!! FIX THIS (END) !!! */
2408 FadeIn(REDRAW_FIELD);
2412 if (!game.restart_level)
2414 /* copy default game door content to main double buffer */
2415 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2416 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2419 SetPanelBackground();
2420 SetDrawBackgroundMask(REDRAW_DOOR_1);
2422 DrawGameDoorValues();
2424 if (!game.restart_level)
2428 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2429 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2430 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2434 /* copy actual game door content to door double buffer for OpenDoor() */
2435 BlitBitmap(drawto, bitmap_db_door,
2436 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2438 OpenDoor(DOOR_OPEN_ALL);
2440 PlaySound(SND_GAME_STARTING);
2442 if (setup.sound_music)
2445 KeyboardAutoRepeatOffUnlessAutoplay();
2449 for (i = 0; i < MAX_PLAYERS; i++)
2450 printf("Player %d %sactive.\n",
2451 i + 1, (stored_player[i].active ? "" : "not "));
2462 game.restart_level = FALSE;
2465 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2467 /* this is used for non-R'n'D game engines to update certain engine values */
2469 /* needed to determine if sounds are played within the visible screen area */
2470 scroll_x = actual_scroll_x;
2471 scroll_y = actual_scroll_y;
2474 void InitMovDir(int x, int y)
2476 int i, element = Feld[x][y];
2477 static int xy[4][2] =
2484 static int direction[3][4] =
2486 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2487 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2488 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2497 Feld[x][y] = EL_BUG;
2498 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2501 case EL_SPACESHIP_RIGHT:
2502 case EL_SPACESHIP_UP:
2503 case EL_SPACESHIP_LEFT:
2504 case EL_SPACESHIP_DOWN:
2505 Feld[x][y] = EL_SPACESHIP;
2506 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2509 case EL_BD_BUTTERFLY_RIGHT:
2510 case EL_BD_BUTTERFLY_UP:
2511 case EL_BD_BUTTERFLY_LEFT:
2512 case EL_BD_BUTTERFLY_DOWN:
2513 Feld[x][y] = EL_BD_BUTTERFLY;
2514 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2517 case EL_BD_FIREFLY_RIGHT:
2518 case EL_BD_FIREFLY_UP:
2519 case EL_BD_FIREFLY_LEFT:
2520 case EL_BD_FIREFLY_DOWN:
2521 Feld[x][y] = EL_BD_FIREFLY;
2522 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2525 case EL_PACMAN_RIGHT:
2527 case EL_PACMAN_LEFT:
2528 case EL_PACMAN_DOWN:
2529 Feld[x][y] = EL_PACMAN;
2530 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2533 case EL_YAMYAM_LEFT:
2534 case EL_YAMYAM_RIGHT:
2536 case EL_YAMYAM_DOWN:
2537 Feld[x][y] = EL_YAMYAM;
2538 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2541 case EL_SP_SNIKSNAK:
2542 MovDir[x][y] = MV_UP;
2545 case EL_SP_ELECTRON:
2546 MovDir[x][y] = MV_LEFT;
2553 Feld[x][y] = EL_MOLE;
2554 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2558 if (IS_CUSTOM_ELEMENT(element))
2560 struct ElementInfo *ei = &element_info[element];
2561 int move_direction_initial = ei->move_direction_initial;
2562 int move_pattern = ei->move_pattern;
2564 if (move_direction_initial == MV_START_PREVIOUS)
2566 if (MovDir[x][y] != MV_NONE)
2569 move_direction_initial = MV_START_AUTOMATIC;
2572 if (move_direction_initial == MV_START_RANDOM)
2573 MovDir[x][y] = 1 << RND(4);
2574 else if (move_direction_initial & MV_ANY_DIRECTION)
2575 MovDir[x][y] = move_direction_initial;
2576 else if (move_pattern == MV_ALL_DIRECTIONS ||
2577 move_pattern == MV_TURNING_LEFT ||
2578 move_pattern == MV_TURNING_RIGHT ||
2579 move_pattern == MV_TURNING_LEFT_RIGHT ||
2580 move_pattern == MV_TURNING_RIGHT_LEFT ||
2581 move_pattern == MV_TURNING_RANDOM)
2582 MovDir[x][y] = 1 << RND(4);
2583 else if (move_pattern == MV_HORIZONTAL)
2584 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2585 else if (move_pattern == MV_VERTICAL)
2586 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2587 else if (move_pattern & MV_ANY_DIRECTION)
2588 MovDir[x][y] = element_info[element].move_pattern;
2589 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2590 move_pattern == MV_ALONG_RIGHT_SIDE)
2592 /* use random direction as default start direction */
2593 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2594 MovDir[x][y] = 1 << RND(4);
2596 for (i = 0; i < NUM_DIRECTIONS; i++)
2598 int x1 = x + xy[i][0];
2599 int y1 = y + xy[i][1];
2601 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2603 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2604 MovDir[x][y] = direction[0][i];
2606 MovDir[x][y] = direction[1][i];
2615 MovDir[x][y] = 1 << RND(4);
2617 if (element != EL_BUG &&
2618 element != EL_SPACESHIP &&
2619 element != EL_BD_BUTTERFLY &&
2620 element != EL_BD_FIREFLY)
2623 for (i = 0; i < NUM_DIRECTIONS; i++)
2625 int x1 = x + xy[i][0];
2626 int y1 = y + xy[i][1];
2628 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2630 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2632 MovDir[x][y] = direction[0][i];
2635 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2636 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2638 MovDir[x][y] = direction[1][i];
2647 GfxDir[x][y] = MovDir[x][y];
2650 void InitAmoebaNr(int x, int y)
2653 int group_nr = AmoebeNachbarNr(x, y);
2657 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2659 if (AmoebaCnt[i] == 0)
2667 AmoebaNr[x][y] = group_nr;
2668 AmoebaCnt[group_nr]++;
2669 AmoebaCnt2[group_nr]++;
2672 static void PlayerWins(struct PlayerInfo *player)
2674 player->LevelSolved = TRUE;
2675 player->GameOver = TRUE;
2677 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2678 level.native_em_level->lev->score : player->score);
2683 static int time, time_final;
2684 static int score, score_final;
2685 static int game_over_delay = 0;
2686 int game_over_delay_value = 50;
2688 if (!local_player->LevelSolved_GameEnd)
2692 /* do not start end game actions before the player stops moving (to exit) */
2693 if (local_player->MovPos)
2696 local_player->LevelSolved_GameEnd = TRUE;
2697 local_player->LevelSolved_SaveTape = tape.recording;
2698 local_player->LevelSolved_SaveScore = !tape.playing;
2700 if (tape.auto_play) /* tape might already be stopped here */
2701 tape.auto_play_level_solved = TRUE;
2707 game_over_delay = game_over_delay_value;
2709 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
2710 score = score_final = local_player->score_final;
2715 score_final += TimeLeft * level.score[SC_TIME_BONUS];
2717 else if (level.time == 0 && TimePlayed < 999)
2720 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
2723 local_player->score_final = score_final;
2725 if (level_editor_test_game)
2728 score = score_final;
2730 DrawGameValue_Time(time);
2731 DrawGameValue_Score(score);
2734 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
2736 /* close exit door after last player */
2737 if (AllPlayersGone &&
2738 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2739 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2741 int element = Feld[ExitX][ExitY];
2743 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2744 EL_SP_EXIT_CLOSING);
2746 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2749 /* player disappears */
2750 DrawLevelField(ExitX, ExitY);
2753 for (i = 0; i < MAX_PLAYERS; i++)
2755 struct PlayerInfo *player = &stored_player[i];
2757 if (player->present)
2759 RemovePlayer(player);
2761 /* player disappears */
2762 DrawLevelField(player->jx, player->jy);
2766 PlaySound(SND_GAME_WINNING);
2769 if (game_over_delay > 0)
2776 if (time != time_final)
2778 int time_to_go = ABS(time_final - time);
2779 int time_count_dir = (time < time_final ? +1 : -1);
2780 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
2782 time += time_count_steps * time_count_dir;
2783 score += time_count_steps * level.score[SC_TIME_BONUS];
2785 DrawGameValue_Time(time);
2786 DrawGameValue_Score(score);
2788 if (time == time_final)
2789 StopSound(SND_GAME_LEVELTIME_BONUS);
2790 else if (setup.sound_loops)
2791 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
2793 PlaySound(SND_GAME_LEVELTIME_BONUS);
2800 boolean raise_level = FALSE;
2802 CloseDoor(DOOR_CLOSE_1);
2804 if (local_player->LevelSolved_SaveTape)
2811 SaveTapeChecked(tape.level_nr); /* ask to save tape */
2813 SaveTape(tape.level_nr); /* ask to save tape */
2817 if (level_editor_test_game)
2819 game_status = GAME_MODE_MAIN;
2826 if (!local_player->LevelSolved_SaveScore)
2828 FadeOut(REDRAW_FIELD);
2830 game_status = GAME_MODE_MAIN;
2832 DrawAndFadeInMainMenu(REDRAW_FIELD);
2837 if (level_nr == leveldir_current->handicap_level)
2839 leveldir_current->handicap_level++;
2840 SaveLevelSetup_SeriesInfo();
2843 if (level_nr < leveldir_current->last_level)
2844 raise_level = TRUE; /* advance to next level */
2846 if ((hi_pos = NewHiScore()) >= 0)
2848 game_status = GAME_MODE_SCORES;
2850 DrawHallOfFame(hi_pos);
2860 FadeOut(REDRAW_FIELD);
2862 game_status = GAME_MODE_MAIN;
2870 DrawAndFadeInMainMenu(REDRAW_FIELD);
2879 LoadScore(level_nr);
2881 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2882 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
2885 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2887 if (local_player->score_final > highscore[k].Score)
2889 /* player has made it to the hall of fame */
2891 if (k < MAX_SCORE_ENTRIES - 1)
2893 int m = MAX_SCORE_ENTRIES - 1;
2896 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2897 if (strEqual(setup.player_name, highscore[l].Name))
2899 if (m == k) /* player's new highscore overwrites his old one */
2903 for (l = m; l > k; l--)
2905 strcpy(highscore[l].Name, highscore[l - 1].Name);
2906 highscore[l].Score = highscore[l - 1].Score;
2913 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2914 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2915 highscore[k].Score = local_player->score_final;
2921 else if (!strncmp(setup.player_name, highscore[k].Name,
2922 MAX_PLAYER_NAME_LEN))
2923 break; /* player already there with a higher score */
2929 SaveScore(level_nr);
2934 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
2936 int element = Feld[x][y];
2937 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2938 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2939 int horiz_move = (dx != 0);
2940 int sign = (horiz_move ? dx : dy);
2941 int step = sign * element_info[element].move_stepsize;
2943 /* special values for move stepsize for spring and things on conveyor belt */
2946 if (CAN_FALL(element) &&
2947 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2948 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2949 else if (element == EL_SPRING)
2950 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2956 inline static int getElementMoveStepsize(int x, int y)
2958 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
2961 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2963 if (player->GfxAction != action || player->GfxDir != dir)
2966 printf("Player frame reset! (%d => %d, %d => %d)\n",
2967 player->GfxAction, action, player->GfxDir, dir);
2970 player->GfxAction = action;
2971 player->GfxDir = dir;
2973 player->StepFrame = 0;
2977 #if USE_GFX_RESET_GFX_ANIMATION
2978 static void ResetGfxFrame(int x, int y, boolean redraw)
2980 int element = Feld[x][y];
2981 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2982 int last_gfx_frame = GfxFrame[x][y];
2984 if (graphic_info[graphic].anim_global_sync)
2985 GfxFrame[x][y] = FrameCounter;
2986 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2987 GfxFrame[x][y] = CustomValue[x][y];
2988 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2989 GfxFrame[x][y] = element_info[element].collect_score;
2990 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
2991 GfxFrame[x][y] = ChangeDelay[x][y];
2993 if (redraw && GfxFrame[x][y] != last_gfx_frame)
2994 DrawLevelGraphicAnimation(x, y, graphic);
2998 static void ResetGfxAnimation(int x, int y)
3000 GfxAction[x][y] = ACTION_DEFAULT;
3001 GfxDir[x][y] = MovDir[x][y];
3004 #if USE_GFX_RESET_GFX_ANIMATION
3005 ResetGfxFrame(x, y, FALSE);
3009 static void ResetRandomAnimationValue(int x, int y)
3011 GfxRandom[x][y] = INIT_GFX_RANDOM();
3014 void InitMovingField(int x, int y, int direction)
3016 int element = Feld[x][y];
3017 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3018 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3021 boolean is_moving_before, is_moving_after;
3023 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3026 /* check if element was/is moving or being moved before/after mode change */
3028 is_moving_before = WasJustMoving[x][y];
3030 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3032 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3034 /* reset animation only for moving elements which change direction of moving
3035 or which just started or stopped moving
3036 (else CEs with property "can move" / "not moving" are reset each frame) */
3037 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3039 if (is_moving_before != is_moving_after ||
3040 direction != MovDir[x][y])
3041 ResetGfxAnimation(x, y);
3043 if ((is_moving_before || is_moving_after) && !continues_moving)
3044 ResetGfxAnimation(x, y);
3047 if (!continues_moving)
3048 ResetGfxAnimation(x, y);
3051 MovDir[x][y] = direction;
3052 GfxDir[x][y] = direction;
3054 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3055 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3056 direction == MV_DOWN && CAN_FALL(element) ?
3057 ACTION_FALLING : ACTION_MOVING);
3059 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3060 ACTION_FALLING : ACTION_MOVING);
3063 /* this is needed for CEs with property "can move" / "not moving" */
3065 if (is_moving_after)
3067 if (Feld[newx][newy] == EL_EMPTY)
3068 Feld[newx][newy] = EL_BLOCKED;
3070 MovDir[newx][newy] = MovDir[x][y];
3072 #if USE_NEW_CUSTOM_VALUE
3073 CustomValue[newx][newy] = CustomValue[x][y];
3076 GfxFrame[newx][newy] = GfxFrame[x][y];
3077 GfxRandom[newx][newy] = GfxRandom[x][y];
3078 GfxAction[newx][newy] = GfxAction[x][y];
3079 GfxDir[newx][newy] = GfxDir[x][y];
3083 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3085 int direction = MovDir[x][y];
3086 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3087 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3093 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3095 int oldx = x, oldy = y;
3096 int direction = MovDir[x][y];
3098 if (direction == MV_LEFT)
3100 else if (direction == MV_RIGHT)
3102 else if (direction == MV_UP)
3104 else if (direction == MV_DOWN)
3107 *comes_from_x = oldx;
3108 *comes_from_y = oldy;
3111 int MovingOrBlocked2Element(int x, int y)
3113 int element = Feld[x][y];
3115 if (element == EL_BLOCKED)
3119 Blocked2Moving(x, y, &oldx, &oldy);
3120 return Feld[oldx][oldy];
3126 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3128 /* like MovingOrBlocked2Element(), but if element is moving
3129 and (x,y) is the field the moving element is just leaving,
3130 return EL_BLOCKED instead of the element value */
3131 int element = Feld[x][y];
3133 if (IS_MOVING(x, y))
3135 if (element == EL_BLOCKED)
3139 Blocked2Moving(x, y, &oldx, &oldy);
3140 return Feld[oldx][oldy];
3149 static void RemoveField(int x, int y)
3151 Feld[x][y] = EL_EMPTY;
3157 #if USE_NEW_CUSTOM_VALUE
3158 CustomValue[x][y] = 0;
3162 ChangeDelay[x][y] = 0;
3163 ChangePage[x][y] = -1;
3164 Pushed[x][y] = FALSE;
3167 ExplodeField[x][y] = EX_TYPE_NONE;
3170 GfxElement[x][y] = EL_UNDEFINED;
3171 GfxAction[x][y] = ACTION_DEFAULT;
3172 GfxDir[x][y] = MV_NONE;
3175 void RemoveMovingField(int x, int y)
3177 int oldx = x, oldy = y, newx = x, newy = y;
3178 int element = Feld[x][y];
3179 int next_element = EL_UNDEFINED;
3181 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3184 if (IS_MOVING(x, y))
3186 Moving2Blocked(x, y, &newx, &newy);
3188 if (Feld[newx][newy] != EL_BLOCKED)
3190 /* element is moving, but target field is not free (blocked), but
3191 already occupied by something different (example: acid pool);
3192 in this case, only remove the moving field, but not the target */
3194 RemoveField(oldx, oldy);
3196 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3198 DrawLevelField(oldx, oldy);
3203 else if (element == EL_BLOCKED)
3205 Blocked2Moving(x, y, &oldx, &oldy);
3206 if (!IS_MOVING(oldx, oldy))
3210 if (element == EL_BLOCKED &&
3211 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3212 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3213 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3214 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3215 next_element = get_next_element(Feld[oldx][oldy]);
3217 RemoveField(oldx, oldy);
3218 RemoveField(newx, newy);
3220 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3222 if (next_element != EL_UNDEFINED)
3223 Feld[oldx][oldy] = next_element;
3225 DrawLevelField(oldx, oldy);
3226 DrawLevelField(newx, newy);
3229 void DrawDynamite(int x, int y)
3231 int sx = SCREENX(x), sy = SCREENY(y);
3232 int graphic = el2img(Feld[x][y]);
3235 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3238 if (IS_WALKABLE_INSIDE(Back[x][y]))
3242 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3243 else if (Store[x][y])
3244 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3246 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3248 if (Back[x][y] || Store[x][y])
3249 DrawGraphicThruMask(sx, sy, graphic, frame);
3251 DrawGraphic(sx, sy, graphic, frame);
3254 void CheckDynamite(int x, int y)
3256 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3260 if (MovDelay[x][y] != 0)
3263 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3269 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3274 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3276 boolean num_checked_players = 0;
3279 for (i = 0; i < MAX_PLAYERS; i++)
3281 if (stored_player[i].active)
3283 int sx = stored_player[i].jx;
3284 int sy = stored_player[i].jy;
3286 if (num_checked_players == 0)
3293 *sx1 = MIN(*sx1, sx);
3294 *sy1 = MIN(*sy1, sy);
3295 *sx2 = MAX(*sx2, sx);
3296 *sy2 = MAX(*sy2, sy);
3299 num_checked_players++;
3304 static boolean checkIfAllPlayersFitToScreen_RND()
3306 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3308 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3310 return (sx2 - sx1 < SCR_FIELDX &&
3311 sy2 - sy1 < SCR_FIELDY);
3314 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3316 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3318 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3320 *sx = (sx1 + sx2) / 2;
3321 *sy = (sy1 + sy2) / 2;
3324 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3325 boolean quick_relocation)
3327 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3328 boolean no_delay = (tape.warp_forward);
3329 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3330 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3332 if (quick_relocation)
3334 int offset = (setup.scroll_delay ? 3 : 0);
3336 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3338 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3339 x > SBX_Right + MIDPOSX ? SBX_Right :
3342 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3343 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3348 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3349 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3350 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3352 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3353 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3354 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3356 /* don't scroll over playfield boundaries */
3357 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3358 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3360 /* don't scroll over playfield boundaries */
3361 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3362 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3365 RedrawPlayfield(TRUE, 0,0,0,0);
3369 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3370 x > SBX_Right + MIDPOSX ? SBX_Right :
3373 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3374 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3377 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3379 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3382 int fx = FX, fy = FY;
3384 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3385 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3387 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3393 fx += dx * TILEX / 2;
3394 fy += dy * TILEY / 2;
3396 ScrollLevel(dx, dy);
3399 /* scroll in two steps of half tile size to make things smoother */
3400 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3402 Delay(wait_delay_value);
3404 /* scroll second step to align at full tile size */
3406 Delay(wait_delay_value);
3411 Delay(wait_delay_value);
3415 void RelocatePlayer(int jx, int jy, int el_player_raw)
3417 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3418 int player_nr = GET_PLAYER_NR(el_player);
3419 struct PlayerInfo *player = &stored_player[player_nr];
3420 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3421 boolean no_delay = (tape.warp_forward);
3422 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3423 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3424 int old_jx = player->jx;
3425 int old_jy = player->jy;
3426 int old_element = Feld[old_jx][old_jy];
3427 int element = Feld[jx][jy];
3428 boolean player_relocated = (old_jx != jx || old_jy != jy);
3430 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3431 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3432 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3433 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3434 int leave_side_horiz = move_dir_horiz;
3435 int leave_side_vert = move_dir_vert;
3436 int enter_side = enter_side_horiz | enter_side_vert;
3437 int leave_side = leave_side_horiz | leave_side_vert;
3439 if (player->GameOver) /* do not reanimate dead player */
3442 if (!player_relocated) /* no need to relocate the player */
3445 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3447 RemoveField(jx, jy); /* temporarily remove newly placed player */
3448 DrawLevelField(jx, jy);
3451 if (player->present)
3453 while (player->MovPos)
3455 ScrollPlayer(player, SCROLL_GO_ON);
3456 ScrollScreen(NULL, SCROLL_GO_ON);
3458 AdvanceFrameAndPlayerCounters(player->index_nr);
3463 Delay(wait_delay_value);
3466 DrawPlayer(player); /* needed here only to cleanup last field */
3467 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3469 player->is_moving = FALSE;
3472 if (IS_CUSTOM_ELEMENT(old_element))
3473 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3475 player->index_bit, leave_side);
3477 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3479 player->index_bit, leave_side);
3481 Feld[jx][jy] = el_player;
3482 InitPlayerField(jx, jy, el_player, TRUE);
3484 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3486 Feld[jx][jy] = element;
3487 InitField(jx, jy, FALSE);
3490 /* only visually relocate centered player */
3491 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3492 level.instant_relocation);
3494 TestIfPlayerTouchesBadThing(jx, jy);
3495 TestIfPlayerTouchesCustomElement(jx, jy);
3497 if (IS_CUSTOM_ELEMENT(element))
3498 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3499 player->index_bit, enter_side);
3501 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3502 player->index_bit, enter_side);
3505 void Explode(int ex, int ey, int phase, int mode)
3511 /* !!! eliminate this variable !!! */
3512 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3514 if (game.explosions_delayed)
3516 ExplodeField[ex][ey] = mode;
3520 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3522 int center_element = Feld[ex][ey];
3523 int artwork_element, explosion_element; /* set these values later */
3526 /* --- This is only really needed (and now handled) in "Impact()". --- */
3527 /* do not explode moving elements that left the explode field in time */
3528 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3529 center_element == EL_EMPTY &&
3530 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3535 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3536 if (mode == EX_TYPE_NORMAL ||
3537 mode == EX_TYPE_CENTER ||
3538 mode == EX_TYPE_CROSS)
3539 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3542 /* remove things displayed in background while burning dynamite */
3543 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3546 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3548 /* put moving element to center field (and let it explode there) */
3549 center_element = MovingOrBlocked2Element(ex, ey);
3550 RemoveMovingField(ex, ey);
3551 Feld[ex][ey] = center_element;
3554 /* now "center_element" is finally determined -- set related values now */
3555 artwork_element = center_element; /* for custom player artwork */
3556 explosion_element = center_element; /* for custom player artwork */
3558 if (IS_PLAYER(ex, ey))
3560 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3562 artwork_element = stored_player[player_nr].artwork_element;
3564 if (level.use_explosion_element[player_nr])
3566 explosion_element = level.explosion_element[player_nr];
3567 artwork_element = explosion_element;
3572 if (mode == EX_TYPE_NORMAL ||
3573 mode == EX_TYPE_CENTER ||
3574 mode == EX_TYPE_CROSS)
3575 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3578 last_phase = element_info[explosion_element].explosion_delay + 1;
3580 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3582 int xx = x - ex + 1;
3583 int yy = y - ey + 1;
3586 if (!IN_LEV_FIELD(x, y) ||
3587 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3588 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3591 element = Feld[x][y];
3593 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3595 element = MovingOrBlocked2Element(x, y);
3597 if (!IS_EXPLOSION_PROOF(element))
3598 RemoveMovingField(x, y);
3601 /* indestructible elements can only explode in center (but not flames) */
3602 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3603 mode == EX_TYPE_BORDER)) ||
3604 element == EL_FLAMES)
3607 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3608 behaviour, for example when touching a yamyam that explodes to rocks
3609 with active deadly shield, a rock is created under the player !!! */
3610 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3612 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3613 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3614 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3616 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3619 if (IS_ACTIVE_BOMB(element))
3621 /* re-activate things under the bomb like gate or penguin */
3622 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3629 /* save walkable background elements while explosion on same tile */
3630 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3631 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3632 Back[x][y] = element;
3634 /* ignite explodable elements reached by other explosion */
3635 if (element == EL_EXPLOSION)
3636 element = Store2[x][y];
3638 if (AmoebaNr[x][y] &&
3639 (element == EL_AMOEBA_FULL ||
3640 element == EL_BD_AMOEBA ||
3641 element == EL_AMOEBA_GROWING))
3643 AmoebaCnt[AmoebaNr[x][y]]--;
3644 AmoebaCnt2[AmoebaNr[x][y]]--;
3649 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3651 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3653 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3655 if (PLAYERINFO(ex, ey)->use_murphy)
3656 Store[x][y] = EL_EMPTY;
3659 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3660 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3661 else if (ELEM_IS_PLAYER(center_element))
3662 Store[x][y] = EL_EMPTY;
3663 else if (center_element == EL_YAMYAM)
3664 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3665 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3666 Store[x][y] = element_info[center_element].content.e[xx][yy];
3668 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3669 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3670 otherwise) -- FIX THIS !!! */
3671 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3672 Store[x][y] = element_info[element].content.e[1][1];
3674 else if (!CAN_EXPLODE(element))
3675 Store[x][y] = element_info[element].content.e[1][1];
3678 Store[x][y] = EL_EMPTY;
3680 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3681 center_element == EL_AMOEBA_TO_DIAMOND)
3682 Store2[x][y] = element;
3684 Feld[x][y] = EL_EXPLOSION;
3685 GfxElement[x][y] = artwork_element;
3687 ExplodePhase[x][y] = 1;
3688 ExplodeDelay[x][y] = last_phase;
3693 if (center_element == EL_YAMYAM)
3694 game.yamyam_content_nr =
3695 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3707 GfxFrame[x][y] = 0; /* restart explosion animation */
3709 last_phase = ExplodeDelay[x][y];
3711 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3715 /* activate this even in non-DEBUG version until cause for crash in
3716 getGraphicAnimationFrame() (see below) is found and eliminated */
3722 /* this can happen if the player leaves an explosion just in time */
3723 if (GfxElement[x][y] == EL_UNDEFINED)
3724 GfxElement[x][y] = EL_EMPTY;
3726 if (GfxElement[x][y] == EL_UNDEFINED)
3729 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3730 printf("Explode(): This should never happen!\n");
3733 GfxElement[x][y] = EL_EMPTY;
3739 border_element = Store2[x][y];
3740 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3741 border_element = StorePlayer[x][y];
3743 if (phase == element_info[border_element].ignition_delay ||
3744 phase == last_phase)
3746 boolean border_explosion = FALSE;
3748 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3749 !PLAYER_EXPLOSION_PROTECTED(x, y))
3751 KillPlayerUnlessExplosionProtected(x, y);
3752 border_explosion = TRUE;
3754 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3756 Feld[x][y] = Store2[x][y];
3759 border_explosion = TRUE;
3761 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3763 AmoebeUmwandeln(x, y);
3765 border_explosion = TRUE;
3768 /* if an element just explodes due to another explosion (chain-reaction),
3769 do not immediately end the new explosion when it was the last frame of
3770 the explosion (as it would be done in the following "if"-statement!) */
3771 if (border_explosion && phase == last_phase)
3775 if (phase == last_phase)
3779 element = Feld[x][y] = Store[x][y];
3780 Store[x][y] = Store2[x][y] = 0;
3781 GfxElement[x][y] = EL_UNDEFINED;
3783 /* player can escape from explosions and might therefore be still alive */
3784 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3785 element <= EL_PLAYER_IS_EXPLODING_4)
3787 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3788 int explosion_element = EL_PLAYER_1 + player_nr;
3789 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3790 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3792 if (level.use_explosion_element[player_nr])
3793 explosion_element = level.explosion_element[player_nr];
3795 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3796 element_info[explosion_element].content.e[xx][yy]);
3799 /* restore probably existing indestructible background element */
3800 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3801 element = Feld[x][y] = Back[x][y];
3804 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3805 GfxDir[x][y] = MV_NONE;
3806 ChangeDelay[x][y] = 0;
3807 ChangePage[x][y] = -1;
3809 #if USE_NEW_CUSTOM_VALUE
3810 CustomValue[x][y] = 0;
3813 InitField_WithBug2(x, y, FALSE);
3815 DrawLevelField(x, y);
3817 TestIfElementTouchesCustomElement(x, y);
3819 if (GFX_CRUMBLED(element))
3820 DrawLevelFieldCrumbledSandNeighbours(x, y);
3822 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3823 StorePlayer[x][y] = 0;
3825 if (ELEM_IS_PLAYER(element))
3826 RelocatePlayer(x, y, element);
3828 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3830 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3831 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3834 DrawLevelFieldCrumbledSand(x, y);
3836 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3838 DrawLevelElement(x, y, Back[x][y]);
3839 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3841 else if (IS_WALKABLE_UNDER(Back[x][y]))
3843 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3844 DrawLevelElementThruMask(x, y, Back[x][y]);
3846 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3847 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3851 void DynaExplode(int ex, int ey)
3854 int dynabomb_element = Feld[ex][ey];
3855 int dynabomb_size = 1;
3856 boolean dynabomb_xl = FALSE;
3857 struct PlayerInfo *player;
3858 static int xy[4][2] =
3866 if (IS_ACTIVE_BOMB(dynabomb_element))
3868 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3869 dynabomb_size = player->dynabomb_size;
3870 dynabomb_xl = player->dynabomb_xl;
3871 player->dynabombs_left++;
3874 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3876 for (i = 0; i < NUM_DIRECTIONS; i++)
3878 for (j = 1; j <= dynabomb_size; j++)
3880 int x = ex + j * xy[i][0];
3881 int y = ey + j * xy[i][1];
3884 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3887 element = Feld[x][y];
3889 /* do not restart explosions of fields with active bombs */
3890 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3893 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3895 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3896 !IS_DIGGABLE(element) && !dynabomb_xl)
3902 void Bang(int x, int y)
3904 int element = MovingOrBlocked2Element(x, y);
3905 int explosion_type = EX_TYPE_NORMAL;
3907 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3909 struct PlayerInfo *player = PLAYERINFO(x, y);
3911 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3912 player->element_nr);
3914 if (level.use_explosion_element[player->index_nr])
3916 int explosion_element = level.explosion_element[player->index_nr];
3918 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3919 explosion_type = EX_TYPE_CROSS;
3920 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3921 explosion_type = EX_TYPE_CENTER;
3929 case EL_BD_BUTTERFLY:
3932 case EL_DARK_YAMYAM:
3936 RaiseScoreElement(element);
3939 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3940 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3941 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3942 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3943 case EL_DYNABOMB_INCREASE_NUMBER:
3944 case EL_DYNABOMB_INCREASE_SIZE:
3945 case EL_DYNABOMB_INCREASE_POWER:
3946 explosion_type = EX_TYPE_DYNA;
3951 case EL_LAMP_ACTIVE:
3952 case EL_AMOEBA_TO_DIAMOND:
3953 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3954 explosion_type = EX_TYPE_CENTER;
3958 if (element_info[element].explosion_type == EXPLODES_CROSS)
3959 explosion_type = EX_TYPE_CROSS;
3960 else if (element_info[element].explosion_type == EXPLODES_1X1)
3961 explosion_type = EX_TYPE_CENTER;
3965 if (explosion_type == EX_TYPE_DYNA)
3968 Explode(x, y, EX_PHASE_START, explosion_type);
3970 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3973 void SplashAcid(int x, int y)
3975 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3976 (!IN_LEV_FIELD(x - 1, y - 2) ||
3977 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3978 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3980 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3981 (!IN_LEV_FIELD(x + 1, y - 2) ||
3982 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3983 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3985 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3988 static void InitBeltMovement()
3990 static int belt_base_element[4] =
3992 EL_CONVEYOR_BELT_1_LEFT,
3993 EL_CONVEYOR_BELT_2_LEFT,
3994 EL_CONVEYOR_BELT_3_LEFT,
3995 EL_CONVEYOR_BELT_4_LEFT
3997 static int belt_base_active_element[4] =
3999 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4000 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4001 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4002 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4007 /* set frame order for belt animation graphic according to belt direction */
4008 for (i = 0; i < NUM_BELTS; i++)
4012 for (j = 0; j < NUM_BELT_PARTS; j++)
4014 int element = belt_base_active_element[belt_nr] + j;
4015 int graphic = el2img(element);
4017 if (game.belt_dir[i] == MV_LEFT)
4018 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4020 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4024 SCAN_PLAYFIELD(x, y)
4026 int element = Feld[x][y];
4028 for (i = 0; i < NUM_BELTS; i++)
4030 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4032 int e_belt_nr = getBeltNrFromBeltElement(element);
4035 if (e_belt_nr == belt_nr)
4037 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4039 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4046 static void ToggleBeltSwitch(int x, int y)
4048 static int belt_base_element[4] =
4050 EL_CONVEYOR_BELT_1_LEFT,
4051 EL_CONVEYOR_BELT_2_LEFT,
4052 EL_CONVEYOR_BELT_3_LEFT,
4053 EL_CONVEYOR_BELT_4_LEFT
4055 static int belt_base_active_element[4] =
4057 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4058 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4059 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4060 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4062 static int belt_base_switch_element[4] =
4064 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4065 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4066 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4067 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4069 static int belt_move_dir[4] =
4077 int element = Feld[x][y];
4078 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4079 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4080 int belt_dir = belt_move_dir[belt_dir_nr];
4083 if (!IS_BELT_SWITCH(element))
4086 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4087 game.belt_dir[belt_nr] = belt_dir;
4089 if (belt_dir_nr == 3)
4092 /* set frame order for belt animation graphic according to belt direction */
4093 for (i = 0; i < NUM_BELT_PARTS; i++)
4095 int element = belt_base_active_element[belt_nr] + i;
4096 int graphic = el2img(element);
4098 if (belt_dir == MV_LEFT)
4099 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4101 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4104 SCAN_PLAYFIELD(xx, yy)
4106 int element = Feld[xx][yy];
4108 if (IS_BELT_SWITCH(element))
4110 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4112 if (e_belt_nr == belt_nr)
4114 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4115 DrawLevelField(xx, yy);
4118 else if (IS_BELT(element) && belt_dir != MV_NONE)
4120 int e_belt_nr = getBeltNrFromBeltElement(element);
4122 if (e_belt_nr == belt_nr)
4124 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4126 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4127 DrawLevelField(xx, yy);
4130 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4132 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4134 if (e_belt_nr == belt_nr)
4136 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4138 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4139 DrawLevelField(xx, yy);
4145 static void ToggleSwitchgateSwitch(int x, int y)
4149 game.switchgate_pos = !game.switchgate_pos;
4151 SCAN_PLAYFIELD(xx, yy)
4153 int element = Feld[xx][yy];
4155 #if !USE_BOTH_SWITCHGATE_SWITCHES
4156 if (element == EL_SWITCHGATE_SWITCH_UP ||
4157 element == EL_SWITCHGATE_SWITCH_DOWN)
4159 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4160 DrawLevelField(xx, yy);
4163 if (element == EL_SWITCHGATE_SWITCH_UP)
4165 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4166 DrawLevelField(xx, yy);
4168 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4170 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4171 DrawLevelField(xx, yy);
4174 else if (element == EL_SWITCHGATE_OPEN ||
4175 element == EL_SWITCHGATE_OPENING)
4177 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4179 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4181 else if (element == EL_SWITCHGATE_CLOSED ||
4182 element == EL_SWITCHGATE_CLOSING)
4184 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4186 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4191 static int getInvisibleActiveFromInvisibleElement(int element)
4193 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4194 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4195 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4199 static int getInvisibleFromInvisibleActiveElement(int element)
4201 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4202 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4203 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4207 static void RedrawAllLightSwitchesAndInvisibleElements()
4211 SCAN_PLAYFIELD(x, y)
4213 int element = Feld[x][y];
4215 if (element == EL_LIGHT_SWITCH &&
4216 game.light_time_left > 0)
4218 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4219 DrawLevelField(x, y);
4221 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4222 game.light_time_left == 0)
4224 Feld[x][y] = EL_LIGHT_SWITCH;
4225 DrawLevelField(x, y);
4227 else if (element == EL_EMC_DRIPPER &&
4228 game.light_time_left > 0)
4230 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4231 DrawLevelField(x, y);
4233 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4234 game.light_time_left == 0)
4236 Feld[x][y] = EL_EMC_DRIPPER;
4237 DrawLevelField(x, y);
4239 else if (element == EL_INVISIBLE_STEELWALL ||
4240 element == EL_INVISIBLE_WALL ||
4241 element == EL_INVISIBLE_SAND)
4243 if (game.light_time_left > 0)
4244 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4246 DrawLevelField(x, y);
4248 /* uncrumble neighbour fields, if needed */
4249 if (element == EL_INVISIBLE_SAND)
4250 DrawLevelFieldCrumbledSandNeighbours(x, y);
4252 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4253 element == EL_INVISIBLE_WALL_ACTIVE ||
4254 element == EL_INVISIBLE_SAND_ACTIVE)
4256 if (game.light_time_left == 0)
4257 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4259 DrawLevelField(x, y);
4261 /* re-crumble neighbour fields, if needed */
4262 if (element == EL_INVISIBLE_SAND)
4263 DrawLevelFieldCrumbledSandNeighbours(x, y);
4268 static void RedrawAllInvisibleElementsForLenses()
4272 SCAN_PLAYFIELD(x, y)
4274 int element = Feld[x][y];
4276 if (element == EL_EMC_DRIPPER &&
4277 game.lenses_time_left > 0)
4279 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4280 DrawLevelField(x, y);
4282 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4283 game.lenses_time_left == 0)
4285 Feld[x][y] = EL_EMC_DRIPPER;
4286 DrawLevelField(x, y);
4288 else if (element == EL_INVISIBLE_STEELWALL ||
4289 element == EL_INVISIBLE_WALL ||
4290 element == EL_INVISIBLE_SAND)
4292 if (game.lenses_time_left > 0)
4293 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4295 DrawLevelField(x, y);
4297 /* uncrumble neighbour fields, if needed */
4298 if (element == EL_INVISIBLE_SAND)
4299 DrawLevelFieldCrumbledSandNeighbours(x, y);
4301 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4302 element == EL_INVISIBLE_WALL_ACTIVE ||
4303 element == EL_INVISIBLE_SAND_ACTIVE)
4305 if (game.lenses_time_left == 0)
4306 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4308 DrawLevelField(x, y);
4310 /* re-crumble neighbour fields, if needed */
4311 if (element == EL_INVISIBLE_SAND)
4312 DrawLevelFieldCrumbledSandNeighbours(x, y);
4317 static void RedrawAllInvisibleElementsForMagnifier()
4321 SCAN_PLAYFIELD(x, y)
4323 int element = Feld[x][y];
4325 if (element == EL_EMC_FAKE_GRASS &&
4326 game.magnify_time_left > 0)
4328 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4329 DrawLevelField(x, y);
4331 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4332 game.magnify_time_left == 0)
4334 Feld[x][y] = EL_EMC_FAKE_GRASS;
4335 DrawLevelField(x, y);
4337 else if (IS_GATE_GRAY(element) &&
4338 game.magnify_time_left > 0)
4340 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4341 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4342 IS_EM_GATE_GRAY(element) ?
4343 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4344 IS_EMC_GATE_GRAY(element) ?
4345 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4347 DrawLevelField(x, y);
4349 else if (IS_GATE_GRAY_ACTIVE(element) &&
4350 game.magnify_time_left == 0)
4352 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4353 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4354 IS_EM_GATE_GRAY_ACTIVE(element) ?
4355 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4356 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4357 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4359 DrawLevelField(x, y);
4364 static void ToggleLightSwitch(int x, int y)
4366 int element = Feld[x][y];
4368 game.light_time_left =
4369 (element == EL_LIGHT_SWITCH ?
4370 level.time_light * FRAMES_PER_SECOND : 0);
4372 RedrawAllLightSwitchesAndInvisibleElements();
4375 static void ActivateTimegateSwitch(int x, int y)
4379 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4381 SCAN_PLAYFIELD(xx, yy)
4383 int element = Feld[xx][yy];
4385 if (element == EL_TIMEGATE_CLOSED ||
4386 element == EL_TIMEGATE_CLOSING)
4388 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4389 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4393 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4395 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4396 DrawLevelField(xx, yy);
4402 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4405 void Impact(int x, int y)
4407 boolean last_line = (y == lev_fieldy - 1);
4408 boolean object_hit = FALSE;
4409 boolean impact = (last_line || object_hit);
4410 int element = Feld[x][y];
4411 int smashed = EL_STEELWALL;
4413 if (!last_line) /* check if element below was hit */
4415 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4418 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4419 MovDir[x][y + 1] != MV_DOWN ||
4420 MovPos[x][y + 1] <= TILEY / 2));
4422 /* do not smash moving elements that left the smashed field in time */
4423 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4424 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4427 #if USE_QUICKSAND_IMPACT_BUGFIX
4428 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4430 RemoveMovingField(x, y + 1);
4431 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4432 Feld[x][y + 2] = EL_ROCK;
4433 DrawLevelField(x, y + 2);
4440 smashed = MovingOrBlocked2Element(x, y + 1);
4442 impact = (last_line || object_hit);
4445 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4447 SplashAcid(x, y + 1);
4451 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4452 /* only reset graphic animation if graphic really changes after impact */
4454 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4456 ResetGfxAnimation(x, y);
4457 DrawLevelField(x, y);
4460 if (impact && CAN_EXPLODE_IMPACT(element))
4465 else if (impact && element == EL_PEARL)
4467 ResetGfxAnimation(x, y);
4469 Feld[x][y] = EL_PEARL_BREAKING;
4470 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4473 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4475 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4480 if (impact && element == EL_AMOEBA_DROP)
4482 if (object_hit && IS_PLAYER(x, y + 1))
4483 KillPlayerUnlessEnemyProtected(x, y + 1);
4484 else if (object_hit && smashed == EL_PENGUIN)
4488 Feld[x][y] = EL_AMOEBA_GROWING;
4489 Store[x][y] = EL_AMOEBA_WET;
4491 ResetRandomAnimationValue(x, y);
4496 if (object_hit) /* check which object was hit */
4498 if (CAN_PASS_MAGIC_WALL(element) &&
4499 (smashed == EL_MAGIC_WALL ||
4500 smashed == EL_BD_MAGIC_WALL))
4503 int activated_magic_wall =
4504 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4505 EL_BD_MAGIC_WALL_ACTIVE);
4507 /* activate magic wall / mill */
4508 SCAN_PLAYFIELD(xx, yy)
4509 if (Feld[xx][yy] == smashed)
4510 Feld[xx][yy] = activated_magic_wall;
4512 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4513 game.magic_wall_active = TRUE;
4515 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4516 SND_MAGIC_WALL_ACTIVATING :
4517 SND_BD_MAGIC_WALL_ACTIVATING));
4520 if (IS_PLAYER(x, y + 1))
4522 if (CAN_SMASH_PLAYER(element))
4524 KillPlayerUnlessEnemyProtected(x, y + 1);
4528 else if (smashed == EL_PENGUIN)
4530 if (CAN_SMASH_PLAYER(element))
4536 else if (element == EL_BD_DIAMOND)
4538 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4544 else if (((element == EL_SP_INFOTRON ||
4545 element == EL_SP_ZONK) &&
4546 (smashed == EL_SP_SNIKSNAK ||
4547 smashed == EL_SP_ELECTRON ||
4548 smashed == EL_SP_DISK_ORANGE)) ||
4549 (element == EL_SP_INFOTRON &&
4550 smashed == EL_SP_DISK_YELLOW))
4555 else if (CAN_SMASH_EVERYTHING(element))
4557 if (IS_CLASSIC_ENEMY(smashed) ||
4558 CAN_EXPLODE_SMASHED(smashed))
4563 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4565 if (smashed == EL_LAMP ||
4566 smashed == EL_LAMP_ACTIVE)
4571 else if (smashed == EL_NUT)
4573 Feld[x][y + 1] = EL_NUT_BREAKING;
4574 PlayLevelSound(x, y, SND_NUT_BREAKING);
4575 RaiseScoreElement(EL_NUT);
4578 else if (smashed == EL_PEARL)
4580 ResetGfxAnimation(x, y);
4582 Feld[x][y + 1] = EL_PEARL_BREAKING;
4583 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4586 else if (smashed == EL_DIAMOND)
4588 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4589 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4592 else if (IS_BELT_SWITCH(smashed))
4594 ToggleBeltSwitch(x, y + 1);
4596 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4597 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4599 ToggleSwitchgateSwitch(x, y + 1);
4601 else if (smashed == EL_LIGHT_SWITCH ||
4602 smashed == EL_LIGHT_SWITCH_ACTIVE)
4604 ToggleLightSwitch(x, y + 1);
4609 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4612 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4614 CheckElementChangeBySide(x, y + 1, smashed, element,
4615 CE_SWITCHED, CH_SIDE_TOP);
4616 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4622 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4627 /* play sound of magic wall / mill */
4629 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4630 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4632 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4633 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4634 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4635 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4640 /* play sound of object that hits the ground */
4641 if (last_line || object_hit)
4642 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4645 inline static void TurnRoundExt(int x, int y)
4657 { 0, 0 }, { 0, 0 }, { 0, 0 },
4662 int left, right, back;
4666 { MV_DOWN, MV_UP, MV_RIGHT },
4667 { MV_UP, MV_DOWN, MV_LEFT },
4669 { MV_LEFT, MV_RIGHT, MV_DOWN },
4673 { MV_RIGHT, MV_LEFT, MV_UP }
4676 int element = Feld[x][y];
4677 int move_pattern = element_info[element].move_pattern;
4679 int old_move_dir = MovDir[x][y];
4680 int left_dir = turn[old_move_dir].left;
4681 int right_dir = turn[old_move_dir].right;
4682 int back_dir = turn[old_move_dir].back;
4684 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4685 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4686 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4687 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4689 int left_x = x + left_dx, left_y = y + left_dy;
4690 int right_x = x + right_dx, right_y = y + right_dy;
4691 int move_x = x + move_dx, move_y = y + move_dy;
4695 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4697 TestIfBadThingTouchesOtherBadThing(x, y);
4699 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4700 MovDir[x][y] = right_dir;
4701 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4702 MovDir[x][y] = left_dir;
4704 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4706 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4709 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4711 TestIfBadThingTouchesOtherBadThing(x, y);
4713 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4714 MovDir[x][y] = left_dir;
4715 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4716 MovDir[x][y] = right_dir;
4718 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4720 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4723 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4725 TestIfBadThingTouchesOtherBadThing(x, y);
4727 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4728 MovDir[x][y] = left_dir;
4729 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4730 MovDir[x][y] = right_dir;
4732 if (MovDir[x][y] != old_move_dir)
4735 else if (element == EL_YAMYAM)
4737 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4738 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4740 if (can_turn_left && can_turn_right)
4741 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4742 else if (can_turn_left)
4743 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4744 else if (can_turn_right)
4745 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4747 MovDir[x][y] = back_dir;
4749 MovDelay[x][y] = 16 + 16 * RND(3);
4751 else if (element == EL_DARK_YAMYAM)
4753 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4755 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4758 if (can_turn_left && can_turn_right)
4759 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4760 else if (can_turn_left)
4761 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4762 else if (can_turn_right)
4763 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4765 MovDir[x][y] = back_dir;
4767 MovDelay[x][y] = 16 + 16 * RND(3);
4769 else if (element == EL_PACMAN)
4771 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4772 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4774 if (can_turn_left && can_turn_right)
4775 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4776 else if (can_turn_left)
4777 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4778 else if (can_turn_right)
4779 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4781 MovDir[x][y] = back_dir;
4783 MovDelay[x][y] = 6 + RND(40);
4785 else if (element == EL_PIG)
4787 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4788 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4789 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4790 boolean should_turn_left, should_turn_right, should_move_on;
4792 int rnd = RND(rnd_value);
4794 should_turn_left = (can_turn_left &&
4796 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4797 y + back_dy + left_dy)));
4798 should_turn_right = (can_turn_right &&
4800 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4801 y + back_dy + right_dy)));
4802 should_move_on = (can_move_on &&
4805 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4806 y + move_dy + left_dy) ||
4807 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4808 y + move_dy + right_dy)));
4810 if (should_turn_left || should_turn_right || should_move_on)
4812 if (should_turn_left && should_turn_right && should_move_on)
4813 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4814 rnd < 2 * rnd_value / 3 ? right_dir :
4816 else if (should_turn_left && should_turn_right)
4817 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4818 else if (should_turn_left && should_move_on)
4819 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4820 else if (should_turn_right && should_move_on)
4821 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4822 else if (should_turn_left)
4823 MovDir[x][y] = left_dir;
4824 else if (should_turn_right)
4825 MovDir[x][y] = right_dir;
4826 else if (should_move_on)
4827 MovDir[x][y] = old_move_dir;
4829 else if (can_move_on && rnd > rnd_value / 8)
4830 MovDir[x][y] = old_move_dir;
4831 else if (can_turn_left && can_turn_right)
4832 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4833 else if (can_turn_left && rnd > rnd_value / 8)
4834 MovDir[x][y] = left_dir;
4835 else if (can_turn_right && rnd > rnd_value/8)
4836 MovDir[x][y] = right_dir;
4838 MovDir[x][y] = back_dir;
4840 xx = x + move_xy[MovDir[x][y]].dx;
4841 yy = y + move_xy[MovDir[x][y]].dy;
4843 if (!IN_LEV_FIELD(xx, yy) ||
4844 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4845 MovDir[x][y] = old_move_dir;
4849 else if (element == EL_DRAGON)
4851 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4852 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4853 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4855 int rnd = RND(rnd_value);
4857 if (can_move_on && rnd > rnd_value / 8)
4858 MovDir[x][y] = old_move_dir;
4859 else if (can_turn_left && can_turn_right)
4860 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4861 else if (can_turn_left && rnd > rnd_value / 8)
4862 MovDir[x][y] = left_dir;
4863 else if (can_turn_right && rnd > rnd_value / 8)
4864 MovDir[x][y] = right_dir;
4866 MovDir[x][y] = back_dir;
4868 xx = x + move_xy[MovDir[x][y]].dx;
4869 yy = y + move_xy[MovDir[x][y]].dy;
4871 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4872 MovDir[x][y] = old_move_dir;
4876 else if (element == EL_MOLE)
4878 boolean can_move_on =
4879 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4880 IS_AMOEBOID(Feld[move_x][move_y]) ||
4881 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4884 boolean can_turn_left =
4885 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4886 IS_AMOEBOID(Feld[left_x][left_y])));
4888 boolean can_turn_right =
4889 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4890 IS_AMOEBOID(Feld[right_x][right_y])));
4892 if (can_turn_left && can_turn_right)
4893 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4894 else if (can_turn_left)
4895 MovDir[x][y] = left_dir;
4897 MovDir[x][y] = right_dir;
4900 if (MovDir[x][y] != old_move_dir)
4903 else if (element == EL_BALLOON)
4905 MovDir[x][y] = game.wind_direction;
4908 else if (element == EL_SPRING)
4910 #if USE_NEW_SPRING_BUMPER
4911 if (MovDir[x][y] & MV_HORIZONTAL)
4913 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4914 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4916 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4917 ResetGfxAnimation(move_x, move_y);
4918 DrawLevelField(move_x, move_y);
4920 MovDir[x][y] = back_dir;
4922 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4923 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4924 MovDir[x][y] = MV_NONE;
4927 if (MovDir[x][y] & MV_HORIZONTAL &&
4928 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4929 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4930 MovDir[x][y] = MV_NONE;
4935 else if (element == EL_ROBOT ||
4936 element == EL_SATELLITE ||
4937 element == EL_PENGUIN ||
4938 element == EL_EMC_ANDROID)
4940 int attr_x = -1, attr_y = -1;
4951 for (i = 0; i < MAX_PLAYERS; i++)
4953 struct PlayerInfo *player = &stored_player[i];
4954 int jx = player->jx, jy = player->jy;
4956 if (!player->active)
4960 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4968 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4969 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4970 game.engine_version < VERSION_IDENT(3,1,0,0)))
4976 if (element == EL_PENGUIN)
4979 static int xy[4][2] =
4987 for (i = 0; i < NUM_DIRECTIONS; i++)
4989 int ex = x + xy[i][0];
4990 int ey = y + xy[i][1];
4992 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5001 MovDir[x][y] = MV_NONE;
5003 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5004 else if (attr_x > x)
5005 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5007 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5008 else if (attr_y > y)
5009 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5011 if (element == EL_ROBOT)
5015 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5016 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5017 Moving2Blocked(x, y, &newx, &newy);
5019 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5020 MovDelay[x][y] = 8 + 8 * !RND(3);
5022 MovDelay[x][y] = 16;
5024 else if (element == EL_PENGUIN)
5030 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5032 boolean first_horiz = RND(2);
5033 int new_move_dir = MovDir[x][y];
5036 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5037 Moving2Blocked(x, y, &newx, &newy);
5039 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5043 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5044 Moving2Blocked(x, y, &newx, &newy);
5046 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5049 MovDir[x][y] = old_move_dir;
5053 else if (element == EL_SATELLITE)
5059 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5061 boolean first_horiz = RND(2);
5062 int new_move_dir = MovDir[x][y];
5065 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5066 Moving2Blocked(x, y, &newx, &newy);
5068 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5072 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5073 Moving2Blocked(x, y, &newx, &newy);
5075 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5078 MovDir[x][y] = old_move_dir;
5082 else if (element == EL_EMC_ANDROID)
5084 static int check_pos[16] =
5086 -1, /* 0 => (invalid) */
5087 7, /* 1 => MV_LEFT */
5088 3, /* 2 => MV_RIGHT */
5089 -1, /* 3 => (invalid) */
5091 0, /* 5 => MV_LEFT | MV_UP */
5092 2, /* 6 => MV_RIGHT | MV_UP */
5093 -1, /* 7 => (invalid) */
5094 5, /* 8 => MV_DOWN */
5095 6, /* 9 => MV_LEFT | MV_DOWN */
5096 4, /* 10 => MV_RIGHT | MV_DOWN */
5097 -1, /* 11 => (invalid) */
5098 -1, /* 12 => (invalid) */
5099 -1, /* 13 => (invalid) */
5100 -1, /* 14 => (invalid) */
5101 -1, /* 15 => (invalid) */
5109 { -1, -1, MV_LEFT | MV_UP },
5111 { +1, -1, MV_RIGHT | MV_UP },
5112 { +1, 0, MV_RIGHT },
5113 { +1, +1, MV_RIGHT | MV_DOWN },
5115 { -1, +1, MV_LEFT | MV_DOWN },
5118 int start_pos, check_order;
5119 boolean can_clone = FALSE;
5122 /* check if there is any free field around current position */
5123 for (i = 0; i < 8; i++)
5125 int newx = x + check_xy[i].dx;
5126 int newy = y + check_xy[i].dy;
5128 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5136 if (can_clone) /* randomly find an element to clone */
5140 start_pos = check_pos[RND(8)];
5141 check_order = (RND(2) ? -1 : +1);
5143 for (i = 0; i < 8; i++)
5145 int pos_raw = start_pos + i * check_order;
5146 int pos = (pos_raw + 8) % 8;
5147 int newx = x + check_xy[pos].dx;
5148 int newy = y + check_xy[pos].dy;
5150 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5152 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5153 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5155 Store[x][y] = Feld[newx][newy];
5164 if (can_clone) /* randomly find a direction to move */
5168 start_pos = check_pos[RND(8)];
5169 check_order = (RND(2) ? -1 : +1);
5171 for (i = 0; i < 8; i++)
5173 int pos_raw = start_pos + i * check_order;
5174 int pos = (pos_raw + 8) % 8;
5175 int newx = x + check_xy[pos].dx;
5176 int newy = y + check_xy[pos].dy;
5177 int new_move_dir = check_xy[pos].dir;
5179 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5181 MovDir[x][y] = new_move_dir;
5182 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5191 if (can_clone) /* cloning and moving successful */
5194 /* cannot clone -- try to move towards player */
5196 start_pos = check_pos[MovDir[x][y] & 0x0f];
5197 check_order = (RND(2) ? -1 : +1);
5199 for (i = 0; i < 3; i++)
5201 /* first check start_pos, then previous/next or (next/previous) pos */
5202 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5203 int pos = (pos_raw + 8) % 8;
5204 int newx = x + check_xy[pos].dx;
5205 int newy = y + check_xy[pos].dy;
5206 int new_move_dir = check_xy[pos].dir;
5208 if (IS_PLAYER(newx, newy))
5211 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5213 MovDir[x][y] = new_move_dir;
5214 MovDelay[x][y] = level.android_move_time * 8 + 1;
5221 else if (move_pattern == MV_TURNING_LEFT ||
5222 move_pattern == MV_TURNING_RIGHT ||
5223 move_pattern == MV_TURNING_LEFT_RIGHT ||
5224 move_pattern == MV_TURNING_RIGHT_LEFT ||
5225 move_pattern == MV_TURNING_RANDOM ||
5226 move_pattern == MV_ALL_DIRECTIONS)
5228 boolean can_turn_left =
5229 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5230 boolean can_turn_right =
5231 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5233 if (element_info[element].move_stepsize == 0) /* "not moving" */
5236 if (move_pattern == MV_TURNING_LEFT)
5237 MovDir[x][y] = left_dir;
5238 else if (move_pattern == MV_TURNING_RIGHT)
5239 MovDir[x][y] = right_dir;
5240 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5241 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5242 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5243 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5244 else if (move_pattern == MV_TURNING_RANDOM)
5245 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5246 can_turn_right && !can_turn_left ? right_dir :
5247 RND(2) ? left_dir : right_dir);
5248 else if (can_turn_left && can_turn_right)
5249 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5250 else if (can_turn_left)
5251 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5252 else if (can_turn_right)
5253 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5255 MovDir[x][y] = back_dir;
5257 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5259 else if (move_pattern == MV_HORIZONTAL ||
5260 move_pattern == MV_VERTICAL)
5262 if (move_pattern & old_move_dir)
5263 MovDir[x][y] = back_dir;
5264 else if (move_pattern == MV_HORIZONTAL)
5265 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5266 else if (move_pattern == MV_VERTICAL)
5267 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5269 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5271 else if (move_pattern & MV_ANY_DIRECTION)
5273 MovDir[x][y] = move_pattern;
5274 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5276 else if (move_pattern & MV_WIND_DIRECTION)
5278 MovDir[x][y] = game.wind_direction;
5279 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5281 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5283 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5284 MovDir[x][y] = left_dir;
5285 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5286 MovDir[x][y] = right_dir;
5288 if (MovDir[x][y] != old_move_dir)
5289 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5291 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5293 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5294 MovDir[x][y] = right_dir;
5295 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5296 MovDir[x][y] = left_dir;
5298 if (MovDir[x][y] != old_move_dir)
5299 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5301 else if (move_pattern == MV_TOWARDS_PLAYER ||
5302 move_pattern == MV_AWAY_FROM_PLAYER)
5304 int attr_x = -1, attr_y = -1;
5306 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5317 for (i = 0; i < MAX_PLAYERS; i++)
5319 struct PlayerInfo *player = &stored_player[i];
5320 int jx = player->jx, jy = player->jy;
5322 if (!player->active)
5326 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5334 MovDir[x][y] = MV_NONE;
5336 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5337 else if (attr_x > x)
5338 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5340 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5341 else if (attr_y > y)
5342 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5344 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5346 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5348 boolean first_horiz = RND(2);
5349 int new_move_dir = MovDir[x][y];
5351 if (element_info[element].move_stepsize == 0) /* "not moving" */
5353 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5354 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5360 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5361 Moving2Blocked(x, y, &newx, &newy);
5363 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5367 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5368 Moving2Blocked(x, y, &newx, &newy);
5370 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5373 MovDir[x][y] = old_move_dir;
5376 else if (move_pattern == MV_WHEN_PUSHED ||
5377 move_pattern == MV_WHEN_DROPPED)
5379 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5380 MovDir[x][y] = MV_NONE;
5384 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5386 static int test_xy[7][2] =
5396 static int test_dir[7] =
5406 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5407 int move_preference = -1000000; /* start with very low preference */
5408 int new_move_dir = MV_NONE;
5409 int start_test = RND(4);
5412 for (i = 0; i < NUM_DIRECTIONS; i++)
5414 int move_dir = test_dir[start_test + i];
5415 int move_dir_preference;
5417 xx = x + test_xy[start_test + i][0];
5418 yy = y + test_xy[start_test + i][1];
5420 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5421 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5423 new_move_dir = move_dir;
5428 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5431 move_dir_preference = -1 * RunnerVisit[xx][yy];
5432 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5433 move_dir_preference = PlayerVisit[xx][yy];
5435 if (move_dir_preference > move_preference)
5437 /* prefer field that has not been visited for the longest time */
5438 move_preference = move_dir_preference;
5439 new_move_dir = move_dir;
5441 else if (move_dir_preference == move_preference &&
5442 move_dir == old_move_dir)
5444 /* prefer last direction when all directions are preferred equally */
5445 move_preference = move_dir_preference;
5446 new_move_dir = move_dir;
5450 MovDir[x][y] = new_move_dir;
5451 if (old_move_dir != new_move_dir)
5452 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5456 static void TurnRound(int x, int y)
5458 int direction = MovDir[x][y];
5462 GfxDir[x][y] = MovDir[x][y];
5464 if (direction != MovDir[x][y])
5468 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5470 ResetGfxFrame(x, y, FALSE);
5473 static boolean JustBeingPushed(int x, int y)
5477 for (i = 0; i < MAX_PLAYERS; i++)
5479 struct PlayerInfo *player = &stored_player[i];
5481 if (player->active && player->is_pushing && player->MovPos)
5483 int next_jx = player->jx + (player->jx - player->last_jx);
5484 int next_jy = player->jy + (player->jy - player->last_jy);
5486 if (x == next_jx && y == next_jy)
5494 void StartMoving(int x, int y)
5496 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5497 int element = Feld[x][y];
5502 if (MovDelay[x][y] == 0)
5503 GfxAction[x][y] = ACTION_DEFAULT;
5505 if (CAN_FALL(element) && y < lev_fieldy - 1)
5507 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5508 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5509 if (JustBeingPushed(x, y))
5512 if (element == EL_QUICKSAND_FULL)
5514 if (IS_FREE(x, y + 1))
5516 InitMovingField(x, y, MV_DOWN);
5517 started_moving = TRUE;
5519 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5520 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5521 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5522 Store[x][y] = EL_ROCK;
5524 Store[x][y] = EL_ROCK;
5527 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5529 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5531 if (!MovDelay[x][y])
5532 MovDelay[x][y] = TILEY + 1;
5541 Feld[x][y] = EL_QUICKSAND_EMPTY;
5542 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5543 Store[x][y + 1] = Store[x][y];
5546 PlayLevelSoundAction(x, y, ACTION_FILLING);
5549 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5550 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5552 InitMovingField(x, y, MV_DOWN);
5553 started_moving = TRUE;
5555 Feld[x][y] = EL_QUICKSAND_FILLING;
5556 Store[x][y] = element;
5558 PlayLevelSoundAction(x, y, ACTION_FILLING);
5560 else if (element == EL_MAGIC_WALL_FULL)
5562 if (IS_FREE(x, y + 1))
5564 InitMovingField(x, y, MV_DOWN);
5565 started_moving = TRUE;
5567 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5568 Store[x][y] = EL_CHANGED(Store[x][y]);
5570 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5572 if (!MovDelay[x][y])
5573 MovDelay[x][y] = TILEY/4 + 1;
5582 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5583 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5584 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5588 else if (element == EL_BD_MAGIC_WALL_FULL)
5590 if (IS_FREE(x, y + 1))
5592 InitMovingField(x, y, MV_DOWN);
5593 started_moving = TRUE;
5595 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5596 Store[x][y] = EL_CHANGED2(Store[x][y]);
5598 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5600 if (!MovDelay[x][y])
5601 MovDelay[x][y] = TILEY/4 + 1;
5610 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5611 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5612 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5616 else if (CAN_PASS_MAGIC_WALL(element) &&
5617 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5618 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5620 InitMovingField(x, y, MV_DOWN);
5621 started_moving = TRUE;
5624 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5625 EL_BD_MAGIC_WALL_FILLING);
5626 Store[x][y] = element;
5628 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5630 SplashAcid(x, y + 1);
5632 InitMovingField(x, y, MV_DOWN);
5633 started_moving = TRUE;
5635 Store[x][y] = EL_ACID;
5637 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5638 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5640 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5641 CAN_FALL(element) && WasJustFalling[x][y] &&
5642 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5644 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5645 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5646 (Feld[x][y + 1] == EL_BLOCKED)))
5648 /* this is needed for a special case not covered by calling "Impact()"
5649 from "ContinueMoving()": if an element moves to a tile directly below
5650 another element which was just falling on that tile (which was empty
5651 in the previous frame), the falling element above would just stop
5652 instead of smashing the element below (in previous version, the above
5653 element was just checked for "moving" instead of "falling", resulting
5654 in incorrect smashes caused by horizontal movement of the above
5655 element; also, the case of the player being the element to smash was
5656 simply not covered here... :-/ ) */
5658 CheckCollision[x][y] = 0;
5662 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5664 if (MovDir[x][y] == MV_NONE)
5666 InitMovingField(x, y, MV_DOWN);
5667 started_moving = TRUE;
5670 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5672 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5673 MovDir[x][y] = MV_DOWN;
5675 InitMovingField(x, y, MV_DOWN);
5676 started_moving = TRUE;
5678 else if (element == EL_AMOEBA_DROP)
5680 Feld[x][y] = EL_AMOEBA_GROWING;
5681 Store[x][y] = EL_AMOEBA_WET;
5683 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5684 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5685 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5686 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5688 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5689 (IS_FREE(x - 1, y + 1) ||
5690 Feld[x - 1][y + 1] == EL_ACID));
5691 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5692 (IS_FREE(x + 1, y + 1) ||
5693 Feld[x + 1][y + 1] == EL_ACID));
5694 boolean can_fall_any = (can_fall_left || can_fall_right);
5695 boolean can_fall_both = (can_fall_left && can_fall_right);
5696 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5698 #if USE_NEW_ALL_SLIPPERY
5699 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5701 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5702 can_fall_right = FALSE;
5703 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5704 can_fall_left = FALSE;
5705 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5706 can_fall_right = FALSE;
5707 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5708 can_fall_left = FALSE;
5710 can_fall_any = (can_fall_left || can_fall_right);
5711 can_fall_both = FALSE;
5714 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5716 if (slippery_type == SLIPPERY_ONLY_LEFT)
5717 can_fall_right = FALSE;
5718 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5719 can_fall_left = FALSE;
5720 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5721 can_fall_right = FALSE;
5722 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5723 can_fall_left = FALSE;
5725 can_fall_any = (can_fall_left || can_fall_right);
5726 can_fall_both = (can_fall_left && can_fall_right);
5730 #if USE_NEW_ALL_SLIPPERY
5732 #if USE_NEW_SP_SLIPPERY
5733 /* !!! better use the same properties as for custom elements here !!! */
5734 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5735 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5737 can_fall_right = FALSE; /* slip down on left side */
5738 can_fall_both = FALSE;
5743 #if USE_NEW_ALL_SLIPPERY
5746 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5747 can_fall_right = FALSE; /* slip down on left side */
5749 can_fall_left = !(can_fall_right = RND(2));
5751 can_fall_both = FALSE;
5756 if (game.emulation == EMU_BOULDERDASH ||
5757 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5758 can_fall_right = FALSE; /* slip down on left side */
5760 can_fall_left = !(can_fall_right = RND(2));
5762 can_fall_both = FALSE;
5768 /* if not determined otherwise, prefer left side for slipping down */
5769 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5770 started_moving = TRUE;
5774 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5776 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5779 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5780 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5781 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5782 int belt_dir = game.belt_dir[belt_nr];
5784 if ((belt_dir == MV_LEFT && left_is_free) ||
5785 (belt_dir == MV_RIGHT && right_is_free))
5787 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5789 InitMovingField(x, y, belt_dir);
5790 started_moving = TRUE;
5792 Pushed[x][y] = TRUE;
5793 Pushed[nextx][y] = TRUE;
5795 GfxAction[x][y] = ACTION_DEFAULT;
5799 MovDir[x][y] = 0; /* if element was moving, stop it */
5804 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5806 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5808 if (CAN_MOVE(element) && !started_moving)
5811 int move_pattern = element_info[element].move_pattern;
5816 if (MovDir[x][y] == MV_NONE)
5818 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5819 x, y, element, element_info[element].token_name);
5820 printf("StartMoving(): This should never happen!\n");
5825 Moving2Blocked(x, y, &newx, &newy);
5827 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5830 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5831 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5833 WasJustMoving[x][y] = 0;
5834 CheckCollision[x][y] = 0;
5836 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5838 if (Feld[x][y] != element) /* element has changed */
5842 if (!MovDelay[x][y]) /* start new movement phase */
5844 /* all objects that can change their move direction after each step
5845 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5847 if (element != EL_YAMYAM &&
5848 element != EL_DARK_YAMYAM &&
5849 element != EL_PACMAN &&
5850 !(move_pattern & MV_ANY_DIRECTION) &&
5851 move_pattern != MV_TURNING_LEFT &&
5852 move_pattern != MV_TURNING_RIGHT &&
5853 move_pattern != MV_TURNING_LEFT_RIGHT &&
5854 move_pattern != MV_TURNING_RIGHT_LEFT &&
5855 move_pattern != MV_TURNING_RANDOM)
5859 if (MovDelay[x][y] && (element == EL_BUG ||
5860 element == EL_SPACESHIP ||
5861 element == EL_SP_SNIKSNAK ||
5862 element == EL_SP_ELECTRON ||
5863 element == EL_MOLE))
5864 DrawLevelField(x, y);
5868 if (MovDelay[x][y]) /* wait some time before next movement */
5872 if (element == EL_ROBOT ||
5873 element == EL_YAMYAM ||
5874 element == EL_DARK_YAMYAM)
5876 DrawLevelElementAnimationIfNeeded(x, y, element);
5877 PlayLevelSoundAction(x, y, ACTION_WAITING);
5879 else if (element == EL_SP_ELECTRON)
5880 DrawLevelElementAnimationIfNeeded(x, y, element);
5881 else if (element == EL_DRAGON)
5884 int dir = MovDir[x][y];
5885 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5886 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5887 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5888 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5889 dir == MV_UP ? IMG_FLAMES_1_UP :
5890 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5891 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5893 GfxAction[x][y] = ACTION_ATTACKING;
5895 if (IS_PLAYER(x, y))
5896 DrawPlayerField(x, y);
5898 DrawLevelField(x, y);
5900 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5902 for (i = 1; i <= 3; i++)
5904 int xx = x + i * dx;
5905 int yy = y + i * dy;
5906 int sx = SCREENX(xx);
5907 int sy = SCREENY(yy);
5908 int flame_graphic = graphic + (i - 1);
5910 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5915 int flamed = MovingOrBlocked2Element(xx, yy);
5919 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5921 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5922 RemoveMovingField(xx, yy);
5924 RemoveField(xx, yy);
5926 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5929 RemoveMovingField(xx, yy);
5932 ChangeDelay[xx][yy] = 0;
5934 Feld[xx][yy] = EL_FLAMES;
5936 if (IN_SCR_FIELD(sx, sy))
5938 DrawLevelFieldCrumbledSand(xx, yy);
5939 DrawGraphic(sx, sy, flame_graphic, frame);
5944 if (Feld[xx][yy] == EL_FLAMES)
5945 Feld[xx][yy] = EL_EMPTY;
5946 DrawLevelField(xx, yy);
5951 if (MovDelay[x][y]) /* element still has to wait some time */
5953 PlayLevelSoundAction(x, y, ACTION_WAITING);
5959 /* now make next step */
5961 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5963 if (DONT_COLLIDE_WITH(element) &&
5964 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5965 !PLAYER_ENEMY_PROTECTED(newx, newy))
5967 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5972 else if (CAN_MOVE_INTO_ACID(element) &&
5973 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5974 !IS_MV_DIAGONAL(MovDir[x][y]) &&
5975 (MovDir[x][y] == MV_DOWN ||
5976 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5978 SplashAcid(newx, newy);
5979 Store[x][y] = EL_ACID;
5981 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5983 if (Feld[newx][newy] == EL_EXIT_OPEN)
5986 DrawLevelField(x, y);
5988 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5989 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5990 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5992 local_player->friends_still_needed--;
5993 if (!local_player->friends_still_needed &&
5994 !local_player->GameOver && AllPlayersGone)
5995 PlayerWins(local_player);
5999 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6001 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6002 DrawLevelField(newx, newy);
6004 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6006 else if (!IS_FREE(newx, newy))
6008 GfxAction[x][y] = ACTION_WAITING;
6010 if (IS_PLAYER(x, y))
6011 DrawPlayerField(x, y);
6013 DrawLevelField(x, y);
6018 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6020 if (IS_FOOD_PIG(Feld[newx][newy]))
6022 if (IS_MOVING(newx, newy))
6023 RemoveMovingField(newx, newy);
6026 Feld[newx][newy] = EL_EMPTY;
6027 DrawLevelField(newx, newy);
6030 PlayLevelSound(x, y, SND_PIG_DIGGING);
6032 else if (!IS_FREE(newx, newy))
6034 if (IS_PLAYER(x, y))
6035 DrawPlayerField(x, y);
6037 DrawLevelField(x, y);
6042 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6044 if (Store[x][y] != EL_EMPTY)
6046 boolean can_clone = FALSE;
6049 /* check if element to clone is still there */
6050 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6052 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6060 /* cannot clone or target field not free anymore -- do not clone */
6061 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6062 Store[x][y] = EL_EMPTY;
6065 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6067 if (IS_MV_DIAGONAL(MovDir[x][y]))
6069 int diagonal_move_dir = MovDir[x][y];
6070 int stored = Store[x][y];
6071 int change_delay = 8;
6074 /* android is moving diagonally */
6076 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6078 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6079 GfxElement[x][y] = EL_EMC_ANDROID;
6080 GfxAction[x][y] = ACTION_SHRINKING;
6081 GfxDir[x][y] = diagonal_move_dir;
6082 ChangeDelay[x][y] = change_delay;
6084 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6087 DrawLevelGraphicAnimation(x, y, graphic);
6088 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6090 if (Feld[newx][newy] == EL_ACID)
6092 SplashAcid(newx, newy);
6097 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6099 Store[newx][newy] = EL_EMC_ANDROID;
6100 GfxElement[newx][newy] = EL_EMC_ANDROID;
6101 GfxAction[newx][newy] = ACTION_GROWING;
6102 GfxDir[newx][newy] = diagonal_move_dir;
6103 ChangeDelay[newx][newy] = change_delay;
6105 graphic = el_act_dir2img(GfxElement[newx][newy],
6106 GfxAction[newx][newy], GfxDir[newx][newy]);
6108 DrawLevelGraphicAnimation(newx, newy, graphic);
6109 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6115 Feld[newx][newy] = EL_EMPTY;
6116 DrawLevelField(newx, newy);
6118 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6121 else if (!IS_FREE(newx, newy))
6124 if (IS_PLAYER(x, y))
6125 DrawPlayerField(x, y);
6127 DrawLevelField(x, y);
6133 else if (IS_CUSTOM_ELEMENT(element) &&
6134 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6136 int new_element = Feld[newx][newy];
6138 if (!IS_FREE(newx, newy))
6140 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6141 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6144 /* no element can dig solid indestructible elements */
6145 if (IS_INDESTRUCTIBLE(new_element) &&
6146 !IS_DIGGABLE(new_element) &&
6147 !IS_COLLECTIBLE(new_element))
6150 if (AmoebaNr[newx][newy] &&
6151 (new_element == EL_AMOEBA_FULL ||
6152 new_element == EL_BD_AMOEBA ||
6153 new_element == EL_AMOEBA_GROWING))
6155 AmoebaCnt[AmoebaNr[newx][newy]]--;
6156 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6159 if (IS_MOVING(newx, newy))
6160 RemoveMovingField(newx, newy);
6163 RemoveField(newx, newy);
6164 DrawLevelField(newx, newy);
6167 /* if digged element was about to explode, prevent the explosion */
6168 ExplodeField[newx][newy] = EX_TYPE_NONE;
6170 PlayLevelSoundAction(x, y, action);
6173 Store[newx][newy] = EL_EMPTY;
6175 /* this makes it possible to leave the removed element again */
6176 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6177 Store[newx][newy] = new_element;
6179 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6181 int move_leave_element = element_info[element].move_leave_element;
6183 /* this makes it possible to leave the removed element again */
6184 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6185 new_element : move_leave_element);
6189 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6191 RunnerVisit[x][y] = FrameCounter;
6192 PlayerVisit[x][y] /= 8; /* expire player visit path */
6195 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6197 if (!IS_FREE(newx, newy))
6199 if (IS_PLAYER(x, y))
6200 DrawPlayerField(x, y);
6202 DrawLevelField(x, y);
6208 boolean wanna_flame = !RND(10);
6209 int dx = newx - x, dy = newy - y;
6210 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6211 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6212 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6213 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6214 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6215 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6218 IS_CLASSIC_ENEMY(element1) ||
6219 IS_CLASSIC_ENEMY(element2)) &&
6220 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6221 element1 != EL_FLAMES && element2 != EL_FLAMES)
6223 ResetGfxAnimation(x, y);
6224 GfxAction[x][y] = ACTION_ATTACKING;
6226 if (IS_PLAYER(x, y))
6227 DrawPlayerField(x, y);
6229 DrawLevelField(x, y);
6231 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6233 MovDelay[x][y] = 50;
6237 RemoveField(newx, newy);
6239 Feld[newx][newy] = EL_FLAMES;
6240 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6243 RemoveField(newx1, newy1);
6245 Feld[newx1][newy1] = EL_FLAMES;
6247 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6250 RemoveField(newx2, newy2);
6252 Feld[newx2][newy2] = EL_FLAMES;
6259 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6260 Feld[newx][newy] == EL_DIAMOND)
6262 if (IS_MOVING(newx, newy))
6263 RemoveMovingField(newx, newy);
6266 Feld[newx][newy] = EL_EMPTY;
6267 DrawLevelField(newx, newy);
6270 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6272 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6273 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6275 if (AmoebaNr[newx][newy])
6277 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6278 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6279 Feld[newx][newy] == EL_BD_AMOEBA)
6280 AmoebaCnt[AmoebaNr[newx][newy]]--;
6285 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6287 RemoveMovingField(newx, newy);
6290 if (IS_MOVING(newx, newy))
6292 RemoveMovingField(newx, newy);
6297 Feld[newx][newy] = EL_EMPTY;
6298 DrawLevelField(newx, newy);
6301 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6303 else if ((element == EL_PACMAN || element == EL_MOLE)
6304 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6306 if (AmoebaNr[newx][newy])
6308 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6309 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6310 Feld[newx][newy] == EL_BD_AMOEBA)
6311 AmoebaCnt[AmoebaNr[newx][newy]]--;
6314 if (element == EL_MOLE)
6316 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6317 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6319 ResetGfxAnimation(x, y);
6320 GfxAction[x][y] = ACTION_DIGGING;
6321 DrawLevelField(x, y);
6323 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6325 return; /* wait for shrinking amoeba */
6327 else /* element == EL_PACMAN */
6329 Feld[newx][newy] = EL_EMPTY;
6330 DrawLevelField(newx, newy);
6331 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6334 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6335 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6336 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6338 /* wait for shrinking amoeba to completely disappear */
6341 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6343 /* object was running against a wall */
6348 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6349 if (move_pattern & MV_ANY_DIRECTION &&
6350 move_pattern == MovDir[x][y])
6352 int blocking_element =
6353 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6355 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6358 element = Feld[x][y]; /* element might have changed */
6362 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6363 DrawLevelElementAnimation(x, y, element);
6365 if (DONT_TOUCH(element))
6366 TestIfBadThingTouchesPlayer(x, y);
6371 InitMovingField(x, y, MovDir[x][y]);
6373 PlayLevelSoundAction(x, y, ACTION_MOVING);
6377 ContinueMoving(x, y);
6380 void ContinueMoving(int x, int y)
6382 int element = Feld[x][y];
6383 struct ElementInfo *ei = &element_info[element];
6384 int direction = MovDir[x][y];
6385 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6386 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6387 int newx = x + dx, newy = y + dy;
6388 int stored = Store[x][y];
6389 int stored_new = Store[newx][newy];
6390 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6391 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6392 boolean last_line = (newy == lev_fieldy - 1);
6394 MovPos[x][y] += getElementMoveStepsize(x, y);
6396 if (pushed_by_player) /* special case: moving object pushed by player */
6397 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6399 if (ABS(MovPos[x][y]) < TILEX)
6401 DrawLevelField(x, y);
6403 return; /* element is still moving */
6406 /* element reached destination field */
6408 Feld[x][y] = EL_EMPTY;
6409 Feld[newx][newy] = element;
6410 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6412 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6414 element = Feld[newx][newy] = EL_ACID;
6416 else if (element == EL_MOLE)
6418 Feld[x][y] = EL_SAND;
6420 DrawLevelFieldCrumbledSandNeighbours(x, y);
6422 else if (element == EL_QUICKSAND_FILLING)
6424 element = Feld[newx][newy] = get_next_element(element);
6425 Store[newx][newy] = Store[x][y];
6427 else if (element == EL_QUICKSAND_EMPTYING)
6429 Feld[x][y] = get_next_element(element);
6430 element = Feld[newx][newy] = Store[x][y];
6432 else if (element == EL_MAGIC_WALL_FILLING)
6434 element = Feld[newx][newy] = get_next_element(element);
6435 if (!game.magic_wall_active)
6436 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6437 Store[newx][newy] = Store[x][y];
6439 else if (element == EL_MAGIC_WALL_EMPTYING)
6441 Feld[x][y] = get_next_element(element);
6442 if (!game.magic_wall_active)
6443 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6444 element = Feld[newx][newy] = Store[x][y];
6446 #if USE_NEW_CUSTOM_VALUE
6447 InitField(newx, newy, FALSE);
6450 else if (element == EL_BD_MAGIC_WALL_FILLING)
6452 element = Feld[newx][newy] = get_next_element(element);
6453 if (!game.magic_wall_active)
6454 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6455 Store[newx][newy] = Store[x][y];
6457 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6459 Feld[x][y] = get_next_element(element);
6460 if (!game.magic_wall_active)
6461 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6462 element = Feld[newx][newy] = Store[x][y];
6464 #if USE_NEW_CUSTOM_VALUE
6465 InitField(newx, newy, FALSE);
6468 else if (element == EL_AMOEBA_DROPPING)
6470 Feld[x][y] = get_next_element(element);
6471 element = Feld[newx][newy] = Store[x][y];
6473 else if (element == EL_SOKOBAN_OBJECT)
6476 Feld[x][y] = Back[x][y];
6478 if (Back[newx][newy])
6479 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6481 Back[x][y] = Back[newx][newy] = 0;
6484 Store[x][y] = EL_EMPTY;
6489 MovDelay[newx][newy] = 0;
6491 if (CAN_CHANGE_OR_HAS_ACTION(element))
6493 /* copy element change control values to new field */
6494 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6495 ChangePage[newx][newy] = ChangePage[x][y];
6496 ChangeCount[newx][newy] = ChangeCount[x][y];
6497 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6500 #if USE_NEW_CUSTOM_VALUE
6501 CustomValue[newx][newy] = CustomValue[x][y];
6504 ChangeDelay[x][y] = 0;
6505 ChangePage[x][y] = -1;
6506 ChangeCount[x][y] = 0;
6507 ChangeEvent[x][y] = -1;
6509 #if USE_NEW_CUSTOM_VALUE
6510 CustomValue[x][y] = 0;
6513 /* copy animation control values to new field */
6514 GfxFrame[newx][newy] = GfxFrame[x][y];
6515 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6516 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6517 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6519 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6521 /* some elements can leave other elements behind after moving */
6523 if (ei->move_leave_element != EL_EMPTY &&
6524 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6525 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6527 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6528 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6529 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6532 int move_leave_element = ei->move_leave_element;
6536 /* this makes it possible to leave the removed element again */
6537 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6538 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6540 /* this makes it possible to leave the removed element again */
6541 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6542 move_leave_element = stored;
6545 /* this makes it possible to leave the removed element again */
6546 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6547 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6548 move_leave_element = stored;
6551 Feld[x][y] = move_leave_element;
6553 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6554 MovDir[x][y] = direction;
6556 InitField(x, y, FALSE);
6558 if (GFX_CRUMBLED(Feld[x][y]))
6559 DrawLevelFieldCrumbledSandNeighbours(x, y);
6561 if (ELEM_IS_PLAYER(move_leave_element))
6562 RelocatePlayer(x, y, move_leave_element);
6565 /* do this after checking for left-behind element */
6566 ResetGfxAnimation(x, y); /* reset animation values for old field */
6568 if (!CAN_MOVE(element) ||
6569 (CAN_FALL(element) && direction == MV_DOWN &&
6570 (element == EL_SPRING ||
6571 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6572 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6573 GfxDir[x][y] = MovDir[newx][newy] = 0;
6575 DrawLevelField(x, y);
6576 DrawLevelField(newx, newy);
6578 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6580 /* prevent pushed element from moving on in pushed direction */
6581 if (pushed_by_player && CAN_MOVE(element) &&
6582 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6583 !(element_info[element].move_pattern & direction))
6584 TurnRound(newx, newy);
6586 /* prevent elements on conveyor belt from moving on in last direction */
6587 if (pushed_by_conveyor && CAN_FALL(element) &&
6588 direction & MV_HORIZONTAL)
6589 MovDir[newx][newy] = 0;
6591 if (!pushed_by_player)
6593 int nextx = newx + dx, nexty = newy + dy;
6594 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6596 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6598 if (CAN_FALL(element) && direction == MV_DOWN)
6599 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6601 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6602 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6605 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6607 TestIfBadThingTouchesPlayer(newx, newy);
6608 TestIfBadThingTouchesFriend(newx, newy);
6610 if (!IS_CUSTOM_ELEMENT(element))
6611 TestIfBadThingTouchesOtherBadThing(newx, newy);
6613 else if (element == EL_PENGUIN)
6614 TestIfFriendTouchesBadThing(newx, newy);
6616 /* give the player one last chance (one more frame) to move away */
6617 if (CAN_FALL(element) && direction == MV_DOWN &&
6618 (last_line || (!IS_FREE(x, newy + 1) &&
6619 (!IS_PLAYER(x, newy + 1) ||
6620 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6623 if (pushed_by_player && !game.use_change_when_pushing_bug)
6625 int push_side = MV_DIR_OPPOSITE(direction);
6626 struct PlayerInfo *player = PLAYERINFO(x, y);
6628 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6629 player->index_bit, push_side);
6630 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6631 player->index_bit, push_side);
6634 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6635 MovDelay[newx][newy] = 1;
6637 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6639 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6642 if (ChangePage[newx][newy] != -1) /* delayed change */
6644 int page = ChangePage[newx][newy];
6645 struct ElementChangeInfo *change = &ei->change_page[page];
6647 ChangePage[newx][newy] = -1;
6649 if (change->can_change)
6651 if (ChangeElement(newx, newy, element, page))
6653 if (change->post_change_function)
6654 change->post_change_function(newx, newy);
6658 if (change->has_action)
6659 ExecuteCustomElementAction(newx, newy, element, page);
6663 TestIfElementHitsCustomElement(newx, newy, direction);
6664 TestIfPlayerTouchesCustomElement(newx, newy);
6665 TestIfElementTouchesCustomElement(newx, newy);
6667 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6668 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6669 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6670 MV_DIR_OPPOSITE(direction));
6673 int AmoebeNachbarNr(int ax, int ay)
6676 int element = Feld[ax][ay];
6678 static int xy[4][2] =
6686 for (i = 0; i < NUM_DIRECTIONS; i++)
6688 int x = ax + xy[i][0];
6689 int y = ay + xy[i][1];
6691 if (!IN_LEV_FIELD(x, y))
6694 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6695 group_nr = AmoebaNr[x][y];
6701 void AmoebenVereinigen(int ax, int ay)
6703 int i, x, y, xx, yy;
6704 int new_group_nr = AmoebaNr[ax][ay];
6705 static int xy[4][2] =
6713 if (new_group_nr == 0)
6716 for (i = 0; i < NUM_DIRECTIONS; i++)
6721 if (!IN_LEV_FIELD(x, y))
6724 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6725 Feld[x][y] == EL_BD_AMOEBA ||
6726 Feld[x][y] == EL_AMOEBA_DEAD) &&
6727 AmoebaNr[x][y] != new_group_nr)
6729 int old_group_nr = AmoebaNr[x][y];
6731 if (old_group_nr == 0)
6734 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6735 AmoebaCnt[old_group_nr] = 0;
6736 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6737 AmoebaCnt2[old_group_nr] = 0;
6739 SCAN_PLAYFIELD(xx, yy)
6741 if (AmoebaNr[xx][yy] == old_group_nr)
6742 AmoebaNr[xx][yy] = new_group_nr;
6748 void AmoebeUmwandeln(int ax, int ay)
6752 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6754 int group_nr = AmoebaNr[ax][ay];
6759 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6760 printf("AmoebeUmwandeln(): This should never happen!\n");
6765 SCAN_PLAYFIELD(x, y)
6767 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6770 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6774 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6775 SND_AMOEBA_TURNING_TO_GEM :
6776 SND_AMOEBA_TURNING_TO_ROCK));
6781 static int xy[4][2] =
6789 for (i = 0; i < NUM_DIRECTIONS; i++)
6794 if (!IN_LEV_FIELD(x, y))
6797 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6799 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6800 SND_AMOEBA_TURNING_TO_GEM :
6801 SND_AMOEBA_TURNING_TO_ROCK));
6808 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6811 int group_nr = AmoebaNr[ax][ay];
6812 boolean done = FALSE;
6817 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6818 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6823 SCAN_PLAYFIELD(x, y)
6825 if (AmoebaNr[x][y] == group_nr &&
6826 (Feld[x][y] == EL_AMOEBA_DEAD ||
6827 Feld[x][y] == EL_BD_AMOEBA ||
6828 Feld[x][y] == EL_AMOEBA_GROWING))
6831 Feld[x][y] = new_element;
6832 InitField(x, y, FALSE);
6833 DrawLevelField(x, y);
6839 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6840 SND_BD_AMOEBA_TURNING_TO_ROCK :
6841 SND_BD_AMOEBA_TURNING_TO_GEM));
6844 void AmoebeWaechst(int x, int y)
6846 static unsigned long sound_delay = 0;
6847 static unsigned long sound_delay_value = 0;
6849 if (!MovDelay[x][y]) /* start new growing cycle */
6853 if (DelayReached(&sound_delay, sound_delay_value))
6855 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6856 sound_delay_value = 30;
6860 if (MovDelay[x][y]) /* wait some time before growing bigger */
6863 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6865 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6866 6 - MovDelay[x][y]);
6868 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6871 if (!MovDelay[x][y])
6873 Feld[x][y] = Store[x][y];
6875 DrawLevelField(x, y);
6880 void AmoebaDisappearing(int x, int y)
6882 static unsigned long sound_delay = 0;
6883 static unsigned long sound_delay_value = 0;
6885 if (!MovDelay[x][y]) /* start new shrinking cycle */
6889 if (DelayReached(&sound_delay, sound_delay_value))
6890 sound_delay_value = 30;
6893 if (MovDelay[x][y]) /* wait some time before shrinking */
6896 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6898 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6899 6 - MovDelay[x][y]);
6901 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6904 if (!MovDelay[x][y])
6906 Feld[x][y] = EL_EMPTY;
6907 DrawLevelField(x, y);
6909 /* don't let mole enter this field in this cycle;
6910 (give priority to objects falling to this field from above) */
6916 void AmoebeAbleger(int ax, int ay)
6919 int element = Feld[ax][ay];
6920 int graphic = el2img(element);
6921 int newax = ax, neway = ay;
6922 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6923 static int xy[4][2] =
6931 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6933 Feld[ax][ay] = EL_AMOEBA_DEAD;
6934 DrawLevelField(ax, ay);
6938 if (IS_ANIMATED(graphic))
6939 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6941 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6942 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6944 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6947 if (MovDelay[ax][ay])
6951 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6954 int x = ax + xy[start][0];
6955 int y = ay + xy[start][1];
6957 if (!IN_LEV_FIELD(x, y))
6960 if (IS_FREE(x, y) ||
6961 CAN_GROW_INTO(Feld[x][y]) ||
6962 Feld[x][y] == EL_QUICKSAND_EMPTY)
6968 if (newax == ax && neway == ay)
6971 else /* normal or "filled" (BD style) amoeba */
6974 boolean waiting_for_player = FALSE;
6976 for (i = 0; i < NUM_DIRECTIONS; i++)
6978 int j = (start + i) % 4;
6979 int x = ax + xy[j][0];
6980 int y = ay + xy[j][1];
6982 if (!IN_LEV_FIELD(x, y))
6985 if (IS_FREE(x, y) ||
6986 CAN_GROW_INTO(Feld[x][y]) ||
6987 Feld[x][y] == EL_QUICKSAND_EMPTY)
6993 else if (IS_PLAYER(x, y))
6994 waiting_for_player = TRUE;
6997 if (newax == ax && neway == ay) /* amoeba cannot grow */
6999 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7001 Feld[ax][ay] = EL_AMOEBA_DEAD;
7002 DrawLevelField(ax, ay);
7003 AmoebaCnt[AmoebaNr[ax][ay]]--;
7005 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7007 if (element == EL_AMOEBA_FULL)
7008 AmoebeUmwandeln(ax, ay);
7009 else if (element == EL_BD_AMOEBA)
7010 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7015 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7017 /* amoeba gets larger by growing in some direction */
7019 int new_group_nr = AmoebaNr[ax][ay];
7022 if (new_group_nr == 0)
7024 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7025 printf("AmoebeAbleger(): This should never happen!\n");
7030 AmoebaNr[newax][neway] = new_group_nr;
7031 AmoebaCnt[new_group_nr]++;
7032 AmoebaCnt2[new_group_nr]++;
7034 /* if amoeba touches other amoeba(s) after growing, unify them */
7035 AmoebenVereinigen(newax, neway);
7037 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7039 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7045 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7046 (neway == lev_fieldy - 1 && newax != ax))
7048 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7049 Store[newax][neway] = element;
7051 else if (neway == ay || element == EL_EMC_DRIPPER)
7053 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7055 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7059 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7060 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7061 Store[ax][ay] = EL_AMOEBA_DROP;
7062 ContinueMoving(ax, ay);
7066 DrawLevelField(newax, neway);
7069 void Life(int ax, int ay)
7073 int element = Feld[ax][ay];
7074 int graphic = el2img(element);
7075 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7077 boolean changed = FALSE;
7079 if (IS_ANIMATED(graphic))
7080 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7085 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7086 MovDelay[ax][ay] = life_time;
7088 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7091 if (MovDelay[ax][ay])
7095 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7097 int xx = ax+x1, yy = ay+y1;
7100 if (!IN_LEV_FIELD(xx, yy))
7103 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7105 int x = xx+x2, y = yy+y2;
7107 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7110 if (((Feld[x][y] == element ||
7111 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7113 (IS_FREE(x, y) && Stop[x][y]))
7117 if (xx == ax && yy == ay) /* field in the middle */
7119 if (nachbarn < life_parameter[0] ||
7120 nachbarn > life_parameter[1])
7122 Feld[xx][yy] = EL_EMPTY;
7124 DrawLevelField(xx, yy);
7125 Stop[xx][yy] = TRUE;
7129 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7130 { /* free border field */
7131 if (nachbarn >= life_parameter[2] &&
7132 nachbarn <= life_parameter[3])
7134 Feld[xx][yy] = element;
7135 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7137 DrawLevelField(xx, yy);
7138 Stop[xx][yy] = TRUE;
7145 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7146 SND_GAME_OF_LIFE_GROWING);
7149 static void InitRobotWheel(int x, int y)
7151 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7154 static void RunRobotWheel(int x, int y)
7156 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7159 static void StopRobotWheel(int x, int y)
7161 if (ZX == x && ZY == y)
7165 static void InitTimegateWheel(int x, int y)
7167 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7170 static void RunTimegateWheel(int x, int y)
7172 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7175 static void InitMagicBallDelay(int x, int y)
7178 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7180 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7184 static void ActivateMagicBall(int bx, int by)
7188 if (level.ball_random)
7190 int pos_border = RND(8); /* select one of the eight border elements */
7191 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7192 int xx = pos_content % 3;
7193 int yy = pos_content / 3;
7198 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7199 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7203 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7205 int xx = x - bx + 1;
7206 int yy = y - by + 1;
7208 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7209 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7213 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7216 void CheckExit(int x, int y)
7218 if (local_player->gems_still_needed > 0 ||
7219 local_player->sokobanfields_still_needed > 0 ||
7220 local_player->lights_still_needed > 0)
7222 int element = Feld[x][y];
7223 int graphic = el2img(element);
7225 if (IS_ANIMATED(graphic))
7226 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7231 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7234 Feld[x][y] = EL_EXIT_OPENING;
7236 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7239 void CheckExitSP(int x, int y)
7241 if (local_player->gems_still_needed > 0)
7243 int element = Feld[x][y];
7244 int graphic = el2img(element);
7246 if (IS_ANIMATED(graphic))
7247 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7252 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7255 Feld[x][y] = EL_SP_EXIT_OPENING;
7257 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7260 static void CloseAllOpenTimegates()
7264 SCAN_PLAYFIELD(x, y)
7266 int element = Feld[x][y];
7268 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7270 Feld[x][y] = EL_TIMEGATE_CLOSING;
7272 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7277 void DrawTwinkleOnField(int x, int y)
7279 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7282 if (Feld[x][y] == EL_BD_DIAMOND)
7285 if (MovDelay[x][y] == 0) /* next animation frame */
7286 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7288 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7292 if (setup.direct_draw && MovDelay[x][y])
7293 SetDrawtoField(DRAW_BUFFERED);
7295 DrawLevelElementAnimation(x, y, Feld[x][y]);
7297 if (MovDelay[x][y] != 0)
7299 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7300 10 - MovDelay[x][y]);
7302 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7304 if (setup.direct_draw)
7308 dest_x = FX + SCREENX(x) * TILEX;
7309 dest_y = FY + SCREENY(y) * TILEY;
7311 BlitBitmap(drawto_field, window,
7312 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7313 SetDrawtoField(DRAW_DIRECT);
7319 void MauerWaechst(int x, int y)
7323 if (!MovDelay[x][y]) /* next animation frame */
7324 MovDelay[x][y] = 3 * delay;
7326 if (MovDelay[x][y]) /* wait some time before next frame */
7330 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7332 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7333 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7335 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7338 if (!MovDelay[x][y])
7340 if (MovDir[x][y] == MV_LEFT)
7342 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7343 DrawLevelField(x - 1, y);
7345 else if (MovDir[x][y] == MV_RIGHT)
7347 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7348 DrawLevelField(x + 1, y);
7350 else if (MovDir[x][y] == MV_UP)
7352 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7353 DrawLevelField(x, y - 1);
7357 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7358 DrawLevelField(x, y + 1);
7361 Feld[x][y] = Store[x][y];
7363 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7364 DrawLevelField(x, y);
7369 void MauerAbleger(int ax, int ay)
7371 int element = Feld[ax][ay];
7372 int graphic = el2img(element);
7373 boolean oben_frei = FALSE, unten_frei = FALSE;
7374 boolean links_frei = FALSE, rechts_frei = FALSE;
7375 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7376 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7377 boolean new_wall = FALSE;
7379 if (IS_ANIMATED(graphic))
7380 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7382 if (!MovDelay[ax][ay]) /* start building new wall */
7383 MovDelay[ax][ay] = 6;
7385 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7388 if (MovDelay[ax][ay])
7392 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7394 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7396 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7398 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7401 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7402 element == EL_EXPANDABLE_WALL_ANY)
7406 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7407 Store[ax][ay-1] = element;
7408 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7409 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7410 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7411 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7416 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7417 Store[ax][ay+1] = element;
7418 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7419 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7420 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7421 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7426 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7427 element == EL_EXPANDABLE_WALL_ANY ||
7428 element == EL_EXPANDABLE_WALL ||
7429 element == EL_BD_EXPANDABLE_WALL)
7433 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7434 Store[ax-1][ay] = element;
7435 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7436 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7437 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7438 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7444 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7445 Store[ax+1][ay] = element;
7446 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7447 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7448 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7449 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7454 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7455 DrawLevelField(ax, ay);
7457 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7459 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7460 unten_massiv = TRUE;
7461 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7462 links_massiv = TRUE;
7463 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7464 rechts_massiv = TRUE;
7466 if (((oben_massiv && unten_massiv) ||
7467 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7468 element == EL_EXPANDABLE_WALL) &&
7469 ((links_massiv && rechts_massiv) ||
7470 element == EL_EXPANDABLE_WALL_VERTICAL))
7471 Feld[ax][ay] = EL_WALL;
7474 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7477 void CheckForDragon(int x, int y)
7480 boolean dragon_found = FALSE;
7481 static int xy[4][2] =
7489 for (i = 0; i < NUM_DIRECTIONS; i++)
7491 for (j = 0; j < 4; j++)
7493 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7495 if (IN_LEV_FIELD(xx, yy) &&
7496 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7498 if (Feld[xx][yy] == EL_DRAGON)
7499 dragon_found = TRUE;
7508 for (i = 0; i < NUM_DIRECTIONS; i++)
7510 for (j = 0; j < 3; j++)
7512 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7514 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7516 Feld[xx][yy] = EL_EMPTY;
7517 DrawLevelField(xx, yy);
7526 static void InitBuggyBase(int x, int y)
7528 int element = Feld[x][y];
7529 int activating_delay = FRAMES_PER_SECOND / 4;
7532 (element == EL_SP_BUGGY_BASE ?
7533 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7534 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7536 element == EL_SP_BUGGY_BASE_ACTIVE ?
7537 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7540 static void WarnBuggyBase(int x, int y)
7543 static int xy[4][2] =
7551 for (i = 0; i < NUM_DIRECTIONS; i++)
7553 int xx = x + xy[i][0];
7554 int yy = y + xy[i][1];
7556 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7558 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7565 static void InitTrap(int x, int y)
7567 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7570 static void ActivateTrap(int x, int y)
7572 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7575 static void ChangeActiveTrap(int x, int y)
7577 int graphic = IMG_TRAP_ACTIVE;
7579 /* if new animation frame was drawn, correct crumbled sand border */
7580 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7581 DrawLevelFieldCrumbledSand(x, y);
7584 static int getSpecialActionElement(int element, int number, int base_element)
7586 return (element != EL_EMPTY ? element :
7587 number != -1 ? base_element + number - 1 :
7591 static int getModifiedActionNumber(int value_old, int operator, int operand,
7592 int value_min, int value_max)
7594 int value_new = (operator == CA_MODE_SET ? operand :
7595 operator == CA_MODE_ADD ? value_old + operand :
7596 operator == CA_MODE_SUBTRACT ? value_old - operand :
7597 operator == CA_MODE_MULTIPLY ? value_old * operand :
7598 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7599 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7602 return (value_new < value_min ? value_min :
7603 value_new > value_max ? value_max :
7607 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7609 struct ElementInfo *ei = &element_info[element];
7610 struct ElementChangeInfo *change = &ei->change_page[page];
7611 int target_element = change->target_element;
7612 int action_type = change->action_type;
7613 int action_mode = change->action_mode;
7614 int action_arg = change->action_arg;
7617 if (!change->has_action)
7620 /* ---------- determine action paramater values -------------------------- */
7622 int level_time_value =
7623 (level.time > 0 ? TimeLeft :
7626 int action_arg_element =
7627 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7628 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7629 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7632 int action_arg_direction =
7633 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7634 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7635 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7636 change->actual_trigger_side :
7637 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7638 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7641 int action_arg_number_min =
7642 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7645 int action_arg_number_max =
7646 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7647 action_type == CA_SET_LEVEL_GEMS ? 999 :
7648 action_type == CA_SET_LEVEL_TIME ? 9999 :
7649 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7650 action_type == CA_SET_CE_VALUE ? 9999 :
7651 action_type == CA_SET_CE_SCORE ? 9999 :
7654 int action_arg_number_reset =
7655 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
7656 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7657 action_type == CA_SET_LEVEL_TIME ? level.time :
7658 action_type == CA_SET_LEVEL_SCORE ? 0 :
7659 #if USE_NEW_CUSTOM_VALUE
7660 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
7662 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7664 action_type == CA_SET_CE_SCORE ? 0 :
7667 int action_arg_number =
7668 (action_arg <= CA_ARG_MAX ? action_arg :
7669 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7670 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7671 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7672 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7673 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7674 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7675 #if USE_NEW_CUSTOM_VALUE
7676 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7678 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7680 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7681 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7682 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7683 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7684 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7685 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
7686 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7687 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
7688 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
7689 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7690 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7693 int action_arg_number_old =
7694 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7695 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7696 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7697 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7698 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7701 int action_arg_number_new =
7702 getModifiedActionNumber(action_arg_number_old,
7703 action_mode, action_arg_number,
7704 action_arg_number_min, action_arg_number_max);
7706 int trigger_player_bits =
7707 (change->actual_trigger_player >= EL_PLAYER_1 &&
7708 change->actual_trigger_player <= EL_PLAYER_4 ?
7709 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7712 int action_arg_player_bits =
7713 (action_arg >= CA_ARG_PLAYER_1 &&
7714 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7715 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7718 /* ---------- execute action -------------------------------------------- */
7720 switch (action_type)
7727 /* ---------- level actions ------------------------------------------- */
7729 case CA_RESTART_LEVEL:
7731 game.restart_level = TRUE;
7736 case CA_SHOW_ENVELOPE:
7738 int element = getSpecialActionElement(action_arg_element,
7739 action_arg_number, EL_ENVELOPE_1);
7741 if (IS_ENVELOPE(element))
7742 local_player->show_envelope = element;
7747 case CA_SET_LEVEL_TIME:
7749 if (level.time > 0) /* only modify limited time value */
7751 TimeLeft = action_arg_number_new;
7753 DrawGameValue_Time(TimeLeft);
7755 if (!TimeLeft && setup.time_limit)
7756 for (i = 0; i < MAX_PLAYERS; i++)
7757 KillPlayer(&stored_player[i]);
7763 case CA_SET_LEVEL_SCORE:
7765 local_player->score = action_arg_number_new;
7767 DrawGameValue_Score(local_player->score);
7772 case CA_SET_LEVEL_GEMS:
7774 local_player->gems_still_needed = action_arg_number_new;
7776 DrawGameValue_Emeralds(local_player->gems_still_needed);
7781 #if !USE_PLAYER_GRAVITY
7782 case CA_SET_LEVEL_GRAVITY:
7784 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7785 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7786 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7792 case CA_SET_LEVEL_WIND:
7794 game.wind_direction = action_arg_direction;
7799 /* ---------- player actions ------------------------------------------ */
7801 case CA_MOVE_PLAYER:
7803 /* automatically move to the next field in specified direction */
7804 for (i = 0; i < MAX_PLAYERS; i++)
7805 if (trigger_player_bits & (1 << i))
7806 stored_player[i].programmed_action = action_arg_direction;
7811 case CA_EXIT_PLAYER:
7813 for (i = 0; i < MAX_PLAYERS; i++)
7814 if (action_arg_player_bits & (1 << i))
7815 PlayerWins(&stored_player[i]);
7820 case CA_KILL_PLAYER:
7822 for (i = 0; i < MAX_PLAYERS; i++)
7823 if (action_arg_player_bits & (1 << i))
7824 KillPlayer(&stored_player[i]);
7829 case CA_SET_PLAYER_KEYS:
7831 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7832 int element = getSpecialActionElement(action_arg_element,
7833 action_arg_number, EL_KEY_1);
7835 if (IS_KEY(element))
7837 for (i = 0; i < MAX_PLAYERS; i++)
7839 if (trigger_player_bits & (1 << i))
7841 stored_player[i].key[KEY_NR(element)] = key_state;
7843 DrawGameDoorValues();
7851 case CA_SET_PLAYER_SPEED:
7853 for (i = 0; i < MAX_PLAYERS; i++)
7855 if (trigger_player_bits & (1 << i))
7857 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7859 if (action_arg == CA_ARG_SPEED_FASTER &&
7860 stored_player[i].cannot_move)
7862 action_arg_number = STEPSIZE_VERY_SLOW;
7864 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7865 action_arg == CA_ARG_SPEED_FASTER)
7867 action_arg_number = 2;
7868 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7871 else if (action_arg == CA_ARG_NUMBER_RESET)
7873 action_arg_number = level.initial_player_stepsize[i];
7877 getModifiedActionNumber(move_stepsize,
7880 action_arg_number_min,
7881 action_arg_number_max);
7883 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7890 case CA_SET_PLAYER_SHIELD:
7892 for (i = 0; i < MAX_PLAYERS; i++)
7894 if (trigger_player_bits & (1 << i))
7896 if (action_arg == CA_ARG_SHIELD_OFF)
7898 stored_player[i].shield_normal_time_left = 0;
7899 stored_player[i].shield_deadly_time_left = 0;
7901 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7903 stored_player[i].shield_normal_time_left = 999999;
7905 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7907 stored_player[i].shield_normal_time_left = 999999;
7908 stored_player[i].shield_deadly_time_left = 999999;
7916 #if USE_PLAYER_GRAVITY
7917 case CA_SET_PLAYER_GRAVITY:
7919 for (i = 0; i < MAX_PLAYERS; i++)
7921 if (trigger_player_bits & (1 << i))
7923 stored_player[i].gravity =
7924 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7925 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7926 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
7927 stored_player[i].gravity);
7935 case CA_SET_PLAYER_ARTWORK:
7937 for (i = 0; i < MAX_PLAYERS; i++)
7939 if (trigger_player_bits & (1 << i))
7941 int artwork_element = action_arg_element;
7943 if (action_arg == CA_ARG_ELEMENT_RESET)
7945 (level.use_artwork_element[i] ? level.artwork_element[i] :
7946 stored_player[i].element_nr);
7948 #if USE_GFX_RESET_PLAYER_ARTWORK
7949 if (stored_player[i].artwork_element != artwork_element)
7950 stored_player[i].Frame = 0;
7953 stored_player[i].artwork_element = artwork_element;
7955 SetPlayerWaiting(&stored_player[i], FALSE);
7957 /* set number of special actions for bored and sleeping animation */
7958 stored_player[i].num_special_action_bored =
7959 get_num_special_action(artwork_element,
7960 ACTION_BORING_1, ACTION_BORING_LAST);
7961 stored_player[i].num_special_action_sleeping =
7962 get_num_special_action(artwork_element,
7963 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7970 /* ---------- CE actions ---------------------------------------------- */
7972 case CA_SET_CE_VALUE:
7974 #if USE_NEW_CUSTOM_VALUE
7975 int last_ce_value = CustomValue[x][y];
7977 CustomValue[x][y] = action_arg_number_new;
7979 if (CustomValue[x][y] != last_ce_value)
7981 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
7982 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
7984 if (CustomValue[x][y] == 0)
7986 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7987 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7995 case CA_SET_CE_SCORE:
7997 #if USE_NEW_CUSTOM_VALUE
7998 int last_ce_score = ei->collect_score;
8000 ei->collect_score = action_arg_number_new;
8002 if (ei->collect_score != last_ce_score)
8004 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8005 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8007 if (ei->collect_score == 0)
8011 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8012 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8015 This is a very special case that seems to be a mixture between
8016 CheckElementChange() and CheckTriggeredElementChange(): while
8017 the first one only affects single elements that are triggered
8018 directly, the second one affects multiple elements in the playfield
8019 that are triggered indirectly by another element. This is a third
8020 case: Changing the CE score always affects multiple identical CEs,
8021 so every affected CE must be checked, not only the single CE for
8022 which the CE score was changed in the first place (as every instance
8023 of that CE shares the same CE score, and therefore also can change)!
8025 SCAN_PLAYFIELD(xx, yy)
8027 if (Feld[xx][yy] == element)
8028 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8029 CE_SCORE_GETS_ZERO);
8038 /* ---------- engine actions ------------------------------------------ */
8040 case CA_SET_ENGINE_SCAN_MODE:
8042 InitPlayfieldScanMode(action_arg);
8052 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8054 int old_element = Feld[x][y];
8055 int new_element = get_element_from_group_element(element);
8056 int previous_move_direction = MovDir[x][y];
8057 #if USE_NEW_CUSTOM_VALUE
8058 int last_ce_value = CustomValue[x][y];
8060 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8061 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8062 boolean add_player_onto_element = (new_element_is_player &&
8063 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8064 /* this breaks SnakeBite when a snake is
8065 halfway through a door that closes */
8066 /* NOW FIXED AT LEVEL INIT IN files.c */
8067 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8069 IS_WALKABLE(old_element));
8072 /* check if element under the player changes from accessible to unaccessible
8073 (needed for special case of dropping element which then changes) */
8074 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8075 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8083 if (!add_player_onto_element)
8085 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8086 RemoveMovingField(x, y);
8090 Feld[x][y] = new_element;
8092 #if !USE_GFX_RESET_GFX_ANIMATION
8093 ResetGfxAnimation(x, y);
8094 ResetRandomAnimationValue(x, y);
8097 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8098 MovDir[x][y] = previous_move_direction;
8100 #if USE_NEW_CUSTOM_VALUE
8101 if (element_info[new_element].use_last_ce_value)
8102 CustomValue[x][y] = last_ce_value;
8105 InitField_WithBug1(x, y, FALSE);
8107 new_element = Feld[x][y]; /* element may have changed */
8109 #if USE_GFX_RESET_GFX_ANIMATION
8110 ResetGfxAnimation(x, y);
8111 ResetRandomAnimationValue(x, y);
8114 DrawLevelField(x, y);
8116 if (GFX_CRUMBLED(new_element))
8117 DrawLevelFieldCrumbledSandNeighbours(x, y);
8121 /* check if element under the player changes from accessible to unaccessible
8122 (needed for special case of dropping element which then changes) */
8123 /* (must be checked after creating new element for walkable group elements) */
8124 #if USE_FIX_KILLED_BY_NON_WALKABLE
8125 if (IS_PLAYER(x, y) && !player_explosion_protected &&
8126 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8133 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8134 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8143 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8144 if (new_element_is_player)
8145 RelocatePlayer(x, y, new_element);
8148 ChangeCount[x][y]++; /* count number of changes in the same frame */
8150 TestIfBadThingTouchesPlayer(x, y);
8151 TestIfPlayerTouchesCustomElement(x, y);
8152 TestIfElementTouchesCustomElement(x, y);
8155 static void CreateField(int x, int y, int element)
8157 CreateFieldExt(x, y, element, FALSE);
8160 static void CreateElementFromChange(int x, int y, int element)
8162 element = GET_VALID_RUNTIME_ELEMENT(element);
8164 #if USE_STOP_CHANGED_ELEMENTS
8165 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8167 int old_element = Feld[x][y];
8169 /* prevent changed element from moving in same engine frame
8170 unless both old and new element can either fall or move */
8171 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8172 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8177 CreateFieldExt(x, y, element, TRUE);
8180 static boolean ChangeElement(int x, int y, int element, int page)
8182 struct ElementInfo *ei = &element_info[element];
8183 struct ElementChangeInfo *change = &ei->change_page[page];
8184 int ce_value = CustomValue[x][y];
8185 int ce_score = ei->collect_score;
8187 int old_element = Feld[x][y];
8189 /* always use default change event to prevent running into a loop */
8190 if (ChangeEvent[x][y] == -1)
8191 ChangeEvent[x][y] = CE_DELAY;
8193 if (ChangeEvent[x][y] == CE_DELAY)
8195 /* reset actual trigger element, trigger player and action element */
8196 change->actual_trigger_element = EL_EMPTY;
8197 change->actual_trigger_player = EL_PLAYER_1;
8198 change->actual_trigger_side = CH_SIDE_NONE;
8199 change->actual_trigger_ce_value = 0;
8200 change->actual_trigger_ce_score = 0;
8203 /* do not change elements more than a specified maximum number of changes */
8204 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8207 ChangeCount[x][y]++; /* count number of changes in the same frame */
8209 if (change->explode)
8216 if (change->use_target_content)
8218 boolean complete_replace = TRUE;
8219 boolean can_replace[3][3];
8222 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8225 boolean is_walkable;
8226 boolean is_diggable;
8227 boolean is_collectible;
8228 boolean is_removable;
8229 boolean is_destructible;
8230 int ex = x + xx - 1;
8231 int ey = y + yy - 1;
8232 int content_element = change->target_content.e[xx][yy];
8235 can_replace[xx][yy] = TRUE;
8237 if (ex == x && ey == y) /* do not check changing element itself */
8240 if (content_element == EL_EMPTY_SPACE)
8242 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8247 if (!IN_LEV_FIELD(ex, ey))
8249 can_replace[xx][yy] = FALSE;
8250 complete_replace = FALSE;
8257 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8258 e = MovingOrBlocked2Element(ex, ey);
8260 is_empty = (IS_FREE(ex, ey) ||
8261 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8263 is_walkable = (is_empty || IS_WALKABLE(e));
8264 is_diggable = (is_empty || IS_DIGGABLE(e));
8265 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8266 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8267 is_removable = (is_diggable || is_collectible);
8269 can_replace[xx][yy] =
8270 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8271 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8272 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8273 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8274 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8275 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8276 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8278 if (!can_replace[xx][yy])
8279 complete_replace = FALSE;
8282 if (!change->only_if_complete || complete_replace)
8284 boolean something_has_changed = FALSE;
8286 if (change->only_if_complete && change->use_random_replace &&
8287 RND(100) < change->random_percentage)
8290 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8292 int ex = x + xx - 1;
8293 int ey = y + yy - 1;
8294 int content_element;
8296 if (can_replace[xx][yy] && (!change->use_random_replace ||
8297 RND(100) < change->random_percentage))
8299 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8300 RemoveMovingField(ex, ey);
8302 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8304 content_element = change->target_content.e[xx][yy];
8305 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8306 ce_value, ce_score);
8308 CreateElementFromChange(ex, ey, target_element);
8310 something_has_changed = TRUE;
8312 /* for symmetry reasons, freeze newly created border elements */
8313 if (ex != x || ey != y)
8314 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8318 if (something_has_changed)
8320 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8321 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8327 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
8328 ce_value, ce_score);
8330 if (element == EL_DIAGONAL_GROWING ||
8331 element == EL_DIAGONAL_SHRINKING)
8333 target_element = Store[x][y];
8335 Store[x][y] = EL_EMPTY;
8338 CreateElementFromChange(x, y, target_element);
8340 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8341 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8344 /* this uses direct change before indirect change */
8345 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8350 #if USE_NEW_DELAYED_ACTION
8352 static void HandleElementChange(int x, int y, int page)
8354 int element = MovingOrBlocked2Element(x, y);
8355 struct ElementInfo *ei = &element_info[element];
8356 struct ElementChangeInfo *change = &ei->change_page[page];
8359 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8360 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8363 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8364 x, y, element, element_info[element].token_name);
8365 printf("HandleElementChange(): This should never happen!\n");
8370 /* this can happen with classic bombs on walkable, changing elements */
8371 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8374 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8375 ChangeDelay[x][y] = 0;
8381 if (ChangeDelay[x][y] == 0) /* initialize element change */
8383 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8385 if (change->can_change)
8387 ResetGfxAnimation(x, y);
8388 ResetRandomAnimationValue(x, y);
8390 if (change->pre_change_function)
8391 change->pre_change_function(x, y);
8395 ChangeDelay[x][y]--;
8397 if (ChangeDelay[x][y] != 0) /* continue element change */
8399 if (change->can_change)
8401 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8403 if (IS_ANIMATED(graphic))
8404 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8406 if (change->change_function)
8407 change->change_function(x, y);
8410 else /* finish element change */
8412 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8414 page = ChangePage[x][y];
8415 ChangePage[x][y] = -1;
8417 change = &ei->change_page[page];
8420 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8422 ChangeDelay[x][y] = 1; /* try change after next move step */
8423 ChangePage[x][y] = page; /* remember page to use for change */
8428 if (change->can_change)
8430 if (ChangeElement(x, y, element, page))
8432 if (change->post_change_function)
8433 change->post_change_function(x, y);
8437 if (change->has_action)
8438 ExecuteCustomElementAction(x, y, element, page);
8444 static void HandleElementChange(int x, int y, int page)
8446 int element = MovingOrBlocked2Element(x, y);
8447 struct ElementInfo *ei = &element_info[element];
8448 struct ElementChangeInfo *change = &ei->change_page[page];
8451 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8454 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8455 x, y, element, element_info[element].token_name);
8456 printf("HandleElementChange(): This should never happen!\n");
8461 /* this can happen with classic bombs on walkable, changing elements */
8462 if (!CAN_CHANGE(element))
8465 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8466 ChangeDelay[x][y] = 0;
8472 if (ChangeDelay[x][y] == 0) /* initialize element change */
8474 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8476 ResetGfxAnimation(x, y);
8477 ResetRandomAnimationValue(x, y);
8479 if (change->pre_change_function)
8480 change->pre_change_function(x, y);
8483 ChangeDelay[x][y]--;
8485 if (ChangeDelay[x][y] != 0) /* continue element change */
8487 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8489 if (IS_ANIMATED(graphic))
8490 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8492 if (change->change_function)
8493 change->change_function(x, y);
8495 else /* finish element change */
8497 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8499 page = ChangePage[x][y];
8500 ChangePage[x][y] = -1;
8502 change = &ei->change_page[page];
8505 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8507 ChangeDelay[x][y] = 1; /* try change after next move step */
8508 ChangePage[x][y] = page; /* remember page to use for change */
8513 if (ChangeElement(x, y, element, page))
8515 if (change->post_change_function)
8516 change->post_change_function(x, y);
8523 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8524 int trigger_element,
8530 boolean change_done_any = FALSE;
8531 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8534 if (!(trigger_events[trigger_element][trigger_event]))
8537 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8539 int element = EL_CUSTOM_START + i;
8540 boolean change_done = FALSE;
8543 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8544 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8547 for (p = 0; p < element_info[element].num_change_pages; p++)
8549 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8551 if (change->can_change_or_has_action &&
8552 change->has_event[trigger_event] &&
8553 change->trigger_side & trigger_side &&
8554 change->trigger_player & trigger_player &&
8555 change->trigger_page & trigger_page_bits &&
8556 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8558 change->actual_trigger_element = trigger_element;
8559 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8560 change->actual_trigger_side = trigger_side;
8561 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8562 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8564 if ((change->can_change && !change_done) || change->has_action)
8568 SCAN_PLAYFIELD(x, y)
8570 if (Feld[x][y] == element)
8572 if (change->can_change && !change_done)
8574 ChangeDelay[x][y] = 1;
8575 ChangeEvent[x][y] = trigger_event;
8577 HandleElementChange(x, y, p);
8579 #if USE_NEW_DELAYED_ACTION
8580 else if (change->has_action)
8582 ExecuteCustomElementAction(x, y, element, p);
8583 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8586 if (change->has_action)
8588 ExecuteCustomElementAction(x, y, element, p);
8589 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8595 if (change->can_change)
8598 change_done_any = TRUE;
8605 return change_done_any;
8608 static boolean CheckElementChangeExt(int x, int y,
8610 int trigger_element,
8615 boolean change_done = FALSE;
8618 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8619 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8622 if (Feld[x][y] == EL_BLOCKED)
8624 Blocked2Moving(x, y, &x, &y);
8625 element = Feld[x][y];
8629 /* check if element has already changed */
8630 if (Feld[x][y] != element)
8633 /* check if element has already changed or is about to change after moving */
8634 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8635 Feld[x][y] != element) ||
8637 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8638 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8639 ChangePage[x][y] != -1)))
8643 for (p = 0; p < element_info[element].num_change_pages; p++)
8645 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8647 boolean check_trigger_element =
8648 (trigger_event == CE_TOUCHING_X ||
8649 trigger_event == CE_HITTING_X ||
8650 trigger_event == CE_HIT_BY_X);
8652 if (change->can_change_or_has_action &&
8653 change->has_event[trigger_event] &&
8654 change->trigger_side & trigger_side &&
8655 change->trigger_player & trigger_player &&
8656 (!check_trigger_element ||
8657 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8659 change->actual_trigger_element = trigger_element;
8660 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8661 change->actual_trigger_side = trigger_side;
8662 change->actual_trigger_ce_value = CustomValue[x][y];
8663 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8665 /* special case: trigger element not at (x,y) position for some events */
8666 if (check_trigger_element)
8678 { 0, 0 }, { 0, 0 }, { 0, 0 },
8682 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8683 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8685 change->actual_trigger_ce_value = CustomValue[xx][yy];
8686 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8689 if (change->can_change && !change_done)
8691 ChangeDelay[x][y] = 1;
8692 ChangeEvent[x][y] = trigger_event;
8694 HandleElementChange(x, y, p);
8698 #if USE_NEW_DELAYED_ACTION
8699 else if (change->has_action)
8701 ExecuteCustomElementAction(x, y, element, p);
8702 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8705 if (change->has_action)
8707 ExecuteCustomElementAction(x, y, element, p);
8708 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8717 static void PlayPlayerSound(struct PlayerInfo *player)
8719 int jx = player->jx, jy = player->jy;
8720 int sound_element = player->artwork_element;
8721 int last_action = player->last_action_waiting;
8722 int action = player->action_waiting;
8724 if (player->is_waiting)
8726 if (action != last_action)
8727 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8729 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8733 if (action != last_action)
8734 StopSound(element_info[sound_element].sound[last_action]);
8736 if (last_action == ACTION_SLEEPING)
8737 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8741 static void PlayAllPlayersSound()
8745 for (i = 0; i < MAX_PLAYERS; i++)
8746 if (stored_player[i].active)
8747 PlayPlayerSound(&stored_player[i]);
8750 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8752 boolean last_waiting = player->is_waiting;
8753 int move_dir = player->MovDir;
8755 player->dir_waiting = move_dir;
8756 player->last_action_waiting = player->action_waiting;
8760 if (!last_waiting) /* not waiting -> waiting */
8762 player->is_waiting = TRUE;
8764 player->frame_counter_bored =
8766 game.player_boring_delay_fixed +
8767 GetSimpleRandom(game.player_boring_delay_random);
8768 player->frame_counter_sleeping =
8770 game.player_sleeping_delay_fixed +
8771 GetSimpleRandom(game.player_sleeping_delay_random);
8773 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
8776 if (game.player_sleeping_delay_fixed +
8777 game.player_sleeping_delay_random > 0 &&
8778 player->anim_delay_counter == 0 &&
8779 player->post_delay_counter == 0 &&
8780 FrameCounter >= player->frame_counter_sleeping)
8781 player->is_sleeping = TRUE;
8782 else if (game.player_boring_delay_fixed +
8783 game.player_boring_delay_random > 0 &&
8784 FrameCounter >= player->frame_counter_bored)
8785 player->is_bored = TRUE;
8787 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8788 player->is_bored ? ACTION_BORING :
8791 if (player->is_sleeping && player->use_murphy)
8793 /* special case for sleeping Murphy when leaning against non-free tile */
8795 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
8796 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
8797 !IS_MOVING(player->jx - 1, player->jy)))
8799 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
8800 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
8801 !IS_MOVING(player->jx + 1, player->jy)))
8802 move_dir = MV_RIGHT;
8804 player->is_sleeping = FALSE;
8806 player->dir_waiting = move_dir;
8809 if (player->is_sleeping)
8811 if (player->num_special_action_sleeping > 0)
8813 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8815 int last_special_action = player->special_action_sleeping;
8816 int num_special_action = player->num_special_action_sleeping;
8817 int special_action =
8818 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8819 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8820 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8821 last_special_action + 1 : ACTION_SLEEPING);
8822 int special_graphic =
8823 el_act_dir2img(player->artwork_element, special_action, move_dir);
8825 player->anim_delay_counter =
8826 graphic_info[special_graphic].anim_delay_fixed +
8827 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8828 player->post_delay_counter =
8829 graphic_info[special_graphic].post_delay_fixed +
8830 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8832 player->special_action_sleeping = special_action;
8835 if (player->anim_delay_counter > 0)
8837 player->action_waiting = player->special_action_sleeping;
8838 player->anim_delay_counter--;
8840 else if (player->post_delay_counter > 0)
8842 player->post_delay_counter--;
8846 else if (player->is_bored)
8848 if (player->num_special_action_bored > 0)
8850 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8852 int special_action =
8853 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
8854 int special_graphic =
8855 el_act_dir2img(player->artwork_element, special_action, move_dir);
8857 player->anim_delay_counter =
8858 graphic_info[special_graphic].anim_delay_fixed +
8859 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8860 player->post_delay_counter =
8861 graphic_info[special_graphic].post_delay_fixed +
8862 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8864 player->special_action_bored = special_action;
8867 if (player->anim_delay_counter > 0)
8869 player->action_waiting = player->special_action_bored;
8870 player->anim_delay_counter--;
8872 else if (player->post_delay_counter > 0)
8874 player->post_delay_counter--;
8879 else if (last_waiting) /* waiting -> not waiting */
8881 player->is_waiting = FALSE;
8882 player->is_bored = FALSE;
8883 player->is_sleeping = FALSE;
8885 player->frame_counter_bored = -1;
8886 player->frame_counter_sleeping = -1;
8888 player->anim_delay_counter = 0;
8889 player->post_delay_counter = 0;
8891 player->dir_waiting = player->MovDir;
8892 player->action_waiting = ACTION_DEFAULT;
8894 player->special_action_bored = ACTION_DEFAULT;
8895 player->special_action_sleeping = ACTION_DEFAULT;
8899 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8901 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8902 int left = player_action & JOY_LEFT;
8903 int right = player_action & JOY_RIGHT;
8904 int up = player_action & JOY_UP;
8905 int down = player_action & JOY_DOWN;
8906 int button1 = player_action & JOY_BUTTON_1;
8907 int button2 = player_action & JOY_BUTTON_2;
8908 int dx = (left ? -1 : right ? 1 : 0);
8909 int dy = (up ? -1 : down ? 1 : 0);
8911 if (!player->active || tape.pausing)
8917 snapped = SnapField(player, dx, dy);
8921 dropped = DropElement(player);
8923 moved = MovePlayer(player, dx, dy);
8926 if (tape.single_step && tape.recording && !tape.pausing)
8928 if (button1 || (dropped && !moved))
8930 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8931 SnapField(player, 0, 0); /* stop snapping */
8935 SetPlayerWaiting(player, FALSE);
8937 return player_action;
8941 /* no actions for this player (no input at player's configured device) */
8943 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8944 SnapField(player, 0, 0);
8945 CheckGravityMovementWhenNotMoving(player);
8947 if (player->MovPos == 0)
8948 SetPlayerWaiting(player, TRUE);
8950 if (player->MovPos == 0) /* needed for tape.playing */
8951 player->is_moving = FALSE;
8953 player->is_dropping = FALSE;
8954 player->is_dropping_pressed = FALSE;
8955 player->drop_pressed_delay = 0;
8961 static void CheckLevelTime()
8965 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8967 if (level.native_em_level->lev->home == 0) /* all players at home */
8969 PlayerWins(local_player);
8971 AllPlayersGone = TRUE;
8973 level.native_em_level->lev->home = -1;
8976 if (level.native_em_level->ply[0]->alive == 0 &&
8977 level.native_em_level->ply[1]->alive == 0 &&
8978 level.native_em_level->ply[2]->alive == 0 &&
8979 level.native_em_level->ply[3]->alive == 0) /* all dead */
8980 AllPlayersGone = TRUE;
8983 if (TimeFrames >= FRAMES_PER_SECOND)
8988 for (i = 0; i < MAX_PLAYERS; i++)
8990 struct PlayerInfo *player = &stored_player[i];
8992 if (SHIELD_ON(player))
8994 player->shield_normal_time_left--;
8996 if (player->shield_deadly_time_left > 0)
8997 player->shield_deadly_time_left--;
9001 if (!local_player->LevelSolved && !level.use_step_counter)
9009 if (TimeLeft <= 10 && setup.time_limit)
9010 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9012 DrawGameValue_Time(TimeLeft);
9014 if (!TimeLeft && setup.time_limit)
9016 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9017 level.native_em_level->lev->killed_out_of_time = TRUE;
9019 for (i = 0; i < MAX_PLAYERS; i++)
9020 KillPlayer(&stored_player[i]);
9023 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9024 DrawGameValue_Time(TimePlayed);
9026 level.native_em_level->lev->time =
9027 (level.time == 0 ? TimePlayed : TimeLeft);
9030 if (tape.recording || tape.playing)
9031 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9035 void AdvanceFrameAndPlayerCounters(int player_nr)
9039 /* advance frame counters (global frame counter and time frame counter) */
9043 /* advance player counters (counters for move delay, move animation etc.) */
9044 for (i = 0; i < MAX_PLAYERS; i++)
9046 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9047 int move_delay_value = stored_player[i].move_delay_value;
9048 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9050 if (!advance_player_counters) /* not all players may be affected */
9053 #if USE_NEW_PLAYER_ANIM
9054 if (move_frames == 0) /* less than one move per game frame */
9056 int stepsize = TILEX / move_delay_value;
9057 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9058 int count = (stored_player[i].is_moving ?
9059 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9061 if (count % delay == 0)
9066 stored_player[i].Frame += move_frames;
9068 if (stored_player[i].MovPos != 0)
9069 stored_player[i].StepFrame += move_frames;
9071 if (stored_player[i].move_delay > 0)
9072 stored_player[i].move_delay--;
9074 /* due to bugs in previous versions, counter must count up, not down */
9075 if (stored_player[i].push_delay != -1)
9076 stored_player[i].push_delay++;
9078 if (stored_player[i].drop_delay > 0)
9079 stored_player[i].drop_delay--;
9081 if (stored_player[i].is_dropping_pressed)
9082 stored_player[i].drop_pressed_delay++;
9086 void StartGameActions(boolean init_network_game, boolean record_tape,
9089 unsigned long new_random_seed = InitRND(random_seed);
9092 TapeStartRecording(new_random_seed);
9094 #if defined(NETWORK_AVALIABLE)
9095 if (init_network_game)
9097 SendToServer_StartPlaying();
9108 static unsigned long game_frame_delay = 0;
9109 unsigned long game_frame_delay_value;
9110 byte *recorded_player_action;
9111 byte summarized_player_action = 0;
9112 byte tape_action[MAX_PLAYERS];
9115 if (game.restart_level)
9116 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9118 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9120 if (level.native_em_level->lev->home == 0) /* all players at home */
9122 PlayerWins(local_player);
9124 AllPlayersGone = TRUE;
9126 level.native_em_level->lev->home = -1;
9129 if (level.native_em_level->ply[0]->alive == 0 &&
9130 level.native_em_level->ply[1]->alive == 0 &&
9131 level.native_em_level->ply[2]->alive == 0 &&
9132 level.native_em_level->ply[3]->alive == 0) /* all dead */
9133 AllPlayersGone = TRUE;
9136 if (local_player->LevelSolved)
9139 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9142 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9145 game_frame_delay_value =
9146 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9148 if (tape.playing && tape.warp_forward && !tape.pausing)
9149 game_frame_delay_value = 0;
9151 /* ---------- main game synchronization point ---------- */
9153 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9155 if (network_playing && !network_player_action_received)
9157 /* try to get network player actions in time */
9159 #if defined(NETWORK_AVALIABLE)
9160 /* last chance to get network player actions without main loop delay */
9164 /* game was quit by network peer */
9165 if (game_status != GAME_MODE_PLAYING)
9168 if (!network_player_action_received)
9169 return; /* failed to get network player actions in time */
9171 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9177 /* at this point we know that we really continue executing the game */
9179 network_player_action_received = FALSE;
9181 /* when playing tape, read previously recorded player input from tape data */
9182 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9185 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9190 if (tape.set_centered_player)
9192 game.centered_player_nr_next = tape.centered_player_nr_next;
9193 game.set_centered_player = TRUE;
9196 for (i = 0; i < MAX_PLAYERS; i++)
9198 summarized_player_action |= stored_player[i].action;
9200 if (!network_playing)
9201 stored_player[i].effective_action = stored_player[i].action;
9204 #if defined(NETWORK_AVALIABLE)
9205 if (network_playing)
9206 SendToServer_MovePlayer(summarized_player_action);
9209 if (!options.network && !setup.team_mode)
9210 local_player->effective_action = summarized_player_action;
9212 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9214 for (i = 0; i < MAX_PLAYERS; i++)
9215 stored_player[i].effective_action =
9216 (i == game.centered_player_nr ? summarized_player_action : 0);
9219 if (recorded_player_action != NULL)
9220 for (i = 0; i < MAX_PLAYERS; i++)
9221 stored_player[i].effective_action = recorded_player_action[i];
9223 for (i = 0; i < MAX_PLAYERS; i++)
9225 tape_action[i] = stored_player[i].effective_action;
9227 /* (this can only happen in the R'n'D game engine) */
9228 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9229 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9232 /* only record actions from input devices, but not programmed actions */
9234 TapeRecordAction(tape_action);
9236 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9238 GameActions_EM_Main();
9246 void GameActions_EM_Main()
9248 byte effective_action[MAX_PLAYERS];
9249 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9252 for (i = 0; i < MAX_PLAYERS; i++)
9253 effective_action[i] = stored_player[i].effective_action;
9255 GameActions_EM(effective_action, warp_mode);
9259 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9262 void GameActions_RND()
9264 int magic_wall_x = 0, magic_wall_y = 0;
9265 int i, x, y, element, graphic;
9267 InitPlayfieldScanModeVars();
9269 #if USE_ONE_MORE_CHANGE_PER_FRAME
9270 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9272 SCAN_PLAYFIELD(x, y)
9274 ChangeCount[x][y] = 0;
9275 ChangeEvent[x][y] = -1;
9280 if (game.set_centered_player)
9282 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9284 /* switching to "all players" only possible if all players fit to screen */
9285 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9287 game.centered_player_nr_next = game.centered_player_nr;
9288 game.set_centered_player = FALSE;
9291 /* do not switch focus to non-existing (or non-active) player */
9292 if (game.centered_player_nr_next >= 0 &&
9293 !stored_player[game.centered_player_nr_next].active)
9295 game.centered_player_nr_next = game.centered_player_nr;
9296 game.set_centered_player = FALSE;
9300 if (game.set_centered_player &&
9301 ScreenMovPos == 0) /* screen currently aligned at tile position */
9305 if (game.centered_player_nr_next == -1)
9307 setScreenCenteredToAllPlayers(&sx, &sy);
9311 sx = stored_player[game.centered_player_nr_next].jx;
9312 sy = stored_player[game.centered_player_nr_next].jy;
9315 game.centered_player_nr = game.centered_player_nr_next;
9316 game.set_centered_player = FALSE;
9318 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9319 DrawGameDoorValues();
9322 for (i = 0; i < MAX_PLAYERS; i++)
9324 int actual_player_action = stored_player[i].effective_action;
9327 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9328 - rnd_equinox_tetrachloride 048
9329 - rnd_equinox_tetrachloride_ii 096
9330 - rnd_emanuel_schmieg 002
9331 - doctor_sloan_ww 001, 020
9333 if (stored_player[i].MovPos == 0)
9334 CheckGravityMovement(&stored_player[i]);
9337 /* overwrite programmed action with tape action */
9338 if (stored_player[i].programmed_action)
9339 actual_player_action = stored_player[i].programmed_action;
9341 PlayerActions(&stored_player[i], actual_player_action);
9343 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9346 ScrollScreen(NULL, SCROLL_GO_ON);
9348 /* for backwards compatibility, the following code emulates a fixed bug that
9349 occured when pushing elements (causing elements that just made their last
9350 pushing step to already (if possible) make their first falling step in the
9351 same game frame, which is bad); this code is also needed to use the famous
9352 "spring push bug" which is used in older levels and might be wanted to be
9353 used also in newer levels, but in this case the buggy pushing code is only
9354 affecting the "spring" element and no other elements */
9356 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9358 for (i = 0; i < MAX_PLAYERS; i++)
9360 struct PlayerInfo *player = &stored_player[i];
9364 if (player->active && player->is_pushing && player->is_moving &&
9366 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9367 Feld[x][y] == EL_SPRING))
9369 ContinueMoving(x, y);
9371 /* continue moving after pushing (this is actually a bug) */
9372 if (!IS_MOVING(x, y))
9380 SCAN_PLAYFIELD(x, y)
9382 ChangeCount[x][y] = 0;
9383 ChangeEvent[x][y] = -1;
9385 /* this must be handled before main playfield loop */
9386 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9389 if (MovDelay[x][y] <= 0)
9393 #if USE_NEW_SNAP_DELAY
9394 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9397 if (MovDelay[x][y] <= 0)
9400 DrawLevelField(x, y);
9402 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9408 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9410 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9411 printf("GameActions(): This should never happen!\n");
9413 ChangePage[x][y] = -1;
9418 if (WasJustMoving[x][y] > 0)
9419 WasJustMoving[x][y]--;
9420 if (WasJustFalling[x][y] > 0)
9421 WasJustFalling[x][y]--;
9422 if (CheckCollision[x][y] > 0)
9423 CheckCollision[x][y]--;
9427 /* reset finished pushing action (not done in ContinueMoving() to allow
9428 continuous pushing animation for elements with zero push delay) */
9429 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9431 ResetGfxAnimation(x, y);
9432 DrawLevelField(x, y);
9436 if (IS_BLOCKED(x, y))
9440 Blocked2Moving(x, y, &oldx, &oldy);
9441 if (!IS_MOVING(oldx, oldy))
9443 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9444 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9445 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9446 printf("GameActions(): This should never happen!\n");
9452 SCAN_PLAYFIELD(x, y)
9454 element = Feld[x][y];
9455 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9457 ResetGfxFrame(x, y, TRUE);
9459 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9460 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9461 ResetRandomAnimationValue(x, y);
9463 SetRandomAnimationValue(x, y);
9465 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9467 if (IS_INACTIVE(element))
9469 if (IS_ANIMATED(graphic))
9470 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9475 /* this may take place after moving, so 'element' may have changed */
9476 if (IS_CHANGING(x, y) &&
9477 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9479 int page = element_info[element].event_page_nr[CE_DELAY];
9482 HandleElementChange(x, y, page);
9484 if (CAN_CHANGE(element))
9485 HandleElementChange(x, y, page);
9487 if (HAS_ACTION(element))
9488 ExecuteCustomElementAction(x, y, element, page);
9491 element = Feld[x][y];
9492 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9495 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9499 element = Feld[x][y];
9500 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9502 if (IS_ANIMATED(graphic) &&
9505 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9507 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9508 DrawTwinkleOnField(x, y);
9510 else if ((element == EL_ACID ||
9511 element == EL_EXIT_OPEN ||
9512 element == EL_SP_EXIT_OPEN ||
9513 element == EL_SP_TERMINAL ||
9514 element == EL_SP_TERMINAL_ACTIVE ||
9515 element == EL_EXTRA_TIME ||
9516 element == EL_SHIELD_NORMAL ||
9517 element == EL_SHIELD_DEADLY) &&
9518 IS_ANIMATED(graphic))
9519 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9520 else if (IS_MOVING(x, y))
9521 ContinueMoving(x, y);
9522 else if (IS_ACTIVE_BOMB(element))
9523 CheckDynamite(x, y);
9524 else if (element == EL_AMOEBA_GROWING)
9525 AmoebeWaechst(x, y);
9526 else if (element == EL_AMOEBA_SHRINKING)
9527 AmoebaDisappearing(x, y);
9529 #if !USE_NEW_AMOEBA_CODE
9530 else if (IS_AMOEBALIVE(element))
9531 AmoebeAbleger(x, y);
9534 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9536 else if (element == EL_EXIT_CLOSED)
9538 else if (element == EL_SP_EXIT_CLOSED)
9540 else if (element == EL_EXPANDABLE_WALL_GROWING)
9542 else if (element == EL_EXPANDABLE_WALL ||
9543 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9544 element == EL_EXPANDABLE_WALL_VERTICAL ||
9545 element == EL_EXPANDABLE_WALL_ANY ||
9546 element == EL_BD_EXPANDABLE_WALL)
9548 else if (element == EL_FLAMES)
9549 CheckForDragon(x, y);
9550 else if (element == EL_EXPLOSION)
9551 ; /* drawing of correct explosion animation is handled separately */
9552 else if (element == EL_ELEMENT_SNAPPING ||
9553 element == EL_DIAGONAL_SHRINKING ||
9554 element == EL_DIAGONAL_GROWING)
9556 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9558 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9560 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9561 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9563 if (IS_BELT_ACTIVE(element))
9564 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9566 if (game.magic_wall_active)
9568 int jx = local_player->jx, jy = local_player->jy;
9570 /* play the element sound at the position nearest to the player */
9571 if ((element == EL_MAGIC_WALL_FULL ||
9572 element == EL_MAGIC_WALL_ACTIVE ||
9573 element == EL_MAGIC_WALL_EMPTYING ||
9574 element == EL_BD_MAGIC_WALL_FULL ||
9575 element == EL_BD_MAGIC_WALL_ACTIVE ||
9576 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9577 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9585 #if USE_NEW_AMOEBA_CODE
9586 /* new experimental amoeba growth stuff */
9587 if (!(FrameCounter % 8))
9589 static unsigned long random = 1684108901;
9591 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9593 x = RND(lev_fieldx);
9594 y = RND(lev_fieldy);
9595 element = Feld[x][y];
9597 if (!IS_PLAYER(x,y) &&
9598 (element == EL_EMPTY ||
9599 CAN_GROW_INTO(element) ||
9600 element == EL_QUICKSAND_EMPTY ||
9601 element == EL_ACID_SPLASH_LEFT ||
9602 element == EL_ACID_SPLASH_RIGHT))
9604 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9605 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9606 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9607 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9608 Feld[x][y] = EL_AMOEBA_DROP;
9611 random = random * 129 + 1;
9617 if (game.explosions_delayed)
9620 game.explosions_delayed = FALSE;
9622 SCAN_PLAYFIELD(x, y)
9624 element = Feld[x][y];
9626 if (ExplodeField[x][y])
9627 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9628 else if (element == EL_EXPLOSION)
9629 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9631 ExplodeField[x][y] = EX_TYPE_NONE;
9634 game.explosions_delayed = TRUE;
9637 if (game.magic_wall_active)
9639 if (!(game.magic_wall_time_left % 4))
9641 int element = Feld[magic_wall_x][magic_wall_y];
9643 if (element == EL_BD_MAGIC_WALL_FULL ||
9644 element == EL_BD_MAGIC_WALL_ACTIVE ||
9645 element == EL_BD_MAGIC_WALL_EMPTYING)
9646 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9648 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9651 if (game.magic_wall_time_left > 0)
9653 game.magic_wall_time_left--;
9654 if (!game.magic_wall_time_left)
9656 SCAN_PLAYFIELD(x, y)
9658 element = Feld[x][y];
9660 if (element == EL_MAGIC_WALL_ACTIVE ||
9661 element == EL_MAGIC_WALL_FULL)
9663 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9664 DrawLevelField(x, y);
9666 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9667 element == EL_BD_MAGIC_WALL_FULL)
9669 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9670 DrawLevelField(x, y);
9674 game.magic_wall_active = FALSE;
9679 if (game.light_time_left > 0)
9681 game.light_time_left--;
9683 if (game.light_time_left == 0)
9684 RedrawAllLightSwitchesAndInvisibleElements();
9687 if (game.timegate_time_left > 0)
9689 game.timegate_time_left--;
9691 if (game.timegate_time_left == 0)
9692 CloseAllOpenTimegates();
9695 if (game.lenses_time_left > 0)
9697 game.lenses_time_left--;
9699 if (game.lenses_time_left == 0)
9700 RedrawAllInvisibleElementsForLenses();
9703 if (game.magnify_time_left > 0)
9705 game.magnify_time_left--;
9707 if (game.magnify_time_left == 0)
9708 RedrawAllInvisibleElementsForMagnifier();
9711 for (i = 0; i < MAX_PLAYERS; i++)
9713 struct PlayerInfo *player = &stored_player[i];
9715 if (SHIELD_ON(player))
9717 if (player->shield_deadly_time_left)
9718 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9719 else if (player->shield_normal_time_left)
9720 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9727 PlayAllPlayersSound();
9729 if (options.debug) /* calculate frames per second */
9731 static unsigned long fps_counter = 0;
9732 static int fps_frames = 0;
9733 unsigned long fps_delay_ms = Counter() - fps_counter;
9737 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9739 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9742 fps_counter = Counter();
9745 redraw_mask |= REDRAW_FPS;
9748 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9750 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9752 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9754 local_player->show_envelope = 0;
9757 /* use random number generator in every frame to make it less predictable */
9758 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9762 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9764 int min_x = x, min_y = y, max_x = x, max_y = y;
9767 for (i = 0; i < MAX_PLAYERS; i++)
9769 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9771 if (!stored_player[i].active || &stored_player[i] == player)
9774 min_x = MIN(min_x, jx);
9775 min_y = MIN(min_y, jy);
9776 max_x = MAX(max_x, jx);
9777 max_y = MAX(max_y, jy);
9780 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9783 static boolean AllPlayersInVisibleScreen()
9787 for (i = 0; i < MAX_PLAYERS; i++)
9789 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9791 if (!stored_player[i].active)
9794 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9801 void ScrollLevel(int dx, int dy)
9803 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9806 BlitBitmap(drawto_field, drawto_field,
9807 FX + TILEX * (dx == -1) - softscroll_offset,
9808 FY + TILEY * (dy == -1) - softscroll_offset,
9809 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9810 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9811 FX + TILEX * (dx == 1) - softscroll_offset,
9812 FY + TILEY * (dy == 1) - softscroll_offset);
9816 x = (dx == 1 ? BX1 : BX2);
9817 for (y = BY1; y <= BY2; y++)
9818 DrawScreenField(x, y);
9823 y = (dy == 1 ? BY1 : BY2);
9824 for (x = BX1; x <= BX2; x++)
9825 DrawScreenField(x, y);
9828 redraw_mask |= REDRAW_FIELD;
9831 static boolean canFallDown(struct PlayerInfo *player)
9833 int jx = player->jx, jy = player->jy;
9835 return (IN_LEV_FIELD(jx, jy + 1) &&
9836 (IS_FREE(jx, jy + 1) ||
9837 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9838 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9839 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9842 static boolean canPassField(int x, int y, int move_dir)
9844 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9845 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9846 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9849 int element = Feld[x][y];
9851 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9852 !CAN_MOVE(element) &&
9853 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9854 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9855 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9858 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9860 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9861 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9862 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9866 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9867 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9868 (IS_DIGGABLE(Feld[newx][newy]) ||
9869 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9870 canPassField(newx, newy, move_dir)));
9873 static void CheckGravityMovement(struct PlayerInfo *player)
9875 #if USE_PLAYER_GRAVITY
9876 if (player->gravity && !player->programmed_action)
9878 if (game.gravity && !player->programmed_action)
9881 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9882 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9883 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
9884 int jx = player->jx, jy = player->jy;
9885 boolean player_is_moving_to_valid_field =
9886 (!player_is_snapping &&
9887 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9888 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9889 boolean player_can_fall_down = canFallDown(player);
9891 if (player_can_fall_down &&
9892 !player_is_moving_to_valid_field)
9893 player->programmed_action = MV_DOWN;
9897 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9899 return CheckGravityMovement(player);
9901 #if USE_PLAYER_GRAVITY
9902 if (player->gravity && !player->programmed_action)
9904 if (game.gravity && !player->programmed_action)
9907 int jx = player->jx, jy = player->jy;
9908 boolean field_under_player_is_free =
9909 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9910 boolean player_is_standing_on_valid_field =
9911 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9912 (IS_WALKABLE(Feld[jx][jy]) &&
9913 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9915 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9916 player->programmed_action = MV_DOWN;
9922 -----------------------------------------------------------------------------
9923 dx, dy: direction (non-diagonal) to try to move the player to
9924 real_dx, real_dy: direction as read from input device (can be diagonal)
9927 boolean MovePlayerOneStep(struct PlayerInfo *player,
9928 int dx, int dy, int real_dx, int real_dy)
9930 int jx = player->jx, jy = player->jy;
9931 int new_jx = jx + dx, new_jy = jy + dy;
9932 #if !USE_FIXED_DONT_RUN_INTO
9936 boolean player_can_move = !player->cannot_move;
9938 if (!player->active || (!dx && !dy))
9939 return MP_NO_ACTION;
9941 player->MovDir = (dx < 0 ? MV_LEFT :
9944 dy > 0 ? MV_DOWN : MV_NONE);
9946 if (!IN_LEV_FIELD(new_jx, new_jy))
9947 return MP_NO_ACTION;
9949 if (!player_can_move)
9951 if (player->MovPos == 0)
9953 player->is_moving = FALSE;
9954 player->is_digging = FALSE;
9955 player->is_collecting = FALSE;
9956 player->is_snapping = FALSE;
9957 player->is_pushing = FALSE;
9962 if (!options.network && game.centered_player_nr == -1 &&
9963 !AllPlayersInSight(player, new_jx, new_jy))
9964 return MP_NO_ACTION;
9966 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9967 return MP_NO_ACTION;
9970 #if !USE_FIXED_DONT_RUN_INTO
9971 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9973 /* (moved to DigField()) */
9974 if (player_can_move && DONT_RUN_INTO(element))
9976 if (element == EL_ACID && dx == 0 && dy == 1)
9978 SplashAcid(new_jx, new_jy);
9979 Feld[jx][jy] = EL_PLAYER_1;
9980 InitMovingField(jx, jy, MV_DOWN);
9981 Store[jx][jy] = EL_ACID;
9982 ContinueMoving(jx, jy);
9986 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9992 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9993 if (can_move != MP_MOVING)
9996 /* check if DigField() has caused relocation of the player */
9997 if (player->jx != jx || player->jy != jy)
9998 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10000 StorePlayer[jx][jy] = 0;
10001 player->last_jx = jx;
10002 player->last_jy = jy;
10003 player->jx = new_jx;
10004 player->jy = new_jy;
10005 StorePlayer[new_jx][new_jy] = player->element_nr;
10007 if (player->move_delay_value_next != -1)
10009 player->move_delay_value = player->move_delay_value_next;
10010 player->move_delay_value_next = -1;
10014 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10016 player->step_counter++;
10018 PlayerVisit[jx][jy] = FrameCounter;
10020 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10021 player->is_moving = TRUE;
10025 /* should better be called in MovePlayer(), but this breaks some tapes */
10026 ScrollPlayer(player, SCROLL_INIT);
10032 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10034 int jx = player->jx, jy = player->jy;
10035 int old_jx = jx, old_jy = jy;
10036 int moved = MP_NO_ACTION;
10038 if (!player->active)
10043 if (player->MovPos == 0)
10045 player->is_moving = FALSE;
10046 player->is_digging = FALSE;
10047 player->is_collecting = FALSE;
10048 player->is_snapping = FALSE;
10049 player->is_pushing = FALSE;
10055 if (player->move_delay > 0)
10058 player->move_delay = -1; /* set to "uninitialized" value */
10060 /* store if player is automatically moved to next field */
10061 player->is_auto_moving = (player->programmed_action != MV_NONE);
10063 /* remove the last programmed player action */
10064 player->programmed_action = 0;
10066 if (player->MovPos)
10068 /* should only happen if pre-1.2 tape recordings are played */
10069 /* this is only for backward compatibility */
10071 int original_move_delay_value = player->move_delay_value;
10074 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10078 /* scroll remaining steps with finest movement resolution */
10079 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10081 while (player->MovPos)
10083 ScrollPlayer(player, SCROLL_GO_ON);
10084 ScrollScreen(NULL, SCROLL_GO_ON);
10086 AdvanceFrameAndPlayerCounters(player->index_nr);
10092 player->move_delay_value = original_move_delay_value;
10095 player->is_active = FALSE;
10097 if (player->last_move_dir & MV_HORIZONTAL)
10099 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10100 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10104 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10105 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10108 #if USE_FIXED_BORDER_RUNNING_GFX
10109 if (!moved && !player->is_active)
10111 player->is_moving = FALSE;
10112 player->is_digging = FALSE;
10113 player->is_collecting = FALSE;
10114 player->is_snapping = FALSE;
10115 player->is_pushing = FALSE;
10123 if (moved & MP_MOVING && !ScreenMovPos &&
10124 (player->index_nr == game.centered_player_nr ||
10125 game.centered_player_nr == -1))
10127 if (moved & MP_MOVING && !ScreenMovPos &&
10128 (player == local_player || !options.network))
10131 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10132 int offset = (setup.scroll_delay ? 3 : 0);
10134 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10136 /* actual player has left the screen -- scroll in that direction */
10137 if (jx != old_jx) /* player has moved horizontally */
10138 scroll_x += (jx - old_jx);
10139 else /* player has moved vertically */
10140 scroll_y += (jy - old_jy);
10144 if (jx != old_jx) /* player has moved horizontally */
10146 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10147 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10148 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10150 /* don't scroll over playfield boundaries */
10151 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10152 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10154 /* don't scroll more than one field at a time */
10155 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10157 /* don't scroll against the player's moving direction */
10158 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10159 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10160 scroll_x = old_scroll_x;
10162 else /* player has moved vertically */
10164 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10165 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10166 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10168 /* don't scroll over playfield boundaries */
10169 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10170 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10172 /* don't scroll more than one field at a time */
10173 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10175 /* don't scroll against the player's moving direction */
10176 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10177 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10178 scroll_y = old_scroll_y;
10182 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10185 if (!options.network && game.centered_player_nr == -1 &&
10186 !AllPlayersInVisibleScreen())
10188 scroll_x = old_scroll_x;
10189 scroll_y = old_scroll_y;
10193 if (!options.network && !AllPlayersInVisibleScreen())
10195 scroll_x = old_scroll_x;
10196 scroll_y = old_scroll_y;
10201 ScrollScreen(player, SCROLL_INIT);
10202 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10207 player->StepFrame = 0;
10209 if (moved & MP_MOVING)
10211 if (old_jx != jx && old_jy == jy)
10212 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10213 else if (old_jx == jx && old_jy != jy)
10214 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10216 DrawLevelField(jx, jy); /* for "crumbled sand" */
10218 player->last_move_dir = player->MovDir;
10219 player->is_moving = TRUE;
10220 player->is_snapping = FALSE;
10221 player->is_switching = FALSE;
10222 player->is_dropping = FALSE;
10223 player->is_dropping_pressed = FALSE;
10224 player->drop_pressed_delay = 0;
10227 /* should better be called here than above, but this breaks some tapes */
10228 ScrollPlayer(player, SCROLL_INIT);
10233 CheckGravityMovementWhenNotMoving(player);
10235 player->is_moving = FALSE;
10237 /* at this point, the player is allowed to move, but cannot move right now
10238 (e.g. because of something blocking the way) -- ensure that the player
10239 is also allowed to move in the next frame (in old versions before 3.1.1,
10240 the player was forced to wait again for eight frames before next try) */
10242 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10243 player->move_delay = 0; /* allow direct movement in the next frame */
10246 if (player->move_delay == -1) /* not yet initialized by DigField() */
10247 player->move_delay = player->move_delay_value;
10249 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10251 TestIfPlayerTouchesBadThing(jx, jy);
10252 TestIfPlayerTouchesCustomElement(jx, jy);
10255 if (!player->active)
10256 RemovePlayer(player);
10261 void ScrollPlayer(struct PlayerInfo *player, int mode)
10263 int jx = player->jx, jy = player->jy;
10264 int last_jx = player->last_jx, last_jy = player->last_jy;
10265 int move_stepsize = TILEX / player->move_delay_value;
10267 #if USE_NEW_PLAYER_SPEED
10268 if (!player->active)
10271 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10274 if (!player->active || player->MovPos == 0)
10278 if (mode == SCROLL_INIT)
10280 player->actual_frame_counter = FrameCounter;
10281 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10283 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10284 Feld[last_jx][last_jy] == EL_EMPTY)
10286 int last_field_block_delay = 0; /* start with no blocking at all */
10287 int block_delay_adjustment = player->block_delay_adjustment;
10289 /* if player blocks last field, add delay for exactly one move */
10290 if (player->block_last_field)
10292 last_field_block_delay += player->move_delay_value;
10294 /* when blocking enabled, prevent moving up despite gravity */
10295 #if USE_PLAYER_GRAVITY
10296 if (player->gravity && player->MovDir == MV_UP)
10297 block_delay_adjustment = -1;
10299 if (game.gravity && player->MovDir == MV_UP)
10300 block_delay_adjustment = -1;
10304 /* add block delay adjustment (also possible when not blocking) */
10305 last_field_block_delay += block_delay_adjustment;
10307 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10308 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10311 #if USE_NEW_PLAYER_SPEED
10312 if (player->MovPos != 0) /* player has not yet reached destination */
10318 else if (!FrameReached(&player->actual_frame_counter, 1))
10321 #if USE_NEW_PLAYER_SPEED
10322 if (player->MovPos != 0)
10324 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10325 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10327 /* before DrawPlayer() to draw correct player graphic for this case */
10328 if (player->MovPos == 0)
10329 CheckGravityMovement(player);
10332 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10333 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10335 /* before DrawPlayer() to draw correct player graphic for this case */
10336 if (player->MovPos == 0)
10337 CheckGravityMovement(player);
10340 if (player->MovPos == 0) /* player reached destination field */
10342 if (player->move_delay_reset_counter > 0)
10344 player->move_delay_reset_counter--;
10346 if (player->move_delay_reset_counter == 0)
10348 /* continue with normal speed after quickly moving through gate */
10349 HALVE_PLAYER_SPEED(player);
10351 /* be able to make the next move without delay */
10352 player->move_delay = 0;
10356 player->last_jx = jx;
10357 player->last_jy = jy;
10359 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10360 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10361 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10363 DrawPlayer(player); /* needed here only to cleanup last field */
10364 RemovePlayer(player);
10366 if (local_player->friends_still_needed == 0 ||
10367 IS_SP_ELEMENT(Feld[jx][jy]))
10368 PlayerWins(player);
10371 /* this breaks one level: "machine", level 000 */
10373 int move_direction = player->MovDir;
10374 int enter_side = MV_DIR_OPPOSITE(move_direction);
10375 int leave_side = move_direction;
10376 int old_jx = last_jx;
10377 int old_jy = last_jy;
10378 int old_element = Feld[old_jx][old_jy];
10379 int new_element = Feld[jx][jy];
10381 if (IS_CUSTOM_ELEMENT(old_element))
10382 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10384 player->index_bit, leave_side);
10386 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10387 CE_PLAYER_LEAVES_X,
10388 player->index_bit, leave_side);
10390 if (IS_CUSTOM_ELEMENT(new_element))
10391 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10392 player->index_bit, enter_side);
10394 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10395 CE_PLAYER_ENTERS_X,
10396 player->index_bit, enter_side);
10398 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10399 CE_MOVE_OF_X, move_direction);
10402 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10404 TestIfPlayerTouchesBadThing(jx, jy);
10405 TestIfPlayerTouchesCustomElement(jx, jy);
10407 /* needed because pushed element has not yet reached its destination,
10408 so it would trigger a change event at its previous field location */
10409 if (!player->is_pushing)
10410 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10412 if (!player->active)
10413 RemovePlayer(player);
10416 if (!local_player->LevelSolved && level.use_step_counter)
10426 if (TimeLeft <= 10 && setup.time_limit)
10427 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10429 DrawGameValue_Time(TimeLeft);
10431 if (!TimeLeft && setup.time_limit)
10432 for (i = 0; i < MAX_PLAYERS; i++)
10433 KillPlayer(&stored_player[i]);
10435 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10436 DrawGameValue_Time(TimePlayed);
10439 if (tape.single_step && tape.recording && !tape.pausing &&
10440 !player->programmed_action)
10441 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10445 void ScrollScreen(struct PlayerInfo *player, int mode)
10447 static unsigned long screen_frame_counter = 0;
10449 if (mode == SCROLL_INIT)
10451 /* set scrolling step size according to actual player's moving speed */
10452 ScrollStepSize = TILEX / player->move_delay_value;
10454 screen_frame_counter = FrameCounter;
10455 ScreenMovDir = player->MovDir;
10456 ScreenMovPos = player->MovPos;
10457 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10460 else if (!FrameReached(&screen_frame_counter, 1))
10465 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10466 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10467 redraw_mask |= REDRAW_FIELD;
10470 ScreenMovDir = MV_NONE;
10473 void TestIfPlayerTouchesCustomElement(int x, int y)
10475 static int xy[4][2] =
10482 static int trigger_sides[4][2] =
10484 /* center side border side */
10485 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10486 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10487 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10488 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10490 static int touch_dir[4] =
10492 MV_LEFT | MV_RIGHT,
10497 int center_element = Feld[x][y]; /* should always be non-moving! */
10500 for (i = 0; i < NUM_DIRECTIONS; i++)
10502 int xx = x + xy[i][0];
10503 int yy = y + xy[i][1];
10504 int center_side = trigger_sides[i][0];
10505 int border_side = trigger_sides[i][1];
10506 int border_element;
10508 if (!IN_LEV_FIELD(xx, yy))
10511 if (IS_PLAYER(x, y))
10513 struct PlayerInfo *player = PLAYERINFO(x, y);
10515 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10516 border_element = Feld[xx][yy]; /* may be moving! */
10517 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10518 border_element = Feld[xx][yy];
10519 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10520 border_element = MovingOrBlocked2Element(xx, yy);
10522 continue; /* center and border element do not touch */
10524 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10525 player->index_bit, border_side);
10526 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10527 CE_PLAYER_TOUCHES_X,
10528 player->index_bit, border_side);
10530 else if (IS_PLAYER(xx, yy))
10532 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10534 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10536 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10537 continue; /* center and border element do not touch */
10540 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10541 player->index_bit, center_side);
10542 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10543 CE_PLAYER_TOUCHES_X,
10544 player->index_bit, center_side);
10550 #if USE_ELEMENT_TOUCHING_BUGFIX
10552 void TestIfElementTouchesCustomElement(int x, int y)
10554 static int xy[4][2] =
10561 static int trigger_sides[4][2] =
10563 /* center side border side */
10564 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10565 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10566 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10567 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10569 static int touch_dir[4] =
10571 MV_LEFT | MV_RIGHT,
10576 boolean change_center_element = FALSE;
10577 int center_element = Feld[x][y]; /* should always be non-moving! */
10578 int border_element_old[NUM_DIRECTIONS];
10581 for (i = 0; i < NUM_DIRECTIONS; i++)
10583 int xx = x + xy[i][0];
10584 int yy = y + xy[i][1];
10585 int border_element;
10587 border_element_old[i] = -1;
10589 if (!IN_LEV_FIELD(xx, yy))
10592 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10593 border_element = Feld[xx][yy]; /* may be moving! */
10594 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10595 border_element = Feld[xx][yy];
10596 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10597 border_element = MovingOrBlocked2Element(xx, yy);
10599 continue; /* center and border element do not touch */
10601 border_element_old[i] = border_element;
10604 for (i = 0; i < NUM_DIRECTIONS; i++)
10606 int xx = x + xy[i][0];
10607 int yy = y + xy[i][1];
10608 int center_side = trigger_sides[i][0];
10609 int border_element = border_element_old[i];
10611 if (border_element == -1)
10614 /* check for change of border element */
10615 CheckElementChangeBySide(xx, yy, border_element, center_element,
10616 CE_TOUCHING_X, center_side);
10619 for (i = 0; i < NUM_DIRECTIONS; i++)
10621 int border_side = trigger_sides[i][1];
10622 int border_element = border_element_old[i];
10624 if (border_element == -1)
10627 /* check for change of center element (but change it only once) */
10628 if (!change_center_element)
10629 change_center_element =
10630 CheckElementChangeBySide(x, y, center_element, border_element,
10631 CE_TOUCHING_X, border_side);
10637 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10639 static int xy[4][2] =
10646 static int trigger_sides[4][2] =
10648 /* center side border side */
10649 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10650 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10651 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10652 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10654 static int touch_dir[4] =
10656 MV_LEFT | MV_RIGHT,
10661 boolean change_center_element = FALSE;
10662 int center_element = Feld[x][y]; /* should always be non-moving! */
10665 for (i = 0; i < NUM_DIRECTIONS; i++)
10667 int xx = x + xy[i][0];
10668 int yy = y + xy[i][1];
10669 int center_side = trigger_sides[i][0];
10670 int border_side = trigger_sides[i][1];
10671 int border_element;
10673 if (!IN_LEV_FIELD(xx, yy))
10676 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10677 border_element = Feld[xx][yy]; /* may be moving! */
10678 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10679 border_element = Feld[xx][yy];
10680 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10681 border_element = MovingOrBlocked2Element(xx, yy);
10683 continue; /* center and border element do not touch */
10685 /* check for change of center element (but change it only once) */
10686 if (!change_center_element)
10687 change_center_element =
10688 CheckElementChangeBySide(x, y, center_element, border_element,
10689 CE_TOUCHING_X, border_side);
10691 /* check for change of border element */
10692 CheckElementChangeBySide(xx, yy, border_element, center_element,
10693 CE_TOUCHING_X, center_side);
10699 void TestIfElementHitsCustomElement(int x, int y, int direction)
10701 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10702 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10703 int hitx = x + dx, hity = y + dy;
10704 int hitting_element = Feld[x][y];
10705 int touched_element;
10707 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10710 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10711 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10713 if (IN_LEV_FIELD(hitx, hity))
10715 int opposite_direction = MV_DIR_OPPOSITE(direction);
10716 int hitting_side = direction;
10717 int touched_side = opposite_direction;
10718 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10719 MovDir[hitx][hity] != direction ||
10720 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10726 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10727 CE_HITTING_X, touched_side);
10729 CheckElementChangeBySide(hitx, hity, touched_element,
10730 hitting_element, CE_HIT_BY_X, hitting_side);
10732 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10733 CE_HIT_BY_SOMETHING, opposite_direction);
10737 /* "hitting something" is also true when hitting the playfield border */
10738 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10739 CE_HITTING_SOMETHING, direction);
10743 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10745 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10746 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10747 int hitx = x + dx, hity = y + dy;
10748 int hitting_element = Feld[x][y];
10749 int touched_element;
10751 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10752 !IS_FREE(hitx, hity) &&
10753 (!IS_MOVING(hitx, hity) ||
10754 MovDir[hitx][hity] != direction ||
10755 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10758 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10762 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10766 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10767 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10769 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10770 EP_CAN_SMASH_EVERYTHING, direction);
10772 if (IN_LEV_FIELD(hitx, hity))
10774 int opposite_direction = MV_DIR_OPPOSITE(direction);
10775 int hitting_side = direction;
10776 int touched_side = opposite_direction;
10778 int touched_element = MovingOrBlocked2Element(hitx, hity);
10781 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10782 MovDir[hitx][hity] != direction ||
10783 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10792 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10793 CE_SMASHED_BY_SOMETHING, opposite_direction);
10795 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10796 CE_OTHER_IS_SMASHING, touched_side);
10798 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10799 CE_OTHER_GETS_SMASHED, hitting_side);
10805 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10807 int i, kill_x = -1, kill_y = -1;
10809 int bad_element = -1;
10810 static int test_xy[4][2] =
10817 static int test_dir[4] =
10825 for (i = 0; i < NUM_DIRECTIONS; i++)
10827 int test_x, test_y, test_move_dir, test_element;
10829 test_x = good_x + test_xy[i][0];
10830 test_y = good_y + test_xy[i][1];
10832 if (!IN_LEV_FIELD(test_x, test_y))
10836 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10838 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10840 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10841 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10843 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10844 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10848 bad_element = test_element;
10854 if (kill_x != -1 || kill_y != -1)
10856 if (IS_PLAYER(good_x, good_y))
10858 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10860 if (player->shield_deadly_time_left > 0 &&
10861 !IS_INDESTRUCTIBLE(bad_element))
10862 Bang(kill_x, kill_y);
10863 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10864 KillPlayer(player);
10867 Bang(good_x, good_y);
10871 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10873 int i, kill_x = -1, kill_y = -1;
10874 int bad_element = Feld[bad_x][bad_y];
10875 static int test_xy[4][2] =
10882 static int touch_dir[4] =
10884 MV_LEFT | MV_RIGHT,
10889 static int test_dir[4] =
10897 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10900 for (i = 0; i < NUM_DIRECTIONS; i++)
10902 int test_x, test_y, test_move_dir, test_element;
10904 test_x = bad_x + test_xy[i][0];
10905 test_y = bad_y + test_xy[i][1];
10906 if (!IN_LEV_FIELD(test_x, test_y))
10910 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10912 test_element = Feld[test_x][test_y];
10914 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10915 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10917 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10918 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10920 /* good thing is player or penguin that does not move away */
10921 if (IS_PLAYER(test_x, test_y))
10923 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10925 if (bad_element == EL_ROBOT && player->is_moving)
10926 continue; /* robot does not kill player if he is moving */
10928 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10930 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10931 continue; /* center and border element do not touch */
10938 else if (test_element == EL_PENGUIN)
10947 if (kill_x != -1 || kill_y != -1)
10949 if (IS_PLAYER(kill_x, kill_y))
10951 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10953 if (player->shield_deadly_time_left > 0 &&
10954 !IS_INDESTRUCTIBLE(bad_element))
10955 Bang(bad_x, bad_y);
10956 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10957 KillPlayer(player);
10960 Bang(kill_x, kill_y);
10964 void TestIfPlayerTouchesBadThing(int x, int y)
10966 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10969 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10971 TestIfGoodThingHitsBadThing(x, y, move_dir);
10974 void TestIfBadThingTouchesPlayer(int x, int y)
10976 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10979 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10981 TestIfBadThingHitsGoodThing(x, y, move_dir);
10984 void TestIfFriendTouchesBadThing(int x, int y)
10986 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10989 void TestIfBadThingTouchesFriend(int x, int y)
10991 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10994 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10996 int i, kill_x = bad_x, kill_y = bad_y;
10997 static int xy[4][2] =
11005 for (i = 0; i < NUM_DIRECTIONS; i++)
11009 x = bad_x + xy[i][0];
11010 y = bad_y + xy[i][1];
11011 if (!IN_LEV_FIELD(x, y))
11014 element = Feld[x][y];
11015 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11016 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11024 if (kill_x != bad_x || kill_y != bad_y)
11025 Bang(bad_x, bad_y);
11028 void KillPlayer(struct PlayerInfo *player)
11030 int jx = player->jx, jy = player->jy;
11032 if (!player->active)
11035 /* remove accessible field at the player's position */
11036 Feld[jx][jy] = EL_EMPTY;
11038 /* deactivate shield (else Bang()/Explode() would not work right) */
11039 player->shield_normal_time_left = 0;
11040 player->shield_deadly_time_left = 0;
11043 BuryPlayer(player);
11046 static void KillPlayerUnlessEnemyProtected(int x, int y)
11048 if (!PLAYER_ENEMY_PROTECTED(x, y))
11049 KillPlayer(PLAYERINFO(x, y));
11052 static void KillPlayerUnlessExplosionProtected(int x, int y)
11054 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11055 KillPlayer(PLAYERINFO(x, y));
11058 void BuryPlayer(struct PlayerInfo *player)
11060 int jx = player->jx, jy = player->jy;
11062 if (!player->active)
11065 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11066 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11068 player->GameOver = TRUE;
11069 RemovePlayer(player);
11072 void RemovePlayer(struct PlayerInfo *player)
11074 int jx = player->jx, jy = player->jy;
11075 int i, found = FALSE;
11077 player->present = FALSE;
11078 player->active = FALSE;
11080 if (!ExplodeField[jx][jy])
11081 StorePlayer[jx][jy] = 0;
11083 if (player->is_moving)
11084 DrawLevelField(player->last_jx, player->last_jy);
11086 for (i = 0; i < MAX_PLAYERS; i++)
11087 if (stored_player[i].active)
11091 AllPlayersGone = TRUE;
11097 #if USE_NEW_SNAP_DELAY
11098 static void setFieldForSnapping(int x, int y, int element, int direction)
11100 struct ElementInfo *ei = &element_info[element];
11101 int direction_bit = MV_DIR_TO_BIT(direction);
11102 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11103 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11104 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11106 Feld[x][y] = EL_ELEMENT_SNAPPING;
11107 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11109 ResetGfxAnimation(x, y);
11111 GfxElement[x][y] = element;
11112 GfxAction[x][y] = action;
11113 GfxDir[x][y] = direction;
11114 GfxFrame[x][y] = -1;
11119 =============================================================================
11120 checkDiagonalPushing()
11121 -----------------------------------------------------------------------------
11122 check if diagonal input device direction results in pushing of object
11123 (by checking if the alternative direction is walkable, diggable, ...)
11124 =============================================================================
11127 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11128 int x, int y, int real_dx, int real_dy)
11130 int jx, jy, dx, dy, xx, yy;
11132 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11135 /* diagonal direction: check alternative direction */
11140 xx = jx + (dx == 0 ? real_dx : 0);
11141 yy = jy + (dy == 0 ? real_dy : 0);
11143 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11147 =============================================================================
11149 -----------------------------------------------------------------------------
11150 x, y: field next to player (non-diagonal) to try to dig to
11151 real_dx, real_dy: direction as read from input device (can be diagonal)
11152 =============================================================================
11155 int DigField(struct PlayerInfo *player,
11156 int oldx, int oldy, int x, int y,
11157 int real_dx, int real_dy, int mode)
11159 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11160 boolean player_was_pushing = player->is_pushing;
11161 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11162 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11163 int jx = oldx, jy = oldy;
11164 int dx = x - jx, dy = y - jy;
11165 int nextx = x + dx, nexty = y + dy;
11166 int move_direction = (dx == -1 ? MV_LEFT :
11167 dx == +1 ? MV_RIGHT :
11169 dy == +1 ? MV_DOWN : MV_NONE);
11170 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11171 int dig_side = MV_DIR_OPPOSITE(move_direction);
11172 int old_element = Feld[jx][jy];
11173 #if USE_FIXED_DONT_RUN_INTO
11174 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11180 if (is_player) /* function can also be called by EL_PENGUIN */
11182 if (player->MovPos == 0)
11184 player->is_digging = FALSE;
11185 player->is_collecting = FALSE;
11188 if (player->MovPos == 0) /* last pushing move finished */
11189 player->is_pushing = FALSE;
11191 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11193 player->is_switching = FALSE;
11194 player->push_delay = -1;
11196 return MP_NO_ACTION;
11200 #if !USE_FIXED_DONT_RUN_INTO
11201 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11202 return MP_NO_ACTION;
11205 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11206 old_element = Back[jx][jy];
11208 /* in case of element dropped at player position, check background */
11209 else if (Back[jx][jy] != EL_EMPTY &&
11210 game.engine_version >= VERSION_IDENT(2,2,0,0))
11211 old_element = Back[jx][jy];
11213 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11214 return MP_NO_ACTION; /* field has no opening in this direction */
11216 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11217 return MP_NO_ACTION; /* field has no opening in this direction */
11219 #if USE_FIXED_DONT_RUN_INTO
11220 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11224 Feld[jx][jy] = player->artwork_element;
11225 InitMovingField(jx, jy, MV_DOWN);
11226 Store[jx][jy] = EL_ACID;
11227 ContinueMoving(jx, jy);
11228 BuryPlayer(player);
11230 return MP_DONT_RUN_INTO;
11234 #if USE_FIXED_DONT_RUN_INTO
11235 if (player_can_move && DONT_RUN_INTO(element))
11237 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11239 return MP_DONT_RUN_INTO;
11243 #if USE_FIXED_DONT_RUN_INTO
11244 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11245 return MP_NO_ACTION;
11248 #if !USE_FIXED_DONT_RUN_INTO
11249 element = Feld[x][y];
11252 collect_count = element_info[element].collect_count_initial;
11254 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11255 return MP_NO_ACTION;
11257 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11258 player_can_move = player_can_move_or_snap;
11260 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11261 game.engine_version >= VERSION_IDENT(2,2,0,0))
11263 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11264 player->index_bit, dig_side);
11265 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11266 player->index_bit, dig_side);
11268 if (Feld[x][y] != element) /* field changed by snapping */
11271 return MP_NO_ACTION;
11274 #if USE_PLAYER_GRAVITY
11275 if (player->gravity && is_player && !player->is_auto_moving &&
11276 canFallDown(player) && move_direction != MV_DOWN &&
11277 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11278 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11280 if (game.gravity && is_player && !player->is_auto_moving &&
11281 canFallDown(player) && move_direction != MV_DOWN &&
11282 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11283 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11286 if (player_can_move &&
11287 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11289 int sound_element = SND_ELEMENT(element);
11290 int sound_action = ACTION_WALKING;
11292 if (IS_RND_GATE(element))
11294 if (!player->key[RND_GATE_NR(element)])
11295 return MP_NO_ACTION;
11297 else if (IS_RND_GATE_GRAY(element))
11299 if (!player->key[RND_GATE_GRAY_NR(element)])
11300 return MP_NO_ACTION;
11302 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11304 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11305 return MP_NO_ACTION;
11307 else if (element == EL_EXIT_OPEN ||
11308 element == EL_SP_EXIT_OPEN ||
11309 element == EL_SP_EXIT_OPENING)
11311 sound_action = ACTION_PASSING; /* player is passing exit */
11313 else if (element == EL_EMPTY)
11315 sound_action = ACTION_MOVING; /* nothing to walk on */
11318 /* play sound from background or player, whatever is available */
11319 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11320 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11322 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11324 else if (player_can_move &&
11325 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11327 if (!ACCESS_FROM(element, opposite_direction))
11328 return MP_NO_ACTION; /* field not accessible from this direction */
11330 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11331 return MP_NO_ACTION;
11333 if (IS_EM_GATE(element))
11335 if (!player->key[EM_GATE_NR(element)])
11336 return MP_NO_ACTION;
11338 else if (IS_EM_GATE_GRAY(element))
11340 if (!player->key[EM_GATE_GRAY_NR(element)])
11341 return MP_NO_ACTION;
11343 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11345 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11346 return MP_NO_ACTION;
11348 else if (IS_EMC_GATE(element))
11350 if (!player->key[EMC_GATE_NR(element)])
11351 return MP_NO_ACTION;
11353 else if (IS_EMC_GATE_GRAY(element))
11355 if (!player->key[EMC_GATE_GRAY_NR(element)])
11356 return MP_NO_ACTION;
11358 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11360 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11361 return MP_NO_ACTION;
11363 else if (IS_SP_PORT(element))
11365 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11366 element == EL_SP_GRAVITY_PORT_RIGHT ||
11367 element == EL_SP_GRAVITY_PORT_UP ||
11368 element == EL_SP_GRAVITY_PORT_DOWN)
11369 #if USE_PLAYER_GRAVITY
11370 player->gravity = !player->gravity;
11372 game.gravity = !game.gravity;
11374 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11375 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11376 element == EL_SP_GRAVITY_ON_PORT_UP ||
11377 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11378 #if USE_PLAYER_GRAVITY
11379 player->gravity = TRUE;
11381 game.gravity = TRUE;
11383 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11384 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11385 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11386 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11387 #if USE_PLAYER_GRAVITY
11388 player->gravity = FALSE;
11390 game.gravity = FALSE;
11394 /* automatically move to the next field with double speed */
11395 player->programmed_action = move_direction;
11397 if (player->move_delay_reset_counter == 0)
11399 player->move_delay_reset_counter = 2; /* two double speed steps */
11401 DOUBLE_PLAYER_SPEED(player);
11404 PlayLevelSoundAction(x, y, ACTION_PASSING);
11406 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11410 if (mode != DF_SNAP)
11412 GfxElement[x][y] = GFX_ELEMENT(element);
11413 player->is_digging = TRUE;
11416 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11418 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11419 player->index_bit, dig_side);
11421 if (mode == DF_SNAP)
11423 #if USE_NEW_SNAP_DELAY
11424 if (level.block_snap_field)
11425 setFieldForSnapping(x, y, element, move_direction);
11427 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11429 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11432 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11433 player->index_bit, dig_side);
11436 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11440 if (is_player && mode != DF_SNAP)
11442 GfxElement[x][y] = element;
11443 player->is_collecting = TRUE;
11446 if (element == EL_SPEED_PILL)
11448 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11450 else if (element == EL_EXTRA_TIME && level.time > 0)
11452 TimeLeft += level.extra_time;
11453 DrawGameValue_Time(TimeLeft);
11455 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11457 player->shield_normal_time_left += level.shield_normal_time;
11458 if (element == EL_SHIELD_DEADLY)
11459 player->shield_deadly_time_left += level.shield_deadly_time;
11461 else if (element == EL_DYNAMITE ||
11462 element == EL_EM_DYNAMITE ||
11463 element == EL_SP_DISK_RED)
11465 if (player->inventory_size < MAX_INVENTORY_SIZE)
11466 player->inventory_element[player->inventory_size++] = element;
11468 DrawGameDoorValues();
11470 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11472 player->dynabomb_count++;
11473 player->dynabombs_left++;
11475 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11477 player->dynabomb_size++;
11479 else if (element == EL_DYNABOMB_INCREASE_POWER)
11481 player->dynabomb_xl = TRUE;
11483 else if (IS_KEY(element))
11485 player->key[KEY_NR(element)] = TRUE;
11487 DrawGameDoorValues();
11489 else if (IS_ENVELOPE(element))
11491 player->show_envelope = element;
11493 else if (element == EL_EMC_LENSES)
11495 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11497 RedrawAllInvisibleElementsForLenses();
11499 else if (element == EL_EMC_MAGNIFIER)
11501 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11503 RedrawAllInvisibleElementsForMagnifier();
11505 else if (IS_DROPPABLE(element) ||
11506 IS_THROWABLE(element)) /* can be collected and dropped */
11510 if (collect_count == 0)
11511 player->inventory_infinite_element = element;
11513 for (i = 0; i < collect_count; i++)
11514 if (player->inventory_size < MAX_INVENTORY_SIZE)
11515 player->inventory_element[player->inventory_size++] = element;
11517 DrawGameDoorValues();
11519 else if (collect_count > 0)
11521 local_player->gems_still_needed -= collect_count;
11522 if (local_player->gems_still_needed < 0)
11523 local_player->gems_still_needed = 0;
11525 DrawGameValue_Emeralds(local_player->gems_still_needed);
11528 RaiseScoreElement(element);
11529 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11532 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11533 player->index_bit, dig_side);
11535 if (mode == DF_SNAP)
11537 #if USE_NEW_SNAP_DELAY
11538 if (level.block_snap_field)
11539 setFieldForSnapping(x, y, element, move_direction);
11541 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11543 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11546 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11547 player->index_bit, dig_side);
11550 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11552 if (mode == DF_SNAP && element != EL_BD_ROCK)
11553 return MP_NO_ACTION;
11555 if (CAN_FALL(element) && dy)
11556 return MP_NO_ACTION;
11558 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11559 !(element == EL_SPRING && level.use_spring_bug))
11560 return MP_NO_ACTION;
11562 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11563 ((move_direction & MV_VERTICAL &&
11564 ((element_info[element].move_pattern & MV_LEFT &&
11565 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11566 (element_info[element].move_pattern & MV_RIGHT &&
11567 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11568 (move_direction & MV_HORIZONTAL &&
11569 ((element_info[element].move_pattern & MV_UP &&
11570 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11571 (element_info[element].move_pattern & MV_DOWN &&
11572 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11573 return MP_NO_ACTION;
11575 /* do not push elements already moving away faster than player */
11576 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11577 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11578 return MP_NO_ACTION;
11580 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11582 if (player->push_delay_value == -1 || !player_was_pushing)
11583 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11585 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11587 if (player->push_delay_value == -1)
11588 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11590 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11592 if (!player->is_pushing)
11593 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11596 player->is_pushing = TRUE;
11597 player->is_active = TRUE;
11599 if (!(IN_LEV_FIELD(nextx, nexty) &&
11600 (IS_FREE(nextx, nexty) ||
11601 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11602 IS_SB_ELEMENT(element)))))
11603 return MP_NO_ACTION;
11605 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11606 return MP_NO_ACTION;
11608 if (player->push_delay == -1) /* new pushing; restart delay */
11609 player->push_delay = 0;
11611 if (player->push_delay < player->push_delay_value &&
11612 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11613 element != EL_SPRING && element != EL_BALLOON)
11615 /* make sure that there is no move delay before next try to push */
11616 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11617 player->move_delay = 0;
11619 return MP_NO_ACTION;
11622 if (IS_SB_ELEMENT(element))
11624 if (element == EL_SOKOBAN_FIELD_FULL)
11626 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11627 local_player->sokobanfields_still_needed++;
11630 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11632 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11633 local_player->sokobanfields_still_needed--;
11636 Feld[x][y] = EL_SOKOBAN_OBJECT;
11638 if (Back[x][y] == Back[nextx][nexty])
11639 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11640 else if (Back[x][y] != 0)
11641 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11644 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11647 if (local_player->sokobanfields_still_needed == 0 &&
11648 game.emulation == EMU_SOKOBAN)
11650 PlayerWins(player);
11652 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11656 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11658 InitMovingField(x, y, move_direction);
11659 GfxAction[x][y] = ACTION_PUSHING;
11661 if (mode == DF_SNAP)
11662 ContinueMoving(x, y);
11664 MovPos[x][y] = (dx != 0 ? dx : dy);
11666 Pushed[x][y] = TRUE;
11667 Pushed[nextx][nexty] = TRUE;
11669 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11670 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11672 player->push_delay_value = -1; /* get new value later */
11674 /* check for element change _after_ element has been pushed */
11675 if (game.use_change_when_pushing_bug)
11677 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11678 player->index_bit, dig_side);
11679 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11680 player->index_bit, dig_side);
11683 else if (IS_SWITCHABLE(element))
11685 if (PLAYER_SWITCHING(player, x, y))
11687 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11688 player->index_bit, dig_side);
11693 player->is_switching = TRUE;
11694 player->switch_x = x;
11695 player->switch_y = y;
11697 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11699 if (element == EL_ROBOT_WHEEL)
11701 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11705 DrawLevelField(x, y);
11707 else if (element == EL_SP_TERMINAL)
11711 SCAN_PLAYFIELD(xx, yy)
11713 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11715 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11716 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11719 else if (IS_BELT_SWITCH(element))
11721 ToggleBeltSwitch(x, y);
11723 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11724 element == EL_SWITCHGATE_SWITCH_DOWN)
11726 ToggleSwitchgateSwitch(x, y);
11728 else if (element == EL_LIGHT_SWITCH ||
11729 element == EL_LIGHT_SWITCH_ACTIVE)
11731 ToggleLightSwitch(x, y);
11733 else if (element == EL_TIMEGATE_SWITCH)
11735 ActivateTimegateSwitch(x, y);
11737 else if (element == EL_BALLOON_SWITCH_LEFT ||
11738 element == EL_BALLOON_SWITCH_RIGHT ||
11739 element == EL_BALLOON_SWITCH_UP ||
11740 element == EL_BALLOON_SWITCH_DOWN ||
11741 element == EL_BALLOON_SWITCH_NONE ||
11742 element == EL_BALLOON_SWITCH_ANY)
11744 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11745 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11746 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11747 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11748 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11751 else if (element == EL_LAMP)
11753 Feld[x][y] = EL_LAMP_ACTIVE;
11754 local_player->lights_still_needed--;
11756 ResetGfxAnimation(x, y);
11757 DrawLevelField(x, y);
11759 else if (element == EL_TIME_ORB_FULL)
11761 Feld[x][y] = EL_TIME_ORB_EMPTY;
11763 if (level.time > 0 || level.use_time_orb_bug)
11765 TimeLeft += level.time_orb_time;
11766 DrawGameValue_Time(TimeLeft);
11769 ResetGfxAnimation(x, y);
11770 DrawLevelField(x, y);
11772 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11773 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11777 game.ball_state = !game.ball_state;
11779 SCAN_PLAYFIELD(xx, yy)
11781 int e = Feld[xx][yy];
11783 if (game.ball_state)
11785 if (e == EL_EMC_MAGIC_BALL)
11786 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11787 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11788 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11792 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11793 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11794 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11795 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11800 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11801 player->index_bit, dig_side);
11803 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11804 player->index_bit, dig_side);
11806 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11807 player->index_bit, dig_side);
11813 if (!PLAYER_SWITCHING(player, x, y))
11815 player->is_switching = TRUE;
11816 player->switch_x = x;
11817 player->switch_y = y;
11819 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11820 player->index_bit, dig_side);
11821 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11822 player->index_bit, dig_side);
11824 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11825 player->index_bit, dig_side);
11826 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11827 player->index_bit, dig_side);
11830 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11831 player->index_bit, dig_side);
11832 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11833 player->index_bit, dig_side);
11835 return MP_NO_ACTION;
11838 player->push_delay = -1;
11840 if (is_player) /* function can also be called by EL_PENGUIN */
11842 if (Feld[x][y] != element) /* really digged/collected something */
11844 player->is_collecting = !player->is_digging;
11845 player->is_active = TRUE;
11852 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11854 int jx = player->jx, jy = player->jy;
11855 int x = jx + dx, y = jy + dy;
11856 int snap_direction = (dx == -1 ? MV_LEFT :
11857 dx == +1 ? MV_RIGHT :
11859 dy == +1 ? MV_DOWN : MV_NONE);
11860 boolean can_continue_snapping = (level.continuous_snapping &&
11861 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
11863 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11866 if (!player->active || !IN_LEV_FIELD(x, y))
11874 if (player->MovPos == 0)
11875 player->is_pushing = FALSE;
11877 player->is_snapping = FALSE;
11879 if (player->MovPos == 0)
11881 player->is_moving = FALSE;
11882 player->is_digging = FALSE;
11883 player->is_collecting = FALSE;
11889 #if USE_NEW_CONTINUOUS_SNAPPING
11890 /* prevent snapping with already pressed snap key when not allowed */
11891 if (player->is_snapping && !can_continue_snapping)
11894 if (player->is_snapping)
11898 player->MovDir = snap_direction;
11900 if (player->MovPos == 0)
11902 player->is_moving = FALSE;
11903 player->is_digging = FALSE;
11904 player->is_collecting = FALSE;
11907 player->is_dropping = FALSE;
11908 player->is_dropping_pressed = FALSE;
11909 player->drop_pressed_delay = 0;
11911 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11914 player->is_snapping = TRUE;
11915 player->is_active = TRUE;
11917 if (player->MovPos == 0)
11919 player->is_moving = FALSE;
11920 player->is_digging = FALSE;
11921 player->is_collecting = FALSE;
11924 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11925 DrawLevelField(player->last_jx, player->last_jy);
11927 DrawLevelField(x, y);
11932 boolean DropElement(struct PlayerInfo *player)
11934 int old_element, new_element;
11935 int dropx = player->jx, dropy = player->jy;
11936 int drop_direction = player->MovDir;
11937 int drop_side = drop_direction;
11938 int drop_element = (player->inventory_size > 0 ?
11939 player->inventory_element[player->inventory_size - 1] :
11940 player->inventory_infinite_element != EL_UNDEFINED ?
11941 player->inventory_infinite_element :
11942 player->dynabombs_left > 0 ?
11943 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11946 player->is_dropping_pressed = TRUE;
11948 /* do not drop an element on top of another element; when holding drop key
11949 pressed without moving, dropped element must move away before the next
11950 element can be dropped (this is especially important if the next element
11951 is dynamite, which can be placed on background for historical reasons) */
11952 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11955 if (IS_THROWABLE(drop_element))
11957 dropx += GET_DX_FROM_DIR(drop_direction);
11958 dropy += GET_DY_FROM_DIR(drop_direction);
11960 if (!IN_LEV_FIELD(dropx, dropy))
11964 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11965 new_element = drop_element; /* default: no change when dropping */
11967 /* check if player is active, not moving and ready to drop */
11968 if (!player->active || player->MovPos || player->drop_delay > 0)
11971 /* check if player has anything that can be dropped */
11972 if (new_element == EL_UNDEFINED)
11975 /* check if drop key was pressed long enough for EM style dynamite */
11976 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
11979 /* check if anything can be dropped at the current position */
11980 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11983 /* collected custom elements can only be dropped on empty fields */
11984 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11987 if (old_element != EL_EMPTY)
11988 Back[dropx][dropy] = old_element; /* store old element on this field */
11990 ResetGfxAnimation(dropx, dropy);
11991 ResetRandomAnimationValue(dropx, dropy);
11993 if (player->inventory_size > 0 ||
11994 player->inventory_infinite_element != EL_UNDEFINED)
11996 if (player->inventory_size > 0)
11998 player->inventory_size--;
12000 DrawGameDoorValues();
12002 if (new_element == EL_DYNAMITE)
12003 new_element = EL_DYNAMITE_ACTIVE;
12004 else if (new_element == EL_EM_DYNAMITE)
12005 new_element = EL_EM_DYNAMITE_ACTIVE;
12006 else if (new_element == EL_SP_DISK_RED)
12007 new_element = EL_SP_DISK_RED_ACTIVE;
12010 Feld[dropx][dropy] = new_element;
12012 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12013 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12014 el2img(Feld[dropx][dropy]), 0);
12016 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12018 /* needed if previous element just changed to "empty" in the last frame */
12019 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12021 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12022 player->index_bit, drop_side);
12023 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12025 player->index_bit, drop_side);
12027 TestIfElementTouchesCustomElement(dropx, dropy);
12029 else /* player is dropping a dyna bomb */
12031 player->dynabombs_left--;
12033 Feld[dropx][dropy] = new_element;
12035 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12036 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12037 el2img(Feld[dropx][dropy]), 0);
12039 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12042 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12043 InitField_WithBug1(dropx, dropy, FALSE);
12045 new_element = Feld[dropx][dropy]; /* element might have changed */
12047 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12048 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12050 int move_direction, nextx, nexty;
12052 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12053 MovDir[dropx][dropy] = drop_direction;
12055 move_direction = MovDir[dropx][dropy];
12056 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12057 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12059 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12060 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12063 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12064 player->is_dropping = TRUE;
12066 player->drop_pressed_delay = 0;
12067 player->is_dropping_pressed = FALSE;
12069 player->drop_x = dropx;
12070 player->drop_y = dropy;
12075 /* ------------------------------------------------------------------------- */
12076 /* game sound playing functions */
12077 /* ------------------------------------------------------------------------- */
12079 static int *loop_sound_frame = NULL;
12080 static int *loop_sound_volume = NULL;
12082 void InitPlayLevelSound()
12084 int num_sounds = getSoundListSize();
12086 checked_free(loop_sound_frame);
12087 checked_free(loop_sound_volume);
12089 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12090 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12093 static void PlayLevelSound(int x, int y, int nr)
12095 int sx = SCREENX(x), sy = SCREENY(y);
12096 int volume, stereo_position;
12097 int max_distance = 8;
12098 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12100 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12101 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12104 if (!IN_LEV_FIELD(x, y) ||
12105 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12106 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12109 volume = SOUND_MAX_VOLUME;
12111 if (!IN_SCR_FIELD(sx, sy))
12113 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12114 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12116 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12119 stereo_position = (SOUND_MAX_LEFT +
12120 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12121 (SCR_FIELDX + 2 * max_distance));
12123 if (IS_LOOP_SOUND(nr))
12125 /* This assures that quieter loop sounds do not overwrite louder ones,
12126 while restarting sound volume comparison with each new game frame. */
12128 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12131 loop_sound_volume[nr] = volume;
12132 loop_sound_frame[nr] = FrameCounter;
12135 PlaySoundExt(nr, volume, stereo_position, type);
12138 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12140 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12141 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12142 y < LEVELY(BY1) ? LEVELY(BY1) :
12143 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12147 static void PlayLevelSoundAction(int x, int y, int action)
12149 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12152 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12154 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12156 if (sound_effect != SND_UNDEFINED)
12157 PlayLevelSound(x, y, sound_effect);
12160 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12163 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12165 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12166 PlayLevelSound(x, y, sound_effect);
12169 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12171 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12173 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12174 PlayLevelSound(x, y, sound_effect);
12177 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12179 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12181 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12182 StopSound(sound_effect);
12185 static void PlayLevelMusic()
12187 if (levelset.music[level_nr] != MUS_UNDEFINED)
12188 PlayMusic(levelset.music[level_nr]); /* from config file */
12190 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12193 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
12195 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12196 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
12197 int x = xx - 1 - offset;
12198 int y = yy - 1 - offset;
12203 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12207 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12211 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12215 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12219 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12223 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12227 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12230 case SAMPLE_android_clone:
12231 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12234 case SAMPLE_android_move:
12235 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12238 case SAMPLE_spring:
12239 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12243 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12247 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12250 case SAMPLE_eater_eat:
12251 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12255 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12258 case SAMPLE_collect:
12259 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12262 case SAMPLE_diamond:
12263 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12266 case SAMPLE_squash:
12267 /* !!! CHECK THIS !!! */
12269 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12271 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12275 case SAMPLE_wonderfall:
12276 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12280 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12284 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12288 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12292 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12296 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12300 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12303 case SAMPLE_wonder:
12304 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12308 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12311 case SAMPLE_exit_open:
12312 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12315 case SAMPLE_exit_leave:
12316 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12319 case SAMPLE_dynamite:
12320 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12324 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12328 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12332 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12336 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12340 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12344 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12348 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12354 void ChangeTime(int value)
12356 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
12360 /* EMC game engine uses value from time counter of RND game engine */
12361 level.native_em_level->lev->time = *time;
12363 DrawGameValue_Time(*time);
12366 void RaiseScore(int value)
12368 /* EMC game engine and RND game engine have separate score counters */
12369 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
12370 &level.native_em_level->lev->score : &local_player->score);
12374 DrawGameValue_Score(*score);
12378 void RaiseScore(int value)
12380 local_player->score += value;
12382 DrawGameValue_Score(local_player->score);
12385 void RaiseScoreElement(int element)
12390 case EL_BD_DIAMOND:
12391 case EL_EMERALD_YELLOW:
12392 case EL_EMERALD_RED:
12393 case EL_EMERALD_PURPLE:
12394 case EL_SP_INFOTRON:
12395 RaiseScore(level.score[SC_EMERALD]);
12398 RaiseScore(level.score[SC_DIAMOND]);
12401 RaiseScore(level.score[SC_CRYSTAL]);
12404 RaiseScore(level.score[SC_PEARL]);
12407 case EL_BD_BUTTERFLY:
12408 case EL_SP_ELECTRON:
12409 RaiseScore(level.score[SC_BUG]);
12412 case EL_BD_FIREFLY:
12413 case EL_SP_SNIKSNAK:
12414 RaiseScore(level.score[SC_SPACESHIP]);
12417 case EL_DARK_YAMYAM:
12418 RaiseScore(level.score[SC_YAMYAM]);
12421 RaiseScore(level.score[SC_ROBOT]);
12424 RaiseScore(level.score[SC_PACMAN]);
12427 RaiseScore(level.score[SC_NUT]);
12430 case EL_EM_DYNAMITE:
12431 case EL_SP_DISK_RED:
12432 case EL_DYNABOMB_INCREASE_NUMBER:
12433 case EL_DYNABOMB_INCREASE_SIZE:
12434 case EL_DYNABOMB_INCREASE_POWER:
12435 RaiseScore(level.score[SC_DYNAMITE]);
12437 case EL_SHIELD_NORMAL:
12438 case EL_SHIELD_DEADLY:
12439 RaiseScore(level.score[SC_SHIELD]);
12441 case EL_EXTRA_TIME:
12442 RaiseScore(level.extra_time_score);
12456 RaiseScore(level.score[SC_KEY]);
12459 RaiseScore(element_info[element].collect_score);
12464 void RequestQuitGame(boolean ask_if_really_quit)
12466 if (AllPlayersGone ||
12467 !ask_if_really_quit ||
12468 level_editor_test_game ||
12469 Request("Do you really want to quit the game ?",
12470 REQ_ASK | REQ_STAY_CLOSED))
12472 #if defined(NETWORK_AVALIABLE)
12473 if (options.network)
12474 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12478 if (!ask_if_really_quit || level_editor_test_game)
12480 game_status = GAME_MODE_MAIN;
12486 FadeOut(REDRAW_FIELD);
12488 game_status = GAME_MODE_MAIN;
12490 DrawAndFadeInMainMenu(REDRAW_FIELD);
12496 if (tape.playing && tape.deactivate_display)
12497 TapeDeactivateDisplayOff(TRUE);
12499 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12501 if (tape.playing && tape.deactivate_display)
12502 TapeDeactivateDisplayOn();
12507 /* ------------------------------------------------------------------------- */
12508 /* random generator functions */
12509 /* ------------------------------------------------------------------------- */
12511 unsigned int InitEngineRandom_RND(long seed)
12513 game.num_random_calls = 0;
12516 unsigned int rnd_seed = InitEngineRandom(seed);
12518 printf("::: START RND: %d\n", rnd_seed);
12523 return InitEngineRandom(seed);
12529 unsigned int RND(int max)
12533 game.num_random_calls++;
12535 return GetEngineRandom(max);
12542 /* ------------------------------------------------------------------------- */
12543 /* game engine snapshot handling functions */
12544 /* ------------------------------------------------------------------------- */
12546 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
12548 struct EngineSnapshotInfo
12550 /* runtime values for custom element collect score */
12551 int collect_score[NUM_CUSTOM_ELEMENTS];
12553 /* runtime values for group element choice position */
12554 int choice_pos[NUM_GROUP_ELEMENTS];
12556 /* runtime values for belt position animations */
12557 int belt_graphic[4 * NUM_BELT_PARTS];
12558 int belt_anim_mode[4 * NUM_BELT_PARTS];
12561 struct EngineSnapshotNodeInfo
12568 static struct EngineSnapshotInfo engine_snapshot_rnd;
12569 static ListNode *engine_snapshot_list = NULL;
12570 static char *snapshot_level_identifier = NULL;
12571 static int snapshot_level_nr = -1;
12573 void FreeEngineSnapshot()
12575 while (engine_snapshot_list != NULL)
12576 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
12579 setString(&snapshot_level_identifier, NULL);
12580 snapshot_level_nr = -1;
12583 static void SaveEngineSnapshotValues_RND()
12585 static int belt_base_active_element[4] =
12587 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
12588 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
12589 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
12590 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
12594 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12596 int element = EL_CUSTOM_START + i;
12598 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
12601 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12603 int element = EL_GROUP_START + i;
12605 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
12608 for (i = 0; i < 4; i++)
12610 for (j = 0; j < NUM_BELT_PARTS; j++)
12612 int element = belt_base_active_element[i] + j;
12613 int graphic = el2img(element);
12614 int anim_mode = graphic_info[graphic].anim_mode;
12616 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
12617 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
12622 static void LoadEngineSnapshotValues_RND()
12624 unsigned long num_random_calls = game.num_random_calls;
12627 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12629 int element = EL_CUSTOM_START + i;
12631 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
12634 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12636 int element = EL_GROUP_START + i;
12638 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
12641 for (i = 0; i < 4; i++)
12643 for (j = 0; j < NUM_BELT_PARTS; j++)
12645 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
12646 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
12648 graphic_info[graphic].anim_mode = anim_mode;
12652 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
12654 InitRND(tape.random_seed);
12655 for (i = 0; i < num_random_calls; i++)
12659 if (game.num_random_calls != num_random_calls)
12661 Error(ERR_RETURN, "number of random calls out of sync");
12662 Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
12663 Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
12664 Error(ERR_EXIT, "this should not happen -- please debug");
12668 static void SaveEngineSnapshotBuffer(void *buffer, int size)
12670 struct EngineSnapshotNodeInfo *bi =
12671 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
12673 bi->buffer_orig = buffer;
12674 bi->buffer_copy = checked_malloc(size);
12677 memcpy(bi->buffer_copy, buffer, size);
12679 addNodeToList(&engine_snapshot_list, NULL, bi);
12682 void SaveEngineSnapshot()
12684 FreeEngineSnapshot(); /* free previous snapshot, if needed */
12686 /* copy some special values to a structure better suited for the snapshot */
12688 SaveEngineSnapshotValues_RND();
12689 SaveEngineSnapshotValues_EM();
12691 /* save values stored in special snapshot structure */
12693 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
12694 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
12696 /* save further RND engine values */
12698 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
12699 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
12700 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
12702 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
12703 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
12704 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
12705 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
12707 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
12708 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
12709 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
12710 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
12711 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
12713 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
12714 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
12715 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
12717 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
12719 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
12721 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
12722 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
12724 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
12725 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
12726 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
12727 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
12728 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
12729 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
12730 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
12731 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
12732 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
12733 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
12734 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
12735 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
12736 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
12737 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
12738 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
12739 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
12740 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
12742 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
12743 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
12745 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
12746 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
12747 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
12749 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
12750 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
12752 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
12753 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
12754 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
12755 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
12756 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
12758 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
12759 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
12761 /* save level identification information */
12763 setString(&snapshot_level_identifier, leveldir_current->identifier);
12764 snapshot_level_nr = level_nr;
12767 ListNode *node = engine_snapshot_list;
12770 while (node != NULL)
12772 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
12777 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
12781 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
12783 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
12786 void LoadEngineSnapshot()
12788 ListNode *node = engine_snapshot_list;
12790 if (engine_snapshot_list == NULL)
12793 while (node != NULL)
12795 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
12800 /* restore special values from snapshot structure */
12802 LoadEngineSnapshotValues_RND();
12803 LoadEngineSnapshotValues_EM();
12806 boolean CheckEngineSnapshot()
12808 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
12809 snapshot_level_nr == level_nr);
12813 /* ---------- new game button stuff ---------------------------------------- */
12815 /* graphic position values for game buttons */
12816 #define GAME_BUTTON_XSIZE 30
12817 #define GAME_BUTTON_YSIZE 30
12818 #define GAME_BUTTON_XPOS 5
12819 #define GAME_BUTTON_YPOS 215
12820 #define SOUND_BUTTON_XPOS 5
12821 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12823 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12824 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12825 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12826 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12827 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12828 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12835 } gamebutton_info[NUM_GAME_BUTTONS] =
12838 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12843 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12844 GAME_CTRL_ID_PAUSE,
12848 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12853 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12854 SOUND_CTRL_ID_MUSIC,
12855 "background music on/off"
12858 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12859 SOUND_CTRL_ID_LOOPS,
12860 "sound loops on/off"
12863 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12864 SOUND_CTRL_ID_SIMPLE,
12865 "normal sounds on/off"
12869 void CreateGameButtons()
12873 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12875 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12876 struct GadgetInfo *gi;
12879 unsigned long event_mask;
12880 int gd_xoffset, gd_yoffset;
12881 int gd_x1, gd_x2, gd_y1, gd_y2;
12884 gd_xoffset = gamebutton_info[i].x;
12885 gd_yoffset = gamebutton_info[i].y;
12886 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12887 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12889 if (id == GAME_CTRL_ID_STOP ||
12890 id == GAME_CTRL_ID_PAUSE ||
12891 id == GAME_CTRL_ID_PLAY)
12893 button_type = GD_TYPE_NORMAL_BUTTON;
12895 event_mask = GD_EVENT_RELEASED;
12896 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12897 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12901 button_type = GD_TYPE_CHECK_BUTTON;
12903 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12904 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12905 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12906 event_mask = GD_EVENT_PRESSED;
12907 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12908 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12911 gi = CreateGadget(GDI_CUSTOM_ID, id,
12912 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12913 GDI_X, DX + gd_xoffset,
12914 GDI_Y, DY + gd_yoffset,
12915 GDI_WIDTH, GAME_BUTTON_XSIZE,
12916 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12917 GDI_TYPE, button_type,
12918 GDI_STATE, GD_BUTTON_UNPRESSED,
12919 GDI_CHECKED, checked,
12920 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12921 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12922 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12923 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12924 GDI_EVENT_MASK, event_mask,
12925 GDI_CALLBACK_ACTION, HandleGameButtons,
12929 Error(ERR_EXIT, "cannot create gadget");
12931 game_gadget[id] = gi;
12935 void FreeGameButtons()
12939 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12940 FreeGadget(game_gadget[i]);
12943 static void MapGameButtons()
12947 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12948 MapGadget(game_gadget[i]);
12951 void UnmapGameButtons()
12955 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12956 UnmapGadget(game_gadget[i]);
12959 static void HandleGameButtons(struct GadgetInfo *gi)
12961 int id = gi->custom_id;
12963 if (game_status != GAME_MODE_PLAYING)
12968 case GAME_CTRL_ID_STOP:
12972 RequestQuitGame(TRUE);
12975 case GAME_CTRL_ID_PAUSE:
12976 if (options.network)
12978 #if defined(NETWORK_AVALIABLE)
12980 SendToServer_ContinuePlaying();
12982 SendToServer_PausePlaying();
12986 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12989 case GAME_CTRL_ID_PLAY:
12992 #if defined(NETWORK_AVALIABLE)
12993 if (options.network)
12994 SendToServer_ContinuePlaying();
12998 tape.pausing = FALSE;
12999 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13004 case SOUND_CTRL_ID_MUSIC:
13005 if (setup.sound_music)
13007 setup.sound_music = FALSE;
13010 else if (audio.music_available)
13012 setup.sound = setup.sound_music = TRUE;
13014 SetAudioMode(setup.sound);
13020 case SOUND_CTRL_ID_LOOPS:
13021 if (setup.sound_loops)
13022 setup.sound_loops = FALSE;
13023 else if (audio.loops_available)
13025 setup.sound = setup.sound_loops = TRUE;
13026 SetAudioMode(setup.sound);
13030 case SOUND_CTRL_ID_SIMPLE:
13031 if (setup.sound_simple)
13032 setup.sound_simple = FALSE;
13033 else if (audio.sound_available)
13035 setup.sound = setup.sound_simple = TRUE;
13036 SetAudioMode(setup.sound);