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)
65 /* for MovePlayer() */
66 #define MP_NO_ACTION 0
69 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
71 /* for ScrollPlayer() */
73 #define SCROLL_GO_ON 1
75 /* for Bang()/Explode() */
76 #define EX_PHASE_START 0
77 #define EX_TYPE_NONE 0
78 #define EX_TYPE_NORMAL (1 << 0)
79 #define EX_TYPE_CENTER (1 << 1)
80 #define EX_TYPE_BORDER (1 << 2)
81 #define EX_TYPE_CROSS (1 << 3)
82 #define EX_TYPE_DYNA (1 << 4)
83 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
85 #define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0)
87 /* special positions in the game control window (relative to control window) */
88 #define XX_LEVEL1 (game.panel.level.x)
89 #define XX_LEVEL2 (game.panel.level.x - 1)
90 #define YY_LEVEL (game.panel.level.y)
91 #define XX_EMERALDS (game.panel.gems.x)
92 #define YY_EMERALDS (game.panel.gems.y)
93 #define XX_DYNAMITE (game.panel.inventory.x)
94 #define YY_DYNAMITE (game.panel.inventory.y)
95 #define XX_KEYS (game.panel.keys.x)
96 #define YY_KEYS (game.panel.keys.y)
97 #define XX_SCORE (game.panel.score.x)
98 #define YY_SCORE (game.panel.score.y)
99 #define XX_TIME1 (game.panel.time.x)
100 #define XX_TIME2 (game.panel.time.x + 1)
101 #define YY_TIME (game.panel.time.y)
103 /* special positions in the game control window (relative to main window) */
104 #define DX_LEVEL1 (DX + XX_LEVEL1)
105 #define DX_LEVEL2 (DX + XX_LEVEL2)
106 #define DY_LEVEL (DY + YY_LEVEL)
107 #define DX_EMERALDS (DX + XX_EMERALDS)
108 #define DY_EMERALDS (DY + YY_EMERALDS)
109 #define DX_DYNAMITE (DX + XX_DYNAMITE)
110 #define DY_DYNAMITE (DY + YY_DYNAMITE)
111 #define DX_KEYS (DX + XX_KEYS)
112 #define DY_KEYS (DY + YY_KEYS)
113 #define DX_SCORE (DX + XX_SCORE)
114 #define DY_SCORE (DY + YY_SCORE)
115 #define DX_TIME1 (DX + XX_TIME1)
116 #define DX_TIME2 (DX + XX_TIME2)
117 #define DY_TIME (DY + YY_TIME)
119 /* values for delayed check of falling and moving elements and for collision */
120 #define CHECK_DELAY_MOVING 3
121 #define CHECK_DELAY_FALLING 3
122 #define CHECK_DELAY_COLLISION 2
124 /* values for initial player move delay (initial delay counter value) */
125 #define INITIAL_MOVE_DELAY_OFF -1
126 #define INITIAL_MOVE_DELAY_ON 0
128 /* values for player movement speed (which is in fact a delay value) */
129 #define MOVE_DELAY_MIN_SPEED 32
130 #define MOVE_DELAY_NORMAL_SPEED 8
131 #define MOVE_DELAY_HIGH_SPEED 4
132 #define MOVE_DELAY_MAX_SPEED 1
134 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
135 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
137 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
138 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
140 /* values for other actions */
141 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
142 #define MOVE_STEPSIZE_MIN (1)
143 #define MOVE_STEPSIZE_MAX (TILEX)
145 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
146 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
148 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
150 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
151 RND(element_info[e].push_delay_random))
152 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
153 RND(element_info[e].drop_delay_random))
154 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
155 RND(element_info[e].move_delay_random))
156 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
157 (element_info[e].move_delay_random))
158 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
159 RND(element_info[e].ce_value_random_initial))
160 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
161 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
162 RND((c)->delay_random * (c)->delay_frames))
163 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
164 RND((c)->delay_random))
167 #define GET_VALID_RUNTIME_ELEMENT(e) \
168 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
170 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
171 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
172 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
173 (be) + (e) - EL_SELF)
175 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
176 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
177 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
178 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
179 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
180 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
181 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
182 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
183 RESOLVED_REFERENCE_ELEMENT(be, e) : \
186 #define CAN_GROW_INTO(e) \
187 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
189 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
190 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
193 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
194 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
195 (CAN_MOVE_INTO_ACID(e) && \
196 Feld[x][y] == EL_ACID) || \
199 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
200 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
201 (CAN_MOVE_INTO_ACID(e) && \
202 Feld[x][y] == EL_ACID) || \
205 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
206 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
208 (CAN_MOVE_INTO_ACID(e) && \
209 Feld[x][y] == EL_ACID) || \
210 (DONT_COLLIDE_WITH(e) && \
212 !PLAYER_ENEMY_PROTECTED(x, y))))
214 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
215 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
217 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
218 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
220 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
221 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
223 #define ANDROID_CAN_CLONE_FIELD(x, y) \
224 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
225 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
227 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
228 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
230 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
231 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
233 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
234 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
236 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
237 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
239 #define PIG_CAN_ENTER_FIELD(e, x, y) \
240 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
242 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
243 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
244 IS_FOOD_PENGUIN(Feld[x][y])))
245 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
246 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
248 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
249 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
251 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
252 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
254 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
255 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
256 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
258 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
260 #define CE_ENTER_FIELD_COND(e, x, y) \
261 (!IS_PLAYER(x, y) && \
262 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
264 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
265 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
267 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
268 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
270 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
271 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
272 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
273 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
275 /* game button identifiers */
276 #define GAME_CTRL_ID_STOP 0
277 #define GAME_CTRL_ID_PAUSE 1
278 #define GAME_CTRL_ID_PLAY 2
279 #define SOUND_CTRL_ID_MUSIC 3
280 #define SOUND_CTRL_ID_LOOPS 4
281 #define SOUND_CTRL_ID_SIMPLE 5
283 #define NUM_GAME_BUTTONS 6
286 /* forward declaration for internal use */
288 static void CreateField(int, int, int);
290 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
291 static void AdvanceFrameAndPlayerCounters(int);
293 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
294 static boolean MovePlayer(struct PlayerInfo *, int, int);
295 static void ScrollPlayer(struct PlayerInfo *, int);
296 static void ScrollScreen(struct PlayerInfo *, int);
298 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
300 static void InitBeltMovement(void);
301 static void CloseAllOpenTimegates(void);
302 static void CheckGravityMovement(struct PlayerInfo *);
303 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
304 static void KillPlayerUnlessEnemyProtected(int, int);
305 static void KillPlayerUnlessExplosionProtected(int, int);
307 static void TestIfPlayerTouchesCustomElement(int, int);
308 static void TestIfElementTouchesCustomElement(int, int);
309 static void TestIfElementHitsCustomElement(int, int, int);
311 static void TestIfElementSmashesCustomElement(int, int, int);
314 static void HandleElementChange(int, int, int);
315 static void ExecuteCustomElementAction(int, int, int, int);
316 static boolean ChangeElement(int, int, int, int);
318 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
319 #define CheckTriggeredElementChange(x, y, e, ev) \
320 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
321 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
322 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
323 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
324 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
325 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
326 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
328 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
329 #define CheckElementChange(x, y, e, te, ev) \
330 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
331 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
332 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
333 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
334 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
336 static void PlayLevelSound(int, int, int);
337 static void PlayLevelSoundNearest(int, int, int);
338 static void PlayLevelSoundAction(int, int, int);
339 static void PlayLevelSoundElementAction(int, int, int, int);
340 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
341 static void PlayLevelSoundActionIfLoop(int, int, int);
342 static void StopLevelSoundActionIfLoop(int, int, int);
343 static void PlayLevelMusic();
345 static void MapGameButtons();
346 static void HandleGameButtons(struct GadgetInfo *);
348 int AmoebeNachbarNr(int, int);
349 void AmoebeUmwandeln(int, int);
350 void ContinueMoving(int, int);
352 void InitMovDir(int, int);
353 void InitAmoebaNr(int, int);
354 int NewHiScore(void);
356 void TestIfGoodThingHitsBadThing(int, int, int);
357 void TestIfBadThingHitsGoodThing(int, int, int);
358 void TestIfPlayerTouchesBadThing(int, int);
359 void TestIfPlayerRunsIntoBadThing(int, int, int);
360 void TestIfBadThingTouchesPlayer(int, int);
361 void TestIfBadThingRunsIntoPlayer(int, int, int);
362 void TestIfFriendTouchesBadThing(int, int);
363 void TestIfBadThingTouchesFriend(int, int);
364 void TestIfBadThingTouchesOtherBadThing(int, int);
366 void KillPlayer(struct PlayerInfo *);
367 void BuryPlayer(struct PlayerInfo *);
368 void RemovePlayer(struct PlayerInfo *);
370 boolean SnapField(struct PlayerInfo *, int, int);
371 boolean DropElement(struct PlayerInfo *);
373 static int getInvisibleActiveFromInvisibleElement(int);
374 static int getInvisibleFromInvisibleActiveElement(int);
376 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
379 /* ------------------------------------------------------------------------- */
380 /* definition of elements that automatically change to other elements after */
381 /* a specified time, eventually calling a function when changing */
382 /* ------------------------------------------------------------------------- */
384 /* forward declaration for changer functions */
385 static void InitBuggyBase(int, int);
386 static void WarnBuggyBase(int, int);
388 static void InitTrap(int, int);
389 static void ActivateTrap(int, int);
390 static void ChangeActiveTrap(int, int);
392 static void InitRobotWheel(int, int);
393 static void RunRobotWheel(int, int);
394 static void StopRobotWheel(int, int);
396 static void InitTimegateWheel(int, int);
397 static void RunTimegateWheel(int, int);
399 static void InitMagicBallDelay(int, int);
400 static void ActivateMagicBall(int, int);
402 struct ChangingElementInfo
407 void (*pre_change_function)(int x, int y);
408 void (*change_function)(int x, int y);
409 void (*post_change_function)(int x, int y);
412 static struct ChangingElementInfo change_delay_list[] =
463 EL_SWITCHGATE_OPENING,
471 EL_SWITCHGATE_CLOSING,
472 EL_SWITCHGATE_CLOSED,
504 EL_ACID_SPLASH_RIGHT,
513 EL_SP_BUGGY_BASE_ACTIVATING,
520 EL_SP_BUGGY_BASE_ACTIVATING,
521 EL_SP_BUGGY_BASE_ACTIVE,
528 EL_SP_BUGGY_BASE_ACTIVE,
552 EL_ROBOT_WHEEL_ACTIVE,
560 EL_TIMEGATE_SWITCH_ACTIVE,
568 EL_EMC_MAGIC_BALL_ACTIVE,
569 EL_EMC_MAGIC_BALL_ACTIVE,
576 EL_EMC_SPRING_BUMPER_ACTIVE,
577 EL_EMC_SPRING_BUMPER,
584 EL_DIAGONAL_SHRINKING,
613 int push_delay_fixed, push_delay_random;
618 { EL_BALLOON, 0, 0 },
620 { EL_SOKOBAN_OBJECT, 2, 0 },
621 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
622 { EL_SATELLITE, 2, 0 },
623 { EL_SP_DISK_YELLOW, 2, 0 },
625 { EL_UNDEFINED, 0, 0 },
633 move_stepsize_list[] =
635 { EL_AMOEBA_DROP, 2 },
636 { EL_AMOEBA_DROPPING, 2 },
637 { EL_QUICKSAND_FILLING, 1 },
638 { EL_QUICKSAND_EMPTYING, 1 },
639 { EL_MAGIC_WALL_FILLING, 2 },
640 { EL_BD_MAGIC_WALL_FILLING, 2 },
641 { EL_MAGIC_WALL_EMPTYING, 2 },
642 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
652 collect_count_list[] =
655 { EL_BD_DIAMOND, 1 },
656 { EL_EMERALD_YELLOW, 1 },
657 { EL_EMERALD_RED, 1 },
658 { EL_EMERALD_PURPLE, 1 },
660 { EL_SP_INFOTRON, 1 },
672 access_direction_list[] =
674 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
675 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
676 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
677 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
678 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
679 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
680 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
681 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
682 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
683 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
684 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
686 { EL_SP_PORT_LEFT, MV_RIGHT },
687 { EL_SP_PORT_RIGHT, MV_LEFT },
688 { EL_SP_PORT_UP, MV_DOWN },
689 { EL_SP_PORT_DOWN, MV_UP },
690 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
691 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
692 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
693 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
694 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
695 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
696 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
697 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
698 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
699 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
700 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
701 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
702 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
703 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
704 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
706 { EL_UNDEFINED, MV_NONE }
709 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
711 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
712 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
713 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
714 IS_JUST_CHANGING(x, y))
716 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
718 /* static variables for playfield scan mode (scanning forward or backward) */
719 static int playfield_scan_start_x = 0;
720 static int playfield_scan_start_y = 0;
721 static int playfield_scan_delta_x = 1;
722 static int playfield_scan_delta_y = 1;
724 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
725 (y) >= 0 && (y) <= lev_fieldy - 1; \
726 (y) += playfield_scan_delta_y) \
727 for ((x) = playfield_scan_start_x; \
728 (x) >= 0 && (x) <= lev_fieldx - 1; \
729 (x) += playfield_scan_delta_x) \
732 void DEBUG_SetMaximumDynamite()
736 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
737 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
738 local_player->inventory_element[local_player->inventory_size++] =
743 static void InitPlayfieldScanModeVars()
745 if (game.use_reverse_scan_direction)
747 playfield_scan_start_x = lev_fieldx - 1;
748 playfield_scan_start_y = lev_fieldy - 1;
750 playfield_scan_delta_x = -1;
751 playfield_scan_delta_y = -1;
755 playfield_scan_start_x = 0;
756 playfield_scan_start_y = 0;
758 playfield_scan_delta_x = 1;
759 playfield_scan_delta_y = 1;
763 static void InitPlayfieldScanMode(int mode)
765 game.use_reverse_scan_direction =
766 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
768 InitPlayfieldScanModeVars();
771 static int get_move_delay_from_stepsize(int move_stepsize)
774 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
776 /* make sure that stepsize value is always a power of 2 */
777 move_stepsize = (1 << log_2(move_stepsize));
779 return TILEX / move_stepsize;
782 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
785 int player_nr = player->index_nr;
786 int move_delay = get_move_delay_from_stepsize(move_stepsize);
787 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
789 /* do no immediately change move delay -- the player might just be moving */
790 player->move_delay_value_next = move_delay;
792 /* information if player can move must be set separately */
793 player->cannot_move = cannot_move;
797 player->move_delay = game.initial_move_delay[player_nr];
798 player->move_delay_value = game.initial_move_delay_value[player_nr];
800 player->move_delay_value_next = -1;
802 player->move_delay_reset_counter = 0;
806 void GetPlayerConfig()
808 if (!audio.sound_available)
809 setup.sound_simple = FALSE;
811 if (!audio.loops_available)
812 setup.sound_loops = FALSE;
814 if (!audio.music_available)
815 setup.sound_music = FALSE;
817 if (!video.fullscreen_available)
818 setup.fullscreen = FALSE;
820 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
822 SetAudioMode(setup.sound);
826 static int getBeltNrFromBeltElement(int element)
828 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
829 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
830 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
833 static int getBeltNrFromBeltActiveElement(int element)
835 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
836 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
837 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
840 static int getBeltNrFromBeltSwitchElement(int element)
842 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
843 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
844 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
847 static int getBeltDirNrFromBeltSwitchElement(int element)
849 static int belt_base_element[4] =
851 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
852 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
853 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
854 EL_CONVEYOR_BELT_4_SWITCH_LEFT
857 int belt_nr = getBeltNrFromBeltSwitchElement(element);
858 int belt_dir_nr = element - belt_base_element[belt_nr];
860 return (belt_dir_nr % 3);
863 static int getBeltDirFromBeltSwitchElement(int element)
865 static int belt_move_dir[3] =
872 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
874 return belt_move_dir[belt_dir_nr];
877 static int get_element_from_group_element(int element)
879 if (IS_GROUP_ELEMENT(element))
881 struct ElementGroupInfo *group = element_info[element].group;
882 int last_anim_random_frame = gfx.anim_random_frame;
885 if (group->choice_mode == ANIM_RANDOM)
886 gfx.anim_random_frame = RND(group->num_elements_resolved);
888 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
889 group->choice_mode, 0,
892 if (group->choice_mode == ANIM_RANDOM)
893 gfx.anim_random_frame = last_anim_random_frame;
897 element = group->element_resolved[element_pos];
903 static void InitPlayerField(int x, int y, int element, boolean init_game)
905 if (element == EL_SP_MURPHY)
909 if (stored_player[0].present)
911 Feld[x][y] = EL_SP_MURPHY_CLONE;
917 stored_player[0].use_murphy = TRUE;
919 if (!level.use_artwork_element[0])
920 stored_player[0].artwork_element = EL_SP_MURPHY;
923 Feld[x][y] = EL_PLAYER_1;
929 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
930 int jx = player->jx, jy = player->jy;
932 player->present = TRUE;
934 player->block_last_field = (element == EL_SP_MURPHY ?
935 level.sp_block_last_field :
936 level.block_last_field);
938 /* ---------- initialize player's last field block delay --------------- */
940 /* always start with reliable default value (no adjustment needed) */
941 player->block_delay_adjustment = 0;
943 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
944 if (player->block_last_field && element == EL_SP_MURPHY)
945 player->block_delay_adjustment = 1;
947 /* special case 2: in game engines before 3.1.1, blocking was different */
948 if (game.use_block_last_field_bug)
949 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
951 if (!options.network || player->connected)
953 player->active = TRUE;
955 /* remove potentially duplicate players */
956 if (StorePlayer[jx][jy] == Feld[x][y])
957 StorePlayer[jx][jy] = 0;
959 StorePlayer[x][y] = Feld[x][y];
963 printf("Player %d activated.\n", player->element_nr);
964 printf("[Local player is %d and currently %s.]\n",
965 local_player->element_nr,
966 local_player->active ? "active" : "not active");
970 Feld[x][y] = EL_EMPTY;
972 player->jx = player->last_jx = x;
973 player->jy = player->last_jy = y;
977 static void InitField(int x, int y, boolean init_game)
979 int element = Feld[x][y];
988 InitPlayerField(x, y, element, init_game);
991 case EL_SOKOBAN_FIELD_PLAYER:
992 element = Feld[x][y] = EL_PLAYER_1;
993 InitField(x, y, init_game);
995 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
996 InitField(x, y, init_game);
999 case EL_SOKOBAN_FIELD_EMPTY:
1000 local_player->sokobanfields_still_needed++;
1004 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1005 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1006 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1007 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1008 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1009 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1010 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1011 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1012 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1013 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1022 case EL_SPACESHIP_RIGHT:
1023 case EL_SPACESHIP_UP:
1024 case EL_SPACESHIP_LEFT:
1025 case EL_SPACESHIP_DOWN:
1026 case EL_BD_BUTTERFLY:
1027 case EL_BD_BUTTERFLY_RIGHT:
1028 case EL_BD_BUTTERFLY_UP:
1029 case EL_BD_BUTTERFLY_LEFT:
1030 case EL_BD_BUTTERFLY_DOWN:
1032 case EL_BD_FIREFLY_RIGHT:
1033 case EL_BD_FIREFLY_UP:
1034 case EL_BD_FIREFLY_LEFT:
1035 case EL_BD_FIREFLY_DOWN:
1036 case EL_PACMAN_RIGHT:
1038 case EL_PACMAN_LEFT:
1039 case EL_PACMAN_DOWN:
1041 case EL_YAMYAM_LEFT:
1042 case EL_YAMYAM_RIGHT:
1044 case EL_YAMYAM_DOWN:
1045 case EL_DARK_YAMYAM:
1048 case EL_SP_SNIKSNAK:
1049 case EL_SP_ELECTRON:
1058 case EL_AMOEBA_FULL:
1063 case EL_AMOEBA_DROP:
1064 if (y == lev_fieldy - 1)
1066 Feld[x][y] = EL_AMOEBA_GROWING;
1067 Store[x][y] = EL_AMOEBA_WET;
1071 case EL_DYNAMITE_ACTIVE:
1072 case EL_SP_DISK_RED_ACTIVE:
1073 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1074 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1075 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1076 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1077 MovDelay[x][y] = 96;
1080 case EL_EM_DYNAMITE_ACTIVE:
1081 MovDelay[x][y] = 32;
1085 local_player->lights_still_needed++;
1089 local_player->friends_still_needed++;
1094 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1097 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1098 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1099 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1100 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1101 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1102 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1103 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1104 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1105 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1106 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1107 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1108 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1111 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1112 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1113 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1115 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1117 game.belt_dir[belt_nr] = belt_dir;
1118 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1120 else /* more than one switch -- set it like the first switch */
1122 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1127 #if !USE_BOTH_SWITCHGATE_SWITCHES
1128 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1130 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1134 case EL_LIGHT_SWITCH_ACTIVE:
1136 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1139 case EL_INVISIBLE_STEELWALL:
1140 case EL_INVISIBLE_WALL:
1141 case EL_INVISIBLE_SAND:
1142 if (game.light_time_left > 0 ||
1143 game.lenses_time_left > 0)
1144 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1147 case EL_EMC_MAGIC_BALL:
1148 if (game.ball_state)
1149 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1152 case EL_EMC_MAGIC_BALL_SWITCH:
1153 if (game.ball_state)
1154 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1158 if (IS_CUSTOM_ELEMENT(element))
1160 if (CAN_MOVE(element))
1163 #if USE_NEW_CUSTOM_VALUE
1164 if (!element_info[element].use_last_ce_value || init_game)
1165 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1168 else if (IS_GROUP_ELEMENT(element))
1170 Feld[x][y] = get_element_from_group_element(element);
1172 InitField(x, y, init_game);
1179 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1182 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1184 InitField(x, y, init_game);
1186 /* not needed to call InitMovDir() -- already done by InitField()! */
1187 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1188 CAN_MOVE(Feld[x][y]))
1192 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1194 int old_element = Feld[x][y];
1196 InitField(x, y, init_game);
1198 /* not needed to call InitMovDir() -- already done by InitField()! */
1199 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1200 CAN_MOVE(old_element) &&
1201 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1204 /* this case is in fact a combination of not less than three bugs:
1205 first, it calls InitMovDir() for elements that can move, although this is
1206 already done by InitField(); then, it checks the element that was at this
1207 field _before_ the call to InitField() (which can change it); lastly, it
1208 was not called for "mole with direction" elements, which were treated as
1209 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1213 inline void DrawGameValue_Emeralds(int value)
1215 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1217 if (PANEL_DEACTIVATED(game.panel.gems))
1220 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1223 inline void DrawGameValue_Dynamite(int value)
1225 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1227 if (PANEL_DEACTIVATED(game.panel.inventory))
1230 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1233 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1235 int base_key_graphic = EL_KEY_1;
1238 if (PANEL_DEACTIVATED(game.panel.keys))
1241 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1242 base_key_graphic = EL_EM_KEY_1;
1244 /* currently only 4 of 8 possible keys are displayed */
1245 for (i = 0; i < STD_NUM_KEYS; i++)
1247 int x = XX_KEYS + i * MINI_TILEX;
1251 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1253 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1254 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1258 inline void DrawGameValue_Score(int value)
1260 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1262 if (PANEL_DEACTIVATED(game.panel.score))
1265 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1268 inline void DrawGameValue_Time(int value)
1270 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1271 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1273 if (PANEL_DEACTIVATED(game.panel.time))
1276 /* clear background if value just changed its size */
1277 if (value == 999 || value == 1000)
1278 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1281 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1283 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1286 inline void DrawGameValue_Level(int value)
1288 if (PANEL_DEACTIVATED(game.panel.level))
1292 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1294 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
1297 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1300 int key[MAX_NUM_KEYS];
1303 /* prevent EM engine from updating time/score values parallel to GameWon() */
1304 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1305 local_player->LevelSolved)
1308 for (i = 0; i < MAX_NUM_KEYS; i++)
1309 key[i] = key_bits & (1 << i);
1311 DrawGameValue_Level(level_nr);
1313 DrawGameValue_Emeralds(emeralds);
1314 DrawGameValue_Dynamite(dynamite);
1315 DrawGameValue_Score(score);
1316 DrawGameValue_Time(time);
1318 DrawGameValue_Keys(key);
1321 void DrawGameDoorValues()
1323 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1324 int dynamite_value = 0;
1325 int score_value = (local_player->LevelSolved ? local_player->score_final :
1326 local_player->score);
1327 int gems_value = local_player->gems_still_needed;
1331 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1333 DrawGameDoorValues_EM();
1338 if (game.centered_player_nr == -1)
1340 for (i = 0; i < MAX_PLAYERS; i++)
1342 for (j = 0; j < MAX_NUM_KEYS; j++)
1343 if (stored_player[i].key[j])
1344 key_bits |= (1 << j);
1346 dynamite_value += stored_player[i].inventory_size;
1351 int player_nr = game.centered_player_nr;
1353 for (i = 0; i < MAX_NUM_KEYS; i++)
1354 if (stored_player[player_nr].key[i])
1355 key_bits |= (1 << i);
1357 dynamite_value = stored_player[player_nr].inventory_size;
1360 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1366 =============================================================================
1368 -----------------------------------------------------------------------------
1369 initialize game engine due to level / tape version number
1370 =============================================================================
1373 static void InitGameEngine()
1375 int i, j, k, l, x, y;
1377 /* set game engine from tape file when re-playing, else from level file */
1378 game.engine_version = (tape.playing ? tape.engine_version :
1379 level.game_version);
1381 /* ---------------------------------------------------------------------- */
1382 /* set flags for bugs and changes according to active game engine version */
1383 /* ---------------------------------------------------------------------- */
1386 Summary of bugfix/change:
1387 Fixed handling for custom elements that change when pushed by the player.
1389 Fixed/changed in version:
1393 Before 3.1.0, custom elements that "change when pushing" changed directly
1394 after the player started pushing them (until then handled in "DigField()").
1395 Since 3.1.0, these custom elements are not changed until the "pushing"
1396 move of the element is finished (now handled in "ContinueMoving()").
1398 Affected levels/tapes:
1399 The first condition is generally needed for all levels/tapes before version
1400 3.1.0, which might use the old behaviour before it was changed; known tapes
1401 that are affected are some tapes from the level set "Walpurgis Gardens" by
1403 The second condition is an exception from the above case and is needed for
1404 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1405 above (including some development versions of 3.1.0), but before it was
1406 known that this change would break tapes like the above and was fixed in
1407 3.1.1, so that the changed behaviour was active although the engine version
1408 while recording maybe was before 3.1.0. There is at least one tape that is
1409 affected by this exception, which is the tape for the one-level set "Bug
1410 Machine" by Juergen Bonhagen.
1413 game.use_change_when_pushing_bug =
1414 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1416 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1417 tape.game_version < VERSION_IDENT(3,1,1,0)));
1420 Summary of bugfix/change:
1421 Fixed handling for blocking the field the player leaves when moving.
1423 Fixed/changed in version:
1427 Before 3.1.1, when "block last field when moving" was enabled, the field
1428 the player is leaving when moving was blocked for the time of the move,
1429 and was directly unblocked afterwards. This resulted in the last field
1430 being blocked for exactly one less than the number of frames of one player
1431 move. Additionally, even when blocking was disabled, the last field was
1432 blocked for exactly one frame.
1433 Since 3.1.1, due to changes in player movement handling, the last field
1434 is not blocked at all when blocking is disabled. When blocking is enabled,
1435 the last field is blocked for exactly the number of frames of one player
1436 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1437 last field is blocked for exactly one more than the number of frames of
1440 Affected levels/tapes:
1441 (!!! yet to be determined -- probably many !!!)
1444 game.use_block_last_field_bug =
1445 (game.engine_version < VERSION_IDENT(3,1,1,0));
1448 Summary of bugfix/change:
1449 Changed behaviour of CE changes with multiple changes per single frame.
1451 Fixed/changed in version:
1455 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1456 This resulted in race conditions where CEs seem to behave strange in some
1457 situations (where triggered CE changes were just skipped because there was
1458 already a CE change on that tile in the playfield in that engine frame).
1459 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1460 (The number of changes per frame must be limited in any case, because else
1461 it is easily possible to define CE changes that would result in an infinite
1462 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1463 should be set large enough so that it would only be reached in cases where
1464 the corresponding CE change conditions run into a loop. Therefore, it seems
1465 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1466 maximal number of change pages for custom elements.)
1468 Affected levels/tapes:
1472 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1473 game.max_num_changes_per_frame = 1;
1475 game.max_num_changes_per_frame =
1476 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1479 /* ---------------------------------------------------------------------- */
1481 /* default scan direction: scan playfield from top/left to bottom/right */
1482 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1484 /* dynamically adjust element properties according to game engine version */
1485 InitElementPropertiesEngine(game.engine_version);
1488 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1489 printf(" tape version == %06d [%s] [file: %06d]\n",
1490 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1492 printf(" => game.engine_version == %06d\n", game.engine_version);
1495 /* ---------- initialize player's initial move delay --------------------- */
1497 /* dynamically adjust player properties according to level information */
1498 for (i = 0; i < MAX_PLAYERS; i++)
1499 game.initial_move_delay_value[i] =
1500 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1502 /* dynamically adjust player properties according to game engine version */
1503 for (i = 0; i < MAX_PLAYERS; i++)
1504 game.initial_move_delay[i] =
1505 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1506 game.initial_move_delay_value[i] : 0);
1508 /* ---------- initialize player's initial push delay --------------------- */
1510 /* dynamically adjust player properties according to game engine version */
1511 game.initial_push_delay_value =
1512 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1514 /* ---------- initialize changing elements ------------------------------- */
1516 /* initialize changing elements information */
1517 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1519 struct ElementInfo *ei = &element_info[i];
1521 /* this pointer might have been changed in the level editor */
1522 ei->change = &ei->change_page[0];
1524 if (!IS_CUSTOM_ELEMENT(i))
1526 ei->change->target_element = EL_EMPTY_SPACE;
1527 ei->change->delay_fixed = 0;
1528 ei->change->delay_random = 0;
1529 ei->change->delay_frames = 1;
1532 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1534 ei->has_change_event[j] = FALSE;
1536 ei->event_page_nr[j] = 0;
1537 ei->event_page[j] = &ei->change_page[0];
1541 /* add changing elements from pre-defined list */
1542 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1544 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1545 struct ElementInfo *ei = &element_info[ch_delay->element];
1547 ei->change->target_element = ch_delay->target_element;
1548 ei->change->delay_fixed = ch_delay->change_delay;
1550 ei->change->pre_change_function = ch_delay->pre_change_function;
1551 ei->change->change_function = ch_delay->change_function;
1552 ei->change->post_change_function = ch_delay->post_change_function;
1554 ei->change->can_change = TRUE;
1555 ei->change->can_change_or_has_action = TRUE;
1557 ei->has_change_event[CE_DELAY] = TRUE;
1559 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1560 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1563 /* ---------- initialize internal run-time variables ------------- */
1565 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1567 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1569 for (j = 0; j < ei->num_change_pages; j++)
1571 ei->change_page[j].can_change_or_has_action =
1572 (ei->change_page[j].can_change |
1573 ei->change_page[j].has_action);
1577 /* add change events from custom element configuration */
1578 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1580 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1582 for (j = 0; j < ei->num_change_pages; j++)
1584 if (!ei->change_page[j].can_change_or_has_action)
1587 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1589 /* only add event page for the first page found with this event */
1590 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1592 ei->has_change_event[k] = TRUE;
1594 ei->event_page_nr[k] = j;
1595 ei->event_page[k] = &ei->change_page[j];
1601 /* ---------- initialize run-time trigger player and element ------------- */
1603 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1605 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1607 for (j = 0; j < ei->num_change_pages; j++)
1609 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1610 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1611 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1612 ei->change_page[j].actual_trigger_ce_value = 0;
1613 ei->change_page[j].actual_trigger_ce_score = 0;
1617 /* ---------- initialize trigger events ---------------------------------- */
1619 /* initialize trigger events information */
1620 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1621 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1622 trigger_events[i][j] = FALSE;
1624 /* add trigger events from element change event properties */
1625 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1627 struct ElementInfo *ei = &element_info[i];
1629 for (j = 0; j < ei->num_change_pages; j++)
1631 if (!ei->change_page[j].can_change_or_has_action)
1634 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1636 int trigger_element = ei->change_page[j].trigger_element;
1638 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1640 if (ei->change_page[j].has_event[k])
1642 if (IS_GROUP_ELEMENT(trigger_element))
1644 struct ElementGroupInfo *group =
1645 element_info[trigger_element].group;
1647 for (l = 0; l < group->num_elements_resolved; l++)
1648 trigger_events[group->element_resolved[l]][k] = TRUE;
1650 else if (trigger_element == EL_ANY_ELEMENT)
1651 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1652 trigger_events[l][k] = TRUE;
1654 trigger_events[trigger_element][k] = TRUE;
1661 /* ---------- initialize push delay -------------------------------------- */
1663 /* initialize push delay values to default */
1664 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1666 if (!IS_CUSTOM_ELEMENT(i))
1668 /* set default push delay values (corrected since version 3.0.7-1) */
1669 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1671 element_info[i].push_delay_fixed = 2;
1672 element_info[i].push_delay_random = 8;
1676 element_info[i].push_delay_fixed = 8;
1677 element_info[i].push_delay_random = 8;
1682 /* set push delay value for certain elements from pre-defined list */
1683 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1685 int e = push_delay_list[i].element;
1687 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1688 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1691 /* set push delay value for Supaplex elements for newer engine versions */
1692 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1694 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1696 if (IS_SP_ELEMENT(i))
1698 /* set SP push delay to just enough to push under a falling zonk */
1699 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1701 element_info[i].push_delay_fixed = delay;
1702 element_info[i].push_delay_random = 0;
1707 /* ---------- initialize move stepsize ----------------------------------- */
1709 /* initialize move stepsize values to default */
1710 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1711 if (!IS_CUSTOM_ELEMENT(i))
1712 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1714 /* set move stepsize value for certain elements from pre-defined list */
1715 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1717 int e = move_stepsize_list[i].element;
1719 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1722 /* ---------- initialize collect score ----------------------------------- */
1724 /* initialize collect score values for custom elements from initial value */
1725 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1726 if (IS_CUSTOM_ELEMENT(i))
1727 element_info[i].collect_score = element_info[i].collect_score_initial;
1729 /* ---------- initialize collect count ----------------------------------- */
1731 /* initialize collect count values for non-custom elements */
1732 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1733 if (!IS_CUSTOM_ELEMENT(i))
1734 element_info[i].collect_count_initial = 0;
1736 /* add collect count values for all elements from pre-defined list */
1737 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1738 element_info[collect_count_list[i].element].collect_count_initial =
1739 collect_count_list[i].count;
1741 /* ---------- initialize access direction -------------------------------- */
1743 /* initialize access direction values to default (access from every side) */
1744 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1745 if (!IS_CUSTOM_ELEMENT(i))
1746 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1748 /* set access direction value for certain elements from pre-defined list */
1749 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1750 element_info[access_direction_list[i].element].access_direction =
1751 access_direction_list[i].direction;
1753 /* ---------- initialize explosion content ------------------------------- */
1754 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1756 if (IS_CUSTOM_ELEMENT(i))
1759 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1761 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1763 element_info[i].content.e[x][y] =
1764 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1765 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1766 i == EL_PLAYER_3 ? EL_EMERALD :
1767 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1768 i == EL_MOLE ? EL_EMERALD_RED :
1769 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1770 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1771 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1772 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1773 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1774 i == EL_WALL_EMERALD ? EL_EMERALD :
1775 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1776 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1777 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1778 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1779 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1780 i == EL_WALL_PEARL ? EL_PEARL :
1781 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1787 int get_num_special_action(int element, int action_first, int action_last)
1789 int num_special_action = 0;
1792 for (i = action_first; i <= action_last; i++)
1794 boolean found = FALSE;
1796 for (j = 0; j < NUM_DIRECTIONS; j++)
1797 if (el_act_dir2img(element, i, j) !=
1798 el_act_dir2img(element, ACTION_DEFAULT, j))
1802 num_special_action++;
1807 return num_special_action;
1812 =============================================================================
1814 -----------------------------------------------------------------------------
1815 initialize and start new game
1816 =============================================================================
1821 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1822 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1823 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1824 boolean do_fading = (game_status == GAME_MODE_MAIN);
1827 game_status = GAME_MODE_PLAYING;
1831 /* don't play tapes over network */
1832 network_playing = (options.network && !tape.playing);
1834 for (i = 0; i < MAX_PLAYERS; i++)
1836 struct PlayerInfo *player = &stored_player[i];
1838 player->index_nr = i;
1839 player->index_bit = (1 << i);
1840 player->element_nr = EL_PLAYER_1 + i;
1842 player->present = FALSE;
1843 player->active = FALSE;
1846 player->effective_action = 0;
1847 player->programmed_action = 0;
1850 player->score_final = 0;
1852 player->gems_still_needed = level.gems_needed;
1853 player->sokobanfields_still_needed = 0;
1854 player->lights_still_needed = 0;
1855 player->friends_still_needed = 0;
1857 for (j = 0; j < MAX_NUM_KEYS; j++)
1858 player->key[j] = FALSE;
1860 player->dynabomb_count = 0;
1861 player->dynabomb_size = 1;
1862 player->dynabombs_left = 0;
1863 player->dynabomb_xl = FALSE;
1865 player->MovDir = MV_NONE;
1868 player->GfxDir = MV_NONE;
1869 player->GfxAction = ACTION_DEFAULT;
1871 player->StepFrame = 0;
1873 player->use_murphy = FALSE;
1874 player->artwork_element =
1875 (level.use_artwork_element[i] ? level.artwork_element[i] :
1876 player->element_nr);
1878 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1879 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1881 player->gravity = level.initial_player_gravity[i];
1883 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1885 player->actual_frame_counter = 0;
1887 player->step_counter = 0;
1889 player->last_move_dir = MV_NONE;
1891 player->is_active = FALSE;
1893 player->is_waiting = FALSE;
1894 player->is_moving = FALSE;
1895 player->is_auto_moving = FALSE;
1896 player->is_digging = FALSE;
1897 player->is_snapping = FALSE;
1898 player->is_collecting = FALSE;
1899 player->is_pushing = FALSE;
1900 player->is_switching = FALSE;
1901 player->is_dropping = FALSE;
1902 player->is_dropping_pressed = FALSE;
1904 player->is_bored = FALSE;
1905 player->is_sleeping = FALSE;
1907 player->frame_counter_bored = -1;
1908 player->frame_counter_sleeping = -1;
1910 player->anim_delay_counter = 0;
1911 player->post_delay_counter = 0;
1913 player->dir_waiting = MV_NONE;
1914 player->action_waiting = ACTION_DEFAULT;
1915 player->last_action_waiting = ACTION_DEFAULT;
1916 player->special_action_bored = ACTION_DEFAULT;
1917 player->special_action_sleeping = ACTION_DEFAULT;
1919 player->switch_x = -1;
1920 player->switch_y = -1;
1922 player->drop_x = -1;
1923 player->drop_y = -1;
1925 player->show_envelope = 0;
1927 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
1929 player->push_delay = -1; /* initialized when pushing starts */
1930 player->push_delay_value = game.initial_push_delay_value;
1932 player->drop_delay = 0;
1933 player->drop_pressed_delay = 0;
1935 player->last_jx = player->last_jy = 0;
1936 player->jx = player->jy = 0;
1938 player->shield_normal_time_left = 0;
1939 player->shield_deadly_time_left = 0;
1941 player->inventory_infinite_element = EL_UNDEFINED;
1942 player->inventory_size = 0;
1944 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1945 SnapField(player, 0, 0);
1947 player->LevelSolved = FALSE;
1948 player->GameOver = FALSE;
1950 player->LevelSolved_GameEnd = FALSE;
1951 player->LevelSolved_SaveTape = FALSE;
1952 player->LevelSolved_SaveScore = FALSE;
1955 network_player_action_received = FALSE;
1957 #if defined(NETWORK_AVALIABLE)
1958 /* initial null action */
1959 if (network_playing)
1960 SendToServer_MovePlayer(MV_NONE);
1969 TimeLeft = level.time;
1972 ScreenMovDir = MV_NONE;
1976 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1978 AllPlayersGone = FALSE;
1980 game.yamyam_content_nr = 0;
1981 game.magic_wall_active = FALSE;
1982 game.magic_wall_time_left = 0;
1983 game.light_time_left = 0;
1984 game.timegate_time_left = 0;
1985 game.switchgate_pos = 0;
1986 game.wind_direction = level.wind_direction_initial;
1988 #if !USE_PLAYER_GRAVITY
1989 game.gravity = FALSE;
1990 game.explosions_delayed = TRUE;
1993 game.lenses_time_left = 0;
1994 game.magnify_time_left = 0;
1996 game.ball_state = level.ball_state_initial;
1997 game.ball_content_nr = 0;
1999 game.envelope_active = FALSE;
2001 /* set focus to local player for network games, else to all players */
2002 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2003 game.centered_player_nr_next = game.centered_player_nr;
2004 game.set_centered_player = FALSE;
2006 if (network_playing && tape.recording)
2008 /* store client dependent player focus when recording network games */
2009 tape.centered_player_nr_next = game.centered_player_nr_next;
2010 tape.set_centered_player = TRUE;
2013 for (i = 0; i < NUM_BELTS; i++)
2015 game.belt_dir[i] = MV_NONE;
2016 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2019 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2020 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2022 SCAN_PLAYFIELD(x, y)
2024 Feld[x][y] = level.field[x][y];
2025 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2026 ChangeDelay[x][y] = 0;
2027 ChangePage[x][y] = -1;
2028 #if USE_NEW_CUSTOM_VALUE
2029 CustomValue[x][y] = 0; /* initialized in InitField() */
2031 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2033 WasJustMoving[x][y] = 0;
2034 WasJustFalling[x][y] = 0;
2035 CheckCollision[x][y] = 0;
2037 Pushed[x][y] = FALSE;
2039 ChangeCount[x][y] = 0;
2040 ChangeEvent[x][y] = -1;
2042 ExplodePhase[x][y] = 0;
2043 ExplodeDelay[x][y] = 0;
2044 ExplodeField[x][y] = EX_TYPE_NONE;
2046 RunnerVisit[x][y] = 0;
2047 PlayerVisit[x][y] = 0;
2050 GfxRandom[x][y] = INIT_GFX_RANDOM();
2051 GfxElement[x][y] = EL_UNDEFINED;
2052 GfxAction[x][y] = ACTION_DEFAULT;
2053 GfxDir[x][y] = MV_NONE;
2056 SCAN_PLAYFIELD(x, y)
2058 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2060 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2062 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2065 InitField(x, y, TRUE);
2070 for (i = 0; i < MAX_PLAYERS; i++)
2072 struct PlayerInfo *player = &stored_player[i];
2074 /* set number of special actions for bored and sleeping animation */
2075 player->num_special_action_bored =
2076 get_num_special_action(player->artwork_element,
2077 ACTION_BORING_1, ACTION_BORING_LAST);
2078 player->num_special_action_sleeping =
2079 get_num_special_action(player->artwork_element,
2080 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2083 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2084 emulate_sb ? EMU_SOKOBAN :
2085 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2087 #if USE_NEW_ALL_SLIPPERY
2088 /* initialize type of slippery elements */
2089 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2091 if (!IS_CUSTOM_ELEMENT(i))
2093 /* default: elements slip down either to the left or right randomly */
2094 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2096 /* SP style elements prefer to slip down on the left side */
2097 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2098 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2100 /* BD style elements prefer to slip down on the left side */
2101 if (game.emulation == EMU_BOULDERDASH)
2102 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2107 /* initialize explosion and ignition delay */
2108 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2110 if (!IS_CUSTOM_ELEMENT(i))
2113 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2114 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2115 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2116 int last_phase = (num_phase + 1) * delay;
2117 int half_phase = (num_phase / 2) * delay;
2119 element_info[i].explosion_delay = last_phase - 1;
2120 element_info[i].ignition_delay = half_phase;
2122 if (i == EL_BLACK_ORB)
2123 element_info[i].ignition_delay = 1;
2127 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2128 element_info[i].explosion_delay = 1;
2130 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2131 element_info[i].ignition_delay = 1;
2135 /* correct non-moving belts to start moving left */
2136 for (i = 0; i < NUM_BELTS; i++)
2137 if (game.belt_dir[i] == MV_NONE)
2138 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2140 /* check if any connected player was not found in playfield */
2141 for (i = 0; i < MAX_PLAYERS; i++)
2143 struct PlayerInfo *player = &stored_player[i];
2145 if (player->connected && !player->present)
2147 for (j = 0; j < MAX_PLAYERS; j++)
2149 struct PlayerInfo *some_player = &stored_player[j];
2150 int jx = some_player->jx, jy = some_player->jy;
2152 /* assign first free player found that is present in the playfield */
2153 if (some_player->present && !some_player->connected)
2155 player->present = TRUE;
2156 player->active = TRUE;
2158 some_player->present = FALSE;
2159 some_player->active = FALSE;
2161 player->artwork_element = some_player->artwork_element;
2163 player->block_last_field = some_player->block_last_field;
2164 player->block_delay_adjustment = some_player->block_delay_adjustment;
2166 StorePlayer[jx][jy] = player->element_nr;
2167 player->jx = player->last_jx = jx;
2168 player->jy = player->last_jy = jy;
2178 /* when playing a tape, eliminate all players who do not participate */
2180 for (i = 0; i < MAX_PLAYERS; i++)
2182 if (stored_player[i].active && !tape.player_participates[i])
2184 struct PlayerInfo *player = &stored_player[i];
2185 int jx = player->jx, jy = player->jy;
2187 player->active = FALSE;
2188 StorePlayer[jx][jy] = 0;
2189 Feld[jx][jy] = EL_EMPTY;
2193 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2195 /* when in single player mode, eliminate all but the first active player */
2197 for (i = 0; i < MAX_PLAYERS; i++)
2199 if (stored_player[i].active)
2201 for (j = i + 1; j < MAX_PLAYERS; j++)
2203 if (stored_player[j].active)
2205 struct PlayerInfo *player = &stored_player[j];
2206 int jx = player->jx, jy = player->jy;
2208 player->active = FALSE;
2209 player->present = FALSE;
2211 StorePlayer[jx][jy] = 0;
2212 Feld[jx][jy] = EL_EMPTY;
2219 /* when recording the game, store which players take part in the game */
2222 for (i = 0; i < MAX_PLAYERS; i++)
2223 if (stored_player[i].active)
2224 tape.player_participates[i] = TRUE;
2229 for (i = 0; i < MAX_PLAYERS; i++)
2231 struct PlayerInfo *player = &stored_player[i];
2233 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2238 if (local_player == player)
2239 printf("Player %d is local player.\n", i+1);
2243 if (BorderElement == EL_EMPTY)
2246 SBX_Right = lev_fieldx - SCR_FIELDX;
2248 SBY_Lower = lev_fieldy - SCR_FIELDY;
2253 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2255 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2258 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2259 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2261 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2262 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2264 /* if local player not found, look for custom element that might create
2265 the player (make some assumptions about the right custom element) */
2266 if (!local_player->present)
2268 int start_x = 0, start_y = 0;
2269 int found_rating = 0;
2270 int found_element = EL_UNDEFINED;
2271 int player_nr = local_player->index_nr;
2273 SCAN_PLAYFIELD(x, y)
2275 int element = Feld[x][y];
2280 if (level.use_start_element[player_nr] &&
2281 level.start_element[player_nr] == element &&
2288 found_element = element;
2291 if (!IS_CUSTOM_ELEMENT(element))
2294 if (CAN_CHANGE(element))
2296 for (i = 0; i < element_info[element].num_change_pages; i++)
2298 /* check for player created from custom element as single target */
2299 content = element_info[element].change_page[i].target_element;
2300 is_player = ELEM_IS_PLAYER(content);
2302 if (is_player && (found_rating < 3 || element < found_element))
2308 found_element = element;
2313 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2315 /* check for player created from custom element as explosion content */
2316 content = element_info[element].content.e[xx][yy];
2317 is_player = ELEM_IS_PLAYER(content);
2319 if (is_player && (found_rating < 2 || element < found_element))
2321 start_x = x + xx - 1;
2322 start_y = y + yy - 1;
2325 found_element = element;
2328 if (!CAN_CHANGE(element))
2331 for (i = 0; i < element_info[element].num_change_pages; i++)
2333 /* check for player created from custom element as extended target */
2335 element_info[element].change_page[i].target_content.e[xx][yy];
2337 is_player = ELEM_IS_PLAYER(content);
2339 if (is_player && (found_rating < 1 || element < found_element))
2341 start_x = x + xx - 1;
2342 start_y = y + yy - 1;
2345 found_element = element;
2351 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2352 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2355 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2356 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2361 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2362 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2363 local_player->jx - MIDPOSX);
2365 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2366 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2367 local_player->jy - MIDPOSY);
2372 if (!game.restart_level)
2373 CloseDoor(DOOR_CLOSE_1);
2376 FadeOut(REDRAW_FIELD);
2378 /* !!! FIX THIS (START) !!! */
2379 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2381 InitGameEngine_EM();
2383 /* blit playfield from scroll buffer to normal back buffer for fading in */
2384 BlitScreenToBitmap_EM(backbuffer);
2391 /* after drawing the level, correct some elements */
2392 if (game.timegate_time_left == 0)
2393 CloseAllOpenTimegates();
2395 /* blit playfield from scroll buffer to normal back buffer for fading in */
2396 if (setup.soft_scrolling)
2397 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2399 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2401 /* !!! FIX THIS (END) !!! */
2404 FadeIn(REDRAW_FIELD);
2408 if (!game.restart_level)
2410 /* copy default game door content to main double buffer */
2411 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2412 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2415 SetPanelBackground();
2416 SetDrawBackgroundMask(REDRAW_DOOR_1);
2418 DrawGameDoorValues();
2420 if (!game.restart_level)
2424 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2425 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2426 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2430 /* copy actual game door content to door double buffer for OpenDoor() */
2431 BlitBitmap(drawto, bitmap_db_door,
2432 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2434 OpenDoor(DOOR_OPEN_ALL);
2436 PlaySound(SND_GAME_STARTING);
2438 if (setup.sound_music)
2441 KeyboardAutoRepeatOffUnlessAutoplay();
2445 for (i = 0; i < MAX_PLAYERS; i++)
2446 printf("Player %d %sactive.\n",
2447 i + 1, (stored_player[i].active ? "" : "not "));
2458 game.restart_level = FALSE;
2461 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2463 /* this is used for non-R'n'D game engines to update certain engine values */
2465 /* needed to determine if sounds are played within the visible screen area */
2466 scroll_x = actual_scroll_x;
2467 scroll_y = actual_scroll_y;
2470 void InitMovDir(int x, int y)
2472 int i, element = Feld[x][y];
2473 static int xy[4][2] =
2480 static int direction[3][4] =
2482 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2483 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2484 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2493 Feld[x][y] = EL_BUG;
2494 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2497 case EL_SPACESHIP_RIGHT:
2498 case EL_SPACESHIP_UP:
2499 case EL_SPACESHIP_LEFT:
2500 case EL_SPACESHIP_DOWN:
2501 Feld[x][y] = EL_SPACESHIP;
2502 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2505 case EL_BD_BUTTERFLY_RIGHT:
2506 case EL_BD_BUTTERFLY_UP:
2507 case EL_BD_BUTTERFLY_LEFT:
2508 case EL_BD_BUTTERFLY_DOWN:
2509 Feld[x][y] = EL_BD_BUTTERFLY;
2510 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2513 case EL_BD_FIREFLY_RIGHT:
2514 case EL_BD_FIREFLY_UP:
2515 case EL_BD_FIREFLY_LEFT:
2516 case EL_BD_FIREFLY_DOWN:
2517 Feld[x][y] = EL_BD_FIREFLY;
2518 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2521 case EL_PACMAN_RIGHT:
2523 case EL_PACMAN_LEFT:
2524 case EL_PACMAN_DOWN:
2525 Feld[x][y] = EL_PACMAN;
2526 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2529 case EL_YAMYAM_LEFT:
2530 case EL_YAMYAM_RIGHT:
2532 case EL_YAMYAM_DOWN:
2533 Feld[x][y] = EL_YAMYAM;
2534 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2537 case EL_SP_SNIKSNAK:
2538 MovDir[x][y] = MV_UP;
2541 case EL_SP_ELECTRON:
2542 MovDir[x][y] = MV_LEFT;
2549 Feld[x][y] = EL_MOLE;
2550 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2554 if (IS_CUSTOM_ELEMENT(element))
2556 struct ElementInfo *ei = &element_info[element];
2557 int move_direction_initial = ei->move_direction_initial;
2558 int move_pattern = ei->move_pattern;
2560 if (move_direction_initial == MV_START_PREVIOUS)
2562 if (MovDir[x][y] != MV_NONE)
2565 move_direction_initial = MV_START_AUTOMATIC;
2568 if (move_direction_initial == MV_START_RANDOM)
2569 MovDir[x][y] = 1 << RND(4);
2570 else if (move_direction_initial & MV_ANY_DIRECTION)
2571 MovDir[x][y] = move_direction_initial;
2572 else if (move_pattern == MV_ALL_DIRECTIONS ||
2573 move_pattern == MV_TURNING_LEFT ||
2574 move_pattern == MV_TURNING_RIGHT ||
2575 move_pattern == MV_TURNING_LEFT_RIGHT ||
2576 move_pattern == MV_TURNING_RIGHT_LEFT ||
2577 move_pattern == MV_TURNING_RANDOM)
2578 MovDir[x][y] = 1 << RND(4);
2579 else if (move_pattern == MV_HORIZONTAL)
2580 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2581 else if (move_pattern == MV_VERTICAL)
2582 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2583 else if (move_pattern & MV_ANY_DIRECTION)
2584 MovDir[x][y] = element_info[element].move_pattern;
2585 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2586 move_pattern == MV_ALONG_RIGHT_SIDE)
2588 /* use random direction as default start direction */
2589 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2590 MovDir[x][y] = 1 << RND(4);
2592 for (i = 0; i < NUM_DIRECTIONS; i++)
2594 int x1 = x + xy[i][0];
2595 int y1 = y + xy[i][1];
2597 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2599 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2600 MovDir[x][y] = direction[0][i];
2602 MovDir[x][y] = direction[1][i];
2611 MovDir[x][y] = 1 << RND(4);
2613 if (element != EL_BUG &&
2614 element != EL_SPACESHIP &&
2615 element != EL_BD_BUTTERFLY &&
2616 element != EL_BD_FIREFLY)
2619 for (i = 0; i < NUM_DIRECTIONS; i++)
2621 int x1 = x + xy[i][0];
2622 int y1 = y + xy[i][1];
2624 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2626 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2628 MovDir[x][y] = direction[0][i];
2631 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2632 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2634 MovDir[x][y] = direction[1][i];
2643 GfxDir[x][y] = MovDir[x][y];
2646 void InitAmoebaNr(int x, int y)
2649 int group_nr = AmoebeNachbarNr(x, y);
2653 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2655 if (AmoebaCnt[i] == 0)
2663 AmoebaNr[x][y] = group_nr;
2664 AmoebaCnt[group_nr]++;
2665 AmoebaCnt2[group_nr]++;
2668 static void PlayerWins(struct PlayerInfo *player)
2670 player->LevelSolved = TRUE;
2671 player->GameOver = TRUE;
2673 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2674 level.native_em_level->lev->score : player->score);
2679 static int time, time_final;
2680 static int score, score_final;
2681 static int game_over_delay = 0;
2682 int game_over_delay_value = 50;
2684 if (!local_player->LevelSolved_GameEnd)
2688 /* do not start end game actions before the player stops moving (to exit) */
2689 if (local_player->MovPos)
2692 local_player->LevelSolved_GameEnd = TRUE;
2693 local_player->LevelSolved_SaveTape = tape.recording;
2694 local_player->LevelSolved_SaveScore = !tape.playing;
2696 if (tape.auto_play) /* tape might already be stopped here */
2697 tape.auto_play_level_solved = TRUE;
2703 game_over_delay = game_over_delay_value;
2705 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
2706 score = score_final = local_player->score_final;
2711 score_final += TimeLeft * level.score[SC_TIME_BONUS];
2713 else if (level.time == 0 && TimePlayed < 999)
2716 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
2719 local_player->score_final = score_final;
2721 if (level_editor_test_game)
2724 score = score_final;
2726 DrawGameValue_Time(time);
2727 DrawGameValue_Score(score);
2730 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
2732 /* close exit door after last player */
2733 if (AllPlayersGone &&
2734 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2735 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2737 int element = Feld[ExitX][ExitY];
2739 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2740 EL_SP_EXIT_CLOSING);
2742 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2745 /* player disappears */
2746 DrawLevelField(ExitX, ExitY);
2749 for (i = 0; i < MAX_PLAYERS; i++)
2751 struct PlayerInfo *player = &stored_player[i];
2753 if (player->present)
2755 RemovePlayer(player);
2757 /* player disappears */
2758 DrawLevelField(player->jx, player->jy);
2762 PlaySound(SND_GAME_WINNING);
2765 if (game_over_delay > 0)
2772 if (time != time_final)
2774 int time_to_go = ABS(time_final - time);
2775 int time_count_dir = (time < time_final ? +1 : -1);
2776 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
2778 time += time_count_steps * time_count_dir;
2779 score += time_count_steps * level.score[SC_TIME_BONUS];
2781 DrawGameValue_Time(time);
2782 DrawGameValue_Score(score);
2784 if (time == time_final)
2785 StopSound(SND_GAME_LEVELTIME_BONUS);
2786 else if (setup.sound_loops)
2787 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
2789 PlaySound(SND_GAME_LEVELTIME_BONUS);
2796 boolean raise_level = FALSE;
2798 CloseDoor(DOOR_CLOSE_1);
2800 if (local_player->LevelSolved_SaveTape)
2807 SaveTapeChecked(tape.level_nr); /* ask to save tape */
2809 SaveTape(tape.level_nr); /* ask to save tape */
2813 if (level_editor_test_game)
2815 game_status = GAME_MODE_MAIN;
2822 if (!local_player->LevelSolved_SaveScore)
2824 FadeOut(REDRAW_FIELD);
2826 game_status = GAME_MODE_MAIN;
2828 DrawAndFadeInMainMenu(REDRAW_FIELD);
2833 if (level_nr == leveldir_current->handicap_level)
2835 leveldir_current->handicap_level++;
2836 SaveLevelSetup_SeriesInfo();
2839 if (level_nr < leveldir_current->last_level)
2840 raise_level = TRUE; /* advance to next level */
2842 if ((hi_pos = NewHiScore()) >= 0)
2844 game_status = GAME_MODE_SCORES;
2846 DrawHallOfFame(hi_pos);
2856 FadeOut(REDRAW_FIELD);
2858 game_status = GAME_MODE_MAIN;
2866 DrawAndFadeInMainMenu(REDRAW_FIELD);
2875 LoadScore(level_nr);
2877 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2878 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
2881 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2883 if (local_player->score_final > highscore[k].Score)
2885 /* player has made it to the hall of fame */
2887 if (k < MAX_SCORE_ENTRIES - 1)
2889 int m = MAX_SCORE_ENTRIES - 1;
2892 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2893 if (strEqual(setup.player_name, highscore[l].Name))
2895 if (m == k) /* player's new highscore overwrites his old one */
2899 for (l = m; l > k; l--)
2901 strcpy(highscore[l].Name, highscore[l - 1].Name);
2902 highscore[l].Score = highscore[l - 1].Score;
2909 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2910 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2911 highscore[k].Score = local_player->score_final;
2917 else if (!strncmp(setup.player_name, highscore[k].Name,
2918 MAX_PLAYER_NAME_LEN))
2919 break; /* player already there with a higher score */
2925 SaveScore(level_nr);
2930 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
2932 int element = Feld[x][y];
2933 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2934 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2935 int horiz_move = (dx != 0);
2936 int sign = (horiz_move ? dx : dy);
2937 int step = sign * element_info[element].move_stepsize;
2939 /* special values for move stepsize for spring and things on conveyor belt */
2942 if (CAN_FALL(element) &&
2943 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2944 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2945 else if (element == EL_SPRING)
2946 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2952 inline static int getElementMoveStepsize(int x, int y)
2954 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
2957 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2959 if (player->GfxAction != action || player->GfxDir != dir)
2962 printf("Player frame reset! (%d => %d, %d => %d)\n",
2963 player->GfxAction, action, player->GfxDir, dir);
2966 player->GfxAction = action;
2967 player->GfxDir = dir;
2969 player->StepFrame = 0;
2973 #if USE_GFX_RESET_GFX_ANIMATION
2974 static void ResetGfxFrame(int x, int y, boolean redraw)
2976 int element = Feld[x][y];
2977 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2978 int last_gfx_frame = GfxFrame[x][y];
2980 if (graphic_info[graphic].anim_global_sync)
2981 GfxFrame[x][y] = FrameCounter;
2982 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2983 GfxFrame[x][y] = CustomValue[x][y];
2984 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2985 GfxFrame[x][y] = element_info[element].collect_score;
2986 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
2987 GfxFrame[x][y] = ChangeDelay[x][y];
2989 if (redraw && GfxFrame[x][y] != last_gfx_frame)
2990 DrawLevelGraphicAnimation(x, y, graphic);
2994 static void ResetGfxAnimation(int x, int y)
2996 GfxAction[x][y] = ACTION_DEFAULT;
2997 GfxDir[x][y] = MovDir[x][y];
3000 #if USE_GFX_RESET_GFX_ANIMATION
3001 ResetGfxFrame(x, y, FALSE);
3005 static void ResetRandomAnimationValue(int x, int y)
3007 GfxRandom[x][y] = INIT_GFX_RANDOM();
3010 void InitMovingField(int x, int y, int direction)
3012 int element = Feld[x][y];
3013 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3014 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3017 boolean is_moving_before, is_moving_after;
3019 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3022 /* check if element was/is moving or being moved before/after mode change */
3024 is_moving_before = WasJustMoving[x][y];
3026 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3028 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3030 /* reset animation only for moving elements which change direction of moving
3031 or which just started or stopped moving
3032 (else CEs with property "can move" / "not moving" are reset each frame) */
3033 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3035 if (is_moving_before != is_moving_after ||
3036 direction != MovDir[x][y])
3037 ResetGfxAnimation(x, y);
3039 if ((is_moving_before || is_moving_after) && !continues_moving)
3040 ResetGfxAnimation(x, y);
3043 if (!continues_moving)
3044 ResetGfxAnimation(x, y);
3047 MovDir[x][y] = direction;
3048 GfxDir[x][y] = direction;
3050 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3051 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3052 direction == MV_DOWN && CAN_FALL(element) ?
3053 ACTION_FALLING : ACTION_MOVING);
3055 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3056 ACTION_FALLING : ACTION_MOVING);
3059 /* this is needed for CEs with property "can move" / "not moving" */
3061 if (is_moving_after)
3063 if (Feld[newx][newy] == EL_EMPTY)
3064 Feld[newx][newy] = EL_BLOCKED;
3066 MovDir[newx][newy] = MovDir[x][y];
3068 #if USE_NEW_CUSTOM_VALUE
3069 CustomValue[newx][newy] = CustomValue[x][y];
3072 GfxFrame[newx][newy] = GfxFrame[x][y];
3073 GfxRandom[newx][newy] = GfxRandom[x][y];
3074 GfxAction[newx][newy] = GfxAction[x][y];
3075 GfxDir[newx][newy] = GfxDir[x][y];
3079 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3081 int direction = MovDir[x][y];
3082 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3083 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3089 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3091 int oldx = x, oldy = y;
3092 int direction = MovDir[x][y];
3094 if (direction == MV_LEFT)
3096 else if (direction == MV_RIGHT)
3098 else if (direction == MV_UP)
3100 else if (direction == MV_DOWN)
3103 *comes_from_x = oldx;
3104 *comes_from_y = oldy;
3107 int MovingOrBlocked2Element(int x, int y)
3109 int element = Feld[x][y];
3111 if (element == EL_BLOCKED)
3115 Blocked2Moving(x, y, &oldx, &oldy);
3116 return Feld[oldx][oldy];
3122 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3124 /* like MovingOrBlocked2Element(), but if element is moving
3125 and (x,y) is the field the moving element is just leaving,
3126 return EL_BLOCKED instead of the element value */
3127 int element = Feld[x][y];
3129 if (IS_MOVING(x, y))
3131 if (element == EL_BLOCKED)
3135 Blocked2Moving(x, y, &oldx, &oldy);
3136 return Feld[oldx][oldy];
3145 static void RemoveField(int x, int y)
3147 Feld[x][y] = EL_EMPTY;
3153 #if USE_NEW_CUSTOM_VALUE
3154 CustomValue[x][y] = 0;
3158 ChangeDelay[x][y] = 0;
3159 ChangePage[x][y] = -1;
3160 Pushed[x][y] = FALSE;
3163 ExplodeField[x][y] = EX_TYPE_NONE;
3166 GfxElement[x][y] = EL_UNDEFINED;
3167 GfxAction[x][y] = ACTION_DEFAULT;
3168 GfxDir[x][y] = MV_NONE;
3171 void RemoveMovingField(int x, int y)
3173 int oldx = x, oldy = y, newx = x, newy = y;
3174 int element = Feld[x][y];
3175 int next_element = EL_UNDEFINED;
3177 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3180 if (IS_MOVING(x, y))
3182 Moving2Blocked(x, y, &newx, &newy);
3184 if (Feld[newx][newy] != EL_BLOCKED)
3186 /* element is moving, but target field is not free (blocked), but
3187 already occupied by something different (example: acid pool);
3188 in this case, only remove the moving field, but not the target */
3190 RemoveField(oldx, oldy);
3192 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3194 DrawLevelField(oldx, oldy);
3199 else if (element == EL_BLOCKED)
3201 Blocked2Moving(x, y, &oldx, &oldy);
3202 if (!IS_MOVING(oldx, oldy))
3206 if (element == EL_BLOCKED &&
3207 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3208 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3209 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3210 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3211 next_element = get_next_element(Feld[oldx][oldy]);
3213 RemoveField(oldx, oldy);
3214 RemoveField(newx, newy);
3216 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3218 if (next_element != EL_UNDEFINED)
3219 Feld[oldx][oldy] = next_element;
3221 DrawLevelField(oldx, oldy);
3222 DrawLevelField(newx, newy);
3225 void DrawDynamite(int x, int y)
3227 int sx = SCREENX(x), sy = SCREENY(y);
3228 int graphic = el2img(Feld[x][y]);
3231 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3234 if (IS_WALKABLE_INSIDE(Back[x][y]))
3238 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3239 else if (Store[x][y])
3240 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3242 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3244 if (Back[x][y] || Store[x][y])
3245 DrawGraphicThruMask(sx, sy, graphic, frame);
3247 DrawGraphic(sx, sy, graphic, frame);
3250 void CheckDynamite(int x, int y)
3252 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3256 if (MovDelay[x][y] != 0)
3259 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3265 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3270 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3272 boolean num_checked_players = 0;
3275 for (i = 0; i < MAX_PLAYERS; i++)
3277 if (stored_player[i].active)
3279 int sx = stored_player[i].jx;
3280 int sy = stored_player[i].jy;
3282 if (num_checked_players == 0)
3289 *sx1 = MIN(*sx1, sx);
3290 *sy1 = MIN(*sy1, sy);
3291 *sx2 = MAX(*sx2, sx);
3292 *sy2 = MAX(*sy2, sy);
3295 num_checked_players++;
3300 static boolean checkIfAllPlayersFitToScreen_RND()
3302 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3304 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3306 return (sx2 - sx1 < SCR_FIELDX &&
3307 sy2 - sy1 < SCR_FIELDY);
3310 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3312 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3314 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3316 *sx = (sx1 + sx2) / 2;
3317 *sy = (sy1 + sy2) / 2;
3320 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3321 boolean quick_relocation)
3323 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3324 boolean no_delay = (tape.warp_forward);
3325 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3326 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3328 if (quick_relocation)
3330 int offset = (setup.scroll_delay ? 3 : 0);
3332 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3334 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3335 x > SBX_Right + MIDPOSX ? SBX_Right :
3338 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3339 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3344 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3345 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3346 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3348 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3349 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3350 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3352 /* don't scroll over playfield boundaries */
3353 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3354 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3356 /* don't scroll over playfield boundaries */
3357 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3358 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3361 RedrawPlayfield(TRUE, 0,0,0,0);
3365 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3366 x > SBX_Right + MIDPOSX ? SBX_Right :
3369 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3370 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3373 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3375 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3378 int fx = FX, fy = FY;
3380 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3381 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3383 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3389 fx += dx * TILEX / 2;
3390 fy += dy * TILEY / 2;
3392 ScrollLevel(dx, dy);
3395 /* scroll in two steps of half tile size to make things smoother */
3396 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3398 Delay(wait_delay_value);
3400 /* scroll second step to align at full tile size */
3402 Delay(wait_delay_value);
3407 Delay(wait_delay_value);
3411 void RelocatePlayer(int jx, int jy, int el_player_raw)
3413 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3414 int player_nr = GET_PLAYER_NR(el_player);
3415 struct PlayerInfo *player = &stored_player[player_nr];
3416 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3417 boolean no_delay = (tape.warp_forward);
3418 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3419 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3420 int old_jx = player->jx;
3421 int old_jy = player->jy;
3422 int old_element = Feld[old_jx][old_jy];
3423 int element = Feld[jx][jy];
3424 boolean player_relocated = (old_jx != jx || old_jy != jy);
3426 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3427 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3428 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3429 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3430 int leave_side_horiz = move_dir_horiz;
3431 int leave_side_vert = move_dir_vert;
3432 int enter_side = enter_side_horiz | enter_side_vert;
3433 int leave_side = leave_side_horiz | leave_side_vert;
3435 if (player->GameOver) /* do not reanimate dead player */
3438 if (!player_relocated) /* no need to relocate the player */
3441 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3443 RemoveField(jx, jy); /* temporarily remove newly placed player */
3444 DrawLevelField(jx, jy);
3447 if (player->present)
3449 while (player->MovPos)
3451 ScrollPlayer(player, SCROLL_GO_ON);
3452 ScrollScreen(NULL, SCROLL_GO_ON);
3454 AdvanceFrameAndPlayerCounters(player->index_nr);
3459 Delay(wait_delay_value);
3462 DrawPlayer(player); /* needed here only to cleanup last field */
3463 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3465 player->is_moving = FALSE;
3468 if (IS_CUSTOM_ELEMENT(old_element))
3469 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3471 player->index_bit, leave_side);
3473 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3475 player->index_bit, leave_side);
3477 Feld[jx][jy] = el_player;
3478 InitPlayerField(jx, jy, el_player, TRUE);
3480 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3482 Feld[jx][jy] = element;
3483 InitField(jx, jy, FALSE);
3486 /* only visually relocate centered player */
3487 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3488 level.instant_relocation);
3490 TestIfPlayerTouchesBadThing(jx, jy);
3491 TestIfPlayerTouchesCustomElement(jx, jy);
3493 if (IS_CUSTOM_ELEMENT(element))
3494 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3495 player->index_bit, enter_side);
3497 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3498 player->index_bit, enter_side);
3501 void Explode(int ex, int ey, int phase, int mode)
3507 /* !!! eliminate this variable !!! */
3508 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3510 if (game.explosions_delayed)
3512 ExplodeField[ex][ey] = mode;
3516 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3518 int center_element = Feld[ex][ey];
3519 int artwork_element, explosion_element; /* set these values later */
3522 /* --- This is only really needed (and now handled) in "Impact()". --- */
3523 /* do not explode moving elements that left the explode field in time */
3524 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3525 center_element == EL_EMPTY &&
3526 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3531 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3532 if (mode == EX_TYPE_NORMAL ||
3533 mode == EX_TYPE_CENTER ||
3534 mode == EX_TYPE_CROSS)
3535 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3538 /* remove things displayed in background while burning dynamite */
3539 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3542 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3544 /* put moving element to center field (and let it explode there) */
3545 center_element = MovingOrBlocked2Element(ex, ey);
3546 RemoveMovingField(ex, ey);
3547 Feld[ex][ey] = center_element;
3550 /* now "center_element" is finally determined -- set related values now */
3551 artwork_element = center_element; /* for custom player artwork */
3552 explosion_element = center_element; /* for custom player artwork */
3554 if (IS_PLAYER(ex, ey))
3556 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3558 artwork_element = stored_player[player_nr].artwork_element;
3560 if (level.use_explosion_element[player_nr])
3562 explosion_element = level.explosion_element[player_nr];
3563 artwork_element = explosion_element;
3568 if (mode == EX_TYPE_NORMAL ||
3569 mode == EX_TYPE_CENTER ||
3570 mode == EX_TYPE_CROSS)
3571 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3574 last_phase = element_info[explosion_element].explosion_delay + 1;
3576 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3578 int xx = x - ex + 1;
3579 int yy = y - ey + 1;
3582 if (!IN_LEV_FIELD(x, y) ||
3583 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3584 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3587 element = Feld[x][y];
3589 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3591 element = MovingOrBlocked2Element(x, y);
3593 if (!IS_EXPLOSION_PROOF(element))
3594 RemoveMovingField(x, y);
3597 /* indestructible elements can only explode in center (but not flames) */
3598 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3599 mode == EX_TYPE_BORDER)) ||
3600 element == EL_FLAMES)
3603 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3604 behaviour, for example when touching a yamyam that explodes to rocks
3605 with active deadly shield, a rock is created under the player !!! */
3606 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3608 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3609 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3610 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3612 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3615 if (IS_ACTIVE_BOMB(element))
3617 /* re-activate things under the bomb like gate or penguin */
3618 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3625 /* save walkable background elements while explosion on same tile */
3626 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3627 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3628 Back[x][y] = element;
3630 /* ignite explodable elements reached by other explosion */
3631 if (element == EL_EXPLOSION)
3632 element = Store2[x][y];
3634 if (AmoebaNr[x][y] &&
3635 (element == EL_AMOEBA_FULL ||
3636 element == EL_BD_AMOEBA ||
3637 element == EL_AMOEBA_GROWING))
3639 AmoebaCnt[AmoebaNr[x][y]]--;
3640 AmoebaCnt2[AmoebaNr[x][y]]--;
3645 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3647 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3649 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3651 if (PLAYERINFO(ex, ey)->use_murphy)
3652 Store[x][y] = EL_EMPTY;
3655 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3656 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3657 else if (ELEM_IS_PLAYER(center_element))
3658 Store[x][y] = EL_EMPTY;
3659 else if (center_element == EL_YAMYAM)
3660 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3661 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3662 Store[x][y] = element_info[center_element].content.e[xx][yy];
3664 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3665 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3666 otherwise) -- FIX THIS !!! */
3667 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3668 Store[x][y] = element_info[element].content.e[1][1];
3670 else if (!CAN_EXPLODE(element))
3671 Store[x][y] = element_info[element].content.e[1][1];
3674 Store[x][y] = EL_EMPTY;
3676 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3677 center_element == EL_AMOEBA_TO_DIAMOND)
3678 Store2[x][y] = element;
3680 Feld[x][y] = EL_EXPLOSION;
3681 GfxElement[x][y] = artwork_element;
3683 ExplodePhase[x][y] = 1;
3684 ExplodeDelay[x][y] = last_phase;
3689 if (center_element == EL_YAMYAM)
3690 game.yamyam_content_nr =
3691 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3703 GfxFrame[x][y] = 0; /* restart explosion animation */
3705 last_phase = ExplodeDelay[x][y];
3707 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3711 /* activate this even in non-DEBUG version until cause for crash in
3712 getGraphicAnimationFrame() (see below) is found and eliminated */
3718 /* this can happen if the player leaves an explosion just in time */
3719 if (GfxElement[x][y] == EL_UNDEFINED)
3720 GfxElement[x][y] = EL_EMPTY;
3722 if (GfxElement[x][y] == EL_UNDEFINED)
3725 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3726 printf("Explode(): This should never happen!\n");
3729 GfxElement[x][y] = EL_EMPTY;
3735 border_element = Store2[x][y];
3736 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3737 border_element = StorePlayer[x][y];
3739 if (phase == element_info[border_element].ignition_delay ||
3740 phase == last_phase)
3742 boolean border_explosion = FALSE;
3744 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3745 !PLAYER_EXPLOSION_PROTECTED(x, y))
3747 KillPlayerUnlessExplosionProtected(x, y);
3748 border_explosion = TRUE;
3750 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3752 Feld[x][y] = Store2[x][y];
3755 border_explosion = TRUE;
3757 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3759 AmoebeUmwandeln(x, y);
3761 border_explosion = TRUE;
3764 /* if an element just explodes due to another explosion (chain-reaction),
3765 do not immediately end the new explosion when it was the last frame of
3766 the explosion (as it would be done in the following "if"-statement!) */
3767 if (border_explosion && phase == last_phase)
3771 if (phase == last_phase)
3775 element = Feld[x][y] = Store[x][y];
3776 Store[x][y] = Store2[x][y] = 0;
3777 GfxElement[x][y] = EL_UNDEFINED;
3779 /* player can escape from explosions and might therefore be still alive */
3780 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3781 element <= EL_PLAYER_IS_EXPLODING_4)
3783 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3784 int explosion_element = EL_PLAYER_1 + player_nr;
3785 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3786 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3788 if (level.use_explosion_element[player_nr])
3789 explosion_element = level.explosion_element[player_nr];
3791 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3792 element_info[explosion_element].content.e[xx][yy]);
3795 /* restore probably existing indestructible background element */
3796 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3797 element = Feld[x][y] = Back[x][y];
3800 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3801 GfxDir[x][y] = MV_NONE;
3802 ChangeDelay[x][y] = 0;
3803 ChangePage[x][y] = -1;
3805 #if USE_NEW_CUSTOM_VALUE
3806 CustomValue[x][y] = 0;
3809 InitField_WithBug2(x, y, FALSE);
3811 DrawLevelField(x, y);
3813 TestIfElementTouchesCustomElement(x, y);
3815 if (GFX_CRUMBLED(element))
3816 DrawLevelFieldCrumbledSandNeighbours(x, y);
3818 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3819 StorePlayer[x][y] = 0;
3821 if (ELEM_IS_PLAYER(element))
3822 RelocatePlayer(x, y, element);
3824 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3826 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3827 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3830 DrawLevelFieldCrumbledSand(x, y);
3832 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3834 DrawLevelElement(x, y, Back[x][y]);
3835 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3837 else if (IS_WALKABLE_UNDER(Back[x][y]))
3839 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3840 DrawLevelElementThruMask(x, y, Back[x][y]);
3842 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3843 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3847 void DynaExplode(int ex, int ey)
3850 int dynabomb_element = Feld[ex][ey];
3851 int dynabomb_size = 1;
3852 boolean dynabomb_xl = FALSE;
3853 struct PlayerInfo *player;
3854 static int xy[4][2] =
3862 if (IS_ACTIVE_BOMB(dynabomb_element))
3864 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3865 dynabomb_size = player->dynabomb_size;
3866 dynabomb_xl = player->dynabomb_xl;
3867 player->dynabombs_left++;
3870 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3872 for (i = 0; i < NUM_DIRECTIONS; i++)
3874 for (j = 1; j <= dynabomb_size; j++)
3876 int x = ex + j * xy[i][0];
3877 int y = ey + j * xy[i][1];
3880 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3883 element = Feld[x][y];
3885 /* do not restart explosions of fields with active bombs */
3886 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3889 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3891 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3892 !IS_DIGGABLE(element) && !dynabomb_xl)
3898 void Bang(int x, int y)
3900 int element = MovingOrBlocked2Element(x, y);
3901 int explosion_type = EX_TYPE_NORMAL;
3903 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3905 struct PlayerInfo *player = PLAYERINFO(x, y);
3907 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3908 player->element_nr);
3910 if (level.use_explosion_element[player->index_nr])
3912 int explosion_element = level.explosion_element[player->index_nr];
3914 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3915 explosion_type = EX_TYPE_CROSS;
3916 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3917 explosion_type = EX_TYPE_CENTER;
3925 case EL_BD_BUTTERFLY:
3928 case EL_DARK_YAMYAM:
3932 RaiseScoreElement(element);
3935 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3936 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3937 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3938 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3939 case EL_DYNABOMB_INCREASE_NUMBER:
3940 case EL_DYNABOMB_INCREASE_SIZE:
3941 case EL_DYNABOMB_INCREASE_POWER:
3942 explosion_type = EX_TYPE_DYNA;
3947 case EL_LAMP_ACTIVE:
3948 case EL_AMOEBA_TO_DIAMOND:
3949 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3950 explosion_type = EX_TYPE_CENTER;
3954 if (element_info[element].explosion_type == EXPLODES_CROSS)
3955 explosion_type = EX_TYPE_CROSS;
3956 else if (element_info[element].explosion_type == EXPLODES_1X1)
3957 explosion_type = EX_TYPE_CENTER;
3961 if (explosion_type == EX_TYPE_DYNA)
3964 Explode(x, y, EX_PHASE_START, explosion_type);
3966 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3969 void SplashAcid(int x, int y)
3971 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3972 (!IN_LEV_FIELD(x - 1, y - 2) ||
3973 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3974 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3976 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3977 (!IN_LEV_FIELD(x + 1, y - 2) ||
3978 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3979 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3981 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3984 static void InitBeltMovement()
3986 static int belt_base_element[4] =
3988 EL_CONVEYOR_BELT_1_LEFT,
3989 EL_CONVEYOR_BELT_2_LEFT,
3990 EL_CONVEYOR_BELT_3_LEFT,
3991 EL_CONVEYOR_BELT_4_LEFT
3993 static int belt_base_active_element[4] =
3995 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3996 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3997 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3998 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4003 /* set frame order for belt animation graphic according to belt direction */
4004 for (i = 0; i < NUM_BELTS; i++)
4008 for (j = 0; j < NUM_BELT_PARTS; j++)
4010 int element = belt_base_active_element[belt_nr] + j;
4011 int graphic = el2img(element);
4013 if (game.belt_dir[i] == MV_LEFT)
4014 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4016 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4020 SCAN_PLAYFIELD(x, y)
4022 int element = Feld[x][y];
4024 for (i = 0; i < NUM_BELTS; i++)
4026 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4028 int e_belt_nr = getBeltNrFromBeltElement(element);
4031 if (e_belt_nr == belt_nr)
4033 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4035 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4042 static void ToggleBeltSwitch(int x, int y)
4044 static int belt_base_element[4] =
4046 EL_CONVEYOR_BELT_1_LEFT,
4047 EL_CONVEYOR_BELT_2_LEFT,
4048 EL_CONVEYOR_BELT_3_LEFT,
4049 EL_CONVEYOR_BELT_4_LEFT
4051 static int belt_base_active_element[4] =
4053 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4054 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4055 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4056 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4058 static int belt_base_switch_element[4] =
4060 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4061 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4062 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4063 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4065 static int belt_move_dir[4] =
4073 int element = Feld[x][y];
4074 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4075 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4076 int belt_dir = belt_move_dir[belt_dir_nr];
4079 if (!IS_BELT_SWITCH(element))
4082 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4083 game.belt_dir[belt_nr] = belt_dir;
4085 if (belt_dir_nr == 3)
4088 /* set frame order for belt animation graphic according to belt direction */
4089 for (i = 0; i < NUM_BELT_PARTS; i++)
4091 int element = belt_base_active_element[belt_nr] + i;
4092 int graphic = el2img(element);
4094 if (belt_dir == MV_LEFT)
4095 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4097 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4100 SCAN_PLAYFIELD(xx, yy)
4102 int element = Feld[xx][yy];
4104 if (IS_BELT_SWITCH(element))
4106 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4108 if (e_belt_nr == belt_nr)
4110 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4111 DrawLevelField(xx, yy);
4114 else if (IS_BELT(element) && belt_dir != MV_NONE)
4116 int e_belt_nr = getBeltNrFromBeltElement(element);
4118 if (e_belt_nr == belt_nr)
4120 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4122 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4123 DrawLevelField(xx, yy);
4126 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4128 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4130 if (e_belt_nr == belt_nr)
4132 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4134 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4135 DrawLevelField(xx, yy);
4141 static void ToggleSwitchgateSwitch(int x, int y)
4145 game.switchgate_pos = !game.switchgate_pos;
4147 SCAN_PLAYFIELD(xx, yy)
4149 int element = Feld[xx][yy];
4151 #if !USE_BOTH_SWITCHGATE_SWITCHES
4152 if (element == EL_SWITCHGATE_SWITCH_UP ||
4153 element == EL_SWITCHGATE_SWITCH_DOWN)
4155 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4156 DrawLevelField(xx, yy);
4159 if (element == EL_SWITCHGATE_SWITCH_UP)
4161 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4162 DrawLevelField(xx, yy);
4164 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4166 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4167 DrawLevelField(xx, yy);
4170 else if (element == EL_SWITCHGATE_OPEN ||
4171 element == EL_SWITCHGATE_OPENING)
4173 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4175 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4177 else if (element == EL_SWITCHGATE_CLOSED ||
4178 element == EL_SWITCHGATE_CLOSING)
4180 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4182 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4187 static int getInvisibleActiveFromInvisibleElement(int element)
4189 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4190 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4191 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4195 static int getInvisibleFromInvisibleActiveElement(int element)
4197 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4198 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4199 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4203 static void RedrawAllLightSwitchesAndInvisibleElements()
4207 SCAN_PLAYFIELD(x, y)
4209 int element = Feld[x][y];
4211 if (element == EL_LIGHT_SWITCH &&
4212 game.light_time_left > 0)
4214 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4215 DrawLevelField(x, y);
4217 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4218 game.light_time_left == 0)
4220 Feld[x][y] = EL_LIGHT_SWITCH;
4221 DrawLevelField(x, y);
4223 else if (element == EL_EMC_DRIPPER &&
4224 game.light_time_left > 0)
4226 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4227 DrawLevelField(x, y);
4229 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4230 game.light_time_left == 0)
4232 Feld[x][y] = EL_EMC_DRIPPER;
4233 DrawLevelField(x, y);
4235 else if (element == EL_INVISIBLE_STEELWALL ||
4236 element == EL_INVISIBLE_WALL ||
4237 element == EL_INVISIBLE_SAND)
4239 if (game.light_time_left > 0)
4240 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4242 DrawLevelField(x, y);
4244 /* uncrumble neighbour fields, if needed */
4245 if (element == EL_INVISIBLE_SAND)
4246 DrawLevelFieldCrumbledSandNeighbours(x, y);
4248 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4249 element == EL_INVISIBLE_WALL_ACTIVE ||
4250 element == EL_INVISIBLE_SAND_ACTIVE)
4252 if (game.light_time_left == 0)
4253 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4255 DrawLevelField(x, y);
4257 /* re-crumble neighbour fields, if needed */
4258 if (element == EL_INVISIBLE_SAND)
4259 DrawLevelFieldCrumbledSandNeighbours(x, y);
4264 static void RedrawAllInvisibleElementsForLenses()
4268 SCAN_PLAYFIELD(x, y)
4270 int element = Feld[x][y];
4272 if (element == EL_EMC_DRIPPER &&
4273 game.lenses_time_left > 0)
4275 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4276 DrawLevelField(x, y);
4278 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4279 game.lenses_time_left == 0)
4281 Feld[x][y] = EL_EMC_DRIPPER;
4282 DrawLevelField(x, y);
4284 else if (element == EL_INVISIBLE_STEELWALL ||
4285 element == EL_INVISIBLE_WALL ||
4286 element == EL_INVISIBLE_SAND)
4288 if (game.lenses_time_left > 0)
4289 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4291 DrawLevelField(x, y);
4293 /* uncrumble neighbour fields, if needed */
4294 if (element == EL_INVISIBLE_SAND)
4295 DrawLevelFieldCrumbledSandNeighbours(x, y);
4297 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4298 element == EL_INVISIBLE_WALL_ACTIVE ||
4299 element == EL_INVISIBLE_SAND_ACTIVE)
4301 if (game.lenses_time_left == 0)
4302 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4304 DrawLevelField(x, y);
4306 /* re-crumble neighbour fields, if needed */
4307 if (element == EL_INVISIBLE_SAND)
4308 DrawLevelFieldCrumbledSandNeighbours(x, y);
4313 static void RedrawAllInvisibleElementsForMagnifier()
4317 SCAN_PLAYFIELD(x, y)
4319 int element = Feld[x][y];
4321 if (element == EL_EMC_FAKE_GRASS &&
4322 game.magnify_time_left > 0)
4324 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4325 DrawLevelField(x, y);
4327 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4328 game.magnify_time_left == 0)
4330 Feld[x][y] = EL_EMC_FAKE_GRASS;
4331 DrawLevelField(x, y);
4333 else if (IS_GATE_GRAY(element) &&
4334 game.magnify_time_left > 0)
4336 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4337 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4338 IS_EM_GATE_GRAY(element) ?
4339 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4340 IS_EMC_GATE_GRAY(element) ?
4341 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4343 DrawLevelField(x, y);
4345 else if (IS_GATE_GRAY_ACTIVE(element) &&
4346 game.magnify_time_left == 0)
4348 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4349 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4350 IS_EM_GATE_GRAY_ACTIVE(element) ?
4351 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4352 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4353 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4355 DrawLevelField(x, y);
4360 static void ToggleLightSwitch(int x, int y)
4362 int element = Feld[x][y];
4364 game.light_time_left =
4365 (element == EL_LIGHT_SWITCH ?
4366 level.time_light * FRAMES_PER_SECOND : 0);
4368 RedrawAllLightSwitchesAndInvisibleElements();
4371 static void ActivateTimegateSwitch(int x, int y)
4375 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4377 SCAN_PLAYFIELD(xx, yy)
4379 int element = Feld[xx][yy];
4381 if (element == EL_TIMEGATE_CLOSED ||
4382 element == EL_TIMEGATE_CLOSING)
4384 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4385 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4389 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4391 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4392 DrawLevelField(xx, yy);
4398 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4401 void Impact(int x, int y)
4403 boolean last_line = (y == lev_fieldy - 1);
4404 boolean object_hit = FALSE;
4405 boolean impact = (last_line || object_hit);
4406 int element = Feld[x][y];
4407 int smashed = EL_STEELWALL;
4409 if (!last_line) /* check if element below was hit */
4411 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4414 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4415 MovDir[x][y + 1] != MV_DOWN ||
4416 MovPos[x][y + 1] <= TILEY / 2));
4418 /* do not smash moving elements that left the smashed field in time */
4419 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4420 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4423 #if USE_QUICKSAND_IMPACT_BUGFIX
4424 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4426 RemoveMovingField(x, y + 1);
4427 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4428 Feld[x][y + 2] = EL_ROCK;
4429 DrawLevelField(x, y + 2);
4436 smashed = MovingOrBlocked2Element(x, y + 1);
4438 impact = (last_line || object_hit);
4441 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4443 SplashAcid(x, y + 1);
4447 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4448 /* only reset graphic animation if graphic really changes after impact */
4450 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4452 ResetGfxAnimation(x, y);
4453 DrawLevelField(x, y);
4456 if (impact && CAN_EXPLODE_IMPACT(element))
4461 else if (impact && element == EL_PEARL)
4463 ResetGfxAnimation(x, y);
4465 Feld[x][y] = EL_PEARL_BREAKING;
4466 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4469 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4471 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4476 if (impact && element == EL_AMOEBA_DROP)
4478 if (object_hit && IS_PLAYER(x, y + 1))
4479 KillPlayerUnlessEnemyProtected(x, y + 1);
4480 else if (object_hit && smashed == EL_PENGUIN)
4484 Feld[x][y] = EL_AMOEBA_GROWING;
4485 Store[x][y] = EL_AMOEBA_WET;
4487 ResetRandomAnimationValue(x, y);
4492 if (object_hit) /* check which object was hit */
4494 if (CAN_PASS_MAGIC_WALL(element) &&
4495 (smashed == EL_MAGIC_WALL ||
4496 smashed == EL_BD_MAGIC_WALL))
4499 int activated_magic_wall =
4500 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4501 EL_BD_MAGIC_WALL_ACTIVE);
4503 /* activate magic wall / mill */
4504 SCAN_PLAYFIELD(xx, yy)
4505 if (Feld[xx][yy] == smashed)
4506 Feld[xx][yy] = activated_magic_wall;
4508 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4509 game.magic_wall_active = TRUE;
4511 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4512 SND_MAGIC_WALL_ACTIVATING :
4513 SND_BD_MAGIC_WALL_ACTIVATING));
4516 if (IS_PLAYER(x, y + 1))
4518 if (CAN_SMASH_PLAYER(element))
4520 KillPlayerUnlessEnemyProtected(x, y + 1);
4524 else if (smashed == EL_PENGUIN)
4526 if (CAN_SMASH_PLAYER(element))
4532 else if (element == EL_BD_DIAMOND)
4534 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4540 else if (((element == EL_SP_INFOTRON ||
4541 element == EL_SP_ZONK) &&
4542 (smashed == EL_SP_SNIKSNAK ||
4543 smashed == EL_SP_ELECTRON ||
4544 smashed == EL_SP_DISK_ORANGE)) ||
4545 (element == EL_SP_INFOTRON &&
4546 smashed == EL_SP_DISK_YELLOW))
4551 else if (CAN_SMASH_EVERYTHING(element))
4553 if (IS_CLASSIC_ENEMY(smashed) ||
4554 CAN_EXPLODE_SMASHED(smashed))
4559 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4561 if (smashed == EL_LAMP ||
4562 smashed == EL_LAMP_ACTIVE)
4567 else if (smashed == EL_NUT)
4569 Feld[x][y + 1] = EL_NUT_BREAKING;
4570 PlayLevelSound(x, y, SND_NUT_BREAKING);
4571 RaiseScoreElement(EL_NUT);
4574 else if (smashed == EL_PEARL)
4576 ResetGfxAnimation(x, y);
4578 Feld[x][y + 1] = EL_PEARL_BREAKING;
4579 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4582 else if (smashed == EL_DIAMOND)
4584 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4585 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4588 else if (IS_BELT_SWITCH(smashed))
4590 ToggleBeltSwitch(x, y + 1);
4592 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4593 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4595 ToggleSwitchgateSwitch(x, y + 1);
4597 else if (smashed == EL_LIGHT_SWITCH ||
4598 smashed == EL_LIGHT_SWITCH_ACTIVE)
4600 ToggleLightSwitch(x, y + 1);
4605 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4608 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4610 CheckElementChangeBySide(x, y + 1, smashed, element,
4611 CE_SWITCHED, CH_SIDE_TOP);
4612 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4618 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4623 /* play sound of magic wall / mill */
4625 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4626 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4628 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4629 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4630 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4631 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4636 /* play sound of object that hits the ground */
4637 if (last_line || object_hit)
4638 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4641 inline static void TurnRoundExt(int x, int y)
4653 { 0, 0 }, { 0, 0 }, { 0, 0 },
4658 int left, right, back;
4662 { MV_DOWN, MV_UP, MV_RIGHT },
4663 { MV_UP, MV_DOWN, MV_LEFT },
4665 { MV_LEFT, MV_RIGHT, MV_DOWN },
4669 { MV_RIGHT, MV_LEFT, MV_UP }
4672 int element = Feld[x][y];
4673 int move_pattern = element_info[element].move_pattern;
4675 int old_move_dir = MovDir[x][y];
4676 int left_dir = turn[old_move_dir].left;
4677 int right_dir = turn[old_move_dir].right;
4678 int back_dir = turn[old_move_dir].back;
4680 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4681 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4682 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4683 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4685 int left_x = x + left_dx, left_y = y + left_dy;
4686 int right_x = x + right_dx, right_y = y + right_dy;
4687 int move_x = x + move_dx, move_y = y + move_dy;
4691 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4693 TestIfBadThingTouchesOtherBadThing(x, y);
4695 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4696 MovDir[x][y] = right_dir;
4697 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4698 MovDir[x][y] = left_dir;
4700 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4702 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4705 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4707 TestIfBadThingTouchesOtherBadThing(x, y);
4709 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4710 MovDir[x][y] = left_dir;
4711 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4712 MovDir[x][y] = right_dir;
4714 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4716 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4719 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4721 TestIfBadThingTouchesOtherBadThing(x, y);
4723 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4724 MovDir[x][y] = left_dir;
4725 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4726 MovDir[x][y] = right_dir;
4728 if (MovDir[x][y] != old_move_dir)
4731 else if (element == EL_YAMYAM)
4733 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4734 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4736 if (can_turn_left && can_turn_right)
4737 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4738 else if (can_turn_left)
4739 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4740 else if (can_turn_right)
4741 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4743 MovDir[x][y] = back_dir;
4745 MovDelay[x][y] = 16 + 16 * RND(3);
4747 else if (element == EL_DARK_YAMYAM)
4749 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4751 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4754 if (can_turn_left && can_turn_right)
4755 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4756 else if (can_turn_left)
4757 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4758 else if (can_turn_right)
4759 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4761 MovDir[x][y] = back_dir;
4763 MovDelay[x][y] = 16 + 16 * RND(3);
4765 else if (element == EL_PACMAN)
4767 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4768 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4770 if (can_turn_left && can_turn_right)
4771 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4772 else if (can_turn_left)
4773 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4774 else if (can_turn_right)
4775 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4777 MovDir[x][y] = back_dir;
4779 MovDelay[x][y] = 6 + RND(40);
4781 else if (element == EL_PIG)
4783 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4784 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4785 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4786 boolean should_turn_left, should_turn_right, should_move_on;
4788 int rnd = RND(rnd_value);
4790 should_turn_left = (can_turn_left &&
4792 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4793 y + back_dy + left_dy)));
4794 should_turn_right = (can_turn_right &&
4796 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4797 y + back_dy + right_dy)));
4798 should_move_on = (can_move_on &&
4801 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4802 y + move_dy + left_dy) ||
4803 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4804 y + move_dy + right_dy)));
4806 if (should_turn_left || should_turn_right || should_move_on)
4808 if (should_turn_left && should_turn_right && should_move_on)
4809 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4810 rnd < 2 * rnd_value / 3 ? right_dir :
4812 else if (should_turn_left && should_turn_right)
4813 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4814 else if (should_turn_left && should_move_on)
4815 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4816 else if (should_turn_right && should_move_on)
4817 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4818 else if (should_turn_left)
4819 MovDir[x][y] = left_dir;
4820 else if (should_turn_right)
4821 MovDir[x][y] = right_dir;
4822 else if (should_move_on)
4823 MovDir[x][y] = old_move_dir;
4825 else if (can_move_on && rnd > rnd_value / 8)
4826 MovDir[x][y] = old_move_dir;
4827 else if (can_turn_left && can_turn_right)
4828 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4829 else if (can_turn_left && rnd > rnd_value / 8)
4830 MovDir[x][y] = left_dir;
4831 else if (can_turn_right && rnd > rnd_value/8)
4832 MovDir[x][y] = right_dir;
4834 MovDir[x][y] = back_dir;
4836 xx = x + move_xy[MovDir[x][y]].dx;
4837 yy = y + move_xy[MovDir[x][y]].dy;
4839 if (!IN_LEV_FIELD(xx, yy) ||
4840 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4841 MovDir[x][y] = old_move_dir;
4845 else if (element == EL_DRAGON)
4847 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4848 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4849 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4851 int rnd = RND(rnd_value);
4853 if (can_move_on && rnd > rnd_value / 8)
4854 MovDir[x][y] = old_move_dir;
4855 else if (can_turn_left && can_turn_right)
4856 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4857 else if (can_turn_left && rnd > rnd_value / 8)
4858 MovDir[x][y] = left_dir;
4859 else if (can_turn_right && rnd > rnd_value / 8)
4860 MovDir[x][y] = right_dir;
4862 MovDir[x][y] = back_dir;
4864 xx = x + move_xy[MovDir[x][y]].dx;
4865 yy = y + move_xy[MovDir[x][y]].dy;
4867 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4868 MovDir[x][y] = old_move_dir;
4872 else if (element == EL_MOLE)
4874 boolean can_move_on =
4875 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4876 IS_AMOEBOID(Feld[move_x][move_y]) ||
4877 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4880 boolean can_turn_left =
4881 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4882 IS_AMOEBOID(Feld[left_x][left_y])));
4884 boolean can_turn_right =
4885 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4886 IS_AMOEBOID(Feld[right_x][right_y])));
4888 if (can_turn_left && can_turn_right)
4889 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4890 else if (can_turn_left)
4891 MovDir[x][y] = left_dir;
4893 MovDir[x][y] = right_dir;
4896 if (MovDir[x][y] != old_move_dir)
4899 else if (element == EL_BALLOON)
4901 MovDir[x][y] = game.wind_direction;
4904 else if (element == EL_SPRING)
4906 #if USE_NEW_SPRING_BUMPER
4907 if (MovDir[x][y] & MV_HORIZONTAL)
4909 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4910 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4912 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4913 ResetGfxAnimation(move_x, move_y);
4914 DrawLevelField(move_x, move_y);
4916 MovDir[x][y] = back_dir;
4918 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4919 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4920 MovDir[x][y] = MV_NONE;
4923 if (MovDir[x][y] & MV_HORIZONTAL &&
4924 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4925 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4926 MovDir[x][y] = MV_NONE;
4931 else if (element == EL_ROBOT ||
4932 element == EL_SATELLITE ||
4933 element == EL_PENGUIN ||
4934 element == EL_EMC_ANDROID)
4936 int attr_x = -1, attr_y = -1;
4947 for (i = 0; i < MAX_PLAYERS; i++)
4949 struct PlayerInfo *player = &stored_player[i];
4950 int jx = player->jx, jy = player->jy;
4952 if (!player->active)
4956 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4964 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4965 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4966 game.engine_version < VERSION_IDENT(3,1,0,0)))
4972 if (element == EL_PENGUIN)
4975 static int xy[4][2] =
4983 for (i = 0; i < NUM_DIRECTIONS; i++)
4985 int ex = x + xy[i][0];
4986 int ey = y + xy[i][1];
4988 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4997 MovDir[x][y] = MV_NONE;
4999 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5000 else if (attr_x > x)
5001 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5003 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5004 else if (attr_y > y)
5005 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5007 if (element == EL_ROBOT)
5011 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5012 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5013 Moving2Blocked(x, y, &newx, &newy);
5015 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5016 MovDelay[x][y] = 8 + 8 * !RND(3);
5018 MovDelay[x][y] = 16;
5020 else if (element == EL_PENGUIN)
5026 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5028 boolean first_horiz = RND(2);
5029 int new_move_dir = MovDir[x][y];
5032 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5033 Moving2Blocked(x, y, &newx, &newy);
5035 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5039 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5040 Moving2Blocked(x, y, &newx, &newy);
5042 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5045 MovDir[x][y] = old_move_dir;
5049 else if (element == EL_SATELLITE)
5055 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5057 boolean first_horiz = RND(2);
5058 int new_move_dir = MovDir[x][y];
5061 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5062 Moving2Blocked(x, y, &newx, &newy);
5064 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5068 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5069 Moving2Blocked(x, y, &newx, &newy);
5071 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5074 MovDir[x][y] = old_move_dir;
5078 else if (element == EL_EMC_ANDROID)
5080 static int check_pos[16] =
5082 -1, /* 0 => (invalid) */
5083 7, /* 1 => MV_LEFT */
5084 3, /* 2 => MV_RIGHT */
5085 -1, /* 3 => (invalid) */
5087 0, /* 5 => MV_LEFT | MV_UP */
5088 2, /* 6 => MV_RIGHT | MV_UP */
5089 -1, /* 7 => (invalid) */
5090 5, /* 8 => MV_DOWN */
5091 6, /* 9 => MV_LEFT | MV_DOWN */
5092 4, /* 10 => MV_RIGHT | MV_DOWN */
5093 -1, /* 11 => (invalid) */
5094 -1, /* 12 => (invalid) */
5095 -1, /* 13 => (invalid) */
5096 -1, /* 14 => (invalid) */
5097 -1, /* 15 => (invalid) */
5105 { -1, -1, MV_LEFT | MV_UP },
5107 { +1, -1, MV_RIGHT | MV_UP },
5108 { +1, 0, MV_RIGHT },
5109 { +1, +1, MV_RIGHT | MV_DOWN },
5111 { -1, +1, MV_LEFT | MV_DOWN },
5114 int start_pos, check_order;
5115 boolean can_clone = FALSE;
5118 /* check if there is any free field around current position */
5119 for (i = 0; i < 8; i++)
5121 int newx = x + check_xy[i].dx;
5122 int newy = y + check_xy[i].dy;
5124 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5132 if (can_clone) /* randomly find an element to clone */
5136 start_pos = check_pos[RND(8)];
5137 check_order = (RND(2) ? -1 : +1);
5139 for (i = 0; i < 8; i++)
5141 int pos_raw = start_pos + i * check_order;
5142 int pos = (pos_raw + 8) % 8;
5143 int newx = x + check_xy[pos].dx;
5144 int newy = y + check_xy[pos].dy;
5146 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5148 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5149 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5151 Store[x][y] = Feld[newx][newy];
5160 if (can_clone) /* randomly find a direction to move */
5164 start_pos = check_pos[RND(8)];
5165 check_order = (RND(2) ? -1 : +1);
5167 for (i = 0; i < 8; i++)
5169 int pos_raw = start_pos + i * check_order;
5170 int pos = (pos_raw + 8) % 8;
5171 int newx = x + check_xy[pos].dx;
5172 int newy = y + check_xy[pos].dy;
5173 int new_move_dir = check_xy[pos].dir;
5175 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5177 MovDir[x][y] = new_move_dir;
5178 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5187 if (can_clone) /* cloning and moving successful */
5190 /* cannot clone -- try to move towards player */
5192 start_pos = check_pos[MovDir[x][y] & 0x0f];
5193 check_order = (RND(2) ? -1 : +1);
5195 for (i = 0; i < 3; i++)
5197 /* first check start_pos, then previous/next or (next/previous) pos */
5198 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5199 int pos = (pos_raw + 8) % 8;
5200 int newx = x + check_xy[pos].dx;
5201 int newy = y + check_xy[pos].dy;
5202 int new_move_dir = check_xy[pos].dir;
5204 if (IS_PLAYER(newx, newy))
5207 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5209 MovDir[x][y] = new_move_dir;
5210 MovDelay[x][y] = level.android_move_time * 8 + 1;
5217 else if (move_pattern == MV_TURNING_LEFT ||
5218 move_pattern == MV_TURNING_RIGHT ||
5219 move_pattern == MV_TURNING_LEFT_RIGHT ||
5220 move_pattern == MV_TURNING_RIGHT_LEFT ||
5221 move_pattern == MV_TURNING_RANDOM ||
5222 move_pattern == MV_ALL_DIRECTIONS)
5224 boolean can_turn_left =
5225 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5226 boolean can_turn_right =
5227 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5229 if (element_info[element].move_stepsize == 0) /* "not moving" */
5232 if (move_pattern == MV_TURNING_LEFT)
5233 MovDir[x][y] = left_dir;
5234 else if (move_pattern == MV_TURNING_RIGHT)
5235 MovDir[x][y] = right_dir;
5236 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5237 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5238 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5239 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5240 else if (move_pattern == MV_TURNING_RANDOM)
5241 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5242 can_turn_right && !can_turn_left ? right_dir :
5243 RND(2) ? left_dir : right_dir);
5244 else if (can_turn_left && can_turn_right)
5245 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5246 else if (can_turn_left)
5247 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5248 else if (can_turn_right)
5249 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5251 MovDir[x][y] = back_dir;
5253 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5255 else if (move_pattern == MV_HORIZONTAL ||
5256 move_pattern == MV_VERTICAL)
5258 if (move_pattern & old_move_dir)
5259 MovDir[x][y] = back_dir;
5260 else if (move_pattern == MV_HORIZONTAL)
5261 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5262 else if (move_pattern == MV_VERTICAL)
5263 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5265 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5267 else if (move_pattern & MV_ANY_DIRECTION)
5269 MovDir[x][y] = move_pattern;
5270 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5272 else if (move_pattern & MV_WIND_DIRECTION)
5274 MovDir[x][y] = game.wind_direction;
5275 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5277 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5279 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5280 MovDir[x][y] = left_dir;
5281 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5282 MovDir[x][y] = right_dir;
5284 if (MovDir[x][y] != old_move_dir)
5285 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5287 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5289 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5290 MovDir[x][y] = right_dir;
5291 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5292 MovDir[x][y] = left_dir;
5294 if (MovDir[x][y] != old_move_dir)
5295 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5297 else if (move_pattern == MV_TOWARDS_PLAYER ||
5298 move_pattern == MV_AWAY_FROM_PLAYER)
5300 int attr_x = -1, attr_y = -1;
5302 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5313 for (i = 0; i < MAX_PLAYERS; i++)
5315 struct PlayerInfo *player = &stored_player[i];
5316 int jx = player->jx, jy = player->jy;
5318 if (!player->active)
5322 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5330 MovDir[x][y] = MV_NONE;
5332 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5333 else if (attr_x > x)
5334 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5336 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5337 else if (attr_y > y)
5338 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5340 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5342 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5344 boolean first_horiz = RND(2);
5345 int new_move_dir = MovDir[x][y];
5347 if (element_info[element].move_stepsize == 0) /* "not moving" */
5349 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5350 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5356 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5357 Moving2Blocked(x, y, &newx, &newy);
5359 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5363 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5364 Moving2Blocked(x, y, &newx, &newy);
5366 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5369 MovDir[x][y] = old_move_dir;
5372 else if (move_pattern == MV_WHEN_PUSHED ||
5373 move_pattern == MV_WHEN_DROPPED)
5375 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5376 MovDir[x][y] = MV_NONE;
5380 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5382 static int test_xy[7][2] =
5392 static int test_dir[7] =
5402 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5403 int move_preference = -1000000; /* start with very low preference */
5404 int new_move_dir = MV_NONE;
5405 int start_test = RND(4);
5408 for (i = 0; i < NUM_DIRECTIONS; i++)
5410 int move_dir = test_dir[start_test + i];
5411 int move_dir_preference;
5413 xx = x + test_xy[start_test + i][0];
5414 yy = y + test_xy[start_test + i][1];
5416 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5417 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5419 new_move_dir = move_dir;
5424 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5427 move_dir_preference = -1 * RunnerVisit[xx][yy];
5428 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5429 move_dir_preference = PlayerVisit[xx][yy];
5431 if (move_dir_preference > move_preference)
5433 /* prefer field that has not been visited for the longest time */
5434 move_preference = move_dir_preference;
5435 new_move_dir = move_dir;
5437 else if (move_dir_preference == move_preference &&
5438 move_dir == old_move_dir)
5440 /* prefer last direction when all directions are preferred equally */
5441 move_preference = move_dir_preference;
5442 new_move_dir = move_dir;
5446 MovDir[x][y] = new_move_dir;
5447 if (old_move_dir != new_move_dir)
5448 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5452 static void TurnRound(int x, int y)
5454 int direction = MovDir[x][y];
5458 GfxDir[x][y] = MovDir[x][y];
5460 if (direction != MovDir[x][y])
5464 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5466 ResetGfxFrame(x, y, FALSE);
5469 static boolean JustBeingPushed(int x, int y)
5473 for (i = 0; i < MAX_PLAYERS; i++)
5475 struct PlayerInfo *player = &stored_player[i];
5477 if (player->active && player->is_pushing && player->MovPos)
5479 int next_jx = player->jx + (player->jx - player->last_jx);
5480 int next_jy = player->jy + (player->jy - player->last_jy);
5482 if (x == next_jx && y == next_jy)
5490 void StartMoving(int x, int y)
5492 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5493 int element = Feld[x][y];
5498 if (MovDelay[x][y] == 0)
5499 GfxAction[x][y] = ACTION_DEFAULT;
5501 if (CAN_FALL(element) && y < lev_fieldy - 1)
5503 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5504 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5505 if (JustBeingPushed(x, y))
5508 if (element == EL_QUICKSAND_FULL)
5510 if (IS_FREE(x, y + 1))
5512 InitMovingField(x, y, MV_DOWN);
5513 started_moving = TRUE;
5515 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5516 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5517 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5518 Store[x][y] = EL_ROCK;
5520 Store[x][y] = EL_ROCK;
5523 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5525 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5527 if (!MovDelay[x][y])
5528 MovDelay[x][y] = TILEY + 1;
5537 Feld[x][y] = EL_QUICKSAND_EMPTY;
5538 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5539 Store[x][y + 1] = Store[x][y];
5542 PlayLevelSoundAction(x, y, ACTION_FILLING);
5545 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5546 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5548 InitMovingField(x, y, MV_DOWN);
5549 started_moving = TRUE;
5551 Feld[x][y] = EL_QUICKSAND_FILLING;
5552 Store[x][y] = element;
5554 PlayLevelSoundAction(x, y, ACTION_FILLING);
5556 else if (element == EL_MAGIC_WALL_FULL)
5558 if (IS_FREE(x, y + 1))
5560 InitMovingField(x, y, MV_DOWN);
5561 started_moving = TRUE;
5563 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5564 Store[x][y] = EL_CHANGED(Store[x][y]);
5566 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5568 if (!MovDelay[x][y])
5569 MovDelay[x][y] = TILEY/4 + 1;
5578 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5579 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5580 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5584 else if (element == EL_BD_MAGIC_WALL_FULL)
5586 if (IS_FREE(x, y + 1))
5588 InitMovingField(x, y, MV_DOWN);
5589 started_moving = TRUE;
5591 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5592 Store[x][y] = EL_CHANGED2(Store[x][y]);
5594 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5596 if (!MovDelay[x][y])
5597 MovDelay[x][y] = TILEY/4 + 1;
5606 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5607 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5608 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5612 else if (CAN_PASS_MAGIC_WALL(element) &&
5613 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5614 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5616 InitMovingField(x, y, MV_DOWN);
5617 started_moving = TRUE;
5620 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5621 EL_BD_MAGIC_WALL_FILLING);
5622 Store[x][y] = element;
5624 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5626 SplashAcid(x, y + 1);
5628 InitMovingField(x, y, MV_DOWN);
5629 started_moving = TRUE;
5631 Store[x][y] = EL_ACID;
5633 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5634 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5636 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5637 CAN_FALL(element) && WasJustFalling[x][y] &&
5638 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5640 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5641 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5642 (Feld[x][y + 1] == EL_BLOCKED)))
5644 /* this is needed for a special case not covered by calling "Impact()"
5645 from "ContinueMoving()": if an element moves to a tile directly below
5646 another element which was just falling on that tile (which was empty
5647 in the previous frame), the falling element above would just stop
5648 instead of smashing the element below (in previous version, the above
5649 element was just checked for "moving" instead of "falling", resulting
5650 in incorrect smashes caused by horizontal movement of the above
5651 element; also, the case of the player being the element to smash was
5652 simply not covered here... :-/ ) */
5654 CheckCollision[x][y] = 0;
5658 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5660 if (MovDir[x][y] == MV_NONE)
5662 InitMovingField(x, y, MV_DOWN);
5663 started_moving = TRUE;
5666 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5668 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5669 MovDir[x][y] = MV_DOWN;
5671 InitMovingField(x, y, MV_DOWN);
5672 started_moving = TRUE;
5674 else if (element == EL_AMOEBA_DROP)
5676 Feld[x][y] = EL_AMOEBA_GROWING;
5677 Store[x][y] = EL_AMOEBA_WET;
5679 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5680 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5681 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5682 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5684 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5685 (IS_FREE(x - 1, y + 1) ||
5686 Feld[x - 1][y + 1] == EL_ACID));
5687 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5688 (IS_FREE(x + 1, y + 1) ||
5689 Feld[x + 1][y + 1] == EL_ACID));
5690 boolean can_fall_any = (can_fall_left || can_fall_right);
5691 boolean can_fall_both = (can_fall_left && can_fall_right);
5692 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5694 #if USE_NEW_ALL_SLIPPERY
5695 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5697 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5698 can_fall_right = FALSE;
5699 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5700 can_fall_left = FALSE;
5701 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5702 can_fall_right = FALSE;
5703 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5704 can_fall_left = FALSE;
5706 can_fall_any = (can_fall_left || can_fall_right);
5707 can_fall_both = FALSE;
5710 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5712 if (slippery_type == SLIPPERY_ONLY_LEFT)
5713 can_fall_right = FALSE;
5714 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5715 can_fall_left = FALSE;
5716 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5717 can_fall_right = FALSE;
5718 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5719 can_fall_left = FALSE;
5721 can_fall_any = (can_fall_left || can_fall_right);
5722 can_fall_both = (can_fall_left && can_fall_right);
5726 #if USE_NEW_ALL_SLIPPERY
5728 #if USE_NEW_SP_SLIPPERY
5729 /* !!! better use the same properties as for custom elements here !!! */
5730 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5731 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5733 can_fall_right = FALSE; /* slip down on left side */
5734 can_fall_both = FALSE;
5739 #if USE_NEW_ALL_SLIPPERY
5742 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5743 can_fall_right = FALSE; /* slip down on left side */
5745 can_fall_left = !(can_fall_right = RND(2));
5747 can_fall_both = FALSE;
5752 if (game.emulation == EMU_BOULDERDASH ||
5753 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5754 can_fall_right = FALSE; /* slip down on left side */
5756 can_fall_left = !(can_fall_right = RND(2));
5758 can_fall_both = FALSE;
5764 /* if not determined otherwise, prefer left side for slipping down */
5765 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5766 started_moving = TRUE;
5770 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5772 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5775 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5776 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5777 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5778 int belt_dir = game.belt_dir[belt_nr];
5780 if ((belt_dir == MV_LEFT && left_is_free) ||
5781 (belt_dir == MV_RIGHT && right_is_free))
5783 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5785 InitMovingField(x, y, belt_dir);
5786 started_moving = TRUE;
5788 Pushed[x][y] = TRUE;
5789 Pushed[nextx][y] = TRUE;
5791 GfxAction[x][y] = ACTION_DEFAULT;
5795 MovDir[x][y] = 0; /* if element was moving, stop it */
5800 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5802 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5804 if (CAN_MOVE(element) && !started_moving)
5807 int move_pattern = element_info[element].move_pattern;
5812 if (MovDir[x][y] == MV_NONE)
5814 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5815 x, y, element, element_info[element].token_name);
5816 printf("StartMoving(): This should never happen!\n");
5821 Moving2Blocked(x, y, &newx, &newy);
5823 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5826 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5827 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5829 WasJustMoving[x][y] = 0;
5830 CheckCollision[x][y] = 0;
5832 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5834 if (Feld[x][y] != element) /* element has changed */
5838 if (!MovDelay[x][y]) /* start new movement phase */
5840 /* all objects that can change their move direction after each step
5841 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5843 if (element != EL_YAMYAM &&
5844 element != EL_DARK_YAMYAM &&
5845 element != EL_PACMAN &&
5846 !(move_pattern & MV_ANY_DIRECTION) &&
5847 move_pattern != MV_TURNING_LEFT &&
5848 move_pattern != MV_TURNING_RIGHT &&
5849 move_pattern != MV_TURNING_LEFT_RIGHT &&
5850 move_pattern != MV_TURNING_RIGHT_LEFT &&
5851 move_pattern != MV_TURNING_RANDOM)
5855 if (MovDelay[x][y] && (element == EL_BUG ||
5856 element == EL_SPACESHIP ||
5857 element == EL_SP_SNIKSNAK ||
5858 element == EL_SP_ELECTRON ||
5859 element == EL_MOLE))
5860 DrawLevelField(x, y);
5864 if (MovDelay[x][y]) /* wait some time before next movement */
5868 if (element == EL_ROBOT ||
5869 element == EL_YAMYAM ||
5870 element == EL_DARK_YAMYAM)
5872 DrawLevelElementAnimationIfNeeded(x, y, element);
5873 PlayLevelSoundAction(x, y, ACTION_WAITING);
5875 else if (element == EL_SP_ELECTRON)
5876 DrawLevelElementAnimationIfNeeded(x, y, element);
5877 else if (element == EL_DRAGON)
5880 int dir = MovDir[x][y];
5881 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5882 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5883 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5884 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5885 dir == MV_UP ? IMG_FLAMES_1_UP :
5886 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5887 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5889 GfxAction[x][y] = ACTION_ATTACKING;
5891 if (IS_PLAYER(x, y))
5892 DrawPlayerField(x, y);
5894 DrawLevelField(x, y);
5896 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5898 for (i = 1; i <= 3; i++)
5900 int xx = x + i * dx;
5901 int yy = y + i * dy;
5902 int sx = SCREENX(xx);
5903 int sy = SCREENY(yy);
5904 int flame_graphic = graphic + (i - 1);
5906 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5911 int flamed = MovingOrBlocked2Element(xx, yy);
5915 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5917 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5918 RemoveMovingField(xx, yy);
5920 RemoveField(xx, yy);
5922 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5925 RemoveMovingField(xx, yy);
5928 ChangeDelay[xx][yy] = 0;
5930 Feld[xx][yy] = EL_FLAMES;
5932 if (IN_SCR_FIELD(sx, sy))
5934 DrawLevelFieldCrumbledSand(xx, yy);
5935 DrawGraphic(sx, sy, flame_graphic, frame);
5940 if (Feld[xx][yy] == EL_FLAMES)
5941 Feld[xx][yy] = EL_EMPTY;
5942 DrawLevelField(xx, yy);
5947 if (MovDelay[x][y]) /* element still has to wait some time */
5949 PlayLevelSoundAction(x, y, ACTION_WAITING);
5955 /* now make next step */
5957 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5959 if (DONT_COLLIDE_WITH(element) &&
5960 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5961 !PLAYER_ENEMY_PROTECTED(newx, newy))
5963 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5968 else if (CAN_MOVE_INTO_ACID(element) &&
5969 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5970 !IS_MV_DIAGONAL(MovDir[x][y]) &&
5971 (MovDir[x][y] == MV_DOWN ||
5972 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5974 SplashAcid(newx, newy);
5975 Store[x][y] = EL_ACID;
5977 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5979 if (Feld[newx][newy] == EL_EXIT_OPEN)
5982 DrawLevelField(x, y);
5984 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5985 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5986 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5988 local_player->friends_still_needed--;
5989 if (!local_player->friends_still_needed &&
5990 !local_player->GameOver && AllPlayersGone)
5991 PlayerWins(local_player);
5995 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5997 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
5998 DrawLevelField(newx, newy);
6000 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6002 else if (!IS_FREE(newx, newy))
6004 GfxAction[x][y] = ACTION_WAITING;
6006 if (IS_PLAYER(x, y))
6007 DrawPlayerField(x, y);
6009 DrawLevelField(x, y);
6014 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6016 if (IS_FOOD_PIG(Feld[newx][newy]))
6018 if (IS_MOVING(newx, newy))
6019 RemoveMovingField(newx, newy);
6022 Feld[newx][newy] = EL_EMPTY;
6023 DrawLevelField(newx, newy);
6026 PlayLevelSound(x, y, SND_PIG_DIGGING);
6028 else if (!IS_FREE(newx, newy))
6030 if (IS_PLAYER(x, y))
6031 DrawPlayerField(x, y);
6033 DrawLevelField(x, y);
6038 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6040 if (Store[x][y] != EL_EMPTY)
6042 boolean can_clone = FALSE;
6045 /* check if element to clone is still there */
6046 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6048 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6056 /* cannot clone or target field not free anymore -- do not clone */
6057 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6058 Store[x][y] = EL_EMPTY;
6061 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6063 if (IS_MV_DIAGONAL(MovDir[x][y]))
6065 int diagonal_move_dir = MovDir[x][y];
6066 int stored = Store[x][y];
6067 int change_delay = 8;
6070 /* android is moving diagonally */
6072 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6074 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6075 GfxElement[x][y] = EL_EMC_ANDROID;
6076 GfxAction[x][y] = ACTION_SHRINKING;
6077 GfxDir[x][y] = diagonal_move_dir;
6078 ChangeDelay[x][y] = change_delay;
6080 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6083 DrawLevelGraphicAnimation(x, y, graphic);
6084 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6086 if (Feld[newx][newy] == EL_ACID)
6088 SplashAcid(newx, newy);
6093 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6095 Store[newx][newy] = EL_EMC_ANDROID;
6096 GfxElement[newx][newy] = EL_EMC_ANDROID;
6097 GfxAction[newx][newy] = ACTION_GROWING;
6098 GfxDir[newx][newy] = diagonal_move_dir;
6099 ChangeDelay[newx][newy] = change_delay;
6101 graphic = el_act_dir2img(GfxElement[newx][newy],
6102 GfxAction[newx][newy], GfxDir[newx][newy]);
6104 DrawLevelGraphicAnimation(newx, newy, graphic);
6105 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6111 Feld[newx][newy] = EL_EMPTY;
6112 DrawLevelField(newx, newy);
6114 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6117 else if (!IS_FREE(newx, newy))
6120 if (IS_PLAYER(x, y))
6121 DrawPlayerField(x, y);
6123 DrawLevelField(x, y);
6129 else if (IS_CUSTOM_ELEMENT(element) &&
6130 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6132 int new_element = Feld[newx][newy];
6134 if (!IS_FREE(newx, newy))
6136 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6137 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6140 /* no element can dig solid indestructible elements */
6141 if (IS_INDESTRUCTIBLE(new_element) &&
6142 !IS_DIGGABLE(new_element) &&
6143 !IS_COLLECTIBLE(new_element))
6146 if (AmoebaNr[newx][newy] &&
6147 (new_element == EL_AMOEBA_FULL ||
6148 new_element == EL_BD_AMOEBA ||
6149 new_element == EL_AMOEBA_GROWING))
6151 AmoebaCnt[AmoebaNr[newx][newy]]--;
6152 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6155 if (IS_MOVING(newx, newy))
6156 RemoveMovingField(newx, newy);
6159 RemoveField(newx, newy);
6160 DrawLevelField(newx, newy);
6163 /* if digged element was about to explode, prevent the explosion */
6164 ExplodeField[newx][newy] = EX_TYPE_NONE;
6166 PlayLevelSoundAction(x, y, action);
6169 Store[newx][newy] = EL_EMPTY;
6171 /* this makes it possible to leave the removed element again */
6172 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6173 Store[newx][newy] = new_element;
6175 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6177 int move_leave_element = element_info[element].move_leave_element;
6179 /* this makes it possible to leave the removed element again */
6180 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6181 new_element : move_leave_element);
6185 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6187 RunnerVisit[x][y] = FrameCounter;
6188 PlayerVisit[x][y] /= 8; /* expire player visit path */
6191 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6193 if (!IS_FREE(newx, newy))
6195 if (IS_PLAYER(x, y))
6196 DrawPlayerField(x, y);
6198 DrawLevelField(x, y);
6204 boolean wanna_flame = !RND(10);
6205 int dx = newx - x, dy = newy - y;
6206 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6207 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6208 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6209 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6210 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6211 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6214 IS_CLASSIC_ENEMY(element1) ||
6215 IS_CLASSIC_ENEMY(element2)) &&
6216 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6217 element1 != EL_FLAMES && element2 != EL_FLAMES)
6219 ResetGfxAnimation(x, y);
6220 GfxAction[x][y] = ACTION_ATTACKING;
6222 if (IS_PLAYER(x, y))
6223 DrawPlayerField(x, y);
6225 DrawLevelField(x, y);
6227 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6229 MovDelay[x][y] = 50;
6233 RemoveField(newx, newy);
6235 Feld[newx][newy] = EL_FLAMES;
6236 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6239 RemoveField(newx1, newy1);
6241 Feld[newx1][newy1] = EL_FLAMES;
6243 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6246 RemoveField(newx2, newy2);
6248 Feld[newx2][newy2] = EL_FLAMES;
6255 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6256 Feld[newx][newy] == EL_DIAMOND)
6258 if (IS_MOVING(newx, newy))
6259 RemoveMovingField(newx, newy);
6262 Feld[newx][newy] = EL_EMPTY;
6263 DrawLevelField(newx, newy);
6266 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6268 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6269 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6271 if (AmoebaNr[newx][newy])
6273 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6274 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6275 Feld[newx][newy] == EL_BD_AMOEBA)
6276 AmoebaCnt[AmoebaNr[newx][newy]]--;
6281 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6283 RemoveMovingField(newx, newy);
6286 if (IS_MOVING(newx, newy))
6288 RemoveMovingField(newx, newy);
6293 Feld[newx][newy] = EL_EMPTY;
6294 DrawLevelField(newx, newy);
6297 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6299 else if ((element == EL_PACMAN || element == EL_MOLE)
6300 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6302 if (AmoebaNr[newx][newy])
6304 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6305 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6306 Feld[newx][newy] == EL_BD_AMOEBA)
6307 AmoebaCnt[AmoebaNr[newx][newy]]--;
6310 if (element == EL_MOLE)
6312 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6313 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6315 ResetGfxAnimation(x, y);
6316 GfxAction[x][y] = ACTION_DIGGING;
6317 DrawLevelField(x, y);
6319 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6321 return; /* wait for shrinking amoeba */
6323 else /* element == EL_PACMAN */
6325 Feld[newx][newy] = EL_EMPTY;
6326 DrawLevelField(newx, newy);
6327 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6330 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6331 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6332 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6334 /* wait for shrinking amoeba to completely disappear */
6337 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6339 /* object was running against a wall */
6344 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6345 if (move_pattern & MV_ANY_DIRECTION &&
6346 move_pattern == MovDir[x][y])
6348 int blocking_element =
6349 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6351 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6354 element = Feld[x][y]; /* element might have changed */
6358 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6359 DrawLevelElementAnimation(x, y, element);
6361 if (DONT_TOUCH(element))
6362 TestIfBadThingTouchesPlayer(x, y);
6367 InitMovingField(x, y, MovDir[x][y]);
6369 PlayLevelSoundAction(x, y, ACTION_MOVING);
6373 ContinueMoving(x, y);
6376 void ContinueMoving(int x, int y)
6378 int element = Feld[x][y];
6379 struct ElementInfo *ei = &element_info[element];
6380 int direction = MovDir[x][y];
6381 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6382 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6383 int newx = x + dx, newy = y + dy;
6384 int stored = Store[x][y];
6385 int stored_new = Store[newx][newy];
6386 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6387 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6388 boolean last_line = (newy == lev_fieldy - 1);
6390 MovPos[x][y] += getElementMoveStepsize(x, y);
6392 if (pushed_by_player) /* special case: moving object pushed by player */
6393 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6395 if (ABS(MovPos[x][y]) < TILEX)
6397 DrawLevelField(x, y);
6399 return; /* element is still moving */
6402 /* element reached destination field */
6404 Feld[x][y] = EL_EMPTY;
6405 Feld[newx][newy] = element;
6406 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6408 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6410 element = Feld[newx][newy] = EL_ACID;
6412 else if (element == EL_MOLE)
6414 Feld[x][y] = EL_SAND;
6416 DrawLevelFieldCrumbledSandNeighbours(x, y);
6418 else if (element == EL_QUICKSAND_FILLING)
6420 element = Feld[newx][newy] = get_next_element(element);
6421 Store[newx][newy] = Store[x][y];
6423 else if (element == EL_QUICKSAND_EMPTYING)
6425 Feld[x][y] = get_next_element(element);
6426 element = Feld[newx][newy] = Store[x][y];
6428 else if (element == EL_MAGIC_WALL_FILLING)
6430 element = Feld[newx][newy] = get_next_element(element);
6431 if (!game.magic_wall_active)
6432 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6433 Store[newx][newy] = Store[x][y];
6435 else if (element == EL_MAGIC_WALL_EMPTYING)
6437 Feld[x][y] = get_next_element(element);
6438 if (!game.magic_wall_active)
6439 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6440 element = Feld[newx][newy] = Store[x][y];
6442 #if USE_NEW_CUSTOM_VALUE
6443 InitField(newx, newy, FALSE);
6446 else if (element == EL_BD_MAGIC_WALL_FILLING)
6448 element = Feld[newx][newy] = get_next_element(element);
6449 if (!game.magic_wall_active)
6450 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6451 Store[newx][newy] = Store[x][y];
6453 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6455 Feld[x][y] = get_next_element(element);
6456 if (!game.magic_wall_active)
6457 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6458 element = Feld[newx][newy] = Store[x][y];
6460 #if USE_NEW_CUSTOM_VALUE
6461 InitField(newx, newy, FALSE);
6464 else if (element == EL_AMOEBA_DROPPING)
6466 Feld[x][y] = get_next_element(element);
6467 element = Feld[newx][newy] = Store[x][y];
6469 else if (element == EL_SOKOBAN_OBJECT)
6472 Feld[x][y] = Back[x][y];
6474 if (Back[newx][newy])
6475 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6477 Back[x][y] = Back[newx][newy] = 0;
6480 Store[x][y] = EL_EMPTY;
6485 MovDelay[newx][newy] = 0;
6487 if (CAN_CHANGE_OR_HAS_ACTION(element))
6489 /* copy element change control values to new field */
6490 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6491 ChangePage[newx][newy] = ChangePage[x][y];
6492 ChangeCount[newx][newy] = ChangeCount[x][y];
6493 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6496 #if USE_NEW_CUSTOM_VALUE
6497 CustomValue[newx][newy] = CustomValue[x][y];
6500 ChangeDelay[x][y] = 0;
6501 ChangePage[x][y] = -1;
6502 ChangeCount[x][y] = 0;
6503 ChangeEvent[x][y] = -1;
6505 #if USE_NEW_CUSTOM_VALUE
6506 CustomValue[x][y] = 0;
6509 /* copy animation control values to new field */
6510 GfxFrame[newx][newy] = GfxFrame[x][y];
6511 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6512 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6513 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6515 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6517 /* some elements can leave other elements behind after moving */
6519 if (ei->move_leave_element != EL_EMPTY &&
6520 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6521 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6523 if (IS_CUSTOM_ELEMENT(element) && 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)))
6528 int move_leave_element = ei->move_leave_element;
6532 /* this makes it possible to leave the removed element again */
6533 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6534 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
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;
6541 /* this makes it possible to leave the removed element again */
6542 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6543 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6544 move_leave_element = stored;
6547 Feld[x][y] = move_leave_element;
6549 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6550 MovDir[x][y] = direction;
6552 InitField(x, y, FALSE);
6554 if (GFX_CRUMBLED(Feld[x][y]))
6555 DrawLevelFieldCrumbledSandNeighbours(x, y);
6557 if (ELEM_IS_PLAYER(move_leave_element))
6558 RelocatePlayer(x, y, move_leave_element);
6561 /* do this after checking for left-behind element */
6562 ResetGfxAnimation(x, y); /* reset animation values for old field */
6564 if (!CAN_MOVE(element) ||
6565 (CAN_FALL(element) && direction == MV_DOWN &&
6566 (element == EL_SPRING ||
6567 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6568 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6569 GfxDir[x][y] = MovDir[newx][newy] = 0;
6571 DrawLevelField(x, y);
6572 DrawLevelField(newx, newy);
6574 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6576 /* prevent pushed element from moving on in pushed direction */
6577 if (pushed_by_player && CAN_MOVE(element) &&
6578 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6579 !(element_info[element].move_pattern & direction))
6580 TurnRound(newx, newy);
6582 /* prevent elements on conveyor belt from moving on in last direction */
6583 if (pushed_by_conveyor && CAN_FALL(element) &&
6584 direction & MV_HORIZONTAL)
6585 MovDir[newx][newy] = 0;
6587 if (!pushed_by_player)
6589 int nextx = newx + dx, nexty = newy + dy;
6590 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6592 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6594 if (CAN_FALL(element) && direction == MV_DOWN)
6595 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6597 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6598 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6601 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6603 TestIfBadThingTouchesPlayer(newx, newy);
6604 TestIfBadThingTouchesFriend(newx, newy);
6606 if (!IS_CUSTOM_ELEMENT(element))
6607 TestIfBadThingTouchesOtherBadThing(newx, newy);
6609 else if (element == EL_PENGUIN)
6610 TestIfFriendTouchesBadThing(newx, newy);
6612 /* give the player one last chance (one more frame) to move away */
6613 if (CAN_FALL(element) && direction == MV_DOWN &&
6614 (last_line || (!IS_FREE(x, newy + 1) &&
6615 (!IS_PLAYER(x, newy + 1) ||
6616 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6619 if (pushed_by_player && !game.use_change_when_pushing_bug)
6621 int push_side = MV_DIR_OPPOSITE(direction);
6622 struct PlayerInfo *player = PLAYERINFO(x, y);
6624 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6625 player->index_bit, push_side);
6626 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6627 player->index_bit, push_side);
6630 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6631 MovDelay[newx][newy] = 1;
6633 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6635 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6638 if (ChangePage[newx][newy] != -1) /* delayed change */
6640 int page = ChangePage[newx][newy];
6641 struct ElementChangeInfo *change = &ei->change_page[page];
6643 ChangePage[newx][newy] = -1;
6645 if (change->can_change)
6647 if (ChangeElement(newx, newy, element, page))
6649 if (change->post_change_function)
6650 change->post_change_function(newx, newy);
6654 if (change->has_action)
6655 ExecuteCustomElementAction(newx, newy, element, page);
6659 TestIfElementHitsCustomElement(newx, newy, direction);
6660 TestIfPlayerTouchesCustomElement(newx, newy);
6661 TestIfElementTouchesCustomElement(newx, newy);
6663 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6664 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6665 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6666 MV_DIR_OPPOSITE(direction));
6669 int AmoebeNachbarNr(int ax, int ay)
6672 int element = Feld[ax][ay];
6674 static int xy[4][2] =
6682 for (i = 0; i < NUM_DIRECTIONS; i++)
6684 int x = ax + xy[i][0];
6685 int y = ay + xy[i][1];
6687 if (!IN_LEV_FIELD(x, y))
6690 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6691 group_nr = AmoebaNr[x][y];
6697 void AmoebenVereinigen(int ax, int ay)
6699 int i, x, y, xx, yy;
6700 int new_group_nr = AmoebaNr[ax][ay];
6701 static int xy[4][2] =
6709 if (new_group_nr == 0)
6712 for (i = 0; i < NUM_DIRECTIONS; i++)
6717 if (!IN_LEV_FIELD(x, y))
6720 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6721 Feld[x][y] == EL_BD_AMOEBA ||
6722 Feld[x][y] == EL_AMOEBA_DEAD) &&
6723 AmoebaNr[x][y] != new_group_nr)
6725 int old_group_nr = AmoebaNr[x][y];
6727 if (old_group_nr == 0)
6730 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6731 AmoebaCnt[old_group_nr] = 0;
6732 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6733 AmoebaCnt2[old_group_nr] = 0;
6735 SCAN_PLAYFIELD(xx, yy)
6737 if (AmoebaNr[xx][yy] == old_group_nr)
6738 AmoebaNr[xx][yy] = new_group_nr;
6744 void AmoebeUmwandeln(int ax, int ay)
6748 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6750 int group_nr = AmoebaNr[ax][ay];
6755 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6756 printf("AmoebeUmwandeln(): This should never happen!\n");
6761 SCAN_PLAYFIELD(x, y)
6763 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6766 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6770 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6771 SND_AMOEBA_TURNING_TO_GEM :
6772 SND_AMOEBA_TURNING_TO_ROCK));
6777 static int xy[4][2] =
6785 for (i = 0; i < NUM_DIRECTIONS; i++)
6790 if (!IN_LEV_FIELD(x, y))
6793 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6795 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6796 SND_AMOEBA_TURNING_TO_GEM :
6797 SND_AMOEBA_TURNING_TO_ROCK));
6804 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6807 int group_nr = AmoebaNr[ax][ay];
6808 boolean done = FALSE;
6813 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6814 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6819 SCAN_PLAYFIELD(x, y)
6821 if (AmoebaNr[x][y] == group_nr &&
6822 (Feld[x][y] == EL_AMOEBA_DEAD ||
6823 Feld[x][y] == EL_BD_AMOEBA ||
6824 Feld[x][y] == EL_AMOEBA_GROWING))
6827 Feld[x][y] = new_element;
6828 InitField(x, y, FALSE);
6829 DrawLevelField(x, y);
6835 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6836 SND_BD_AMOEBA_TURNING_TO_ROCK :
6837 SND_BD_AMOEBA_TURNING_TO_GEM));
6840 void AmoebeWaechst(int x, int y)
6842 static unsigned long sound_delay = 0;
6843 static unsigned long sound_delay_value = 0;
6845 if (!MovDelay[x][y]) /* start new growing cycle */
6849 if (DelayReached(&sound_delay, sound_delay_value))
6851 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6852 sound_delay_value = 30;
6856 if (MovDelay[x][y]) /* wait some time before growing bigger */
6859 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6861 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6862 6 - MovDelay[x][y]);
6864 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6867 if (!MovDelay[x][y])
6869 Feld[x][y] = Store[x][y];
6871 DrawLevelField(x, y);
6876 void AmoebaDisappearing(int x, int y)
6878 static unsigned long sound_delay = 0;
6879 static unsigned long sound_delay_value = 0;
6881 if (!MovDelay[x][y]) /* start new shrinking cycle */
6885 if (DelayReached(&sound_delay, sound_delay_value))
6886 sound_delay_value = 30;
6889 if (MovDelay[x][y]) /* wait some time before shrinking */
6892 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6894 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6895 6 - MovDelay[x][y]);
6897 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6900 if (!MovDelay[x][y])
6902 Feld[x][y] = EL_EMPTY;
6903 DrawLevelField(x, y);
6905 /* don't let mole enter this field in this cycle;
6906 (give priority to objects falling to this field from above) */
6912 void AmoebeAbleger(int ax, int ay)
6915 int element = Feld[ax][ay];
6916 int graphic = el2img(element);
6917 int newax = ax, neway = ay;
6918 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6919 static int xy[4][2] =
6927 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6929 Feld[ax][ay] = EL_AMOEBA_DEAD;
6930 DrawLevelField(ax, ay);
6934 if (IS_ANIMATED(graphic))
6935 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6937 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6938 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6940 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6943 if (MovDelay[ax][ay])
6947 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6950 int x = ax + xy[start][0];
6951 int y = ay + xy[start][1];
6953 if (!IN_LEV_FIELD(x, y))
6956 if (IS_FREE(x, y) ||
6957 CAN_GROW_INTO(Feld[x][y]) ||
6958 Feld[x][y] == EL_QUICKSAND_EMPTY)
6964 if (newax == ax && neway == ay)
6967 else /* normal or "filled" (BD style) amoeba */
6970 boolean waiting_for_player = FALSE;
6972 for (i = 0; i < NUM_DIRECTIONS; i++)
6974 int j = (start + i) % 4;
6975 int x = ax + xy[j][0];
6976 int y = ay + xy[j][1];
6978 if (!IN_LEV_FIELD(x, y))
6981 if (IS_FREE(x, y) ||
6982 CAN_GROW_INTO(Feld[x][y]) ||
6983 Feld[x][y] == EL_QUICKSAND_EMPTY)
6989 else if (IS_PLAYER(x, y))
6990 waiting_for_player = TRUE;
6993 if (newax == ax && neway == ay) /* amoeba cannot grow */
6995 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6997 Feld[ax][ay] = EL_AMOEBA_DEAD;
6998 DrawLevelField(ax, ay);
6999 AmoebaCnt[AmoebaNr[ax][ay]]--;
7001 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7003 if (element == EL_AMOEBA_FULL)
7004 AmoebeUmwandeln(ax, ay);
7005 else if (element == EL_BD_AMOEBA)
7006 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7011 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7013 /* amoeba gets larger by growing in some direction */
7015 int new_group_nr = AmoebaNr[ax][ay];
7018 if (new_group_nr == 0)
7020 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7021 printf("AmoebeAbleger(): This should never happen!\n");
7026 AmoebaNr[newax][neway] = new_group_nr;
7027 AmoebaCnt[new_group_nr]++;
7028 AmoebaCnt2[new_group_nr]++;
7030 /* if amoeba touches other amoeba(s) after growing, unify them */
7031 AmoebenVereinigen(newax, neway);
7033 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7035 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7041 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7042 (neway == lev_fieldy - 1 && newax != ax))
7044 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7045 Store[newax][neway] = element;
7047 else if (neway == ay || element == EL_EMC_DRIPPER)
7049 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7051 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7055 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7056 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7057 Store[ax][ay] = EL_AMOEBA_DROP;
7058 ContinueMoving(ax, ay);
7062 DrawLevelField(newax, neway);
7065 void Life(int ax, int ay)
7069 int element = Feld[ax][ay];
7070 int graphic = el2img(element);
7071 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7073 boolean changed = FALSE;
7075 if (IS_ANIMATED(graphic))
7076 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7081 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7082 MovDelay[ax][ay] = life_time;
7084 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7087 if (MovDelay[ax][ay])
7091 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7093 int xx = ax+x1, yy = ay+y1;
7096 if (!IN_LEV_FIELD(xx, yy))
7099 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7101 int x = xx+x2, y = yy+y2;
7103 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7106 if (((Feld[x][y] == element ||
7107 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7109 (IS_FREE(x, y) && Stop[x][y]))
7113 if (xx == ax && yy == ay) /* field in the middle */
7115 if (nachbarn < life_parameter[0] ||
7116 nachbarn > life_parameter[1])
7118 Feld[xx][yy] = EL_EMPTY;
7120 DrawLevelField(xx, yy);
7121 Stop[xx][yy] = TRUE;
7125 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7126 { /* free border field */
7127 if (nachbarn >= life_parameter[2] &&
7128 nachbarn <= life_parameter[3])
7130 Feld[xx][yy] = element;
7131 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7133 DrawLevelField(xx, yy);
7134 Stop[xx][yy] = TRUE;
7141 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7142 SND_GAME_OF_LIFE_GROWING);
7145 static void InitRobotWheel(int x, int y)
7147 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7150 static void RunRobotWheel(int x, int y)
7152 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7155 static void StopRobotWheel(int x, int y)
7157 if (ZX == x && ZY == y)
7161 static void InitTimegateWheel(int x, int y)
7163 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7166 static void RunTimegateWheel(int x, int y)
7168 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7171 static void InitMagicBallDelay(int x, int y)
7174 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7176 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7180 static void ActivateMagicBall(int bx, int by)
7184 if (level.ball_random)
7186 int pos_border = RND(8); /* select one of the eight border elements */
7187 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7188 int xx = pos_content % 3;
7189 int yy = pos_content / 3;
7194 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7195 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7199 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7201 int xx = x - bx + 1;
7202 int yy = y - by + 1;
7204 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7205 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7209 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7212 void CheckExit(int x, int y)
7214 if (local_player->gems_still_needed > 0 ||
7215 local_player->sokobanfields_still_needed > 0 ||
7216 local_player->lights_still_needed > 0)
7218 int element = Feld[x][y];
7219 int graphic = el2img(element);
7221 if (IS_ANIMATED(graphic))
7222 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7227 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7230 Feld[x][y] = EL_EXIT_OPENING;
7232 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7235 void CheckExitSP(int x, int y)
7237 if (local_player->gems_still_needed > 0)
7239 int element = Feld[x][y];
7240 int graphic = el2img(element);
7242 if (IS_ANIMATED(graphic))
7243 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7248 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7251 Feld[x][y] = EL_SP_EXIT_OPENING;
7253 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7256 static void CloseAllOpenTimegates()
7260 SCAN_PLAYFIELD(x, y)
7262 int element = Feld[x][y];
7264 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7266 Feld[x][y] = EL_TIMEGATE_CLOSING;
7268 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7273 void DrawTwinkleOnField(int x, int y)
7275 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7278 if (Feld[x][y] == EL_BD_DIAMOND)
7281 if (MovDelay[x][y] == 0) /* next animation frame */
7282 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7284 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7288 if (setup.direct_draw && MovDelay[x][y])
7289 SetDrawtoField(DRAW_BUFFERED);
7291 DrawLevelElementAnimation(x, y, Feld[x][y]);
7293 if (MovDelay[x][y] != 0)
7295 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7296 10 - MovDelay[x][y]);
7298 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7300 if (setup.direct_draw)
7304 dest_x = FX + SCREENX(x) * TILEX;
7305 dest_y = FY + SCREENY(y) * TILEY;
7307 BlitBitmap(drawto_field, window,
7308 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7309 SetDrawtoField(DRAW_DIRECT);
7315 void MauerWaechst(int x, int y)
7319 if (!MovDelay[x][y]) /* next animation frame */
7320 MovDelay[x][y] = 3 * delay;
7322 if (MovDelay[x][y]) /* wait some time before next frame */
7326 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7328 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7329 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7331 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7334 if (!MovDelay[x][y])
7336 if (MovDir[x][y] == MV_LEFT)
7338 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7339 DrawLevelField(x - 1, y);
7341 else if (MovDir[x][y] == MV_RIGHT)
7343 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7344 DrawLevelField(x + 1, y);
7346 else if (MovDir[x][y] == MV_UP)
7348 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7349 DrawLevelField(x, y - 1);
7353 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7354 DrawLevelField(x, y + 1);
7357 Feld[x][y] = Store[x][y];
7359 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7360 DrawLevelField(x, y);
7365 void MauerAbleger(int ax, int ay)
7367 int element = Feld[ax][ay];
7368 int graphic = el2img(element);
7369 boolean oben_frei = FALSE, unten_frei = FALSE;
7370 boolean links_frei = FALSE, rechts_frei = FALSE;
7371 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7372 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7373 boolean new_wall = FALSE;
7375 if (IS_ANIMATED(graphic))
7376 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7378 if (!MovDelay[ax][ay]) /* start building new wall */
7379 MovDelay[ax][ay] = 6;
7381 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7384 if (MovDelay[ax][ay])
7388 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7390 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7392 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7394 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7397 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7398 element == EL_EXPANDABLE_WALL_ANY)
7402 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7403 Store[ax][ay-1] = element;
7404 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7405 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7406 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7407 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7412 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7413 Store[ax][ay+1] = element;
7414 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7415 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7416 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7417 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7422 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7423 element == EL_EXPANDABLE_WALL_ANY ||
7424 element == EL_EXPANDABLE_WALL ||
7425 element == EL_BD_EXPANDABLE_WALL)
7429 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7430 Store[ax-1][ay] = element;
7431 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7432 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7433 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7434 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7440 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7441 Store[ax+1][ay] = element;
7442 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7443 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7444 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7445 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7450 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7451 DrawLevelField(ax, ay);
7453 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7455 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7456 unten_massiv = TRUE;
7457 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7458 links_massiv = TRUE;
7459 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7460 rechts_massiv = TRUE;
7462 if (((oben_massiv && unten_massiv) ||
7463 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7464 element == EL_EXPANDABLE_WALL) &&
7465 ((links_massiv && rechts_massiv) ||
7466 element == EL_EXPANDABLE_WALL_VERTICAL))
7467 Feld[ax][ay] = EL_WALL;
7470 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7473 void CheckForDragon(int x, int y)
7476 boolean dragon_found = FALSE;
7477 static int xy[4][2] =
7485 for (i = 0; i < NUM_DIRECTIONS; i++)
7487 for (j = 0; j < 4; j++)
7489 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7491 if (IN_LEV_FIELD(xx, yy) &&
7492 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7494 if (Feld[xx][yy] == EL_DRAGON)
7495 dragon_found = TRUE;
7504 for (i = 0; i < NUM_DIRECTIONS; i++)
7506 for (j = 0; j < 3; j++)
7508 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7510 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7512 Feld[xx][yy] = EL_EMPTY;
7513 DrawLevelField(xx, yy);
7522 static void InitBuggyBase(int x, int y)
7524 int element = Feld[x][y];
7525 int activating_delay = FRAMES_PER_SECOND / 4;
7528 (element == EL_SP_BUGGY_BASE ?
7529 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7530 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7532 element == EL_SP_BUGGY_BASE_ACTIVE ?
7533 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7536 static void WarnBuggyBase(int x, int y)
7539 static int xy[4][2] =
7547 for (i = 0; i < NUM_DIRECTIONS; i++)
7549 int xx = x + xy[i][0];
7550 int yy = y + xy[i][1];
7552 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7554 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7561 static void InitTrap(int x, int y)
7563 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7566 static void ActivateTrap(int x, int y)
7568 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7571 static void ChangeActiveTrap(int x, int y)
7573 int graphic = IMG_TRAP_ACTIVE;
7575 /* if new animation frame was drawn, correct crumbled sand border */
7576 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7577 DrawLevelFieldCrumbledSand(x, y);
7580 static int getSpecialActionElement(int element, int number, int base_element)
7582 return (element != EL_EMPTY ? element :
7583 number != -1 ? base_element + number - 1 :
7587 static int getModifiedActionNumber(int value_old, int operator, int operand,
7588 int value_min, int value_max)
7590 int value_new = (operator == CA_MODE_SET ? operand :
7591 operator == CA_MODE_ADD ? value_old + operand :
7592 operator == CA_MODE_SUBTRACT ? value_old - operand :
7593 operator == CA_MODE_MULTIPLY ? value_old * operand :
7594 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7595 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7598 return (value_new < value_min ? value_min :
7599 value_new > value_max ? value_max :
7603 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7605 struct ElementInfo *ei = &element_info[element];
7606 struct ElementChangeInfo *change = &ei->change_page[page];
7607 int target_element = change->target_element;
7608 int action_type = change->action_type;
7609 int action_mode = change->action_mode;
7610 int action_arg = change->action_arg;
7613 if (!change->has_action)
7616 /* ---------- determine action paramater values -------------------------- */
7618 int level_time_value =
7619 (level.time > 0 ? TimeLeft :
7622 int action_arg_element =
7623 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7624 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7625 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7628 int action_arg_direction =
7629 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7630 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7631 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7632 change->actual_trigger_side :
7633 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7634 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7637 int action_arg_number_min =
7638 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7641 int action_arg_number_max =
7642 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7643 action_type == CA_SET_LEVEL_GEMS ? 999 :
7644 action_type == CA_SET_LEVEL_TIME ? 9999 :
7645 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7646 action_type == CA_SET_CE_VALUE ? 9999 :
7647 action_type == CA_SET_CE_SCORE ? 9999 :
7650 int action_arg_number_reset =
7651 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
7652 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7653 action_type == CA_SET_LEVEL_TIME ? level.time :
7654 action_type == CA_SET_LEVEL_SCORE ? 0 :
7655 #if USE_NEW_CUSTOM_VALUE
7656 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
7658 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7660 action_type == CA_SET_CE_SCORE ? 0 :
7663 int action_arg_number =
7664 (action_arg <= CA_ARG_MAX ? action_arg :
7665 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7666 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7667 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7668 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7669 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7670 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7671 #if USE_NEW_CUSTOM_VALUE
7672 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7674 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7676 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7677 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7678 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7679 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7680 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7681 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
7682 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7683 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
7684 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
7685 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7686 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7689 int action_arg_number_old =
7690 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7691 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7692 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7693 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7694 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7697 int action_arg_number_new =
7698 getModifiedActionNumber(action_arg_number_old,
7699 action_mode, action_arg_number,
7700 action_arg_number_min, action_arg_number_max);
7702 int trigger_player_bits =
7703 (change->actual_trigger_player >= EL_PLAYER_1 &&
7704 change->actual_trigger_player <= EL_PLAYER_4 ?
7705 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7708 int action_arg_player_bits =
7709 (action_arg >= CA_ARG_PLAYER_1 &&
7710 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7711 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7714 /* ---------- execute action -------------------------------------------- */
7723 /* ---------- level actions ------------------------------------------- */
7725 case CA_RESTART_LEVEL:
7727 game.restart_level = TRUE;
7732 case CA_SHOW_ENVELOPE:
7734 int element = getSpecialActionElement(action_arg_element,
7735 action_arg_number, EL_ENVELOPE_1);
7737 if (IS_ENVELOPE(element))
7738 local_player->show_envelope = element;
7743 case CA_SET_LEVEL_TIME:
7745 if (level.time > 0) /* only modify limited time value */
7747 TimeLeft = action_arg_number_new;
7749 DrawGameValue_Time(TimeLeft);
7751 if (!TimeLeft && setup.time_limit)
7752 for (i = 0; i < MAX_PLAYERS; i++)
7753 KillPlayer(&stored_player[i]);
7759 case CA_SET_LEVEL_SCORE:
7761 local_player->score = action_arg_number_new;
7763 DrawGameValue_Score(local_player->score);
7768 case CA_SET_LEVEL_GEMS:
7770 local_player->gems_still_needed = action_arg_number_new;
7772 DrawGameValue_Emeralds(local_player->gems_still_needed);
7777 #if !USE_PLAYER_GRAVITY
7778 case CA_SET_LEVEL_GRAVITY:
7780 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7781 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7782 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7788 case CA_SET_LEVEL_WIND:
7790 game.wind_direction = action_arg_direction;
7795 /* ---------- player actions ------------------------------------------ */
7797 case CA_MOVE_PLAYER:
7799 /* automatically move to the next field in specified direction */
7800 for (i = 0; i < MAX_PLAYERS; i++)
7801 if (trigger_player_bits & (1 << i))
7802 stored_player[i].programmed_action = action_arg_direction;
7807 case CA_EXIT_PLAYER:
7809 for (i = 0; i < MAX_PLAYERS; i++)
7810 if (action_arg_player_bits & (1 << i))
7811 PlayerWins(&stored_player[i]);
7816 case CA_KILL_PLAYER:
7818 for (i = 0; i < MAX_PLAYERS; i++)
7819 if (action_arg_player_bits & (1 << i))
7820 KillPlayer(&stored_player[i]);
7825 case CA_SET_PLAYER_KEYS:
7827 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7828 int element = getSpecialActionElement(action_arg_element,
7829 action_arg_number, EL_KEY_1);
7831 if (IS_KEY(element))
7833 for (i = 0; i < MAX_PLAYERS; i++)
7835 if (trigger_player_bits & (1 << i))
7837 stored_player[i].key[KEY_NR(element)] = key_state;
7839 DrawGameDoorValues();
7847 case CA_SET_PLAYER_SPEED:
7849 for (i = 0; i < MAX_PLAYERS; i++)
7851 if (trigger_player_bits & (1 << i))
7853 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7855 if (action_arg == CA_ARG_SPEED_FASTER &&
7856 stored_player[i].cannot_move)
7858 action_arg_number = STEPSIZE_VERY_SLOW;
7860 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7861 action_arg == CA_ARG_SPEED_FASTER)
7863 action_arg_number = 2;
7864 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7867 else if (action_arg == CA_ARG_NUMBER_RESET)
7869 action_arg_number = level.initial_player_stepsize[i];
7873 getModifiedActionNumber(move_stepsize,
7876 action_arg_number_min,
7877 action_arg_number_max);
7879 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7886 case CA_SET_PLAYER_SHIELD:
7888 for (i = 0; i < MAX_PLAYERS; i++)
7890 if (trigger_player_bits & (1 << i))
7892 if (action_arg == CA_ARG_SHIELD_OFF)
7894 stored_player[i].shield_normal_time_left = 0;
7895 stored_player[i].shield_deadly_time_left = 0;
7897 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7899 stored_player[i].shield_normal_time_left = 999999;
7901 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7903 stored_player[i].shield_normal_time_left = 999999;
7904 stored_player[i].shield_deadly_time_left = 999999;
7912 #if USE_PLAYER_GRAVITY
7913 case CA_SET_PLAYER_GRAVITY:
7915 for (i = 0; i < MAX_PLAYERS; i++)
7917 if (trigger_player_bits & (1 << i))
7919 stored_player[i].gravity =
7920 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7921 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7922 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
7923 stored_player[i].gravity);
7931 case CA_SET_PLAYER_ARTWORK:
7933 for (i = 0; i < MAX_PLAYERS; i++)
7935 if (trigger_player_bits & (1 << i))
7937 int artwork_element = action_arg_element;
7939 if (action_arg == CA_ARG_ELEMENT_RESET)
7941 (level.use_artwork_element[i] ? level.artwork_element[i] :
7942 stored_player[i].element_nr);
7944 #if USE_GFX_RESET_PLAYER_ARTWORK
7945 if (stored_player[i].artwork_element != artwork_element)
7946 stored_player[i].Frame = 0;
7949 stored_player[i].artwork_element = artwork_element;
7951 SetPlayerWaiting(&stored_player[i], FALSE);
7953 /* set number of special actions for bored and sleeping animation */
7954 stored_player[i].num_special_action_bored =
7955 get_num_special_action(artwork_element,
7956 ACTION_BORING_1, ACTION_BORING_LAST);
7957 stored_player[i].num_special_action_sleeping =
7958 get_num_special_action(artwork_element,
7959 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7966 /* ---------- CE actions ---------------------------------------------- */
7968 case CA_SET_CE_VALUE:
7970 #if USE_NEW_CUSTOM_VALUE
7971 int last_ce_value = CustomValue[x][y];
7973 CustomValue[x][y] = action_arg_number_new;
7975 if (CustomValue[x][y] != last_ce_value)
7977 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
7978 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
7980 if (CustomValue[x][y] == 0)
7982 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7983 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7991 case CA_SET_CE_SCORE:
7993 #if USE_NEW_CUSTOM_VALUE
7994 int last_ce_score = ei->collect_score;
7996 ei->collect_score = action_arg_number_new;
7998 if (ei->collect_score != last_ce_score)
8000 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8001 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8003 if (ei->collect_score == 0)
8007 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8008 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8011 This is a very special case that seems to be a mixture between
8012 CheckElementChange() and CheckTriggeredElementChange(): while
8013 the first one only affects single elements that are triggered
8014 directly, the second one affects multiple elements in the playfield
8015 that are triggered indirectly by another element. This is a third
8016 case: Changing the CE score always affects multiple identical CEs,
8017 so every affected CE must be checked, not only the single CE for
8018 which the CE score was changed in the first place (as every instance
8019 of that CE shares the same CE score, and therefore also can change)!
8021 SCAN_PLAYFIELD(xx, yy)
8023 if (Feld[xx][yy] == element)
8024 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8025 CE_SCORE_GETS_ZERO);
8034 /* ---------- engine actions ------------------------------------------ */
8036 case CA_SET_ENGINE_SCAN_MODE:
8038 InitPlayfieldScanMode(action_arg);
8048 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8050 int old_element = Feld[x][y];
8051 int new_element = get_element_from_group_element(element);
8052 int previous_move_direction = MovDir[x][y];
8053 #if USE_NEW_CUSTOM_VALUE
8054 int last_ce_value = CustomValue[x][y];
8056 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8057 boolean add_player_onto_element = (new_element_is_player &&
8058 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8059 /* this breaks SnakeBite when a snake is
8060 halfway through a door that closes */
8061 /* NOW FIXED AT LEVEL INIT IN files.c */
8062 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8064 IS_WALKABLE(old_element));
8067 /* check if element under the player changes from accessible to unaccessible
8068 (needed for special case of dropping element which then changes) */
8069 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8070 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8078 if (!add_player_onto_element)
8080 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8081 RemoveMovingField(x, y);
8085 Feld[x][y] = new_element;
8087 #if !USE_GFX_RESET_GFX_ANIMATION
8088 ResetGfxAnimation(x, y);
8089 ResetRandomAnimationValue(x, y);
8092 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8093 MovDir[x][y] = previous_move_direction;
8095 #if USE_NEW_CUSTOM_VALUE
8096 if (element_info[new_element].use_last_ce_value)
8097 CustomValue[x][y] = last_ce_value;
8100 InitField_WithBug1(x, y, FALSE);
8102 new_element = Feld[x][y]; /* element may have changed */
8104 #if USE_GFX_RESET_GFX_ANIMATION
8105 ResetGfxAnimation(x, y);
8106 ResetRandomAnimationValue(x, y);
8109 DrawLevelField(x, y);
8111 if (GFX_CRUMBLED(new_element))
8112 DrawLevelFieldCrumbledSandNeighbours(x, y);
8116 /* check if element under the player changes from accessible to unaccessible
8117 (needed for special case of dropping element which then changes) */
8118 /* (must be checked after creating new element for walkable group elements) */
8119 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8120 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8128 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8129 if (new_element_is_player)
8130 RelocatePlayer(x, y, new_element);
8133 ChangeCount[x][y]++; /* count number of changes in the same frame */
8135 TestIfBadThingTouchesPlayer(x, y);
8136 TestIfPlayerTouchesCustomElement(x, y);
8137 TestIfElementTouchesCustomElement(x, y);
8140 static void CreateField(int x, int y, int element)
8142 CreateFieldExt(x, y, element, FALSE);
8145 static void CreateElementFromChange(int x, int y, int element)
8147 element = GET_VALID_RUNTIME_ELEMENT(element);
8149 #if USE_STOP_CHANGED_ELEMENTS
8150 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8152 int old_element = Feld[x][y];
8154 /* prevent changed element from moving in same engine frame
8155 unless both old and new element can either fall or move */
8156 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8157 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8162 CreateFieldExt(x, y, element, TRUE);
8165 static boolean ChangeElement(int x, int y, int element, int page)
8167 struct ElementInfo *ei = &element_info[element];
8168 struct ElementChangeInfo *change = &ei->change_page[page];
8169 int ce_value = CustomValue[x][y];
8170 int ce_score = ei->collect_score;
8172 int old_element = Feld[x][y];
8174 /* always use default change event to prevent running into a loop */
8175 if (ChangeEvent[x][y] == -1)
8176 ChangeEvent[x][y] = CE_DELAY;
8178 if (ChangeEvent[x][y] == CE_DELAY)
8180 /* reset actual trigger element, trigger player and action element */
8181 change->actual_trigger_element = EL_EMPTY;
8182 change->actual_trigger_player = EL_PLAYER_1;
8183 change->actual_trigger_side = CH_SIDE_NONE;
8184 change->actual_trigger_ce_value = 0;
8185 change->actual_trigger_ce_score = 0;
8188 /* do not change elements more than a specified maximum number of changes */
8189 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8192 ChangeCount[x][y]++; /* count number of changes in the same frame */
8194 if (change->explode)
8201 if (change->use_target_content)
8203 boolean complete_replace = TRUE;
8204 boolean can_replace[3][3];
8207 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8210 boolean is_walkable;
8211 boolean is_diggable;
8212 boolean is_collectible;
8213 boolean is_removable;
8214 boolean is_destructible;
8215 int ex = x + xx - 1;
8216 int ey = y + yy - 1;
8217 int content_element = change->target_content.e[xx][yy];
8220 can_replace[xx][yy] = TRUE;
8222 if (ex == x && ey == y) /* do not check changing element itself */
8225 if (content_element == EL_EMPTY_SPACE)
8227 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8232 if (!IN_LEV_FIELD(ex, ey))
8234 can_replace[xx][yy] = FALSE;
8235 complete_replace = FALSE;
8242 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8243 e = MovingOrBlocked2Element(ex, ey);
8245 is_empty = (IS_FREE(ex, ey) ||
8246 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8248 is_walkable = (is_empty || IS_WALKABLE(e));
8249 is_diggable = (is_empty || IS_DIGGABLE(e));
8250 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8251 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8252 is_removable = (is_diggable || is_collectible);
8254 can_replace[xx][yy] =
8255 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8256 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8257 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8258 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8259 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8260 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8261 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8263 if (!can_replace[xx][yy])
8264 complete_replace = FALSE;
8267 if (!change->only_if_complete || complete_replace)
8269 boolean something_has_changed = FALSE;
8271 if (change->only_if_complete && change->use_random_replace &&
8272 RND(100) < change->random_percentage)
8275 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8277 int ex = x + xx - 1;
8278 int ey = y + yy - 1;
8279 int content_element;
8281 if (can_replace[xx][yy] && (!change->use_random_replace ||
8282 RND(100) < change->random_percentage))
8284 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8285 RemoveMovingField(ex, ey);
8287 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8289 content_element = change->target_content.e[xx][yy];
8290 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8291 ce_value, ce_score);
8293 CreateElementFromChange(ex, ey, target_element);
8295 something_has_changed = TRUE;
8297 /* for symmetry reasons, freeze newly created border elements */
8298 if (ex != x || ey != y)
8299 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8303 if (something_has_changed)
8305 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8306 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8312 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
8313 ce_value, ce_score);
8315 if (element == EL_DIAGONAL_GROWING ||
8316 element == EL_DIAGONAL_SHRINKING)
8318 target_element = Store[x][y];
8320 Store[x][y] = EL_EMPTY;
8323 CreateElementFromChange(x, y, target_element);
8325 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8326 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8329 /* this uses direct change before indirect change */
8330 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8335 #if USE_NEW_DELAYED_ACTION
8337 static void HandleElementChange(int x, int y, int page)
8339 int element = MovingOrBlocked2Element(x, y);
8340 struct ElementInfo *ei = &element_info[element];
8341 struct ElementChangeInfo *change = &ei->change_page[page];
8344 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8345 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8348 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8349 x, y, element, element_info[element].token_name);
8350 printf("HandleElementChange(): This should never happen!\n");
8355 /* this can happen with classic bombs on walkable, changing elements */
8356 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8359 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8360 ChangeDelay[x][y] = 0;
8366 if (ChangeDelay[x][y] == 0) /* initialize element change */
8368 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8370 if (change->can_change)
8372 ResetGfxAnimation(x, y);
8373 ResetRandomAnimationValue(x, y);
8375 if (change->pre_change_function)
8376 change->pre_change_function(x, y);
8380 ChangeDelay[x][y]--;
8382 if (ChangeDelay[x][y] != 0) /* continue element change */
8384 if (change->can_change)
8386 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8388 if (IS_ANIMATED(graphic))
8389 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8391 if (change->change_function)
8392 change->change_function(x, y);
8395 else /* finish element change */
8397 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8399 page = ChangePage[x][y];
8400 ChangePage[x][y] = -1;
8402 change = &ei->change_page[page];
8405 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8407 ChangeDelay[x][y] = 1; /* try change after next move step */
8408 ChangePage[x][y] = page; /* remember page to use for change */
8413 if (change->can_change)
8415 if (ChangeElement(x, y, element, page))
8417 if (change->post_change_function)
8418 change->post_change_function(x, y);
8422 if (change->has_action)
8423 ExecuteCustomElementAction(x, y, element, page);
8429 static void HandleElementChange(int x, int y, int page)
8431 int element = MovingOrBlocked2Element(x, y);
8432 struct ElementInfo *ei = &element_info[element];
8433 struct ElementChangeInfo *change = &ei->change_page[page];
8436 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8439 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8440 x, y, element, element_info[element].token_name);
8441 printf("HandleElementChange(): This should never happen!\n");
8446 /* this can happen with classic bombs on walkable, changing elements */
8447 if (!CAN_CHANGE(element))
8450 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8451 ChangeDelay[x][y] = 0;
8457 if (ChangeDelay[x][y] == 0) /* initialize element change */
8459 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8461 ResetGfxAnimation(x, y);
8462 ResetRandomAnimationValue(x, y);
8464 if (change->pre_change_function)
8465 change->pre_change_function(x, y);
8468 ChangeDelay[x][y]--;
8470 if (ChangeDelay[x][y] != 0) /* continue element change */
8472 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8474 if (IS_ANIMATED(graphic))
8475 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8477 if (change->change_function)
8478 change->change_function(x, y);
8480 else /* finish element change */
8482 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8484 page = ChangePage[x][y];
8485 ChangePage[x][y] = -1;
8487 change = &ei->change_page[page];
8490 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8492 ChangeDelay[x][y] = 1; /* try change after next move step */
8493 ChangePage[x][y] = page; /* remember page to use for change */
8498 if (ChangeElement(x, y, element, page))
8500 if (change->post_change_function)
8501 change->post_change_function(x, y);
8508 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8509 int trigger_element,
8515 boolean change_done_any = FALSE;
8516 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8519 if (!(trigger_events[trigger_element][trigger_event]))
8522 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8524 int element = EL_CUSTOM_START + i;
8525 boolean change_done = FALSE;
8528 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8529 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8532 for (p = 0; p < element_info[element].num_change_pages; p++)
8534 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8536 if (change->can_change_or_has_action &&
8537 change->has_event[trigger_event] &&
8538 change->trigger_side & trigger_side &&
8539 change->trigger_player & trigger_player &&
8540 change->trigger_page & trigger_page_bits &&
8541 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8543 change->actual_trigger_element = trigger_element;
8544 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8545 change->actual_trigger_side = trigger_side;
8546 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8547 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8549 if ((change->can_change && !change_done) || change->has_action)
8553 SCAN_PLAYFIELD(x, y)
8555 if (Feld[x][y] == element)
8557 if (change->can_change && !change_done)
8559 ChangeDelay[x][y] = 1;
8560 ChangeEvent[x][y] = trigger_event;
8562 HandleElementChange(x, y, p);
8564 #if USE_NEW_DELAYED_ACTION
8565 else if (change->has_action)
8567 ExecuteCustomElementAction(x, y, element, p);
8568 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8571 if (change->has_action)
8573 ExecuteCustomElementAction(x, y, element, p);
8574 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8580 if (change->can_change)
8583 change_done_any = TRUE;
8590 return change_done_any;
8593 static boolean CheckElementChangeExt(int x, int y,
8595 int trigger_element,
8600 boolean change_done = FALSE;
8603 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8604 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8607 if (Feld[x][y] == EL_BLOCKED)
8609 Blocked2Moving(x, y, &x, &y);
8610 element = Feld[x][y];
8614 /* check if element has already changed */
8615 if (Feld[x][y] != element)
8618 /* check if element has already changed or is about to change after moving */
8619 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8620 Feld[x][y] != element) ||
8622 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8623 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8624 ChangePage[x][y] != -1)))
8628 for (p = 0; p < element_info[element].num_change_pages; p++)
8630 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8632 boolean check_trigger_element =
8633 (trigger_event == CE_TOUCHING_X ||
8634 trigger_event == CE_HITTING_X ||
8635 trigger_event == CE_HIT_BY_X);
8637 if (change->can_change_or_has_action &&
8638 change->has_event[trigger_event] &&
8639 change->trigger_side & trigger_side &&
8640 change->trigger_player & trigger_player &&
8641 (!check_trigger_element ||
8642 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8644 change->actual_trigger_element = trigger_element;
8645 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8646 change->actual_trigger_side = trigger_side;
8647 change->actual_trigger_ce_value = CustomValue[x][y];
8648 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8650 /* special case: trigger element not at (x,y) position for some events */
8651 if (check_trigger_element)
8663 { 0, 0 }, { 0, 0 }, { 0, 0 },
8667 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8668 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8670 change->actual_trigger_ce_value = CustomValue[xx][yy];
8671 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8674 if (change->can_change && !change_done)
8676 ChangeDelay[x][y] = 1;
8677 ChangeEvent[x][y] = trigger_event;
8679 HandleElementChange(x, y, p);
8683 #if USE_NEW_DELAYED_ACTION
8684 else if (change->has_action)
8686 ExecuteCustomElementAction(x, y, element, p);
8687 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8690 if (change->has_action)
8692 ExecuteCustomElementAction(x, y, element, p);
8693 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8702 static void PlayPlayerSound(struct PlayerInfo *player)
8704 int jx = player->jx, jy = player->jy;
8705 int sound_element = player->artwork_element;
8706 int last_action = player->last_action_waiting;
8707 int action = player->action_waiting;
8709 if (player->is_waiting)
8711 if (action != last_action)
8712 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8714 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8718 if (action != last_action)
8719 StopSound(element_info[sound_element].sound[last_action]);
8721 if (last_action == ACTION_SLEEPING)
8722 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8726 static void PlayAllPlayersSound()
8730 for (i = 0; i < MAX_PLAYERS; i++)
8731 if (stored_player[i].active)
8732 PlayPlayerSound(&stored_player[i]);
8735 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8737 boolean last_waiting = player->is_waiting;
8738 int move_dir = player->MovDir;
8740 player->dir_waiting = move_dir;
8741 player->last_action_waiting = player->action_waiting;
8745 if (!last_waiting) /* not waiting -> waiting */
8747 player->is_waiting = TRUE;
8749 player->frame_counter_bored =
8751 game.player_boring_delay_fixed +
8752 GetSimpleRandom(game.player_boring_delay_random);
8753 player->frame_counter_sleeping =
8755 game.player_sleeping_delay_fixed +
8756 GetSimpleRandom(game.player_sleeping_delay_random);
8758 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
8761 if (game.player_sleeping_delay_fixed +
8762 game.player_sleeping_delay_random > 0 &&
8763 player->anim_delay_counter == 0 &&
8764 player->post_delay_counter == 0 &&
8765 FrameCounter >= player->frame_counter_sleeping)
8766 player->is_sleeping = TRUE;
8767 else if (game.player_boring_delay_fixed +
8768 game.player_boring_delay_random > 0 &&
8769 FrameCounter >= player->frame_counter_bored)
8770 player->is_bored = TRUE;
8772 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8773 player->is_bored ? ACTION_BORING :
8776 if (player->is_sleeping && player->use_murphy)
8778 /* special case for sleeping Murphy when leaning against non-free tile */
8780 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
8781 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
8782 !IS_MOVING(player->jx - 1, player->jy)))
8784 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
8785 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
8786 !IS_MOVING(player->jx + 1, player->jy)))
8787 move_dir = MV_RIGHT;
8789 player->is_sleeping = FALSE;
8791 player->dir_waiting = move_dir;
8794 if (player->is_sleeping)
8796 if (player->num_special_action_sleeping > 0)
8798 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8800 int last_special_action = player->special_action_sleeping;
8801 int num_special_action = player->num_special_action_sleeping;
8802 int special_action =
8803 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8804 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8805 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8806 last_special_action + 1 : ACTION_SLEEPING);
8807 int special_graphic =
8808 el_act_dir2img(player->artwork_element, special_action, move_dir);
8810 player->anim_delay_counter =
8811 graphic_info[special_graphic].anim_delay_fixed +
8812 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8813 player->post_delay_counter =
8814 graphic_info[special_graphic].post_delay_fixed +
8815 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8817 player->special_action_sleeping = special_action;
8820 if (player->anim_delay_counter > 0)
8822 player->action_waiting = player->special_action_sleeping;
8823 player->anim_delay_counter--;
8825 else if (player->post_delay_counter > 0)
8827 player->post_delay_counter--;
8831 else if (player->is_bored)
8833 if (player->num_special_action_bored > 0)
8835 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8837 int special_action =
8838 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
8839 int special_graphic =
8840 el_act_dir2img(player->artwork_element, special_action, move_dir);
8842 player->anim_delay_counter =
8843 graphic_info[special_graphic].anim_delay_fixed +
8844 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8845 player->post_delay_counter =
8846 graphic_info[special_graphic].post_delay_fixed +
8847 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8849 player->special_action_bored = special_action;
8852 if (player->anim_delay_counter > 0)
8854 player->action_waiting = player->special_action_bored;
8855 player->anim_delay_counter--;
8857 else if (player->post_delay_counter > 0)
8859 player->post_delay_counter--;
8864 else if (last_waiting) /* waiting -> not waiting */
8866 player->is_waiting = FALSE;
8867 player->is_bored = FALSE;
8868 player->is_sleeping = FALSE;
8870 player->frame_counter_bored = -1;
8871 player->frame_counter_sleeping = -1;
8873 player->anim_delay_counter = 0;
8874 player->post_delay_counter = 0;
8876 player->dir_waiting = player->MovDir;
8877 player->action_waiting = ACTION_DEFAULT;
8879 player->special_action_bored = ACTION_DEFAULT;
8880 player->special_action_sleeping = ACTION_DEFAULT;
8884 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8886 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8887 int left = player_action & JOY_LEFT;
8888 int right = player_action & JOY_RIGHT;
8889 int up = player_action & JOY_UP;
8890 int down = player_action & JOY_DOWN;
8891 int button1 = player_action & JOY_BUTTON_1;
8892 int button2 = player_action & JOY_BUTTON_2;
8893 int dx = (left ? -1 : right ? 1 : 0);
8894 int dy = (up ? -1 : down ? 1 : 0);
8896 if (!player->active || tape.pausing)
8902 snapped = SnapField(player, dx, dy);
8906 dropped = DropElement(player);
8908 moved = MovePlayer(player, dx, dy);
8911 if (tape.single_step && tape.recording && !tape.pausing)
8913 if (button1 || (dropped && !moved))
8915 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8916 SnapField(player, 0, 0); /* stop snapping */
8920 SetPlayerWaiting(player, FALSE);
8922 return player_action;
8926 /* no actions for this player (no input at player's configured device) */
8928 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8929 SnapField(player, 0, 0);
8930 CheckGravityMovementWhenNotMoving(player);
8932 if (player->MovPos == 0)
8933 SetPlayerWaiting(player, TRUE);
8935 if (player->MovPos == 0) /* needed for tape.playing */
8936 player->is_moving = FALSE;
8938 player->is_dropping = FALSE;
8939 player->is_dropping_pressed = FALSE;
8940 player->drop_pressed_delay = 0;
8946 static void CheckLevelTime()
8950 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8952 if (level.native_em_level->lev->home == 0) /* all players at home */
8954 PlayerWins(local_player);
8956 AllPlayersGone = TRUE;
8958 level.native_em_level->lev->home = -1;
8961 if (level.native_em_level->ply[0]->alive == 0 &&
8962 level.native_em_level->ply[1]->alive == 0 &&
8963 level.native_em_level->ply[2]->alive == 0 &&
8964 level.native_em_level->ply[3]->alive == 0) /* all dead */
8965 AllPlayersGone = TRUE;
8968 if (TimeFrames >= FRAMES_PER_SECOND)
8973 for (i = 0; i < MAX_PLAYERS; i++)
8975 struct PlayerInfo *player = &stored_player[i];
8977 if (SHIELD_ON(player))
8979 player->shield_normal_time_left--;
8981 if (player->shield_deadly_time_left > 0)
8982 player->shield_deadly_time_left--;
8986 if (!local_player->LevelSolved && !level.use_step_counter)
8994 if (TimeLeft <= 10 && setup.time_limit)
8995 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
8997 DrawGameValue_Time(TimeLeft);
8999 if (!TimeLeft && setup.time_limit)
9001 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9002 level.native_em_level->lev->killed_out_of_time = TRUE;
9004 for (i = 0; i < MAX_PLAYERS; i++)
9005 KillPlayer(&stored_player[i]);
9008 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9009 DrawGameValue_Time(TimePlayed);
9011 level.native_em_level->lev->time =
9012 (level.time == 0 ? TimePlayed : TimeLeft);
9015 if (tape.recording || tape.playing)
9016 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9020 void AdvanceFrameAndPlayerCounters(int player_nr)
9024 /* advance frame counters (global frame counter and time frame counter) */
9028 /* advance player counters (counters for move delay, move animation etc.) */
9029 for (i = 0; i < MAX_PLAYERS; i++)
9031 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9032 int move_delay_value = stored_player[i].move_delay_value;
9033 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9035 if (!advance_player_counters) /* not all players may be affected */
9038 #if USE_NEW_PLAYER_ANIM
9039 if (move_frames == 0) /* less than one move per game frame */
9041 int stepsize = TILEX / move_delay_value;
9042 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9043 int count = (stored_player[i].is_moving ?
9044 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9046 if (count % delay == 0)
9051 stored_player[i].Frame += move_frames;
9053 if (stored_player[i].MovPos != 0)
9054 stored_player[i].StepFrame += move_frames;
9056 if (stored_player[i].move_delay > 0)
9057 stored_player[i].move_delay--;
9059 /* due to bugs in previous versions, counter must count up, not down */
9060 if (stored_player[i].push_delay != -1)
9061 stored_player[i].push_delay++;
9063 if (stored_player[i].drop_delay > 0)
9064 stored_player[i].drop_delay--;
9066 if (stored_player[i].is_dropping_pressed)
9067 stored_player[i].drop_pressed_delay++;
9071 void StartGameActions(boolean init_network_game, boolean record_tape,
9074 unsigned long new_random_seed = InitRND(random_seed);
9077 TapeStartRecording(new_random_seed);
9079 #if defined(NETWORK_AVALIABLE)
9080 if (init_network_game)
9082 SendToServer_StartPlaying();
9093 static unsigned long game_frame_delay = 0;
9094 unsigned long game_frame_delay_value;
9095 byte *recorded_player_action;
9096 byte summarized_player_action = 0;
9097 byte tape_action[MAX_PLAYERS];
9100 if (game.restart_level)
9101 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9103 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9105 if (level.native_em_level->lev->home == 0) /* all players at home */
9107 PlayerWins(local_player);
9109 AllPlayersGone = TRUE;
9111 level.native_em_level->lev->home = -1;
9114 if (level.native_em_level->ply[0]->alive == 0 &&
9115 level.native_em_level->ply[1]->alive == 0 &&
9116 level.native_em_level->ply[2]->alive == 0 &&
9117 level.native_em_level->ply[3]->alive == 0) /* all dead */
9118 AllPlayersGone = TRUE;
9121 if (local_player->LevelSolved)
9124 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9127 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9130 game_frame_delay_value =
9131 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9133 if (tape.playing && tape.warp_forward && !tape.pausing)
9134 game_frame_delay_value = 0;
9136 /* ---------- main game synchronization point ---------- */
9138 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9140 if (network_playing && !network_player_action_received)
9142 /* try to get network player actions in time */
9144 #if defined(NETWORK_AVALIABLE)
9145 /* last chance to get network player actions without main loop delay */
9149 /* game was quit by network peer */
9150 if (game_status != GAME_MODE_PLAYING)
9153 if (!network_player_action_received)
9154 return; /* failed to get network player actions in time */
9156 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9162 /* at this point we know that we really continue executing the game */
9164 network_player_action_received = FALSE;
9166 /* when playing tape, read previously recorded player input from tape data */
9167 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9170 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9175 if (tape.set_centered_player)
9177 game.centered_player_nr_next = tape.centered_player_nr_next;
9178 game.set_centered_player = TRUE;
9181 for (i = 0; i < MAX_PLAYERS; i++)
9183 summarized_player_action |= stored_player[i].action;
9185 if (!network_playing)
9186 stored_player[i].effective_action = stored_player[i].action;
9189 #if defined(NETWORK_AVALIABLE)
9190 if (network_playing)
9191 SendToServer_MovePlayer(summarized_player_action);
9194 if (!options.network && !setup.team_mode)
9195 local_player->effective_action = summarized_player_action;
9197 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9199 for (i = 0; i < MAX_PLAYERS; i++)
9200 stored_player[i].effective_action =
9201 (i == game.centered_player_nr ? summarized_player_action : 0);
9204 if (recorded_player_action != NULL)
9205 for (i = 0; i < MAX_PLAYERS; i++)
9206 stored_player[i].effective_action = recorded_player_action[i];
9208 for (i = 0; i < MAX_PLAYERS; i++)
9210 tape_action[i] = stored_player[i].effective_action;
9212 /* (this can only happen in the R'n'D game engine) */
9213 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9214 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9217 /* only record actions from input devices, but not programmed actions */
9219 TapeRecordAction(tape_action);
9221 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9223 GameActions_EM_Main();
9231 void GameActions_EM_Main()
9233 byte effective_action[MAX_PLAYERS];
9234 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9237 for (i = 0; i < MAX_PLAYERS; i++)
9238 effective_action[i] = stored_player[i].effective_action;
9240 GameActions_EM(effective_action, warp_mode);
9244 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9247 void GameActions_RND()
9249 int magic_wall_x = 0, magic_wall_y = 0;
9250 int i, x, y, element, graphic;
9252 InitPlayfieldScanModeVars();
9254 #if USE_ONE_MORE_CHANGE_PER_FRAME
9255 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9257 SCAN_PLAYFIELD(x, y)
9259 ChangeCount[x][y] = 0;
9260 ChangeEvent[x][y] = -1;
9265 if (game.set_centered_player)
9267 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9269 /* switching to "all players" only possible if all players fit to screen */
9270 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9272 game.centered_player_nr_next = game.centered_player_nr;
9273 game.set_centered_player = FALSE;
9276 /* do not switch focus to non-existing (or non-active) player */
9277 if (game.centered_player_nr_next >= 0 &&
9278 !stored_player[game.centered_player_nr_next].active)
9280 game.centered_player_nr_next = game.centered_player_nr;
9281 game.set_centered_player = FALSE;
9285 if (game.set_centered_player &&
9286 ScreenMovPos == 0) /* screen currently aligned at tile position */
9290 if (game.centered_player_nr_next == -1)
9292 setScreenCenteredToAllPlayers(&sx, &sy);
9296 sx = stored_player[game.centered_player_nr_next].jx;
9297 sy = stored_player[game.centered_player_nr_next].jy;
9300 game.centered_player_nr = game.centered_player_nr_next;
9301 game.set_centered_player = FALSE;
9303 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9304 DrawGameDoorValues();
9307 for (i = 0; i < MAX_PLAYERS; i++)
9309 int actual_player_action = stored_player[i].effective_action;
9312 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9313 - rnd_equinox_tetrachloride 048
9314 - rnd_equinox_tetrachloride_ii 096
9315 - rnd_emanuel_schmieg 002
9316 - doctor_sloan_ww 001, 020
9318 if (stored_player[i].MovPos == 0)
9319 CheckGravityMovement(&stored_player[i]);
9322 /* overwrite programmed action with tape action */
9323 if (stored_player[i].programmed_action)
9324 actual_player_action = stored_player[i].programmed_action;
9326 PlayerActions(&stored_player[i], actual_player_action);
9328 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9331 ScrollScreen(NULL, SCROLL_GO_ON);
9333 /* for backwards compatibility, the following code emulates a fixed bug that
9334 occured when pushing elements (causing elements that just made their last
9335 pushing step to already (if possible) make their first falling step in the
9336 same game frame, which is bad); this code is also needed to use the famous
9337 "spring push bug" which is used in older levels and might be wanted to be
9338 used also in newer levels, but in this case the buggy pushing code is only
9339 affecting the "spring" element and no other elements */
9341 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9343 for (i = 0; i < MAX_PLAYERS; i++)
9345 struct PlayerInfo *player = &stored_player[i];
9349 if (player->active && player->is_pushing && player->is_moving &&
9351 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9352 Feld[x][y] == EL_SPRING))
9354 ContinueMoving(x, y);
9356 /* continue moving after pushing (this is actually a bug) */
9357 if (!IS_MOVING(x, y))
9365 SCAN_PLAYFIELD(x, y)
9367 ChangeCount[x][y] = 0;
9368 ChangeEvent[x][y] = -1;
9370 /* this must be handled before main playfield loop */
9371 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9374 if (MovDelay[x][y] <= 0)
9378 #if USE_NEW_SNAP_DELAY
9379 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9382 if (MovDelay[x][y] <= 0)
9385 DrawLevelField(x, y);
9387 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9393 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9395 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9396 printf("GameActions(): This should never happen!\n");
9398 ChangePage[x][y] = -1;
9403 if (WasJustMoving[x][y] > 0)
9404 WasJustMoving[x][y]--;
9405 if (WasJustFalling[x][y] > 0)
9406 WasJustFalling[x][y]--;
9407 if (CheckCollision[x][y] > 0)
9408 CheckCollision[x][y]--;
9412 /* reset finished pushing action (not done in ContinueMoving() to allow
9413 continuous pushing animation for elements with zero push delay) */
9414 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9416 ResetGfxAnimation(x, y);
9417 DrawLevelField(x, y);
9421 if (IS_BLOCKED(x, y))
9425 Blocked2Moving(x, y, &oldx, &oldy);
9426 if (!IS_MOVING(oldx, oldy))
9428 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9429 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9430 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9431 printf("GameActions(): This should never happen!\n");
9437 SCAN_PLAYFIELD(x, y)
9439 element = Feld[x][y];
9440 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9442 ResetGfxFrame(x, y, TRUE);
9444 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9445 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9446 ResetRandomAnimationValue(x, y);
9448 SetRandomAnimationValue(x, y);
9450 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9452 if (IS_INACTIVE(element))
9454 if (IS_ANIMATED(graphic))
9455 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9460 /* this may take place after moving, so 'element' may have changed */
9461 if (IS_CHANGING(x, y) &&
9462 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9464 int page = element_info[element].event_page_nr[CE_DELAY];
9467 HandleElementChange(x, y, page);
9469 if (CAN_CHANGE(element))
9470 HandleElementChange(x, y, page);
9472 if (HAS_ACTION(element))
9473 ExecuteCustomElementAction(x, y, element, page);
9476 element = Feld[x][y];
9477 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9480 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9484 element = Feld[x][y];
9485 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9487 if (IS_ANIMATED(graphic) &&
9490 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9492 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9493 DrawTwinkleOnField(x, y);
9495 else if ((element == EL_ACID ||
9496 element == EL_EXIT_OPEN ||
9497 element == EL_SP_EXIT_OPEN ||
9498 element == EL_SP_TERMINAL ||
9499 element == EL_SP_TERMINAL_ACTIVE ||
9500 element == EL_EXTRA_TIME ||
9501 element == EL_SHIELD_NORMAL ||
9502 element == EL_SHIELD_DEADLY) &&
9503 IS_ANIMATED(graphic))
9504 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9505 else if (IS_MOVING(x, y))
9506 ContinueMoving(x, y);
9507 else if (IS_ACTIVE_BOMB(element))
9508 CheckDynamite(x, y);
9509 else if (element == EL_AMOEBA_GROWING)
9510 AmoebeWaechst(x, y);
9511 else if (element == EL_AMOEBA_SHRINKING)
9512 AmoebaDisappearing(x, y);
9514 #if !USE_NEW_AMOEBA_CODE
9515 else if (IS_AMOEBALIVE(element))
9516 AmoebeAbleger(x, y);
9519 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9521 else if (element == EL_EXIT_CLOSED)
9523 else if (element == EL_SP_EXIT_CLOSED)
9525 else if (element == EL_EXPANDABLE_WALL_GROWING)
9527 else if (element == EL_EXPANDABLE_WALL ||
9528 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9529 element == EL_EXPANDABLE_WALL_VERTICAL ||
9530 element == EL_EXPANDABLE_WALL_ANY ||
9531 element == EL_BD_EXPANDABLE_WALL)
9533 else if (element == EL_FLAMES)
9534 CheckForDragon(x, y);
9535 else if (element == EL_EXPLOSION)
9536 ; /* drawing of correct explosion animation is handled separately */
9537 else if (element == EL_ELEMENT_SNAPPING ||
9538 element == EL_DIAGONAL_SHRINKING ||
9539 element == EL_DIAGONAL_GROWING)
9541 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9543 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9545 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9546 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9548 if (IS_BELT_ACTIVE(element))
9549 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9551 if (game.magic_wall_active)
9553 int jx = local_player->jx, jy = local_player->jy;
9555 /* play the element sound at the position nearest to the player */
9556 if ((element == EL_MAGIC_WALL_FULL ||
9557 element == EL_MAGIC_WALL_ACTIVE ||
9558 element == EL_MAGIC_WALL_EMPTYING ||
9559 element == EL_BD_MAGIC_WALL_FULL ||
9560 element == EL_BD_MAGIC_WALL_ACTIVE ||
9561 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9562 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9570 #if USE_NEW_AMOEBA_CODE
9571 /* new experimental amoeba growth stuff */
9572 if (!(FrameCounter % 8))
9574 static unsigned long random = 1684108901;
9576 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9578 x = RND(lev_fieldx);
9579 y = RND(lev_fieldy);
9580 element = Feld[x][y];
9582 if (!IS_PLAYER(x,y) &&
9583 (element == EL_EMPTY ||
9584 CAN_GROW_INTO(element) ||
9585 element == EL_QUICKSAND_EMPTY ||
9586 element == EL_ACID_SPLASH_LEFT ||
9587 element == EL_ACID_SPLASH_RIGHT))
9589 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9590 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9591 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9592 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9593 Feld[x][y] = EL_AMOEBA_DROP;
9596 random = random * 129 + 1;
9602 if (game.explosions_delayed)
9605 game.explosions_delayed = FALSE;
9607 SCAN_PLAYFIELD(x, y)
9609 element = Feld[x][y];
9611 if (ExplodeField[x][y])
9612 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9613 else if (element == EL_EXPLOSION)
9614 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9616 ExplodeField[x][y] = EX_TYPE_NONE;
9619 game.explosions_delayed = TRUE;
9622 if (game.magic_wall_active)
9624 if (!(game.magic_wall_time_left % 4))
9626 int element = Feld[magic_wall_x][magic_wall_y];
9628 if (element == EL_BD_MAGIC_WALL_FULL ||
9629 element == EL_BD_MAGIC_WALL_ACTIVE ||
9630 element == EL_BD_MAGIC_WALL_EMPTYING)
9631 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9633 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9636 if (game.magic_wall_time_left > 0)
9638 game.magic_wall_time_left--;
9639 if (!game.magic_wall_time_left)
9641 SCAN_PLAYFIELD(x, y)
9643 element = Feld[x][y];
9645 if (element == EL_MAGIC_WALL_ACTIVE ||
9646 element == EL_MAGIC_WALL_FULL)
9648 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9649 DrawLevelField(x, y);
9651 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9652 element == EL_BD_MAGIC_WALL_FULL)
9654 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9655 DrawLevelField(x, y);
9659 game.magic_wall_active = FALSE;
9664 if (game.light_time_left > 0)
9666 game.light_time_left--;
9668 if (game.light_time_left == 0)
9669 RedrawAllLightSwitchesAndInvisibleElements();
9672 if (game.timegate_time_left > 0)
9674 game.timegate_time_left--;
9676 if (game.timegate_time_left == 0)
9677 CloseAllOpenTimegates();
9680 if (game.lenses_time_left > 0)
9682 game.lenses_time_left--;
9684 if (game.lenses_time_left == 0)
9685 RedrawAllInvisibleElementsForLenses();
9688 if (game.magnify_time_left > 0)
9690 game.magnify_time_left--;
9692 if (game.magnify_time_left == 0)
9693 RedrawAllInvisibleElementsForMagnifier();
9696 for (i = 0; i < MAX_PLAYERS; i++)
9698 struct PlayerInfo *player = &stored_player[i];
9700 if (SHIELD_ON(player))
9702 if (player->shield_deadly_time_left)
9703 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9704 else if (player->shield_normal_time_left)
9705 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9712 PlayAllPlayersSound();
9714 if (options.debug) /* calculate frames per second */
9716 static unsigned long fps_counter = 0;
9717 static int fps_frames = 0;
9718 unsigned long fps_delay_ms = Counter() - fps_counter;
9722 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9724 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9727 fps_counter = Counter();
9730 redraw_mask |= REDRAW_FPS;
9733 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9735 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9737 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9739 local_player->show_envelope = 0;
9742 /* use random number generator in every frame to make it less predictable */
9743 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9747 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9749 int min_x = x, min_y = y, max_x = x, max_y = y;
9752 for (i = 0; i < MAX_PLAYERS; i++)
9754 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9756 if (!stored_player[i].active || &stored_player[i] == player)
9759 min_x = MIN(min_x, jx);
9760 min_y = MIN(min_y, jy);
9761 max_x = MAX(max_x, jx);
9762 max_y = MAX(max_y, jy);
9765 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9768 static boolean AllPlayersInVisibleScreen()
9772 for (i = 0; i < MAX_PLAYERS; i++)
9774 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9776 if (!stored_player[i].active)
9779 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9786 void ScrollLevel(int dx, int dy)
9788 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9791 BlitBitmap(drawto_field, drawto_field,
9792 FX + TILEX * (dx == -1) - softscroll_offset,
9793 FY + TILEY * (dy == -1) - softscroll_offset,
9794 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9795 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9796 FX + TILEX * (dx == 1) - softscroll_offset,
9797 FY + TILEY * (dy == 1) - softscroll_offset);
9801 x = (dx == 1 ? BX1 : BX2);
9802 for (y = BY1; y <= BY2; y++)
9803 DrawScreenField(x, y);
9808 y = (dy == 1 ? BY1 : BY2);
9809 for (x = BX1; x <= BX2; x++)
9810 DrawScreenField(x, y);
9813 redraw_mask |= REDRAW_FIELD;
9816 static boolean canFallDown(struct PlayerInfo *player)
9818 int jx = player->jx, jy = player->jy;
9820 return (IN_LEV_FIELD(jx, jy + 1) &&
9821 (IS_FREE(jx, jy + 1) ||
9822 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9823 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9824 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9827 static boolean canPassField(int x, int y, int move_dir)
9829 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9830 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9831 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9834 int element = Feld[x][y];
9836 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9837 !CAN_MOVE(element) &&
9838 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9839 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9840 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9843 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9845 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9846 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9847 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9851 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9852 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9853 (IS_DIGGABLE(Feld[newx][newy]) ||
9854 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9855 canPassField(newx, newy, move_dir)));
9858 static void CheckGravityMovement(struct PlayerInfo *player)
9860 #if USE_PLAYER_GRAVITY
9861 if (player->gravity && !player->programmed_action)
9863 if (game.gravity && !player->programmed_action)
9866 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9867 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9868 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
9869 int jx = player->jx, jy = player->jy;
9870 boolean player_is_moving_to_valid_field =
9871 (!player_is_snapping &&
9872 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9873 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9874 boolean player_can_fall_down = canFallDown(player);
9876 if (player_can_fall_down &&
9877 !player_is_moving_to_valid_field)
9878 player->programmed_action = MV_DOWN;
9882 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9884 return CheckGravityMovement(player);
9886 #if USE_PLAYER_GRAVITY
9887 if (player->gravity && !player->programmed_action)
9889 if (game.gravity && !player->programmed_action)
9892 int jx = player->jx, jy = player->jy;
9893 boolean field_under_player_is_free =
9894 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9895 boolean player_is_standing_on_valid_field =
9896 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9897 (IS_WALKABLE(Feld[jx][jy]) &&
9898 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9900 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9901 player->programmed_action = MV_DOWN;
9907 -----------------------------------------------------------------------------
9908 dx, dy: direction (non-diagonal) to try to move the player to
9909 real_dx, real_dy: direction as read from input device (can be diagonal)
9912 boolean MovePlayerOneStep(struct PlayerInfo *player,
9913 int dx, int dy, int real_dx, int real_dy)
9915 int jx = player->jx, jy = player->jy;
9916 int new_jx = jx + dx, new_jy = jy + dy;
9917 #if !USE_FIXED_DONT_RUN_INTO
9921 boolean player_can_move = !player->cannot_move;
9923 if (!player->active || (!dx && !dy))
9924 return MP_NO_ACTION;
9926 player->MovDir = (dx < 0 ? MV_LEFT :
9929 dy > 0 ? MV_DOWN : MV_NONE);
9931 if (!IN_LEV_FIELD(new_jx, new_jy))
9932 return MP_NO_ACTION;
9934 if (!player_can_move)
9936 if (player->MovPos == 0)
9938 player->is_moving = FALSE;
9939 player->is_digging = FALSE;
9940 player->is_collecting = FALSE;
9941 player->is_snapping = FALSE;
9942 player->is_pushing = FALSE;
9947 if (!options.network && game.centered_player_nr == -1 &&
9948 !AllPlayersInSight(player, new_jx, new_jy))
9949 return MP_NO_ACTION;
9951 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9952 return MP_NO_ACTION;
9955 #if !USE_FIXED_DONT_RUN_INTO
9956 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9958 /* (moved to DigField()) */
9959 if (player_can_move && DONT_RUN_INTO(element))
9961 if (element == EL_ACID && dx == 0 && dy == 1)
9963 SplashAcid(new_jx, new_jy);
9964 Feld[jx][jy] = EL_PLAYER_1;
9965 InitMovingField(jx, jy, MV_DOWN);
9966 Store[jx][jy] = EL_ACID;
9967 ContinueMoving(jx, jy);
9971 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9977 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9978 if (can_move != MP_MOVING)
9981 /* check if DigField() has caused relocation of the player */
9982 if (player->jx != jx || player->jy != jy)
9983 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
9985 StorePlayer[jx][jy] = 0;
9986 player->last_jx = jx;
9987 player->last_jy = jy;
9988 player->jx = new_jx;
9989 player->jy = new_jy;
9990 StorePlayer[new_jx][new_jy] = player->element_nr;
9992 if (player->move_delay_value_next != -1)
9994 player->move_delay_value = player->move_delay_value_next;
9995 player->move_delay_value_next = -1;
9999 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10001 player->step_counter++;
10003 PlayerVisit[jx][jy] = FrameCounter;
10005 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10006 player->is_moving = TRUE;
10010 /* should better be called in MovePlayer(), but this breaks some tapes */
10011 ScrollPlayer(player, SCROLL_INIT);
10017 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10019 int jx = player->jx, jy = player->jy;
10020 int old_jx = jx, old_jy = jy;
10021 int moved = MP_NO_ACTION;
10023 if (!player->active)
10028 if (player->MovPos == 0)
10030 player->is_moving = FALSE;
10031 player->is_digging = FALSE;
10032 player->is_collecting = FALSE;
10033 player->is_snapping = FALSE;
10034 player->is_pushing = FALSE;
10040 if (player->move_delay > 0)
10043 player->move_delay = -1; /* set to "uninitialized" value */
10045 /* store if player is automatically moved to next field */
10046 player->is_auto_moving = (player->programmed_action != MV_NONE);
10048 /* remove the last programmed player action */
10049 player->programmed_action = 0;
10051 if (player->MovPos)
10053 /* should only happen if pre-1.2 tape recordings are played */
10054 /* this is only for backward compatibility */
10056 int original_move_delay_value = player->move_delay_value;
10059 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10063 /* scroll remaining steps with finest movement resolution */
10064 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10066 while (player->MovPos)
10068 ScrollPlayer(player, SCROLL_GO_ON);
10069 ScrollScreen(NULL, SCROLL_GO_ON);
10071 AdvanceFrameAndPlayerCounters(player->index_nr);
10077 player->move_delay_value = original_move_delay_value;
10080 player->is_active = FALSE;
10082 if (player->last_move_dir & MV_HORIZONTAL)
10084 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10085 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10089 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10090 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10093 #if USE_FIXED_BORDER_RUNNING_GFX
10094 if (!moved && !player->is_active)
10096 player->is_moving = FALSE;
10097 player->is_digging = FALSE;
10098 player->is_collecting = FALSE;
10099 player->is_snapping = FALSE;
10100 player->is_pushing = FALSE;
10108 if (moved & MP_MOVING && !ScreenMovPos &&
10109 (player->index_nr == game.centered_player_nr ||
10110 game.centered_player_nr == -1))
10112 if (moved & MP_MOVING && !ScreenMovPos &&
10113 (player == local_player || !options.network))
10116 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10117 int offset = (setup.scroll_delay ? 3 : 0);
10119 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10121 /* actual player has left the screen -- scroll in that direction */
10122 if (jx != old_jx) /* player has moved horizontally */
10123 scroll_x += (jx - old_jx);
10124 else /* player has moved vertically */
10125 scroll_y += (jy - old_jy);
10129 if (jx != old_jx) /* player has moved horizontally */
10131 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10132 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10133 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10135 /* don't scroll over playfield boundaries */
10136 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10137 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10139 /* don't scroll more than one field at a time */
10140 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10142 /* don't scroll against the player's moving direction */
10143 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10144 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10145 scroll_x = old_scroll_x;
10147 else /* player has moved vertically */
10149 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10150 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10151 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10153 /* don't scroll over playfield boundaries */
10154 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10155 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10157 /* don't scroll more than one field at a time */
10158 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10160 /* don't scroll against the player's moving direction */
10161 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10162 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10163 scroll_y = old_scroll_y;
10167 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10170 if (!options.network && game.centered_player_nr == -1 &&
10171 !AllPlayersInVisibleScreen())
10173 scroll_x = old_scroll_x;
10174 scroll_y = old_scroll_y;
10178 if (!options.network && !AllPlayersInVisibleScreen())
10180 scroll_x = old_scroll_x;
10181 scroll_y = old_scroll_y;
10186 ScrollScreen(player, SCROLL_INIT);
10187 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10192 player->StepFrame = 0;
10194 if (moved & MP_MOVING)
10196 if (old_jx != jx && old_jy == jy)
10197 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10198 else if (old_jx == jx && old_jy != jy)
10199 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10201 DrawLevelField(jx, jy); /* for "crumbled sand" */
10203 player->last_move_dir = player->MovDir;
10204 player->is_moving = TRUE;
10205 player->is_snapping = FALSE;
10206 player->is_switching = FALSE;
10207 player->is_dropping = FALSE;
10208 player->is_dropping_pressed = FALSE;
10209 player->drop_pressed_delay = 0;
10212 /* should better be called here than above, but this breaks some tapes */
10213 ScrollPlayer(player, SCROLL_INIT);
10218 CheckGravityMovementWhenNotMoving(player);
10220 player->is_moving = FALSE;
10222 /* at this point, the player is allowed to move, but cannot move right now
10223 (e.g. because of something blocking the way) -- ensure that the player
10224 is also allowed to move in the next frame (in old versions before 3.1.1,
10225 the player was forced to wait again for eight frames before next try) */
10227 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10228 player->move_delay = 0; /* allow direct movement in the next frame */
10231 if (player->move_delay == -1) /* not yet initialized by DigField() */
10232 player->move_delay = player->move_delay_value;
10234 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10236 TestIfPlayerTouchesBadThing(jx, jy);
10237 TestIfPlayerTouchesCustomElement(jx, jy);
10240 if (!player->active)
10241 RemovePlayer(player);
10246 void ScrollPlayer(struct PlayerInfo *player, int mode)
10248 int jx = player->jx, jy = player->jy;
10249 int last_jx = player->last_jx, last_jy = player->last_jy;
10250 int move_stepsize = TILEX / player->move_delay_value;
10252 #if USE_NEW_PLAYER_SPEED
10253 if (!player->active)
10256 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10259 if (!player->active || player->MovPos == 0)
10263 if (mode == SCROLL_INIT)
10265 player->actual_frame_counter = FrameCounter;
10266 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10268 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10269 Feld[last_jx][last_jy] == EL_EMPTY)
10271 int last_field_block_delay = 0; /* start with no blocking at all */
10272 int block_delay_adjustment = player->block_delay_adjustment;
10274 /* if player blocks last field, add delay for exactly one move */
10275 if (player->block_last_field)
10277 last_field_block_delay += player->move_delay_value;
10279 /* when blocking enabled, prevent moving up despite gravity */
10280 #if USE_PLAYER_GRAVITY
10281 if (player->gravity && player->MovDir == MV_UP)
10282 block_delay_adjustment = -1;
10284 if (game.gravity && player->MovDir == MV_UP)
10285 block_delay_adjustment = -1;
10289 /* add block delay adjustment (also possible when not blocking) */
10290 last_field_block_delay += block_delay_adjustment;
10292 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10293 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10296 #if USE_NEW_PLAYER_SPEED
10297 if (player->MovPos != 0) /* player has not yet reached destination */
10303 else if (!FrameReached(&player->actual_frame_counter, 1))
10306 #if USE_NEW_PLAYER_SPEED
10307 if (player->MovPos != 0)
10309 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10310 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10312 /* before DrawPlayer() to draw correct player graphic for this case */
10313 if (player->MovPos == 0)
10314 CheckGravityMovement(player);
10317 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10318 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10320 /* before DrawPlayer() to draw correct player graphic for this case */
10321 if (player->MovPos == 0)
10322 CheckGravityMovement(player);
10325 if (player->MovPos == 0) /* player reached destination field */
10327 if (player->move_delay_reset_counter > 0)
10329 player->move_delay_reset_counter--;
10331 if (player->move_delay_reset_counter == 0)
10333 /* continue with normal speed after quickly moving through gate */
10334 HALVE_PLAYER_SPEED(player);
10336 /* be able to make the next move without delay */
10337 player->move_delay = 0;
10341 player->last_jx = jx;
10342 player->last_jy = jy;
10344 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10345 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10346 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10348 DrawPlayer(player); /* needed here only to cleanup last field */
10349 RemovePlayer(player);
10351 if (local_player->friends_still_needed == 0 ||
10352 IS_SP_ELEMENT(Feld[jx][jy]))
10353 PlayerWins(player);
10356 /* this breaks one level: "machine", level 000 */
10358 int move_direction = player->MovDir;
10359 int enter_side = MV_DIR_OPPOSITE(move_direction);
10360 int leave_side = move_direction;
10361 int old_jx = last_jx;
10362 int old_jy = last_jy;
10363 int old_element = Feld[old_jx][old_jy];
10364 int new_element = Feld[jx][jy];
10366 if (IS_CUSTOM_ELEMENT(old_element))
10367 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10369 player->index_bit, leave_side);
10371 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10372 CE_PLAYER_LEAVES_X,
10373 player->index_bit, leave_side);
10375 if (IS_CUSTOM_ELEMENT(new_element))
10376 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10377 player->index_bit, enter_side);
10379 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10380 CE_PLAYER_ENTERS_X,
10381 player->index_bit, enter_side);
10383 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10384 CE_MOVE_OF_X, move_direction);
10387 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10389 TestIfPlayerTouchesBadThing(jx, jy);
10390 TestIfPlayerTouchesCustomElement(jx, jy);
10392 /* needed because pushed element has not yet reached its destination,
10393 so it would trigger a change event at its previous field location */
10394 if (!player->is_pushing)
10395 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10397 if (!player->active)
10398 RemovePlayer(player);
10401 if (!local_player->LevelSolved && level.use_step_counter)
10411 if (TimeLeft <= 10 && setup.time_limit)
10412 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10414 DrawGameValue_Time(TimeLeft);
10416 if (!TimeLeft && setup.time_limit)
10417 for (i = 0; i < MAX_PLAYERS; i++)
10418 KillPlayer(&stored_player[i]);
10420 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10421 DrawGameValue_Time(TimePlayed);
10424 if (tape.single_step && tape.recording && !tape.pausing &&
10425 !player->programmed_action)
10426 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10430 void ScrollScreen(struct PlayerInfo *player, int mode)
10432 static unsigned long screen_frame_counter = 0;
10434 if (mode == SCROLL_INIT)
10436 /* set scrolling step size according to actual player's moving speed */
10437 ScrollStepSize = TILEX / player->move_delay_value;
10439 screen_frame_counter = FrameCounter;
10440 ScreenMovDir = player->MovDir;
10441 ScreenMovPos = player->MovPos;
10442 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10445 else if (!FrameReached(&screen_frame_counter, 1))
10450 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10451 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10452 redraw_mask |= REDRAW_FIELD;
10455 ScreenMovDir = MV_NONE;
10458 void TestIfPlayerTouchesCustomElement(int x, int y)
10460 static int xy[4][2] =
10467 static int trigger_sides[4][2] =
10469 /* center side border side */
10470 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10471 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10472 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10473 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10475 static int touch_dir[4] =
10477 MV_LEFT | MV_RIGHT,
10482 int center_element = Feld[x][y]; /* should always be non-moving! */
10485 for (i = 0; i < NUM_DIRECTIONS; i++)
10487 int xx = x + xy[i][0];
10488 int yy = y + xy[i][1];
10489 int center_side = trigger_sides[i][0];
10490 int border_side = trigger_sides[i][1];
10491 int border_element;
10493 if (!IN_LEV_FIELD(xx, yy))
10496 if (IS_PLAYER(x, y))
10498 struct PlayerInfo *player = PLAYERINFO(x, y);
10500 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10501 border_element = Feld[xx][yy]; /* may be moving! */
10502 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10503 border_element = Feld[xx][yy];
10504 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10505 border_element = MovingOrBlocked2Element(xx, yy);
10507 continue; /* center and border element do not touch */
10509 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10510 player->index_bit, border_side);
10511 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10512 CE_PLAYER_TOUCHES_X,
10513 player->index_bit, border_side);
10515 else if (IS_PLAYER(xx, yy))
10517 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10519 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10521 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10522 continue; /* center and border element do not touch */
10525 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10526 player->index_bit, center_side);
10527 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10528 CE_PLAYER_TOUCHES_X,
10529 player->index_bit, center_side);
10535 #if USE_ELEMENT_TOUCHING_BUGFIX
10537 void TestIfElementTouchesCustomElement(int x, int y)
10539 static int xy[4][2] =
10546 static int trigger_sides[4][2] =
10548 /* center side border side */
10549 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10550 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10551 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10552 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10554 static int touch_dir[4] =
10556 MV_LEFT | MV_RIGHT,
10561 boolean change_center_element = FALSE;
10562 int center_element = Feld[x][y]; /* should always be non-moving! */
10563 int border_element_old[NUM_DIRECTIONS];
10566 for (i = 0; i < NUM_DIRECTIONS; i++)
10568 int xx = x + xy[i][0];
10569 int yy = y + xy[i][1];
10570 int border_element;
10572 border_element_old[i] = -1;
10574 if (!IN_LEV_FIELD(xx, yy))
10577 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10578 border_element = Feld[xx][yy]; /* may be moving! */
10579 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10580 border_element = Feld[xx][yy];
10581 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10582 border_element = MovingOrBlocked2Element(xx, yy);
10584 continue; /* center and border element do not touch */
10586 border_element_old[i] = border_element;
10589 for (i = 0; i < NUM_DIRECTIONS; i++)
10591 int xx = x + xy[i][0];
10592 int yy = y + xy[i][1];
10593 int center_side = trigger_sides[i][0];
10594 int border_element = border_element_old[i];
10596 if (border_element == -1)
10599 /* check for change of border element */
10600 CheckElementChangeBySide(xx, yy, border_element, center_element,
10601 CE_TOUCHING_X, center_side);
10604 for (i = 0; i < NUM_DIRECTIONS; i++)
10606 int border_side = trigger_sides[i][1];
10607 int border_element = border_element_old[i];
10609 if (border_element == -1)
10612 /* check for change of center element (but change it only once) */
10613 if (!change_center_element)
10614 change_center_element =
10615 CheckElementChangeBySide(x, y, center_element, border_element,
10616 CE_TOUCHING_X, border_side);
10622 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10624 static int xy[4][2] =
10631 static int trigger_sides[4][2] =
10633 /* center side border side */
10634 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10635 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10636 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10637 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10639 static int touch_dir[4] =
10641 MV_LEFT | MV_RIGHT,
10646 boolean change_center_element = FALSE;
10647 int center_element = Feld[x][y]; /* should always be non-moving! */
10650 for (i = 0; i < NUM_DIRECTIONS; i++)
10652 int xx = x + xy[i][0];
10653 int yy = y + xy[i][1];
10654 int center_side = trigger_sides[i][0];
10655 int border_side = trigger_sides[i][1];
10656 int border_element;
10658 if (!IN_LEV_FIELD(xx, yy))
10661 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10662 border_element = Feld[xx][yy]; /* may be moving! */
10663 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10664 border_element = Feld[xx][yy];
10665 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10666 border_element = MovingOrBlocked2Element(xx, yy);
10668 continue; /* center and border element do not touch */
10670 /* check for change of center element (but change it only once) */
10671 if (!change_center_element)
10672 change_center_element =
10673 CheckElementChangeBySide(x, y, center_element, border_element,
10674 CE_TOUCHING_X, border_side);
10676 /* check for change of border element */
10677 CheckElementChangeBySide(xx, yy, border_element, center_element,
10678 CE_TOUCHING_X, center_side);
10684 void TestIfElementHitsCustomElement(int x, int y, int direction)
10686 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10687 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10688 int hitx = x + dx, hity = y + dy;
10689 int hitting_element = Feld[x][y];
10690 int touched_element;
10692 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10695 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10696 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10698 if (IN_LEV_FIELD(hitx, hity))
10700 int opposite_direction = MV_DIR_OPPOSITE(direction);
10701 int hitting_side = direction;
10702 int touched_side = opposite_direction;
10703 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10704 MovDir[hitx][hity] != direction ||
10705 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10711 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10712 CE_HITTING_X, touched_side);
10714 CheckElementChangeBySide(hitx, hity, touched_element,
10715 hitting_element, CE_HIT_BY_X, hitting_side);
10717 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10718 CE_HIT_BY_SOMETHING, opposite_direction);
10722 /* "hitting something" is also true when hitting the playfield border */
10723 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10724 CE_HITTING_SOMETHING, direction);
10728 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10730 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10731 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10732 int hitx = x + dx, hity = y + dy;
10733 int hitting_element = Feld[x][y];
10734 int touched_element;
10736 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10737 !IS_FREE(hitx, hity) &&
10738 (!IS_MOVING(hitx, hity) ||
10739 MovDir[hitx][hity] != direction ||
10740 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10743 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10747 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10751 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10752 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10754 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10755 EP_CAN_SMASH_EVERYTHING, direction);
10757 if (IN_LEV_FIELD(hitx, hity))
10759 int opposite_direction = MV_DIR_OPPOSITE(direction);
10760 int hitting_side = direction;
10761 int touched_side = opposite_direction;
10763 int touched_element = MovingOrBlocked2Element(hitx, hity);
10766 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10767 MovDir[hitx][hity] != direction ||
10768 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10777 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10778 CE_SMASHED_BY_SOMETHING, opposite_direction);
10780 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10781 CE_OTHER_IS_SMASHING, touched_side);
10783 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10784 CE_OTHER_GETS_SMASHED, hitting_side);
10790 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10792 int i, kill_x = -1, kill_y = -1;
10794 int bad_element = -1;
10795 static int test_xy[4][2] =
10802 static int test_dir[4] =
10810 for (i = 0; i < NUM_DIRECTIONS; i++)
10812 int test_x, test_y, test_move_dir, test_element;
10814 test_x = good_x + test_xy[i][0];
10815 test_y = good_y + test_xy[i][1];
10817 if (!IN_LEV_FIELD(test_x, test_y))
10821 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10823 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10825 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10826 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10828 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10829 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10833 bad_element = test_element;
10839 if (kill_x != -1 || kill_y != -1)
10841 if (IS_PLAYER(good_x, good_y))
10843 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10845 if (player->shield_deadly_time_left > 0 &&
10846 !IS_INDESTRUCTIBLE(bad_element))
10847 Bang(kill_x, kill_y);
10848 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10849 KillPlayer(player);
10852 Bang(good_x, good_y);
10856 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10858 int i, kill_x = -1, kill_y = -1;
10859 int bad_element = Feld[bad_x][bad_y];
10860 static int test_xy[4][2] =
10867 static int touch_dir[4] =
10869 MV_LEFT | MV_RIGHT,
10874 static int test_dir[4] =
10882 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10885 for (i = 0; i < NUM_DIRECTIONS; i++)
10887 int test_x, test_y, test_move_dir, test_element;
10889 test_x = bad_x + test_xy[i][0];
10890 test_y = bad_y + test_xy[i][1];
10891 if (!IN_LEV_FIELD(test_x, test_y))
10895 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10897 test_element = Feld[test_x][test_y];
10899 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10900 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10902 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10903 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10905 /* good thing is player or penguin that does not move away */
10906 if (IS_PLAYER(test_x, test_y))
10908 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10910 if (bad_element == EL_ROBOT && player->is_moving)
10911 continue; /* robot does not kill player if he is moving */
10913 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10915 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10916 continue; /* center and border element do not touch */
10923 else if (test_element == EL_PENGUIN)
10932 if (kill_x != -1 || kill_y != -1)
10934 if (IS_PLAYER(kill_x, kill_y))
10936 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10938 if (player->shield_deadly_time_left > 0 &&
10939 !IS_INDESTRUCTIBLE(bad_element))
10940 Bang(bad_x, bad_y);
10941 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10942 KillPlayer(player);
10945 Bang(kill_x, kill_y);
10949 void TestIfPlayerTouchesBadThing(int x, int y)
10951 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10954 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10956 TestIfGoodThingHitsBadThing(x, y, move_dir);
10959 void TestIfBadThingTouchesPlayer(int x, int y)
10961 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10964 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10966 TestIfBadThingHitsGoodThing(x, y, move_dir);
10969 void TestIfFriendTouchesBadThing(int x, int y)
10971 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10974 void TestIfBadThingTouchesFriend(int x, int y)
10976 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10979 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10981 int i, kill_x = bad_x, kill_y = bad_y;
10982 static int xy[4][2] =
10990 for (i = 0; i < NUM_DIRECTIONS; i++)
10994 x = bad_x + xy[i][0];
10995 y = bad_y + xy[i][1];
10996 if (!IN_LEV_FIELD(x, y))
10999 element = Feld[x][y];
11000 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11001 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11009 if (kill_x != bad_x || kill_y != bad_y)
11010 Bang(bad_x, bad_y);
11013 void KillPlayer(struct PlayerInfo *player)
11015 int jx = player->jx, jy = player->jy;
11017 if (!player->active)
11020 /* remove accessible field at the player's position */
11021 Feld[jx][jy] = EL_EMPTY;
11023 /* deactivate shield (else Bang()/Explode() would not work right) */
11024 player->shield_normal_time_left = 0;
11025 player->shield_deadly_time_left = 0;
11028 BuryPlayer(player);
11031 static void KillPlayerUnlessEnemyProtected(int x, int y)
11033 if (!PLAYER_ENEMY_PROTECTED(x, y))
11034 KillPlayer(PLAYERINFO(x, y));
11037 static void KillPlayerUnlessExplosionProtected(int x, int y)
11039 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11040 KillPlayer(PLAYERINFO(x, y));
11043 void BuryPlayer(struct PlayerInfo *player)
11045 int jx = player->jx, jy = player->jy;
11047 if (!player->active)
11050 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11051 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11053 player->GameOver = TRUE;
11054 RemovePlayer(player);
11057 void RemovePlayer(struct PlayerInfo *player)
11059 int jx = player->jx, jy = player->jy;
11060 int i, found = FALSE;
11062 player->present = FALSE;
11063 player->active = FALSE;
11065 if (!ExplodeField[jx][jy])
11066 StorePlayer[jx][jy] = 0;
11068 if (player->is_moving)
11069 DrawLevelField(player->last_jx, player->last_jy);
11071 for (i = 0; i < MAX_PLAYERS; i++)
11072 if (stored_player[i].active)
11076 AllPlayersGone = TRUE;
11082 #if USE_NEW_SNAP_DELAY
11083 static void setFieldForSnapping(int x, int y, int element, int direction)
11085 struct ElementInfo *ei = &element_info[element];
11086 int direction_bit = MV_DIR_TO_BIT(direction);
11087 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11088 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11089 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11091 Feld[x][y] = EL_ELEMENT_SNAPPING;
11092 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11094 ResetGfxAnimation(x, y);
11096 GfxElement[x][y] = element;
11097 GfxAction[x][y] = action;
11098 GfxDir[x][y] = direction;
11099 GfxFrame[x][y] = -1;
11104 =============================================================================
11105 checkDiagonalPushing()
11106 -----------------------------------------------------------------------------
11107 check if diagonal input device direction results in pushing of object
11108 (by checking if the alternative direction is walkable, diggable, ...)
11109 =============================================================================
11112 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11113 int x, int y, int real_dx, int real_dy)
11115 int jx, jy, dx, dy, xx, yy;
11117 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11120 /* diagonal direction: check alternative direction */
11125 xx = jx + (dx == 0 ? real_dx : 0);
11126 yy = jy + (dy == 0 ? real_dy : 0);
11128 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11132 =============================================================================
11134 -----------------------------------------------------------------------------
11135 x, y: field next to player (non-diagonal) to try to dig to
11136 real_dx, real_dy: direction as read from input device (can be diagonal)
11137 =============================================================================
11140 int DigField(struct PlayerInfo *player,
11141 int oldx, int oldy, int x, int y,
11142 int real_dx, int real_dy, int mode)
11144 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11145 boolean player_was_pushing = player->is_pushing;
11146 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11147 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11148 int jx = oldx, jy = oldy;
11149 int dx = x - jx, dy = y - jy;
11150 int nextx = x + dx, nexty = y + dy;
11151 int move_direction = (dx == -1 ? MV_LEFT :
11152 dx == +1 ? MV_RIGHT :
11154 dy == +1 ? MV_DOWN : MV_NONE);
11155 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11156 int dig_side = MV_DIR_OPPOSITE(move_direction);
11157 int old_element = Feld[jx][jy];
11158 #if USE_FIXED_DONT_RUN_INTO
11159 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11165 if (is_player) /* function can also be called by EL_PENGUIN */
11167 if (player->MovPos == 0)
11169 player->is_digging = FALSE;
11170 player->is_collecting = FALSE;
11173 if (player->MovPos == 0) /* last pushing move finished */
11174 player->is_pushing = FALSE;
11176 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11178 player->is_switching = FALSE;
11179 player->push_delay = -1;
11181 return MP_NO_ACTION;
11185 #if !USE_FIXED_DONT_RUN_INTO
11186 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11187 return MP_NO_ACTION;
11190 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11191 old_element = Back[jx][jy];
11193 /* in case of element dropped at player position, check background */
11194 else if (Back[jx][jy] != EL_EMPTY &&
11195 game.engine_version >= VERSION_IDENT(2,2,0,0))
11196 old_element = Back[jx][jy];
11198 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11199 return MP_NO_ACTION; /* field has no opening in this direction */
11201 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11202 return MP_NO_ACTION; /* field has no opening in this direction */
11204 #if USE_FIXED_DONT_RUN_INTO
11205 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11209 Feld[jx][jy] = player->artwork_element;
11210 InitMovingField(jx, jy, MV_DOWN);
11211 Store[jx][jy] = EL_ACID;
11212 ContinueMoving(jx, jy);
11213 BuryPlayer(player);
11215 return MP_DONT_RUN_INTO;
11219 #if USE_FIXED_DONT_RUN_INTO
11220 if (player_can_move && DONT_RUN_INTO(element))
11222 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11224 return MP_DONT_RUN_INTO;
11228 #if USE_FIXED_DONT_RUN_INTO
11229 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11230 return MP_NO_ACTION;
11233 #if !USE_FIXED_DONT_RUN_INTO
11234 element = Feld[x][y];
11237 collect_count = element_info[element].collect_count_initial;
11239 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11240 return MP_NO_ACTION;
11242 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11243 player_can_move = player_can_move_or_snap;
11245 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11246 game.engine_version >= VERSION_IDENT(2,2,0,0))
11248 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11249 player->index_bit, dig_side);
11250 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11251 player->index_bit, dig_side);
11253 if (Feld[x][y] != element) /* field changed by snapping */
11256 return MP_NO_ACTION;
11259 #if USE_PLAYER_GRAVITY
11260 if (player->gravity && is_player && !player->is_auto_moving &&
11261 canFallDown(player) && move_direction != MV_DOWN &&
11262 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11263 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11265 if (game.gravity && is_player && !player->is_auto_moving &&
11266 canFallDown(player) && move_direction != MV_DOWN &&
11267 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11268 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11271 if (player_can_move &&
11272 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11274 int sound_element = SND_ELEMENT(element);
11275 int sound_action = ACTION_WALKING;
11277 if (IS_RND_GATE(element))
11279 if (!player->key[RND_GATE_NR(element)])
11280 return MP_NO_ACTION;
11282 else if (IS_RND_GATE_GRAY(element))
11284 if (!player->key[RND_GATE_GRAY_NR(element)])
11285 return MP_NO_ACTION;
11287 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11289 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11290 return MP_NO_ACTION;
11292 else if (element == EL_EXIT_OPEN ||
11293 element == EL_SP_EXIT_OPEN ||
11294 element == EL_SP_EXIT_OPENING)
11296 sound_action = ACTION_PASSING; /* player is passing exit */
11298 else if (element == EL_EMPTY)
11300 sound_action = ACTION_MOVING; /* nothing to walk on */
11303 /* play sound from background or player, whatever is available */
11304 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11305 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11307 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11309 else if (player_can_move &&
11310 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11312 if (!ACCESS_FROM(element, opposite_direction))
11313 return MP_NO_ACTION; /* field not accessible from this direction */
11315 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11316 return MP_NO_ACTION;
11318 if (IS_EM_GATE(element))
11320 if (!player->key[EM_GATE_NR(element)])
11321 return MP_NO_ACTION;
11323 else if (IS_EM_GATE_GRAY(element))
11325 if (!player->key[EM_GATE_GRAY_NR(element)])
11326 return MP_NO_ACTION;
11328 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11330 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11331 return MP_NO_ACTION;
11333 else if (IS_EMC_GATE(element))
11335 if (!player->key[EMC_GATE_NR(element)])
11336 return MP_NO_ACTION;
11338 else if (IS_EMC_GATE_GRAY(element))
11340 if (!player->key[EMC_GATE_GRAY_NR(element)])
11341 return MP_NO_ACTION;
11343 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11345 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11346 return MP_NO_ACTION;
11348 else if (IS_SP_PORT(element))
11350 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11351 element == EL_SP_GRAVITY_PORT_RIGHT ||
11352 element == EL_SP_GRAVITY_PORT_UP ||
11353 element == EL_SP_GRAVITY_PORT_DOWN)
11354 #if USE_PLAYER_GRAVITY
11355 player->gravity = !player->gravity;
11357 game.gravity = !game.gravity;
11359 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11360 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11361 element == EL_SP_GRAVITY_ON_PORT_UP ||
11362 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11363 #if USE_PLAYER_GRAVITY
11364 player->gravity = TRUE;
11366 game.gravity = TRUE;
11368 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11369 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11370 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11371 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11372 #if USE_PLAYER_GRAVITY
11373 player->gravity = FALSE;
11375 game.gravity = FALSE;
11379 /* automatically move to the next field with double speed */
11380 player->programmed_action = move_direction;
11382 if (player->move_delay_reset_counter == 0)
11384 player->move_delay_reset_counter = 2; /* two double speed steps */
11386 DOUBLE_PLAYER_SPEED(player);
11389 PlayLevelSoundAction(x, y, ACTION_PASSING);
11391 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11395 if (mode != DF_SNAP)
11397 GfxElement[x][y] = GFX_ELEMENT(element);
11398 player->is_digging = TRUE;
11401 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11403 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11404 player->index_bit, dig_side);
11406 if (mode == DF_SNAP)
11408 #if USE_NEW_SNAP_DELAY
11409 if (level.block_snap_field)
11410 setFieldForSnapping(x, y, element, move_direction);
11412 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11414 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11417 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11418 player->index_bit, dig_side);
11421 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11425 if (is_player && mode != DF_SNAP)
11427 GfxElement[x][y] = element;
11428 player->is_collecting = TRUE;
11431 if (element == EL_SPEED_PILL)
11433 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11435 else if (element == EL_EXTRA_TIME && level.time > 0)
11437 TimeLeft += level.extra_time;
11438 DrawGameValue_Time(TimeLeft);
11440 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11442 player->shield_normal_time_left += level.shield_normal_time;
11443 if (element == EL_SHIELD_DEADLY)
11444 player->shield_deadly_time_left += level.shield_deadly_time;
11446 else if (element == EL_DYNAMITE ||
11447 element == EL_EM_DYNAMITE ||
11448 element == EL_SP_DISK_RED)
11450 if (player->inventory_size < MAX_INVENTORY_SIZE)
11451 player->inventory_element[player->inventory_size++] = element;
11453 DrawGameDoorValues();
11455 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11457 player->dynabomb_count++;
11458 player->dynabombs_left++;
11460 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11462 player->dynabomb_size++;
11464 else if (element == EL_DYNABOMB_INCREASE_POWER)
11466 player->dynabomb_xl = TRUE;
11468 else if (IS_KEY(element))
11470 player->key[KEY_NR(element)] = TRUE;
11472 DrawGameDoorValues();
11474 else if (IS_ENVELOPE(element))
11476 player->show_envelope = element;
11478 else if (element == EL_EMC_LENSES)
11480 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11482 RedrawAllInvisibleElementsForLenses();
11484 else if (element == EL_EMC_MAGNIFIER)
11486 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11488 RedrawAllInvisibleElementsForMagnifier();
11490 else if (IS_DROPPABLE(element) ||
11491 IS_THROWABLE(element)) /* can be collected and dropped */
11495 if (collect_count == 0)
11496 player->inventory_infinite_element = element;
11498 for (i = 0; i < collect_count; i++)
11499 if (player->inventory_size < MAX_INVENTORY_SIZE)
11500 player->inventory_element[player->inventory_size++] = element;
11502 DrawGameDoorValues();
11504 else if (collect_count > 0)
11506 local_player->gems_still_needed -= collect_count;
11507 if (local_player->gems_still_needed < 0)
11508 local_player->gems_still_needed = 0;
11510 DrawGameValue_Emeralds(local_player->gems_still_needed);
11513 RaiseScoreElement(element);
11514 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11517 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11518 player->index_bit, dig_side);
11520 if (mode == DF_SNAP)
11522 #if USE_NEW_SNAP_DELAY
11523 if (level.block_snap_field)
11524 setFieldForSnapping(x, y, element, move_direction);
11526 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11528 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11531 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11532 player->index_bit, dig_side);
11535 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11537 if (mode == DF_SNAP && element != EL_BD_ROCK)
11538 return MP_NO_ACTION;
11540 if (CAN_FALL(element) && dy)
11541 return MP_NO_ACTION;
11543 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11544 !(element == EL_SPRING && level.use_spring_bug))
11545 return MP_NO_ACTION;
11547 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11548 ((move_direction & MV_VERTICAL &&
11549 ((element_info[element].move_pattern & MV_LEFT &&
11550 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11551 (element_info[element].move_pattern & MV_RIGHT &&
11552 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11553 (move_direction & MV_HORIZONTAL &&
11554 ((element_info[element].move_pattern & MV_UP &&
11555 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11556 (element_info[element].move_pattern & MV_DOWN &&
11557 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11558 return MP_NO_ACTION;
11560 /* do not push elements already moving away faster than player */
11561 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11562 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11563 return MP_NO_ACTION;
11565 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11567 if (player->push_delay_value == -1 || !player_was_pushing)
11568 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11570 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11572 if (player->push_delay_value == -1)
11573 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11575 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11577 if (!player->is_pushing)
11578 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11581 player->is_pushing = TRUE;
11582 player->is_active = TRUE;
11584 if (!(IN_LEV_FIELD(nextx, nexty) &&
11585 (IS_FREE(nextx, nexty) ||
11586 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11587 IS_SB_ELEMENT(element)))))
11588 return MP_NO_ACTION;
11590 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11591 return MP_NO_ACTION;
11593 if (player->push_delay == -1) /* new pushing; restart delay */
11594 player->push_delay = 0;
11596 if (player->push_delay < player->push_delay_value &&
11597 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11598 element != EL_SPRING && element != EL_BALLOON)
11600 /* make sure that there is no move delay before next try to push */
11601 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11602 player->move_delay = 0;
11604 return MP_NO_ACTION;
11607 if (IS_SB_ELEMENT(element))
11609 if (element == EL_SOKOBAN_FIELD_FULL)
11611 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11612 local_player->sokobanfields_still_needed++;
11615 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11617 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11618 local_player->sokobanfields_still_needed--;
11621 Feld[x][y] = EL_SOKOBAN_OBJECT;
11623 if (Back[x][y] == Back[nextx][nexty])
11624 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11625 else if (Back[x][y] != 0)
11626 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11629 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11632 if (local_player->sokobanfields_still_needed == 0 &&
11633 game.emulation == EMU_SOKOBAN)
11635 PlayerWins(player);
11637 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11641 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11643 InitMovingField(x, y, move_direction);
11644 GfxAction[x][y] = ACTION_PUSHING;
11646 if (mode == DF_SNAP)
11647 ContinueMoving(x, y);
11649 MovPos[x][y] = (dx != 0 ? dx : dy);
11651 Pushed[x][y] = TRUE;
11652 Pushed[nextx][nexty] = TRUE;
11654 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11655 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11657 player->push_delay_value = -1; /* get new value later */
11659 /* check for element change _after_ element has been pushed */
11660 if (game.use_change_when_pushing_bug)
11662 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11663 player->index_bit, dig_side);
11664 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11665 player->index_bit, dig_side);
11668 else if (IS_SWITCHABLE(element))
11670 if (PLAYER_SWITCHING(player, x, y))
11672 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11673 player->index_bit, dig_side);
11678 player->is_switching = TRUE;
11679 player->switch_x = x;
11680 player->switch_y = y;
11682 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11684 if (element == EL_ROBOT_WHEEL)
11686 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11690 DrawLevelField(x, y);
11692 else if (element == EL_SP_TERMINAL)
11696 SCAN_PLAYFIELD(xx, yy)
11698 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11700 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11701 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11704 else if (IS_BELT_SWITCH(element))
11706 ToggleBeltSwitch(x, y);
11708 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11709 element == EL_SWITCHGATE_SWITCH_DOWN)
11711 ToggleSwitchgateSwitch(x, y);
11713 else if (element == EL_LIGHT_SWITCH ||
11714 element == EL_LIGHT_SWITCH_ACTIVE)
11716 ToggleLightSwitch(x, y);
11718 else if (element == EL_TIMEGATE_SWITCH)
11720 ActivateTimegateSwitch(x, y);
11722 else if (element == EL_BALLOON_SWITCH_LEFT ||
11723 element == EL_BALLOON_SWITCH_RIGHT ||
11724 element == EL_BALLOON_SWITCH_UP ||
11725 element == EL_BALLOON_SWITCH_DOWN ||
11726 element == EL_BALLOON_SWITCH_NONE ||
11727 element == EL_BALLOON_SWITCH_ANY)
11729 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11730 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11731 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11732 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11733 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11736 else if (element == EL_LAMP)
11738 Feld[x][y] = EL_LAMP_ACTIVE;
11739 local_player->lights_still_needed--;
11741 ResetGfxAnimation(x, y);
11742 DrawLevelField(x, y);
11744 else if (element == EL_TIME_ORB_FULL)
11746 Feld[x][y] = EL_TIME_ORB_EMPTY;
11748 if (level.time > 0 || level.use_time_orb_bug)
11750 TimeLeft += level.time_orb_time;
11751 DrawGameValue_Time(TimeLeft);
11754 ResetGfxAnimation(x, y);
11755 DrawLevelField(x, y);
11757 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11758 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11762 game.ball_state = !game.ball_state;
11764 SCAN_PLAYFIELD(xx, yy)
11766 int e = Feld[xx][yy];
11768 if (game.ball_state)
11770 if (e == EL_EMC_MAGIC_BALL)
11771 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11772 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11773 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11777 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11778 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11779 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11780 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11785 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11786 player->index_bit, dig_side);
11788 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11789 player->index_bit, dig_side);
11791 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11792 player->index_bit, dig_side);
11798 if (!PLAYER_SWITCHING(player, x, y))
11800 player->is_switching = TRUE;
11801 player->switch_x = x;
11802 player->switch_y = y;
11804 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11805 player->index_bit, dig_side);
11806 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11807 player->index_bit, dig_side);
11809 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11810 player->index_bit, dig_side);
11811 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11812 player->index_bit, dig_side);
11815 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11816 player->index_bit, dig_side);
11817 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11818 player->index_bit, dig_side);
11820 return MP_NO_ACTION;
11823 player->push_delay = -1;
11825 if (is_player) /* function can also be called by EL_PENGUIN */
11827 if (Feld[x][y] != element) /* really digged/collected something */
11829 player->is_collecting = !player->is_digging;
11830 player->is_active = TRUE;
11837 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11839 int jx = player->jx, jy = player->jy;
11840 int x = jx + dx, y = jy + dy;
11841 int snap_direction = (dx == -1 ? MV_LEFT :
11842 dx == +1 ? MV_RIGHT :
11844 dy == +1 ? MV_DOWN : MV_NONE);
11845 boolean can_continue_snapping = (level.continuous_snapping &&
11846 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
11848 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11851 if (!player->active || !IN_LEV_FIELD(x, y))
11859 if (player->MovPos == 0)
11860 player->is_pushing = FALSE;
11862 player->is_snapping = FALSE;
11864 if (player->MovPos == 0)
11866 player->is_moving = FALSE;
11867 player->is_digging = FALSE;
11868 player->is_collecting = FALSE;
11874 #if USE_NEW_CONTINUOUS_SNAPPING
11875 /* prevent snapping with already pressed snap key when not allowed */
11876 if (player->is_snapping && !can_continue_snapping)
11879 if (player->is_snapping)
11883 player->MovDir = snap_direction;
11885 if (player->MovPos == 0)
11887 player->is_moving = FALSE;
11888 player->is_digging = FALSE;
11889 player->is_collecting = FALSE;
11892 player->is_dropping = FALSE;
11893 player->is_dropping_pressed = FALSE;
11894 player->drop_pressed_delay = 0;
11896 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11899 player->is_snapping = TRUE;
11900 player->is_active = TRUE;
11902 if (player->MovPos == 0)
11904 player->is_moving = FALSE;
11905 player->is_digging = FALSE;
11906 player->is_collecting = FALSE;
11909 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11910 DrawLevelField(player->last_jx, player->last_jy);
11912 DrawLevelField(x, y);
11917 boolean DropElement(struct PlayerInfo *player)
11919 int old_element, new_element;
11920 int dropx = player->jx, dropy = player->jy;
11921 int drop_direction = player->MovDir;
11922 int drop_side = drop_direction;
11923 int drop_element = (player->inventory_size > 0 ?
11924 player->inventory_element[player->inventory_size - 1] :
11925 player->inventory_infinite_element != EL_UNDEFINED ?
11926 player->inventory_infinite_element :
11927 player->dynabombs_left > 0 ?
11928 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11931 player->is_dropping_pressed = TRUE;
11933 /* do not drop an element on top of another element; when holding drop key
11934 pressed without moving, dropped element must move away before the next
11935 element can be dropped (this is especially important if the next element
11936 is dynamite, which can be placed on background for historical reasons) */
11937 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11940 if (IS_THROWABLE(drop_element))
11942 dropx += GET_DX_FROM_DIR(drop_direction);
11943 dropy += GET_DY_FROM_DIR(drop_direction);
11945 if (!IN_LEV_FIELD(dropx, dropy))
11949 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11950 new_element = drop_element; /* default: no change when dropping */
11952 /* check if player is active, not moving and ready to drop */
11953 if (!player->active || player->MovPos || player->drop_delay > 0)
11956 /* check if player has anything that can be dropped */
11957 if (new_element == EL_UNDEFINED)
11960 /* check if drop key was pressed long enough for EM style dynamite */
11961 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
11964 /* check if anything can be dropped at the current position */
11965 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11968 /* collected custom elements can only be dropped on empty fields */
11969 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11972 if (old_element != EL_EMPTY)
11973 Back[dropx][dropy] = old_element; /* store old element on this field */
11975 ResetGfxAnimation(dropx, dropy);
11976 ResetRandomAnimationValue(dropx, dropy);
11978 if (player->inventory_size > 0 ||
11979 player->inventory_infinite_element != EL_UNDEFINED)
11981 if (player->inventory_size > 0)
11983 player->inventory_size--;
11985 DrawGameDoorValues();
11987 if (new_element == EL_DYNAMITE)
11988 new_element = EL_DYNAMITE_ACTIVE;
11989 else if (new_element == EL_EM_DYNAMITE)
11990 new_element = EL_EM_DYNAMITE_ACTIVE;
11991 else if (new_element == EL_SP_DISK_RED)
11992 new_element = EL_SP_DISK_RED_ACTIVE;
11995 Feld[dropx][dropy] = new_element;
11997 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11998 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11999 el2img(Feld[dropx][dropy]), 0);
12001 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12003 /* needed if previous element just changed to "empty" in the last frame */
12004 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12006 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12007 player->index_bit, drop_side);
12008 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12010 player->index_bit, drop_side);
12012 TestIfElementTouchesCustomElement(dropx, dropy);
12014 else /* player is dropping a dyna bomb */
12016 player->dynabombs_left--;
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);
12027 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12028 InitField_WithBug1(dropx, dropy, FALSE);
12030 new_element = Feld[dropx][dropy]; /* element might have changed */
12032 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12033 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12035 int move_direction, nextx, nexty;
12037 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12038 MovDir[dropx][dropy] = drop_direction;
12040 move_direction = MovDir[dropx][dropy];
12041 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12042 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12044 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12045 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12048 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12049 player->is_dropping = TRUE;
12051 player->drop_pressed_delay = 0;
12052 player->is_dropping_pressed = FALSE;
12054 player->drop_x = dropx;
12055 player->drop_y = dropy;
12060 /* ------------------------------------------------------------------------- */
12061 /* game sound playing functions */
12062 /* ------------------------------------------------------------------------- */
12064 static int *loop_sound_frame = NULL;
12065 static int *loop_sound_volume = NULL;
12067 void InitPlayLevelSound()
12069 int num_sounds = getSoundListSize();
12071 checked_free(loop_sound_frame);
12072 checked_free(loop_sound_volume);
12074 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12075 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12078 static void PlayLevelSound(int x, int y, int nr)
12080 int sx = SCREENX(x), sy = SCREENY(y);
12081 int volume, stereo_position;
12082 int max_distance = 8;
12083 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12085 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12086 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12089 if (!IN_LEV_FIELD(x, y) ||
12090 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12091 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12094 volume = SOUND_MAX_VOLUME;
12096 if (!IN_SCR_FIELD(sx, sy))
12098 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12099 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12101 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12104 stereo_position = (SOUND_MAX_LEFT +
12105 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12106 (SCR_FIELDX + 2 * max_distance));
12108 if (IS_LOOP_SOUND(nr))
12110 /* This assures that quieter loop sounds do not overwrite louder ones,
12111 while restarting sound volume comparison with each new game frame. */
12113 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12116 loop_sound_volume[nr] = volume;
12117 loop_sound_frame[nr] = FrameCounter;
12120 PlaySoundExt(nr, volume, stereo_position, type);
12123 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12125 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12126 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12127 y < LEVELY(BY1) ? LEVELY(BY1) :
12128 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12132 static void PlayLevelSoundAction(int x, int y, int action)
12134 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12137 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12139 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12141 if (sound_effect != SND_UNDEFINED)
12142 PlayLevelSound(x, y, sound_effect);
12145 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12148 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12150 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12151 PlayLevelSound(x, y, sound_effect);
12154 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12156 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12158 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12159 PlayLevelSound(x, y, sound_effect);
12162 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12164 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12166 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12167 StopSound(sound_effect);
12170 static void PlayLevelMusic()
12172 if (levelset.music[level_nr] != MUS_UNDEFINED)
12173 PlayMusic(levelset.music[level_nr]); /* from config file */
12175 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12178 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
12180 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12181 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
12182 int x = xx - 1 - offset;
12183 int y = yy - 1 - offset;
12188 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12192 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12196 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12200 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12204 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12208 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12212 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12215 case SAMPLE_android_clone:
12216 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12219 case SAMPLE_android_move:
12220 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12223 case SAMPLE_spring:
12224 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12228 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12232 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12235 case SAMPLE_eater_eat:
12236 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12240 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12243 case SAMPLE_collect:
12244 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12247 case SAMPLE_diamond:
12248 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12251 case SAMPLE_squash:
12252 /* !!! CHECK THIS !!! */
12254 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12256 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12260 case SAMPLE_wonderfall:
12261 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12265 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12269 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12273 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12277 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12281 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12285 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12288 case SAMPLE_wonder:
12289 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12293 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12296 case SAMPLE_exit_open:
12297 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12300 case SAMPLE_exit_leave:
12301 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12304 case SAMPLE_dynamite:
12305 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12309 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12313 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12317 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12321 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12325 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12329 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12333 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12339 void ChangeTime(int value)
12341 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
12345 /* EMC game engine uses value from time counter of RND game engine */
12346 level.native_em_level->lev->time = *time;
12348 DrawGameValue_Time(*time);
12351 void RaiseScore(int value)
12353 /* EMC game engine and RND game engine have separate score counters */
12354 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
12355 &level.native_em_level->lev->score : &local_player->score);
12359 DrawGameValue_Score(*score);
12363 void RaiseScore(int value)
12365 local_player->score += value;
12367 DrawGameValue_Score(local_player->score);
12370 void RaiseScoreElement(int element)
12375 case EL_BD_DIAMOND:
12376 case EL_EMERALD_YELLOW:
12377 case EL_EMERALD_RED:
12378 case EL_EMERALD_PURPLE:
12379 case EL_SP_INFOTRON:
12380 RaiseScore(level.score[SC_EMERALD]);
12383 RaiseScore(level.score[SC_DIAMOND]);
12386 RaiseScore(level.score[SC_CRYSTAL]);
12389 RaiseScore(level.score[SC_PEARL]);
12392 case EL_BD_BUTTERFLY:
12393 case EL_SP_ELECTRON:
12394 RaiseScore(level.score[SC_BUG]);
12397 case EL_BD_FIREFLY:
12398 case EL_SP_SNIKSNAK:
12399 RaiseScore(level.score[SC_SPACESHIP]);
12402 case EL_DARK_YAMYAM:
12403 RaiseScore(level.score[SC_YAMYAM]);
12406 RaiseScore(level.score[SC_ROBOT]);
12409 RaiseScore(level.score[SC_PACMAN]);
12412 RaiseScore(level.score[SC_NUT]);
12415 case EL_EM_DYNAMITE:
12416 case EL_SP_DISK_RED:
12417 case EL_DYNABOMB_INCREASE_NUMBER:
12418 case EL_DYNABOMB_INCREASE_SIZE:
12419 case EL_DYNABOMB_INCREASE_POWER:
12420 RaiseScore(level.score[SC_DYNAMITE]);
12422 case EL_SHIELD_NORMAL:
12423 case EL_SHIELD_DEADLY:
12424 RaiseScore(level.score[SC_SHIELD]);
12426 case EL_EXTRA_TIME:
12427 RaiseScore(level.extra_time_score);
12441 RaiseScore(level.score[SC_KEY]);
12444 RaiseScore(element_info[element].collect_score);
12449 void RequestQuitGame(boolean ask_if_really_quit)
12451 if (AllPlayersGone ||
12452 !ask_if_really_quit ||
12453 level_editor_test_game ||
12454 Request("Do you really want to quit the game ?",
12455 REQ_ASK | REQ_STAY_CLOSED))
12457 #if defined(NETWORK_AVALIABLE)
12458 if (options.network)
12459 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12463 if (!ask_if_really_quit || level_editor_test_game)
12465 game_status = GAME_MODE_MAIN;
12471 FadeOut(REDRAW_FIELD);
12473 game_status = GAME_MODE_MAIN;
12475 DrawAndFadeInMainMenu(REDRAW_FIELD);
12481 if (tape.playing && tape.deactivate_display)
12482 TapeDeactivateDisplayOff(TRUE);
12484 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12486 if (tape.playing && tape.deactivate_display)
12487 TapeDeactivateDisplayOn();
12492 /* ------------------------------------------------------------------------- */
12493 /* random generator functions */
12494 /* ------------------------------------------------------------------------- */
12496 unsigned int InitEngineRandom_RND(long seed)
12498 game.num_random_calls = 0;
12501 unsigned int rnd_seed = InitEngineRandom(seed);
12503 printf("::: START RND: %d\n", rnd_seed);
12508 return InitEngineRandom(seed);
12514 unsigned int RND(int max)
12518 game.num_random_calls++;
12520 return GetEngineRandom(max);
12527 /* ------------------------------------------------------------------------- */
12528 /* game engine snapshot handling functions */
12529 /* ------------------------------------------------------------------------- */
12531 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
12533 struct EngineSnapshotInfo
12535 /* runtime values for custom element collect score */
12536 int collect_score[NUM_CUSTOM_ELEMENTS];
12538 /* runtime values for group element choice position */
12539 int choice_pos[NUM_GROUP_ELEMENTS];
12541 /* runtime values for belt position animations */
12542 int belt_graphic[4 * NUM_BELT_PARTS];
12543 int belt_anim_mode[4 * NUM_BELT_PARTS];
12546 struct EngineSnapshotNodeInfo
12553 static struct EngineSnapshotInfo engine_snapshot_rnd;
12554 static ListNode *engine_snapshot_list = NULL;
12555 static char *snapshot_level_identifier = NULL;
12556 static int snapshot_level_nr = -1;
12558 void FreeEngineSnapshot()
12560 while (engine_snapshot_list != NULL)
12561 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
12564 setString(&snapshot_level_identifier, NULL);
12565 snapshot_level_nr = -1;
12568 static void SaveEngineSnapshotValues_RND()
12570 static int belt_base_active_element[4] =
12572 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
12573 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
12574 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
12575 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
12579 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12581 int element = EL_CUSTOM_START + i;
12583 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
12586 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12588 int element = EL_GROUP_START + i;
12590 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
12593 for (i = 0; i < 4; i++)
12595 for (j = 0; j < NUM_BELT_PARTS; j++)
12597 int element = belt_base_active_element[i] + j;
12598 int graphic = el2img(element);
12599 int anim_mode = graphic_info[graphic].anim_mode;
12601 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
12602 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
12607 static void LoadEngineSnapshotValues_RND()
12609 unsigned long num_random_calls = game.num_random_calls;
12612 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12614 int element = EL_CUSTOM_START + i;
12616 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
12619 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12621 int element = EL_GROUP_START + i;
12623 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
12626 for (i = 0; i < 4; i++)
12628 for (j = 0; j < NUM_BELT_PARTS; j++)
12630 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
12631 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
12633 graphic_info[graphic].anim_mode = anim_mode;
12637 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
12639 InitRND(tape.random_seed);
12640 for (i = 0; i < num_random_calls; i++)
12644 if (game.num_random_calls != num_random_calls)
12646 Error(ERR_RETURN, "number of random calls out of sync");
12647 Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
12648 Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
12649 Error(ERR_EXIT, "this should not happen -- please debug");
12653 static void SaveEngineSnapshotBuffer(void *buffer, int size)
12655 struct EngineSnapshotNodeInfo *bi =
12656 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
12658 bi->buffer_orig = buffer;
12659 bi->buffer_copy = checked_malloc(size);
12662 memcpy(bi->buffer_copy, buffer, size);
12664 addNodeToList(&engine_snapshot_list, NULL, bi);
12667 void SaveEngineSnapshot()
12669 FreeEngineSnapshot(); /* free previous snapshot, if needed */
12671 /* copy some special values to a structure better suited for the snapshot */
12673 SaveEngineSnapshotValues_RND();
12674 SaveEngineSnapshotValues_EM();
12676 /* save values stored in special snapshot structure */
12678 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
12679 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
12681 /* save further RND engine values */
12683 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
12684 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
12685 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
12687 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
12688 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
12689 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
12690 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
12692 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
12693 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
12694 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
12695 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
12696 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
12698 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
12699 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
12700 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
12702 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
12704 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
12706 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
12707 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
12709 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
12710 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
12711 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
12712 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
12713 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
12714 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
12715 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
12716 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
12717 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
12718 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
12719 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
12720 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
12721 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
12722 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
12723 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
12724 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
12725 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
12727 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
12728 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
12730 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
12731 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
12732 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
12734 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
12735 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
12737 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
12738 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
12739 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
12740 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
12741 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
12743 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
12744 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
12746 /* save level identification information */
12748 setString(&snapshot_level_identifier, leveldir_current->identifier);
12749 snapshot_level_nr = level_nr;
12752 ListNode *node = engine_snapshot_list;
12755 while (node != NULL)
12757 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
12762 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
12766 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
12768 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
12771 void LoadEngineSnapshot()
12773 ListNode *node = engine_snapshot_list;
12775 if (engine_snapshot_list == NULL)
12778 while (node != NULL)
12780 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
12785 /* restore special values from snapshot structure */
12787 LoadEngineSnapshotValues_RND();
12788 LoadEngineSnapshotValues_EM();
12791 boolean CheckEngineSnapshot()
12793 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
12794 snapshot_level_nr == level_nr);
12798 /* ---------- new game button stuff ---------------------------------------- */
12800 /* graphic position values for game buttons */
12801 #define GAME_BUTTON_XSIZE 30
12802 #define GAME_BUTTON_YSIZE 30
12803 #define GAME_BUTTON_XPOS 5
12804 #define GAME_BUTTON_YPOS 215
12805 #define SOUND_BUTTON_XPOS 5
12806 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12808 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12809 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12810 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12811 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12812 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12813 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12820 } gamebutton_info[NUM_GAME_BUTTONS] =
12823 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12828 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12829 GAME_CTRL_ID_PAUSE,
12833 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12838 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12839 SOUND_CTRL_ID_MUSIC,
12840 "background music on/off"
12843 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12844 SOUND_CTRL_ID_LOOPS,
12845 "sound loops on/off"
12848 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12849 SOUND_CTRL_ID_SIMPLE,
12850 "normal sounds on/off"
12854 void CreateGameButtons()
12858 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12860 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12861 struct GadgetInfo *gi;
12864 unsigned long event_mask;
12865 int gd_xoffset, gd_yoffset;
12866 int gd_x1, gd_x2, gd_y1, gd_y2;
12869 gd_xoffset = gamebutton_info[i].x;
12870 gd_yoffset = gamebutton_info[i].y;
12871 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12872 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12874 if (id == GAME_CTRL_ID_STOP ||
12875 id == GAME_CTRL_ID_PAUSE ||
12876 id == GAME_CTRL_ID_PLAY)
12878 button_type = GD_TYPE_NORMAL_BUTTON;
12880 event_mask = GD_EVENT_RELEASED;
12881 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12882 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12886 button_type = GD_TYPE_CHECK_BUTTON;
12888 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12889 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12890 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12891 event_mask = GD_EVENT_PRESSED;
12892 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12893 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12896 gi = CreateGadget(GDI_CUSTOM_ID, id,
12897 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12898 GDI_X, DX + gd_xoffset,
12899 GDI_Y, DY + gd_yoffset,
12900 GDI_WIDTH, GAME_BUTTON_XSIZE,
12901 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12902 GDI_TYPE, button_type,
12903 GDI_STATE, GD_BUTTON_UNPRESSED,
12904 GDI_CHECKED, checked,
12905 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12906 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12907 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12908 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12909 GDI_EVENT_MASK, event_mask,
12910 GDI_CALLBACK_ACTION, HandleGameButtons,
12914 Error(ERR_EXIT, "cannot create gadget");
12916 game_gadget[id] = gi;
12920 void FreeGameButtons()
12924 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12925 FreeGadget(game_gadget[i]);
12928 static void MapGameButtons()
12932 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12933 MapGadget(game_gadget[i]);
12936 void UnmapGameButtons()
12940 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12941 UnmapGadget(game_gadget[i]);
12944 static void HandleGameButtons(struct GadgetInfo *gi)
12946 int id = gi->custom_id;
12948 if (game_status != GAME_MODE_PLAYING)
12953 case GAME_CTRL_ID_STOP:
12957 RequestQuitGame(TRUE);
12960 case GAME_CTRL_ID_PAUSE:
12961 if (options.network)
12963 #if defined(NETWORK_AVALIABLE)
12965 SendToServer_ContinuePlaying();
12967 SendToServer_PausePlaying();
12971 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12974 case GAME_CTRL_ID_PLAY:
12977 #if defined(NETWORK_AVALIABLE)
12978 if (options.network)
12979 SendToServer_ContinuePlaying();
12983 tape.pausing = FALSE;
12984 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
12989 case SOUND_CTRL_ID_MUSIC:
12990 if (setup.sound_music)
12992 setup.sound_music = FALSE;
12995 else if (audio.music_available)
12997 setup.sound = setup.sound_music = TRUE;
12999 SetAudioMode(setup.sound);
13005 case SOUND_CTRL_ID_LOOPS:
13006 if (setup.sound_loops)
13007 setup.sound_loops = FALSE;
13008 else if (audio.loops_available)
13010 setup.sound = setup.sound_loops = TRUE;
13011 SetAudioMode(setup.sound);
13015 case SOUND_CTRL_ID_SIMPLE:
13016 if (setup.sound_simple)
13017 setup.sound_simple = FALSE;
13018 else if (audio.sound_available)
13020 setup.sound = setup.sound_simple = TRUE;
13021 SetAudioMode(setup.sound);