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 /* check trigger element for all events where the element that is checked
8648 for changing interacts with a directly adjacent element -- this is
8649 different to element changes that affect other elements to change on the
8650 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
8651 boolean check_trigger_element =
8652 (trigger_event == CE_TOUCHING_X ||
8653 trigger_event == CE_HITTING_X ||
8654 trigger_event == CE_HIT_BY_X ||
8656 /* this one was forgotten until 3.2.3 */
8657 trigger_event == CE_DIGGING_X);
8660 if (change->can_change_or_has_action &&
8661 change->has_event[trigger_event] &&
8662 change->trigger_side & trigger_side &&
8663 change->trigger_player & trigger_player &&
8664 (!check_trigger_element ||
8665 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8667 change->actual_trigger_element = trigger_element;
8668 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8669 change->actual_trigger_side = trigger_side;
8670 change->actual_trigger_ce_value = CustomValue[x][y];
8671 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8673 /* special case: trigger element not at (x,y) position for some events */
8674 if (check_trigger_element)
8686 { 0, 0 }, { 0, 0 }, { 0, 0 },
8690 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8691 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8693 change->actual_trigger_ce_value = CustomValue[xx][yy];
8694 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8697 if (change->can_change && !change_done)
8699 ChangeDelay[x][y] = 1;
8700 ChangeEvent[x][y] = trigger_event;
8702 HandleElementChange(x, y, p);
8706 #if USE_NEW_DELAYED_ACTION
8707 else if (change->has_action)
8709 ExecuteCustomElementAction(x, y, element, p);
8710 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8713 if (change->has_action)
8715 ExecuteCustomElementAction(x, y, element, p);
8716 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8725 static void PlayPlayerSound(struct PlayerInfo *player)
8727 int jx = player->jx, jy = player->jy;
8728 int sound_element = player->artwork_element;
8729 int last_action = player->last_action_waiting;
8730 int action = player->action_waiting;
8732 if (player->is_waiting)
8734 if (action != last_action)
8735 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8737 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8741 if (action != last_action)
8742 StopSound(element_info[sound_element].sound[last_action]);
8744 if (last_action == ACTION_SLEEPING)
8745 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8749 static void PlayAllPlayersSound()
8753 for (i = 0; i < MAX_PLAYERS; i++)
8754 if (stored_player[i].active)
8755 PlayPlayerSound(&stored_player[i]);
8758 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8760 boolean last_waiting = player->is_waiting;
8761 int move_dir = player->MovDir;
8763 player->dir_waiting = move_dir;
8764 player->last_action_waiting = player->action_waiting;
8768 if (!last_waiting) /* not waiting -> waiting */
8770 player->is_waiting = TRUE;
8772 player->frame_counter_bored =
8774 game.player_boring_delay_fixed +
8775 GetSimpleRandom(game.player_boring_delay_random);
8776 player->frame_counter_sleeping =
8778 game.player_sleeping_delay_fixed +
8779 GetSimpleRandom(game.player_sleeping_delay_random);
8781 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
8784 if (game.player_sleeping_delay_fixed +
8785 game.player_sleeping_delay_random > 0 &&
8786 player->anim_delay_counter == 0 &&
8787 player->post_delay_counter == 0 &&
8788 FrameCounter >= player->frame_counter_sleeping)
8789 player->is_sleeping = TRUE;
8790 else if (game.player_boring_delay_fixed +
8791 game.player_boring_delay_random > 0 &&
8792 FrameCounter >= player->frame_counter_bored)
8793 player->is_bored = TRUE;
8795 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8796 player->is_bored ? ACTION_BORING :
8799 if (player->is_sleeping && player->use_murphy)
8801 /* special case for sleeping Murphy when leaning against non-free tile */
8803 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
8804 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
8805 !IS_MOVING(player->jx - 1, player->jy)))
8807 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
8808 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
8809 !IS_MOVING(player->jx + 1, player->jy)))
8810 move_dir = MV_RIGHT;
8812 player->is_sleeping = FALSE;
8814 player->dir_waiting = move_dir;
8817 if (player->is_sleeping)
8819 if (player->num_special_action_sleeping > 0)
8821 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8823 int last_special_action = player->special_action_sleeping;
8824 int num_special_action = player->num_special_action_sleeping;
8825 int special_action =
8826 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8827 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8828 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8829 last_special_action + 1 : ACTION_SLEEPING);
8830 int special_graphic =
8831 el_act_dir2img(player->artwork_element, special_action, move_dir);
8833 player->anim_delay_counter =
8834 graphic_info[special_graphic].anim_delay_fixed +
8835 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8836 player->post_delay_counter =
8837 graphic_info[special_graphic].post_delay_fixed +
8838 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8840 player->special_action_sleeping = special_action;
8843 if (player->anim_delay_counter > 0)
8845 player->action_waiting = player->special_action_sleeping;
8846 player->anim_delay_counter--;
8848 else if (player->post_delay_counter > 0)
8850 player->post_delay_counter--;
8854 else if (player->is_bored)
8856 if (player->num_special_action_bored > 0)
8858 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8860 int special_action =
8861 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
8862 int special_graphic =
8863 el_act_dir2img(player->artwork_element, special_action, move_dir);
8865 player->anim_delay_counter =
8866 graphic_info[special_graphic].anim_delay_fixed +
8867 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8868 player->post_delay_counter =
8869 graphic_info[special_graphic].post_delay_fixed +
8870 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8872 player->special_action_bored = special_action;
8875 if (player->anim_delay_counter > 0)
8877 player->action_waiting = player->special_action_bored;
8878 player->anim_delay_counter--;
8880 else if (player->post_delay_counter > 0)
8882 player->post_delay_counter--;
8887 else if (last_waiting) /* waiting -> not waiting */
8889 player->is_waiting = FALSE;
8890 player->is_bored = FALSE;
8891 player->is_sleeping = FALSE;
8893 player->frame_counter_bored = -1;
8894 player->frame_counter_sleeping = -1;
8896 player->anim_delay_counter = 0;
8897 player->post_delay_counter = 0;
8899 player->dir_waiting = player->MovDir;
8900 player->action_waiting = ACTION_DEFAULT;
8902 player->special_action_bored = ACTION_DEFAULT;
8903 player->special_action_sleeping = ACTION_DEFAULT;
8907 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8909 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8910 int left = player_action & JOY_LEFT;
8911 int right = player_action & JOY_RIGHT;
8912 int up = player_action & JOY_UP;
8913 int down = player_action & JOY_DOWN;
8914 int button1 = player_action & JOY_BUTTON_1;
8915 int button2 = player_action & JOY_BUTTON_2;
8916 int dx = (left ? -1 : right ? 1 : 0);
8917 int dy = (up ? -1 : down ? 1 : 0);
8919 if (!player->active || tape.pausing)
8925 snapped = SnapField(player, dx, dy);
8929 dropped = DropElement(player);
8931 moved = MovePlayer(player, dx, dy);
8934 if (tape.single_step && tape.recording && !tape.pausing)
8936 if (button1 || (dropped && !moved))
8938 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8939 SnapField(player, 0, 0); /* stop snapping */
8943 SetPlayerWaiting(player, FALSE);
8945 return player_action;
8949 /* no actions for this player (no input at player's configured device) */
8951 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8952 SnapField(player, 0, 0);
8953 CheckGravityMovementWhenNotMoving(player);
8955 if (player->MovPos == 0)
8956 SetPlayerWaiting(player, TRUE);
8958 if (player->MovPos == 0) /* needed for tape.playing */
8959 player->is_moving = FALSE;
8961 player->is_dropping = FALSE;
8962 player->is_dropping_pressed = FALSE;
8963 player->drop_pressed_delay = 0;
8969 static void CheckLevelTime()
8973 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8975 if (level.native_em_level->lev->home == 0) /* all players at home */
8977 PlayerWins(local_player);
8979 AllPlayersGone = TRUE;
8981 level.native_em_level->lev->home = -1;
8984 if (level.native_em_level->ply[0]->alive == 0 &&
8985 level.native_em_level->ply[1]->alive == 0 &&
8986 level.native_em_level->ply[2]->alive == 0 &&
8987 level.native_em_level->ply[3]->alive == 0) /* all dead */
8988 AllPlayersGone = TRUE;
8991 if (TimeFrames >= FRAMES_PER_SECOND)
8996 for (i = 0; i < MAX_PLAYERS; i++)
8998 struct PlayerInfo *player = &stored_player[i];
9000 if (SHIELD_ON(player))
9002 player->shield_normal_time_left--;
9004 if (player->shield_deadly_time_left > 0)
9005 player->shield_deadly_time_left--;
9009 if (!local_player->LevelSolved && !level.use_step_counter)
9017 if (TimeLeft <= 10 && setup.time_limit)
9018 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9020 DrawGameValue_Time(TimeLeft);
9022 if (!TimeLeft && setup.time_limit)
9024 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9025 level.native_em_level->lev->killed_out_of_time = TRUE;
9027 for (i = 0; i < MAX_PLAYERS; i++)
9028 KillPlayer(&stored_player[i]);
9031 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9032 DrawGameValue_Time(TimePlayed);
9034 level.native_em_level->lev->time =
9035 (level.time == 0 ? TimePlayed : TimeLeft);
9038 if (tape.recording || tape.playing)
9039 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9043 void AdvanceFrameAndPlayerCounters(int player_nr)
9047 /* advance frame counters (global frame counter and time frame counter) */
9051 /* advance player counters (counters for move delay, move animation etc.) */
9052 for (i = 0; i < MAX_PLAYERS; i++)
9054 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9055 int move_delay_value = stored_player[i].move_delay_value;
9056 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9058 if (!advance_player_counters) /* not all players may be affected */
9061 #if USE_NEW_PLAYER_ANIM
9062 if (move_frames == 0) /* less than one move per game frame */
9064 int stepsize = TILEX / move_delay_value;
9065 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9066 int count = (stored_player[i].is_moving ?
9067 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9069 if (count % delay == 0)
9074 stored_player[i].Frame += move_frames;
9076 if (stored_player[i].MovPos != 0)
9077 stored_player[i].StepFrame += move_frames;
9079 if (stored_player[i].move_delay > 0)
9080 stored_player[i].move_delay--;
9082 /* due to bugs in previous versions, counter must count up, not down */
9083 if (stored_player[i].push_delay != -1)
9084 stored_player[i].push_delay++;
9086 if (stored_player[i].drop_delay > 0)
9087 stored_player[i].drop_delay--;
9089 if (stored_player[i].is_dropping_pressed)
9090 stored_player[i].drop_pressed_delay++;
9094 void StartGameActions(boolean init_network_game, boolean record_tape,
9097 unsigned long new_random_seed = InitRND(random_seed);
9100 TapeStartRecording(new_random_seed);
9102 #if defined(NETWORK_AVALIABLE)
9103 if (init_network_game)
9105 SendToServer_StartPlaying();
9116 static unsigned long game_frame_delay = 0;
9117 unsigned long game_frame_delay_value;
9118 byte *recorded_player_action;
9119 byte summarized_player_action = 0;
9120 byte tape_action[MAX_PLAYERS];
9123 if (game.restart_level)
9124 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9126 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9128 if (level.native_em_level->lev->home == 0) /* all players at home */
9130 PlayerWins(local_player);
9132 AllPlayersGone = TRUE;
9134 level.native_em_level->lev->home = -1;
9137 if (level.native_em_level->ply[0]->alive == 0 &&
9138 level.native_em_level->ply[1]->alive == 0 &&
9139 level.native_em_level->ply[2]->alive == 0 &&
9140 level.native_em_level->ply[3]->alive == 0) /* all dead */
9141 AllPlayersGone = TRUE;
9144 if (local_player->LevelSolved)
9147 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9150 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9153 game_frame_delay_value =
9154 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9156 if (tape.playing && tape.warp_forward && !tape.pausing)
9157 game_frame_delay_value = 0;
9159 /* ---------- main game synchronization point ---------- */
9161 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9163 if (network_playing && !network_player_action_received)
9165 /* try to get network player actions in time */
9167 #if defined(NETWORK_AVALIABLE)
9168 /* last chance to get network player actions without main loop delay */
9172 /* game was quit by network peer */
9173 if (game_status != GAME_MODE_PLAYING)
9176 if (!network_player_action_received)
9177 return; /* failed to get network player actions in time */
9179 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9185 /* at this point we know that we really continue executing the game */
9187 network_player_action_received = FALSE;
9189 /* when playing tape, read previously recorded player input from tape data */
9190 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9193 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9198 if (tape.set_centered_player)
9200 game.centered_player_nr_next = tape.centered_player_nr_next;
9201 game.set_centered_player = TRUE;
9204 for (i = 0; i < MAX_PLAYERS; i++)
9206 summarized_player_action |= stored_player[i].action;
9208 if (!network_playing)
9209 stored_player[i].effective_action = stored_player[i].action;
9212 #if defined(NETWORK_AVALIABLE)
9213 if (network_playing)
9214 SendToServer_MovePlayer(summarized_player_action);
9217 if (!options.network && !setup.team_mode)
9218 local_player->effective_action = summarized_player_action;
9220 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9222 for (i = 0; i < MAX_PLAYERS; i++)
9223 stored_player[i].effective_action =
9224 (i == game.centered_player_nr ? summarized_player_action : 0);
9227 if (recorded_player_action != NULL)
9228 for (i = 0; i < MAX_PLAYERS; i++)
9229 stored_player[i].effective_action = recorded_player_action[i];
9231 for (i = 0; i < MAX_PLAYERS; i++)
9233 tape_action[i] = stored_player[i].effective_action;
9235 /* (this can only happen in the R'n'D game engine) */
9236 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9237 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9240 /* only record actions from input devices, but not programmed actions */
9242 TapeRecordAction(tape_action);
9244 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9246 GameActions_EM_Main();
9254 void GameActions_EM_Main()
9256 byte effective_action[MAX_PLAYERS];
9257 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9260 for (i = 0; i < MAX_PLAYERS; i++)
9261 effective_action[i] = stored_player[i].effective_action;
9263 GameActions_EM(effective_action, warp_mode);
9267 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9270 void GameActions_RND()
9272 int magic_wall_x = 0, magic_wall_y = 0;
9273 int i, x, y, element, graphic;
9275 InitPlayfieldScanModeVars();
9277 #if USE_ONE_MORE_CHANGE_PER_FRAME
9278 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9280 SCAN_PLAYFIELD(x, y)
9282 ChangeCount[x][y] = 0;
9283 ChangeEvent[x][y] = -1;
9288 if (game.set_centered_player)
9290 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9292 /* switching to "all players" only possible if all players fit to screen */
9293 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9295 game.centered_player_nr_next = game.centered_player_nr;
9296 game.set_centered_player = FALSE;
9299 /* do not switch focus to non-existing (or non-active) player */
9300 if (game.centered_player_nr_next >= 0 &&
9301 !stored_player[game.centered_player_nr_next].active)
9303 game.centered_player_nr_next = game.centered_player_nr;
9304 game.set_centered_player = FALSE;
9308 if (game.set_centered_player &&
9309 ScreenMovPos == 0) /* screen currently aligned at tile position */
9313 if (game.centered_player_nr_next == -1)
9315 setScreenCenteredToAllPlayers(&sx, &sy);
9319 sx = stored_player[game.centered_player_nr_next].jx;
9320 sy = stored_player[game.centered_player_nr_next].jy;
9323 game.centered_player_nr = game.centered_player_nr_next;
9324 game.set_centered_player = FALSE;
9326 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9327 DrawGameDoorValues();
9330 for (i = 0; i < MAX_PLAYERS; i++)
9332 int actual_player_action = stored_player[i].effective_action;
9335 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9336 - rnd_equinox_tetrachloride 048
9337 - rnd_equinox_tetrachloride_ii 096
9338 - rnd_emanuel_schmieg 002
9339 - doctor_sloan_ww 001, 020
9341 if (stored_player[i].MovPos == 0)
9342 CheckGravityMovement(&stored_player[i]);
9345 /* overwrite programmed action with tape action */
9346 if (stored_player[i].programmed_action)
9347 actual_player_action = stored_player[i].programmed_action;
9349 PlayerActions(&stored_player[i], actual_player_action);
9351 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9354 ScrollScreen(NULL, SCROLL_GO_ON);
9356 /* for backwards compatibility, the following code emulates a fixed bug that
9357 occured when pushing elements (causing elements that just made their last
9358 pushing step to already (if possible) make their first falling step in the
9359 same game frame, which is bad); this code is also needed to use the famous
9360 "spring push bug" which is used in older levels and might be wanted to be
9361 used also in newer levels, but in this case the buggy pushing code is only
9362 affecting the "spring" element and no other elements */
9364 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9366 for (i = 0; i < MAX_PLAYERS; i++)
9368 struct PlayerInfo *player = &stored_player[i];
9372 if (player->active && player->is_pushing && player->is_moving &&
9374 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9375 Feld[x][y] == EL_SPRING))
9377 ContinueMoving(x, y);
9379 /* continue moving after pushing (this is actually a bug) */
9380 if (!IS_MOVING(x, y))
9388 SCAN_PLAYFIELD(x, y)
9390 ChangeCount[x][y] = 0;
9391 ChangeEvent[x][y] = -1;
9393 /* this must be handled before main playfield loop */
9394 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9397 if (MovDelay[x][y] <= 0)
9401 #if USE_NEW_SNAP_DELAY
9402 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9405 if (MovDelay[x][y] <= 0)
9408 DrawLevelField(x, y);
9410 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9416 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9418 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9419 printf("GameActions(): This should never happen!\n");
9421 ChangePage[x][y] = -1;
9426 if (WasJustMoving[x][y] > 0)
9427 WasJustMoving[x][y]--;
9428 if (WasJustFalling[x][y] > 0)
9429 WasJustFalling[x][y]--;
9430 if (CheckCollision[x][y] > 0)
9431 CheckCollision[x][y]--;
9435 /* reset finished pushing action (not done in ContinueMoving() to allow
9436 continuous pushing animation for elements with zero push delay) */
9437 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9439 ResetGfxAnimation(x, y);
9440 DrawLevelField(x, y);
9444 if (IS_BLOCKED(x, y))
9448 Blocked2Moving(x, y, &oldx, &oldy);
9449 if (!IS_MOVING(oldx, oldy))
9451 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9452 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9453 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9454 printf("GameActions(): This should never happen!\n");
9460 SCAN_PLAYFIELD(x, y)
9462 element = Feld[x][y];
9463 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9465 ResetGfxFrame(x, y, TRUE);
9467 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9468 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9469 ResetRandomAnimationValue(x, y);
9471 SetRandomAnimationValue(x, y);
9473 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9475 if (IS_INACTIVE(element))
9477 if (IS_ANIMATED(graphic))
9478 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9483 /* this may take place after moving, so 'element' may have changed */
9484 if (IS_CHANGING(x, y) &&
9485 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9487 int page = element_info[element].event_page_nr[CE_DELAY];
9490 HandleElementChange(x, y, page);
9492 if (CAN_CHANGE(element))
9493 HandleElementChange(x, y, page);
9495 if (HAS_ACTION(element))
9496 ExecuteCustomElementAction(x, y, element, page);
9499 element = Feld[x][y];
9500 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9503 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9507 element = Feld[x][y];
9508 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9510 if (IS_ANIMATED(graphic) &&
9513 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9515 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9516 DrawTwinkleOnField(x, y);
9518 else if ((element == EL_ACID ||
9519 element == EL_EXIT_OPEN ||
9520 element == EL_SP_EXIT_OPEN ||
9521 element == EL_SP_TERMINAL ||
9522 element == EL_SP_TERMINAL_ACTIVE ||
9523 element == EL_EXTRA_TIME ||
9524 element == EL_SHIELD_NORMAL ||
9525 element == EL_SHIELD_DEADLY) &&
9526 IS_ANIMATED(graphic))
9527 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9528 else if (IS_MOVING(x, y))
9529 ContinueMoving(x, y);
9530 else if (IS_ACTIVE_BOMB(element))
9531 CheckDynamite(x, y);
9532 else if (element == EL_AMOEBA_GROWING)
9533 AmoebeWaechst(x, y);
9534 else if (element == EL_AMOEBA_SHRINKING)
9535 AmoebaDisappearing(x, y);
9537 #if !USE_NEW_AMOEBA_CODE
9538 else if (IS_AMOEBALIVE(element))
9539 AmoebeAbleger(x, y);
9542 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9544 else if (element == EL_EXIT_CLOSED)
9546 else if (element == EL_SP_EXIT_CLOSED)
9548 else if (element == EL_EXPANDABLE_WALL_GROWING)
9550 else if (element == EL_EXPANDABLE_WALL ||
9551 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9552 element == EL_EXPANDABLE_WALL_VERTICAL ||
9553 element == EL_EXPANDABLE_WALL_ANY ||
9554 element == EL_BD_EXPANDABLE_WALL)
9556 else if (element == EL_FLAMES)
9557 CheckForDragon(x, y);
9558 else if (element == EL_EXPLOSION)
9559 ; /* drawing of correct explosion animation is handled separately */
9560 else if (element == EL_ELEMENT_SNAPPING ||
9561 element == EL_DIAGONAL_SHRINKING ||
9562 element == EL_DIAGONAL_GROWING)
9564 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9566 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9568 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9569 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9571 if (IS_BELT_ACTIVE(element))
9572 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9574 if (game.magic_wall_active)
9576 int jx = local_player->jx, jy = local_player->jy;
9578 /* play the element sound at the position nearest to the player */
9579 if ((element == EL_MAGIC_WALL_FULL ||
9580 element == EL_MAGIC_WALL_ACTIVE ||
9581 element == EL_MAGIC_WALL_EMPTYING ||
9582 element == EL_BD_MAGIC_WALL_FULL ||
9583 element == EL_BD_MAGIC_WALL_ACTIVE ||
9584 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9585 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9593 #if USE_NEW_AMOEBA_CODE
9594 /* new experimental amoeba growth stuff */
9595 if (!(FrameCounter % 8))
9597 static unsigned long random = 1684108901;
9599 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9601 x = RND(lev_fieldx);
9602 y = RND(lev_fieldy);
9603 element = Feld[x][y];
9605 if (!IS_PLAYER(x,y) &&
9606 (element == EL_EMPTY ||
9607 CAN_GROW_INTO(element) ||
9608 element == EL_QUICKSAND_EMPTY ||
9609 element == EL_ACID_SPLASH_LEFT ||
9610 element == EL_ACID_SPLASH_RIGHT))
9612 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9613 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9614 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9615 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9616 Feld[x][y] = EL_AMOEBA_DROP;
9619 random = random * 129 + 1;
9625 if (game.explosions_delayed)
9628 game.explosions_delayed = FALSE;
9630 SCAN_PLAYFIELD(x, y)
9632 element = Feld[x][y];
9634 if (ExplodeField[x][y])
9635 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9636 else if (element == EL_EXPLOSION)
9637 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9639 ExplodeField[x][y] = EX_TYPE_NONE;
9642 game.explosions_delayed = TRUE;
9645 if (game.magic_wall_active)
9647 if (!(game.magic_wall_time_left % 4))
9649 int element = Feld[magic_wall_x][magic_wall_y];
9651 if (element == EL_BD_MAGIC_WALL_FULL ||
9652 element == EL_BD_MAGIC_WALL_ACTIVE ||
9653 element == EL_BD_MAGIC_WALL_EMPTYING)
9654 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9656 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9659 if (game.magic_wall_time_left > 0)
9661 game.magic_wall_time_left--;
9662 if (!game.magic_wall_time_left)
9664 SCAN_PLAYFIELD(x, y)
9666 element = Feld[x][y];
9668 if (element == EL_MAGIC_WALL_ACTIVE ||
9669 element == EL_MAGIC_WALL_FULL)
9671 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9672 DrawLevelField(x, y);
9674 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9675 element == EL_BD_MAGIC_WALL_FULL)
9677 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9678 DrawLevelField(x, y);
9682 game.magic_wall_active = FALSE;
9687 if (game.light_time_left > 0)
9689 game.light_time_left--;
9691 if (game.light_time_left == 0)
9692 RedrawAllLightSwitchesAndInvisibleElements();
9695 if (game.timegate_time_left > 0)
9697 game.timegate_time_left--;
9699 if (game.timegate_time_left == 0)
9700 CloseAllOpenTimegates();
9703 if (game.lenses_time_left > 0)
9705 game.lenses_time_left--;
9707 if (game.lenses_time_left == 0)
9708 RedrawAllInvisibleElementsForLenses();
9711 if (game.magnify_time_left > 0)
9713 game.magnify_time_left--;
9715 if (game.magnify_time_left == 0)
9716 RedrawAllInvisibleElementsForMagnifier();
9719 for (i = 0; i < MAX_PLAYERS; i++)
9721 struct PlayerInfo *player = &stored_player[i];
9723 if (SHIELD_ON(player))
9725 if (player->shield_deadly_time_left)
9726 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9727 else if (player->shield_normal_time_left)
9728 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9735 PlayAllPlayersSound();
9737 if (options.debug) /* calculate frames per second */
9739 static unsigned long fps_counter = 0;
9740 static int fps_frames = 0;
9741 unsigned long fps_delay_ms = Counter() - fps_counter;
9745 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9747 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9750 fps_counter = Counter();
9753 redraw_mask |= REDRAW_FPS;
9756 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9758 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9760 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9762 local_player->show_envelope = 0;
9765 /* use random number generator in every frame to make it less predictable */
9766 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9770 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9772 int min_x = x, min_y = y, max_x = x, max_y = y;
9775 for (i = 0; i < MAX_PLAYERS; i++)
9777 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9779 if (!stored_player[i].active || &stored_player[i] == player)
9782 min_x = MIN(min_x, jx);
9783 min_y = MIN(min_y, jy);
9784 max_x = MAX(max_x, jx);
9785 max_y = MAX(max_y, jy);
9788 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9791 static boolean AllPlayersInVisibleScreen()
9795 for (i = 0; i < MAX_PLAYERS; i++)
9797 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9799 if (!stored_player[i].active)
9802 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9809 void ScrollLevel(int dx, int dy)
9811 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9814 BlitBitmap(drawto_field, drawto_field,
9815 FX + TILEX * (dx == -1) - softscroll_offset,
9816 FY + TILEY * (dy == -1) - softscroll_offset,
9817 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9818 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9819 FX + TILEX * (dx == 1) - softscroll_offset,
9820 FY + TILEY * (dy == 1) - softscroll_offset);
9824 x = (dx == 1 ? BX1 : BX2);
9825 for (y = BY1; y <= BY2; y++)
9826 DrawScreenField(x, y);
9831 y = (dy == 1 ? BY1 : BY2);
9832 for (x = BX1; x <= BX2; x++)
9833 DrawScreenField(x, y);
9836 redraw_mask |= REDRAW_FIELD;
9839 static boolean canFallDown(struct PlayerInfo *player)
9841 int jx = player->jx, jy = player->jy;
9843 return (IN_LEV_FIELD(jx, jy + 1) &&
9844 (IS_FREE(jx, jy + 1) ||
9845 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9846 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9847 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9850 static boolean canPassField(int x, int y, int move_dir)
9852 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9853 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9854 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9857 int element = Feld[x][y];
9859 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9860 !CAN_MOVE(element) &&
9861 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9862 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9863 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9866 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9868 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9869 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9870 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9874 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9875 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9876 (IS_DIGGABLE(Feld[newx][newy]) ||
9877 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9878 canPassField(newx, newy, move_dir)));
9881 static void CheckGravityMovement(struct PlayerInfo *player)
9883 #if USE_PLAYER_GRAVITY
9884 if (player->gravity && !player->programmed_action)
9886 if (game.gravity && !player->programmed_action)
9889 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9890 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9891 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
9892 int jx = player->jx, jy = player->jy;
9893 boolean player_is_moving_to_valid_field =
9894 (!player_is_snapping &&
9895 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9896 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9897 boolean player_can_fall_down = canFallDown(player);
9899 if (player_can_fall_down &&
9900 !player_is_moving_to_valid_field)
9901 player->programmed_action = MV_DOWN;
9905 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9907 return CheckGravityMovement(player);
9909 #if USE_PLAYER_GRAVITY
9910 if (player->gravity && !player->programmed_action)
9912 if (game.gravity && !player->programmed_action)
9915 int jx = player->jx, jy = player->jy;
9916 boolean field_under_player_is_free =
9917 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9918 boolean player_is_standing_on_valid_field =
9919 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9920 (IS_WALKABLE(Feld[jx][jy]) &&
9921 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9923 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9924 player->programmed_action = MV_DOWN;
9930 -----------------------------------------------------------------------------
9931 dx, dy: direction (non-diagonal) to try to move the player to
9932 real_dx, real_dy: direction as read from input device (can be diagonal)
9935 boolean MovePlayerOneStep(struct PlayerInfo *player,
9936 int dx, int dy, int real_dx, int real_dy)
9938 int jx = player->jx, jy = player->jy;
9939 int new_jx = jx + dx, new_jy = jy + dy;
9940 #if !USE_FIXED_DONT_RUN_INTO
9944 boolean player_can_move = !player->cannot_move;
9946 if (!player->active || (!dx && !dy))
9947 return MP_NO_ACTION;
9949 player->MovDir = (dx < 0 ? MV_LEFT :
9952 dy > 0 ? MV_DOWN : MV_NONE);
9954 if (!IN_LEV_FIELD(new_jx, new_jy))
9955 return MP_NO_ACTION;
9957 if (!player_can_move)
9959 if (player->MovPos == 0)
9961 player->is_moving = FALSE;
9962 player->is_digging = FALSE;
9963 player->is_collecting = FALSE;
9964 player->is_snapping = FALSE;
9965 player->is_pushing = FALSE;
9970 if (!options.network && game.centered_player_nr == -1 &&
9971 !AllPlayersInSight(player, new_jx, new_jy))
9972 return MP_NO_ACTION;
9974 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9975 return MP_NO_ACTION;
9978 #if !USE_FIXED_DONT_RUN_INTO
9979 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9981 /* (moved to DigField()) */
9982 if (player_can_move && DONT_RUN_INTO(element))
9984 if (element == EL_ACID && dx == 0 && dy == 1)
9986 SplashAcid(new_jx, new_jy);
9987 Feld[jx][jy] = EL_PLAYER_1;
9988 InitMovingField(jx, jy, MV_DOWN);
9989 Store[jx][jy] = EL_ACID;
9990 ContinueMoving(jx, jy);
9994 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10000 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10001 if (can_move != MP_MOVING)
10004 /* check if DigField() has caused relocation of the player */
10005 if (player->jx != jx || player->jy != jy)
10006 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10008 StorePlayer[jx][jy] = 0;
10009 player->last_jx = jx;
10010 player->last_jy = jy;
10011 player->jx = new_jx;
10012 player->jy = new_jy;
10013 StorePlayer[new_jx][new_jy] = player->element_nr;
10015 if (player->move_delay_value_next != -1)
10017 player->move_delay_value = player->move_delay_value_next;
10018 player->move_delay_value_next = -1;
10022 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10024 player->step_counter++;
10026 PlayerVisit[jx][jy] = FrameCounter;
10028 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10029 player->is_moving = TRUE;
10033 /* should better be called in MovePlayer(), but this breaks some tapes */
10034 ScrollPlayer(player, SCROLL_INIT);
10040 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10042 int jx = player->jx, jy = player->jy;
10043 int old_jx = jx, old_jy = jy;
10044 int moved = MP_NO_ACTION;
10046 if (!player->active)
10051 if (player->MovPos == 0)
10053 player->is_moving = FALSE;
10054 player->is_digging = FALSE;
10055 player->is_collecting = FALSE;
10056 player->is_snapping = FALSE;
10057 player->is_pushing = FALSE;
10063 if (player->move_delay > 0)
10066 player->move_delay = -1; /* set to "uninitialized" value */
10068 /* store if player is automatically moved to next field */
10069 player->is_auto_moving = (player->programmed_action != MV_NONE);
10071 /* remove the last programmed player action */
10072 player->programmed_action = 0;
10074 if (player->MovPos)
10076 /* should only happen if pre-1.2 tape recordings are played */
10077 /* this is only for backward compatibility */
10079 int original_move_delay_value = player->move_delay_value;
10082 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10086 /* scroll remaining steps with finest movement resolution */
10087 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10089 while (player->MovPos)
10091 ScrollPlayer(player, SCROLL_GO_ON);
10092 ScrollScreen(NULL, SCROLL_GO_ON);
10094 AdvanceFrameAndPlayerCounters(player->index_nr);
10100 player->move_delay_value = original_move_delay_value;
10103 player->is_active = FALSE;
10105 if (player->last_move_dir & MV_HORIZONTAL)
10107 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10108 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10112 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10113 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10116 #if USE_FIXED_BORDER_RUNNING_GFX
10117 if (!moved && !player->is_active)
10119 player->is_moving = FALSE;
10120 player->is_digging = FALSE;
10121 player->is_collecting = FALSE;
10122 player->is_snapping = FALSE;
10123 player->is_pushing = FALSE;
10131 if (moved & MP_MOVING && !ScreenMovPos &&
10132 (player->index_nr == game.centered_player_nr ||
10133 game.centered_player_nr == -1))
10135 if (moved & MP_MOVING && !ScreenMovPos &&
10136 (player == local_player || !options.network))
10139 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10140 int offset = (setup.scroll_delay ? 3 : 0);
10142 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10144 /* actual player has left the screen -- scroll in that direction */
10145 if (jx != old_jx) /* player has moved horizontally */
10146 scroll_x += (jx - old_jx);
10147 else /* player has moved vertically */
10148 scroll_y += (jy - old_jy);
10152 if (jx != old_jx) /* player has moved horizontally */
10154 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10155 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10156 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10158 /* don't scroll over playfield boundaries */
10159 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10160 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10162 /* don't scroll more than one field at a time */
10163 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10165 /* don't scroll against the player's moving direction */
10166 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10167 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10168 scroll_x = old_scroll_x;
10170 else /* player has moved vertically */
10172 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10173 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10174 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10176 /* don't scroll over playfield boundaries */
10177 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10178 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10180 /* don't scroll more than one field at a time */
10181 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10183 /* don't scroll against the player's moving direction */
10184 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10185 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10186 scroll_y = old_scroll_y;
10190 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10193 if (!options.network && game.centered_player_nr == -1 &&
10194 !AllPlayersInVisibleScreen())
10196 scroll_x = old_scroll_x;
10197 scroll_y = old_scroll_y;
10201 if (!options.network && !AllPlayersInVisibleScreen())
10203 scroll_x = old_scroll_x;
10204 scroll_y = old_scroll_y;
10209 ScrollScreen(player, SCROLL_INIT);
10210 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10215 player->StepFrame = 0;
10217 if (moved & MP_MOVING)
10219 if (old_jx != jx && old_jy == jy)
10220 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10221 else if (old_jx == jx && old_jy != jy)
10222 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10224 DrawLevelField(jx, jy); /* for "crumbled sand" */
10226 player->last_move_dir = player->MovDir;
10227 player->is_moving = TRUE;
10228 player->is_snapping = FALSE;
10229 player->is_switching = FALSE;
10230 player->is_dropping = FALSE;
10231 player->is_dropping_pressed = FALSE;
10232 player->drop_pressed_delay = 0;
10235 /* should better be called here than above, but this breaks some tapes */
10236 ScrollPlayer(player, SCROLL_INIT);
10241 CheckGravityMovementWhenNotMoving(player);
10243 player->is_moving = FALSE;
10245 /* at this point, the player is allowed to move, but cannot move right now
10246 (e.g. because of something blocking the way) -- ensure that the player
10247 is also allowed to move in the next frame (in old versions before 3.1.1,
10248 the player was forced to wait again for eight frames before next try) */
10250 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10251 player->move_delay = 0; /* allow direct movement in the next frame */
10254 if (player->move_delay == -1) /* not yet initialized by DigField() */
10255 player->move_delay = player->move_delay_value;
10257 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10259 TestIfPlayerTouchesBadThing(jx, jy);
10260 TestIfPlayerTouchesCustomElement(jx, jy);
10263 if (!player->active)
10264 RemovePlayer(player);
10269 void ScrollPlayer(struct PlayerInfo *player, int mode)
10271 int jx = player->jx, jy = player->jy;
10272 int last_jx = player->last_jx, last_jy = player->last_jy;
10273 int move_stepsize = TILEX / player->move_delay_value;
10275 #if USE_NEW_PLAYER_SPEED
10276 if (!player->active)
10279 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10282 if (!player->active || player->MovPos == 0)
10286 if (mode == SCROLL_INIT)
10288 player->actual_frame_counter = FrameCounter;
10289 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10291 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10292 Feld[last_jx][last_jy] == EL_EMPTY)
10294 int last_field_block_delay = 0; /* start with no blocking at all */
10295 int block_delay_adjustment = player->block_delay_adjustment;
10297 /* if player blocks last field, add delay for exactly one move */
10298 if (player->block_last_field)
10300 last_field_block_delay += player->move_delay_value;
10302 /* when blocking enabled, prevent moving up despite gravity */
10303 #if USE_PLAYER_GRAVITY
10304 if (player->gravity && player->MovDir == MV_UP)
10305 block_delay_adjustment = -1;
10307 if (game.gravity && player->MovDir == MV_UP)
10308 block_delay_adjustment = -1;
10312 /* add block delay adjustment (also possible when not blocking) */
10313 last_field_block_delay += block_delay_adjustment;
10315 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10316 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10319 #if USE_NEW_PLAYER_SPEED
10320 if (player->MovPos != 0) /* player has not yet reached destination */
10326 else if (!FrameReached(&player->actual_frame_counter, 1))
10329 #if USE_NEW_PLAYER_SPEED
10330 if (player->MovPos != 0)
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 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10341 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10343 /* before DrawPlayer() to draw correct player graphic for this case */
10344 if (player->MovPos == 0)
10345 CheckGravityMovement(player);
10348 if (player->MovPos == 0) /* player reached destination field */
10350 if (player->move_delay_reset_counter > 0)
10352 player->move_delay_reset_counter--;
10354 if (player->move_delay_reset_counter == 0)
10356 /* continue with normal speed after quickly moving through gate */
10357 HALVE_PLAYER_SPEED(player);
10359 /* be able to make the next move without delay */
10360 player->move_delay = 0;
10364 player->last_jx = jx;
10365 player->last_jy = jy;
10367 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10368 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10369 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10371 DrawPlayer(player); /* needed here only to cleanup last field */
10372 RemovePlayer(player);
10374 if (local_player->friends_still_needed == 0 ||
10375 IS_SP_ELEMENT(Feld[jx][jy]))
10376 PlayerWins(player);
10379 /* this breaks one level: "machine", level 000 */
10381 int move_direction = player->MovDir;
10382 int enter_side = MV_DIR_OPPOSITE(move_direction);
10383 int leave_side = move_direction;
10384 int old_jx = last_jx;
10385 int old_jy = last_jy;
10386 int old_element = Feld[old_jx][old_jy];
10387 int new_element = Feld[jx][jy];
10389 if (IS_CUSTOM_ELEMENT(old_element))
10390 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10392 player->index_bit, leave_side);
10394 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10395 CE_PLAYER_LEAVES_X,
10396 player->index_bit, leave_side);
10398 if (IS_CUSTOM_ELEMENT(new_element))
10399 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10400 player->index_bit, enter_side);
10402 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10403 CE_PLAYER_ENTERS_X,
10404 player->index_bit, enter_side);
10406 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10407 CE_MOVE_OF_X, move_direction);
10410 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10412 TestIfPlayerTouchesBadThing(jx, jy);
10413 TestIfPlayerTouchesCustomElement(jx, jy);
10415 /* needed because pushed element has not yet reached its destination,
10416 so it would trigger a change event at its previous field location */
10417 if (!player->is_pushing)
10418 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10420 if (!player->active)
10421 RemovePlayer(player);
10424 if (!local_player->LevelSolved && level.use_step_counter)
10434 if (TimeLeft <= 10 && setup.time_limit)
10435 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10437 DrawGameValue_Time(TimeLeft);
10439 if (!TimeLeft && setup.time_limit)
10440 for (i = 0; i < MAX_PLAYERS; i++)
10441 KillPlayer(&stored_player[i]);
10443 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10444 DrawGameValue_Time(TimePlayed);
10447 if (tape.single_step && tape.recording && !tape.pausing &&
10448 !player->programmed_action)
10449 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10453 void ScrollScreen(struct PlayerInfo *player, int mode)
10455 static unsigned long screen_frame_counter = 0;
10457 if (mode == SCROLL_INIT)
10459 /* set scrolling step size according to actual player's moving speed */
10460 ScrollStepSize = TILEX / player->move_delay_value;
10462 screen_frame_counter = FrameCounter;
10463 ScreenMovDir = player->MovDir;
10464 ScreenMovPos = player->MovPos;
10465 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10468 else if (!FrameReached(&screen_frame_counter, 1))
10473 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10474 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10475 redraw_mask |= REDRAW_FIELD;
10478 ScreenMovDir = MV_NONE;
10481 void TestIfPlayerTouchesCustomElement(int x, int y)
10483 static int xy[4][2] =
10490 static int trigger_sides[4][2] =
10492 /* center side border side */
10493 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10494 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10495 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10496 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10498 static int touch_dir[4] =
10500 MV_LEFT | MV_RIGHT,
10505 int center_element = Feld[x][y]; /* should always be non-moving! */
10508 for (i = 0; i < NUM_DIRECTIONS; i++)
10510 int xx = x + xy[i][0];
10511 int yy = y + xy[i][1];
10512 int center_side = trigger_sides[i][0];
10513 int border_side = trigger_sides[i][1];
10514 int border_element;
10516 if (!IN_LEV_FIELD(xx, yy))
10519 if (IS_PLAYER(x, y))
10521 struct PlayerInfo *player = PLAYERINFO(x, y);
10523 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10524 border_element = Feld[xx][yy]; /* may be moving! */
10525 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10526 border_element = Feld[xx][yy];
10527 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10528 border_element = MovingOrBlocked2Element(xx, yy);
10530 continue; /* center and border element do not touch */
10532 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10533 player->index_bit, border_side);
10534 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10535 CE_PLAYER_TOUCHES_X,
10536 player->index_bit, border_side);
10538 else if (IS_PLAYER(xx, yy))
10540 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10542 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10544 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10545 continue; /* center and border element do not touch */
10548 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10549 player->index_bit, center_side);
10550 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10551 CE_PLAYER_TOUCHES_X,
10552 player->index_bit, center_side);
10558 #if USE_ELEMENT_TOUCHING_BUGFIX
10560 void TestIfElementTouchesCustomElement(int x, int y)
10562 static int xy[4][2] =
10569 static int trigger_sides[4][2] =
10571 /* center side border side */
10572 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10573 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10574 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10575 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10577 static int touch_dir[4] =
10579 MV_LEFT | MV_RIGHT,
10584 boolean change_center_element = FALSE;
10585 int center_element = Feld[x][y]; /* should always be non-moving! */
10586 int border_element_old[NUM_DIRECTIONS];
10589 for (i = 0; i < NUM_DIRECTIONS; i++)
10591 int xx = x + xy[i][0];
10592 int yy = y + xy[i][1];
10593 int border_element;
10595 border_element_old[i] = -1;
10597 if (!IN_LEV_FIELD(xx, yy))
10600 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10601 border_element = Feld[xx][yy]; /* may be moving! */
10602 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10603 border_element = Feld[xx][yy];
10604 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10605 border_element = MovingOrBlocked2Element(xx, yy);
10607 continue; /* center and border element do not touch */
10609 border_element_old[i] = border_element;
10612 for (i = 0; i < NUM_DIRECTIONS; i++)
10614 int xx = x + xy[i][0];
10615 int yy = y + xy[i][1];
10616 int center_side = trigger_sides[i][0];
10617 int border_element = border_element_old[i];
10619 if (border_element == -1)
10622 /* check for change of border element */
10623 CheckElementChangeBySide(xx, yy, border_element, center_element,
10624 CE_TOUCHING_X, center_side);
10627 for (i = 0; i < NUM_DIRECTIONS; i++)
10629 int border_side = trigger_sides[i][1];
10630 int border_element = border_element_old[i];
10632 if (border_element == -1)
10635 /* check for change of center element (but change it only once) */
10636 if (!change_center_element)
10637 change_center_element =
10638 CheckElementChangeBySide(x, y, center_element, border_element,
10639 CE_TOUCHING_X, border_side);
10645 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10647 static int xy[4][2] =
10654 static int trigger_sides[4][2] =
10656 /* center side border side */
10657 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10658 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10659 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10660 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10662 static int touch_dir[4] =
10664 MV_LEFT | MV_RIGHT,
10669 boolean change_center_element = FALSE;
10670 int center_element = Feld[x][y]; /* should always be non-moving! */
10673 for (i = 0; i < NUM_DIRECTIONS; i++)
10675 int xx = x + xy[i][0];
10676 int yy = y + xy[i][1];
10677 int center_side = trigger_sides[i][0];
10678 int border_side = trigger_sides[i][1];
10679 int border_element;
10681 if (!IN_LEV_FIELD(xx, yy))
10684 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10685 border_element = Feld[xx][yy]; /* may be moving! */
10686 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10687 border_element = Feld[xx][yy];
10688 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10689 border_element = MovingOrBlocked2Element(xx, yy);
10691 continue; /* center and border element do not touch */
10693 /* check for change of center element (but change it only once) */
10694 if (!change_center_element)
10695 change_center_element =
10696 CheckElementChangeBySide(x, y, center_element, border_element,
10697 CE_TOUCHING_X, border_side);
10699 /* check for change of border element */
10700 CheckElementChangeBySide(xx, yy, border_element, center_element,
10701 CE_TOUCHING_X, center_side);
10707 void TestIfElementHitsCustomElement(int x, int y, int direction)
10709 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10710 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10711 int hitx = x + dx, hity = y + dy;
10712 int hitting_element = Feld[x][y];
10713 int touched_element;
10715 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10718 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10719 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10721 if (IN_LEV_FIELD(hitx, hity))
10723 int opposite_direction = MV_DIR_OPPOSITE(direction);
10724 int hitting_side = direction;
10725 int touched_side = opposite_direction;
10726 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10727 MovDir[hitx][hity] != direction ||
10728 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10734 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10735 CE_HITTING_X, touched_side);
10737 CheckElementChangeBySide(hitx, hity, touched_element,
10738 hitting_element, CE_HIT_BY_X, hitting_side);
10740 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10741 CE_HIT_BY_SOMETHING, opposite_direction);
10745 /* "hitting something" is also true when hitting the playfield border */
10746 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10747 CE_HITTING_SOMETHING, direction);
10751 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10753 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10754 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10755 int hitx = x + dx, hity = y + dy;
10756 int hitting_element = Feld[x][y];
10757 int touched_element;
10759 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10760 !IS_FREE(hitx, hity) &&
10761 (!IS_MOVING(hitx, hity) ||
10762 MovDir[hitx][hity] != direction ||
10763 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10766 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10770 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10774 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10775 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10777 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10778 EP_CAN_SMASH_EVERYTHING, direction);
10780 if (IN_LEV_FIELD(hitx, hity))
10782 int opposite_direction = MV_DIR_OPPOSITE(direction);
10783 int hitting_side = direction;
10784 int touched_side = opposite_direction;
10786 int touched_element = MovingOrBlocked2Element(hitx, hity);
10789 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10790 MovDir[hitx][hity] != direction ||
10791 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10800 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10801 CE_SMASHED_BY_SOMETHING, opposite_direction);
10803 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10804 CE_OTHER_IS_SMASHING, touched_side);
10806 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10807 CE_OTHER_GETS_SMASHED, hitting_side);
10813 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10815 int i, kill_x = -1, kill_y = -1;
10817 int bad_element = -1;
10818 static int test_xy[4][2] =
10825 static int test_dir[4] =
10833 for (i = 0; i < NUM_DIRECTIONS; i++)
10835 int test_x, test_y, test_move_dir, test_element;
10837 test_x = good_x + test_xy[i][0];
10838 test_y = good_y + test_xy[i][1];
10840 if (!IN_LEV_FIELD(test_x, test_y))
10844 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10846 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10848 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10849 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10851 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10852 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10856 bad_element = test_element;
10862 if (kill_x != -1 || kill_y != -1)
10864 if (IS_PLAYER(good_x, good_y))
10866 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10868 if (player->shield_deadly_time_left > 0 &&
10869 !IS_INDESTRUCTIBLE(bad_element))
10870 Bang(kill_x, kill_y);
10871 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10872 KillPlayer(player);
10875 Bang(good_x, good_y);
10879 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10881 int i, kill_x = -1, kill_y = -1;
10882 int bad_element = Feld[bad_x][bad_y];
10883 static int test_xy[4][2] =
10890 static int touch_dir[4] =
10892 MV_LEFT | MV_RIGHT,
10897 static int test_dir[4] =
10905 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10908 for (i = 0; i < NUM_DIRECTIONS; i++)
10910 int test_x, test_y, test_move_dir, test_element;
10912 test_x = bad_x + test_xy[i][0];
10913 test_y = bad_y + test_xy[i][1];
10914 if (!IN_LEV_FIELD(test_x, test_y))
10918 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10920 test_element = Feld[test_x][test_y];
10922 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10923 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10925 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10926 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10928 /* good thing is player or penguin that does not move away */
10929 if (IS_PLAYER(test_x, test_y))
10931 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10933 if (bad_element == EL_ROBOT && player->is_moving)
10934 continue; /* robot does not kill player if he is moving */
10936 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10938 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10939 continue; /* center and border element do not touch */
10946 else if (test_element == EL_PENGUIN)
10955 if (kill_x != -1 || kill_y != -1)
10957 if (IS_PLAYER(kill_x, kill_y))
10959 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10961 if (player->shield_deadly_time_left > 0 &&
10962 !IS_INDESTRUCTIBLE(bad_element))
10963 Bang(bad_x, bad_y);
10964 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10965 KillPlayer(player);
10968 Bang(kill_x, kill_y);
10972 void TestIfPlayerTouchesBadThing(int x, int y)
10974 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10977 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10979 TestIfGoodThingHitsBadThing(x, y, move_dir);
10982 void TestIfBadThingTouchesPlayer(int x, int y)
10984 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10987 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10989 TestIfBadThingHitsGoodThing(x, y, move_dir);
10992 void TestIfFriendTouchesBadThing(int x, int y)
10994 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10997 void TestIfBadThingTouchesFriend(int x, int y)
10999 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11002 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11004 int i, kill_x = bad_x, kill_y = bad_y;
11005 static int xy[4][2] =
11013 for (i = 0; i < NUM_DIRECTIONS; i++)
11017 x = bad_x + xy[i][0];
11018 y = bad_y + xy[i][1];
11019 if (!IN_LEV_FIELD(x, y))
11022 element = Feld[x][y];
11023 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11024 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11032 if (kill_x != bad_x || kill_y != bad_y)
11033 Bang(bad_x, bad_y);
11036 void KillPlayer(struct PlayerInfo *player)
11038 int jx = player->jx, jy = player->jy;
11040 if (!player->active)
11043 /* remove accessible field at the player's position */
11044 Feld[jx][jy] = EL_EMPTY;
11046 /* deactivate shield (else Bang()/Explode() would not work right) */
11047 player->shield_normal_time_left = 0;
11048 player->shield_deadly_time_left = 0;
11051 BuryPlayer(player);
11054 static void KillPlayerUnlessEnemyProtected(int x, int y)
11056 if (!PLAYER_ENEMY_PROTECTED(x, y))
11057 KillPlayer(PLAYERINFO(x, y));
11060 static void KillPlayerUnlessExplosionProtected(int x, int y)
11062 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11063 KillPlayer(PLAYERINFO(x, y));
11066 void BuryPlayer(struct PlayerInfo *player)
11068 int jx = player->jx, jy = player->jy;
11070 if (!player->active)
11073 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11074 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11076 player->GameOver = TRUE;
11077 RemovePlayer(player);
11080 void RemovePlayer(struct PlayerInfo *player)
11082 int jx = player->jx, jy = player->jy;
11083 int i, found = FALSE;
11085 player->present = FALSE;
11086 player->active = FALSE;
11088 if (!ExplodeField[jx][jy])
11089 StorePlayer[jx][jy] = 0;
11091 if (player->is_moving)
11092 DrawLevelField(player->last_jx, player->last_jy);
11094 for (i = 0; i < MAX_PLAYERS; i++)
11095 if (stored_player[i].active)
11099 AllPlayersGone = TRUE;
11105 #if USE_NEW_SNAP_DELAY
11106 static void setFieldForSnapping(int x, int y, int element, int direction)
11108 struct ElementInfo *ei = &element_info[element];
11109 int direction_bit = MV_DIR_TO_BIT(direction);
11110 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11111 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11112 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11114 Feld[x][y] = EL_ELEMENT_SNAPPING;
11115 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11117 ResetGfxAnimation(x, y);
11119 GfxElement[x][y] = element;
11120 GfxAction[x][y] = action;
11121 GfxDir[x][y] = direction;
11122 GfxFrame[x][y] = -1;
11127 =============================================================================
11128 checkDiagonalPushing()
11129 -----------------------------------------------------------------------------
11130 check if diagonal input device direction results in pushing of object
11131 (by checking if the alternative direction is walkable, diggable, ...)
11132 =============================================================================
11135 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11136 int x, int y, int real_dx, int real_dy)
11138 int jx, jy, dx, dy, xx, yy;
11140 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11143 /* diagonal direction: check alternative direction */
11148 xx = jx + (dx == 0 ? real_dx : 0);
11149 yy = jy + (dy == 0 ? real_dy : 0);
11151 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11155 =============================================================================
11157 -----------------------------------------------------------------------------
11158 x, y: field next to player (non-diagonal) to try to dig to
11159 real_dx, real_dy: direction as read from input device (can be diagonal)
11160 =============================================================================
11163 int DigField(struct PlayerInfo *player,
11164 int oldx, int oldy, int x, int y,
11165 int real_dx, int real_dy, int mode)
11167 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11168 boolean player_was_pushing = player->is_pushing;
11169 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11170 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11171 int jx = oldx, jy = oldy;
11172 int dx = x - jx, dy = y - jy;
11173 int nextx = x + dx, nexty = y + dy;
11174 int move_direction = (dx == -1 ? MV_LEFT :
11175 dx == +1 ? MV_RIGHT :
11177 dy == +1 ? MV_DOWN : MV_NONE);
11178 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11179 int dig_side = MV_DIR_OPPOSITE(move_direction);
11180 int old_element = Feld[jx][jy];
11181 #if USE_FIXED_DONT_RUN_INTO
11182 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11188 if (is_player) /* function can also be called by EL_PENGUIN */
11190 if (player->MovPos == 0)
11192 player->is_digging = FALSE;
11193 player->is_collecting = FALSE;
11196 if (player->MovPos == 0) /* last pushing move finished */
11197 player->is_pushing = FALSE;
11199 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11201 player->is_switching = FALSE;
11202 player->push_delay = -1;
11204 return MP_NO_ACTION;
11208 #if !USE_FIXED_DONT_RUN_INTO
11209 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11210 return MP_NO_ACTION;
11213 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11214 old_element = Back[jx][jy];
11216 /* in case of element dropped at player position, check background */
11217 else if (Back[jx][jy] != EL_EMPTY &&
11218 game.engine_version >= VERSION_IDENT(2,2,0,0))
11219 old_element = Back[jx][jy];
11221 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11222 return MP_NO_ACTION; /* field has no opening in this direction */
11224 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11225 return MP_NO_ACTION; /* field has no opening in this direction */
11227 #if USE_FIXED_DONT_RUN_INTO
11228 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11232 Feld[jx][jy] = player->artwork_element;
11233 InitMovingField(jx, jy, MV_DOWN);
11234 Store[jx][jy] = EL_ACID;
11235 ContinueMoving(jx, jy);
11236 BuryPlayer(player);
11238 return MP_DONT_RUN_INTO;
11242 #if USE_FIXED_DONT_RUN_INTO
11243 if (player_can_move && DONT_RUN_INTO(element))
11245 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11247 return MP_DONT_RUN_INTO;
11251 #if USE_FIXED_DONT_RUN_INTO
11252 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11253 return MP_NO_ACTION;
11256 #if !USE_FIXED_DONT_RUN_INTO
11257 element = Feld[x][y];
11260 collect_count = element_info[element].collect_count_initial;
11262 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11263 return MP_NO_ACTION;
11265 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11266 player_can_move = player_can_move_or_snap;
11268 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11269 game.engine_version >= VERSION_IDENT(2,2,0,0))
11271 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11272 player->index_bit, dig_side);
11273 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11274 player->index_bit, dig_side);
11276 if (Feld[x][y] != element) /* field changed by snapping */
11279 return MP_NO_ACTION;
11282 #if USE_PLAYER_GRAVITY
11283 if (player->gravity && is_player && !player->is_auto_moving &&
11284 canFallDown(player) && move_direction != MV_DOWN &&
11285 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11286 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11288 if (game.gravity && is_player && !player->is_auto_moving &&
11289 canFallDown(player) && move_direction != MV_DOWN &&
11290 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11291 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11294 if (player_can_move &&
11295 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11297 int sound_element = SND_ELEMENT(element);
11298 int sound_action = ACTION_WALKING;
11300 if (IS_RND_GATE(element))
11302 if (!player->key[RND_GATE_NR(element)])
11303 return MP_NO_ACTION;
11305 else if (IS_RND_GATE_GRAY(element))
11307 if (!player->key[RND_GATE_GRAY_NR(element)])
11308 return MP_NO_ACTION;
11310 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11312 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11313 return MP_NO_ACTION;
11315 else if (element == EL_EXIT_OPEN ||
11316 element == EL_SP_EXIT_OPEN ||
11317 element == EL_SP_EXIT_OPENING)
11319 sound_action = ACTION_PASSING; /* player is passing exit */
11321 else if (element == EL_EMPTY)
11323 sound_action = ACTION_MOVING; /* nothing to walk on */
11326 /* play sound from background or player, whatever is available */
11327 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11328 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11330 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11332 else if (player_can_move &&
11333 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11335 if (!ACCESS_FROM(element, opposite_direction))
11336 return MP_NO_ACTION; /* field not accessible from this direction */
11338 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11339 return MP_NO_ACTION;
11341 if (IS_EM_GATE(element))
11343 if (!player->key[EM_GATE_NR(element)])
11344 return MP_NO_ACTION;
11346 else if (IS_EM_GATE_GRAY(element))
11348 if (!player->key[EM_GATE_GRAY_NR(element)])
11349 return MP_NO_ACTION;
11351 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11353 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11354 return MP_NO_ACTION;
11356 else if (IS_EMC_GATE(element))
11358 if (!player->key[EMC_GATE_NR(element)])
11359 return MP_NO_ACTION;
11361 else if (IS_EMC_GATE_GRAY(element))
11363 if (!player->key[EMC_GATE_GRAY_NR(element)])
11364 return MP_NO_ACTION;
11366 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11368 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11369 return MP_NO_ACTION;
11371 else if (IS_SP_PORT(element))
11373 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11374 element == EL_SP_GRAVITY_PORT_RIGHT ||
11375 element == EL_SP_GRAVITY_PORT_UP ||
11376 element == EL_SP_GRAVITY_PORT_DOWN)
11377 #if USE_PLAYER_GRAVITY
11378 player->gravity = !player->gravity;
11380 game.gravity = !game.gravity;
11382 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11383 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11384 element == EL_SP_GRAVITY_ON_PORT_UP ||
11385 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11386 #if USE_PLAYER_GRAVITY
11387 player->gravity = TRUE;
11389 game.gravity = TRUE;
11391 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11392 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11393 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11394 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11395 #if USE_PLAYER_GRAVITY
11396 player->gravity = FALSE;
11398 game.gravity = FALSE;
11402 /* automatically move to the next field with double speed */
11403 player->programmed_action = move_direction;
11405 if (player->move_delay_reset_counter == 0)
11407 player->move_delay_reset_counter = 2; /* two double speed steps */
11409 DOUBLE_PLAYER_SPEED(player);
11412 PlayLevelSoundAction(x, y, ACTION_PASSING);
11414 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11418 if (mode != DF_SNAP)
11420 GfxElement[x][y] = GFX_ELEMENT(element);
11421 player->is_digging = TRUE;
11424 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11426 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11427 player->index_bit, dig_side);
11429 if (mode == DF_SNAP)
11431 #if USE_NEW_SNAP_DELAY
11432 if (level.block_snap_field)
11433 setFieldForSnapping(x, y, element, move_direction);
11435 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11437 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11440 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11441 player->index_bit, dig_side);
11444 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11448 if (is_player && mode != DF_SNAP)
11450 GfxElement[x][y] = element;
11451 player->is_collecting = TRUE;
11454 if (element == EL_SPEED_PILL)
11456 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11458 else if (element == EL_EXTRA_TIME && level.time > 0)
11460 TimeLeft += level.extra_time;
11461 DrawGameValue_Time(TimeLeft);
11463 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11465 player->shield_normal_time_left += level.shield_normal_time;
11466 if (element == EL_SHIELD_DEADLY)
11467 player->shield_deadly_time_left += level.shield_deadly_time;
11469 else if (element == EL_DYNAMITE ||
11470 element == EL_EM_DYNAMITE ||
11471 element == EL_SP_DISK_RED)
11473 if (player->inventory_size < MAX_INVENTORY_SIZE)
11474 player->inventory_element[player->inventory_size++] = element;
11476 DrawGameDoorValues();
11478 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11480 player->dynabomb_count++;
11481 player->dynabombs_left++;
11483 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11485 player->dynabomb_size++;
11487 else if (element == EL_DYNABOMB_INCREASE_POWER)
11489 player->dynabomb_xl = TRUE;
11491 else if (IS_KEY(element))
11493 player->key[KEY_NR(element)] = TRUE;
11495 DrawGameDoorValues();
11497 else if (IS_ENVELOPE(element))
11499 player->show_envelope = element;
11501 else if (element == EL_EMC_LENSES)
11503 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11505 RedrawAllInvisibleElementsForLenses();
11507 else if (element == EL_EMC_MAGNIFIER)
11509 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11511 RedrawAllInvisibleElementsForMagnifier();
11513 else if (IS_DROPPABLE(element) ||
11514 IS_THROWABLE(element)) /* can be collected and dropped */
11518 if (collect_count == 0)
11519 player->inventory_infinite_element = element;
11521 for (i = 0; i < collect_count; i++)
11522 if (player->inventory_size < MAX_INVENTORY_SIZE)
11523 player->inventory_element[player->inventory_size++] = element;
11525 DrawGameDoorValues();
11527 else if (collect_count > 0)
11529 local_player->gems_still_needed -= collect_count;
11530 if (local_player->gems_still_needed < 0)
11531 local_player->gems_still_needed = 0;
11533 DrawGameValue_Emeralds(local_player->gems_still_needed);
11536 RaiseScoreElement(element);
11537 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11540 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11541 player->index_bit, dig_side);
11543 if (mode == DF_SNAP)
11545 #if USE_NEW_SNAP_DELAY
11546 if (level.block_snap_field)
11547 setFieldForSnapping(x, y, element, move_direction);
11549 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11551 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11554 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11555 player->index_bit, dig_side);
11558 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11560 if (mode == DF_SNAP && element != EL_BD_ROCK)
11561 return MP_NO_ACTION;
11563 if (CAN_FALL(element) && dy)
11564 return MP_NO_ACTION;
11566 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11567 !(element == EL_SPRING && level.use_spring_bug))
11568 return MP_NO_ACTION;
11570 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11571 ((move_direction & MV_VERTICAL &&
11572 ((element_info[element].move_pattern & MV_LEFT &&
11573 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11574 (element_info[element].move_pattern & MV_RIGHT &&
11575 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11576 (move_direction & MV_HORIZONTAL &&
11577 ((element_info[element].move_pattern & MV_UP &&
11578 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11579 (element_info[element].move_pattern & MV_DOWN &&
11580 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11581 return MP_NO_ACTION;
11583 /* do not push elements already moving away faster than player */
11584 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11585 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11586 return MP_NO_ACTION;
11588 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11590 if (player->push_delay_value == -1 || !player_was_pushing)
11591 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11593 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11595 if (player->push_delay_value == -1)
11596 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11598 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11600 if (!player->is_pushing)
11601 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11604 player->is_pushing = TRUE;
11605 player->is_active = TRUE;
11607 if (!(IN_LEV_FIELD(nextx, nexty) &&
11608 (IS_FREE(nextx, nexty) ||
11609 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11610 IS_SB_ELEMENT(element)))))
11611 return MP_NO_ACTION;
11613 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11614 return MP_NO_ACTION;
11616 if (player->push_delay == -1) /* new pushing; restart delay */
11617 player->push_delay = 0;
11619 if (player->push_delay < player->push_delay_value &&
11620 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11621 element != EL_SPRING && element != EL_BALLOON)
11623 /* make sure that there is no move delay before next try to push */
11624 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11625 player->move_delay = 0;
11627 return MP_NO_ACTION;
11630 if (IS_SB_ELEMENT(element))
11632 if (element == EL_SOKOBAN_FIELD_FULL)
11634 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11635 local_player->sokobanfields_still_needed++;
11638 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11640 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11641 local_player->sokobanfields_still_needed--;
11644 Feld[x][y] = EL_SOKOBAN_OBJECT;
11646 if (Back[x][y] == Back[nextx][nexty])
11647 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11648 else if (Back[x][y] != 0)
11649 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11652 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11655 if (local_player->sokobanfields_still_needed == 0 &&
11656 game.emulation == EMU_SOKOBAN)
11658 PlayerWins(player);
11660 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11664 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11666 InitMovingField(x, y, move_direction);
11667 GfxAction[x][y] = ACTION_PUSHING;
11669 if (mode == DF_SNAP)
11670 ContinueMoving(x, y);
11672 MovPos[x][y] = (dx != 0 ? dx : dy);
11674 Pushed[x][y] = TRUE;
11675 Pushed[nextx][nexty] = TRUE;
11677 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11678 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11680 player->push_delay_value = -1; /* get new value later */
11682 /* check for element change _after_ element has been pushed */
11683 if (game.use_change_when_pushing_bug)
11685 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11686 player->index_bit, dig_side);
11687 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11688 player->index_bit, dig_side);
11691 else if (IS_SWITCHABLE(element))
11693 if (PLAYER_SWITCHING(player, x, y))
11695 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11696 player->index_bit, dig_side);
11701 player->is_switching = TRUE;
11702 player->switch_x = x;
11703 player->switch_y = y;
11705 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11707 if (element == EL_ROBOT_WHEEL)
11709 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11713 DrawLevelField(x, y);
11715 else if (element == EL_SP_TERMINAL)
11719 SCAN_PLAYFIELD(xx, yy)
11721 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11723 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11724 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11727 else if (IS_BELT_SWITCH(element))
11729 ToggleBeltSwitch(x, y);
11731 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11732 element == EL_SWITCHGATE_SWITCH_DOWN)
11734 ToggleSwitchgateSwitch(x, y);
11736 else if (element == EL_LIGHT_SWITCH ||
11737 element == EL_LIGHT_SWITCH_ACTIVE)
11739 ToggleLightSwitch(x, y);
11741 else if (element == EL_TIMEGATE_SWITCH)
11743 ActivateTimegateSwitch(x, y);
11745 else if (element == EL_BALLOON_SWITCH_LEFT ||
11746 element == EL_BALLOON_SWITCH_RIGHT ||
11747 element == EL_BALLOON_SWITCH_UP ||
11748 element == EL_BALLOON_SWITCH_DOWN ||
11749 element == EL_BALLOON_SWITCH_NONE ||
11750 element == EL_BALLOON_SWITCH_ANY)
11752 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11753 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11754 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11755 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11756 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11759 else if (element == EL_LAMP)
11761 Feld[x][y] = EL_LAMP_ACTIVE;
11762 local_player->lights_still_needed--;
11764 ResetGfxAnimation(x, y);
11765 DrawLevelField(x, y);
11767 else if (element == EL_TIME_ORB_FULL)
11769 Feld[x][y] = EL_TIME_ORB_EMPTY;
11771 if (level.time > 0 || level.use_time_orb_bug)
11773 TimeLeft += level.time_orb_time;
11774 DrawGameValue_Time(TimeLeft);
11777 ResetGfxAnimation(x, y);
11778 DrawLevelField(x, y);
11780 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11781 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11785 game.ball_state = !game.ball_state;
11787 SCAN_PLAYFIELD(xx, yy)
11789 int e = Feld[xx][yy];
11791 if (game.ball_state)
11793 if (e == EL_EMC_MAGIC_BALL)
11794 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11795 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11796 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11800 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11801 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11802 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11803 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11808 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11809 player->index_bit, dig_side);
11811 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11812 player->index_bit, dig_side);
11814 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11815 player->index_bit, dig_side);
11821 if (!PLAYER_SWITCHING(player, x, y))
11823 player->is_switching = TRUE;
11824 player->switch_x = x;
11825 player->switch_y = y;
11827 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11828 player->index_bit, dig_side);
11829 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11830 player->index_bit, dig_side);
11832 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11833 player->index_bit, dig_side);
11834 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11835 player->index_bit, dig_side);
11838 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11839 player->index_bit, dig_side);
11840 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11841 player->index_bit, dig_side);
11843 return MP_NO_ACTION;
11846 player->push_delay = -1;
11848 if (is_player) /* function can also be called by EL_PENGUIN */
11850 if (Feld[x][y] != element) /* really digged/collected something */
11852 player->is_collecting = !player->is_digging;
11853 player->is_active = TRUE;
11860 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11862 int jx = player->jx, jy = player->jy;
11863 int x = jx + dx, y = jy + dy;
11864 int snap_direction = (dx == -1 ? MV_LEFT :
11865 dx == +1 ? MV_RIGHT :
11867 dy == +1 ? MV_DOWN : MV_NONE);
11868 boolean can_continue_snapping = (level.continuous_snapping &&
11869 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
11871 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11874 if (!player->active || !IN_LEV_FIELD(x, y))
11882 if (player->MovPos == 0)
11883 player->is_pushing = FALSE;
11885 player->is_snapping = FALSE;
11887 if (player->MovPos == 0)
11889 player->is_moving = FALSE;
11890 player->is_digging = FALSE;
11891 player->is_collecting = FALSE;
11897 #if USE_NEW_CONTINUOUS_SNAPPING
11898 /* prevent snapping with already pressed snap key when not allowed */
11899 if (player->is_snapping && !can_continue_snapping)
11902 if (player->is_snapping)
11906 player->MovDir = snap_direction;
11908 if (player->MovPos == 0)
11910 player->is_moving = FALSE;
11911 player->is_digging = FALSE;
11912 player->is_collecting = FALSE;
11915 player->is_dropping = FALSE;
11916 player->is_dropping_pressed = FALSE;
11917 player->drop_pressed_delay = 0;
11919 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11922 player->is_snapping = TRUE;
11923 player->is_active = TRUE;
11925 if (player->MovPos == 0)
11927 player->is_moving = FALSE;
11928 player->is_digging = FALSE;
11929 player->is_collecting = FALSE;
11932 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11933 DrawLevelField(player->last_jx, player->last_jy);
11935 DrawLevelField(x, y);
11940 boolean DropElement(struct PlayerInfo *player)
11942 int old_element, new_element;
11943 int dropx = player->jx, dropy = player->jy;
11944 int drop_direction = player->MovDir;
11945 int drop_side = drop_direction;
11946 int drop_element = (player->inventory_size > 0 ?
11947 player->inventory_element[player->inventory_size - 1] :
11948 player->inventory_infinite_element != EL_UNDEFINED ?
11949 player->inventory_infinite_element :
11950 player->dynabombs_left > 0 ?
11951 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11954 player->is_dropping_pressed = TRUE;
11956 /* do not drop an element on top of another element; when holding drop key
11957 pressed without moving, dropped element must move away before the next
11958 element can be dropped (this is especially important if the next element
11959 is dynamite, which can be placed on background for historical reasons) */
11960 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11963 if (IS_THROWABLE(drop_element))
11965 dropx += GET_DX_FROM_DIR(drop_direction);
11966 dropy += GET_DY_FROM_DIR(drop_direction);
11968 if (!IN_LEV_FIELD(dropx, dropy))
11972 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11973 new_element = drop_element; /* default: no change when dropping */
11975 /* check if player is active, not moving and ready to drop */
11976 if (!player->active || player->MovPos || player->drop_delay > 0)
11979 /* check if player has anything that can be dropped */
11980 if (new_element == EL_UNDEFINED)
11983 /* check if drop key was pressed long enough for EM style dynamite */
11984 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
11987 /* check if anything can be dropped at the current position */
11988 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11991 /* collected custom elements can only be dropped on empty fields */
11992 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11995 if (old_element != EL_EMPTY)
11996 Back[dropx][dropy] = old_element; /* store old element on this field */
11998 ResetGfxAnimation(dropx, dropy);
11999 ResetRandomAnimationValue(dropx, dropy);
12001 if (player->inventory_size > 0 ||
12002 player->inventory_infinite_element != EL_UNDEFINED)
12004 if (player->inventory_size > 0)
12006 player->inventory_size--;
12008 DrawGameDoorValues();
12010 if (new_element == EL_DYNAMITE)
12011 new_element = EL_DYNAMITE_ACTIVE;
12012 else if (new_element == EL_EM_DYNAMITE)
12013 new_element = EL_EM_DYNAMITE_ACTIVE;
12014 else if (new_element == EL_SP_DISK_RED)
12015 new_element = EL_SP_DISK_RED_ACTIVE;
12018 Feld[dropx][dropy] = new_element;
12020 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12021 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12022 el2img(Feld[dropx][dropy]), 0);
12024 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12026 /* needed if previous element just changed to "empty" in the last frame */
12027 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12029 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12030 player->index_bit, drop_side);
12031 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12033 player->index_bit, drop_side);
12035 TestIfElementTouchesCustomElement(dropx, dropy);
12037 else /* player is dropping a dyna bomb */
12039 player->dynabombs_left--;
12041 Feld[dropx][dropy] = new_element;
12043 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12044 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12045 el2img(Feld[dropx][dropy]), 0);
12047 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12050 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12051 InitField_WithBug1(dropx, dropy, FALSE);
12053 new_element = Feld[dropx][dropy]; /* element might have changed */
12055 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12056 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12058 int move_direction, nextx, nexty;
12060 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12061 MovDir[dropx][dropy] = drop_direction;
12063 move_direction = MovDir[dropx][dropy];
12064 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12065 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12067 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12068 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12071 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12072 player->is_dropping = TRUE;
12074 player->drop_pressed_delay = 0;
12075 player->is_dropping_pressed = FALSE;
12077 player->drop_x = dropx;
12078 player->drop_y = dropy;
12083 /* ------------------------------------------------------------------------- */
12084 /* game sound playing functions */
12085 /* ------------------------------------------------------------------------- */
12087 static int *loop_sound_frame = NULL;
12088 static int *loop_sound_volume = NULL;
12090 void InitPlayLevelSound()
12092 int num_sounds = getSoundListSize();
12094 checked_free(loop_sound_frame);
12095 checked_free(loop_sound_volume);
12097 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12098 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12101 static void PlayLevelSound(int x, int y, int nr)
12103 int sx = SCREENX(x), sy = SCREENY(y);
12104 int volume, stereo_position;
12105 int max_distance = 8;
12106 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12108 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12109 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12112 if (!IN_LEV_FIELD(x, y) ||
12113 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12114 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12117 volume = SOUND_MAX_VOLUME;
12119 if (!IN_SCR_FIELD(sx, sy))
12121 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12122 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12124 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12127 stereo_position = (SOUND_MAX_LEFT +
12128 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12129 (SCR_FIELDX + 2 * max_distance));
12131 if (IS_LOOP_SOUND(nr))
12133 /* This assures that quieter loop sounds do not overwrite louder ones,
12134 while restarting sound volume comparison with each new game frame. */
12136 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12139 loop_sound_volume[nr] = volume;
12140 loop_sound_frame[nr] = FrameCounter;
12143 PlaySoundExt(nr, volume, stereo_position, type);
12146 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12148 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12149 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12150 y < LEVELY(BY1) ? LEVELY(BY1) :
12151 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12155 static void PlayLevelSoundAction(int x, int y, int action)
12157 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12160 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12162 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12164 if (sound_effect != SND_UNDEFINED)
12165 PlayLevelSound(x, y, sound_effect);
12168 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12171 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12173 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12174 PlayLevelSound(x, y, sound_effect);
12177 static void PlayLevelSoundActionIfLoop(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 PlayLevelSound(x, y, sound_effect);
12185 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12187 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12189 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12190 StopSound(sound_effect);
12193 static void PlayLevelMusic()
12195 if (levelset.music[level_nr] != MUS_UNDEFINED)
12196 PlayMusic(levelset.music[level_nr]); /* from config file */
12198 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12201 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
12203 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12204 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
12205 int x = xx - 1 - offset;
12206 int y = yy - 1 - offset;
12211 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12215 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12219 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12223 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12227 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12231 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12235 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12238 case SAMPLE_android_clone:
12239 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12242 case SAMPLE_android_move:
12243 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12246 case SAMPLE_spring:
12247 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12251 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12255 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12258 case SAMPLE_eater_eat:
12259 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12263 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12266 case SAMPLE_collect:
12267 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12270 case SAMPLE_diamond:
12271 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12274 case SAMPLE_squash:
12275 /* !!! CHECK THIS !!! */
12277 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12279 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12283 case SAMPLE_wonderfall:
12284 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12288 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12292 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12296 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12300 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12304 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12308 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12311 case SAMPLE_wonder:
12312 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12316 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12319 case SAMPLE_exit_open:
12320 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12323 case SAMPLE_exit_leave:
12324 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12327 case SAMPLE_dynamite:
12328 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12332 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12336 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12340 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12344 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12348 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12352 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12356 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12362 void ChangeTime(int value)
12364 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
12368 /* EMC game engine uses value from time counter of RND game engine */
12369 level.native_em_level->lev->time = *time;
12371 DrawGameValue_Time(*time);
12374 void RaiseScore(int value)
12376 /* EMC game engine and RND game engine have separate score counters */
12377 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
12378 &level.native_em_level->lev->score : &local_player->score);
12382 DrawGameValue_Score(*score);
12386 void RaiseScore(int value)
12388 local_player->score += value;
12390 DrawGameValue_Score(local_player->score);
12393 void RaiseScoreElement(int element)
12398 case EL_BD_DIAMOND:
12399 case EL_EMERALD_YELLOW:
12400 case EL_EMERALD_RED:
12401 case EL_EMERALD_PURPLE:
12402 case EL_SP_INFOTRON:
12403 RaiseScore(level.score[SC_EMERALD]);
12406 RaiseScore(level.score[SC_DIAMOND]);
12409 RaiseScore(level.score[SC_CRYSTAL]);
12412 RaiseScore(level.score[SC_PEARL]);
12415 case EL_BD_BUTTERFLY:
12416 case EL_SP_ELECTRON:
12417 RaiseScore(level.score[SC_BUG]);
12420 case EL_BD_FIREFLY:
12421 case EL_SP_SNIKSNAK:
12422 RaiseScore(level.score[SC_SPACESHIP]);
12425 case EL_DARK_YAMYAM:
12426 RaiseScore(level.score[SC_YAMYAM]);
12429 RaiseScore(level.score[SC_ROBOT]);
12432 RaiseScore(level.score[SC_PACMAN]);
12435 RaiseScore(level.score[SC_NUT]);
12438 case EL_EM_DYNAMITE:
12439 case EL_SP_DISK_RED:
12440 case EL_DYNABOMB_INCREASE_NUMBER:
12441 case EL_DYNABOMB_INCREASE_SIZE:
12442 case EL_DYNABOMB_INCREASE_POWER:
12443 RaiseScore(level.score[SC_DYNAMITE]);
12445 case EL_SHIELD_NORMAL:
12446 case EL_SHIELD_DEADLY:
12447 RaiseScore(level.score[SC_SHIELD]);
12449 case EL_EXTRA_TIME:
12450 RaiseScore(level.extra_time_score);
12464 RaiseScore(level.score[SC_KEY]);
12467 RaiseScore(element_info[element].collect_score);
12472 void RequestQuitGame(boolean ask_if_really_quit)
12474 if (AllPlayersGone ||
12475 !ask_if_really_quit ||
12476 level_editor_test_game ||
12477 Request("Do you really want to quit the game ?",
12478 REQ_ASK | REQ_STAY_CLOSED))
12480 #if defined(NETWORK_AVALIABLE)
12481 if (options.network)
12482 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12486 if (!ask_if_really_quit || level_editor_test_game)
12488 game_status = GAME_MODE_MAIN;
12494 FadeOut(REDRAW_FIELD);
12496 game_status = GAME_MODE_MAIN;
12498 DrawAndFadeInMainMenu(REDRAW_FIELD);
12504 if (tape.playing && tape.deactivate_display)
12505 TapeDeactivateDisplayOff(TRUE);
12507 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12509 if (tape.playing && tape.deactivate_display)
12510 TapeDeactivateDisplayOn();
12515 /* ------------------------------------------------------------------------- */
12516 /* random generator functions */
12517 /* ------------------------------------------------------------------------- */
12519 unsigned int InitEngineRandom_RND(long seed)
12521 game.num_random_calls = 0;
12524 unsigned int rnd_seed = InitEngineRandom(seed);
12526 printf("::: START RND: %d\n", rnd_seed);
12531 return InitEngineRandom(seed);
12537 unsigned int RND(int max)
12541 game.num_random_calls++;
12543 return GetEngineRandom(max);
12550 /* ------------------------------------------------------------------------- */
12551 /* game engine snapshot handling functions */
12552 /* ------------------------------------------------------------------------- */
12554 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
12556 struct EngineSnapshotInfo
12558 /* runtime values for custom element collect score */
12559 int collect_score[NUM_CUSTOM_ELEMENTS];
12561 /* runtime values for group element choice position */
12562 int choice_pos[NUM_GROUP_ELEMENTS];
12564 /* runtime values for belt position animations */
12565 int belt_graphic[4 * NUM_BELT_PARTS];
12566 int belt_anim_mode[4 * NUM_BELT_PARTS];
12569 struct EngineSnapshotNodeInfo
12576 static struct EngineSnapshotInfo engine_snapshot_rnd;
12577 static ListNode *engine_snapshot_list = NULL;
12578 static char *snapshot_level_identifier = NULL;
12579 static int snapshot_level_nr = -1;
12581 void FreeEngineSnapshot()
12583 while (engine_snapshot_list != NULL)
12584 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
12587 setString(&snapshot_level_identifier, NULL);
12588 snapshot_level_nr = -1;
12591 static void SaveEngineSnapshotValues_RND()
12593 static int belt_base_active_element[4] =
12595 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
12596 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
12597 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
12598 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
12602 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12604 int element = EL_CUSTOM_START + i;
12606 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
12609 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12611 int element = EL_GROUP_START + i;
12613 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
12616 for (i = 0; i < 4; i++)
12618 for (j = 0; j < NUM_BELT_PARTS; j++)
12620 int element = belt_base_active_element[i] + j;
12621 int graphic = el2img(element);
12622 int anim_mode = graphic_info[graphic].anim_mode;
12624 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
12625 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
12630 static void LoadEngineSnapshotValues_RND()
12632 unsigned long num_random_calls = game.num_random_calls;
12635 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12637 int element = EL_CUSTOM_START + i;
12639 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
12642 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12644 int element = EL_GROUP_START + i;
12646 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
12649 for (i = 0; i < 4; i++)
12651 for (j = 0; j < NUM_BELT_PARTS; j++)
12653 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
12654 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
12656 graphic_info[graphic].anim_mode = anim_mode;
12660 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
12662 InitRND(tape.random_seed);
12663 for (i = 0; i < num_random_calls; i++)
12667 if (game.num_random_calls != num_random_calls)
12669 Error(ERR_RETURN, "number of random calls out of sync");
12670 Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
12671 Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
12672 Error(ERR_EXIT, "this should not happen -- please debug");
12676 static void SaveEngineSnapshotBuffer(void *buffer, int size)
12678 struct EngineSnapshotNodeInfo *bi =
12679 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
12681 bi->buffer_orig = buffer;
12682 bi->buffer_copy = checked_malloc(size);
12685 memcpy(bi->buffer_copy, buffer, size);
12687 addNodeToList(&engine_snapshot_list, NULL, bi);
12690 void SaveEngineSnapshot()
12692 FreeEngineSnapshot(); /* free previous snapshot, if needed */
12694 if (level_editor_test_game) /* do not save snapshots from editor */
12697 /* copy some special values to a structure better suited for the snapshot */
12699 SaveEngineSnapshotValues_RND();
12700 SaveEngineSnapshotValues_EM();
12702 /* save values stored in special snapshot structure */
12704 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
12705 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
12707 /* save further RND engine values */
12709 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
12710 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
12711 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
12713 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
12714 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
12715 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
12716 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
12718 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
12719 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
12720 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
12721 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
12722 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
12724 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
12725 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
12726 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
12728 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
12730 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
12732 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
12733 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
12735 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
12736 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
12737 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
12738 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
12739 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
12740 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
12741 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
12742 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
12743 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
12744 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
12745 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
12746 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
12747 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
12748 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
12749 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
12750 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
12751 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
12753 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
12754 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
12756 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
12757 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
12758 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
12760 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
12761 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
12763 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
12764 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
12765 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
12766 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
12767 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
12769 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
12770 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
12772 /* save level identification information */
12774 setString(&snapshot_level_identifier, leveldir_current->identifier);
12775 snapshot_level_nr = level_nr;
12778 ListNode *node = engine_snapshot_list;
12781 while (node != NULL)
12783 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
12788 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
12792 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
12794 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
12797 void LoadEngineSnapshot()
12799 ListNode *node = engine_snapshot_list;
12801 if (engine_snapshot_list == NULL)
12804 while (node != NULL)
12806 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
12811 /* restore special values from snapshot structure */
12813 LoadEngineSnapshotValues_RND();
12814 LoadEngineSnapshotValues_EM();
12817 boolean CheckEngineSnapshot()
12819 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
12820 snapshot_level_nr == level_nr);
12824 /* ---------- new game button stuff ---------------------------------------- */
12826 /* graphic position values for game buttons */
12827 #define GAME_BUTTON_XSIZE 30
12828 #define GAME_BUTTON_YSIZE 30
12829 #define GAME_BUTTON_XPOS 5
12830 #define GAME_BUTTON_YPOS 215
12831 #define SOUND_BUTTON_XPOS 5
12832 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12834 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12835 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12836 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12837 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12838 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12839 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12846 } gamebutton_info[NUM_GAME_BUTTONS] =
12849 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12854 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12855 GAME_CTRL_ID_PAUSE,
12859 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12864 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12865 SOUND_CTRL_ID_MUSIC,
12866 "background music on/off"
12869 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12870 SOUND_CTRL_ID_LOOPS,
12871 "sound loops on/off"
12874 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12875 SOUND_CTRL_ID_SIMPLE,
12876 "normal sounds on/off"
12880 void CreateGameButtons()
12884 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12886 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12887 struct GadgetInfo *gi;
12890 unsigned long event_mask;
12891 int gd_xoffset, gd_yoffset;
12892 int gd_x1, gd_x2, gd_y1, gd_y2;
12895 gd_xoffset = gamebutton_info[i].x;
12896 gd_yoffset = gamebutton_info[i].y;
12897 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12898 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12900 if (id == GAME_CTRL_ID_STOP ||
12901 id == GAME_CTRL_ID_PAUSE ||
12902 id == GAME_CTRL_ID_PLAY)
12904 button_type = GD_TYPE_NORMAL_BUTTON;
12906 event_mask = GD_EVENT_RELEASED;
12907 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12908 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12912 button_type = GD_TYPE_CHECK_BUTTON;
12914 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12915 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12916 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12917 event_mask = GD_EVENT_PRESSED;
12918 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12919 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12922 gi = CreateGadget(GDI_CUSTOM_ID, id,
12923 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12924 GDI_X, DX + gd_xoffset,
12925 GDI_Y, DY + gd_yoffset,
12926 GDI_WIDTH, GAME_BUTTON_XSIZE,
12927 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12928 GDI_TYPE, button_type,
12929 GDI_STATE, GD_BUTTON_UNPRESSED,
12930 GDI_CHECKED, checked,
12931 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12932 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12933 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12934 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12935 GDI_EVENT_MASK, event_mask,
12936 GDI_CALLBACK_ACTION, HandleGameButtons,
12940 Error(ERR_EXIT, "cannot create gadget");
12942 game_gadget[id] = gi;
12946 void FreeGameButtons()
12950 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12951 FreeGadget(game_gadget[i]);
12954 static void MapGameButtons()
12958 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12959 MapGadget(game_gadget[i]);
12962 void UnmapGameButtons()
12966 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12967 UnmapGadget(game_gadget[i]);
12970 static void HandleGameButtons(struct GadgetInfo *gi)
12972 int id = gi->custom_id;
12974 if (game_status != GAME_MODE_PLAYING)
12979 case GAME_CTRL_ID_STOP:
12983 RequestQuitGame(TRUE);
12986 case GAME_CTRL_ID_PAUSE:
12987 if (options.network)
12989 #if defined(NETWORK_AVALIABLE)
12991 SendToServer_ContinuePlaying();
12993 SendToServer_PausePlaying();
12997 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13000 case GAME_CTRL_ID_PLAY:
13003 #if defined(NETWORK_AVALIABLE)
13004 if (options.network)
13005 SendToServer_ContinuePlaying();
13009 tape.pausing = FALSE;
13010 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13015 case SOUND_CTRL_ID_MUSIC:
13016 if (setup.sound_music)
13018 setup.sound_music = FALSE;
13021 else if (audio.music_available)
13023 setup.sound = setup.sound_music = TRUE;
13025 SetAudioMode(setup.sound);
13031 case SOUND_CTRL_ID_LOOPS:
13032 if (setup.sound_loops)
13033 setup.sound_loops = FALSE;
13034 else if (audio.loops_available)
13036 setup.sound = setup.sound_loops = TRUE;
13037 SetAudioMode(setup.sound);
13041 case SOUND_CTRL_ID_SIMPLE:
13042 if (setup.sound_simple)
13043 setup.sound_simple = FALSE;
13044 else if (audio.sound_available)
13046 setup.sound = setup.sound_simple = TRUE;
13047 SetAudioMode(setup.sound);