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 = -1;
1936 player->last_jy = -1;
1940 player->shield_normal_time_left = 0;
1941 player->shield_deadly_time_left = 0;
1943 player->inventory_infinite_element = EL_UNDEFINED;
1944 player->inventory_size = 0;
1946 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1947 SnapField(player, 0, 0);
1949 player->LevelSolved = FALSE;
1950 player->GameOver = FALSE;
1952 player->LevelSolved_GameEnd = FALSE;
1953 player->LevelSolved_SaveTape = FALSE;
1954 player->LevelSolved_SaveScore = FALSE;
1957 network_player_action_received = FALSE;
1959 #if defined(NETWORK_AVALIABLE)
1960 /* initial null action */
1961 if (network_playing)
1962 SendToServer_MovePlayer(MV_NONE);
1971 TimeLeft = level.time;
1974 ScreenMovDir = MV_NONE;
1978 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1980 AllPlayersGone = FALSE;
1982 game.yamyam_content_nr = 0;
1983 game.magic_wall_active = FALSE;
1984 game.magic_wall_time_left = 0;
1985 game.light_time_left = 0;
1986 game.timegate_time_left = 0;
1987 game.switchgate_pos = 0;
1988 game.wind_direction = level.wind_direction_initial;
1990 #if !USE_PLAYER_GRAVITY
1991 game.gravity = FALSE;
1992 game.explosions_delayed = TRUE;
1995 game.lenses_time_left = 0;
1996 game.magnify_time_left = 0;
1998 game.ball_state = level.ball_state_initial;
1999 game.ball_content_nr = 0;
2001 game.envelope_active = FALSE;
2003 /* set focus to local player for network games, else to all players */
2004 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2005 game.centered_player_nr_next = game.centered_player_nr;
2006 game.set_centered_player = FALSE;
2008 if (network_playing && tape.recording)
2010 /* store client dependent player focus when recording network games */
2011 tape.centered_player_nr_next = game.centered_player_nr_next;
2012 tape.set_centered_player = TRUE;
2015 for (i = 0; i < NUM_BELTS; i++)
2017 game.belt_dir[i] = MV_NONE;
2018 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2021 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2022 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2024 SCAN_PLAYFIELD(x, y)
2026 Feld[x][y] = level.field[x][y];
2027 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2028 ChangeDelay[x][y] = 0;
2029 ChangePage[x][y] = -1;
2030 #if USE_NEW_CUSTOM_VALUE
2031 CustomValue[x][y] = 0; /* initialized in InitField() */
2033 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2035 WasJustMoving[x][y] = 0;
2036 WasJustFalling[x][y] = 0;
2037 CheckCollision[x][y] = 0;
2039 Pushed[x][y] = FALSE;
2041 ChangeCount[x][y] = 0;
2042 ChangeEvent[x][y] = -1;
2044 ExplodePhase[x][y] = 0;
2045 ExplodeDelay[x][y] = 0;
2046 ExplodeField[x][y] = EX_TYPE_NONE;
2048 RunnerVisit[x][y] = 0;
2049 PlayerVisit[x][y] = 0;
2052 GfxRandom[x][y] = INIT_GFX_RANDOM();
2053 GfxElement[x][y] = EL_UNDEFINED;
2054 GfxAction[x][y] = ACTION_DEFAULT;
2055 GfxDir[x][y] = MV_NONE;
2058 SCAN_PLAYFIELD(x, y)
2060 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2062 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2064 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2067 InitField(x, y, TRUE);
2072 for (i = 0; i < MAX_PLAYERS; i++)
2074 struct PlayerInfo *player = &stored_player[i];
2076 /* set number of special actions for bored and sleeping animation */
2077 player->num_special_action_bored =
2078 get_num_special_action(player->artwork_element,
2079 ACTION_BORING_1, ACTION_BORING_LAST);
2080 player->num_special_action_sleeping =
2081 get_num_special_action(player->artwork_element,
2082 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2085 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2086 emulate_sb ? EMU_SOKOBAN :
2087 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2089 #if USE_NEW_ALL_SLIPPERY
2090 /* initialize type of slippery elements */
2091 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2093 if (!IS_CUSTOM_ELEMENT(i))
2095 /* default: elements slip down either to the left or right randomly */
2096 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2098 /* SP style elements prefer to slip down on the left side */
2099 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2100 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2102 /* BD style elements prefer to slip down on the left side */
2103 if (game.emulation == EMU_BOULDERDASH)
2104 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2109 /* initialize explosion and ignition delay */
2110 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2112 if (!IS_CUSTOM_ELEMENT(i))
2115 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2116 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2117 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2118 int last_phase = (num_phase + 1) * delay;
2119 int half_phase = (num_phase / 2) * delay;
2121 element_info[i].explosion_delay = last_phase - 1;
2122 element_info[i].ignition_delay = half_phase;
2124 if (i == EL_BLACK_ORB)
2125 element_info[i].ignition_delay = 1;
2129 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2130 element_info[i].explosion_delay = 1;
2132 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2133 element_info[i].ignition_delay = 1;
2137 /* correct non-moving belts to start moving left */
2138 for (i = 0; i < NUM_BELTS; i++)
2139 if (game.belt_dir[i] == MV_NONE)
2140 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2142 /* check if any connected player was not found in playfield */
2143 for (i = 0; i < MAX_PLAYERS; i++)
2145 struct PlayerInfo *player = &stored_player[i];
2147 if (player->connected && !player->present)
2149 for (j = 0; j < MAX_PLAYERS; j++)
2151 struct PlayerInfo *some_player = &stored_player[j];
2152 int jx = some_player->jx, jy = some_player->jy;
2154 /* assign first free player found that is present in the playfield */
2155 if (some_player->present && !some_player->connected)
2157 player->present = TRUE;
2158 player->active = TRUE;
2160 some_player->present = FALSE;
2161 some_player->active = FALSE;
2163 player->artwork_element = some_player->artwork_element;
2165 player->block_last_field = some_player->block_last_field;
2166 player->block_delay_adjustment = some_player->block_delay_adjustment;
2168 StorePlayer[jx][jy] = player->element_nr;
2169 player->jx = player->last_jx = jx;
2170 player->jy = player->last_jy = jy;
2180 /* when playing a tape, eliminate all players who do not participate */
2182 for (i = 0; i < MAX_PLAYERS; i++)
2184 if (stored_player[i].active && !tape.player_participates[i])
2186 struct PlayerInfo *player = &stored_player[i];
2187 int jx = player->jx, jy = player->jy;
2189 player->active = FALSE;
2190 StorePlayer[jx][jy] = 0;
2191 Feld[jx][jy] = EL_EMPTY;
2195 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2197 /* when in single player mode, eliminate all but the first active player */
2199 for (i = 0; i < MAX_PLAYERS; i++)
2201 if (stored_player[i].active)
2203 for (j = i + 1; j < MAX_PLAYERS; j++)
2205 if (stored_player[j].active)
2207 struct PlayerInfo *player = &stored_player[j];
2208 int jx = player->jx, jy = player->jy;
2210 player->active = FALSE;
2211 player->present = FALSE;
2213 StorePlayer[jx][jy] = 0;
2214 Feld[jx][jy] = EL_EMPTY;
2221 /* when recording the game, store which players take part in the game */
2224 for (i = 0; i < MAX_PLAYERS; i++)
2225 if (stored_player[i].active)
2226 tape.player_participates[i] = TRUE;
2231 for (i = 0; i < MAX_PLAYERS; i++)
2233 struct PlayerInfo *player = &stored_player[i];
2235 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2240 if (local_player == player)
2241 printf("Player %d is local player.\n", i+1);
2245 if (BorderElement == EL_EMPTY)
2248 SBX_Right = lev_fieldx - SCR_FIELDX;
2250 SBY_Lower = lev_fieldy - SCR_FIELDY;
2255 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2257 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2260 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2261 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2263 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2264 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2266 /* if local player not found, look for custom element that might create
2267 the player (make some assumptions about the right custom element) */
2268 if (!local_player->present)
2270 int start_x = 0, start_y = 0;
2271 int found_rating = 0;
2272 int found_element = EL_UNDEFINED;
2273 int player_nr = local_player->index_nr;
2275 SCAN_PLAYFIELD(x, y)
2277 int element = Feld[x][y];
2282 if (level.use_start_element[player_nr] &&
2283 level.start_element[player_nr] == element &&
2290 found_element = element;
2293 if (!IS_CUSTOM_ELEMENT(element))
2296 if (CAN_CHANGE(element))
2298 for (i = 0; i < element_info[element].num_change_pages; i++)
2300 /* check for player created from custom element as single target */
2301 content = element_info[element].change_page[i].target_element;
2302 is_player = ELEM_IS_PLAYER(content);
2304 if (is_player && (found_rating < 3 || element < found_element))
2310 found_element = element;
2315 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2317 /* check for player created from custom element as explosion content */
2318 content = element_info[element].content.e[xx][yy];
2319 is_player = ELEM_IS_PLAYER(content);
2321 if (is_player && (found_rating < 2 || element < found_element))
2323 start_x = x + xx - 1;
2324 start_y = y + yy - 1;
2327 found_element = element;
2330 if (!CAN_CHANGE(element))
2333 for (i = 0; i < element_info[element].num_change_pages; i++)
2335 /* check for player created from custom element as extended target */
2337 element_info[element].change_page[i].target_content.e[xx][yy];
2339 is_player = ELEM_IS_PLAYER(content);
2341 if (is_player && (found_rating < 1 || element < found_element))
2343 start_x = x + xx - 1;
2344 start_y = y + yy - 1;
2347 found_element = element;
2353 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2354 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2357 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2358 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2363 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2364 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2365 local_player->jx - MIDPOSX);
2367 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2368 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2369 local_player->jy - MIDPOSY);
2374 if (!game.restart_level)
2375 CloseDoor(DOOR_CLOSE_1);
2378 FadeOut(REDRAW_FIELD);
2380 /* !!! FIX THIS (START) !!! */
2381 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2383 InitGameEngine_EM();
2385 /* blit playfield from scroll buffer to normal back buffer for fading in */
2386 BlitScreenToBitmap_EM(backbuffer);
2393 /* after drawing the level, correct some elements */
2394 if (game.timegate_time_left == 0)
2395 CloseAllOpenTimegates();
2397 /* blit playfield from scroll buffer to normal back buffer for fading in */
2398 if (setup.soft_scrolling)
2399 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2401 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2403 /* !!! FIX THIS (END) !!! */
2406 FadeIn(REDRAW_FIELD);
2410 if (!game.restart_level)
2412 /* copy default game door content to main double buffer */
2413 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2414 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2417 SetPanelBackground();
2418 SetDrawBackgroundMask(REDRAW_DOOR_1);
2420 DrawGameDoorValues();
2422 if (!game.restart_level)
2426 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2427 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2428 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2432 /* copy actual game door content to door double buffer for OpenDoor() */
2433 BlitBitmap(drawto, bitmap_db_door,
2434 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2436 OpenDoor(DOOR_OPEN_ALL);
2438 PlaySound(SND_GAME_STARTING);
2440 if (setup.sound_music)
2443 KeyboardAutoRepeatOffUnlessAutoplay();
2447 for (i = 0; i < MAX_PLAYERS; i++)
2448 printf("Player %d %sactive.\n",
2449 i + 1, (stored_player[i].active ? "" : "not "));
2460 game.restart_level = FALSE;
2463 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2465 /* this is used for non-R'n'D game engines to update certain engine values */
2467 /* needed to determine if sounds are played within the visible screen area */
2468 scroll_x = actual_scroll_x;
2469 scroll_y = actual_scroll_y;
2472 void InitMovDir(int x, int y)
2474 int i, element = Feld[x][y];
2475 static int xy[4][2] =
2482 static int direction[3][4] =
2484 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2485 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2486 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2495 Feld[x][y] = EL_BUG;
2496 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2499 case EL_SPACESHIP_RIGHT:
2500 case EL_SPACESHIP_UP:
2501 case EL_SPACESHIP_LEFT:
2502 case EL_SPACESHIP_DOWN:
2503 Feld[x][y] = EL_SPACESHIP;
2504 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2507 case EL_BD_BUTTERFLY_RIGHT:
2508 case EL_BD_BUTTERFLY_UP:
2509 case EL_BD_BUTTERFLY_LEFT:
2510 case EL_BD_BUTTERFLY_DOWN:
2511 Feld[x][y] = EL_BD_BUTTERFLY;
2512 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2515 case EL_BD_FIREFLY_RIGHT:
2516 case EL_BD_FIREFLY_UP:
2517 case EL_BD_FIREFLY_LEFT:
2518 case EL_BD_FIREFLY_DOWN:
2519 Feld[x][y] = EL_BD_FIREFLY;
2520 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2523 case EL_PACMAN_RIGHT:
2525 case EL_PACMAN_LEFT:
2526 case EL_PACMAN_DOWN:
2527 Feld[x][y] = EL_PACMAN;
2528 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2531 case EL_YAMYAM_LEFT:
2532 case EL_YAMYAM_RIGHT:
2534 case EL_YAMYAM_DOWN:
2535 Feld[x][y] = EL_YAMYAM;
2536 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2539 case EL_SP_SNIKSNAK:
2540 MovDir[x][y] = MV_UP;
2543 case EL_SP_ELECTRON:
2544 MovDir[x][y] = MV_LEFT;
2551 Feld[x][y] = EL_MOLE;
2552 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2556 if (IS_CUSTOM_ELEMENT(element))
2558 struct ElementInfo *ei = &element_info[element];
2559 int move_direction_initial = ei->move_direction_initial;
2560 int move_pattern = ei->move_pattern;
2562 if (move_direction_initial == MV_START_PREVIOUS)
2564 if (MovDir[x][y] != MV_NONE)
2567 move_direction_initial = MV_START_AUTOMATIC;
2570 if (move_direction_initial == MV_START_RANDOM)
2571 MovDir[x][y] = 1 << RND(4);
2572 else if (move_direction_initial & MV_ANY_DIRECTION)
2573 MovDir[x][y] = move_direction_initial;
2574 else if (move_pattern == MV_ALL_DIRECTIONS ||
2575 move_pattern == MV_TURNING_LEFT ||
2576 move_pattern == MV_TURNING_RIGHT ||
2577 move_pattern == MV_TURNING_LEFT_RIGHT ||
2578 move_pattern == MV_TURNING_RIGHT_LEFT ||
2579 move_pattern == MV_TURNING_RANDOM)
2580 MovDir[x][y] = 1 << RND(4);
2581 else if (move_pattern == MV_HORIZONTAL)
2582 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2583 else if (move_pattern == MV_VERTICAL)
2584 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2585 else if (move_pattern & MV_ANY_DIRECTION)
2586 MovDir[x][y] = element_info[element].move_pattern;
2587 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2588 move_pattern == MV_ALONG_RIGHT_SIDE)
2590 /* use random direction as default start direction */
2591 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2592 MovDir[x][y] = 1 << RND(4);
2594 for (i = 0; i < NUM_DIRECTIONS; i++)
2596 int x1 = x + xy[i][0];
2597 int y1 = y + xy[i][1];
2599 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2601 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2602 MovDir[x][y] = direction[0][i];
2604 MovDir[x][y] = direction[1][i];
2613 MovDir[x][y] = 1 << RND(4);
2615 if (element != EL_BUG &&
2616 element != EL_SPACESHIP &&
2617 element != EL_BD_BUTTERFLY &&
2618 element != EL_BD_FIREFLY)
2621 for (i = 0; i < NUM_DIRECTIONS; i++)
2623 int x1 = x + xy[i][0];
2624 int y1 = y + xy[i][1];
2626 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2628 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2630 MovDir[x][y] = direction[0][i];
2633 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2634 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2636 MovDir[x][y] = direction[1][i];
2645 GfxDir[x][y] = MovDir[x][y];
2648 void InitAmoebaNr(int x, int y)
2651 int group_nr = AmoebeNachbarNr(x, y);
2655 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2657 if (AmoebaCnt[i] == 0)
2665 AmoebaNr[x][y] = group_nr;
2666 AmoebaCnt[group_nr]++;
2667 AmoebaCnt2[group_nr]++;
2670 static void PlayerWins(struct PlayerInfo *player)
2672 player->LevelSolved = TRUE;
2673 player->GameOver = TRUE;
2675 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2676 level.native_em_level->lev->score : player->score);
2681 static int time, time_final;
2682 static int score, score_final;
2683 static int game_over_delay = 0;
2684 int game_over_delay_value = 50;
2686 if (!local_player->LevelSolved_GameEnd)
2690 /* do not start end game actions before the player stops moving (to exit) */
2691 if (local_player->MovPos)
2694 local_player->LevelSolved_GameEnd = TRUE;
2695 local_player->LevelSolved_SaveTape = tape.recording;
2696 local_player->LevelSolved_SaveScore = !tape.playing;
2698 if (tape.auto_play) /* tape might already be stopped here */
2699 tape.auto_play_level_solved = TRUE;
2705 game_over_delay = game_over_delay_value;
2707 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
2708 score = score_final = local_player->score_final;
2713 score_final += TimeLeft * level.score[SC_TIME_BONUS];
2715 else if (level.time == 0 && TimePlayed < 999)
2718 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
2721 local_player->score_final = score_final;
2723 if (level_editor_test_game)
2726 score = score_final;
2728 DrawGameValue_Time(time);
2729 DrawGameValue_Score(score);
2732 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
2734 /* close exit door after last player */
2735 if (AllPlayersGone &&
2736 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2737 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2739 int element = Feld[ExitX][ExitY];
2741 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2742 EL_SP_EXIT_CLOSING);
2744 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2747 /* player disappears */
2748 DrawLevelField(ExitX, ExitY);
2751 for (i = 0; i < MAX_PLAYERS; i++)
2753 struct PlayerInfo *player = &stored_player[i];
2755 if (player->present)
2757 RemovePlayer(player);
2759 /* player disappears */
2760 DrawLevelField(player->jx, player->jy);
2764 PlaySound(SND_GAME_WINNING);
2767 if (game_over_delay > 0)
2774 if (time != time_final)
2776 int time_to_go = ABS(time_final - time);
2777 int time_count_dir = (time < time_final ? +1 : -1);
2778 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
2780 time += time_count_steps * time_count_dir;
2781 score += time_count_steps * level.score[SC_TIME_BONUS];
2783 DrawGameValue_Time(time);
2784 DrawGameValue_Score(score);
2786 if (time == time_final)
2787 StopSound(SND_GAME_LEVELTIME_BONUS);
2788 else if (setup.sound_loops)
2789 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
2791 PlaySound(SND_GAME_LEVELTIME_BONUS);
2798 boolean raise_level = FALSE;
2800 CloseDoor(DOOR_CLOSE_1);
2802 if (local_player->LevelSolved_SaveTape)
2809 SaveTapeChecked(tape.level_nr); /* ask to save tape */
2811 SaveTape(tape.level_nr); /* ask to save tape */
2815 if (level_editor_test_game)
2817 game_status = GAME_MODE_MAIN;
2824 if (!local_player->LevelSolved_SaveScore)
2826 FadeOut(REDRAW_FIELD);
2828 game_status = GAME_MODE_MAIN;
2830 DrawAndFadeInMainMenu(REDRAW_FIELD);
2835 if (level_nr == leveldir_current->handicap_level)
2837 leveldir_current->handicap_level++;
2838 SaveLevelSetup_SeriesInfo();
2841 if (level_nr < leveldir_current->last_level)
2842 raise_level = TRUE; /* advance to next level */
2844 if ((hi_pos = NewHiScore()) >= 0)
2846 game_status = GAME_MODE_SCORES;
2848 DrawHallOfFame(hi_pos);
2858 FadeOut(REDRAW_FIELD);
2860 game_status = GAME_MODE_MAIN;
2868 DrawAndFadeInMainMenu(REDRAW_FIELD);
2877 LoadScore(level_nr);
2879 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2880 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
2883 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2885 if (local_player->score_final > highscore[k].Score)
2887 /* player has made it to the hall of fame */
2889 if (k < MAX_SCORE_ENTRIES - 1)
2891 int m = MAX_SCORE_ENTRIES - 1;
2894 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2895 if (strEqual(setup.player_name, highscore[l].Name))
2897 if (m == k) /* player's new highscore overwrites his old one */
2901 for (l = m; l > k; l--)
2903 strcpy(highscore[l].Name, highscore[l - 1].Name);
2904 highscore[l].Score = highscore[l - 1].Score;
2911 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2912 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2913 highscore[k].Score = local_player->score_final;
2919 else if (!strncmp(setup.player_name, highscore[k].Name,
2920 MAX_PLAYER_NAME_LEN))
2921 break; /* player already there with a higher score */
2927 SaveScore(level_nr);
2932 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
2934 int element = Feld[x][y];
2935 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2936 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2937 int horiz_move = (dx != 0);
2938 int sign = (horiz_move ? dx : dy);
2939 int step = sign * element_info[element].move_stepsize;
2941 /* special values for move stepsize for spring and things on conveyor belt */
2944 if (CAN_FALL(element) &&
2945 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2946 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2947 else if (element == EL_SPRING)
2948 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2954 inline static int getElementMoveStepsize(int x, int y)
2956 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
2959 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2961 if (player->GfxAction != action || player->GfxDir != dir)
2964 printf("Player frame reset! (%d => %d, %d => %d)\n",
2965 player->GfxAction, action, player->GfxDir, dir);
2968 player->GfxAction = action;
2969 player->GfxDir = dir;
2971 player->StepFrame = 0;
2975 #if USE_GFX_RESET_GFX_ANIMATION
2976 static void ResetGfxFrame(int x, int y, boolean redraw)
2978 int element = Feld[x][y];
2979 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2980 int last_gfx_frame = GfxFrame[x][y];
2982 if (graphic_info[graphic].anim_global_sync)
2983 GfxFrame[x][y] = FrameCounter;
2984 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2985 GfxFrame[x][y] = CustomValue[x][y];
2986 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2987 GfxFrame[x][y] = element_info[element].collect_score;
2988 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
2989 GfxFrame[x][y] = ChangeDelay[x][y];
2991 if (redraw && GfxFrame[x][y] != last_gfx_frame)
2992 DrawLevelGraphicAnimation(x, y, graphic);
2996 static void ResetGfxAnimation(int x, int y)
2998 GfxAction[x][y] = ACTION_DEFAULT;
2999 GfxDir[x][y] = MovDir[x][y];
3002 #if USE_GFX_RESET_GFX_ANIMATION
3003 ResetGfxFrame(x, y, FALSE);
3007 static void ResetRandomAnimationValue(int x, int y)
3009 GfxRandom[x][y] = INIT_GFX_RANDOM();
3012 void InitMovingField(int x, int y, int direction)
3014 int element = Feld[x][y];
3015 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3016 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3019 boolean is_moving_before, is_moving_after;
3021 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3024 /* check if element was/is moving or being moved before/after mode change */
3026 is_moving_before = WasJustMoving[x][y];
3028 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3030 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3032 /* reset animation only for moving elements which change direction of moving
3033 or which just started or stopped moving
3034 (else CEs with property "can move" / "not moving" are reset each frame) */
3035 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3037 if (is_moving_before != is_moving_after ||
3038 direction != MovDir[x][y])
3039 ResetGfxAnimation(x, y);
3041 if ((is_moving_before || is_moving_after) && !continues_moving)
3042 ResetGfxAnimation(x, y);
3045 if (!continues_moving)
3046 ResetGfxAnimation(x, y);
3049 MovDir[x][y] = direction;
3050 GfxDir[x][y] = direction;
3052 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3053 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3054 direction == MV_DOWN && CAN_FALL(element) ?
3055 ACTION_FALLING : ACTION_MOVING);
3057 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3058 ACTION_FALLING : ACTION_MOVING);
3061 /* this is needed for CEs with property "can move" / "not moving" */
3063 if (is_moving_after)
3065 if (Feld[newx][newy] == EL_EMPTY)
3066 Feld[newx][newy] = EL_BLOCKED;
3068 MovDir[newx][newy] = MovDir[x][y];
3070 #if USE_NEW_CUSTOM_VALUE
3071 CustomValue[newx][newy] = CustomValue[x][y];
3074 GfxFrame[newx][newy] = GfxFrame[x][y];
3075 GfxRandom[newx][newy] = GfxRandom[x][y];
3076 GfxAction[newx][newy] = GfxAction[x][y];
3077 GfxDir[newx][newy] = GfxDir[x][y];
3081 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3083 int direction = MovDir[x][y];
3084 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3085 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3091 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3093 int oldx = x, oldy = y;
3094 int direction = MovDir[x][y];
3096 if (direction == MV_LEFT)
3098 else if (direction == MV_RIGHT)
3100 else if (direction == MV_UP)
3102 else if (direction == MV_DOWN)
3105 *comes_from_x = oldx;
3106 *comes_from_y = oldy;
3109 int MovingOrBlocked2Element(int x, int y)
3111 int element = Feld[x][y];
3113 if (element == EL_BLOCKED)
3117 Blocked2Moving(x, y, &oldx, &oldy);
3118 return Feld[oldx][oldy];
3124 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3126 /* like MovingOrBlocked2Element(), but if element is moving
3127 and (x,y) is the field the moving element is just leaving,
3128 return EL_BLOCKED instead of the element value */
3129 int element = Feld[x][y];
3131 if (IS_MOVING(x, y))
3133 if (element == EL_BLOCKED)
3137 Blocked2Moving(x, y, &oldx, &oldy);
3138 return Feld[oldx][oldy];
3147 static void RemoveField(int x, int y)
3149 Feld[x][y] = EL_EMPTY;
3155 #if USE_NEW_CUSTOM_VALUE
3156 CustomValue[x][y] = 0;
3160 ChangeDelay[x][y] = 0;
3161 ChangePage[x][y] = -1;
3162 Pushed[x][y] = FALSE;
3165 ExplodeField[x][y] = EX_TYPE_NONE;
3168 GfxElement[x][y] = EL_UNDEFINED;
3169 GfxAction[x][y] = ACTION_DEFAULT;
3170 GfxDir[x][y] = MV_NONE;
3173 void RemoveMovingField(int x, int y)
3175 int oldx = x, oldy = y, newx = x, newy = y;
3176 int element = Feld[x][y];
3177 int next_element = EL_UNDEFINED;
3179 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3182 if (IS_MOVING(x, y))
3184 Moving2Blocked(x, y, &newx, &newy);
3186 if (Feld[newx][newy] != EL_BLOCKED)
3188 /* element is moving, but target field is not free (blocked), but
3189 already occupied by something different (example: acid pool);
3190 in this case, only remove the moving field, but not the target */
3192 RemoveField(oldx, oldy);
3194 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3196 DrawLevelField(oldx, oldy);
3201 else if (element == EL_BLOCKED)
3203 Blocked2Moving(x, y, &oldx, &oldy);
3204 if (!IS_MOVING(oldx, oldy))
3208 if (element == EL_BLOCKED &&
3209 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3210 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3211 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3212 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3213 next_element = get_next_element(Feld[oldx][oldy]);
3215 RemoveField(oldx, oldy);
3216 RemoveField(newx, newy);
3218 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3220 if (next_element != EL_UNDEFINED)
3221 Feld[oldx][oldy] = next_element;
3223 DrawLevelField(oldx, oldy);
3224 DrawLevelField(newx, newy);
3227 void DrawDynamite(int x, int y)
3229 int sx = SCREENX(x), sy = SCREENY(y);
3230 int graphic = el2img(Feld[x][y]);
3233 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3236 if (IS_WALKABLE_INSIDE(Back[x][y]))
3240 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3241 else if (Store[x][y])
3242 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3244 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3246 if (Back[x][y] || Store[x][y])
3247 DrawGraphicThruMask(sx, sy, graphic, frame);
3249 DrawGraphic(sx, sy, graphic, frame);
3252 void CheckDynamite(int x, int y)
3254 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3258 if (MovDelay[x][y] != 0)
3261 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3267 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3272 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3274 boolean num_checked_players = 0;
3277 for (i = 0; i < MAX_PLAYERS; i++)
3279 if (stored_player[i].active)
3281 int sx = stored_player[i].jx;
3282 int sy = stored_player[i].jy;
3284 if (num_checked_players == 0)
3291 *sx1 = MIN(*sx1, sx);
3292 *sy1 = MIN(*sy1, sy);
3293 *sx2 = MAX(*sx2, sx);
3294 *sy2 = MAX(*sy2, sy);
3297 num_checked_players++;
3302 static boolean checkIfAllPlayersFitToScreen_RND()
3304 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3306 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3308 return (sx2 - sx1 < SCR_FIELDX &&
3309 sy2 - sy1 < SCR_FIELDY);
3312 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3314 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3316 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3318 *sx = (sx1 + sx2) / 2;
3319 *sy = (sy1 + sy2) / 2;
3322 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3323 boolean quick_relocation)
3325 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3326 boolean no_delay = (tape.warp_forward);
3327 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3328 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3330 if (quick_relocation)
3332 int offset = (setup.scroll_delay ? 3 : 0);
3334 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3336 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3337 x > SBX_Right + MIDPOSX ? SBX_Right :
3340 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3341 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3346 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3347 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3348 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3350 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3351 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3352 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3354 /* don't scroll over playfield boundaries */
3355 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3356 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3358 /* don't scroll over playfield boundaries */
3359 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3360 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3363 RedrawPlayfield(TRUE, 0,0,0,0);
3367 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3368 x > SBX_Right + MIDPOSX ? SBX_Right :
3371 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3372 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3375 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3377 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3380 int fx = FX, fy = FY;
3382 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3383 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3385 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3391 fx += dx * TILEX / 2;
3392 fy += dy * TILEY / 2;
3394 ScrollLevel(dx, dy);
3397 /* scroll in two steps of half tile size to make things smoother */
3398 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3400 Delay(wait_delay_value);
3402 /* scroll second step to align at full tile size */
3404 Delay(wait_delay_value);
3409 Delay(wait_delay_value);
3413 void RelocatePlayer(int jx, int jy, int el_player_raw)
3415 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3416 int player_nr = GET_PLAYER_NR(el_player);
3417 struct PlayerInfo *player = &stored_player[player_nr];
3418 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3419 boolean no_delay = (tape.warp_forward);
3420 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3421 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3422 int old_jx = player->jx;
3423 int old_jy = player->jy;
3424 int old_element = Feld[old_jx][old_jy];
3425 int element = Feld[jx][jy];
3426 boolean player_relocated = (old_jx != jx || old_jy != jy);
3428 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3429 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3430 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3431 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3432 int leave_side_horiz = move_dir_horiz;
3433 int leave_side_vert = move_dir_vert;
3434 int enter_side = enter_side_horiz | enter_side_vert;
3435 int leave_side = leave_side_horiz | leave_side_vert;
3437 if (player->GameOver) /* do not reanimate dead player */
3440 if (!player_relocated) /* no need to relocate the player */
3443 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3445 RemoveField(jx, jy); /* temporarily remove newly placed player */
3446 DrawLevelField(jx, jy);
3449 if (player->present)
3451 while (player->MovPos)
3453 ScrollPlayer(player, SCROLL_GO_ON);
3454 ScrollScreen(NULL, SCROLL_GO_ON);
3456 AdvanceFrameAndPlayerCounters(player->index_nr);
3461 Delay(wait_delay_value);
3464 DrawPlayer(player); /* needed here only to cleanup last field */
3465 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3467 player->is_moving = FALSE;
3470 if (IS_CUSTOM_ELEMENT(old_element))
3471 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3473 player->index_bit, leave_side);
3475 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3477 player->index_bit, leave_side);
3479 Feld[jx][jy] = el_player;
3480 InitPlayerField(jx, jy, el_player, TRUE);
3482 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3484 Feld[jx][jy] = element;
3485 InitField(jx, jy, FALSE);
3488 /* only visually relocate centered player */
3489 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3490 level.instant_relocation);
3492 TestIfPlayerTouchesBadThing(jx, jy);
3493 TestIfPlayerTouchesCustomElement(jx, jy);
3495 if (IS_CUSTOM_ELEMENT(element))
3496 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3497 player->index_bit, enter_side);
3499 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3500 player->index_bit, enter_side);
3503 void Explode(int ex, int ey, int phase, int mode)
3509 /* !!! eliminate this variable !!! */
3510 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3512 if (game.explosions_delayed)
3514 ExplodeField[ex][ey] = mode;
3518 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3520 int center_element = Feld[ex][ey];
3521 int artwork_element, explosion_element; /* set these values later */
3524 /* --- This is only really needed (and now handled) in "Impact()". --- */
3525 /* do not explode moving elements that left the explode field in time */
3526 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3527 center_element == EL_EMPTY &&
3528 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3533 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3534 if (mode == EX_TYPE_NORMAL ||
3535 mode == EX_TYPE_CENTER ||
3536 mode == EX_TYPE_CROSS)
3537 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3540 /* remove things displayed in background while burning dynamite */
3541 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3544 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3546 /* put moving element to center field (and let it explode there) */
3547 center_element = MovingOrBlocked2Element(ex, ey);
3548 RemoveMovingField(ex, ey);
3549 Feld[ex][ey] = center_element;
3552 /* now "center_element" is finally determined -- set related values now */
3553 artwork_element = center_element; /* for custom player artwork */
3554 explosion_element = center_element; /* for custom player artwork */
3556 if (IS_PLAYER(ex, ey))
3558 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3560 artwork_element = stored_player[player_nr].artwork_element;
3562 if (level.use_explosion_element[player_nr])
3564 explosion_element = level.explosion_element[player_nr];
3565 artwork_element = explosion_element;
3570 if (mode == EX_TYPE_NORMAL ||
3571 mode == EX_TYPE_CENTER ||
3572 mode == EX_TYPE_CROSS)
3573 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3576 last_phase = element_info[explosion_element].explosion_delay + 1;
3578 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3580 int xx = x - ex + 1;
3581 int yy = y - ey + 1;
3584 if (!IN_LEV_FIELD(x, y) ||
3585 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3586 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3589 element = Feld[x][y];
3591 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3593 element = MovingOrBlocked2Element(x, y);
3595 if (!IS_EXPLOSION_PROOF(element))
3596 RemoveMovingField(x, y);
3599 /* indestructible elements can only explode in center (but not flames) */
3600 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3601 mode == EX_TYPE_BORDER)) ||
3602 element == EL_FLAMES)
3605 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3606 behaviour, for example when touching a yamyam that explodes to rocks
3607 with active deadly shield, a rock is created under the player !!! */
3608 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3610 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3611 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3612 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3614 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3617 if (IS_ACTIVE_BOMB(element))
3619 /* re-activate things under the bomb like gate or penguin */
3620 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3627 /* save walkable background elements while explosion on same tile */
3628 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3629 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3630 Back[x][y] = element;
3632 /* ignite explodable elements reached by other explosion */
3633 if (element == EL_EXPLOSION)
3634 element = Store2[x][y];
3636 if (AmoebaNr[x][y] &&
3637 (element == EL_AMOEBA_FULL ||
3638 element == EL_BD_AMOEBA ||
3639 element == EL_AMOEBA_GROWING))
3641 AmoebaCnt[AmoebaNr[x][y]]--;
3642 AmoebaCnt2[AmoebaNr[x][y]]--;
3647 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3649 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3651 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3653 if (PLAYERINFO(ex, ey)->use_murphy)
3654 Store[x][y] = EL_EMPTY;
3657 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3658 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3659 else if (ELEM_IS_PLAYER(center_element))
3660 Store[x][y] = EL_EMPTY;
3661 else if (center_element == EL_YAMYAM)
3662 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3663 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3664 Store[x][y] = element_info[center_element].content.e[xx][yy];
3666 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3667 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3668 otherwise) -- FIX THIS !!! */
3669 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3670 Store[x][y] = element_info[element].content.e[1][1];
3672 else if (!CAN_EXPLODE(element))
3673 Store[x][y] = element_info[element].content.e[1][1];
3676 Store[x][y] = EL_EMPTY;
3678 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3679 center_element == EL_AMOEBA_TO_DIAMOND)
3680 Store2[x][y] = element;
3682 Feld[x][y] = EL_EXPLOSION;
3683 GfxElement[x][y] = artwork_element;
3685 ExplodePhase[x][y] = 1;
3686 ExplodeDelay[x][y] = last_phase;
3691 if (center_element == EL_YAMYAM)
3692 game.yamyam_content_nr =
3693 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3705 GfxFrame[x][y] = 0; /* restart explosion animation */
3707 last_phase = ExplodeDelay[x][y];
3709 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3713 /* activate this even in non-DEBUG version until cause for crash in
3714 getGraphicAnimationFrame() (see below) is found and eliminated */
3720 /* this can happen if the player leaves an explosion just in time */
3721 if (GfxElement[x][y] == EL_UNDEFINED)
3722 GfxElement[x][y] = EL_EMPTY;
3724 if (GfxElement[x][y] == EL_UNDEFINED)
3727 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3728 printf("Explode(): This should never happen!\n");
3731 GfxElement[x][y] = EL_EMPTY;
3737 border_element = Store2[x][y];
3738 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3739 border_element = StorePlayer[x][y];
3741 if (phase == element_info[border_element].ignition_delay ||
3742 phase == last_phase)
3744 boolean border_explosion = FALSE;
3746 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3747 !PLAYER_EXPLOSION_PROTECTED(x, y))
3749 KillPlayerUnlessExplosionProtected(x, y);
3750 border_explosion = TRUE;
3752 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3754 Feld[x][y] = Store2[x][y];
3757 border_explosion = TRUE;
3759 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3761 AmoebeUmwandeln(x, y);
3763 border_explosion = TRUE;
3766 /* if an element just explodes due to another explosion (chain-reaction),
3767 do not immediately end the new explosion when it was the last frame of
3768 the explosion (as it would be done in the following "if"-statement!) */
3769 if (border_explosion && phase == last_phase)
3773 if (phase == last_phase)
3777 element = Feld[x][y] = Store[x][y];
3778 Store[x][y] = Store2[x][y] = 0;
3779 GfxElement[x][y] = EL_UNDEFINED;
3781 /* player can escape from explosions and might therefore be still alive */
3782 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3783 element <= EL_PLAYER_IS_EXPLODING_4)
3785 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3786 int explosion_element = EL_PLAYER_1 + player_nr;
3787 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3788 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3790 if (level.use_explosion_element[player_nr])
3791 explosion_element = level.explosion_element[player_nr];
3793 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3794 element_info[explosion_element].content.e[xx][yy]);
3797 /* restore probably existing indestructible background element */
3798 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3799 element = Feld[x][y] = Back[x][y];
3802 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3803 GfxDir[x][y] = MV_NONE;
3804 ChangeDelay[x][y] = 0;
3805 ChangePage[x][y] = -1;
3807 #if USE_NEW_CUSTOM_VALUE
3808 CustomValue[x][y] = 0;
3811 InitField_WithBug2(x, y, FALSE);
3813 DrawLevelField(x, y);
3815 TestIfElementTouchesCustomElement(x, y);
3817 if (GFX_CRUMBLED(element))
3818 DrawLevelFieldCrumbledSandNeighbours(x, y);
3820 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3821 StorePlayer[x][y] = 0;
3823 if (ELEM_IS_PLAYER(element))
3824 RelocatePlayer(x, y, element);
3826 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3828 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3829 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3832 DrawLevelFieldCrumbledSand(x, y);
3834 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3836 DrawLevelElement(x, y, Back[x][y]);
3837 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3839 else if (IS_WALKABLE_UNDER(Back[x][y]))
3841 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3842 DrawLevelElementThruMask(x, y, Back[x][y]);
3844 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3845 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3849 void DynaExplode(int ex, int ey)
3852 int dynabomb_element = Feld[ex][ey];
3853 int dynabomb_size = 1;
3854 boolean dynabomb_xl = FALSE;
3855 struct PlayerInfo *player;
3856 static int xy[4][2] =
3864 if (IS_ACTIVE_BOMB(dynabomb_element))
3866 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3867 dynabomb_size = player->dynabomb_size;
3868 dynabomb_xl = player->dynabomb_xl;
3869 player->dynabombs_left++;
3872 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3874 for (i = 0; i < NUM_DIRECTIONS; i++)
3876 for (j = 1; j <= dynabomb_size; j++)
3878 int x = ex + j * xy[i][0];
3879 int y = ey + j * xy[i][1];
3882 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3885 element = Feld[x][y];
3887 /* do not restart explosions of fields with active bombs */
3888 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3891 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3893 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3894 !IS_DIGGABLE(element) && !dynabomb_xl)
3900 void Bang(int x, int y)
3902 int element = MovingOrBlocked2Element(x, y);
3903 int explosion_type = EX_TYPE_NORMAL;
3905 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3907 struct PlayerInfo *player = PLAYERINFO(x, y);
3909 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3910 player->element_nr);
3912 if (level.use_explosion_element[player->index_nr])
3914 int explosion_element = level.explosion_element[player->index_nr];
3916 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3917 explosion_type = EX_TYPE_CROSS;
3918 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3919 explosion_type = EX_TYPE_CENTER;
3927 case EL_BD_BUTTERFLY:
3930 case EL_DARK_YAMYAM:
3934 RaiseScoreElement(element);
3937 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3938 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3939 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3940 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3941 case EL_DYNABOMB_INCREASE_NUMBER:
3942 case EL_DYNABOMB_INCREASE_SIZE:
3943 case EL_DYNABOMB_INCREASE_POWER:
3944 explosion_type = EX_TYPE_DYNA;
3949 case EL_LAMP_ACTIVE:
3950 case EL_AMOEBA_TO_DIAMOND:
3951 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3952 explosion_type = EX_TYPE_CENTER;
3956 if (element_info[element].explosion_type == EXPLODES_CROSS)
3957 explosion_type = EX_TYPE_CROSS;
3958 else if (element_info[element].explosion_type == EXPLODES_1X1)
3959 explosion_type = EX_TYPE_CENTER;
3963 if (explosion_type == EX_TYPE_DYNA)
3966 Explode(x, y, EX_PHASE_START, explosion_type);
3968 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3971 void SplashAcid(int x, int y)
3973 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3974 (!IN_LEV_FIELD(x - 1, y - 2) ||
3975 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3976 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3978 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3979 (!IN_LEV_FIELD(x + 1, y - 2) ||
3980 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3981 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3983 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3986 static void InitBeltMovement()
3988 static int belt_base_element[4] =
3990 EL_CONVEYOR_BELT_1_LEFT,
3991 EL_CONVEYOR_BELT_2_LEFT,
3992 EL_CONVEYOR_BELT_3_LEFT,
3993 EL_CONVEYOR_BELT_4_LEFT
3995 static int belt_base_active_element[4] =
3997 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3998 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3999 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4000 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4005 /* set frame order for belt animation graphic according to belt direction */
4006 for (i = 0; i < NUM_BELTS; i++)
4010 for (j = 0; j < NUM_BELT_PARTS; j++)
4012 int element = belt_base_active_element[belt_nr] + j;
4013 int graphic = el2img(element);
4015 if (game.belt_dir[i] == MV_LEFT)
4016 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4018 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4022 SCAN_PLAYFIELD(x, y)
4024 int element = Feld[x][y];
4026 for (i = 0; i < NUM_BELTS; i++)
4028 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4030 int e_belt_nr = getBeltNrFromBeltElement(element);
4033 if (e_belt_nr == belt_nr)
4035 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4037 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4044 static void ToggleBeltSwitch(int x, int y)
4046 static int belt_base_element[4] =
4048 EL_CONVEYOR_BELT_1_LEFT,
4049 EL_CONVEYOR_BELT_2_LEFT,
4050 EL_CONVEYOR_BELT_3_LEFT,
4051 EL_CONVEYOR_BELT_4_LEFT
4053 static int belt_base_active_element[4] =
4055 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4056 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4057 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4058 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4060 static int belt_base_switch_element[4] =
4062 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4063 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4064 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4065 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4067 static int belt_move_dir[4] =
4075 int element = Feld[x][y];
4076 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4077 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4078 int belt_dir = belt_move_dir[belt_dir_nr];
4081 if (!IS_BELT_SWITCH(element))
4084 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4085 game.belt_dir[belt_nr] = belt_dir;
4087 if (belt_dir_nr == 3)
4090 /* set frame order for belt animation graphic according to belt direction */
4091 for (i = 0; i < NUM_BELT_PARTS; i++)
4093 int element = belt_base_active_element[belt_nr] + i;
4094 int graphic = el2img(element);
4096 if (belt_dir == MV_LEFT)
4097 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4099 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4102 SCAN_PLAYFIELD(xx, yy)
4104 int element = Feld[xx][yy];
4106 if (IS_BELT_SWITCH(element))
4108 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4110 if (e_belt_nr == belt_nr)
4112 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4113 DrawLevelField(xx, yy);
4116 else if (IS_BELT(element) && belt_dir != MV_NONE)
4118 int e_belt_nr = getBeltNrFromBeltElement(element);
4120 if (e_belt_nr == belt_nr)
4122 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4124 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4125 DrawLevelField(xx, yy);
4128 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4130 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4132 if (e_belt_nr == belt_nr)
4134 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4136 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4137 DrawLevelField(xx, yy);
4143 static void ToggleSwitchgateSwitch(int x, int y)
4147 game.switchgate_pos = !game.switchgate_pos;
4149 SCAN_PLAYFIELD(xx, yy)
4151 int element = Feld[xx][yy];
4153 #if !USE_BOTH_SWITCHGATE_SWITCHES
4154 if (element == EL_SWITCHGATE_SWITCH_UP ||
4155 element == EL_SWITCHGATE_SWITCH_DOWN)
4157 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4158 DrawLevelField(xx, yy);
4161 if (element == EL_SWITCHGATE_SWITCH_UP)
4163 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4164 DrawLevelField(xx, yy);
4166 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4168 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4169 DrawLevelField(xx, yy);
4172 else if (element == EL_SWITCHGATE_OPEN ||
4173 element == EL_SWITCHGATE_OPENING)
4175 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4177 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4179 else if (element == EL_SWITCHGATE_CLOSED ||
4180 element == EL_SWITCHGATE_CLOSING)
4182 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4184 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4189 static int getInvisibleActiveFromInvisibleElement(int element)
4191 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4192 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4193 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4197 static int getInvisibleFromInvisibleActiveElement(int element)
4199 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4200 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4201 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4205 static void RedrawAllLightSwitchesAndInvisibleElements()
4209 SCAN_PLAYFIELD(x, y)
4211 int element = Feld[x][y];
4213 if (element == EL_LIGHT_SWITCH &&
4214 game.light_time_left > 0)
4216 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4217 DrawLevelField(x, y);
4219 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4220 game.light_time_left == 0)
4222 Feld[x][y] = EL_LIGHT_SWITCH;
4223 DrawLevelField(x, y);
4225 else if (element == EL_EMC_DRIPPER &&
4226 game.light_time_left > 0)
4228 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4229 DrawLevelField(x, y);
4231 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4232 game.light_time_left == 0)
4234 Feld[x][y] = EL_EMC_DRIPPER;
4235 DrawLevelField(x, y);
4237 else if (element == EL_INVISIBLE_STEELWALL ||
4238 element == EL_INVISIBLE_WALL ||
4239 element == EL_INVISIBLE_SAND)
4241 if (game.light_time_left > 0)
4242 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4244 DrawLevelField(x, y);
4246 /* uncrumble neighbour fields, if needed */
4247 if (element == EL_INVISIBLE_SAND)
4248 DrawLevelFieldCrumbledSandNeighbours(x, y);
4250 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4251 element == EL_INVISIBLE_WALL_ACTIVE ||
4252 element == EL_INVISIBLE_SAND_ACTIVE)
4254 if (game.light_time_left == 0)
4255 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4257 DrawLevelField(x, y);
4259 /* re-crumble neighbour fields, if needed */
4260 if (element == EL_INVISIBLE_SAND)
4261 DrawLevelFieldCrumbledSandNeighbours(x, y);
4266 static void RedrawAllInvisibleElementsForLenses()
4270 SCAN_PLAYFIELD(x, y)
4272 int element = Feld[x][y];
4274 if (element == EL_EMC_DRIPPER &&
4275 game.lenses_time_left > 0)
4277 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4278 DrawLevelField(x, y);
4280 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4281 game.lenses_time_left == 0)
4283 Feld[x][y] = EL_EMC_DRIPPER;
4284 DrawLevelField(x, y);
4286 else if (element == EL_INVISIBLE_STEELWALL ||
4287 element == EL_INVISIBLE_WALL ||
4288 element == EL_INVISIBLE_SAND)
4290 if (game.lenses_time_left > 0)
4291 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4293 DrawLevelField(x, y);
4295 /* uncrumble neighbour fields, if needed */
4296 if (element == EL_INVISIBLE_SAND)
4297 DrawLevelFieldCrumbledSandNeighbours(x, y);
4299 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4300 element == EL_INVISIBLE_WALL_ACTIVE ||
4301 element == EL_INVISIBLE_SAND_ACTIVE)
4303 if (game.lenses_time_left == 0)
4304 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4306 DrawLevelField(x, y);
4308 /* re-crumble neighbour fields, if needed */
4309 if (element == EL_INVISIBLE_SAND)
4310 DrawLevelFieldCrumbledSandNeighbours(x, y);
4315 static void RedrawAllInvisibleElementsForMagnifier()
4319 SCAN_PLAYFIELD(x, y)
4321 int element = Feld[x][y];
4323 if (element == EL_EMC_FAKE_GRASS &&
4324 game.magnify_time_left > 0)
4326 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4327 DrawLevelField(x, y);
4329 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4330 game.magnify_time_left == 0)
4332 Feld[x][y] = EL_EMC_FAKE_GRASS;
4333 DrawLevelField(x, y);
4335 else if (IS_GATE_GRAY(element) &&
4336 game.magnify_time_left > 0)
4338 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4339 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4340 IS_EM_GATE_GRAY(element) ?
4341 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4342 IS_EMC_GATE_GRAY(element) ?
4343 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4345 DrawLevelField(x, y);
4347 else if (IS_GATE_GRAY_ACTIVE(element) &&
4348 game.magnify_time_left == 0)
4350 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4351 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4352 IS_EM_GATE_GRAY_ACTIVE(element) ?
4353 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4354 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4355 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4357 DrawLevelField(x, y);
4362 static void ToggleLightSwitch(int x, int y)
4364 int element = Feld[x][y];
4366 game.light_time_left =
4367 (element == EL_LIGHT_SWITCH ?
4368 level.time_light * FRAMES_PER_SECOND : 0);
4370 RedrawAllLightSwitchesAndInvisibleElements();
4373 static void ActivateTimegateSwitch(int x, int y)
4377 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4379 SCAN_PLAYFIELD(xx, yy)
4381 int element = Feld[xx][yy];
4383 if (element == EL_TIMEGATE_CLOSED ||
4384 element == EL_TIMEGATE_CLOSING)
4386 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4387 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4391 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4393 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4394 DrawLevelField(xx, yy);
4400 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4403 void Impact(int x, int y)
4405 boolean last_line = (y == lev_fieldy - 1);
4406 boolean object_hit = FALSE;
4407 boolean impact = (last_line || object_hit);
4408 int element = Feld[x][y];
4409 int smashed = EL_STEELWALL;
4411 if (!last_line) /* check if element below was hit */
4413 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4416 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4417 MovDir[x][y + 1] != MV_DOWN ||
4418 MovPos[x][y + 1] <= TILEY / 2));
4420 /* do not smash moving elements that left the smashed field in time */
4421 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4422 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4425 #if USE_QUICKSAND_IMPACT_BUGFIX
4426 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4428 RemoveMovingField(x, y + 1);
4429 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4430 Feld[x][y + 2] = EL_ROCK;
4431 DrawLevelField(x, y + 2);
4438 smashed = MovingOrBlocked2Element(x, y + 1);
4440 impact = (last_line || object_hit);
4443 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4445 SplashAcid(x, y + 1);
4449 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4450 /* only reset graphic animation if graphic really changes after impact */
4452 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4454 ResetGfxAnimation(x, y);
4455 DrawLevelField(x, y);
4458 if (impact && CAN_EXPLODE_IMPACT(element))
4463 else if (impact && element == EL_PEARL)
4465 ResetGfxAnimation(x, y);
4467 Feld[x][y] = EL_PEARL_BREAKING;
4468 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4471 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4473 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4478 if (impact && element == EL_AMOEBA_DROP)
4480 if (object_hit && IS_PLAYER(x, y + 1))
4481 KillPlayerUnlessEnemyProtected(x, y + 1);
4482 else if (object_hit && smashed == EL_PENGUIN)
4486 Feld[x][y] = EL_AMOEBA_GROWING;
4487 Store[x][y] = EL_AMOEBA_WET;
4489 ResetRandomAnimationValue(x, y);
4494 if (object_hit) /* check which object was hit */
4496 if (CAN_PASS_MAGIC_WALL(element) &&
4497 (smashed == EL_MAGIC_WALL ||
4498 smashed == EL_BD_MAGIC_WALL))
4501 int activated_magic_wall =
4502 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4503 EL_BD_MAGIC_WALL_ACTIVE);
4505 /* activate magic wall / mill */
4506 SCAN_PLAYFIELD(xx, yy)
4507 if (Feld[xx][yy] == smashed)
4508 Feld[xx][yy] = activated_magic_wall;
4510 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4511 game.magic_wall_active = TRUE;
4513 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4514 SND_MAGIC_WALL_ACTIVATING :
4515 SND_BD_MAGIC_WALL_ACTIVATING));
4518 if (IS_PLAYER(x, y + 1))
4520 if (CAN_SMASH_PLAYER(element))
4522 KillPlayerUnlessEnemyProtected(x, y + 1);
4526 else if (smashed == EL_PENGUIN)
4528 if (CAN_SMASH_PLAYER(element))
4534 else if (element == EL_BD_DIAMOND)
4536 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4542 else if (((element == EL_SP_INFOTRON ||
4543 element == EL_SP_ZONK) &&
4544 (smashed == EL_SP_SNIKSNAK ||
4545 smashed == EL_SP_ELECTRON ||
4546 smashed == EL_SP_DISK_ORANGE)) ||
4547 (element == EL_SP_INFOTRON &&
4548 smashed == EL_SP_DISK_YELLOW))
4553 else if (CAN_SMASH_EVERYTHING(element))
4555 if (IS_CLASSIC_ENEMY(smashed) ||
4556 CAN_EXPLODE_SMASHED(smashed))
4561 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4563 if (smashed == EL_LAMP ||
4564 smashed == EL_LAMP_ACTIVE)
4569 else if (smashed == EL_NUT)
4571 Feld[x][y + 1] = EL_NUT_BREAKING;
4572 PlayLevelSound(x, y, SND_NUT_BREAKING);
4573 RaiseScoreElement(EL_NUT);
4576 else if (smashed == EL_PEARL)
4578 ResetGfxAnimation(x, y);
4580 Feld[x][y + 1] = EL_PEARL_BREAKING;
4581 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4584 else if (smashed == EL_DIAMOND)
4586 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4587 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4590 else if (IS_BELT_SWITCH(smashed))
4592 ToggleBeltSwitch(x, y + 1);
4594 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4595 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4597 ToggleSwitchgateSwitch(x, y + 1);
4599 else if (smashed == EL_LIGHT_SWITCH ||
4600 smashed == EL_LIGHT_SWITCH_ACTIVE)
4602 ToggleLightSwitch(x, y + 1);
4607 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4610 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4612 CheckElementChangeBySide(x, y + 1, smashed, element,
4613 CE_SWITCHED, CH_SIDE_TOP);
4614 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4620 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4625 /* play sound of magic wall / mill */
4627 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4628 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4630 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4631 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4632 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4633 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4638 /* play sound of object that hits the ground */
4639 if (last_line || object_hit)
4640 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4643 inline static void TurnRoundExt(int x, int y)
4655 { 0, 0 }, { 0, 0 }, { 0, 0 },
4660 int left, right, back;
4664 { MV_DOWN, MV_UP, MV_RIGHT },
4665 { MV_UP, MV_DOWN, MV_LEFT },
4667 { MV_LEFT, MV_RIGHT, MV_DOWN },
4671 { MV_RIGHT, MV_LEFT, MV_UP }
4674 int element = Feld[x][y];
4675 int move_pattern = element_info[element].move_pattern;
4677 int old_move_dir = MovDir[x][y];
4678 int left_dir = turn[old_move_dir].left;
4679 int right_dir = turn[old_move_dir].right;
4680 int back_dir = turn[old_move_dir].back;
4682 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4683 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4684 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4685 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4687 int left_x = x + left_dx, left_y = y + left_dy;
4688 int right_x = x + right_dx, right_y = y + right_dy;
4689 int move_x = x + move_dx, move_y = y + move_dy;
4693 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4695 TestIfBadThingTouchesOtherBadThing(x, y);
4697 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4698 MovDir[x][y] = right_dir;
4699 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4700 MovDir[x][y] = left_dir;
4702 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4704 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4707 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4709 TestIfBadThingTouchesOtherBadThing(x, y);
4711 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4712 MovDir[x][y] = left_dir;
4713 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4714 MovDir[x][y] = right_dir;
4716 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4718 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4721 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4723 TestIfBadThingTouchesOtherBadThing(x, y);
4725 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4726 MovDir[x][y] = left_dir;
4727 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4728 MovDir[x][y] = right_dir;
4730 if (MovDir[x][y] != old_move_dir)
4733 else if (element == EL_YAMYAM)
4735 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4736 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4738 if (can_turn_left && can_turn_right)
4739 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4740 else if (can_turn_left)
4741 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4742 else if (can_turn_right)
4743 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4745 MovDir[x][y] = back_dir;
4747 MovDelay[x][y] = 16 + 16 * RND(3);
4749 else if (element == EL_DARK_YAMYAM)
4751 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4753 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4756 if (can_turn_left && can_turn_right)
4757 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4758 else if (can_turn_left)
4759 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4760 else if (can_turn_right)
4761 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4763 MovDir[x][y] = back_dir;
4765 MovDelay[x][y] = 16 + 16 * RND(3);
4767 else if (element == EL_PACMAN)
4769 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4770 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4772 if (can_turn_left && can_turn_right)
4773 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4774 else if (can_turn_left)
4775 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4776 else if (can_turn_right)
4777 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4779 MovDir[x][y] = back_dir;
4781 MovDelay[x][y] = 6 + RND(40);
4783 else if (element == EL_PIG)
4785 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4786 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4787 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4788 boolean should_turn_left, should_turn_right, should_move_on;
4790 int rnd = RND(rnd_value);
4792 should_turn_left = (can_turn_left &&
4794 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4795 y + back_dy + left_dy)));
4796 should_turn_right = (can_turn_right &&
4798 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4799 y + back_dy + right_dy)));
4800 should_move_on = (can_move_on &&
4803 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4804 y + move_dy + left_dy) ||
4805 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4806 y + move_dy + right_dy)));
4808 if (should_turn_left || should_turn_right || should_move_on)
4810 if (should_turn_left && should_turn_right && should_move_on)
4811 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4812 rnd < 2 * rnd_value / 3 ? right_dir :
4814 else if (should_turn_left && should_turn_right)
4815 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4816 else if (should_turn_left && should_move_on)
4817 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4818 else if (should_turn_right && should_move_on)
4819 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4820 else if (should_turn_left)
4821 MovDir[x][y] = left_dir;
4822 else if (should_turn_right)
4823 MovDir[x][y] = right_dir;
4824 else if (should_move_on)
4825 MovDir[x][y] = old_move_dir;
4827 else if (can_move_on && rnd > rnd_value / 8)
4828 MovDir[x][y] = old_move_dir;
4829 else if (can_turn_left && can_turn_right)
4830 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4831 else if (can_turn_left && rnd > rnd_value / 8)
4832 MovDir[x][y] = left_dir;
4833 else if (can_turn_right && rnd > rnd_value/8)
4834 MovDir[x][y] = right_dir;
4836 MovDir[x][y] = back_dir;
4838 xx = x + move_xy[MovDir[x][y]].dx;
4839 yy = y + move_xy[MovDir[x][y]].dy;
4841 if (!IN_LEV_FIELD(xx, yy) ||
4842 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4843 MovDir[x][y] = old_move_dir;
4847 else if (element == EL_DRAGON)
4849 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4850 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4851 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4853 int rnd = RND(rnd_value);
4855 if (can_move_on && rnd > rnd_value / 8)
4856 MovDir[x][y] = old_move_dir;
4857 else if (can_turn_left && can_turn_right)
4858 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4859 else if (can_turn_left && rnd > rnd_value / 8)
4860 MovDir[x][y] = left_dir;
4861 else if (can_turn_right && rnd > rnd_value / 8)
4862 MovDir[x][y] = right_dir;
4864 MovDir[x][y] = back_dir;
4866 xx = x + move_xy[MovDir[x][y]].dx;
4867 yy = y + move_xy[MovDir[x][y]].dy;
4869 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4870 MovDir[x][y] = old_move_dir;
4874 else if (element == EL_MOLE)
4876 boolean can_move_on =
4877 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4878 IS_AMOEBOID(Feld[move_x][move_y]) ||
4879 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4882 boolean can_turn_left =
4883 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4884 IS_AMOEBOID(Feld[left_x][left_y])));
4886 boolean can_turn_right =
4887 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4888 IS_AMOEBOID(Feld[right_x][right_y])));
4890 if (can_turn_left && can_turn_right)
4891 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4892 else if (can_turn_left)
4893 MovDir[x][y] = left_dir;
4895 MovDir[x][y] = right_dir;
4898 if (MovDir[x][y] != old_move_dir)
4901 else if (element == EL_BALLOON)
4903 MovDir[x][y] = game.wind_direction;
4906 else if (element == EL_SPRING)
4908 #if USE_NEW_SPRING_BUMPER
4909 if (MovDir[x][y] & MV_HORIZONTAL)
4911 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4912 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4914 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4915 ResetGfxAnimation(move_x, move_y);
4916 DrawLevelField(move_x, move_y);
4918 MovDir[x][y] = back_dir;
4920 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4921 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4922 MovDir[x][y] = MV_NONE;
4925 if (MovDir[x][y] & MV_HORIZONTAL &&
4926 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4927 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4928 MovDir[x][y] = MV_NONE;
4933 else if (element == EL_ROBOT ||
4934 element == EL_SATELLITE ||
4935 element == EL_PENGUIN ||
4936 element == EL_EMC_ANDROID)
4938 int attr_x = -1, attr_y = -1;
4949 for (i = 0; i < MAX_PLAYERS; i++)
4951 struct PlayerInfo *player = &stored_player[i];
4952 int jx = player->jx, jy = player->jy;
4954 if (!player->active)
4958 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4966 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4967 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4968 game.engine_version < VERSION_IDENT(3,1,0,0)))
4974 if (element == EL_PENGUIN)
4977 static int xy[4][2] =
4985 for (i = 0; i < NUM_DIRECTIONS; i++)
4987 int ex = x + xy[i][0];
4988 int ey = y + xy[i][1];
4990 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4999 MovDir[x][y] = MV_NONE;
5001 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5002 else if (attr_x > x)
5003 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5005 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5006 else if (attr_y > y)
5007 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5009 if (element == EL_ROBOT)
5013 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5014 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5015 Moving2Blocked(x, y, &newx, &newy);
5017 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5018 MovDelay[x][y] = 8 + 8 * !RND(3);
5020 MovDelay[x][y] = 16;
5022 else if (element == EL_PENGUIN)
5028 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5030 boolean first_horiz = RND(2);
5031 int new_move_dir = MovDir[x][y];
5034 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5035 Moving2Blocked(x, y, &newx, &newy);
5037 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5041 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5042 Moving2Blocked(x, y, &newx, &newy);
5044 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5047 MovDir[x][y] = old_move_dir;
5051 else if (element == EL_SATELLITE)
5057 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5059 boolean first_horiz = RND(2);
5060 int new_move_dir = MovDir[x][y];
5063 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5064 Moving2Blocked(x, y, &newx, &newy);
5066 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5070 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5071 Moving2Blocked(x, y, &newx, &newy);
5073 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5076 MovDir[x][y] = old_move_dir;
5080 else if (element == EL_EMC_ANDROID)
5082 static int check_pos[16] =
5084 -1, /* 0 => (invalid) */
5085 7, /* 1 => MV_LEFT */
5086 3, /* 2 => MV_RIGHT */
5087 -1, /* 3 => (invalid) */
5089 0, /* 5 => MV_LEFT | MV_UP */
5090 2, /* 6 => MV_RIGHT | MV_UP */
5091 -1, /* 7 => (invalid) */
5092 5, /* 8 => MV_DOWN */
5093 6, /* 9 => MV_LEFT | MV_DOWN */
5094 4, /* 10 => MV_RIGHT | MV_DOWN */
5095 -1, /* 11 => (invalid) */
5096 -1, /* 12 => (invalid) */
5097 -1, /* 13 => (invalid) */
5098 -1, /* 14 => (invalid) */
5099 -1, /* 15 => (invalid) */
5107 { -1, -1, MV_LEFT | MV_UP },
5109 { +1, -1, MV_RIGHT | MV_UP },
5110 { +1, 0, MV_RIGHT },
5111 { +1, +1, MV_RIGHT | MV_DOWN },
5113 { -1, +1, MV_LEFT | MV_DOWN },
5116 int start_pos, check_order;
5117 boolean can_clone = FALSE;
5120 /* check if there is any free field around current position */
5121 for (i = 0; i < 8; i++)
5123 int newx = x + check_xy[i].dx;
5124 int newy = y + check_xy[i].dy;
5126 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5134 if (can_clone) /* randomly find an element to clone */
5138 start_pos = check_pos[RND(8)];
5139 check_order = (RND(2) ? -1 : +1);
5141 for (i = 0; i < 8; i++)
5143 int pos_raw = start_pos + i * check_order;
5144 int pos = (pos_raw + 8) % 8;
5145 int newx = x + check_xy[pos].dx;
5146 int newy = y + check_xy[pos].dy;
5148 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5150 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5151 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5153 Store[x][y] = Feld[newx][newy];
5162 if (can_clone) /* randomly find a direction to move */
5166 start_pos = check_pos[RND(8)];
5167 check_order = (RND(2) ? -1 : +1);
5169 for (i = 0; i < 8; i++)
5171 int pos_raw = start_pos + i * check_order;
5172 int pos = (pos_raw + 8) % 8;
5173 int newx = x + check_xy[pos].dx;
5174 int newy = y + check_xy[pos].dy;
5175 int new_move_dir = check_xy[pos].dir;
5177 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5179 MovDir[x][y] = new_move_dir;
5180 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5189 if (can_clone) /* cloning and moving successful */
5192 /* cannot clone -- try to move towards player */
5194 start_pos = check_pos[MovDir[x][y] & 0x0f];
5195 check_order = (RND(2) ? -1 : +1);
5197 for (i = 0; i < 3; i++)
5199 /* first check start_pos, then previous/next or (next/previous) pos */
5200 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5201 int pos = (pos_raw + 8) % 8;
5202 int newx = x + check_xy[pos].dx;
5203 int newy = y + check_xy[pos].dy;
5204 int new_move_dir = check_xy[pos].dir;
5206 if (IS_PLAYER(newx, newy))
5209 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5211 MovDir[x][y] = new_move_dir;
5212 MovDelay[x][y] = level.android_move_time * 8 + 1;
5219 else if (move_pattern == MV_TURNING_LEFT ||
5220 move_pattern == MV_TURNING_RIGHT ||
5221 move_pattern == MV_TURNING_LEFT_RIGHT ||
5222 move_pattern == MV_TURNING_RIGHT_LEFT ||
5223 move_pattern == MV_TURNING_RANDOM ||
5224 move_pattern == MV_ALL_DIRECTIONS)
5226 boolean can_turn_left =
5227 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5228 boolean can_turn_right =
5229 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5231 if (element_info[element].move_stepsize == 0) /* "not moving" */
5234 if (move_pattern == MV_TURNING_LEFT)
5235 MovDir[x][y] = left_dir;
5236 else if (move_pattern == MV_TURNING_RIGHT)
5237 MovDir[x][y] = right_dir;
5238 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5239 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5240 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5241 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5242 else if (move_pattern == MV_TURNING_RANDOM)
5243 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5244 can_turn_right && !can_turn_left ? right_dir :
5245 RND(2) ? left_dir : right_dir);
5246 else if (can_turn_left && can_turn_right)
5247 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5248 else if (can_turn_left)
5249 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5250 else if (can_turn_right)
5251 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5253 MovDir[x][y] = back_dir;
5255 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5257 else if (move_pattern == MV_HORIZONTAL ||
5258 move_pattern == MV_VERTICAL)
5260 if (move_pattern & old_move_dir)
5261 MovDir[x][y] = back_dir;
5262 else if (move_pattern == MV_HORIZONTAL)
5263 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5264 else if (move_pattern == MV_VERTICAL)
5265 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5267 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5269 else if (move_pattern & MV_ANY_DIRECTION)
5271 MovDir[x][y] = move_pattern;
5272 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5274 else if (move_pattern & MV_WIND_DIRECTION)
5276 MovDir[x][y] = game.wind_direction;
5277 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5279 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5281 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5282 MovDir[x][y] = left_dir;
5283 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5284 MovDir[x][y] = right_dir;
5286 if (MovDir[x][y] != old_move_dir)
5287 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5289 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5291 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5292 MovDir[x][y] = right_dir;
5293 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5294 MovDir[x][y] = left_dir;
5296 if (MovDir[x][y] != old_move_dir)
5297 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5299 else if (move_pattern == MV_TOWARDS_PLAYER ||
5300 move_pattern == MV_AWAY_FROM_PLAYER)
5302 int attr_x = -1, attr_y = -1;
5304 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5315 for (i = 0; i < MAX_PLAYERS; i++)
5317 struct PlayerInfo *player = &stored_player[i];
5318 int jx = player->jx, jy = player->jy;
5320 if (!player->active)
5324 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5332 MovDir[x][y] = MV_NONE;
5334 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5335 else if (attr_x > x)
5336 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5338 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5339 else if (attr_y > y)
5340 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5342 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5344 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5346 boolean first_horiz = RND(2);
5347 int new_move_dir = MovDir[x][y];
5349 if (element_info[element].move_stepsize == 0) /* "not moving" */
5351 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5352 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5358 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5359 Moving2Blocked(x, y, &newx, &newy);
5361 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5365 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5366 Moving2Blocked(x, y, &newx, &newy);
5368 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5371 MovDir[x][y] = old_move_dir;
5374 else if (move_pattern == MV_WHEN_PUSHED ||
5375 move_pattern == MV_WHEN_DROPPED)
5377 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5378 MovDir[x][y] = MV_NONE;
5382 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5384 static int test_xy[7][2] =
5394 static int test_dir[7] =
5404 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5405 int move_preference = -1000000; /* start with very low preference */
5406 int new_move_dir = MV_NONE;
5407 int start_test = RND(4);
5410 for (i = 0; i < NUM_DIRECTIONS; i++)
5412 int move_dir = test_dir[start_test + i];
5413 int move_dir_preference;
5415 xx = x + test_xy[start_test + i][0];
5416 yy = y + test_xy[start_test + i][1];
5418 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5419 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5421 new_move_dir = move_dir;
5426 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5429 move_dir_preference = -1 * RunnerVisit[xx][yy];
5430 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5431 move_dir_preference = PlayerVisit[xx][yy];
5433 if (move_dir_preference > move_preference)
5435 /* prefer field that has not been visited for the longest time */
5436 move_preference = move_dir_preference;
5437 new_move_dir = move_dir;
5439 else if (move_dir_preference == move_preference &&
5440 move_dir == old_move_dir)
5442 /* prefer last direction when all directions are preferred equally */
5443 move_preference = move_dir_preference;
5444 new_move_dir = move_dir;
5448 MovDir[x][y] = new_move_dir;
5449 if (old_move_dir != new_move_dir)
5450 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5454 static void TurnRound(int x, int y)
5456 int direction = MovDir[x][y];
5460 GfxDir[x][y] = MovDir[x][y];
5462 if (direction != MovDir[x][y])
5466 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5468 ResetGfxFrame(x, y, FALSE);
5471 static boolean JustBeingPushed(int x, int y)
5475 for (i = 0; i < MAX_PLAYERS; i++)
5477 struct PlayerInfo *player = &stored_player[i];
5479 if (player->active && player->is_pushing && player->MovPos)
5481 int next_jx = player->jx + (player->jx - player->last_jx);
5482 int next_jy = player->jy + (player->jy - player->last_jy);
5484 if (x == next_jx && y == next_jy)
5492 void StartMoving(int x, int y)
5494 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5495 int element = Feld[x][y];
5500 if (MovDelay[x][y] == 0)
5501 GfxAction[x][y] = ACTION_DEFAULT;
5503 if (CAN_FALL(element) && y < lev_fieldy - 1)
5505 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5506 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5507 if (JustBeingPushed(x, y))
5510 if (element == EL_QUICKSAND_FULL)
5512 if (IS_FREE(x, y + 1))
5514 InitMovingField(x, y, MV_DOWN);
5515 started_moving = TRUE;
5517 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5518 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5519 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5520 Store[x][y] = EL_ROCK;
5522 Store[x][y] = EL_ROCK;
5525 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5527 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5529 if (!MovDelay[x][y])
5530 MovDelay[x][y] = TILEY + 1;
5539 Feld[x][y] = EL_QUICKSAND_EMPTY;
5540 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5541 Store[x][y + 1] = Store[x][y];
5544 PlayLevelSoundAction(x, y, ACTION_FILLING);
5547 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5548 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5550 InitMovingField(x, y, MV_DOWN);
5551 started_moving = TRUE;
5553 Feld[x][y] = EL_QUICKSAND_FILLING;
5554 Store[x][y] = element;
5556 PlayLevelSoundAction(x, y, ACTION_FILLING);
5558 else if (element == EL_MAGIC_WALL_FULL)
5560 if (IS_FREE(x, y + 1))
5562 InitMovingField(x, y, MV_DOWN);
5563 started_moving = TRUE;
5565 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5566 Store[x][y] = EL_CHANGED(Store[x][y]);
5568 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5570 if (!MovDelay[x][y])
5571 MovDelay[x][y] = TILEY/4 + 1;
5580 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5581 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5582 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5586 else if (element == EL_BD_MAGIC_WALL_FULL)
5588 if (IS_FREE(x, y + 1))
5590 InitMovingField(x, y, MV_DOWN);
5591 started_moving = TRUE;
5593 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5594 Store[x][y] = EL_CHANGED2(Store[x][y]);
5596 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5598 if (!MovDelay[x][y])
5599 MovDelay[x][y] = TILEY/4 + 1;
5608 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5609 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5610 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5614 else if (CAN_PASS_MAGIC_WALL(element) &&
5615 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5616 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5618 InitMovingField(x, y, MV_DOWN);
5619 started_moving = TRUE;
5622 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5623 EL_BD_MAGIC_WALL_FILLING);
5624 Store[x][y] = element;
5626 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5628 SplashAcid(x, y + 1);
5630 InitMovingField(x, y, MV_DOWN);
5631 started_moving = TRUE;
5633 Store[x][y] = EL_ACID;
5635 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5636 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5638 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5639 CAN_FALL(element) && WasJustFalling[x][y] &&
5640 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5642 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5643 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5644 (Feld[x][y + 1] == EL_BLOCKED)))
5646 /* this is needed for a special case not covered by calling "Impact()"
5647 from "ContinueMoving()": if an element moves to a tile directly below
5648 another element which was just falling on that tile (which was empty
5649 in the previous frame), the falling element above would just stop
5650 instead of smashing the element below (in previous version, the above
5651 element was just checked for "moving" instead of "falling", resulting
5652 in incorrect smashes caused by horizontal movement of the above
5653 element; also, the case of the player being the element to smash was
5654 simply not covered here... :-/ ) */
5656 CheckCollision[x][y] = 0;
5660 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5662 if (MovDir[x][y] == MV_NONE)
5664 InitMovingField(x, y, MV_DOWN);
5665 started_moving = TRUE;
5668 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5670 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5671 MovDir[x][y] = MV_DOWN;
5673 InitMovingField(x, y, MV_DOWN);
5674 started_moving = TRUE;
5676 else if (element == EL_AMOEBA_DROP)
5678 Feld[x][y] = EL_AMOEBA_GROWING;
5679 Store[x][y] = EL_AMOEBA_WET;
5681 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5682 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5683 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5684 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5686 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5687 (IS_FREE(x - 1, y + 1) ||
5688 Feld[x - 1][y + 1] == EL_ACID));
5689 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5690 (IS_FREE(x + 1, y + 1) ||
5691 Feld[x + 1][y + 1] == EL_ACID));
5692 boolean can_fall_any = (can_fall_left || can_fall_right);
5693 boolean can_fall_both = (can_fall_left && can_fall_right);
5694 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5696 #if USE_NEW_ALL_SLIPPERY
5697 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5699 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5700 can_fall_right = FALSE;
5701 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5702 can_fall_left = FALSE;
5703 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5704 can_fall_right = FALSE;
5705 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5706 can_fall_left = FALSE;
5708 can_fall_any = (can_fall_left || can_fall_right);
5709 can_fall_both = FALSE;
5712 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5714 if (slippery_type == SLIPPERY_ONLY_LEFT)
5715 can_fall_right = FALSE;
5716 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5717 can_fall_left = FALSE;
5718 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5719 can_fall_right = FALSE;
5720 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5721 can_fall_left = FALSE;
5723 can_fall_any = (can_fall_left || can_fall_right);
5724 can_fall_both = (can_fall_left && can_fall_right);
5728 #if USE_NEW_ALL_SLIPPERY
5730 #if USE_NEW_SP_SLIPPERY
5731 /* !!! better use the same properties as for custom elements here !!! */
5732 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5733 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5735 can_fall_right = FALSE; /* slip down on left side */
5736 can_fall_both = FALSE;
5741 #if USE_NEW_ALL_SLIPPERY
5744 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5745 can_fall_right = FALSE; /* slip down on left side */
5747 can_fall_left = !(can_fall_right = RND(2));
5749 can_fall_both = FALSE;
5754 if (game.emulation == EMU_BOULDERDASH ||
5755 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5756 can_fall_right = FALSE; /* slip down on left side */
5758 can_fall_left = !(can_fall_right = RND(2));
5760 can_fall_both = FALSE;
5766 /* if not determined otherwise, prefer left side for slipping down */
5767 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5768 started_moving = TRUE;
5772 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5774 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5777 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5778 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5779 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5780 int belt_dir = game.belt_dir[belt_nr];
5782 if ((belt_dir == MV_LEFT && left_is_free) ||
5783 (belt_dir == MV_RIGHT && right_is_free))
5785 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5787 InitMovingField(x, y, belt_dir);
5788 started_moving = TRUE;
5790 Pushed[x][y] = TRUE;
5791 Pushed[nextx][y] = TRUE;
5793 GfxAction[x][y] = ACTION_DEFAULT;
5797 MovDir[x][y] = 0; /* if element was moving, stop it */
5802 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5804 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5806 if (CAN_MOVE(element) && !started_moving)
5809 int move_pattern = element_info[element].move_pattern;
5814 if (MovDir[x][y] == MV_NONE)
5816 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5817 x, y, element, element_info[element].token_name);
5818 printf("StartMoving(): This should never happen!\n");
5823 Moving2Blocked(x, y, &newx, &newy);
5825 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5828 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5829 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5831 WasJustMoving[x][y] = 0;
5832 CheckCollision[x][y] = 0;
5834 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5836 if (Feld[x][y] != element) /* element has changed */
5840 if (!MovDelay[x][y]) /* start new movement phase */
5842 /* all objects that can change their move direction after each step
5843 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5845 if (element != EL_YAMYAM &&
5846 element != EL_DARK_YAMYAM &&
5847 element != EL_PACMAN &&
5848 !(move_pattern & MV_ANY_DIRECTION) &&
5849 move_pattern != MV_TURNING_LEFT &&
5850 move_pattern != MV_TURNING_RIGHT &&
5851 move_pattern != MV_TURNING_LEFT_RIGHT &&
5852 move_pattern != MV_TURNING_RIGHT_LEFT &&
5853 move_pattern != MV_TURNING_RANDOM)
5857 if (MovDelay[x][y] && (element == EL_BUG ||
5858 element == EL_SPACESHIP ||
5859 element == EL_SP_SNIKSNAK ||
5860 element == EL_SP_ELECTRON ||
5861 element == EL_MOLE))
5862 DrawLevelField(x, y);
5866 if (MovDelay[x][y]) /* wait some time before next movement */
5870 if (element == EL_ROBOT ||
5871 element == EL_YAMYAM ||
5872 element == EL_DARK_YAMYAM)
5874 DrawLevelElementAnimationIfNeeded(x, y, element);
5875 PlayLevelSoundAction(x, y, ACTION_WAITING);
5877 else if (element == EL_SP_ELECTRON)
5878 DrawLevelElementAnimationIfNeeded(x, y, element);
5879 else if (element == EL_DRAGON)
5882 int dir = MovDir[x][y];
5883 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5884 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5885 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5886 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5887 dir == MV_UP ? IMG_FLAMES_1_UP :
5888 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5889 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5891 GfxAction[x][y] = ACTION_ATTACKING;
5893 if (IS_PLAYER(x, y))
5894 DrawPlayerField(x, y);
5896 DrawLevelField(x, y);
5898 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5900 for (i = 1; i <= 3; i++)
5902 int xx = x + i * dx;
5903 int yy = y + i * dy;
5904 int sx = SCREENX(xx);
5905 int sy = SCREENY(yy);
5906 int flame_graphic = graphic + (i - 1);
5908 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5913 int flamed = MovingOrBlocked2Element(xx, yy);
5917 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5919 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5920 RemoveMovingField(xx, yy);
5922 RemoveField(xx, yy);
5924 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5927 RemoveMovingField(xx, yy);
5930 ChangeDelay[xx][yy] = 0;
5932 Feld[xx][yy] = EL_FLAMES;
5934 if (IN_SCR_FIELD(sx, sy))
5936 DrawLevelFieldCrumbledSand(xx, yy);
5937 DrawGraphic(sx, sy, flame_graphic, frame);
5942 if (Feld[xx][yy] == EL_FLAMES)
5943 Feld[xx][yy] = EL_EMPTY;
5944 DrawLevelField(xx, yy);
5949 if (MovDelay[x][y]) /* element still has to wait some time */
5951 PlayLevelSoundAction(x, y, ACTION_WAITING);
5957 /* now make next step */
5959 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5961 if (DONT_COLLIDE_WITH(element) &&
5962 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5963 !PLAYER_ENEMY_PROTECTED(newx, newy))
5965 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5970 else if (CAN_MOVE_INTO_ACID(element) &&
5971 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5972 !IS_MV_DIAGONAL(MovDir[x][y]) &&
5973 (MovDir[x][y] == MV_DOWN ||
5974 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5976 SplashAcid(newx, newy);
5977 Store[x][y] = EL_ACID;
5979 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5981 if (Feld[newx][newy] == EL_EXIT_OPEN)
5984 DrawLevelField(x, y);
5986 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5987 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5988 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5990 local_player->friends_still_needed--;
5991 if (!local_player->friends_still_needed &&
5992 !local_player->GameOver && AllPlayersGone)
5993 PlayerWins(local_player);
5997 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5999 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6000 DrawLevelField(newx, newy);
6002 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6004 else if (!IS_FREE(newx, newy))
6006 GfxAction[x][y] = ACTION_WAITING;
6008 if (IS_PLAYER(x, y))
6009 DrawPlayerField(x, y);
6011 DrawLevelField(x, y);
6016 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6018 if (IS_FOOD_PIG(Feld[newx][newy]))
6020 if (IS_MOVING(newx, newy))
6021 RemoveMovingField(newx, newy);
6024 Feld[newx][newy] = EL_EMPTY;
6025 DrawLevelField(newx, newy);
6028 PlayLevelSound(x, y, SND_PIG_DIGGING);
6030 else if (!IS_FREE(newx, newy))
6032 if (IS_PLAYER(x, y))
6033 DrawPlayerField(x, y);
6035 DrawLevelField(x, y);
6040 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6042 if (Store[x][y] != EL_EMPTY)
6044 boolean can_clone = FALSE;
6047 /* check if element to clone is still there */
6048 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6050 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6058 /* cannot clone or target field not free anymore -- do not clone */
6059 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6060 Store[x][y] = EL_EMPTY;
6063 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6065 if (IS_MV_DIAGONAL(MovDir[x][y]))
6067 int diagonal_move_dir = MovDir[x][y];
6068 int stored = Store[x][y];
6069 int change_delay = 8;
6072 /* android is moving diagonally */
6074 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6076 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6077 GfxElement[x][y] = EL_EMC_ANDROID;
6078 GfxAction[x][y] = ACTION_SHRINKING;
6079 GfxDir[x][y] = diagonal_move_dir;
6080 ChangeDelay[x][y] = change_delay;
6082 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6085 DrawLevelGraphicAnimation(x, y, graphic);
6086 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6088 if (Feld[newx][newy] == EL_ACID)
6090 SplashAcid(newx, newy);
6095 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6097 Store[newx][newy] = EL_EMC_ANDROID;
6098 GfxElement[newx][newy] = EL_EMC_ANDROID;
6099 GfxAction[newx][newy] = ACTION_GROWING;
6100 GfxDir[newx][newy] = diagonal_move_dir;
6101 ChangeDelay[newx][newy] = change_delay;
6103 graphic = el_act_dir2img(GfxElement[newx][newy],
6104 GfxAction[newx][newy], GfxDir[newx][newy]);
6106 DrawLevelGraphicAnimation(newx, newy, graphic);
6107 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6113 Feld[newx][newy] = EL_EMPTY;
6114 DrawLevelField(newx, newy);
6116 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6119 else if (!IS_FREE(newx, newy))
6122 if (IS_PLAYER(x, y))
6123 DrawPlayerField(x, y);
6125 DrawLevelField(x, y);
6131 else if (IS_CUSTOM_ELEMENT(element) &&
6132 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6134 int new_element = Feld[newx][newy];
6136 if (!IS_FREE(newx, newy))
6138 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6139 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6142 /* no element can dig solid indestructible elements */
6143 if (IS_INDESTRUCTIBLE(new_element) &&
6144 !IS_DIGGABLE(new_element) &&
6145 !IS_COLLECTIBLE(new_element))
6148 if (AmoebaNr[newx][newy] &&
6149 (new_element == EL_AMOEBA_FULL ||
6150 new_element == EL_BD_AMOEBA ||
6151 new_element == EL_AMOEBA_GROWING))
6153 AmoebaCnt[AmoebaNr[newx][newy]]--;
6154 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6157 if (IS_MOVING(newx, newy))
6158 RemoveMovingField(newx, newy);
6161 RemoveField(newx, newy);
6162 DrawLevelField(newx, newy);
6165 /* if digged element was about to explode, prevent the explosion */
6166 ExplodeField[newx][newy] = EX_TYPE_NONE;
6168 PlayLevelSoundAction(x, y, action);
6171 Store[newx][newy] = EL_EMPTY;
6173 /* this makes it possible to leave the removed element again */
6174 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6175 Store[newx][newy] = new_element;
6177 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6179 int move_leave_element = element_info[element].move_leave_element;
6181 /* this makes it possible to leave the removed element again */
6182 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6183 new_element : move_leave_element);
6187 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6189 RunnerVisit[x][y] = FrameCounter;
6190 PlayerVisit[x][y] /= 8; /* expire player visit path */
6193 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6195 if (!IS_FREE(newx, newy))
6197 if (IS_PLAYER(x, y))
6198 DrawPlayerField(x, y);
6200 DrawLevelField(x, y);
6206 boolean wanna_flame = !RND(10);
6207 int dx = newx - x, dy = newy - y;
6208 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6209 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6210 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6211 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6212 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6213 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6216 IS_CLASSIC_ENEMY(element1) ||
6217 IS_CLASSIC_ENEMY(element2)) &&
6218 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6219 element1 != EL_FLAMES && element2 != EL_FLAMES)
6221 ResetGfxAnimation(x, y);
6222 GfxAction[x][y] = ACTION_ATTACKING;
6224 if (IS_PLAYER(x, y))
6225 DrawPlayerField(x, y);
6227 DrawLevelField(x, y);
6229 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6231 MovDelay[x][y] = 50;
6235 RemoveField(newx, newy);
6237 Feld[newx][newy] = EL_FLAMES;
6238 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6241 RemoveField(newx1, newy1);
6243 Feld[newx1][newy1] = EL_FLAMES;
6245 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6248 RemoveField(newx2, newy2);
6250 Feld[newx2][newy2] = EL_FLAMES;
6257 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6258 Feld[newx][newy] == EL_DIAMOND)
6260 if (IS_MOVING(newx, newy))
6261 RemoveMovingField(newx, newy);
6264 Feld[newx][newy] = EL_EMPTY;
6265 DrawLevelField(newx, newy);
6268 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6270 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6271 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6273 if (AmoebaNr[newx][newy])
6275 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6276 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6277 Feld[newx][newy] == EL_BD_AMOEBA)
6278 AmoebaCnt[AmoebaNr[newx][newy]]--;
6283 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6285 RemoveMovingField(newx, newy);
6288 if (IS_MOVING(newx, newy))
6290 RemoveMovingField(newx, newy);
6295 Feld[newx][newy] = EL_EMPTY;
6296 DrawLevelField(newx, newy);
6299 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6301 else if ((element == EL_PACMAN || element == EL_MOLE)
6302 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6304 if (AmoebaNr[newx][newy])
6306 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6307 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6308 Feld[newx][newy] == EL_BD_AMOEBA)
6309 AmoebaCnt[AmoebaNr[newx][newy]]--;
6312 if (element == EL_MOLE)
6314 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6315 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6317 ResetGfxAnimation(x, y);
6318 GfxAction[x][y] = ACTION_DIGGING;
6319 DrawLevelField(x, y);
6321 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6323 return; /* wait for shrinking amoeba */
6325 else /* element == EL_PACMAN */
6327 Feld[newx][newy] = EL_EMPTY;
6328 DrawLevelField(newx, newy);
6329 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6332 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6333 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6334 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6336 /* wait for shrinking amoeba to completely disappear */
6339 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6341 /* object was running against a wall */
6346 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6347 if (move_pattern & MV_ANY_DIRECTION &&
6348 move_pattern == MovDir[x][y])
6350 int blocking_element =
6351 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6353 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6356 element = Feld[x][y]; /* element might have changed */
6360 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6361 DrawLevelElementAnimation(x, y, element);
6363 if (DONT_TOUCH(element))
6364 TestIfBadThingTouchesPlayer(x, y);
6369 InitMovingField(x, y, MovDir[x][y]);
6371 PlayLevelSoundAction(x, y, ACTION_MOVING);
6375 ContinueMoving(x, y);
6378 void ContinueMoving(int x, int y)
6380 int element = Feld[x][y];
6381 struct ElementInfo *ei = &element_info[element];
6382 int direction = MovDir[x][y];
6383 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6384 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6385 int newx = x + dx, newy = y + dy;
6386 int stored = Store[x][y];
6387 int stored_new = Store[newx][newy];
6388 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6389 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6390 boolean last_line = (newy == lev_fieldy - 1);
6392 MovPos[x][y] += getElementMoveStepsize(x, y);
6394 if (pushed_by_player) /* special case: moving object pushed by player */
6395 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6397 if (ABS(MovPos[x][y]) < TILEX)
6399 DrawLevelField(x, y);
6401 return; /* element is still moving */
6404 /* element reached destination field */
6406 Feld[x][y] = EL_EMPTY;
6407 Feld[newx][newy] = element;
6408 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6410 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6412 element = Feld[newx][newy] = EL_ACID;
6414 else if (element == EL_MOLE)
6416 Feld[x][y] = EL_SAND;
6418 DrawLevelFieldCrumbledSandNeighbours(x, y);
6420 else if (element == EL_QUICKSAND_FILLING)
6422 element = Feld[newx][newy] = get_next_element(element);
6423 Store[newx][newy] = Store[x][y];
6425 else if (element == EL_QUICKSAND_EMPTYING)
6427 Feld[x][y] = get_next_element(element);
6428 element = Feld[newx][newy] = Store[x][y];
6430 else if (element == EL_MAGIC_WALL_FILLING)
6432 element = Feld[newx][newy] = get_next_element(element);
6433 if (!game.magic_wall_active)
6434 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6435 Store[newx][newy] = Store[x][y];
6437 else if (element == EL_MAGIC_WALL_EMPTYING)
6439 Feld[x][y] = get_next_element(element);
6440 if (!game.magic_wall_active)
6441 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6442 element = Feld[newx][newy] = Store[x][y];
6444 #if USE_NEW_CUSTOM_VALUE
6445 InitField(newx, newy, FALSE);
6448 else if (element == EL_BD_MAGIC_WALL_FILLING)
6450 element = Feld[newx][newy] = get_next_element(element);
6451 if (!game.magic_wall_active)
6452 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6453 Store[newx][newy] = Store[x][y];
6455 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6457 Feld[x][y] = get_next_element(element);
6458 if (!game.magic_wall_active)
6459 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6460 element = Feld[newx][newy] = Store[x][y];
6462 #if USE_NEW_CUSTOM_VALUE
6463 InitField(newx, newy, FALSE);
6466 else if (element == EL_AMOEBA_DROPPING)
6468 Feld[x][y] = get_next_element(element);
6469 element = Feld[newx][newy] = Store[x][y];
6471 else if (element == EL_SOKOBAN_OBJECT)
6474 Feld[x][y] = Back[x][y];
6476 if (Back[newx][newy])
6477 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6479 Back[x][y] = Back[newx][newy] = 0;
6482 Store[x][y] = EL_EMPTY;
6487 MovDelay[newx][newy] = 0;
6489 if (CAN_CHANGE_OR_HAS_ACTION(element))
6491 /* copy element change control values to new field */
6492 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6493 ChangePage[newx][newy] = ChangePage[x][y];
6494 ChangeCount[newx][newy] = ChangeCount[x][y];
6495 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6498 #if USE_NEW_CUSTOM_VALUE
6499 CustomValue[newx][newy] = CustomValue[x][y];
6502 ChangeDelay[x][y] = 0;
6503 ChangePage[x][y] = -1;
6504 ChangeCount[x][y] = 0;
6505 ChangeEvent[x][y] = -1;
6507 #if USE_NEW_CUSTOM_VALUE
6508 CustomValue[x][y] = 0;
6511 /* copy animation control values to new field */
6512 GfxFrame[newx][newy] = GfxFrame[x][y];
6513 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6514 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6515 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6517 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6519 /* some elements can leave other elements behind after moving */
6521 if (ei->move_leave_element != EL_EMPTY &&
6522 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6523 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6525 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6526 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6527 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6530 int move_leave_element = ei->move_leave_element;
6534 /* this makes it possible to leave the removed element again */
6535 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6536 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6538 /* this makes it possible to leave the removed element again */
6539 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6540 move_leave_element = stored;
6543 /* this makes it possible to leave the removed element again */
6544 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6545 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6546 move_leave_element = stored;
6549 Feld[x][y] = move_leave_element;
6551 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6552 MovDir[x][y] = direction;
6554 InitField(x, y, FALSE);
6556 if (GFX_CRUMBLED(Feld[x][y]))
6557 DrawLevelFieldCrumbledSandNeighbours(x, y);
6559 if (ELEM_IS_PLAYER(move_leave_element))
6560 RelocatePlayer(x, y, move_leave_element);
6563 /* do this after checking for left-behind element */
6564 ResetGfxAnimation(x, y); /* reset animation values for old field */
6566 if (!CAN_MOVE(element) ||
6567 (CAN_FALL(element) && direction == MV_DOWN &&
6568 (element == EL_SPRING ||
6569 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6570 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6571 GfxDir[x][y] = MovDir[newx][newy] = 0;
6573 DrawLevelField(x, y);
6574 DrawLevelField(newx, newy);
6576 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6578 /* prevent pushed element from moving on in pushed direction */
6579 if (pushed_by_player && CAN_MOVE(element) &&
6580 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6581 !(element_info[element].move_pattern & direction))
6582 TurnRound(newx, newy);
6584 /* prevent elements on conveyor belt from moving on in last direction */
6585 if (pushed_by_conveyor && CAN_FALL(element) &&
6586 direction & MV_HORIZONTAL)
6587 MovDir[newx][newy] = 0;
6589 if (!pushed_by_player)
6591 int nextx = newx + dx, nexty = newy + dy;
6592 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6594 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6596 if (CAN_FALL(element) && direction == MV_DOWN)
6597 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6599 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6600 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6603 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6605 TestIfBadThingTouchesPlayer(newx, newy);
6606 TestIfBadThingTouchesFriend(newx, newy);
6608 if (!IS_CUSTOM_ELEMENT(element))
6609 TestIfBadThingTouchesOtherBadThing(newx, newy);
6611 else if (element == EL_PENGUIN)
6612 TestIfFriendTouchesBadThing(newx, newy);
6614 /* give the player one last chance (one more frame) to move away */
6615 if (CAN_FALL(element) && direction == MV_DOWN &&
6616 (last_line || (!IS_FREE(x, newy + 1) &&
6617 (!IS_PLAYER(x, newy + 1) ||
6618 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6621 if (pushed_by_player && !game.use_change_when_pushing_bug)
6623 int push_side = MV_DIR_OPPOSITE(direction);
6624 struct PlayerInfo *player = PLAYERINFO(x, y);
6626 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6627 player->index_bit, push_side);
6628 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6629 player->index_bit, push_side);
6632 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6633 MovDelay[newx][newy] = 1;
6635 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6637 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6640 if (ChangePage[newx][newy] != -1) /* delayed change */
6642 int page = ChangePage[newx][newy];
6643 struct ElementChangeInfo *change = &ei->change_page[page];
6645 ChangePage[newx][newy] = -1;
6647 if (change->can_change)
6649 if (ChangeElement(newx, newy, element, page))
6651 if (change->post_change_function)
6652 change->post_change_function(newx, newy);
6656 if (change->has_action)
6657 ExecuteCustomElementAction(newx, newy, element, page);
6661 TestIfElementHitsCustomElement(newx, newy, direction);
6662 TestIfPlayerTouchesCustomElement(newx, newy);
6663 TestIfElementTouchesCustomElement(newx, newy);
6665 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6666 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6667 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6668 MV_DIR_OPPOSITE(direction));
6671 int AmoebeNachbarNr(int ax, int ay)
6674 int element = Feld[ax][ay];
6676 static int xy[4][2] =
6684 for (i = 0; i < NUM_DIRECTIONS; i++)
6686 int x = ax + xy[i][0];
6687 int y = ay + xy[i][1];
6689 if (!IN_LEV_FIELD(x, y))
6692 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6693 group_nr = AmoebaNr[x][y];
6699 void AmoebenVereinigen(int ax, int ay)
6701 int i, x, y, xx, yy;
6702 int new_group_nr = AmoebaNr[ax][ay];
6703 static int xy[4][2] =
6711 if (new_group_nr == 0)
6714 for (i = 0; i < NUM_DIRECTIONS; i++)
6719 if (!IN_LEV_FIELD(x, y))
6722 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6723 Feld[x][y] == EL_BD_AMOEBA ||
6724 Feld[x][y] == EL_AMOEBA_DEAD) &&
6725 AmoebaNr[x][y] != new_group_nr)
6727 int old_group_nr = AmoebaNr[x][y];
6729 if (old_group_nr == 0)
6732 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6733 AmoebaCnt[old_group_nr] = 0;
6734 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6735 AmoebaCnt2[old_group_nr] = 0;
6737 SCAN_PLAYFIELD(xx, yy)
6739 if (AmoebaNr[xx][yy] == old_group_nr)
6740 AmoebaNr[xx][yy] = new_group_nr;
6746 void AmoebeUmwandeln(int ax, int ay)
6750 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6752 int group_nr = AmoebaNr[ax][ay];
6757 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6758 printf("AmoebeUmwandeln(): This should never happen!\n");
6763 SCAN_PLAYFIELD(x, y)
6765 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6768 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6772 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6773 SND_AMOEBA_TURNING_TO_GEM :
6774 SND_AMOEBA_TURNING_TO_ROCK));
6779 static int xy[4][2] =
6787 for (i = 0; i < NUM_DIRECTIONS; i++)
6792 if (!IN_LEV_FIELD(x, y))
6795 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6797 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6798 SND_AMOEBA_TURNING_TO_GEM :
6799 SND_AMOEBA_TURNING_TO_ROCK));
6806 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6809 int group_nr = AmoebaNr[ax][ay];
6810 boolean done = FALSE;
6815 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6816 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6821 SCAN_PLAYFIELD(x, y)
6823 if (AmoebaNr[x][y] == group_nr &&
6824 (Feld[x][y] == EL_AMOEBA_DEAD ||
6825 Feld[x][y] == EL_BD_AMOEBA ||
6826 Feld[x][y] == EL_AMOEBA_GROWING))
6829 Feld[x][y] = new_element;
6830 InitField(x, y, FALSE);
6831 DrawLevelField(x, y);
6837 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6838 SND_BD_AMOEBA_TURNING_TO_ROCK :
6839 SND_BD_AMOEBA_TURNING_TO_GEM));
6842 void AmoebeWaechst(int x, int y)
6844 static unsigned long sound_delay = 0;
6845 static unsigned long sound_delay_value = 0;
6847 if (!MovDelay[x][y]) /* start new growing cycle */
6851 if (DelayReached(&sound_delay, sound_delay_value))
6853 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6854 sound_delay_value = 30;
6858 if (MovDelay[x][y]) /* wait some time before growing bigger */
6861 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6863 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6864 6 - MovDelay[x][y]);
6866 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6869 if (!MovDelay[x][y])
6871 Feld[x][y] = Store[x][y];
6873 DrawLevelField(x, y);
6878 void AmoebaDisappearing(int x, int y)
6880 static unsigned long sound_delay = 0;
6881 static unsigned long sound_delay_value = 0;
6883 if (!MovDelay[x][y]) /* start new shrinking cycle */
6887 if (DelayReached(&sound_delay, sound_delay_value))
6888 sound_delay_value = 30;
6891 if (MovDelay[x][y]) /* wait some time before shrinking */
6894 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6896 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6897 6 - MovDelay[x][y]);
6899 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6902 if (!MovDelay[x][y])
6904 Feld[x][y] = EL_EMPTY;
6905 DrawLevelField(x, y);
6907 /* don't let mole enter this field in this cycle;
6908 (give priority to objects falling to this field from above) */
6914 void AmoebeAbleger(int ax, int ay)
6917 int element = Feld[ax][ay];
6918 int graphic = el2img(element);
6919 int newax = ax, neway = ay;
6920 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6921 static int xy[4][2] =
6929 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6931 Feld[ax][ay] = EL_AMOEBA_DEAD;
6932 DrawLevelField(ax, ay);
6936 if (IS_ANIMATED(graphic))
6937 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6939 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6940 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6942 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6945 if (MovDelay[ax][ay])
6949 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6952 int x = ax + xy[start][0];
6953 int y = ay + xy[start][1];
6955 if (!IN_LEV_FIELD(x, y))
6958 if (IS_FREE(x, y) ||
6959 CAN_GROW_INTO(Feld[x][y]) ||
6960 Feld[x][y] == EL_QUICKSAND_EMPTY)
6966 if (newax == ax && neway == ay)
6969 else /* normal or "filled" (BD style) amoeba */
6972 boolean waiting_for_player = FALSE;
6974 for (i = 0; i < NUM_DIRECTIONS; i++)
6976 int j = (start + i) % 4;
6977 int x = ax + xy[j][0];
6978 int y = ay + xy[j][1];
6980 if (!IN_LEV_FIELD(x, y))
6983 if (IS_FREE(x, y) ||
6984 CAN_GROW_INTO(Feld[x][y]) ||
6985 Feld[x][y] == EL_QUICKSAND_EMPTY)
6991 else if (IS_PLAYER(x, y))
6992 waiting_for_player = TRUE;
6995 if (newax == ax && neway == ay) /* amoeba cannot grow */
6997 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6999 Feld[ax][ay] = EL_AMOEBA_DEAD;
7000 DrawLevelField(ax, ay);
7001 AmoebaCnt[AmoebaNr[ax][ay]]--;
7003 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7005 if (element == EL_AMOEBA_FULL)
7006 AmoebeUmwandeln(ax, ay);
7007 else if (element == EL_BD_AMOEBA)
7008 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7013 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7015 /* amoeba gets larger by growing in some direction */
7017 int new_group_nr = AmoebaNr[ax][ay];
7020 if (new_group_nr == 0)
7022 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7023 printf("AmoebeAbleger(): This should never happen!\n");
7028 AmoebaNr[newax][neway] = new_group_nr;
7029 AmoebaCnt[new_group_nr]++;
7030 AmoebaCnt2[new_group_nr]++;
7032 /* if amoeba touches other amoeba(s) after growing, unify them */
7033 AmoebenVereinigen(newax, neway);
7035 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7037 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7043 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7044 (neway == lev_fieldy - 1 && newax != ax))
7046 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7047 Store[newax][neway] = element;
7049 else if (neway == ay || element == EL_EMC_DRIPPER)
7051 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7053 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7057 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7058 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7059 Store[ax][ay] = EL_AMOEBA_DROP;
7060 ContinueMoving(ax, ay);
7064 DrawLevelField(newax, neway);
7067 void Life(int ax, int ay)
7071 int element = Feld[ax][ay];
7072 int graphic = el2img(element);
7073 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7075 boolean changed = FALSE;
7077 if (IS_ANIMATED(graphic))
7078 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7083 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7084 MovDelay[ax][ay] = life_time;
7086 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7089 if (MovDelay[ax][ay])
7093 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7095 int xx = ax+x1, yy = ay+y1;
7098 if (!IN_LEV_FIELD(xx, yy))
7101 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7103 int x = xx+x2, y = yy+y2;
7105 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7108 if (((Feld[x][y] == element ||
7109 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7111 (IS_FREE(x, y) && Stop[x][y]))
7115 if (xx == ax && yy == ay) /* field in the middle */
7117 if (nachbarn < life_parameter[0] ||
7118 nachbarn > life_parameter[1])
7120 Feld[xx][yy] = EL_EMPTY;
7122 DrawLevelField(xx, yy);
7123 Stop[xx][yy] = TRUE;
7127 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7128 { /* free border field */
7129 if (nachbarn >= life_parameter[2] &&
7130 nachbarn <= life_parameter[3])
7132 Feld[xx][yy] = element;
7133 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7135 DrawLevelField(xx, yy);
7136 Stop[xx][yy] = TRUE;
7143 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7144 SND_GAME_OF_LIFE_GROWING);
7147 static void InitRobotWheel(int x, int y)
7149 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7152 static void RunRobotWheel(int x, int y)
7154 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7157 static void StopRobotWheel(int x, int y)
7159 if (ZX == x && ZY == y)
7163 static void InitTimegateWheel(int x, int y)
7165 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7168 static void RunTimegateWheel(int x, int y)
7170 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7173 static void InitMagicBallDelay(int x, int y)
7176 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7178 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7182 static void ActivateMagicBall(int bx, int by)
7186 if (level.ball_random)
7188 int pos_border = RND(8); /* select one of the eight border elements */
7189 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7190 int xx = pos_content % 3;
7191 int yy = pos_content / 3;
7196 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7197 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7201 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7203 int xx = x - bx + 1;
7204 int yy = y - by + 1;
7206 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7207 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7211 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7214 void CheckExit(int x, int y)
7216 if (local_player->gems_still_needed > 0 ||
7217 local_player->sokobanfields_still_needed > 0 ||
7218 local_player->lights_still_needed > 0)
7220 int element = Feld[x][y];
7221 int graphic = el2img(element);
7223 if (IS_ANIMATED(graphic))
7224 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7229 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7232 Feld[x][y] = EL_EXIT_OPENING;
7234 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7237 void CheckExitSP(int x, int y)
7239 if (local_player->gems_still_needed > 0)
7241 int element = Feld[x][y];
7242 int graphic = el2img(element);
7244 if (IS_ANIMATED(graphic))
7245 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7250 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7253 Feld[x][y] = EL_SP_EXIT_OPENING;
7255 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7258 static void CloseAllOpenTimegates()
7262 SCAN_PLAYFIELD(x, y)
7264 int element = Feld[x][y];
7266 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7268 Feld[x][y] = EL_TIMEGATE_CLOSING;
7270 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7275 void DrawTwinkleOnField(int x, int y)
7277 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7280 if (Feld[x][y] == EL_BD_DIAMOND)
7283 if (MovDelay[x][y] == 0) /* next animation frame */
7284 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7286 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7290 if (setup.direct_draw && MovDelay[x][y])
7291 SetDrawtoField(DRAW_BUFFERED);
7293 DrawLevelElementAnimation(x, y, Feld[x][y]);
7295 if (MovDelay[x][y] != 0)
7297 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7298 10 - MovDelay[x][y]);
7300 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7302 if (setup.direct_draw)
7306 dest_x = FX + SCREENX(x) * TILEX;
7307 dest_y = FY + SCREENY(y) * TILEY;
7309 BlitBitmap(drawto_field, window,
7310 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7311 SetDrawtoField(DRAW_DIRECT);
7317 void MauerWaechst(int x, int y)
7321 if (!MovDelay[x][y]) /* next animation frame */
7322 MovDelay[x][y] = 3 * delay;
7324 if (MovDelay[x][y]) /* wait some time before next frame */
7328 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7330 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7331 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7333 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7336 if (!MovDelay[x][y])
7338 if (MovDir[x][y] == MV_LEFT)
7340 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7341 DrawLevelField(x - 1, y);
7343 else if (MovDir[x][y] == MV_RIGHT)
7345 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7346 DrawLevelField(x + 1, y);
7348 else if (MovDir[x][y] == MV_UP)
7350 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7351 DrawLevelField(x, y - 1);
7355 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7356 DrawLevelField(x, y + 1);
7359 Feld[x][y] = Store[x][y];
7361 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7362 DrawLevelField(x, y);
7367 void MauerAbleger(int ax, int ay)
7369 int element = Feld[ax][ay];
7370 int graphic = el2img(element);
7371 boolean oben_frei = FALSE, unten_frei = FALSE;
7372 boolean links_frei = FALSE, rechts_frei = FALSE;
7373 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7374 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7375 boolean new_wall = FALSE;
7377 if (IS_ANIMATED(graphic))
7378 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7380 if (!MovDelay[ax][ay]) /* start building new wall */
7381 MovDelay[ax][ay] = 6;
7383 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7386 if (MovDelay[ax][ay])
7390 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7392 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7394 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7396 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7399 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7400 element == EL_EXPANDABLE_WALL_ANY)
7404 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7405 Store[ax][ay-1] = element;
7406 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7407 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7408 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7409 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7414 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7415 Store[ax][ay+1] = element;
7416 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7417 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7418 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7419 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7424 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7425 element == EL_EXPANDABLE_WALL_ANY ||
7426 element == EL_EXPANDABLE_WALL ||
7427 element == EL_BD_EXPANDABLE_WALL)
7431 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7432 Store[ax-1][ay] = element;
7433 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7434 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7435 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7436 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7442 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7443 Store[ax+1][ay] = element;
7444 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7445 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7446 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7447 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7452 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7453 DrawLevelField(ax, ay);
7455 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7457 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7458 unten_massiv = TRUE;
7459 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7460 links_massiv = TRUE;
7461 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7462 rechts_massiv = TRUE;
7464 if (((oben_massiv && unten_massiv) ||
7465 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7466 element == EL_EXPANDABLE_WALL) &&
7467 ((links_massiv && rechts_massiv) ||
7468 element == EL_EXPANDABLE_WALL_VERTICAL))
7469 Feld[ax][ay] = EL_WALL;
7472 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7475 void CheckForDragon(int x, int y)
7478 boolean dragon_found = FALSE;
7479 static int xy[4][2] =
7487 for (i = 0; i < NUM_DIRECTIONS; i++)
7489 for (j = 0; j < 4; j++)
7491 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7493 if (IN_LEV_FIELD(xx, yy) &&
7494 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7496 if (Feld[xx][yy] == EL_DRAGON)
7497 dragon_found = TRUE;
7506 for (i = 0; i < NUM_DIRECTIONS; i++)
7508 for (j = 0; j < 3; j++)
7510 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7512 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7514 Feld[xx][yy] = EL_EMPTY;
7515 DrawLevelField(xx, yy);
7524 static void InitBuggyBase(int x, int y)
7526 int element = Feld[x][y];
7527 int activating_delay = FRAMES_PER_SECOND / 4;
7530 (element == EL_SP_BUGGY_BASE ?
7531 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7532 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7534 element == EL_SP_BUGGY_BASE_ACTIVE ?
7535 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7538 static void WarnBuggyBase(int x, int y)
7541 static int xy[4][2] =
7549 for (i = 0; i < NUM_DIRECTIONS; i++)
7551 int xx = x + xy[i][0];
7552 int yy = y + xy[i][1];
7554 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7556 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7563 static void InitTrap(int x, int y)
7565 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7568 static void ActivateTrap(int x, int y)
7570 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7573 static void ChangeActiveTrap(int x, int y)
7575 int graphic = IMG_TRAP_ACTIVE;
7577 /* if new animation frame was drawn, correct crumbled sand border */
7578 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7579 DrawLevelFieldCrumbledSand(x, y);
7582 static int getSpecialActionElement(int element, int number, int base_element)
7584 return (element != EL_EMPTY ? element :
7585 number != -1 ? base_element + number - 1 :
7589 static int getModifiedActionNumber(int value_old, int operator, int operand,
7590 int value_min, int value_max)
7592 int value_new = (operator == CA_MODE_SET ? operand :
7593 operator == CA_MODE_ADD ? value_old + operand :
7594 operator == CA_MODE_SUBTRACT ? value_old - operand :
7595 operator == CA_MODE_MULTIPLY ? value_old * operand :
7596 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7597 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7600 return (value_new < value_min ? value_min :
7601 value_new > value_max ? value_max :
7605 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7607 struct ElementInfo *ei = &element_info[element];
7608 struct ElementChangeInfo *change = &ei->change_page[page];
7609 int target_element = change->target_element;
7610 int action_type = change->action_type;
7611 int action_mode = change->action_mode;
7612 int action_arg = change->action_arg;
7615 if (!change->has_action)
7618 /* ---------- determine action paramater values -------------------------- */
7620 int level_time_value =
7621 (level.time > 0 ? TimeLeft :
7624 int action_arg_element =
7625 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7626 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7627 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7630 int action_arg_direction =
7631 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7632 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7633 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7634 change->actual_trigger_side :
7635 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7636 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7639 int action_arg_number_min =
7640 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7643 int action_arg_number_max =
7644 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7645 action_type == CA_SET_LEVEL_GEMS ? 999 :
7646 action_type == CA_SET_LEVEL_TIME ? 9999 :
7647 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7648 action_type == CA_SET_CE_VALUE ? 9999 :
7649 action_type == CA_SET_CE_SCORE ? 9999 :
7652 int action_arg_number_reset =
7653 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
7654 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7655 action_type == CA_SET_LEVEL_TIME ? level.time :
7656 action_type == CA_SET_LEVEL_SCORE ? 0 :
7657 #if USE_NEW_CUSTOM_VALUE
7658 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
7660 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7662 action_type == CA_SET_CE_SCORE ? 0 :
7665 int action_arg_number =
7666 (action_arg <= CA_ARG_MAX ? action_arg :
7667 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7668 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7669 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7670 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7671 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7672 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7673 #if USE_NEW_CUSTOM_VALUE
7674 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7676 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7678 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7679 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7680 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7681 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7682 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7683 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
7684 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7685 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
7686 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
7687 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7688 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7691 int action_arg_number_old =
7692 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7693 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7694 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7695 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7696 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7699 int action_arg_number_new =
7700 getModifiedActionNumber(action_arg_number_old,
7701 action_mode, action_arg_number,
7702 action_arg_number_min, action_arg_number_max);
7704 int trigger_player_bits =
7705 (change->actual_trigger_player >= EL_PLAYER_1 &&
7706 change->actual_trigger_player <= EL_PLAYER_4 ?
7707 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7710 int action_arg_player_bits =
7711 (action_arg >= CA_ARG_PLAYER_1 &&
7712 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7713 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7716 /* ---------- execute action -------------------------------------------- */
7725 /* ---------- level actions ------------------------------------------- */
7727 case CA_RESTART_LEVEL:
7729 game.restart_level = TRUE;
7734 case CA_SHOW_ENVELOPE:
7736 int element = getSpecialActionElement(action_arg_element,
7737 action_arg_number, EL_ENVELOPE_1);
7739 if (IS_ENVELOPE(element))
7740 local_player->show_envelope = element;
7745 case CA_SET_LEVEL_TIME:
7747 if (level.time > 0) /* only modify limited time value */
7749 TimeLeft = action_arg_number_new;
7751 DrawGameValue_Time(TimeLeft);
7753 if (!TimeLeft && setup.time_limit)
7754 for (i = 0; i < MAX_PLAYERS; i++)
7755 KillPlayer(&stored_player[i]);
7761 case CA_SET_LEVEL_SCORE:
7763 local_player->score = action_arg_number_new;
7765 DrawGameValue_Score(local_player->score);
7770 case CA_SET_LEVEL_GEMS:
7772 local_player->gems_still_needed = action_arg_number_new;
7774 DrawGameValue_Emeralds(local_player->gems_still_needed);
7779 #if !USE_PLAYER_GRAVITY
7780 case CA_SET_LEVEL_GRAVITY:
7782 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7783 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7784 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7790 case CA_SET_LEVEL_WIND:
7792 game.wind_direction = action_arg_direction;
7797 /* ---------- player actions ------------------------------------------ */
7799 case CA_MOVE_PLAYER:
7801 /* automatically move to the next field in specified direction */
7802 for (i = 0; i < MAX_PLAYERS; i++)
7803 if (trigger_player_bits & (1 << i))
7804 stored_player[i].programmed_action = action_arg_direction;
7809 case CA_EXIT_PLAYER:
7811 for (i = 0; i < MAX_PLAYERS; i++)
7812 if (action_arg_player_bits & (1 << i))
7813 PlayerWins(&stored_player[i]);
7818 case CA_KILL_PLAYER:
7820 for (i = 0; i < MAX_PLAYERS; i++)
7821 if (action_arg_player_bits & (1 << i))
7822 KillPlayer(&stored_player[i]);
7827 case CA_SET_PLAYER_KEYS:
7829 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7830 int element = getSpecialActionElement(action_arg_element,
7831 action_arg_number, EL_KEY_1);
7833 if (IS_KEY(element))
7835 for (i = 0; i < MAX_PLAYERS; i++)
7837 if (trigger_player_bits & (1 << i))
7839 stored_player[i].key[KEY_NR(element)] = key_state;
7841 DrawGameDoorValues();
7849 case CA_SET_PLAYER_SPEED:
7851 for (i = 0; i < MAX_PLAYERS; i++)
7853 if (trigger_player_bits & (1 << i))
7855 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7857 if (action_arg == CA_ARG_SPEED_FASTER &&
7858 stored_player[i].cannot_move)
7860 action_arg_number = STEPSIZE_VERY_SLOW;
7862 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7863 action_arg == CA_ARG_SPEED_FASTER)
7865 action_arg_number = 2;
7866 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7869 else if (action_arg == CA_ARG_NUMBER_RESET)
7871 action_arg_number = level.initial_player_stepsize[i];
7875 getModifiedActionNumber(move_stepsize,
7878 action_arg_number_min,
7879 action_arg_number_max);
7881 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7888 case CA_SET_PLAYER_SHIELD:
7890 for (i = 0; i < MAX_PLAYERS; i++)
7892 if (trigger_player_bits & (1 << i))
7894 if (action_arg == CA_ARG_SHIELD_OFF)
7896 stored_player[i].shield_normal_time_left = 0;
7897 stored_player[i].shield_deadly_time_left = 0;
7899 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7901 stored_player[i].shield_normal_time_left = 999999;
7903 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7905 stored_player[i].shield_normal_time_left = 999999;
7906 stored_player[i].shield_deadly_time_left = 999999;
7914 #if USE_PLAYER_GRAVITY
7915 case CA_SET_PLAYER_GRAVITY:
7917 for (i = 0; i < MAX_PLAYERS; i++)
7919 if (trigger_player_bits & (1 << i))
7921 stored_player[i].gravity =
7922 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7923 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7924 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
7925 stored_player[i].gravity);
7933 case CA_SET_PLAYER_ARTWORK:
7935 for (i = 0; i < MAX_PLAYERS; i++)
7937 if (trigger_player_bits & (1 << i))
7939 int artwork_element = action_arg_element;
7941 if (action_arg == CA_ARG_ELEMENT_RESET)
7943 (level.use_artwork_element[i] ? level.artwork_element[i] :
7944 stored_player[i].element_nr);
7946 #if USE_GFX_RESET_PLAYER_ARTWORK
7947 if (stored_player[i].artwork_element != artwork_element)
7948 stored_player[i].Frame = 0;
7951 stored_player[i].artwork_element = artwork_element;
7953 SetPlayerWaiting(&stored_player[i], FALSE);
7955 /* set number of special actions for bored and sleeping animation */
7956 stored_player[i].num_special_action_bored =
7957 get_num_special_action(artwork_element,
7958 ACTION_BORING_1, ACTION_BORING_LAST);
7959 stored_player[i].num_special_action_sleeping =
7960 get_num_special_action(artwork_element,
7961 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7968 /* ---------- CE actions ---------------------------------------------- */
7970 case CA_SET_CE_VALUE:
7972 #if USE_NEW_CUSTOM_VALUE
7973 int last_ce_value = CustomValue[x][y];
7975 CustomValue[x][y] = action_arg_number_new;
7977 if (CustomValue[x][y] != last_ce_value)
7979 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
7980 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
7982 if (CustomValue[x][y] == 0)
7984 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7985 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7993 case CA_SET_CE_SCORE:
7995 #if USE_NEW_CUSTOM_VALUE
7996 int last_ce_score = ei->collect_score;
7998 ei->collect_score = action_arg_number_new;
8000 if (ei->collect_score != last_ce_score)
8002 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8003 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8005 if (ei->collect_score == 0)
8009 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8010 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8013 This is a very special case that seems to be a mixture between
8014 CheckElementChange() and CheckTriggeredElementChange(): while
8015 the first one only affects single elements that are triggered
8016 directly, the second one affects multiple elements in the playfield
8017 that are triggered indirectly by another element. This is a third
8018 case: Changing the CE score always affects multiple identical CEs,
8019 so every affected CE must be checked, not only the single CE for
8020 which the CE score was changed in the first place (as every instance
8021 of that CE shares the same CE score, and therefore also can change)!
8023 SCAN_PLAYFIELD(xx, yy)
8025 if (Feld[xx][yy] == element)
8026 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8027 CE_SCORE_GETS_ZERO);
8036 /* ---------- engine actions ------------------------------------------ */
8038 case CA_SET_ENGINE_SCAN_MODE:
8040 InitPlayfieldScanMode(action_arg);
8050 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8052 int old_element = Feld[x][y];
8053 int new_element = get_element_from_group_element(element);
8054 int previous_move_direction = MovDir[x][y];
8055 #if USE_NEW_CUSTOM_VALUE
8056 int last_ce_value = CustomValue[x][y];
8058 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8059 boolean add_player_onto_element = (new_element_is_player &&
8060 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8061 /* this breaks SnakeBite when a snake is
8062 halfway through a door that closes */
8063 /* NOW FIXED AT LEVEL INIT IN files.c */
8064 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8066 IS_WALKABLE(old_element));
8069 /* check if element under the player changes from accessible to unaccessible
8070 (needed for special case of dropping element which then changes) */
8071 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8072 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8080 if (!add_player_onto_element)
8082 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8083 RemoveMovingField(x, y);
8087 Feld[x][y] = new_element;
8089 #if !USE_GFX_RESET_GFX_ANIMATION
8090 ResetGfxAnimation(x, y);
8091 ResetRandomAnimationValue(x, y);
8094 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8095 MovDir[x][y] = previous_move_direction;
8097 #if USE_NEW_CUSTOM_VALUE
8098 if (element_info[new_element].use_last_ce_value)
8099 CustomValue[x][y] = last_ce_value;
8102 InitField_WithBug1(x, y, FALSE);
8104 new_element = Feld[x][y]; /* element may have changed */
8106 #if USE_GFX_RESET_GFX_ANIMATION
8107 ResetGfxAnimation(x, y);
8108 ResetRandomAnimationValue(x, y);
8111 DrawLevelField(x, y);
8113 if (GFX_CRUMBLED(new_element))
8114 DrawLevelFieldCrumbledSandNeighbours(x, y);
8118 /* check if element under the player changes from accessible to unaccessible
8119 (needed for special case of dropping element which then changes) */
8120 /* (must be checked after creating new element for walkable group elements) */
8121 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8122 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8130 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8131 if (new_element_is_player)
8132 RelocatePlayer(x, y, new_element);
8135 ChangeCount[x][y]++; /* count number of changes in the same frame */
8137 TestIfBadThingTouchesPlayer(x, y);
8138 TestIfPlayerTouchesCustomElement(x, y);
8139 TestIfElementTouchesCustomElement(x, y);
8142 static void CreateField(int x, int y, int element)
8144 CreateFieldExt(x, y, element, FALSE);
8147 static void CreateElementFromChange(int x, int y, int element)
8149 element = GET_VALID_RUNTIME_ELEMENT(element);
8151 #if USE_STOP_CHANGED_ELEMENTS
8152 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8154 int old_element = Feld[x][y];
8156 /* prevent changed element from moving in same engine frame
8157 unless both old and new element can either fall or move */
8158 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8159 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8164 CreateFieldExt(x, y, element, TRUE);
8167 static boolean ChangeElement(int x, int y, int element, int page)
8169 struct ElementInfo *ei = &element_info[element];
8170 struct ElementChangeInfo *change = &ei->change_page[page];
8171 int ce_value = CustomValue[x][y];
8172 int ce_score = ei->collect_score;
8174 int old_element = Feld[x][y];
8176 /* always use default change event to prevent running into a loop */
8177 if (ChangeEvent[x][y] == -1)
8178 ChangeEvent[x][y] = CE_DELAY;
8180 if (ChangeEvent[x][y] == CE_DELAY)
8182 /* reset actual trigger element, trigger player and action element */
8183 change->actual_trigger_element = EL_EMPTY;
8184 change->actual_trigger_player = EL_PLAYER_1;
8185 change->actual_trigger_side = CH_SIDE_NONE;
8186 change->actual_trigger_ce_value = 0;
8187 change->actual_trigger_ce_score = 0;
8190 /* do not change elements more than a specified maximum number of changes */
8191 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8194 ChangeCount[x][y]++; /* count number of changes in the same frame */
8196 if (change->explode)
8203 if (change->use_target_content)
8205 boolean complete_replace = TRUE;
8206 boolean can_replace[3][3];
8209 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8212 boolean is_walkable;
8213 boolean is_diggable;
8214 boolean is_collectible;
8215 boolean is_removable;
8216 boolean is_destructible;
8217 int ex = x + xx - 1;
8218 int ey = y + yy - 1;
8219 int content_element = change->target_content.e[xx][yy];
8222 can_replace[xx][yy] = TRUE;
8224 if (ex == x && ey == y) /* do not check changing element itself */
8227 if (content_element == EL_EMPTY_SPACE)
8229 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8234 if (!IN_LEV_FIELD(ex, ey))
8236 can_replace[xx][yy] = FALSE;
8237 complete_replace = FALSE;
8244 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8245 e = MovingOrBlocked2Element(ex, ey);
8247 is_empty = (IS_FREE(ex, ey) ||
8248 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8250 is_walkable = (is_empty || IS_WALKABLE(e));
8251 is_diggable = (is_empty || IS_DIGGABLE(e));
8252 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8253 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8254 is_removable = (is_diggable || is_collectible);
8256 can_replace[xx][yy] =
8257 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8258 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8259 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8260 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8261 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8262 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8263 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8265 if (!can_replace[xx][yy])
8266 complete_replace = FALSE;
8269 if (!change->only_if_complete || complete_replace)
8271 boolean something_has_changed = FALSE;
8273 if (change->only_if_complete && change->use_random_replace &&
8274 RND(100) < change->random_percentage)
8277 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8279 int ex = x + xx - 1;
8280 int ey = y + yy - 1;
8281 int content_element;
8283 if (can_replace[xx][yy] && (!change->use_random_replace ||
8284 RND(100) < change->random_percentage))
8286 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8287 RemoveMovingField(ex, ey);
8289 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8291 content_element = change->target_content.e[xx][yy];
8292 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8293 ce_value, ce_score);
8295 CreateElementFromChange(ex, ey, target_element);
8297 something_has_changed = TRUE;
8299 /* for symmetry reasons, freeze newly created border elements */
8300 if (ex != x || ey != y)
8301 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8305 if (something_has_changed)
8307 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8308 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8314 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
8315 ce_value, ce_score);
8317 if (element == EL_DIAGONAL_GROWING ||
8318 element == EL_DIAGONAL_SHRINKING)
8320 target_element = Store[x][y];
8322 Store[x][y] = EL_EMPTY;
8325 CreateElementFromChange(x, y, target_element);
8327 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8328 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8331 /* this uses direct change before indirect change */
8332 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8337 #if USE_NEW_DELAYED_ACTION
8339 static void HandleElementChange(int x, int y, int page)
8341 int element = MovingOrBlocked2Element(x, y);
8342 struct ElementInfo *ei = &element_info[element];
8343 struct ElementChangeInfo *change = &ei->change_page[page];
8346 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8347 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8350 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8351 x, y, element, element_info[element].token_name);
8352 printf("HandleElementChange(): This should never happen!\n");
8357 /* this can happen with classic bombs on walkable, changing elements */
8358 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8361 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8362 ChangeDelay[x][y] = 0;
8368 if (ChangeDelay[x][y] == 0) /* initialize element change */
8370 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8372 if (change->can_change)
8374 ResetGfxAnimation(x, y);
8375 ResetRandomAnimationValue(x, y);
8377 if (change->pre_change_function)
8378 change->pre_change_function(x, y);
8382 ChangeDelay[x][y]--;
8384 if (ChangeDelay[x][y] != 0) /* continue element change */
8386 if (change->can_change)
8388 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8390 if (IS_ANIMATED(graphic))
8391 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8393 if (change->change_function)
8394 change->change_function(x, y);
8397 else /* finish element change */
8399 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8401 page = ChangePage[x][y];
8402 ChangePage[x][y] = -1;
8404 change = &ei->change_page[page];
8407 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8409 ChangeDelay[x][y] = 1; /* try change after next move step */
8410 ChangePage[x][y] = page; /* remember page to use for change */
8415 if (change->can_change)
8417 if (ChangeElement(x, y, element, page))
8419 if (change->post_change_function)
8420 change->post_change_function(x, y);
8424 if (change->has_action)
8425 ExecuteCustomElementAction(x, y, element, page);
8431 static void HandleElementChange(int x, int y, int page)
8433 int element = MovingOrBlocked2Element(x, y);
8434 struct ElementInfo *ei = &element_info[element];
8435 struct ElementChangeInfo *change = &ei->change_page[page];
8438 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8441 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8442 x, y, element, element_info[element].token_name);
8443 printf("HandleElementChange(): This should never happen!\n");
8448 /* this can happen with classic bombs on walkable, changing elements */
8449 if (!CAN_CHANGE(element))
8452 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8453 ChangeDelay[x][y] = 0;
8459 if (ChangeDelay[x][y] == 0) /* initialize element change */
8461 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8463 ResetGfxAnimation(x, y);
8464 ResetRandomAnimationValue(x, y);
8466 if (change->pre_change_function)
8467 change->pre_change_function(x, y);
8470 ChangeDelay[x][y]--;
8472 if (ChangeDelay[x][y] != 0) /* continue element change */
8474 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8476 if (IS_ANIMATED(graphic))
8477 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8479 if (change->change_function)
8480 change->change_function(x, y);
8482 else /* finish element change */
8484 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8486 page = ChangePage[x][y];
8487 ChangePage[x][y] = -1;
8489 change = &ei->change_page[page];
8492 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8494 ChangeDelay[x][y] = 1; /* try change after next move step */
8495 ChangePage[x][y] = page; /* remember page to use for change */
8500 if (ChangeElement(x, y, element, page))
8502 if (change->post_change_function)
8503 change->post_change_function(x, y);
8510 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8511 int trigger_element,
8517 boolean change_done_any = FALSE;
8518 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8521 if (!(trigger_events[trigger_element][trigger_event]))
8524 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8526 int element = EL_CUSTOM_START + i;
8527 boolean change_done = FALSE;
8530 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8531 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8534 for (p = 0; p < element_info[element].num_change_pages; p++)
8536 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8538 if (change->can_change_or_has_action &&
8539 change->has_event[trigger_event] &&
8540 change->trigger_side & trigger_side &&
8541 change->trigger_player & trigger_player &&
8542 change->trigger_page & trigger_page_bits &&
8543 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8545 change->actual_trigger_element = trigger_element;
8546 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8547 change->actual_trigger_side = trigger_side;
8548 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8549 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8551 if ((change->can_change && !change_done) || change->has_action)
8555 SCAN_PLAYFIELD(x, y)
8557 if (Feld[x][y] == element)
8559 if (change->can_change && !change_done)
8561 ChangeDelay[x][y] = 1;
8562 ChangeEvent[x][y] = trigger_event;
8564 HandleElementChange(x, y, p);
8566 #if USE_NEW_DELAYED_ACTION
8567 else if (change->has_action)
8569 ExecuteCustomElementAction(x, y, element, p);
8570 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8573 if (change->has_action)
8575 ExecuteCustomElementAction(x, y, element, p);
8576 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8582 if (change->can_change)
8585 change_done_any = TRUE;
8592 return change_done_any;
8595 static boolean CheckElementChangeExt(int x, int y,
8597 int trigger_element,
8602 boolean change_done = FALSE;
8605 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8606 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8609 if (Feld[x][y] == EL_BLOCKED)
8611 Blocked2Moving(x, y, &x, &y);
8612 element = Feld[x][y];
8616 /* check if element has already changed */
8617 if (Feld[x][y] != element)
8620 /* check if element has already changed or is about to change after moving */
8621 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8622 Feld[x][y] != element) ||
8624 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8625 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8626 ChangePage[x][y] != -1)))
8630 for (p = 0; p < element_info[element].num_change_pages; p++)
8632 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8634 boolean check_trigger_element =
8635 (trigger_event == CE_TOUCHING_X ||
8636 trigger_event == CE_HITTING_X ||
8637 trigger_event == CE_HIT_BY_X);
8639 if (change->can_change_or_has_action &&
8640 change->has_event[trigger_event] &&
8641 change->trigger_side & trigger_side &&
8642 change->trigger_player & trigger_player &&
8643 (!check_trigger_element ||
8644 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8646 change->actual_trigger_element = trigger_element;
8647 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8648 change->actual_trigger_side = trigger_side;
8649 change->actual_trigger_ce_value = CustomValue[x][y];
8650 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8652 /* special case: trigger element not at (x,y) position for some events */
8653 if (check_trigger_element)
8665 { 0, 0 }, { 0, 0 }, { 0, 0 },
8669 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8670 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8672 change->actual_trigger_ce_value = CustomValue[xx][yy];
8673 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8676 if (change->can_change && !change_done)
8678 ChangeDelay[x][y] = 1;
8679 ChangeEvent[x][y] = trigger_event;
8681 HandleElementChange(x, y, p);
8685 #if USE_NEW_DELAYED_ACTION
8686 else if (change->has_action)
8688 ExecuteCustomElementAction(x, y, element, p);
8689 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8692 if (change->has_action)
8694 ExecuteCustomElementAction(x, y, element, p);
8695 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8704 static void PlayPlayerSound(struct PlayerInfo *player)
8706 int jx = player->jx, jy = player->jy;
8707 int sound_element = player->artwork_element;
8708 int last_action = player->last_action_waiting;
8709 int action = player->action_waiting;
8711 if (player->is_waiting)
8713 if (action != last_action)
8714 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8716 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8720 if (action != last_action)
8721 StopSound(element_info[sound_element].sound[last_action]);
8723 if (last_action == ACTION_SLEEPING)
8724 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8728 static void PlayAllPlayersSound()
8732 for (i = 0; i < MAX_PLAYERS; i++)
8733 if (stored_player[i].active)
8734 PlayPlayerSound(&stored_player[i]);
8737 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8739 boolean last_waiting = player->is_waiting;
8740 int move_dir = player->MovDir;
8742 player->dir_waiting = move_dir;
8743 player->last_action_waiting = player->action_waiting;
8747 if (!last_waiting) /* not waiting -> waiting */
8749 player->is_waiting = TRUE;
8751 player->frame_counter_bored =
8753 game.player_boring_delay_fixed +
8754 GetSimpleRandom(game.player_boring_delay_random);
8755 player->frame_counter_sleeping =
8757 game.player_sleeping_delay_fixed +
8758 GetSimpleRandom(game.player_sleeping_delay_random);
8760 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
8763 if (game.player_sleeping_delay_fixed +
8764 game.player_sleeping_delay_random > 0 &&
8765 player->anim_delay_counter == 0 &&
8766 player->post_delay_counter == 0 &&
8767 FrameCounter >= player->frame_counter_sleeping)
8768 player->is_sleeping = TRUE;
8769 else if (game.player_boring_delay_fixed +
8770 game.player_boring_delay_random > 0 &&
8771 FrameCounter >= player->frame_counter_bored)
8772 player->is_bored = TRUE;
8774 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8775 player->is_bored ? ACTION_BORING :
8778 if (player->is_sleeping && player->use_murphy)
8780 /* special case for sleeping Murphy when leaning against non-free tile */
8782 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
8783 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
8784 !IS_MOVING(player->jx - 1, player->jy)))
8786 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
8787 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
8788 !IS_MOVING(player->jx + 1, player->jy)))
8789 move_dir = MV_RIGHT;
8791 player->is_sleeping = FALSE;
8793 player->dir_waiting = move_dir;
8796 if (player->is_sleeping)
8798 if (player->num_special_action_sleeping > 0)
8800 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8802 int last_special_action = player->special_action_sleeping;
8803 int num_special_action = player->num_special_action_sleeping;
8804 int special_action =
8805 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8806 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8807 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8808 last_special_action + 1 : ACTION_SLEEPING);
8809 int special_graphic =
8810 el_act_dir2img(player->artwork_element, special_action, move_dir);
8812 player->anim_delay_counter =
8813 graphic_info[special_graphic].anim_delay_fixed +
8814 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8815 player->post_delay_counter =
8816 graphic_info[special_graphic].post_delay_fixed +
8817 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8819 player->special_action_sleeping = special_action;
8822 if (player->anim_delay_counter > 0)
8824 player->action_waiting = player->special_action_sleeping;
8825 player->anim_delay_counter--;
8827 else if (player->post_delay_counter > 0)
8829 player->post_delay_counter--;
8833 else if (player->is_bored)
8835 if (player->num_special_action_bored > 0)
8837 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8839 int special_action =
8840 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
8841 int special_graphic =
8842 el_act_dir2img(player->artwork_element, special_action, move_dir);
8844 player->anim_delay_counter =
8845 graphic_info[special_graphic].anim_delay_fixed +
8846 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8847 player->post_delay_counter =
8848 graphic_info[special_graphic].post_delay_fixed +
8849 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8851 player->special_action_bored = special_action;
8854 if (player->anim_delay_counter > 0)
8856 player->action_waiting = player->special_action_bored;
8857 player->anim_delay_counter--;
8859 else if (player->post_delay_counter > 0)
8861 player->post_delay_counter--;
8866 else if (last_waiting) /* waiting -> not waiting */
8868 player->is_waiting = FALSE;
8869 player->is_bored = FALSE;
8870 player->is_sleeping = FALSE;
8872 player->frame_counter_bored = -1;
8873 player->frame_counter_sleeping = -1;
8875 player->anim_delay_counter = 0;
8876 player->post_delay_counter = 0;
8878 player->dir_waiting = player->MovDir;
8879 player->action_waiting = ACTION_DEFAULT;
8881 player->special_action_bored = ACTION_DEFAULT;
8882 player->special_action_sleeping = ACTION_DEFAULT;
8886 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8888 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8889 int left = player_action & JOY_LEFT;
8890 int right = player_action & JOY_RIGHT;
8891 int up = player_action & JOY_UP;
8892 int down = player_action & JOY_DOWN;
8893 int button1 = player_action & JOY_BUTTON_1;
8894 int button2 = player_action & JOY_BUTTON_2;
8895 int dx = (left ? -1 : right ? 1 : 0);
8896 int dy = (up ? -1 : down ? 1 : 0);
8898 if (!player->active || tape.pausing)
8904 snapped = SnapField(player, dx, dy);
8908 dropped = DropElement(player);
8910 moved = MovePlayer(player, dx, dy);
8913 if (tape.single_step && tape.recording && !tape.pausing)
8915 if (button1 || (dropped && !moved))
8917 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8918 SnapField(player, 0, 0); /* stop snapping */
8922 SetPlayerWaiting(player, FALSE);
8924 return player_action;
8928 /* no actions for this player (no input at player's configured device) */
8930 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8931 SnapField(player, 0, 0);
8932 CheckGravityMovementWhenNotMoving(player);
8934 if (player->MovPos == 0)
8935 SetPlayerWaiting(player, TRUE);
8937 if (player->MovPos == 0) /* needed for tape.playing */
8938 player->is_moving = FALSE;
8940 player->is_dropping = FALSE;
8941 player->is_dropping_pressed = FALSE;
8942 player->drop_pressed_delay = 0;
8948 static void CheckLevelTime()
8952 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8954 if (level.native_em_level->lev->home == 0) /* all players at home */
8956 PlayerWins(local_player);
8958 AllPlayersGone = TRUE;
8960 level.native_em_level->lev->home = -1;
8963 if (level.native_em_level->ply[0]->alive == 0 &&
8964 level.native_em_level->ply[1]->alive == 0 &&
8965 level.native_em_level->ply[2]->alive == 0 &&
8966 level.native_em_level->ply[3]->alive == 0) /* all dead */
8967 AllPlayersGone = TRUE;
8970 if (TimeFrames >= FRAMES_PER_SECOND)
8975 for (i = 0; i < MAX_PLAYERS; i++)
8977 struct PlayerInfo *player = &stored_player[i];
8979 if (SHIELD_ON(player))
8981 player->shield_normal_time_left--;
8983 if (player->shield_deadly_time_left > 0)
8984 player->shield_deadly_time_left--;
8988 if (!local_player->LevelSolved && !level.use_step_counter)
8996 if (TimeLeft <= 10 && setup.time_limit)
8997 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
8999 DrawGameValue_Time(TimeLeft);
9001 if (!TimeLeft && setup.time_limit)
9003 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9004 level.native_em_level->lev->killed_out_of_time = TRUE;
9006 for (i = 0; i < MAX_PLAYERS; i++)
9007 KillPlayer(&stored_player[i]);
9010 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9011 DrawGameValue_Time(TimePlayed);
9013 level.native_em_level->lev->time =
9014 (level.time == 0 ? TimePlayed : TimeLeft);
9017 if (tape.recording || tape.playing)
9018 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9022 void AdvanceFrameAndPlayerCounters(int player_nr)
9026 /* advance frame counters (global frame counter and time frame counter) */
9030 /* advance player counters (counters for move delay, move animation etc.) */
9031 for (i = 0; i < MAX_PLAYERS; i++)
9033 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9034 int move_delay_value = stored_player[i].move_delay_value;
9035 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9037 if (!advance_player_counters) /* not all players may be affected */
9040 #if USE_NEW_PLAYER_ANIM
9041 if (move_frames == 0) /* less than one move per game frame */
9043 int stepsize = TILEX / move_delay_value;
9044 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9045 int count = (stored_player[i].is_moving ?
9046 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9048 if (count % delay == 0)
9053 stored_player[i].Frame += move_frames;
9055 if (stored_player[i].MovPos != 0)
9056 stored_player[i].StepFrame += move_frames;
9058 if (stored_player[i].move_delay > 0)
9059 stored_player[i].move_delay--;
9061 /* due to bugs in previous versions, counter must count up, not down */
9062 if (stored_player[i].push_delay != -1)
9063 stored_player[i].push_delay++;
9065 if (stored_player[i].drop_delay > 0)
9066 stored_player[i].drop_delay--;
9068 if (stored_player[i].is_dropping_pressed)
9069 stored_player[i].drop_pressed_delay++;
9073 void StartGameActions(boolean init_network_game, boolean record_tape,
9076 unsigned long new_random_seed = InitRND(random_seed);
9079 TapeStartRecording(new_random_seed);
9081 #if defined(NETWORK_AVALIABLE)
9082 if (init_network_game)
9084 SendToServer_StartPlaying();
9095 static unsigned long game_frame_delay = 0;
9096 unsigned long game_frame_delay_value;
9097 byte *recorded_player_action;
9098 byte summarized_player_action = 0;
9099 byte tape_action[MAX_PLAYERS];
9102 if (game.restart_level)
9103 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9105 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9107 if (level.native_em_level->lev->home == 0) /* all players at home */
9109 PlayerWins(local_player);
9111 AllPlayersGone = TRUE;
9113 level.native_em_level->lev->home = -1;
9116 if (level.native_em_level->ply[0]->alive == 0 &&
9117 level.native_em_level->ply[1]->alive == 0 &&
9118 level.native_em_level->ply[2]->alive == 0 &&
9119 level.native_em_level->ply[3]->alive == 0) /* all dead */
9120 AllPlayersGone = TRUE;
9123 if (local_player->LevelSolved)
9126 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9129 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9132 game_frame_delay_value =
9133 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9135 if (tape.playing && tape.warp_forward && !tape.pausing)
9136 game_frame_delay_value = 0;
9138 /* ---------- main game synchronization point ---------- */
9140 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9142 if (network_playing && !network_player_action_received)
9144 /* try to get network player actions in time */
9146 #if defined(NETWORK_AVALIABLE)
9147 /* last chance to get network player actions without main loop delay */
9151 /* game was quit by network peer */
9152 if (game_status != GAME_MODE_PLAYING)
9155 if (!network_player_action_received)
9156 return; /* failed to get network player actions in time */
9158 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9164 /* at this point we know that we really continue executing the game */
9166 network_player_action_received = FALSE;
9168 /* when playing tape, read previously recorded player input from tape data */
9169 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9172 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9177 if (tape.set_centered_player)
9179 game.centered_player_nr_next = tape.centered_player_nr_next;
9180 game.set_centered_player = TRUE;
9183 for (i = 0; i < MAX_PLAYERS; i++)
9185 summarized_player_action |= stored_player[i].action;
9187 if (!network_playing)
9188 stored_player[i].effective_action = stored_player[i].action;
9191 #if defined(NETWORK_AVALIABLE)
9192 if (network_playing)
9193 SendToServer_MovePlayer(summarized_player_action);
9196 if (!options.network && !setup.team_mode)
9197 local_player->effective_action = summarized_player_action;
9199 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9201 for (i = 0; i < MAX_PLAYERS; i++)
9202 stored_player[i].effective_action =
9203 (i == game.centered_player_nr ? summarized_player_action : 0);
9206 if (recorded_player_action != NULL)
9207 for (i = 0; i < MAX_PLAYERS; i++)
9208 stored_player[i].effective_action = recorded_player_action[i];
9210 for (i = 0; i < MAX_PLAYERS; i++)
9212 tape_action[i] = stored_player[i].effective_action;
9214 /* (this can only happen in the R'n'D game engine) */
9215 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9216 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9219 /* only record actions from input devices, but not programmed actions */
9221 TapeRecordAction(tape_action);
9223 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9225 GameActions_EM_Main();
9233 void GameActions_EM_Main()
9235 byte effective_action[MAX_PLAYERS];
9236 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9239 for (i = 0; i < MAX_PLAYERS; i++)
9240 effective_action[i] = stored_player[i].effective_action;
9242 GameActions_EM(effective_action, warp_mode);
9246 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9249 void GameActions_RND()
9251 int magic_wall_x = 0, magic_wall_y = 0;
9252 int i, x, y, element, graphic;
9254 InitPlayfieldScanModeVars();
9256 #if USE_ONE_MORE_CHANGE_PER_FRAME
9257 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9259 SCAN_PLAYFIELD(x, y)
9261 ChangeCount[x][y] = 0;
9262 ChangeEvent[x][y] = -1;
9267 if (game.set_centered_player)
9269 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9271 /* switching to "all players" only possible if all players fit to screen */
9272 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9274 game.centered_player_nr_next = game.centered_player_nr;
9275 game.set_centered_player = FALSE;
9278 /* do not switch focus to non-existing (or non-active) player */
9279 if (game.centered_player_nr_next >= 0 &&
9280 !stored_player[game.centered_player_nr_next].active)
9282 game.centered_player_nr_next = game.centered_player_nr;
9283 game.set_centered_player = FALSE;
9287 if (game.set_centered_player &&
9288 ScreenMovPos == 0) /* screen currently aligned at tile position */
9292 if (game.centered_player_nr_next == -1)
9294 setScreenCenteredToAllPlayers(&sx, &sy);
9298 sx = stored_player[game.centered_player_nr_next].jx;
9299 sy = stored_player[game.centered_player_nr_next].jy;
9302 game.centered_player_nr = game.centered_player_nr_next;
9303 game.set_centered_player = FALSE;
9305 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9306 DrawGameDoorValues();
9309 for (i = 0; i < MAX_PLAYERS; i++)
9311 int actual_player_action = stored_player[i].effective_action;
9314 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9315 - rnd_equinox_tetrachloride 048
9316 - rnd_equinox_tetrachloride_ii 096
9317 - rnd_emanuel_schmieg 002
9318 - doctor_sloan_ww 001, 020
9320 if (stored_player[i].MovPos == 0)
9321 CheckGravityMovement(&stored_player[i]);
9324 /* overwrite programmed action with tape action */
9325 if (stored_player[i].programmed_action)
9326 actual_player_action = stored_player[i].programmed_action;
9328 PlayerActions(&stored_player[i], actual_player_action);
9330 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9333 ScrollScreen(NULL, SCROLL_GO_ON);
9335 /* for backwards compatibility, the following code emulates a fixed bug that
9336 occured when pushing elements (causing elements that just made their last
9337 pushing step to already (if possible) make their first falling step in the
9338 same game frame, which is bad); this code is also needed to use the famous
9339 "spring push bug" which is used in older levels and might be wanted to be
9340 used also in newer levels, but in this case the buggy pushing code is only
9341 affecting the "spring" element and no other elements */
9343 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9345 for (i = 0; i < MAX_PLAYERS; i++)
9347 struct PlayerInfo *player = &stored_player[i];
9351 if (player->active && player->is_pushing && player->is_moving &&
9353 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9354 Feld[x][y] == EL_SPRING))
9356 ContinueMoving(x, y);
9358 /* continue moving after pushing (this is actually a bug) */
9359 if (!IS_MOVING(x, y))
9367 SCAN_PLAYFIELD(x, y)
9369 ChangeCount[x][y] = 0;
9370 ChangeEvent[x][y] = -1;
9372 /* this must be handled before main playfield loop */
9373 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9376 if (MovDelay[x][y] <= 0)
9380 #if USE_NEW_SNAP_DELAY
9381 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9384 if (MovDelay[x][y] <= 0)
9387 DrawLevelField(x, y);
9389 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9395 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9397 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9398 printf("GameActions(): This should never happen!\n");
9400 ChangePage[x][y] = -1;
9405 if (WasJustMoving[x][y] > 0)
9406 WasJustMoving[x][y]--;
9407 if (WasJustFalling[x][y] > 0)
9408 WasJustFalling[x][y]--;
9409 if (CheckCollision[x][y] > 0)
9410 CheckCollision[x][y]--;
9414 /* reset finished pushing action (not done in ContinueMoving() to allow
9415 continuous pushing animation for elements with zero push delay) */
9416 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9418 ResetGfxAnimation(x, y);
9419 DrawLevelField(x, y);
9423 if (IS_BLOCKED(x, y))
9427 Blocked2Moving(x, y, &oldx, &oldy);
9428 if (!IS_MOVING(oldx, oldy))
9430 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9431 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9432 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9433 printf("GameActions(): This should never happen!\n");
9439 SCAN_PLAYFIELD(x, y)
9441 element = Feld[x][y];
9442 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9444 ResetGfxFrame(x, y, TRUE);
9446 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9447 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9448 ResetRandomAnimationValue(x, y);
9450 SetRandomAnimationValue(x, y);
9452 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9454 if (IS_INACTIVE(element))
9456 if (IS_ANIMATED(graphic))
9457 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9462 /* this may take place after moving, so 'element' may have changed */
9463 if (IS_CHANGING(x, y) &&
9464 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9466 int page = element_info[element].event_page_nr[CE_DELAY];
9469 HandleElementChange(x, y, page);
9471 if (CAN_CHANGE(element))
9472 HandleElementChange(x, y, page);
9474 if (HAS_ACTION(element))
9475 ExecuteCustomElementAction(x, y, element, page);
9478 element = Feld[x][y];
9479 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9482 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9486 element = Feld[x][y];
9487 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9489 if (IS_ANIMATED(graphic) &&
9492 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9494 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9495 DrawTwinkleOnField(x, y);
9497 else if ((element == EL_ACID ||
9498 element == EL_EXIT_OPEN ||
9499 element == EL_SP_EXIT_OPEN ||
9500 element == EL_SP_TERMINAL ||
9501 element == EL_SP_TERMINAL_ACTIVE ||
9502 element == EL_EXTRA_TIME ||
9503 element == EL_SHIELD_NORMAL ||
9504 element == EL_SHIELD_DEADLY) &&
9505 IS_ANIMATED(graphic))
9506 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9507 else if (IS_MOVING(x, y))
9508 ContinueMoving(x, y);
9509 else if (IS_ACTIVE_BOMB(element))
9510 CheckDynamite(x, y);
9511 else if (element == EL_AMOEBA_GROWING)
9512 AmoebeWaechst(x, y);
9513 else if (element == EL_AMOEBA_SHRINKING)
9514 AmoebaDisappearing(x, y);
9516 #if !USE_NEW_AMOEBA_CODE
9517 else if (IS_AMOEBALIVE(element))
9518 AmoebeAbleger(x, y);
9521 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9523 else if (element == EL_EXIT_CLOSED)
9525 else if (element == EL_SP_EXIT_CLOSED)
9527 else if (element == EL_EXPANDABLE_WALL_GROWING)
9529 else if (element == EL_EXPANDABLE_WALL ||
9530 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9531 element == EL_EXPANDABLE_WALL_VERTICAL ||
9532 element == EL_EXPANDABLE_WALL_ANY ||
9533 element == EL_BD_EXPANDABLE_WALL)
9535 else if (element == EL_FLAMES)
9536 CheckForDragon(x, y);
9537 else if (element == EL_EXPLOSION)
9538 ; /* drawing of correct explosion animation is handled separately */
9539 else if (element == EL_ELEMENT_SNAPPING ||
9540 element == EL_DIAGONAL_SHRINKING ||
9541 element == EL_DIAGONAL_GROWING)
9543 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9545 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9547 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9548 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9550 if (IS_BELT_ACTIVE(element))
9551 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9553 if (game.magic_wall_active)
9555 int jx = local_player->jx, jy = local_player->jy;
9557 /* play the element sound at the position nearest to the player */
9558 if ((element == EL_MAGIC_WALL_FULL ||
9559 element == EL_MAGIC_WALL_ACTIVE ||
9560 element == EL_MAGIC_WALL_EMPTYING ||
9561 element == EL_BD_MAGIC_WALL_FULL ||
9562 element == EL_BD_MAGIC_WALL_ACTIVE ||
9563 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9564 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9572 #if USE_NEW_AMOEBA_CODE
9573 /* new experimental amoeba growth stuff */
9574 if (!(FrameCounter % 8))
9576 static unsigned long random = 1684108901;
9578 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9580 x = RND(lev_fieldx);
9581 y = RND(lev_fieldy);
9582 element = Feld[x][y];
9584 if (!IS_PLAYER(x,y) &&
9585 (element == EL_EMPTY ||
9586 CAN_GROW_INTO(element) ||
9587 element == EL_QUICKSAND_EMPTY ||
9588 element == EL_ACID_SPLASH_LEFT ||
9589 element == EL_ACID_SPLASH_RIGHT))
9591 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9592 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9593 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9594 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9595 Feld[x][y] = EL_AMOEBA_DROP;
9598 random = random * 129 + 1;
9604 if (game.explosions_delayed)
9607 game.explosions_delayed = FALSE;
9609 SCAN_PLAYFIELD(x, y)
9611 element = Feld[x][y];
9613 if (ExplodeField[x][y])
9614 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9615 else if (element == EL_EXPLOSION)
9616 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9618 ExplodeField[x][y] = EX_TYPE_NONE;
9621 game.explosions_delayed = TRUE;
9624 if (game.magic_wall_active)
9626 if (!(game.magic_wall_time_left % 4))
9628 int element = Feld[magic_wall_x][magic_wall_y];
9630 if (element == EL_BD_MAGIC_WALL_FULL ||
9631 element == EL_BD_MAGIC_WALL_ACTIVE ||
9632 element == EL_BD_MAGIC_WALL_EMPTYING)
9633 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9635 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9638 if (game.magic_wall_time_left > 0)
9640 game.magic_wall_time_left--;
9641 if (!game.magic_wall_time_left)
9643 SCAN_PLAYFIELD(x, y)
9645 element = Feld[x][y];
9647 if (element == EL_MAGIC_WALL_ACTIVE ||
9648 element == EL_MAGIC_WALL_FULL)
9650 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9651 DrawLevelField(x, y);
9653 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9654 element == EL_BD_MAGIC_WALL_FULL)
9656 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9657 DrawLevelField(x, y);
9661 game.magic_wall_active = FALSE;
9666 if (game.light_time_left > 0)
9668 game.light_time_left--;
9670 if (game.light_time_left == 0)
9671 RedrawAllLightSwitchesAndInvisibleElements();
9674 if (game.timegate_time_left > 0)
9676 game.timegate_time_left--;
9678 if (game.timegate_time_left == 0)
9679 CloseAllOpenTimegates();
9682 if (game.lenses_time_left > 0)
9684 game.lenses_time_left--;
9686 if (game.lenses_time_left == 0)
9687 RedrawAllInvisibleElementsForLenses();
9690 if (game.magnify_time_left > 0)
9692 game.magnify_time_left--;
9694 if (game.magnify_time_left == 0)
9695 RedrawAllInvisibleElementsForMagnifier();
9698 for (i = 0; i < MAX_PLAYERS; i++)
9700 struct PlayerInfo *player = &stored_player[i];
9702 if (SHIELD_ON(player))
9704 if (player->shield_deadly_time_left)
9705 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9706 else if (player->shield_normal_time_left)
9707 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9714 PlayAllPlayersSound();
9716 if (options.debug) /* calculate frames per second */
9718 static unsigned long fps_counter = 0;
9719 static int fps_frames = 0;
9720 unsigned long fps_delay_ms = Counter() - fps_counter;
9724 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9726 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9729 fps_counter = Counter();
9732 redraw_mask |= REDRAW_FPS;
9735 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9737 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9739 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9741 local_player->show_envelope = 0;
9744 /* use random number generator in every frame to make it less predictable */
9745 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9749 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9751 int min_x = x, min_y = y, max_x = x, max_y = y;
9754 for (i = 0; i < MAX_PLAYERS; i++)
9756 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9758 if (!stored_player[i].active || &stored_player[i] == player)
9761 min_x = MIN(min_x, jx);
9762 min_y = MIN(min_y, jy);
9763 max_x = MAX(max_x, jx);
9764 max_y = MAX(max_y, jy);
9767 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9770 static boolean AllPlayersInVisibleScreen()
9774 for (i = 0; i < MAX_PLAYERS; i++)
9776 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9778 if (!stored_player[i].active)
9781 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9788 void ScrollLevel(int dx, int dy)
9790 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9793 BlitBitmap(drawto_field, drawto_field,
9794 FX + TILEX * (dx == -1) - softscroll_offset,
9795 FY + TILEY * (dy == -1) - softscroll_offset,
9796 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9797 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9798 FX + TILEX * (dx == 1) - softscroll_offset,
9799 FY + TILEY * (dy == 1) - softscroll_offset);
9803 x = (dx == 1 ? BX1 : BX2);
9804 for (y = BY1; y <= BY2; y++)
9805 DrawScreenField(x, y);
9810 y = (dy == 1 ? BY1 : BY2);
9811 for (x = BX1; x <= BX2; x++)
9812 DrawScreenField(x, y);
9815 redraw_mask |= REDRAW_FIELD;
9818 static boolean canFallDown(struct PlayerInfo *player)
9820 int jx = player->jx, jy = player->jy;
9822 return (IN_LEV_FIELD(jx, jy + 1) &&
9823 (IS_FREE(jx, jy + 1) ||
9824 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9825 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9826 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9829 static boolean canPassField(int x, int y, int move_dir)
9831 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9832 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9833 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9836 int element = Feld[x][y];
9838 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9839 !CAN_MOVE(element) &&
9840 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9841 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9842 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9845 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9847 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9848 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9849 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9853 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9854 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9855 (IS_DIGGABLE(Feld[newx][newy]) ||
9856 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9857 canPassField(newx, newy, move_dir)));
9860 static void CheckGravityMovement(struct PlayerInfo *player)
9862 #if USE_PLAYER_GRAVITY
9863 if (player->gravity && !player->programmed_action)
9865 if (game.gravity && !player->programmed_action)
9868 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9869 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9870 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
9871 int jx = player->jx, jy = player->jy;
9872 boolean player_is_moving_to_valid_field =
9873 (!player_is_snapping &&
9874 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9875 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9876 boolean player_can_fall_down = canFallDown(player);
9878 if (player_can_fall_down &&
9879 !player_is_moving_to_valid_field)
9880 player->programmed_action = MV_DOWN;
9884 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9886 return CheckGravityMovement(player);
9888 #if USE_PLAYER_GRAVITY
9889 if (player->gravity && !player->programmed_action)
9891 if (game.gravity && !player->programmed_action)
9894 int jx = player->jx, jy = player->jy;
9895 boolean field_under_player_is_free =
9896 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9897 boolean player_is_standing_on_valid_field =
9898 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9899 (IS_WALKABLE(Feld[jx][jy]) &&
9900 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9902 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9903 player->programmed_action = MV_DOWN;
9909 -----------------------------------------------------------------------------
9910 dx, dy: direction (non-diagonal) to try to move the player to
9911 real_dx, real_dy: direction as read from input device (can be diagonal)
9914 boolean MovePlayerOneStep(struct PlayerInfo *player,
9915 int dx, int dy, int real_dx, int real_dy)
9917 int jx = player->jx, jy = player->jy;
9918 int new_jx = jx + dx, new_jy = jy + dy;
9919 #if !USE_FIXED_DONT_RUN_INTO
9923 boolean player_can_move = !player->cannot_move;
9925 if (!player->active || (!dx && !dy))
9926 return MP_NO_ACTION;
9928 player->MovDir = (dx < 0 ? MV_LEFT :
9931 dy > 0 ? MV_DOWN : MV_NONE);
9933 if (!IN_LEV_FIELD(new_jx, new_jy))
9934 return MP_NO_ACTION;
9936 if (!player_can_move)
9938 if (player->MovPos == 0)
9940 player->is_moving = FALSE;
9941 player->is_digging = FALSE;
9942 player->is_collecting = FALSE;
9943 player->is_snapping = FALSE;
9944 player->is_pushing = FALSE;
9949 if (!options.network && game.centered_player_nr == -1 &&
9950 !AllPlayersInSight(player, new_jx, new_jy))
9951 return MP_NO_ACTION;
9953 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9954 return MP_NO_ACTION;
9957 #if !USE_FIXED_DONT_RUN_INTO
9958 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9960 /* (moved to DigField()) */
9961 if (player_can_move && DONT_RUN_INTO(element))
9963 if (element == EL_ACID && dx == 0 && dy == 1)
9965 SplashAcid(new_jx, new_jy);
9966 Feld[jx][jy] = EL_PLAYER_1;
9967 InitMovingField(jx, jy, MV_DOWN);
9968 Store[jx][jy] = EL_ACID;
9969 ContinueMoving(jx, jy);
9973 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9979 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9980 if (can_move != MP_MOVING)
9983 /* check if DigField() has caused relocation of the player */
9984 if (player->jx != jx || player->jy != jy)
9985 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
9987 StorePlayer[jx][jy] = 0;
9988 player->last_jx = jx;
9989 player->last_jy = jy;
9990 player->jx = new_jx;
9991 player->jy = new_jy;
9992 StorePlayer[new_jx][new_jy] = player->element_nr;
9994 if (player->move_delay_value_next != -1)
9996 player->move_delay_value = player->move_delay_value_next;
9997 player->move_delay_value_next = -1;
10001 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10003 player->step_counter++;
10005 PlayerVisit[jx][jy] = FrameCounter;
10007 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10008 player->is_moving = TRUE;
10012 /* should better be called in MovePlayer(), but this breaks some tapes */
10013 ScrollPlayer(player, SCROLL_INIT);
10019 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10021 int jx = player->jx, jy = player->jy;
10022 int old_jx = jx, old_jy = jy;
10023 int moved = MP_NO_ACTION;
10025 if (!player->active)
10030 if (player->MovPos == 0)
10032 player->is_moving = FALSE;
10033 player->is_digging = FALSE;
10034 player->is_collecting = FALSE;
10035 player->is_snapping = FALSE;
10036 player->is_pushing = FALSE;
10042 if (player->move_delay > 0)
10045 player->move_delay = -1; /* set to "uninitialized" value */
10047 /* store if player is automatically moved to next field */
10048 player->is_auto_moving = (player->programmed_action != MV_NONE);
10050 /* remove the last programmed player action */
10051 player->programmed_action = 0;
10053 if (player->MovPos)
10055 /* should only happen if pre-1.2 tape recordings are played */
10056 /* this is only for backward compatibility */
10058 int original_move_delay_value = player->move_delay_value;
10061 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10065 /* scroll remaining steps with finest movement resolution */
10066 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10068 while (player->MovPos)
10070 ScrollPlayer(player, SCROLL_GO_ON);
10071 ScrollScreen(NULL, SCROLL_GO_ON);
10073 AdvanceFrameAndPlayerCounters(player->index_nr);
10079 player->move_delay_value = original_move_delay_value;
10082 player->is_active = FALSE;
10084 if (player->last_move_dir & MV_HORIZONTAL)
10086 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10087 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10091 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10092 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10095 #if USE_FIXED_BORDER_RUNNING_GFX
10096 if (!moved && !player->is_active)
10098 player->is_moving = FALSE;
10099 player->is_digging = FALSE;
10100 player->is_collecting = FALSE;
10101 player->is_snapping = FALSE;
10102 player->is_pushing = FALSE;
10110 if (moved & MP_MOVING && !ScreenMovPos &&
10111 (player->index_nr == game.centered_player_nr ||
10112 game.centered_player_nr == -1))
10114 if (moved & MP_MOVING && !ScreenMovPos &&
10115 (player == local_player || !options.network))
10118 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10119 int offset = (setup.scroll_delay ? 3 : 0);
10121 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10123 /* actual player has left the screen -- scroll in that direction */
10124 if (jx != old_jx) /* player has moved horizontally */
10125 scroll_x += (jx - old_jx);
10126 else /* player has moved vertically */
10127 scroll_y += (jy - old_jy);
10131 if (jx != old_jx) /* player has moved horizontally */
10133 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10134 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10135 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10137 /* don't scroll over playfield boundaries */
10138 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10139 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10141 /* don't scroll more than one field at a time */
10142 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10144 /* don't scroll against the player's moving direction */
10145 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10146 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10147 scroll_x = old_scroll_x;
10149 else /* player has moved vertically */
10151 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10152 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10153 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10155 /* don't scroll over playfield boundaries */
10156 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10157 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10159 /* don't scroll more than one field at a time */
10160 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10162 /* don't scroll against the player's moving direction */
10163 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10164 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10165 scroll_y = old_scroll_y;
10169 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10172 if (!options.network && game.centered_player_nr == -1 &&
10173 !AllPlayersInVisibleScreen())
10175 scroll_x = old_scroll_x;
10176 scroll_y = old_scroll_y;
10180 if (!options.network && !AllPlayersInVisibleScreen())
10182 scroll_x = old_scroll_x;
10183 scroll_y = old_scroll_y;
10188 ScrollScreen(player, SCROLL_INIT);
10189 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10194 player->StepFrame = 0;
10196 if (moved & MP_MOVING)
10198 if (old_jx != jx && old_jy == jy)
10199 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10200 else if (old_jx == jx && old_jy != jy)
10201 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10203 DrawLevelField(jx, jy); /* for "crumbled sand" */
10205 player->last_move_dir = player->MovDir;
10206 player->is_moving = TRUE;
10207 player->is_snapping = FALSE;
10208 player->is_switching = FALSE;
10209 player->is_dropping = FALSE;
10210 player->is_dropping_pressed = FALSE;
10211 player->drop_pressed_delay = 0;
10214 /* should better be called here than above, but this breaks some tapes */
10215 ScrollPlayer(player, SCROLL_INIT);
10220 CheckGravityMovementWhenNotMoving(player);
10222 player->is_moving = FALSE;
10224 /* at this point, the player is allowed to move, but cannot move right now
10225 (e.g. because of something blocking the way) -- ensure that the player
10226 is also allowed to move in the next frame (in old versions before 3.1.1,
10227 the player was forced to wait again for eight frames before next try) */
10229 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10230 player->move_delay = 0; /* allow direct movement in the next frame */
10233 if (player->move_delay == -1) /* not yet initialized by DigField() */
10234 player->move_delay = player->move_delay_value;
10236 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10238 TestIfPlayerTouchesBadThing(jx, jy);
10239 TestIfPlayerTouchesCustomElement(jx, jy);
10242 if (!player->active)
10243 RemovePlayer(player);
10248 void ScrollPlayer(struct PlayerInfo *player, int mode)
10250 int jx = player->jx, jy = player->jy;
10251 int last_jx = player->last_jx, last_jy = player->last_jy;
10252 int move_stepsize = TILEX / player->move_delay_value;
10254 #if USE_NEW_PLAYER_SPEED
10255 if (!player->active)
10258 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10261 if (!player->active || player->MovPos == 0)
10265 if (mode == SCROLL_INIT)
10267 player->actual_frame_counter = FrameCounter;
10268 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10270 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10271 Feld[last_jx][last_jy] == EL_EMPTY)
10273 int last_field_block_delay = 0; /* start with no blocking at all */
10274 int block_delay_adjustment = player->block_delay_adjustment;
10276 /* if player blocks last field, add delay for exactly one move */
10277 if (player->block_last_field)
10279 last_field_block_delay += player->move_delay_value;
10281 /* when blocking enabled, prevent moving up despite gravity */
10282 #if USE_PLAYER_GRAVITY
10283 if (player->gravity && player->MovDir == MV_UP)
10284 block_delay_adjustment = -1;
10286 if (game.gravity && player->MovDir == MV_UP)
10287 block_delay_adjustment = -1;
10291 /* add block delay adjustment (also possible when not blocking) */
10292 last_field_block_delay += block_delay_adjustment;
10294 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10295 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10298 #if USE_NEW_PLAYER_SPEED
10299 if (player->MovPos != 0) /* player has not yet reached destination */
10305 else if (!FrameReached(&player->actual_frame_counter, 1))
10308 #if USE_NEW_PLAYER_SPEED
10309 if (player->MovPos != 0)
10311 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10312 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10314 /* before DrawPlayer() to draw correct player graphic for this case */
10315 if (player->MovPos == 0)
10316 CheckGravityMovement(player);
10319 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10320 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10322 /* before DrawPlayer() to draw correct player graphic for this case */
10323 if (player->MovPos == 0)
10324 CheckGravityMovement(player);
10327 if (player->MovPos == 0) /* player reached destination field */
10329 if (player->move_delay_reset_counter > 0)
10331 player->move_delay_reset_counter--;
10333 if (player->move_delay_reset_counter == 0)
10335 /* continue with normal speed after quickly moving through gate */
10336 HALVE_PLAYER_SPEED(player);
10338 /* be able to make the next move without delay */
10339 player->move_delay = 0;
10343 player->last_jx = jx;
10344 player->last_jy = jy;
10346 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10347 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10348 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10350 DrawPlayer(player); /* needed here only to cleanup last field */
10351 RemovePlayer(player);
10353 if (local_player->friends_still_needed == 0 ||
10354 IS_SP_ELEMENT(Feld[jx][jy]))
10355 PlayerWins(player);
10358 /* this breaks one level: "machine", level 000 */
10360 int move_direction = player->MovDir;
10361 int enter_side = MV_DIR_OPPOSITE(move_direction);
10362 int leave_side = move_direction;
10363 int old_jx = last_jx;
10364 int old_jy = last_jy;
10365 int old_element = Feld[old_jx][old_jy];
10366 int new_element = Feld[jx][jy];
10368 if (IS_CUSTOM_ELEMENT(old_element))
10369 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10371 player->index_bit, leave_side);
10373 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10374 CE_PLAYER_LEAVES_X,
10375 player->index_bit, leave_side);
10377 if (IS_CUSTOM_ELEMENT(new_element))
10378 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10379 player->index_bit, enter_side);
10381 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10382 CE_PLAYER_ENTERS_X,
10383 player->index_bit, enter_side);
10385 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10386 CE_MOVE_OF_X, move_direction);
10389 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10391 TestIfPlayerTouchesBadThing(jx, jy);
10392 TestIfPlayerTouchesCustomElement(jx, jy);
10394 /* needed because pushed element has not yet reached its destination,
10395 so it would trigger a change event at its previous field location */
10396 if (!player->is_pushing)
10397 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10399 if (!player->active)
10400 RemovePlayer(player);
10403 if (!local_player->LevelSolved && level.use_step_counter)
10413 if (TimeLeft <= 10 && setup.time_limit)
10414 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10416 DrawGameValue_Time(TimeLeft);
10418 if (!TimeLeft && setup.time_limit)
10419 for (i = 0; i < MAX_PLAYERS; i++)
10420 KillPlayer(&stored_player[i]);
10422 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10423 DrawGameValue_Time(TimePlayed);
10426 if (tape.single_step && tape.recording && !tape.pausing &&
10427 !player->programmed_action)
10428 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10432 void ScrollScreen(struct PlayerInfo *player, int mode)
10434 static unsigned long screen_frame_counter = 0;
10436 if (mode == SCROLL_INIT)
10438 /* set scrolling step size according to actual player's moving speed */
10439 ScrollStepSize = TILEX / player->move_delay_value;
10441 screen_frame_counter = FrameCounter;
10442 ScreenMovDir = player->MovDir;
10443 ScreenMovPos = player->MovPos;
10444 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10447 else if (!FrameReached(&screen_frame_counter, 1))
10452 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10453 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10454 redraw_mask |= REDRAW_FIELD;
10457 ScreenMovDir = MV_NONE;
10460 void TestIfPlayerTouchesCustomElement(int x, int y)
10462 static int xy[4][2] =
10469 static int trigger_sides[4][2] =
10471 /* center side border side */
10472 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10473 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10474 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10475 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10477 static int touch_dir[4] =
10479 MV_LEFT | MV_RIGHT,
10484 int center_element = Feld[x][y]; /* should always be non-moving! */
10487 for (i = 0; i < NUM_DIRECTIONS; i++)
10489 int xx = x + xy[i][0];
10490 int yy = y + xy[i][1];
10491 int center_side = trigger_sides[i][0];
10492 int border_side = trigger_sides[i][1];
10493 int border_element;
10495 if (!IN_LEV_FIELD(xx, yy))
10498 if (IS_PLAYER(x, y))
10500 struct PlayerInfo *player = PLAYERINFO(x, y);
10502 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10503 border_element = Feld[xx][yy]; /* may be moving! */
10504 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10505 border_element = Feld[xx][yy];
10506 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10507 border_element = MovingOrBlocked2Element(xx, yy);
10509 continue; /* center and border element do not touch */
10511 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10512 player->index_bit, border_side);
10513 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10514 CE_PLAYER_TOUCHES_X,
10515 player->index_bit, border_side);
10517 else if (IS_PLAYER(xx, yy))
10519 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10521 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10523 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10524 continue; /* center and border element do not touch */
10527 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10528 player->index_bit, center_side);
10529 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10530 CE_PLAYER_TOUCHES_X,
10531 player->index_bit, center_side);
10537 #if USE_ELEMENT_TOUCHING_BUGFIX
10539 void TestIfElementTouchesCustomElement(int x, int y)
10541 static int xy[4][2] =
10548 static int trigger_sides[4][2] =
10550 /* center side border side */
10551 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10552 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10553 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10554 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10556 static int touch_dir[4] =
10558 MV_LEFT | MV_RIGHT,
10563 boolean change_center_element = FALSE;
10564 int center_element = Feld[x][y]; /* should always be non-moving! */
10565 int border_element_old[NUM_DIRECTIONS];
10568 for (i = 0; i < NUM_DIRECTIONS; i++)
10570 int xx = x + xy[i][0];
10571 int yy = y + xy[i][1];
10572 int border_element;
10574 border_element_old[i] = -1;
10576 if (!IN_LEV_FIELD(xx, yy))
10579 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10580 border_element = Feld[xx][yy]; /* may be moving! */
10581 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10582 border_element = Feld[xx][yy];
10583 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10584 border_element = MovingOrBlocked2Element(xx, yy);
10586 continue; /* center and border element do not touch */
10588 border_element_old[i] = border_element;
10591 for (i = 0; i < NUM_DIRECTIONS; i++)
10593 int xx = x + xy[i][0];
10594 int yy = y + xy[i][1];
10595 int center_side = trigger_sides[i][0];
10596 int border_element = border_element_old[i];
10598 if (border_element == -1)
10601 /* check for change of border element */
10602 CheckElementChangeBySide(xx, yy, border_element, center_element,
10603 CE_TOUCHING_X, center_side);
10606 for (i = 0; i < NUM_DIRECTIONS; i++)
10608 int border_side = trigger_sides[i][1];
10609 int border_element = border_element_old[i];
10611 if (border_element == -1)
10614 /* check for change of center element (but change it only once) */
10615 if (!change_center_element)
10616 change_center_element =
10617 CheckElementChangeBySide(x, y, center_element, border_element,
10618 CE_TOUCHING_X, border_side);
10624 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10626 static int xy[4][2] =
10633 static int trigger_sides[4][2] =
10635 /* center side border side */
10636 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10637 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10638 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10639 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10641 static int touch_dir[4] =
10643 MV_LEFT | MV_RIGHT,
10648 boolean change_center_element = FALSE;
10649 int center_element = Feld[x][y]; /* should always be non-moving! */
10652 for (i = 0; i < NUM_DIRECTIONS; i++)
10654 int xx = x + xy[i][0];
10655 int yy = y + xy[i][1];
10656 int center_side = trigger_sides[i][0];
10657 int border_side = trigger_sides[i][1];
10658 int border_element;
10660 if (!IN_LEV_FIELD(xx, yy))
10663 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10664 border_element = Feld[xx][yy]; /* may be moving! */
10665 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10666 border_element = Feld[xx][yy];
10667 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10668 border_element = MovingOrBlocked2Element(xx, yy);
10670 continue; /* center and border element do not touch */
10672 /* check for change of center element (but change it only once) */
10673 if (!change_center_element)
10674 change_center_element =
10675 CheckElementChangeBySide(x, y, center_element, border_element,
10676 CE_TOUCHING_X, border_side);
10678 /* check for change of border element */
10679 CheckElementChangeBySide(xx, yy, border_element, center_element,
10680 CE_TOUCHING_X, center_side);
10686 void TestIfElementHitsCustomElement(int x, int y, int direction)
10688 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10689 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10690 int hitx = x + dx, hity = y + dy;
10691 int hitting_element = Feld[x][y];
10692 int touched_element;
10694 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10697 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10698 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10700 if (IN_LEV_FIELD(hitx, hity))
10702 int opposite_direction = MV_DIR_OPPOSITE(direction);
10703 int hitting_side = direction;
10704 int touched_side = opposite_direction;
10705 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10706 MovDir[hitx][hity] != direction ||
10707 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10713 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10714 CE_HITTING_X, touched_side);
10716 CheckElementChangeBySide(hitx, hity, touched_element,
10717 hitting_element, CE_HIT_BY_X, hitting_side);
10719 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10720 CE_HIT_BY_SOMETHING, opposite_direction);
10724 /* "hitting something" is also true when hitting the playfield border */
10725 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10726 CE_HITTING_SOMETHING, direction);
10730 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10732 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10733 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10734 int hitx = x + dx, hity = y + dy;
10735 int hitting_element = Feld[x][y];
10736 int touched_element;
10738 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10739 !IS_FREE(hitx, hity) &&
10740 (!IS_MOVING(hitx, hity) ||
10741 MovDir[hitx][hity] != direction ||
10742 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10745 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10749 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10753 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10754 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10756 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10757 EP_CAN_SMASH_EVERYTHING, direction);
10759 if (IN_LEV_FIELD(hitx, hity))
10761 int opposite_direction = MV_DIR_OPPOSITE(direction);
10762 int hitting_side = direction;
10763 int touched_side = opposite_direction;
10765 int touched_element = MovingOrBlocked2Element(hitx, hity);
10768 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10769 MovDir[hitx][hity] != direction ||
10770 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10779 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10780 CE_SMASHED_BY_SOMETHING, opposite_direction);
10782 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10783 CE_OTHER_IS_SMASHING, touched_side);
10785 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10786 CE_OTHER_GETS_SMASHED, hitting_side);
10792 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10794 int i, kill_x = -1, kill_y = -1;
10796 int bad_element = -1;
10797 static int test_xy[4][2] =
10804 static int test_dir[4] =
10812 for (i = 0; i < NUM_DIRECTIONS; i++)
10814 int test_x, test_y, test_move_dir, test_element;
10816 test_x = good_x + test_xy[i][0];
10817 test_y = good_y + test_xy[i][1];
10819 if (!IN_LEV_FIELD(test_x, test_y))
10823 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10825 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10827 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10828 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10830 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10831 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10835 bad_element = test_element;
10841 if (kill_x != -1 || kill_y != -1)
10843 if (IS_PLAYER(good_x, good_y))
10845 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10847 if (player->shield_deadly_time_left > 0 &&
10848 !IS_INDESTRUCTIBLE(bad_element))
10849 Bang(kill_x, kill_y);
10850 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10851 KillPlayer(player);
10854 Bang(good_x, good_y);
10858 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10860 int i, kill_x = -1, kill_y = -1;
10861 int bad_element = Feld[bad_x][bad_y];
10862 static int test_xy[4][2] =
10869 static int touch_dir[4] =
10871 MV_LEFT | MV_RIGHT,
10876 static int test_dir[4] =
10884 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10887 for (i = 0; i < NUM_DIRECTIONS; i++)
10889 int test_x, test_y, test_move_dir, test_element;
10891 test_x = bad_x + test_xy[i][0];
10892 test_y = bad_y + test_xy[i][1];
10893 if (!IN_LEV_FIELD(test_x, test_y))
10897 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10899 test_element = Feld[test_x][test_y];
10901 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10902 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10904 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10905 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10907 /* good thing is player or penguin that does not move away */
10908 if (IS_PLAYER(test_x, test_y))
10910 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10912 if (bad_element == EL_ROBOT && player->is_moving)
10913 continue; /* robot does not kill player if he is moving */
10915 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10917 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10918 continue; /* center and border element do not touch */
10925 else if (test_element == EL_PENGUIN)
10934 if (kill_x != -1 || kill_y != -1)
10936 if (IS_PLAYER(kill_x, kill_y))
10938 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10940 if (player->shield_deadly_time_left > 0 &&
10941 !IS_INDESTRUCTIBLE(bad_element))
10942 Bang(bad_x, bad_y);
10943 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10944 KillPlayer(player);
10947 Bang(kill_x, kill_y);
10951 void TestIfPlayerTouchesBadThing(int x, int y)
10953 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10956 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10958 TestIfGoodThingHitsBadThing(x, y, move_dir);
10961 void TestIfBadThingTouchesPlayer(int x, int y)
10963 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10966 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10968 TestIfBadThingHitsGoodThing(x, y, move_dir);
10971 void TestIfFriendTouchesBadThing(int x, int y)
10973 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10976 void TestIfBadThingTouchesFriend(int x, int y)
10978 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10981 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10983 int i, kill_x = bad_x, kill_y = bad_y;
10984 static int xy[4][2] =
10992 for (i = 0; i < NUM_DIRECTIONS; i++)
10996 x = bad_x + xy[i][0];
10997 y = bad_y + xy[i][1];
10998 if (!IN_LEV_FIELD(x, y))
11001 element = Feld[x][y];
11002 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11003 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11011 if (kill_x != bad_x || kill_y != bad_y)
11012 Bang(bad_x, bad_y);
11015 void KillPlayer(struct PlayerInfo *player)
11017 int jx = player->jx, jy = player->jy;
11019 if (!player->active)
11022 /* remove accessible field at the player's position */
11023 Feld[jx][jy] = EL_EMPTY;
11025 /* deactivate shield (else Bang()/Explode() would not work right) */
11026 player->shield_normal_time_left = 0;
11027 player->shield_deadly_time_left = 0;
11030 BuryPlayer(player);
11033 static void KillPlayerUnlessEnemyProtected(int x, int y)
11035 if (!PLAYER_ENEMY_PROTECTED(x, y))
11036 KillPlayer(PLAYERINFO(x, y));
11039 static void KillPlayerUnlessExplosionProtected(int x, int y)
11041 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11042 KillPlayer(PLAYERINFO(x, y));
11045 void BuryPlayer(struct PlayerInfo *player)
11047 int jx = player->jx, jy = player->jy;
11049 if (!player->active)
11052 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11053 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11055 player->GameOver = TRUE;
11056 RemovePlayer(player);
11059 void RemovePlayer(struct PlayerInfo *player)
11061 int jx = player->jx, jy = player->jy;
11062 int i, found = FALSE;
11064 player->present = FALSE;
11065 player->active = FALSE;
11067 if (!ExplodeField[jx][jy])
11068 StorePlayer[jx][jy] = 0;
11070 if (player->is_moving)
11071 DrawLevelField(player->last_jx, player->last_jy);
11073 for (i = 0; i < MAX_PLAYERS; i++)
11074 if (stored_player[i].active)
11078 AllPlayersGone = TRUE;
11084 #if USE_NEW_SNAP_DELAY
11085 static void setFieldForSnapping(int x, int y, int element, int direction)
11087 struct ElementInfo *ei = &element_info[element];
11088 int direction_bit = MV_DIR_TO_BIT(direction);
11089 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11090 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11091 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11093 Feld[x][y] = EL_ELEMENT_SNAPPING;
11094 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11096 ResetGfxAnimation(x, y);
11098 GfxElement[x][y] = element;
11099 GfxAction[x][y] = action;
11100 GfxDir[x][y] = direction;
11101 GfxFrame[x][y] = -1;
11106 =============================================================================
11107 checkDiagonalPushing()
11108 -----------------------------------------------------------------------------
11109 check if diagonal input device direction results in pushing of object
11110 (by checking if the alternative direction is walkable, diggable, ...)
11111 =============================================================================
11114 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11115 int x, int y, int real_dx, int real_dy)
11117 int jx, jy, dx, dy, xx, yy;
11119 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11122 /* diagonal direction: check alternative direction */
11127 xx = jx + (dx == 0 ? real_dx : 0);
11128 yy = jy + (dy == 0 ? real_dy : 0);
11130 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11134 =============================================================================
11136 -----------------------------------------------------------------------------
11137 x, y: field next to player (non-diagonal) to try to dig to
11138 real_dx, real_dy: direction as read from input device (can be diagonal)
11139 =============================================================================
11142 int DigField(struct PlayerInfo *player,
11143 int oldx, int oldy, int x, int y,
11144 int real_dx, int real_dy, int mode)
11146 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11147 boolean player_was_pushing = player->is_pushing;
11148 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11149 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11150 int jx = oldx, jy = oldy;
11151 int dx = x - jx, dy = y - jy;
11152 int nextx = x + dx, nexty = y + dy;
11153 int move_direction = (dx == -1 ? MV_LEFT :
11154 dx == +1 ? MV_RIGHT :
11156 dy == +1 ? MV_DOWN : MV_NONE);
11157 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11158 int dig_side = MV_DIR_OPPOSITE(move_direction);
11159 int old_element = Feld[jx][jy];
11160 #if USE_FIXED_DONT_RUN_INTO
11161 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11167 if (is_player) /* function can also be called by EL_PENGUIN */
11169 if (player->MovPos == 0)
11171 player->is_digging = FALSE;
11172 player->is_collecting = FALSE;
11175 if (player->MovPos == 0) /* last pushing move finished */
11176 player->is_pushing = FALSE;
11178 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11180 player->is_switching = FALSE;
11181 player->push_delay = -1;
11183 return MP_NO_ACTION;
11187 #if !USE_FIXED_DONT_RUN_INTO
11188 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11189 return MP_NO_ACTION;
11192 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11193 old_element = Back[jx][jy];
11195 /* in case of element dropped at player position, check background */
11196 else if (Back[jx][jy] != EL_EMPTY &&
11197 game.engine_version >= VERSION_IDENT(2,2,0,0))
11198 old_element = Back[jx][jy];
11200 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11201 return MP_NO_ACTION; /* field has no opening in this direction */
11203 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11204 return MP_NO_ACTION; /* field has no opening in this direction */
11206 #if USE_FIXED_DONT_RUN_INTO
11207 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11211 Feld[jx][jy] = player->artwork_element;
11212 InitMovingField(jx, jy, MV_DOWN);
11213 Store[jx][jy] = EL_ACID;
11214 ContinueMoving(jx, jy);
11215 BuryPlayer(player);
11217 return MP_DONT_RUN_INTO;
11221 #if USE_FIXED_DONT_RUN_INTO
11222 if (player_can_move && DONT_RUN_INTO(element))
11224 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11226 return MP_DONT_RUN_INTO;
11230 #if USE_FIXED_DONT_RUN_INTO
11231 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11232 return MP_NO_ACTION;
11235 #if !USE_FIXED_DONT_RUN_INTO
11236 element = Feld[x][y];
11239 collect_count = element_info[element].collect_count_initial;
11241 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11242 return MP_NO_ACTION;
11244 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11245 player_can_move = player_can_move_or_snap;
11247 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11248 game.engine_version >= VERSION_IDENT(2,2,0,0))
11250 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11251 player->index_bit, dig_side);
11252 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11253 player->index_bit, dig_side);
11255 if (Feld[x][y] != element) /* field changed by snapping */
11258 return MP_NO_ACTION;
11261 #if USE_PLAYER_GRAVITY
11262 if (player->gravity && is_player && !player->is_auto_moving &&
11263 canFallDown(player) && move_direction != MV_DOWN &&
11264 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11265 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11267 if (game.gravity && is_player && !player->is_auto_moving &&
11268 canFallDown(player) && move_direction != MV_DOWN &&
11269 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11270 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11273 if (player_can_move &&
11274 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11276 int sound_element = SND_ELEMENT(element);
11277 int sound_action = ACTION_WALKING;
11279 if (IS_RND_GATE(element))
11281 if (!player->key[RND_GATE_NR(element)])
11282 return MP_NO_ACTION;
11284 else if (IS_RND_GATE_GRAY(element))
11286 if (!player->key[RND_GATE_GRAY_NR(element)])
11287 return MP_NO_ACTION;
11289 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11291 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11292 return MP_NO_ACTION;
11294 else if (element == EL_EXIT_OPEN ||
11295 element == EL_SP_EXIT_OPEN ||
11296 element == EL_SP_EXIT_OPENING)
11298 sound_action = ACTION_PASSING; /* player is passing exit */
11300 else if (element == EL_EMPTY)
11302 sound_action = ACTION_MOVING; /* nothing to walk on */
11305 /* play sound from background or player, whatever is available */
11306 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11307 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11309 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11311 else if (player_can_move &&
11312 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11314 if (!ACCESS_FROM(element, opposite_direction))
11315 return MP_NO_ACTION; /* field not accessible from this direction */
11317 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11318 return MP_NO_ACTION;
11320 if (IS_EM_GATE(element))
11322 if (!player->key[EM_GATE_NR(element)])
11323 return MP_NO_ACTION;
11325 else if (IS_EM_GATE_GRAY(element))
11327 if (!player->key[EM_GATE_GRAY_NR(element)])
11328 return MP_NO_ACTION;
11330 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11332 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11333 return MP_NO_ACTION;
11335 else if (IS_EMC_GATE(element))
11337 if (!player->key[EMC_GATE_NR(element)])
11338 return MP_NO_ACTION;
11340 else if (IS_EMC_GATE_GRAY(element))
11342 if (!player->key[EMC_GATE_GRAY_NR(element)])
11343 return MP_NO_ACTION;
11345 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11347 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11348 return MP_NO_ACTION;
11350 else if (IS_SP_PORT(element))
11352 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11353 element == EL_SP_GRAVITY_PORT_RIGHT ||
11354 element == EL_SP_GRAVITY_PORT_UP ||
11355 element == EL_SP_GRAVITY_PORT_DOWN)
11356 #if USE_PLAYER_GRAVITY
11357 player->gravity = !player->gravity;
11359 game.gravity = !game.gravity;
11361 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11362 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11363 element == EL_SP_GRAVITY_ON_PORT_UP ||
11364 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11365 #if USE_PLAYER_GRAVITY
11366 player->gravity = TRUE;
11368 game.gravity = TRUE;
11370 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11371 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11372 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11373 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11374 #if USE_PLAYER_GRAVITY
11375 player->gravity = FALSE;
11377 game.gravity = FALSE;
11381 /* automatically move to the next field with double speed */
11382 player->programmed_action = move_direction;
11384 if (player->move_delay_reset_counter == 0)
11386 player->move_delay_reset_counter = 2; /* two double speed steps */
11388 DOUBLE_PLAYER_SPEED(player);
11391 PlayLevelSoundAction(x, y, ACTION_PASSING);
11393 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11397 if (mode != DF_SNAP)
11399 GfxElement[x][y] = GFX_ELEMENT(element);
11400 player->is_digging = TRUE;
11403 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11405 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11406 player->index_bit, dig_side);
11408 if (mode == DF_SNAP)
11410 #if USE_NEW_SNAP_DELAY
11411 if (level.block_snap_field)
11412 setFieldForSnapping(x, y, element, move_direction);
11414 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11416 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11419 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11420 player->index_bit, dig_side);
11423 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11427 if (is_player && mode != DF_SNAP)
11429 GfxElement[x][y] = element;
11430 player->is_collecting = TRUE;
11433 if (element == EL_SPEED_PILL)
11435 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11437 else if (element == EL_EXTRA_TIME && level.time > 0)
11439 TimeLeft += level.extra_time;
11440 DrawGameValue_Time(TimeLeft);
11442 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11444 player->shield_normal_time_left += level.shield_normal_time;
11445 if (element == EL_SHIELD_DEADLY)
11446 player->shield_deadly_time_left += level.shield_deadly_time;
11448 else if (element == EL_DYNAMITE ||
11449 element == EL_EM_DYNAMITE ||
11450 element == EL_SP_DISK_RED)
11452 if (player->inventory_size < MAX_INVENTORY_SIZE)
11453 player->inventory_element[player->inventory_size++] = element;
11455 DrawGameDoorValues();
11457 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11459 player->dynabomb_count++;
11460 player->dynabombs_left++;
11462 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11464 player->dynabomb_size++;
11466 else if (element == EL_DYNABOMB_INCREASE_POWER)
11468 player->dynabomb_xl = TRUE;
11470 else if (IS_KEY(element))
11472 player->key[KEY_NR(element)] = TRUE;
11474 DrawGameDoorValues();
11476 else if (IS_ENVELOPE(element))
11478 player->show_envelope = element;
11480 else if (element == EL_EMC_LENSES)
11482 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11484 RedrawAllInvisibleElementsForLenses();
11486 else if (element == EL_EMC_MAGNIFIER)
11488 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11490 RedrawAllInvisibleElementsForMagnifier();
11492 else if (IS_DROPPABLE(element) ||
11493 IS_THROWABLE(element)) /* can be collected and dropped */
11497 if (collect_count == 0)
11498 player->inventory_infinite_element = element;
11500 for (i = 0; i < collect_count; i++)
11501 if (player->inventory_size < MAX_INVENTORY_SIZE)
11502 player->inventory_element[player->inventory_size++] = element;
11504 DrawGameDoorValues();
11506 else if (collect_count > 0)
11508 local_player->gems_still_needed -= collect_count;
11509 if (local_player->gems_still_needed < 0)
11510 local_player->gems_still_needed = 0;
11512 DrawGameValue_Emeralds(local_player->gems_still_needed);
11515 RaiseScoreElement(element);
11516 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11519 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11520 player->index_bit, dig_side);
11522 if (mode == DF_SNAP)
11524 #if USE_NEW_SNAP_DELAY
11525 if (level.block_snap_field)
11526 setFieldForSnapping(x, y, element, move_direction);
11528 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11530 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11533 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11534 player->index_bit, dig_side);
11537 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11539 if (mode == DF_SNAP && element != EL_BD_ROCK)
11540 return MP_NO_ACTION;
11542 if (CAN_FALL(element) && dy)
11543 return MP_NO_ACTION;
11545 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11546 !(element == EL_SPRING && level.use_spring_bug))
11547 return MP_NO_ACTION;
11549 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11550 ((move_direction & MV_VERTICAL &&
11551 ((element_info[element].move_pattern & MV_LEFT &&
11552 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11553 (element_info[element].move_pattern & MV_RIGHT &&
11554 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11555 (move_direction & MV_HORIZONTAL &&
11556 ((element_info[element].move_pattern & MV_UP &&
11557 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11558 (element_info[element].move_pattern & MV_DOWN &&
11559 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11560 return MP_NO_ACTION;
11562 /* do not push elements already moving away faster than player */
11563 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11564 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11565 return MP_NO_ACTION;
11567 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11569 if (player->push_delay_value == -1 || !player_was_pushing)
11570 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11572 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11574 if (player->push_delay_value == -1)
11575 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11577 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11579 if (!player->is_pushing)
11580 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11583 player->is_pushing = TRUE;
11584 player->is_active = TRUE;
11586 if (!(IN_LEV_FIELD(nextx, nexty) &&
11587 (IS_FREE(nextx, nexty) ||
11588 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11589 IS_SB_ELEMENT(element)))))
11590 return MP_NO_ACTION;
11592 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11593 return MP_NO_ACTION;
11595 if (player->push_delay == -1) /* new pushing; restart delay */
11596 player->push_delay = 0;
11598 if (player->push_delay < player->push_delay_value &&
11599 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11600 element != EL_SPRING && element != EL_BALLOON)
11602 /* make sure that there is no move delay before next try to push */
11603 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11604 player->move_delay = 0;
11606 return MP_NO_ACTION;
11609 if (IS_SB_ELEMENT(element))
11611 if (element == EL_SOKOBAN_FIELD_FULL)
11613 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11614 local_player->sokobanfields_still_needed++;
11617 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11619 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11620 local_player->sokobanfields_still_needed--;
11623 Feld[x][y] = EL_SOKOBAN_OBJECT;
11625 if (Back[x][y] == Back[nextx][nexty])
11626 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11627 else if (Back[x][y] != 0)
11628 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11631 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11634 if (local_player->sokobanfields_still_needed == 0 &&
11635 game.emulation == EMU_SOKOBAN)
11637 PlayerWins(player);
11639 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11643 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11645 InitMovingField(x, y, move_direction);
11646 GfxAction[x][y] = ACTION_PUSHING;
11648 if (mode == DF_SNAP)
11649 ContinueMoving(x, y);
11651 MovPos[x][y] = (dx != 0 ? dx : dy);
11653 Pushed[x][y] = TRUE;
11654 Pushed[nextx][nexty] = TRUE;
11656 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11657 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11659 player->push_delay_value = -1; /* get new value later */
11661 /* check for element change _after_ element has been pushed */
11662 if (game.use_change_when_pushing_bug)
11664 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11665 player->index_bit, dig_side);
11666 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11667 player->index_bit, dig_side);
11670 else if (IS_SWITCHABLE(element))
11672 if (PLAYER_SWITCHING(player, x, y))
11674 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11675 player->index_bit, dig_side);
11680 player->is_switching = TRUE;
11681 player->switch_x = x;
11682 player->switch_y = y;
11684 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11686 if (element == EL_ROBOT_WHEEL)
11688 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11692 DrawLevelField(x, y);
11694 else if (element == EL_SP_TERMINAL)
11698 SCAN_PLAYFIELD(xx, yy)
11700 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11702 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11703 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11706 else if (IS_BELT_SWITCH(element))
11708 ToggleBeltSwitch(x, y);
11710 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11711 element == EL_SWITCHGATE_SWITCH_DOWN)
11713 ToggleSwitchgateSwitch(x, y);
11715 else if (element == EL_LIGHT_SWITCH ||
11716 element == EL_LIGHT_SWITCH_ACTIVE)
11718 ToggleLightSwitch(x, y);
11720 else if (element == EL_TIMEGATE_SWITCH)
11722 ActivateTimegateSwitch(x, y);
11724 else if (element == EL_BALLOON_SWITCH_LEFT ||
11725 element == EL_BALLOON_SWITCH_RIGHT ||
11726 element == EL_BALLOON_SWITCH_UP ||
11727 element == EL_BALLOON_SWITCH_DOWN ||
11728 element == EL_BALLOON_SWITCH_NONE ||
11729 element == EL_BALLOON_SWITCH_ANY)
11731 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11732 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11733 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11734 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11735 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11738 else if (element == EL_LAMP)
11740 Feld[x][y] = EL_LAMP_ACTIVE;
11741 local_player->lights_still_needed--;
11743 ResetGfxAnimation(x, y);
11744 DrawLevelField(x, y);
11746 else if (element == EL_TIME_ORB_FULL)
11748 Feld[x][y] = EL_TIME_ORB_EMPTY;
11750 if (level.time > 0 || level.use_time_orb_bug)
11752 TimeLeft += level.time_orb_time;
11753 DrawGameValue_Time(TimeLeft);
11756 ResetGfxAnimation(x, y);
11757 DrawLevelField(x, y);
11759 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11760 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11764 game.ball_state = !game.ball_state;
11766 SCAN_PLAYFIELD(xx, yy)
11768 int e = Feld[xx][yy];
11770 if (game.ball_state)
11772 if (e == EL_EMC_MAGIC_BALL)
11773 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11774 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11775 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11779 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11780 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11781 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11782 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11787 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11788 player->index_bit, dig_side);
11790 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11791 player->index_bit, dig_side);
11793 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11794 player->index_bit, dig_side);
11800 if (!PLAYER_SWITCHING(player, x, y))
11802 player->is_switching = TRUE;
11803 player->switch_x = x;
11804 player->switch_y = y;
11806 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11807 player->index_bit, dig_side);
11808 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11809 player->index_bit, dig_side);
11811 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11812 player->index_bit, dig_side);
11813 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11814 player->index_bit, dig_side);
11817 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11818 player->index_bit, dig_side);
11819 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11820 player->index_bit, dig_side);
11822 return MP_NO_ACTION;
11825 player->push_delay = -1;
11827 if (is_player) /* function can also be called by EL_PENGUIN */
11829 if (Feld[x][y] != element) /* really digged/collected something */
11831 player->is_collecting = !player->is_digging;
11832 player->is_active = TRUE;
11839 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11841 int jx = player->jx, jy = player->jy;
11842 int x = jx + dx, y = jy + dy;
11843 int snap_direction = (dx == -1 ? MV_LEFT :
11844 dx == +1 ? MV_RIGHT :
11846 dy == +1 ? MV_DOWN : MV_NONE);
11847 boolean can_continue_snapping = (level.continuous_snapping &&
11848 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
11850 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11853 if (!player->active || !IN_LEV_FIELD(x, y))
11861 if (player->MovPos == 0)
11862 player->is_pushing = FALSE;
11864 player->is_snapping = FALSE;
11866 if (player->MovPos == 0)
11868 player->is_moving = FALSE;
11869 player->is_digging = FALSE;
11870 player->is_collecting = FALSE;
11876 #if USE_NEW_CONTINUOUS_SNAPPING
11877 /* prevent snapping with already pressed snap key when not allowed */
11878 if (player->is_snapping && !can_continue_snapping)
11881 if (player->is_snapping)
11885 player->MovDir = snap_direction;
11887 if (player->MovPos == 0)
11889 player->is_moving = FALSE;
11890 player->is_digging = FALSE;
11891 player->is_collecting = FALSE;
11894 player->is_dropping = FALSE;
11895 player->is_dropping_pressed = FALSE;
11896 player->drop_pressed_delay = 0;
11898 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11901 player->is_snapping = TRUE;
11902 player->is_active = TRUE;
11904 if (player->MovPos == 0)
11906 player->is_moving = FALSE;
11907 player->is_digging = FALSE;
11908 player->is_collecting = FALSE;
11911 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11912 DrawLevelField(player->last_jx, player->last_jy);
11914 DrawLevelField(x, y);
11919 boolean DropElement(struct PlayerInfo *player)
11921 int old_element, new_element;
11922 int dropx = player->jx, dropy = player->jy;
11923 int drop_direction = player->MovDir;
11924 int drop_side = drop_direction;
11925 int drop_element = (player->inventory_size > 0 ?
11926 player->inventory_element[player->inventory_size - 1] :
11927 player->inventory_infinite_element != EL_UNDEFINED ?
11928 player->inventory_infinite_element :
11929 player->dynabombs_left > 0 ?
11930 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11933 player->is_dropping_pressed = TRUE;
11935 /* do not drop an element on top of another element; when holding drop key
11936 pressed without moving, dropped element must move away before the next
11937 element can be dropped (this is especially important if the next element
11938 is dynamite, which can be placed on background for historical reasons) */
11939 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11942 if (IS_THROWABLE(drop_element))
11944 dropx += GET_DX_FROM_DIR(drop_direction);
11945 dropy += GET_DY_FROM_DIR(drop_direction);
11947 if (!IN_LEV_FIELD(dropx, dropy))
11951 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11952 new_element = drop_element; /* default: no change when dropping */
11954 /* check if player is active, not moving and ready to drop */
11955 if (!player->active || player->MovPos || player->drop_delay > 0)
11958 /* check if player has anything that can be dropped */
11959 if (new_element == EL_UNDEFINED)
11962 /* check if drop key was pressed long enough for EM style dynamite */
11963 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
11966 /* check if anything can be dropped at the current position */
11967 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11970 /* collected custom elements can only be dropped on empty fields */
11971 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11974 if (old_element != EL_EMPTY)
11975 Back[dropx][dropy] = old_element; /* store old element on this field */
11977 ResetGfxAnimation(dropx, dropy);
11978 ResetRandomAnimationValue(dropx, dropy);
11980 if (player->inventory_size > 0 ||
11981 player->inventory_infinite_element != EL_UNDEFINED)
11983 if (player->inventory_size > 0)
11985 player->inventory_size--;
11987 DrawGameDoorValues();
11989 if (new_element == EL_DYNAMITE)
11990 new_element = EL_DYNAMITE_ACTIVE;
11991 else if (new_element == EL_EM_DYNAMITE)
11992 new_element = EL_EM_DYNAMITE_ACTIVE;
11993 else if (new_element == EL_SP_DISK_RED)
11994 new_element = EL_SP_DISK_RED_ACTIVE;
11997 Feld[dropx][dropy] = new_element;
11999 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12000 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12001 el2img(Feld[dropx][dropy]), 0);
12003 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12005 /* needed if previous element just changed to "empty" in the last frame */
12006 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12008 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12009 player->index_bit, drop_side);
12010 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12012 player->index_bit, drop_side);
12014 TestIfElementTouchesCustomElement(dropx, dropy);
12016 else /* player is dropping a dyna bomb */
12018 player->dynabombs_left--;
12020 Feld[dropx][dropy] = new_element;
12022 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12023 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12024 el2img(Feld[dropx][dropy]), 0);
12026 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12029 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12030 InitField_WithBug1(dropx, dropy, FALSE);
12032 new_element = Feld[dropx][dropy]; /* element might have changed */
12034 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12035 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12037 int move_direction, nextx, nexty;
12039 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12040 MovDir[dropx][dropy] = drop_direction;
12042 move_direction = MovDir[dropx][dropy];
12043 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12044 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12046 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12047 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12050 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12051 player->is_dropping = TRUE;
12053 player->drop_pressed_delay = 0;
12054 player->is_dropping_pressed = FALSE;
12056 player->drop_x = dropx;
12057 player->drop_y = dropy;
12062 /* ------------------------------------------------------------------------- */
12063 /* game sound playing functions */
12064 /* ------------------------------------------------------------------------- */
12066 static int *loop_sound_frame = NULL;
12067 static int *loop_sound_volume = NULL;
12069 void InitPlayLevelSound()
12071 int num_sounds = getSoundListSize();
12073 checked_free(loop_sound_frame);
12074 checked_free(loop_sound_volume);
12076 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12077 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12080 static void PlayLevelSound(int x, int y, int nr)
12082 int sx = SCREENX(x), sy = SCREENY(y);
12083 int volume, stereo_position;
12084 int max_distance = 8;
12085 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12087 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12088 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12091 if (!IN_LEV_FIELD(x, y) ||
12092 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12093 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12096 volume = SOUND_MAX_VOLUME;
12098 if (!IN_SCR_FIELD(sx, sy))
12100 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12101 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12103 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12106 stereo_position = (SOUND_MAX_LEFT +
12107 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12108 (SCR_FIELDX + 2 * max_distance));
12110 if (IS_LOOP_SOUND(nr))
12112 /* This assures that quieter loop sounds do not overwrite louder ones,
12113 while restarting sound volume comparison with each new game frame. */
12115 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12118 loop_sound_volume[nr] = volume;
12119 loop_sound_frame[nr] = FrameCounter;
12122 PlaySoundExt(nr, volume, stereo_position, type);
12125 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12127 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12128 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12129 y < LEVELY(BY1) ? LEVELY(BY1) :
12130 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12134 static void PlayLevelSoundAction(int x, int y, int action)
12136 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12139 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12141 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12143 if (sound_effect != SND_UNDEFINED)
12144 PlayLevelSound(x, y, sound_effect);
12147 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12150 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12152 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12153 PlayLevelSound(x, y, sound_effect);
12156 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12158 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12160 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12161 PlayLevelSound(x, y, sound_effect);
12164 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12166 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12168 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12169 StopSound(sound_effect);
12172 static void PlayLevelMusic()
12174 if (levelset.music[level_nr] != MUS_UNDEFINED)
12175 PlayMusic(levelset.music[level_nr]); /* from config file */
12177 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12180 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
12182 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12183 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
12184 int x = xx - 1 - offset;
12185 int y = yy - 1 - offset;
12190 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12194 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12198 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12202 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12206 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12210 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12214 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12217 case SAMPLE_android_clone:
12218 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12221 case SAMPLE_android_move:
12222 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12225 case SAMPLE_spring:
12226 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12230 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12234 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12237 case SAMPLE_eater_eat:
12238 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12242 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12245 case SAMPLE_collect:
12246 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12249 case SAMPLE_diamond:
12250 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12253 case SAMPLE_squash:
12254 /* !!! CHECK THIS !!! */
12256 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12258 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12262 case SAMPLE_wonderfall:
12263 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12267 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12271 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12275 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12279 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12283 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12287 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12290 case SAMPLE_wonder:
12291 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12295 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12298 case SAMPLE_exit_open:
12299 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12302 case SAMPLE_exit_leave:
12303 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12306 case SAMPLE_dynamite:
12307 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12311 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12315 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12319 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12323 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12327 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12331 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12335 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12341 void ChangeTime(int value)
12343 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
12347 /* EMC game engine uses value from time counter of RND game engine */
12348 level.native_em_level->lev->time = *time;
12350 DrawGameValue_Time(*time);
12353 void RaiseScore(int value)
12355 /* EMC game engine and RND game engine have separate score counters */
12356 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
12357 &level.native_em_level->lev->score : &local_player->score);
12361 DrawGameValue_Score(*score);
12365 void RaiseScore(int value)
12367 local_player->score += value;
12369 DrawGameValue_Score(local_player->score);
12372 void RaiseScoreElement(int element)
12377 case EL_BD_DIAMOND:
12378 case EL_EMERALD_YELLOW:
12379 case EL_EMERALD_RED:
12380 case EL_EMERALD_PURPLE:
12381 case EL_SP_INFOTRON:
12382 RaiseScore(level.score[SC_EMERALD]);
12385 RaiseScore(level.score[SC_DIAMOND]);
12388 RaiseScore(level.score[SC_CRYSTAL]);
12391 RaiseScore(level.score[SC_PEARL]);
12394 case EL_BD_BUTTERFLY:
12395 case EL_SP_ELECTRON:
12396 RaiseScore(level.score[SC_BUG]);
12399 case EL_BD_FIREFLY:
12400 case EL_SP_SNIKSNAK:
12401 RaiseScore(level.score[SC_SPACESHIP]);
12404 case EL_DARK_YAMYAM:
12405 RaiseScore(level.score[SC_YAMYAM]);
12408 RaiseScore(level.score[SC_ROBOT]);
12411 RaiseScore(level.score[SC_PACMAN]);
12414 RaiseScore(level.score[SC_NUT]);
12417 case EL_EM_DYNAMITE:
12418 case EL_SP_DISK_RED:
12419 case EL_DYNABOMB_INCREASE_NUMBER:
12420 case EL_DYNABOMB_INCREASE_SIZE:
12421 case EL_DYNABOMB_INCREASE_POWER:
12422 RaiseScore(level.score[SC_DYNAMITE]);
12424 case EL_SHIELD_NORMAL:
12425 case EL_SHIELD_DEADLY:
12426 RaiseScore(level.score[SC_SHIELD]);
12428 case EL_EXTRA_TIME:
12429 RaiseScore(level.extra_time_score);
12443 RaiseScore(level.score[SC_KEY]);
12446 RaiseScore(element_info[element].collect_score);
12451 void RequestQuitGame(boolean ask_if_really_quit)
12453 if (AllPlayersGone ||
12454 !ask_if_really_quit ||
12455 level_editor_test_game ||
12456 Request("Do you really want to quit the game ?",
12457 REQ_ASK | REQ_STAY_CLOSED))
12459 #if defined(NETWORK_AVALIABLE)
12460 if (options.network)
12461 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12465 if (!ask_if_really_quit || level_editor_test_game)
12467 game_status = GAME_MODE_MAIN;
12473 FadeOut(REDRAW_FIELD);
12475 game_status = GAME_MODE_MAIN;
12477 DrawAndFadeInMainMenu(REDRAW_FIELD);
12483 if (tape.playing && tape.deactivate_display)
12484 TapeDeactivateDisplayOff(TRUE);
12486 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12488 if (tape.playing && tape.deactivate_display)
12489 TapeDeactivateDisplayOn();
12494 /* ------------------------------------------------------------------------- */
12495 /* random generator functions */
12496 /* ------------------------------------------------------------------------- */
12498 unsigned int InitEngineRandom_RND(long seed)
12500 game.num_random_calls = 0;
12503 unsigned int rnd_seed = InitEngineRandom(seed);
12505 printf("::: START RND: %d\n", rnd_seed);
12510 return InitEngineRandom(seed);
12516 unsigned int RND(int max)
12520 game.num_random_calls++;
12522 return GetEngineRandom(max);
12529 /* ------------------------------------------------------------------------- */
12530 /* game engine snapshot handling functions */
12531 /* ------------------------------------------------------------------------- */
12533 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
12535 struct EngineSnapshotInfo
12537 /* runtime values for custom element collect score */
12538 int collect_score[NUM_CUSTOM_ELEMENTS];
12540 /* runtime values for group element choice position */
12541 int choice_pos[NUM_GROUP_ELEMENTS];
12543 /* runtime values for belt position animations */
12544 int belt_graphic[4 * NUM_BELT_PARTS];
12545 int belt_anim_mode[4 * NUM_BELT_PARTS];
12548 struct EngineSnapshotNodeInfo
12555 static struct EngineSnapshotInfo engine_snapshot_rnd;
12556 static ListNode *engine_snapshot_list = NULL;
12557 static char *snapshot_level_identifier = NULL;
12558 static int snapshot_level_nr = -1;
12560 void FreeEngineSnapshot()
12562 while (engine_snapshot_list != NULL)
12563 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
12566 setString(&snapshot_level_identifier, NULL);
12567 snapshot_level_nr = -1;
12570 static void SaveEngineSnapshotValues_RND()
12572 static int belt_base_active_element[4] =
12574 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
12575 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
12576 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
12577 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
12581 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12583 int element = EL_CUSTOM_START + i;
12585 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
12588 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12590 int element = EL_GROUP_START + i;
12592 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
12595 for (i = 0; i < 4; i++)
12597 for (j = 0; j < NUM_BELT_PARTS; j++)
12599 int element = belt_base_active_element[i] + j;
12600 int graphic = el2img(element);
12601 int anim_mode = graphic_info[graphic].anim_mode;
12603 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
12604 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
12609 static void LoadEngineSnapshotValues_RND()
12611 unsigned long num_random_calls = game.num_random_calls;
12614 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12616 int element = EL_CUSTOM_START + i;
12618 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
12621 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12623 int element = EL_GROUP_START + i;
12625 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
12628 for (i = 0; i < 4; i++)
12630 for (j = 0; j < NUM_BELT_PARTS; j++)
12632 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
12633 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
12635 graphic_info[graphic].anim_mode = anim_mode;
12639 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
12641 InitRND(tape.random_seed);
12642 for (i = 0; i < num_random_calls; i++)
12646 if (game.num_random_calls != num_random_calls)
12648 Error(ERR_RETURN, "number of random calls out of sync");
12649 Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
12650 Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
12651 Error(ERR_EXIT, "this should not happen -- please debug");
12655 static void SaveEngineSnapshotBuffer(void *buffer, int size)
12657 struct EngineSnapshotNodeInfo *bi =
12658 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
12660 bi->buffer_orig = buffer;
12661 bi->buffer_copy = checked_malloc(size);
12664 memcpy(bi->buffer_copy, buffer, size);
12666 addNodeToList(&engine_snapshot_list, NULL, bi);
12669 void SaveEngineSnapshot()
12671 FreeEngineSnapshot(); /* free previous snapshot, if needed */
12673 /* copy some special values to a structure better suited for the snapshot */
12675 SaveEngineSnapshotValues_RND();
12676 SaveEngineSnapshotValues_EM();
12678 /* save values stored in special snapshot structure */
12680 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
12681 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
12683 /* save further RND engine values */
12685 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
12686 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
12687 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
12689 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
12690 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
12691 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
12692 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
12694 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
12695 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
12696 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
12697 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
12698 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
12700 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
12701 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
12702 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
12704 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
12706 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
12708 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
12709 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
12711 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
12712 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
12713 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
12714 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
12715 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
12716 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
12717 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
12718 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
12719 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
12720 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
12721 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
12722 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
12723 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
12724 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
12725 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
12726 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
12727 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
12729 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
12730 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
12732 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
12733 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
12734 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
12736 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
12737 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
12739 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
12740 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
12741 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
12742 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
12743 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
12745 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
12746 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
12748 /* save level identification information */
12750 setString(&snapshot_level_identifier, leveldir_current->identifier);
12751 snapshot_level_nr = level_nr;
12754 ListNode *node = engine_snapshot_list;
12757 while (node != NULL)
12759 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
12764 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
12768 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
12770 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
12773 void LoadEngineSnapshot()
12775 ListNode *node = engine_snapshot_list;
12777 if (engine_snapshot_list == NULL)
12780 while (node != NULL)
12782 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
12787 /* restore special values from snapshot structure */
12789 LoadEngineSnapshotValues_RND();
12790 LoadEngineSnapshotValues_EM();
12793 boolean CheckEngineSnapshot()
12795 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
12796 snapshot_level_nr == level_nr);
12800 /* ---------- new game button stuff ---------------------------------------- */
12802 /* graphic position values for game buttons */
12803 #define GAME_BUTTON_XSIZE 30
12804 #define GAME_BUTTON_YSIZE 30
12805 #define GAME_BUTTON_XPOS 5
12806 #define GAME_BUTTON_YPOS 215
12807 #define SOUND_BUTTON_XPOS 5
12808 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12810 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12811 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12812 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12813 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12814 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12815 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12822 } gamebutton_info[NUM_GAME_BUTTONS] =
12825 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12830 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12831 GAME_CTRL_ID_PAUSE,
12835 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12840 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12841 SOUND_CTRL_ID_MUSIC,
12842 "background music on/off"
12845 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12846 SOUND_CTRL_ID_LOOPS,
12847 "sound loops on/off"
12850 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12851 SOUND_CTRL_ID_SIMPLE,
12852 "normal sounds on/off"
12856 void CreateGameButtons()
12860 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12862 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12863 struct GadgetInfo *gi;
12866 unsigned long event_mask;
12867 int gd_xoffset, gd_yoffset;
12868 int gd_x1, gd_x2, gd_y1, gd_y2;
12871 gd_xoffset = gamebutton_info[i].x;
12872 gd_yoffset = gamebutton_info[i].y;
12873 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12874 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12876 if (id == GAME_CTRL_ID_STOP ||
12877 id == GAME_CTRL_ID_PAUSE ||
12878 id == GAME_CTRL_ID_PLAY)
12880 button_type = GD_TYPE_NORMAL_BUTTON;
12882 event_mask = GD_EVENT_RELEASED;
12883 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12884 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12888 button_type = GD_TYPE_CHECK_BUTTON;
12890 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12891 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12892 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12893 event_mask = GD_EVENT_PRESSED;
12894 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12895 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12898 gi = CreateGadget(GDI_CUSTOM_ID, id,
12899 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12900 GDI_X, DX + gd_xoffset,
12901 GDI_Y, DY + gd_yoffset,
12902 GDI_WIDTH, GAME_BUTTON_XSIZE,
12903 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12904 GDI_TYPE, button_type,
12905 GDI_STATE, GD_BUTTON_UNPRESSED,
12906 GDI_CHECKED, checked,
12907 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12908 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12909 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12910 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12911 GDI_EVENT_MASK, event_mask,
12912 GDI_CALLBACK_ACTION, HandleGameButtons,
12916 Error(ERR_EXIT, "cannot create gadget");
12918 game_gadget[id] = gi;
12922 void FreeGameButtons()
12926 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12927 FreeGadget(game_gadget[i]);
12930 static void MapGameButtons()
12934 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12935 MapGadget(game_gadget[i]);
12938 void UnmapGameButtons()
12942 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12943 UnmapGadget(game_gadget[i]);
12946 static void HandleGameButtons(struct GadgetInfo *gi)
12948 int id = gi->custom_id;
12950 if (game_status != GAME_MODE_PLAYING)
12955 case GAME_CTRL_ID_STOP:
12959 RequestQuitGame(TRUE);
12962 case GAME_CTRL_ID_PAUSE:
12963 if (options.network)
12965 #if defined(NETWORK_AVALIABLE)
12967 SendToServer_ContinuePlaying();
12969 SendToServer_PausePlaying();
12973 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12976 case GAME_CTRL_ID_PLAY:
12979 #if defined(NETWORK_AVALIABLE)
12980 if (options.network)
12981 SendToServer_ContinuePlaying();
12985 tape.pausing = FALSE;
12986 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
12991 case SOUND_CTRL_ID_MUSIC:
12992 if (setup.sound_music)
12994 setup.sound_music = FALSE;
12997 else if (audio.music_available)
12999 setup.sound = setup.sound_music = TRUE;
13001 SetAudioMode(setup.sound);
13007 case SOUND_CTRL_ID_LOOPS:
13008 if (setup.sound_loops)
13009 setup.sound_loops = FALSE;
13010 else if (audio.loops_available)
13012 setup.sound = setup.sound_loops = TRUE;
13013 SetAudioMode(setup.sound);
13017 case SOUND_CTRL_ID_SIMPLE:
13018 if (setup.sound_simple)
13019 setup.sound_simple = FALSE;
13020 else if (audio.sound_available)
13022 setup.sound = setup.sound_simple = TRUE;
13023 SetAudioMode(setup.sound);