1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
65 /* for MovePlayer() */
66 #define MP_NO_ACTION 0
69 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
71 /* for ScrollPlayer() */
73 #define SCROLL_GO_ON 1
75 /* for Bang()/Explode() */
76 #define EX_PHASE_START 0
77 #define EX_TYPE_NONE 0
78 #define EX_TYPE_NORMAL (1 << 0)
79 #define EX_TYPE_CENTER (1 << 1)
80 #define EX_TYPE_BORDER (1 << 2)
81 #define EX_TYPE_CROSS (1 << 3)
82 #define EX_TYPE_DYNA (1 << 4)
83 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
85 #define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0)
87 /* special positions in the game control window (relative to control window) */
88 #define XX_LEVEL1 (game.panel.level.x)
89 #define XX_LEVEL2 (game.panel.level.x - 1)
90 #define YY_LEVEL (game.panel.level.y)
91 #define XX_EMERALDS (game.panel.gems.x)
92 #define YY_EMERALDS (game.panel.gems.y)
93 #define XX_DYNAMITE (game.panel.inventory.x)
94 #define YY_DYNAMITE (game.panel.inventory.y)
95 #define XX_KEYS (game.panel.keys.x)
96 #define YY_KEYS (game.panel.keys.y)
97 #define XX_SCORE (game.panel.score.x)
98 #define YY_SCORE (game.panel.score.y)
99 #define XX_TIME1 (game.panel.time.x)
100 #define XX_TIME2 (game.panel.time.x + 1)
101 #define YY_TIME (game.panel.time.y)
103 /* special positions in the game control window (relative to main window) */
104 #define DX_LEVEL1 (DX + XX_LEVEL1)
105 #define DX_LEVEL2 (DX + XX_LEVEL2)
106 #define DY_LEVEL (DY + YY_LEVEL)
107 #define DX_EMERALDS (DX + XX_EMERALDS)
108 #define DY_EMERALDS (DY + YY_EMERALDS)
109 #define DX_DYNAMITE (DX + XX_DYNAMITE)
110 #define DY_DYNAMITE (DY + YY_DYNAMITE)
111 #define DX_KEYS (DX + XX_KEYS)
112 #define DY_KEYS (DY + YY_KEYS)
113 #define DX_SCORE (DX + XX_SCORE)
114 #define DY_SCORE (DY + YY_SCORE)
115 #define DX_TIME1 (DX + XX_TIME1)
116 #define DX_TIME2 (DX + XX_TIME2)
117 #define DY_TIME (DY + YY_TIME)
119 /* values for delayed check of falling and moving elements and for collision */
120 #define CHECK_DELAY_MOVING 3
121 #define CHECK_DELAY_FALLING 3
122 #define CHECK_DELAY_COLLISION 2
124 /* values for initial player move delay (initial delay counter value) */
125 #define INITIAL_MOVE_DELAY_OFF -1
126 #define INITIAL_MOVE_DELAY_ON 0
128 /* values for player movement speed (which is in fact a delay value) */
129 #define MOVE_DELAY_MIN_SPEED 32
130 #define MOVE_DELAY_NORMAL_SPEED 8
131 #define MOVE_DELAY_HIGH_SPEED 4
132 #define MOVE_DELAY_MAX_SPEED 1
134 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
135 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
137 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
138 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
140 /* values for other actions */
141 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
142 #define MOVE_STEPSIZE_MIN (1)
143 #define MOVE_STEPSIZE_MAX (TILEX)
145 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
146 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
148 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
150 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
151 RND(element_info[e].push_delay_random))
152 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
153 RND(element_info[e].drop_delay_random))
154 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
155 RND(element_info[e].move_delay_random))
156 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
157 (element_info[e].move_delay_random))
158 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
159 RND(element_info[e].ce_value_random_initial))
160 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
161 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
162 RND((c)->delay_random * (c)->delay_frames))
163 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
164 RND((c)->delay_random))
167 #define GET_VALID_RUNTIME_ELEMENT(e) \
168 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
170 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
171 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
172 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
173 (be) + (e) - EL_SELF)
175 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
176 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
177 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
178 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
179 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
180 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
181 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
182 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
183 RESOLVED_REFERENCE_ELEMENT(be, e) : \
186 #define CAN_GROW_INTO(e) \
187 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
189 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
190 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
193 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
194 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
195 (CAN_MOVE_INTO_ACID(e) && \
196 Feld[x][y] == EL_ACID) || \
199 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
200 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
201 (CAN_MOVE_INTO_ACID(e) && \
202 Feld[x][y] == EL_ACID) || \
205 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
206 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
208 (CAN_MOVE_INTO_ACID(e) && \
209 Feld[x][y] == EL_ACID) || \
210 (DONT_COLLIDE_WITH(e) && \
212 !PLAYER_ENEMY_PROTECTED(x, y))))
214 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
215 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
217 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
218 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
220 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
221 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
223 #define ANDROID_CAN_CLONE_FIELD(x, y) \
224 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
225 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
227 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
228 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
230 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
231 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
233 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
234 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
236 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
237 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
239 #define PIG_CAN_ENTER_FIELD(e, x, y) \
240 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
242 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
243 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
244 IS_FOOD_PENGUIN(Feld[x][y])))
245 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
246 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
248 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
249 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
251 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
252 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
254 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
255 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
256 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
258 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
260 #define CE_ENTER_FIELD_COND(e, x, y) \
261 (!IS_PLAYER(x, y) && \
262 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
264 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
265 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
267 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
268 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
270 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
271 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
272 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
273 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
275 /* game button identifiers */
276 #define GAME_CTRL_ID_STOP 0
277 #define GAME_CTRL_ID_PAUSE 1
278 #define GAME_CTRL_ID_PLAY 2
279 #define SOUND_CTRL_ID_MUSIC 3
280 #define SOUND_CTRL_ID_LOOPS 4
281 #define SOUND_CTRL_ID_SIMPLE 5
283 #define NUM_GAME_BUTTONS 6
286 /* forward declaration for internal use */
288 static void CreateField(int, int, int);
290 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
291 static void AdvanceFrameAndPlayerCounters(int);
293 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
294 static boolean MovePlayer(struct PlayerInfo *, int, int);
295 static void ScrollPlayer(struct PlayerInfo *, int);
296 static void ScrollScreen(struct PlayerInfo *, int);
298 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
300 static void InitBeltMovement(void);
301 static void CloseAllOpenTimegates(void);
302 static void CheckGravityMovement(struct PlayerInfo *);
303 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
304 static void KillPlayerUnlessEnemyProtected(int, int);
305 static void KillPlayerUnlessExplosionProtected(int, int);
307 static void TestIfPlayerTouchesCustomElement(int, int);
308 static void TestIfElementTouchesCustomElement(int, int);
309 static void TestIfElementHitsCustomElement(int, int, int);
311 static void TestIfElementSmashesCustomElement(int, int, int);
314 static void HandleElementChange(int, int, int);
315 static void ExecuteCustomElementAction(int, int, int, int);
316 static boolean ChangeElement(int, int, int, int);
318 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
319 #define CheckTriggeredElementChange(x, y, e, ev) \
320 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
321 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
322 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
323 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
324 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
325 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
326 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
328 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
329 #define CheckElementChange(x, y, e, te, ev) \
330 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
331 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
332 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
333 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
334 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
336 static void PlayLevelSound(int, int, int);
337 static void PlayLevelSoundNearest(int, int, int);
338 static void PlayLevelSoundAction(int, int, int);
339 static void PlayLevelSoundElementAction(int, int, int, int);
340 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
341 static void PlayLevelSoundActionIfLoop(int, int, int);
342 static void StopLevelSoundActionIfLoop(int, int, int);
343 static void PlayLevelMusic();
345 static void MapGameButtons();
346 static void HandleGameButtons(struct GadgetInfo *);
348 int AmoebeNachbarNr(int, int);
349 void AmoebeUmwandeln(int, int);
350 void ContinueMoving(int, int);
352 void InitMovDir(int, int);
353 void InitAmoebaNr(int, int);
354 int NewHiScore(void);
356 void TestIfGoodThingHitsBadThing(int, int, int);
357 void TestIfBadThingHitsGoodThing(int, int, int);
358 void TestIfPlayerTouchesBadThing(int, int);
359 void TestIfPlayerRunsIntoBadThing(int, int, int);
360 void TestIfBadThingTouchesPlayer(int, int);
361 void TestIfBadThingRunsIntoPlayer(int, int, int);
362 void TestIfFriendTouchesBadThing(int, int);
363 void TestIfBadThingTouchesFriend(int, int);
364 void TestIfBadThingTouchesOtherBadThing(int, int);
366 void KillPlayer(struct PlayerInfo *);
367 void BuryPlayer(struct PlayerInfo *);
368 void RemovePlayer(struct PlayerInfo *);
370 boolean SnapField(struct PlayerInfo *, int, int);
371 boolean DropElement(struct PlayerInfo *);
373 static int getInvisibleActiveFromInvisibleElement(int);
374 static int getInvisibleFromInvisibleActiveElement(int);
376 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
379 /* ------------------------------------------------------------------------- */
380 /* definition of elements that automatically change to other elements after */
381 /* a specified time, eventually calling a function when changing */
382 /* ------------------------------------------------------------------------- */
384 /* forward declaration for changer functions */
385 static void InitBuggyBase(int, int);
386 static void WarnBuggyBase(int, int);
388 static void InitTrap(int, int);
389 static void ActivateTrap(int, int);
390 static void ChangeActiveTrap(int, int);
392 static void InitRobotWheel(int, int);
393 static void RunRobotWheel(int, int);
394 static void StopRobotWheel(int, int);
396 static void InitTimegateWheel(int, int);
397 static void RunTimegateWheel(int, int);
399 static void InitMagicBallDelay(int, int);
400 static void ActivateMagicBall(int, int);
402 struct ChangingElementInfo
407 void (*pre_change_function)(int x, int y);
408 void (*change_function)(int x, int y);
409 void (*post_change_function)(int x, int y);
412 static struct ChangingElementInfo change_delay_list[] =
463 EL_SWITCHGATE_OPENING,
471 EL_SWITCHGATE_CLOSING,
472 EL_SWITCHGATE_CLOSED,
504 EL_ACID_SPLASH_RIGHT,
513 EL_SP_BUGGY_BASE_ACTIVATING,
520 EL_SP_BUGGY_BASE_ACTIVATING,
521 EL_SP_BUGGY_BASE_ACTIVE,
528 EL_SP_BUGGY_BASE_ACTIVE,
552 EL_ROBOT_WHEEL_ACTIVE,
560 EL_TIMEGATE_SWITCH_ACTIVE,
568 EL_EMC_MAGIC_BALL_ACTIVE,
569 EL_EMC_MAGIC_BALL_ACTIVE,
576 EL_EMC_SPRING_BUMPER_ACTIVE,
577 EL_EMC_SPRING_BUMPER,
584 EL_DIAGONAL_SHRINKING,
613 int push_delay_fixed, push_delay_random;
618 { EL_BALLOON, 0, 0 },
620 { EL_SOKOBAN_OBJECT, 2, 0 },
621 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
622 { EL_SATELLITE, 2, 0 },
623 { EL_SP_DISK_YELLOW, 2, 0 },
625 { EL_UNDEFINED, 0, 0 },
633 move_stepsize_list[] =
635 { EL_AMOEBA_DROP, 2 },
636 { EL_AMOEBA_DROPPING, 2 },
637 { EL_QUICKSAND_FILLING, 1 },
638 { EL_QUICKSAND_EMPTYING, 1 },
639 { EL_MAGIC_WALL_FILLING, 2 },
640 { EL_BD_MAGIC_WALL_FILLING, 2 },
641 { EL_MAGIC_WALL_EMPTYING, 2 },
642 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
652 collect_count_list[] =
655 { EL_BD_DIAMOND, 1 },
656 { EL_EMERALD_YELLOW, 1 },
657 { EL_EMERALD_RED, 1 },
658 { EL_EMERALD_PURPLE, 1 },
660 { EL_SP_INFOTRON, 1 },
672 access_direction_list[] =
674 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
675 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
676 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
677 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
678 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
679 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
680 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
681 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
682 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
683 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
684 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
686 { EL_SP_PORT_LEFT, MV_RIGHT },
687 { EL_SP_PORT_RIGHT, MV_LEFT },
688 { EL_SP_PORT_UP, MV_DOWN },
689 { EL_SP_PORT_DOWN, MV_UP },
690 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
691 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
692 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
693 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
694 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
695 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
696 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
697 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
698 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
699 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
700 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
701 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
702 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
703 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
704 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
706 { EL_UNDEFINED, MV_NONE }
709 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
711 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
712 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
713 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
714 IS_JUST_CHANGING(x, y))
716 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
718 /* static variables for playfield scan mode (scanning forward or backward) */
719 static int playfield_scan_start_x = 0;
720 static int playfield_scan_start_y = 0;
721 static int playfield_scan_delta_x = 1;
722 static int playfield_scan_delta_y = 1;
724 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
725 (y) >= 0 && (y) <= lev_fieldy - 1; \
726 (y) += playfield_scan_delta_y) \
727 for ((x) = playfield_scan_start_x; \
728 (x) >= 0 && (x) <= lev_fieldx - 1; \
729 (x) += playfield_scan_delta_x) \
732 void DEBUG_SetMaximumDynamite()
736 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
737 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
738 local_player->inventory_element[local_player->inventory_size++] =
743 static void InitPlayfieldScanModeVars()
745 if (game.use_reverse_scan_direction)
747 playfield_scan_start_x = lev_fieldx - 1;
748 playfield_scan_start_y = lev_fieldy - 1;
750 playfield_scan_delta_x = -1;
751 playfield_scan_delta_y = -1;
755 playfield_scan_start_x = 0;
756 playfield_scan_start_y = 0;
758 playfield_scan_delta_x = 1;
759 playfield_scan_delta_y = 1;
763 static void InitPlayfieldScanMode(int mode)
765 game.use_reverse_scan_direction =
766 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
768 InitPlayfieldScanModeVars();
771 static int get_move_delay_from_stepsize(int move_stepsize)
774 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
776 /* make sure that stepsize value is always a power of 2 */
777 move_stepsize = (1 << log_2(move_stepsize));
779 return TILEX / move_stepsize;
782 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
785 int player_nr = player->index_nr;
786 int move_delay = get_move_delay_from_stepsize(move_stepsize);
787 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
789 /* do no immediately change move delay -- the player might just be moving */
790 player->move_delay_value_next = move_delay;
792 /* information if player can move must be set separately */
793 player->cannot_move = cannot_move;
797 player->move_delay = game.initial_move_delay[player_nr];
798 player->move_delay_value = game.initial_move_delay_value[player_nr];
800 player->move_delay_value_next = -1;
802 player->move_delay_reset_counter = 0;
806 void GetPlayerConfig()
808 if (!audio.sound_available)
809 setup.sound_simple = FALSE;
811 if (!audio.loops_available)
812 setup.sound_loops = FALSE;
814 if (!audio.music_available)
815 setup.sound_music = FALSE;
817 if (!video.fullscreen_available)
818 setup.fullscreen = FALSE;
820 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
822 SetAudioMode(setup.sound);
826 static int getBeltNrFromBeltElement(int element)
828 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
829 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
830 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
833 static int getBeltNrFromBeltActiveElement(int element)
835 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
836 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
837 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
840 static int getBeltNrFromBeltSwitchElement(int element)
842 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
843 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
844 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
847 static int getBeltDirNrFromBeltSwitchElement(int element)
849 static int belt_base_element[4] =
851 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
852 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
853 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
854 EL_CONVEYOR_BELT_4_SWITCH_LEFT
857 int belt_nr = getBeltNrFromBeltSwitchElement(element);
858 int belt_dir_nr = element - belt_base_element[belt_nr];
860 return (belt_dir_nr % 3);
863 static int getBeltDirFromBeltSwitchElement(int element)
865 static int belt_move_dir[3] =
872 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
874 return belt_move_dir[belt_dir_nr];
877 static int get_element_from_group_element(int element)
879 if (IS_GROUP_ELEMENT(element))
881 struct ElementGroupInfo *group = element_info[element].group;
882 int last_anim_random_frame = gfx.anim_random_frame;
885 if (group->choice_mode == ANIM_RANDOM)
886 gfx.anim_random_frame = RND(group->num_elements_resolved);
888 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
889 group->choice_mode, 0,
892 if (group->choice_mode == ANIM_RANDOM)
893 gfx.anim_random_frame = last_anim_random_frame;
897 element = group->element_resolved[element_pos];
903 static void InitPlayerField(int x, int y, int element, boolean init_game)
905 if (element == EL_SP_MURPHY)
909 if (stored_player[0].present)
911 Feld[x][y] = EL_SP_MURPHY_CLONE;
917 stored_player[0].use_murphy = TRUE;
919 if (!level.use_artwork_element[0])
920 stored_player[0].artwork_element = EL_SP_MURPHY;
923 Feld[x][y] = EL_PLAYER_1;
929 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
930 int jx = player->jx, jy = player->jy;
932 player->present = TRUE;
934 player->block_last_field = (element == EL_SP_MURPHY ?
935 level.sp_block_last_field :
936 level.block_last_field);
938 /* ---------- initialize player's last field block delay --------------- */
940 /* always start with reliable default value (no adjustment needed) */
941 player->block_delay_adjustment = 0;
943 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
944 if (player->block_last_field && element == EL_SP_MURPHY)
945 player->block_delay_adjustment = 1;
947 /* special case 2: in game engines before 3.1.1, blocking was different */
948 if (game.use_block_last_field_bug)
949 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
951 if (!options.network || player->connected)
953 player->active = TRUE;
955 /* remove potentially duplicate players */
956 if (StorePlayer[jx][jy] == Feld[x][y])
957 StorePlayer[jx][jy] = 0;
959 StorePlayer[x][y] = Feld[x][y];
963 printf("Player %d activated.\n", player->element_nr);
964 printf("[Local player is %d and currently %s.]\n",
965 local_player->element_nr,
966 local_player->active ? "active" : "not active");
970 Feld[x][y] = EL_EMPTY;
972 player->jx = player->last_jx = x;
973 player->jy = player->last_jy = y;
977 static void InitField(int x, int y, boolean init_game)
979 int element = Feld[x][y];
988 InitPlayerField(x, y, element, init_game);
991 case EL_SOKOBAN_FIELD_PLAYER:
992 element = Feld[x][y] = EL_PLAYER_1;
993 InitField(x, y, init_game);
995 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
996 InitField(x, y, init_game);
999 case EL_SOKOBAN_FIELD_EMPTY:
1000 local_player->sokobanfields_still_needed++;
1004 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1005 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1006 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1007 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1008 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1009 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1010 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1011 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1012 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1013 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1022 case EL_SPACESHIP_RIGHT:
1023 case EL_SPACESHIP_UP:
1024 case EL_SPACESHIP_LEFT:
1025 case EL_SPACESHIP_DOWN:
1026 case EL_BD_BUTTERFLY:
1027 case EL_BD_BUTTERFLY_RIGHT:
1028 case EL_BD_BUTTERFLY_UP:
1029 case EL_BD_BUTTERFLY_LEFT:
1030 case EL_BD_BUTTERFLY_DOWN:
1032 case EL_BD_FIREFLY_RIGHT:
1033 case EL_BD_FIREFLY_UP:
1034 case EL_BD_FIREFLY_LEFT:
1035 case EL_BD_FIREFLY_DOWN:
1036 case EL_PACMAN_RIGHT:
1038 case EL_PACMAN_LEFT:
1039 case EL_PACMAN_DOWN:
1041 case EL_YAMYAM_LEFT:
1042 case EL_YAMYAM_RIGHT:
1044 case EL_YAMYAM_DOWN:
1045 case EL_DARK_YAMYAM:
1048 case EL_SP_SNIKSNAK:
1049 case EL_SP_ELECTRON:
1058 case EL_AMOEBA_FULL:
1063 case EL_AMOEBA_DROP:
1064 if (y == lev_fieldy - 1)
1066 Feld[x][y] = EL_AMOEBA_GROWING;
1067 Store[x][y] = EL_AMOEBA_WET;
1071 case EL_DYNAMITE_ACTIVE:
1072 case EL_SP_DISK_RED_ACTIVE:
1073 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1074 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1075 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1076 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1077 MovDelay[x][y] = 96;
1080 case EL_EM_DYNAMITE_ACTIVE:
1081 MovDelay[x][y] = 32;
1085 local_player->lights_still_needed++;
1089 local_player->friends_still_needed++;
1094 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1097 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1098 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1099 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1100 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1101 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1102 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1103 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1104 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1105 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1106 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1107 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1108 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1111 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1112 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1113 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1115 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1117 game.belt_dir[belt_nr] = belt_dir;
1118 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1120 else /* more than one switch -- set it like the first switch */
1122 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1127 #if !USE_BOTH_SWITCHGATE_SWITCHES
1128 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1130 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1134 case EL_LIGHT_SWITCH_ACTIVE:
1136 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1139 case EL_INVISIBLE_STEELWALL:
1140 case EL_INVISIBLE_WALL:
1141 case EL_INVISIBLE_SAND:
1142 if (game.light_time_left > 0 ||
1143 game.lenses_time_left > 0)
1144 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1147 case EL_EMC_MAGIC_BALL:
1148 if (game.ball_state)
1149 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1152 case EL_EMC_MAGIC_BALL_SWITCH:
1153 if (game.ball_state)
1154 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1158 if (IS_CUSTOM_ELEMENT(element))
1160 if (CAN_MOVE(element))
1163 #if USE_NEW_CUSTOM_VALUE
1164 if (!element_info[element].use_last_ce_value || init_game)
1165 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1168 else if (IS_GROUP_ELEMENT(element))
1170 Feld[x][y] = get_element_from_group_element(element);
1172 InitField(x, y, init_game);
1179 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1182 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1184 InitField(x, y, init_game);
1186 /* not needed to call InitMovDir() -- already done by InitField()! */
1187 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1188 CAN_MOVE(Feld[x][y]))
1192 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1194 int old_element = Feld[x][y];
1196 InitField(x, y, init_game);
1198 /* not needed to call InitMovDir() -- already done by InitField()! */
1199 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1200 CAN_MOVE(old_element) &&
1201 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1204 /* this case is in fact a combination of not less than three bugs:
1205 first, it calls InitMovDir() for elements that can move, although this is
1206 already done by InitField(); then, it checks the element that was at this
1207 field _before_ the call to InitField() (which can change it); lastly, it
1208 was not called for "mole with direction" elements, which were treated as
1209 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1213 inline void DrawGameValue_Emeralds(int value)
1215 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1217 if (PANEL_DEACTIVATED(game.panel.gems))
1220 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1223 inline void DrawGameValue_Dynamite(int value)
1225 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1227 if (PANEL_DEACTIVATED(game.panel.inventory))
1230 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1233 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1235 int base_key_graphic = EL_KEY_1;
1238 if (PANEL_DEACTIVATED(game.panel.keys))
1241 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1242 base_key_graphic = EL_EM_KEY_1;
1244 /* currently only 4 of 8 possible keys are displayed */
1245 for (i = 0; i < STD_NUM_KEYS; i++)
1247 int x = XX_KEYS + i * MINI_TILEX;
1251 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1253 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1254 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1258 inline void DrawGameValue_Score(int value)
1260 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1262 if (PANEL_DEACTIVATED(game.panel.score))
1265 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1268 inline void DrawGameValue_Time(int value)
1270 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1271 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1273 if (PANEL_DEACTIVATED(game.panel.time))
1276 /* clear background if value just changed its size */
1277 if (value == 999 || value == 1000)
1278 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1281 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1283 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1286 inline void DrawGameValue_Level(int value)
1288 if (PANEL_DEACTIVATED(game.panel.level))
1292 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1294 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
1297 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1300 int key[MAX_NUM_KEYS];
1303 /* prevent EM engine from updating time/score values parallel to GameWon() */
1304 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1305 local_player->LevelSolved)
1308 for (i = 0; i < MAX_NUM_KEYS; i++)
1309 key[i] = key_bits & (1 << i);
1311 DrawGameValue_Level(level_nr);
1313 DrawGameValue_Emeralds(emeralds);
1314 DrawGameValue_Dynamite(dynamite);
1315 DrawGameValue_Score(score);
1316 DrawGameValue_Time(time);
1318 DrawGameValue_Keys(key);
1321 void DrawGameDoorValues()
1323 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1324 int dynamite_value = 0;
1325 int score_value = (local_player->LevelSolved ? local_player->score_final :
1326 local_player->score);
1327 int gems_value = local_player->gems_still_needed;
1331 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1333 DrawGameDoorValues_EM();
1338 if (game.centered_player_nr == -1)
1340 for (i = 0; i < MAX_PLAYERS; i++)
1342 for (j = 0; j < MAX_NUM_KEYS; j++)
1343 if (stored_player[i].key[j])
1344 key_bits |= (1 << j);
1346 dynamite_value += stored_player[i].inventory_size;
1351 int player_nr = game.centered_player_nr;
1353 for (i = 0; i < MAX_NUM_KEYS; i++)
1354 if (stored_player[player_nr].key[i])
1355 key_bits |= (1 << i);
1357 dynamite_value = stored_player[player_nr].inventory_size;
1360 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1366 =============================================================================
1368 -----------------------------------------------------------------------------
1369 initialize game engine due to level / tape version number
1370 =============================================================================
1373 static void InitGameEngine()
1375 int i, j, k, l, x, y;
1377 /* set game engine from tape file when re-playing, else from level file */
1378 game.engine_version = (tape.playing ? tape.engine_version :
1379 level.game_version);
1381 /* ---------------------------------------------------------------------- */
1382 /* set flags for bugs and changes according to active game engine version */
1383 /* ---------------------------------------------------------------------- */
1386 Summary of bugfix/change:
1387 Fixed handling for custom elements that change when pushed by the player.
1389 Fixed/changed in version:
1393 Before 3.1.0, custom elements that "change when pushing" changed directly
1394 after the player started pushing them (until then handled in "DigField()").
1395 Since 3.1.0, these custom elements are not changed until the "pushing"
1396 move of the element is finished (now handled in "ContinueMoving()").
1398 Affected levels/tapes:
1399 The first condition is generally needed for all levels/tapes before version
1400 3.1.0, which might use the old behaviour before it was changed; known tapes
1401 that are affected are some tapes from the level set "Walpurgis Gardens" by
1403 The second condition is an exception from the above case and is needed for
1404 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1405 above (including some development versions of 3.1.0), but before it was
1406 known that this change would break tapes like the above and was fixed in
1407 3.1.1, so that the changed behaviour was active although the engine version
1408 while recording maybe was before 3.1.0. There is at least one tape that is
1409 affected by this exception, which is the tape for the one-level set "Bug
1410 Machine" by Juergen Bonhagen.
1413 game.use_change_when_pushing_bug =
1414 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1416 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1417 tape.game_version < VERSION_IDENT(3,1,1,0)));
1420 Summary of bugfix/change:
1421 Fixed handling for blocking the field the player leaves when moving.
1423 Fixed/changed in version:
1427 Before 3.1.1, when "block last field when moving" was enabled, the field
1428 the player is leaving when moving was blocked for the time of the move,
1429 and was directly unblocked afterwards. This resulted in the last field
1430 being blocked for exactly one less than the number of frames of one player
1431 move. Additionally, even when blocking was disabled, the last field was
1432 blocked for exactly one frame.
1433 Since 3.1.1, due to changes in player movement handling, the last field
1434 is not blocked at all when blocking is disabled. When blocking is enabled,
1435 the last field is blocked for exactly the number of frames of one player
1436 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1437 last field is blocked for exactly one more than the number of frames of
1440 Affected levels/tapes:
1441 (!!! yet to be determined -- probably many !!!)
1444 game.use_block_last_field_bug =
1445 (game.engine_version < VERSION_IDENT(3,1,1,0));
1448 Summary of bugfix/change:
1449 Changed behaviour of CE changes with multiple changes per single frame.
1451 Fixed/changed in version:
1455 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1456 This resulted in race conditions where CEs seem to behave strange in some
1457 situations (where triggered CE changes were just skipped because there was
1458 already a CE change on that tile in the playfield in that engine frame).
1459 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1460 (The number of changes per frame must be limited in any case, because else
1461 it is easily possible to define CE changes that would result in an infinite
1462 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1463 should be set large enough so that it would only be reached in cases where
1464 the corresponding CE change conditions run into a loop. Therefore, it seems
1465 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1466 maximal number of change pages for custom elements.)
1468 Affected levels/tapes:
1472 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1473 game.max_num_changes_per_frame = 1;
1475 game.max_num_changes_per_frame =
1476 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1479 /* ---------------------------------------------------------------------- */
1481 /* default scan direction: scan playfield from top/left to bottom/right */
1482 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1484 /* dynamically adjust element properties according to game engine version */
1485 InitElementPropertiesEngine(game.engine_version);
1488 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1489 printf(" tape version == %06d [%s] [file: %06d]\n",
1490 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1492 printf(" => game.engine_version == %06d\n", game.engine_version);
1495 /* ---------- initialize player's initial move delay --------------------- */
1497 /* dynamically adjust player properties according to level information */
1498 for (i = 0; i < MAX_PLAYERS; i++)
1499 game.initial_move_delay_value[i] =
1500 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1502 /* dynamically adjust player properties according to game engine version */
1503 for (i = 0; i < MAX_PLAYERS; i++)
1504 game.initial_move_delay[i] =
1505 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1506 game.initial_move_delay_value[i] : 0);
1508 /* ---------- initialize player's initial push delay --------------------- */
1510 /* dynamically adjust player properties according to game engine version */
1511 game.initial_push_delay_value =
1512 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1514 /* ---------- initialize changing elements ------------------------------- */
1516 /* initialize changing elements information */
1517 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1519 struct ElementInfo *ei = &element_info[i];
1521 /* this pointer might have been changed in the level editor */
1522 ei->change = &ei->change_page[0];
1524 if (!IS_CUSTOM_ELEMENT(i))
1526 ei->change->target_element = EL_EMPTY_SPACE;
1527 ei->change->delay_fixed = 0;
1528 ei->change->delay_random = 0;
1529 ei->change->delay_frames = 1;
1532 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1534 ei->has_change_event[j] = FALSE;
1536 ei->event_page_nr[j] = 0;
1537 ei->event_page[j] = &ei->change_page[0];
1541 /* add changing elements from pre-defined list */
1542 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1544 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1545 struct ElementInfo *ei = &element_info[ch_delay->element];
1547 ei->change->target_element = ch_delay->target_element;
1548 ei->change->delay_fixed = ch_delay->change_delay;
1550 ei->change->pre_change_function = ch_delay->pre_change_function;
1551 ei->change->change_function = ch_delay->change_function;
1552 ei->change->post_change_function = ch_delay->post_change_function;
1554 ei->change->can_change = TRUE;
1555 ei->change->can_change_or_has_action = TRUE;
1557 ei->has_change_event[CE_DELAY] = TRUE;
1559 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1560 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1563 /* ---------- initialize internal run-time variables ------------- */
1565 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1567 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1569 for (j = 0; j < ei->num_change_pages; j++)
1571 ei->change_page[j].can_change_or_has_action =
1572 (ei->change_page[j].can_change |
1573 ei->change_page[j].has_action);
1577 /* add change events from custom element configuration */
1578 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1580 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1582 for (j = 0; j < ei->num_change_pages; j++)
1584 if (!ei->change_page[j].can_change_or_has_action)
1587 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1589 /* only add event page for the first page found with this event */
1590 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1592 ei->has_change_event[k] = TRUE;
1594 ei->event_page_nr[k] = j;
1595 ei->event_page[k] = &ei->change_page[j];
1601 /* ---------- initialize run-time trigger player and element ------------- */
1603 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1605 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1607 for (j = 0; j < ei->num_change_pages; j++)
1609 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1610 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1611 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1612 ei->change_page[j].actual_trigger_ce_value = 0;
1613 ei->change_page[j].actual_trigger_ce_score = 0;
1617 /* ---------- initialize trigger events ---------------------------------- */
1619 /* initialize trigger events information */
1620 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1621 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1622 trigger_events[i][j] = FALSE;
1624 /* add trigger events from element change event properties */
1625 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1627 struct ElementInfo *ei = &element_info[i];
1629 for (j = 0; j < ei->num_change_pages; j++)
1631 if (!ei->change_page[j].can_change_or_has_action)
1634 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1636 int trigger_element = ei->change_page[j].trigger_element;
1638 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1640 if (ei->change_page[j].has_event[k])
1642 if (IS_GROUP_ELEMENT(trigger_element))
1644 struct ElementGroupInfo *group =
1645 element_info[trigger_element].group;
1647 for (l = 0; l < group->num_elements_resolved; l++)
1648 trigger_events[group->element_resolved[l]][k] = TRUE;
1650 else if (trigger_element == EL_ANY_ELEMENT)
1651 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1652 trigger_events[l][k] = TRUE;
1654 trigger_events[trigger_element][k] = TRUE;
1661 /* ---------- initialize push delay -------------------------------------- */
1663 /* initialize push delay values to default */
1664 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1666 if (!IS_CUSTOM_ELEMENT(i))
1668 /* set default push delay values (corrected since version 3.0.7-1) */
1669 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1671 element_info[i].push_delay_fixed = 2;
1672 element_info[i].push_delay_random = 8;
1676 element_info[i].push_delay_fixed = 8;
1677 element_info[i].push_delay_random = 8;
1682 /* set push delay value for certain elements from pre-defined list */
1683 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1685 int e = push_delay_list[i].element;
1687 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1688 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1691 /* set push delay value for Supaplex elements for newer engine versions */
1692 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1694 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1696 if (IS_SP_ELEMENT(i))
1698 /* set SP push delay to just enough to push under a falling zonk */
1699 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1701 element_info[i].push_delay_fixed = delay;
1702 element_info[i].push_delay_random = 0;
1707 /* ---------- initialize move stepsize ----------------------------------- */
1709 /* initialize move stepsize values to default */
1710 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1711 if (!IS_CUSTOM_ELEMENT(i))
1712 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1714 /* set move stepsize value for certain elements from pre-defined list */
1715 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1717 int e = move_stepsize_list[i].element;
1719 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1722 /* ---------- initialize collect score ----------------------------------- */
1724 /* initialize collect score values for custom elements from initial value */
1725 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1726 if (IS_CUSTOM_ELEMENT(i))
1727 element_info[i].collect_score = element_info[i].collect_score_initial;
1729 /* ---------- initialize collect count ----------------------------------- */
1731 /* initialize collect count values for non-custom elements */
1732 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1733 if (!IS_CUSTOM_ELEMENT(i))
1734 element_info[i].collect_count_initial = 0;
1736 /* add collect count values for all elements from pre-defined list */
1737 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1738 element_info[collect_count_list[i].element].collect_count_initial =
1739 collect_count_list[i].count;
1741 /* ---------- initialize access direction -------------------------------- */
1743 /* initialize access direction values to default (access from every side) */
1744 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1745 if (!IS_CUSTOM_ELEMENT(i))
1746 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1748 /* set access direction value for certain elements from pre-defined list */
1749 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1750 element_info[access_direction_list[i].element].access_direction =
1751 access_direction_list[i].direction;
1753 /* ---------- initialize explosion content ------------------------------- */
1754 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1756 if (IS_CUSTOM_ELEMENT(i))
1759 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1761 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1763 element_info[i].content.e[x][y] =
1764 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1765 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1766 i == EL_PLAYER_3 ? EL_EMERALD :
1767 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1768 i == EL_MOLE ? EL_EMERALD_RED :
1769 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1770 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1771 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1772 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1773 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1774 i == EL_WALL_EMERALD ? EL_EMERALD :
1775 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1776 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1777 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1778 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1779 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1780 i == EL_WALL_PEARL ? EL_PEARL :
1781 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1787 int get_num_special_action(int element, int action_first, int action_last)
1789 int num_special_action = 0;
1792 for (i = action_first; i <= action_last; i++)
1794 boolean found = FALSE;
1796 for (j = 0; j < NUM_DIRECTIONS; j++)
1797 if (el_act_dir2img(element, i, j) !=
1798 el_act_dir2img(element, ACTION_DEFAULT, j))
1802 num_special_action++;
1807 return num_special_action;
1812 =============================================================================
1814 -----------------------------------------------------------------------------
1815 initialize and start new game
1816 =============================================================================
1821 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1822 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1823 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1824 boolean do_fading = (game_status == GAME_MODE_MAIN);
1827 game_status = GAME_MODE_PLAYING;
1831 /* don't play tapes over network */
1832 network_playing = (options.network && !tape.playing);
1834 for (i = 0; i < MAX_PLAYERS; i++)
1836 struct PlayerInfo *player = &stored_player[i];
1838 player->index_nr = i;
1839 player->index_bit = (1 << i);
1840 player->element_nr = EL_PLAYER_1 + i;
1842 player->present = FALSE;
1843 player->active = FALSE;
1846 player->effective_action = 0;
1847 player->programmed_action = 0;
1850 player->score_final = 0;
1852 player->gems_still_needed = level.gems_needed;
1853 player->sokobanfields_still_needed = 0;
1854 player->lights_still_needed = 0;
1855 player->friends_still_needed = 0;
1857 for (j = 0; j < MAX_NUM_KEYS; j++)
1858 player->key[j] = FALSE;
1860 player->dynabomb_count = 0;
1861 player->dynabomb_size = 1;
1862 player->dynabombs_left = 0;
1863 player->dynabomb_xl = FALSE;
1865 player->MovDir = MV_NONE;
1868 player->GfxDir = MV_NONE;
1869 player->GfxAction = ACTION_DEFAULT;
1871 player->StepFrame = 0;
1873 player->use_murphy = FALSE;
1874 player->artwork_element =
1875 (level.use_artwork_element[i] ? level.artwork_element[i] :
1876 player->element_nr);
1878 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1879 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1881 player->gravity = level.initial_player_gravity[i];
1883 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1885 player->actual_frame_counter = 0;
1887 player->step_counter = 0;
1889 player->last_move_dir = MV_NONE;
1891 player->is_active = FALSE;
1893 player->is_waiting = FALSE;
1894 player->is_moving = FALSE;
1895 player->is_auto_moving = FALSE;
1896 player->is_digging = FALSE;
1897 player->is_snapping = FALSE;
1898 player->is_collecting = FALSE;
1899 player->is_pushing = FALSE;
1900 player->is_switching = FALSE;
1901 player->is_dropping = FALSE;
1902 player->is_dropping_pressed = FALSE;
1904 player->is_bored = FALSE;
1905 player->is_sleeping = FALSE;
1907 player->frame_counter_bored = -1;
1908 player->frame_counter_sleeping = -1;
1910 player->anim_delay_counter = 0;
1911 player->post_delay_counter = 0;
1913 player->dir_waiting = MV_NONE;
1914 player->action_waiting = ACTION_DEFAULT;
1915 player->last_action_waiting = ACTION_DEFAULT;
1916 player->special_action_bored = ACTION_DEFAULT;
1917 player->special_action_sleeping = ACTION_DEFAULT;
1919 player->switch_x = -1;
1920 player->switch_y = -1;
1922 player->drop_x = -1;
1923 player->drop_y = -1;
1925 player->show_envelope = 0;
1927 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
1929 player->push_delay = -1; /* initialized when pushing starts */
1930 player->push_delay_value = game.initial_push_delay_value;
1932 player->drop_delay = 0;
1933 player->drop_pressed_delay = 0;
1935 player->last_jx = player->last_jy = 0;
1936 player->jx = player->jy = 0;
1938 player->shield_normal_time_left = 0;
1939 player->shield_deadly_time_left = 0;
1941 player->inventory_infinite_element = EL_UNDEFINED;
1942 player->inventory_size = 0;
1944 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1945 SnapField(player, 0, 0);
1947 player->LevelSolved = FALSE;
1948 player->GameOver = FALSE;
1950 player->LevelSolved_GameEnd = FALSE;
1951 player->LevelSolved_SaveTape = FALSE;
1952 player->LevelSolved_SaveScore = FALSE;
1955 network_player_action_received = FALSE;
1957 #if defined(NETWORK_AVALIABLE)
1958 /* initial null action */
1959 if (network_playing)
1960 SendToServer_MovePlayer(MV_NONE);
1969 TimeLeft = level.time;
1972 ScreenMovDir = MV_NONE;
1976 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1978 AllPlayersGone = FALSE;
1980 game.yamyam_content_nr = 0;
1981 game.magic_wall_active = FALSE;
1982 game.magic_wall_time_left = 0;
1983 game.light_time_left = 0;
1984 game.timegate_time_left = 0;
1985 game.switchgate_pos = 0;
1986 game.wind_direction = level.wind_direction_initial;
1988 #if !USE_PLAYER_GRAVITY
1989 game.gravity = FALSE;
1990 game.explosions_delayed = TRUE;
1993 game.lenses_time_left = 0;
1994 game.magnify_time_left = 0;
1996 game.ball_state = level.ball_state_initial;
1997 game.ball_content_nr = 0;
1999 game.envelope_active = FALSE;
2001 /* set focus to local player for network games, else to all players */
2002 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2003 game.centered_player_nr_next = game.centered_player_nr;
2004 game.set_centered_player = FALSE;
2006 if (network_playing && tape.recording)
2008 /* store client dependent player focus when recording network games */
2009 tape.centered_player_nr_next = game.centered_player_nr_next;
2010 tape.set_centered_player = TRUE;
2013 for (i = 0; i < NUM_BELTS; i++)
2015 game.belt_dir[i] = MV_NONE;
2016 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2019 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2020 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2022 SCAN_PLAYFIELD(x, y)
2024 Feld[x][y] = level.field[x][y];
2025 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2026 ChangeDelay[x][y] = 0;
2027 ChangePage[x][y] = -1;
2028 #if USE_NEW_CUSTOM_VALUE
2029 CustomValue[x][y] = 0; /* initialized in InitField() */
2031 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2033 WasJustMoving[x][y] = 0;
2034 WasJustFalling[x][y] = 0;
2035 CheckCollision[x][y] = 0;
2037 Pushed[x][y] = FALSE;
2039 ChangeCount[x][y] = 0;
2040 ChangeEvent[x][y] = -1;
2042 ExplodePhase[x][y] = 0;
2043 ExplodeDelay[x][y] = 0;
2044 ExplodeField[x][y] = EX_TYPE_NONE;
2046 RunnerVisit[x][y] = 0;
2047 PlayerVisit[x][y] = 0;
2050 GfxRandom[x][y] = INIT_GFX_RANDOM();
2051 GfxElement[x][y] = EL_UNDEFINED;
2052 GfxAction[x][y] = ACTION_DEFAULT;
2053 GfxDir[x][y] = MV_NONE;
2056 SCAN_PLAYFIELD(x, y)
2058 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2060 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2062 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2065 InitField(x, y, TRUE);
2070 for (i = 0; i < MAX_PLAYERS; i++)
2072 struct PlayerInfo *player = &stored_player[i];
2074 /* set number of special actions for bored and sleeping animation */
2075 player->num_special_action_bored =
2076 get_num_special_action(player->artwork_element,
2077 ACTION_BORING_1, ACTION_BORING_LAST);
2078 player->num_special_action_sleeping =
2079 get_num_special_action(player->artwork_element,
2080 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2083 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2084 emulate_sb ? EMU_SOKOBAN :
2085 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2087 #if USE_NEW_ALL_SLIPPERY
2088 /* initialize type of slippery elements */
2089 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2091 if (!IS_CUSTOM_ELEMENT(i))
2093 /* default: elements slip down either to the left or right randomly */
2094 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2096 /* SP style elements prefer to slip down on the left side */
2097 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2098 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2100 /* BD style elements prefer to slip down on the left side */
2101 if (game.emulation == EMU_BOULDERDASH)
2102 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2107 /* initialize explosion and ignition delay */
2108 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2110 if (!IS_CUSTOM_ELEMENT(i))
2113 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2114 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2115 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2116 int last_phase = (num_phase + 1) * delay;
2117 int half_phase = (num_phase / 2) * delay;
2119 element_info[i].explosion_delay = last_phase - 1;
2120 element_info[i].ignition_delay = half_phase;
2122 if (i == EL_BLACK_ORB)
2123 element_info[i].ignition_delay = 1;
2127 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2128 element_info[i].explosion_delay = 1;
2130 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2131 element_info[i].ignition_delay = 1;
2135 /* correct non-moving belts to start moving left */
2136 for (i = 0; i < NUM_BELTS; i++)
2137 if (game.belt_dir[i] == MV_NONE)
2138 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2140 /* check if any connected player was not found in playfield */
2141 for (i = 0; i < MAX_PLAYERS; i++)
2143 struct PlayerInfo *player = &stored_player[i];
2145 if (player->connected && !player->present)
2147 for (j = 0; j < MAX_PLAYERS; j++)
2149 struct PlayerInfo *some_player = &stored_player[j];
2150 int jx = some_player->jx, jy = some_player->jy;
2152 /* assign first free player found that is present in the playfield */
2153 if (some_player->present && !some_player->connected)
2155 player->present = TRUE;
2156 player->active = TRUE;
2158 some_player->present = FALSE;
2159 some_player->active = FALSE;
2161 player->artwork_element = some_player->artwork_element;
2163 player->block_last_field = some_player->block_last_field;
2164 player->block_delay_adjustment = some_player->block_delay_adjustment;
2166 StorePlayer[jx][jy] = player->element_nr;
2167 player->jx = player->last_jx = jx;
2168 player->jy = player->last_jy = jy;
2178 /* when playing a tape, eliminate all players who do not participate */
2180 for (i = 0; i < MAX_PLAYERS; i++)
2182 if (stored_player[i].active && !tape.player_participates[i])
2184 struct PlayerInfo *player = &stored_player[i];
2185 int jx = player->jx, jy = player->jy;
2187 player->active = FALSE;
2188 StorePlayer[jx][jy] = 0;
2189 Feld[jx][jy] = EL_EMPTY;
2193 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2195 /* when in single player mode, eliminate all but the first active player */
2197 for (i = 0; i < MAX_PLAYERS; i++)
2199 if (stored_player[i].active)
2201 for (j = i + 1; j < MAX_PLAYERS; j++)
2203 if (stored_player[j].active)
2205 struct PlayerInfo *player = &stored_player[j];
2206 int jx = player->jx, jy = player->jy;
2208 player->active = FALSE;
2209 player->present = FALSE;
2211 StorePlayer[jx][jy] = 0;
2212 Feld[jx][jy] = EL_EMPTY;
2219 /* when recording the game, store which players take part in the game */
2222 for (i = 0; i < MAX_PLAYERS; i++)
2223 if (stored_player[i].active)
2224 tape.player_participates[i] = TRUE;
2229 for (i = 0; i < MAX_PLAYERS; i++)
2231 struct PlayerInfo *player = &stored_player[i];
2233 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2238 if (local_player == player)
2239 printf("Player %d is local player.\n", i+1);
2243 if (BorderElement == EL_EMPTY)
2246 SBX_Right = lev_fieldx - SCR_FIELDX;
2248 SBY_Lower = lev_fieldy - SCR_FIELDY;
2253 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2255 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2258 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2259 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2261 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2262 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2264 /* if local player not found, look for custom element that might create
2265 the player (make some assumptions about the right custom element) */
2266 if (!local_player->present)
2268 int start_x = 0, start_y = 0;
2269 int found_rating = 0;
2270 int found_element = EL_UNDEFINED;
2271 int player_nr = local_player->index_nr;
2273 SCAN_PLAYFIELD(x, y)
2275 int element = Feld[x][y];
2280 if (level.use_start_element[player_nr] &&
2281 level.start_element[player_nr] == element &&
2288 found_element = element;
2291 if (!IS_CUSTOM_ELEMENT(element))
2294 if (CAN_CHANGE(element))
2296 for (i = 0; i < element_info[element].num_change_pages; i++)
2298 /* check for player created from custom element as single target */
2299 content = element_info[element].change_page[i].target_element;
2300 is_player = ELEM_IS_PLAYER(content);
2302 if (is_player && (found_rating < 3 || element < found_element))
2308 found_element = element;
2313 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2315 /* check for player created from custom element as explosion content */
2316 content = element_info[element].content.e[xx][yy];
2317 is_player = ELEM_IS_PLAYER(content);
2319 if (is_player && (found_rating < 2 || element < found_element))
2321 start_x = x + xx - 1;
2322 start_y = y + yy - 1;
2325 found_element = element;
2328 if (!CAN_CHANGE(element))
2331 for (i = 0; i < element_info[element].num_change_pages; i++)
2333 /* check for player created from custom element as extended target */
2335 element_info[element].change_page[i].target_content.e[xx][yy];
2337 is_player = ELEM_IS_PLAYER(content);
2339 if (is_player && (found_rating < 1 || element < found_element))
2341 start_x = x + xx - 1;
2342 start_y = y + yy - 1;
2345 found_element = element;
2351 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2352 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2355 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2356 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2361 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2362 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2363 local_player->jx - MIDPOSX);
2365 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2366 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2367 local_player->jy - MIDPOSY);
2372 if (!game.restart_level)
2373 CloseDoor(DOOR_CLOSE_1);
2376 FadeOut(REDRAW_FIELD);
2378 /* !!! FIX THIS (START) !!! */
2379 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2381 InitGameEngine_EM();
2383 /* blit playfield from scroll buffer to normal back buffer for fading in */
2384 BlitScreenToBitmap_EM(backbuffer);
2391 /* after drawing the level, correct some elements */
2392 if (game.timegate_time_left == 0)
2393 CloseAllOpenTimegates();
2395 /* blit playfield from scroll buffer to normal back buffer for fading in */
2396 if (setup.soft_scrolling)
2397 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2399 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2401 /* !!! FIX THIS (END) !!! */
2404 FadeIn(REDRAW_FIELD);
2408 if (!game.restart_level)
2410 /* copy default game door content to main double buffer */
2411 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2412 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2415 SetPanelBackground();
2416 SetDrawBackgroundMask(REDRAW_DOOR_1);
2418 DrawGameDoorValues();
2420 if (!game.restart_level)
2424 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2425 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2426 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2430 /* copy actual game door content to door double buffer for OpenDoor() */
2431 BlitBitmap(drawto, bitmap_db_door,
2432 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2434 OpenDoor(DOOR_OPEN_ALL);
2436 PlaySound(SND_GAME_STARTING);
2438 if (setup.sound_music)
2441 KeyboardAutoRepeatOffUnlessAutoplay();
2445 for (i = 0; i < MAX_PLAYERS; i++)
2446 printf("Player %d %sactive.\n",
2447 i + 1, (stored_player[i].active ? "" : "not "));
2458 game.restart_level = FALSE;
2461 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2463 /* this is used for non-R'n'D game engines to update certain engine values */
2465 /* needed to determine if sounds are played within the visible screen area */
2466 scroll_x = actual_scroll_x;
2467 scroll_y = actual_scroll_y;
2470 void InitMovDir(int x, int y)
2472 int i, element = Feld[x][y];
2473 static int xy[4][2] =
2480 static int direction[3][4] =
2482 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2483 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2484 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2493 Feld[x][y] = EL_BUG;
2494 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2497 case EL_SPACESHIP_RIGHT:
2498 case EL_SPACESHIP_UP:
2499 case EL_SPACESHIP_LEFT:
2500 case EL_SPACESHIP_DOWN:
2501 Feld[x][y] = EL_SPACESHIP;
2502 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2505 case EL_BD_BUTTERFLY_RIGHT:
2506 case EL_BD_BUTTERFLY_UP:
2507 case EL_BD_BUTTERFLY_LEFT:
2508 case EL_BD_BUTTERFLY_DOWN:
2509 Feld[x][y] = EL_BD_BUTTERFLY;
2510 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2513 case EL_BD_FIREFLY_RIGHT:
2514 case EL_BD_FIREFLY_UP:
2515 case EL_BD_FIREFLY_LEFT:
2516 case EL_BD_FIREFLY_DOWN:
2517 Feld[x][y] = EL_BD_FIREFLY;
2518 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2521 case EL_PACMAN_RIGHT:
2523 case EL_PACMAN_LEFT:
2524 case EL_PACMAN_DOWN:
2525 Feld[x][y] = EL_PACMAN;
2526 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2529 case EL_YAMYAM_LEFT:
2530 case EL_YAMYAM_RIGHT:
2532 case EL_YAMYAM_DOWN:
2533 Feld[x][y] = EL_YAMYAM;
2534 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2537 case EL_SP_SNIKSNAK:
2538 MovDir[x][y] = MV_UP;
2541 case EL_SP_ELECTRON:
2542 MovDir[x][y] = MV_LEFT;
2549 Feld[x][y] = EL_MOLE;
2550 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2554 if (IS_CUSTOM_ELEMENT(element))
2556 struct ElementInfo *ei = &element_info[element];
2557 int move_direction_initial = ei->move_direction_initial;
2558 int move_pattern = ei->move_pattern;
2560 if (move_direction_initial == MV_START_PREVIOUS)
2562 if (MovDir[x][y] != MV_NONE)
2565 move_direction_initial = MV_START_AUTOMATIC;
2568 if (move_direction_initial == MV_START_RANDOM)
2569 MovDir[x][y] = 1 << RND(4);
2570 else if (move_direction_initial & MV_ANY_DIRECTION)
2571 MovDir[x][y] = move_direction_initial;
2572 else if (move_pattern == MV_ALL_DIRECTIONS ||
2573 move_pattern == MV_TURNING_LEFT ||
2574 move_pattern == MV_TURNING_RIGHT ||
2575 move_pattern == MV_TURNING_LEFT_RIGHT ||
2576 move_pattern == MV_TURNING_RIGHT_LEFT ||
2577 move_pattern == MV_TURNING_RANDOM)
2578 MovDir[x][y] = 1 << RND(4);
2579 else if (move_pattern == MV_HORIZONTAL)
2580 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2581 else if (move_pattern == MV_VERTICAL)
2582 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2583 else if (move_pattern & MV_ANY_DIRECTION)
2584 MovDir[x][y] = element_info[element].move_pattern;
2585 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2586 move_pattern == MV_ALONG_RIGHT_SIDE)
2588 /* use random direction as default start direction */
2589 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2590 MovDir[x][y] = 1 << RND(4);
2592 for (i = 0; i < NUM_DIRECTIONS; i++)
2594 int x1 = x + xy[i][0];
2595 int y1 = y + xy[i][1];
2597 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2599 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2600 MovDir[x][y] = direction[0][i];
2602 MovDir[x][y] = direction[1][i];
2611 MovDir[x][y] = 1 << RND(4);
2613 if (element != EL_BUG &&
2614 element != EL_SPACESHIP &&
2615 element != EL_BD_BUTTERFLY &&
2616 element != EL_BD_FIREFLY)
2619 for (i = 0; i < NUM_DIRECTIONS; i++)
2621 int x1 = x + xy[i][0];
2622 int y1 = y + xy[i][1];
2624 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2626 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2628 MovDir[x][y] = direction[0][i];
2631 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2632 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2634 MovDir[x][y] = direction[1][i];
2643 GfxDir[x][y] = MovDir[x][y];
2646 void InitAmoebaNr(int x, int y)
2649 int group_nr = AmoebeNachbarNr(x, y);
2653 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2655 if (AmoebaCnt[i] == 0)
2663 AmoebaNr[x][y] = group_nr;
2664 AmoebaCnt[group_nr]++;
2665 AmoebaCnt2[group_nr]++;
2668 static void PlayerWins(struct PlayerInfo *player)
2670 player->LevelSolved = TRUE;
2671 player->GameOver = TRUE;
2673 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2674 level.native_em_level->lev->score : player->score);
2679 static int time, time_final;
2680 static int score, score_final;
2681 static int game_over_delay = 0;
2682 int game_over_delay_value = 50;
2684 if (!local_player->LevelSolved_GameEnd)
2688 /* do not start end game actions before the player stops moving (to exit) */
2689 if (local_player->MovPos)
2692 local_player->LevelSolved_GameEnd = TRUE;
2693 local_player->LevelSolved_SaveTape = tape.recording;
2694 local_player->LevelSolved_SaveScore = !tape.playing;
2696 if (tape.auto_play) /* tape might already be stopped here */
2697 tape.auto_play_level_solved = TRUE;
2703 game_over_delay = game_over_delay_value;
2705 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
2706 score = score_final = local_player->score_final;
2711 score_final += TimeLeft * level.score[SC_TIME_BONUS];
2713 else if (level.time == 0 && TimePlayed < 999)
2716 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
2719 local_player->score_final = score_final;
2721 if (level_editor_test_game)
2724 score = score_final;
2726 DrawGameValue_Time(time);
2727 DrawGameValue_Score(score);
2730 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
2732 /* close exit door after last player */
2733 if (AllPlayersGone &&
2734 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2735 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2737 int element = Feld[ExitX][ExitY];
2739 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2740 EL_SP_EXIT_CLOSING);
2742 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2745 /* player disappears */
2746 DrawLevelField(ExitX, ExitY);
2749 for (i = 0; i < MAX_PLAYERS; i++)
2751 struct PlayerInfo *player = &stored_player[i];
2753 if (player->present)
2755 RemovePlayer(player);
2757 /* player disappears */
2758 DrawLevelField(player->jx, player->jy);
2762 PlaySound(SND_GAME_WINNING);
2765 if (game_over_delay > 0)
2772 if (time != time_final)
2774 int time_to_go = ABS(time_final - time);
2775 int time_count_dir = (time < time_final ? +1 : -1);
2776 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
2778 time += time_count_steps * time_count_dir;
2779 score += time_count_steps * level.score[SC_TIME_BONUS];
2781 DrawGameValue_Time(time);
2782 DrawGameValue_Score(score);
2784 if (time == time_final)
2785 StopSound(SND_GAME_LEVELTIME_BONUS);
2786 else if (setup.sound_loops)
2787 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
2789 PlaySound(SND_GAME_LEVELTIME_BONUS);
2796 boolean raise_level = FALSE;
2798 CloseDoor(DOOR_CLOSE_1);
2800 if (local_player->LevelSolved_SaveTape)
2807 SaveTapeChecked(tape.level_nr); /* ask to save tape */
2809 SaveTape(tape.level_nr); /* ask to save tape */
2813 if (level_editor_test_game)
2815 game_status = GAME_MODE_MAIN;
2822 if (!local_player->LevelSolved_SaveScore)
2824 FadeOut(REDRAW_FIELD);
2826 game_status = GAME_MODE_MAIN;
2828 DrawAndFadeInMainMenu(REDRAW_FIELD);
2833 if (level_nr == leveldir_current->handicap_level)
2835 leveldir_current->handicap_level++;
2836 SaveLevelSetup_SeriesInfo();
2839 if (level_nr < leveldir_current->last_level)
2840 raise_level = TRUE; /* advance to next level */
2842 if ((hi_pos = NewHiScore()) >= 0)
2844 game_status = GAME_MODE_SCORES;
2846 DrawHallOfFame(hi_pos);
2856 FadeOut(REDRAW_FIELD);
2858 game_status = GAME_MODE_MAIN;
2866 DrawAndFadeInMainMenu(REDRAW_FIELD);
2875 LoadScore(level_nr);
2877 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2878 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
2881 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2883 if (local_player->score_final > highscore[k].Score)
2885 /* player has made it to the hall of fame */
2887 if (k < MAX_SCORE_ENTRIES - 1)
2889 int m = MAX_SCORE_ENTRIES - 1;
2892 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2893 if (strEqual(setup.player_name, highscore[l].Name))
2895 if (m == k) /* player's new highscore overwrites his old one */
2899 for (l = m; l > k; l--)
2901 strcpy(highscore[l].Name, highscore[l - 1].Name);
2902 highscore[l].Score = highscore[l - 1].Score;
2909 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2910 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2911 highscore[k].Score = local_player->score_final;
2917 else if (!strncmp(setup.player_name, highscore[k].Name,
2918 MAX_PLAYER_NAME_LEN))
2919 break; /* player already there with a higher score */
2925 SaveScore(level_nr);
2930 inline static int getElementMoveStepsize(int x, int y)
2932 int element = Feld[x][y];
2933 int direction = MovDir[x][y];
2934 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2935 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2936 int horiz_move = (dx != 0);
2937 int sign = (horiz_move ? dx : dy);
2938 int step = sign * element_info[element].move_stepsize;
2940 /* special values for move stepsize for spring and things on conveyor belt */
2943 if (CAN_FALL(element) &&
2944 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2945 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2946 else if (element == EL_SPRING)
2947 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2953 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2955 if (player->GfxAction != action || player->GfxDir != dir)
2958 printf("Player frame reset! (%d => %d, %d => %d)\n",
2959 player->GfxAction, action, player->GfxDir, dir);
2962 player->GfxAction = action;
2963 player->GfxDir = dir;
2965 player->StepFrame = 0;
2969 #if USE_GFX_RESET_GFX_ANIMATION
2970 static void ResetGfxFrame(int x, int y, boolean redraw)
2972 int element = Feld[x][y];
2973 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2974 int last_gfx_frame = GfxFrame[x][y];
2976 if (graphic_info[graphic].anim_global_sync)
2977 GfxFrame[x][y] = FrameCounter;
2978 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2979 GfxFrame[x][y] = CustomValue[x][y];
2980 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2981 GfxFrame[x][y] = element_info[element].collect_score;
2982 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
2983 GfxFrame[x][y] = ChangeDelay[x][y];
2985 if (redraw && GfxFrame[x][y] != last_gfx_frame)
2986 DrawLevelGraphicAnimation(x, y, graphic);
2990 static void ResetGfxAnimation(int x, int y)
2992 GfxAction[x][y] = ACTION_DEFAULT;
2993 GfxDir[x][y] = MovDir[x][y];
2996 #if USE_GFX_RESET_GFX_ANIMATION
2997 ResetGfxFrame(x, y, FALSE);
3001 static void ResetRandomAnimationValue(int x, int y)
3003 GfxRandom[x][y] = INIT_GFX_RANDOM();
3006 void InitMovingField(int x, int y, int direction)
3008 int element = Feld[x][y];
3009 int move_stepsize = getElementMoveStepsize(x, y);
3010 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3011 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3014 boolean is_moving = (move_stepsize != 0); /* moving or being moved */
3015 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3017 /* reset animation only for moving elements that change direction of moving
3018 (else CEs with property "can move" / "not moving" are reset each frame) */
3019 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3020 if (is_moving && !continues_moving)
3021 ResetGfxAnimation(x, y);
3023 if (!continues_moving)
3024 ResetGfxAnimation(x, y);
3027 MovDir[x][y] = direction;
3028 GfxDir[x][y] = direction;
3029 GfxAction[x][y] = (!is_moving ? ACTION_WAITING :
3030 direction == MV_DOWN && CAN_FALL(element) ?
3031 ACTION_FALLING : ACTION_MOVING);
3033 /* this is needed for CEs with property "can move" / "not moving" */
3037 if (Feld[newx][newy] == EL_EMPTY)
3038 Feld[newx][newy] = EL_BLOCKED;
3040 MovDir[newx][newy] = MovDir[x][y];
3042 #if USE_NEW_CUSTOM_VALUE
3043 CustomValue[newx][newy] = CustomValue[x][y];
3046 GfxFrame[newx][newy] = GfxFrame[x][y];
3047 GfxRandom[newx][newy] = GfxRandom[x][y];
3048 GfxAction[newx][newy] = GfxAction[x][y];
3049 GfxDir[newx][newy] = GfxDir[x][y];
3053 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3055 int direction = MovDir[x][y];
3056 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3057 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3063 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3065 int oldx = x, oldy = y;
3066 int direction = MovDir[x][y];
3068 if (direction == MV_LEFT)
3070 else if (direction == MV_RIGHT)
3072 else if (direction == MV_UP)
3074 else if (direction == MV_DOWN)
3077 *comes_from_x = oldx;
3078 *comes_from_y = oldy;
3081 int MovingOrBlocked2Element(int x, int y)
3083 int element = Feld[x][y];
3085 if (element == EL_BLOCKED)
3089 Blocked2Moving(x, y, &oldx, &oldy);
3090 return Feld[oldx][oldy];
3096 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3098 /* like MovingOrBlocked2Element(), but if element is moving
3099 and (x,y) is the field the moving element is just leaving,
3100 return EL_BLOCKED instead of the element value */
3101 int element = Feld[x][y];
3103 if (IS_MOVING(x, y))
3105 if (element == EL_BLOCKED)
3109 Blocked2Moving(x, y, &oldx, &oldy);
3110 return Feld[oldx][oldy];
3119 static void RemoveField(int x, int y)
3121 Feld[x][y] = EL_EMPTY;
3127 #if USE_NEW_CUSTOM_VALUE
3128 CustomValue[x][y] = 0;
3132 ChangeDelay[x][y] = 0;
3133 ChangePage[x][y] = -1;
3134 Pushed[x][y] = FALSE;
3137 ExplodeField[x][y] = EX_TYPE_NONE;
3140 GfxElement[x][y] = EL_UNDEFINED;
3141 GfxAction[x][y] = ACTION_DEFAULT;
3142 GfxDir[x][y] = MV_NONE;
3145 void RemoveMovingField(int x, int y)
3147 int oldx = x, oldy = y, newx = x, newy = y;
3148 int element = Feld[x][y];
3149 int next_element = EL_UNDEFINED;
3151 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3154 if (IS_MOVING(x, y))
3156 Moving2Blocked(x, y, &newx, &newy);
3158 if (Feld[newx][newy] != EL_BLOCKED)
3160 /* element is moving, but target field is not free (blocked), but
3161 already occupied by something different (example: acid pool);
3162 in this case, only remove the moving field, but not the target */
3164 RemoveField(oldx, oldy);
3166 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3168 DrawLevelField(oldx, oldy);
3173 else if (element == EL_BLOCKED)
3175 Blocked2Moving(x, y, &oldx, &oldy);
3176 if (!IS_MOVING(oldx, oldy))
3180 if (element == EL_BLOCKED &&
3181 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3182 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3183 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3184 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3185 next_element = get_next_element(Feld[oldx][oldy]);
3187 RemoveField(oldx, oldy);
3188 RemoveField(newx, newy);
3190 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3192 if (next_element != EL_UNDEFINED)
3193 Feld[oldx][oldy] = next_element;
3195 DrawLevelField(oldx, oldy);
3196 DrawLevelField(newx, newy);
3199 void DrawDynamite(int x, int y)
3201 int sx = SCREENX(x), sy = SCREENY(y);
3202 int graphic = el2img(Feld[x][y]);
3205 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3208 if (IS_WALKABLE_INSIDE(Back[x][y]))
3212 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3213 else if (Store[x][y])
3214 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3216 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3218 if (Back[x][y] || Store[x][y])
3219 DrawGraphicThruMask(sx, sy, graphic, frame);
3221 DrawGraphic(sx, sy, graphic, frame);
3224 void CheckDynamite(int x, int y)
3226 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3230 if (MovDelay[x][y] != 0)
3233 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3239 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3244 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3246 boolean num_checked_players = 0;
3249 for (i = 0; i < MAX_PLAYERS; i++)
3251 if (stored_player[i].active)
3253 int sx = stored_player[i].jx;
3254 int sy = stored_player[i].jy;
3256 if (num_checked_players == 0)
3263 *sx1 = MIN(*sx1, sx);
3264 *sy1 = MIN(*sy1, sy);
3265 *sx2 = MAX(*sx2, sx);
3266 *sy2 = MAX(*sy2, sy);
3269 num_checked_players++;
3274 static boolean checkIfAllPlayersFitToScreen_RND()
3276 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3278 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3280 return (sx2 - sx1 < SCR_FIELDX &&
3281 sy2 - sy1 < SCR_FIELDY);
3284 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3286 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3288 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3290 *sx = (sx1 + sx2) / 2;
3291 *sy = (sy1 + sy2) / 2;
3294 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3295 boolean quick_relocation)
3297 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3298 boolean no_delay = (tape.warp_forward);
3299 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3300 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3302 if (quick_relocation)
3304 int offset = (setup.scroll_delay ? 3 : 0);
3306 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3308 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3309 x > SBX_Right + MIDPOSX ? SBX_Right :
3312 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3313 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3318 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3319 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3320 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3322 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3323 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3324 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3326 /* don't scroll over playfield boundaries */
3327 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3328 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3330 /* don't scroll over playfield boundaries */
3331 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3332 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3335 RedrawPlayfield(TRUE, 0,0,0,0);
3339 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3340 x > SBX_Right + MIDPOSX ? SBX_Right :
3343 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3344 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3347 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3349 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3352 int fx = FX, fy = FY;
3354 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3355 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3357 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3363 fx += dx * TILEX / 2;
3364 fy += dy * TILEY / 2;
3366 ScrollLevel(dx, dy);
3369 /* scroll in two steps of half tile size to make things smoother */
3370 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3372 Delay(wait_delay_value);
3374 /* scroll second step to align at full tile size */
3376 Delay(wait_delay_value);
3381 Delay(wait_delay_value);
3385 void RelocatePlayer(int jx, int jy, int el_player_raw)
3387 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3388 int player_nr = GET_PLAYER_NR(el_player);
3389 struct PlayerInfo *player = &stored_player[player_nr];
3390 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3391 boolean no_delay = (tape.warp_forward);
3392 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3393 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3394 int old_jx = player->jx;
3395 int old_jy = player->jy;
3396 int old_element = Feld[old_jx][old_jy];
3397 int element = Feld[jx][jy];
3398 boolean player_relocated = (old_jx != jx || old_jy != jy);
3400 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3401 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3402 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3403 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3404 int leave_side_horiz = move_dir_horiz;
3405 int leave_side_vert = move_dir_vert;
3406 int enter_side = enter_side_horiz | enter_side_vert;
3407 int leave_side = leave_side_horiz | leave_side_vert;
3409 if (player->GameOver) /* do not reanimate dead player */
3412 if (!player_relocated) /* no need to relocate the player */
3415 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3417 RemoveField(jx, jy); /* temporarily remove newly placed player */
3418 DrawLevelField(jx, jy);
3421 if (player->present)
3423 while (player->MovPos)
3425 ScrollPlayer(player, SCROLL_GO_ON);
3426 ScrollScreen(NULL, SCROLL_GO_ON);
3428 AdvanceFrameAndPlayerCounters(player->index_nr);
3433 Delay(wait_delay_value);
3436 DrawPlayer(player); /* needed here only to cleanup last field */
3437 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3439 player->is_moving = FALSE;
3442 if (IS_CUSTOM_ELEMENT(old_element))
3443 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3445 player->index_bit, leave_side);
3447 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3449 player->index_bit, leave_side);
3451 Feld[jx][jy] = el_player;
3452 InitPlayerField(jx, jy, el_player, TRUE);
3454 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3456 Feld[jx][jy] = element;
3457 InitField(jx, jy, FALSE);
3460 /* only visually relocate centered player */
3461 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3462 level.instant_relocation);
3464 TestIfPlayerTouchesBadThing(jx, jy);
3465 TestIfPlayerTouchesCustomElement(jx, jy);
3467 if (IS_CUSTOM_ELEMENT(element))
3468 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3469 player->index_bit, enter_side);
3471 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3472 player->index_bit, enter_side);
3475 void Explode(int ex, int ey, int phase, int mode)
3481 /* !!! eliminate this variable !!! */
3482 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3484 if (game.explosions_delayed)
3486 ExplodeField[ex][ey] = mode;
3490 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3492 int center_element = Feld[ex][ey];
3493 int artwork_element, explosion_element; /* set these values later */
3496 /* --- This is only really needed (and now handled) in "Impact()". --- */
3497 /* do not explode moving elements that left the explode field in time */
3498 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3499 center_element == EL_EMPTY &&
3500 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3505 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3506 if (mode == EX_TYPE_NORMAL ||
3507 mode == EX_TYPE_CENTER ||
3508 mode == EX_TYPE_CROSS)
3509 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3512 /* remove things displayed in background while burning dynamite */
3513 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3516 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3518 /* put moving element to center field (and let it explode there) */
3519 center_element = MovingOrBlocked2Element(ex, ey);
3520 RemoveMovingField(ex, ey);
3521 Feld[ex][ey] = center_element;
3524 /* now "center_element" is finally determined -- set related values now */
3525 artwork_element = center_element; /* for custom player artwork */
3526 explosion_element = center_element; /* for custom player artwork */
3528 if (IS_PLAYER(ex, ey))
3530 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3532 artwork_element = stored_player[player_nr].artwork_element;
3534 if (level.use_explosion_element[player_nr])
3536 explosion_element = level.explosion_element[player_nr];
3537 artwork_element = explosion_element;
3542 if (mode == EX_TYPE_NORMAL ||
3543 mode == EX_TYPE_CENTER ||
3544 mode == EX_TYPE_CROSS)
3545 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3548 last_phase = element_info[explosion_element].explosion_delay + 1;
3550 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3552 int xx = x - ex + 1;
3553 int yy = y - ey + 1;
3556 if (!IN_LEV_FIELD(x, y) ||
3557 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3558 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3561 element = Feld[x][y];
3563 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3565 element = MovingOrBlocked2Element(x, y);
3567 if (!IS_EXPLOSION_PROOF(element))
3568 RemoveMovingField(x, y);
3571 /* indestructible elements can only explode in center (but not flames) */
3572 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3573 mode == EX_TYPE_BORDER)) ||
3574 element == EL_FLAMES)
3577 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3578 behaviour, for example when touching a yamyam that explodes to rocks
3579 with active deadly shield, a rock is created under the player !!! */
3580 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3582 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3583 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3584 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3586 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3589 if (IS_ACTIVE_BOMB(element))
3591 /* re-activate things under the bomb like gate or penguin */
3592 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3599 /* save walkable background elements while explosion on same tile */
3600 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3601 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3602 Back[x][y] = element;
3604 /* ignite explodable elements reached by other explosion */
3605 if (element == EL_EXPLOSION)
3606 element = Store2[x][y];
3608 if (AmoebaNr[x][y] &&
3609 (element == EL_AMOEBA_FULL ||
3610 element == EL_BD_AMOEBA ||
3611 element == EL_AMOEBA_GROWING))
3613 AmoebaCnt[AmoebaNr[x][y]]--;
3614 AmoebaCnt2[AmoebaNr[x][y]]--;
3619 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3621 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3623 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3625 if (PLAYERINFO(ex, ey)->use_murphy)
3626 Store[x][y] = EL_EMPTY;
3629 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3630 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3631 else if (ELEM_IS_PLAYER(center_element))
3632 Store[x][y] = EL_EMPTY;
3633 else if (center_element == EL_YAMYAM)
3634 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3635 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3636 Store[x][y] = element_info[center_element].content.e[xx][yy];
3638 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3639 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3640 otherwise) -- FIX THIS !!! */
3641 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3642 Store[x][y] = element_info[element].content.e[1][1];
3644 else if (!CAN_EXPLODE(element))
3645 Store[x][y] = element_info[element].content.e[1][1];
3648 Store[x][y] = EL_EMPTY;
3650 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3651 center_element == EL_AMOEBA_TO_DIAMOND)
3652 Store2[x][y] = element;
3654 Feld[x][y] = EL_EXPLOSION;
3655 GfxElement[x][y] = artwork_element;
3657 ExplodePhase[x][y] = 1;
3658 ExplodeDelay[x][y] = last_phase;
3663 if (center_element == EL_YAMYAM)
3664 game.yamyam_content_nr =
3665 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3677 GfxFrame[x][y] = 0; /* restart explosion animation */
3679 last_phase = ExplodeDelay[x][y];
3681 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3685 /* activate this even in non-DEBUG version until cause for crash in
3686 getGraphicAnimationFrame() (see below) is found and eliminated */
3692 /* this can happen if the player leaves an explosion just in time */
3693 if (GfxElement[x][y] == EL_UNDEFINED)
3694 GfxElement[x][y] = EL_EMPTY;
3696 if (GfxElement[x][y] == EL_UNDEFINED)
3699 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3700 printf("Explode(): This should never happen!\n");
3703 GfxElement[x][y] = EL_EMPTY;
3709 border_element = Store2[x][y];
3710 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3711 border_element = StorePlayer[x][y];
3713 if (phase == element_info[border_element].ignition_delay ||
3714 phase == last_phase)
3716 boolean border_explosion = FALSE;
3718 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3719 !PLAYER_EXPLOSION_PROTECTED(x, y))
3721 KillPlayerUnlessExplosionProtected(x, y);
3722 border_explosion = TRUE;
3724 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3726 Feld[x][y] = Store2[x][y];
3729 border_explosion = TRUE;
3731 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3733 AmoebeUmwandeln(x, y);
3735 border_explosion = TRUE;
3738 /* if an element just explodes due to another explosion (chain-reaction),
3739 do not immediately end the new explosion when it was the last frame of
3740 the explosion (as it would be done in the following "if"-statement!) */
3741 if (border_explosion && phase == last_phase)
3745 if (phase == last_phase)
3749 element = Feld[x][y] = Store[x][y];
3750 Store[x][y] = Store2[x][y] = 0;
3751 GfxElement[x][y] = EL_UNDEFINED;
3753 /* player can escape from explosions and might therefore be still alive */
3754 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3755 element <= EL_PLAYER_IS_EXPLODING_4)
3757 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3758 int explosion_element = EL_PLAYER_1 + player_nr;
3759 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3760 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3762 if (level.use_explosion_element[player_nr])
3763 explosion_element = level.explosion_element[player_nr];
3765 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3766 element_info[explosion_element].content.e[xx][yy]);
3769 /* restore probably existing indestructible background element */
3770 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3771 element = Feld[x][y] = Back[x][y];
3774 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3775 GfxDir[x][y] = MV_NONE;
3776 ChangeDelay[x][y] = 0;
3777 ChangePage[x][y] = -1;
3779 #if USE_NEW_CUSTOM_VALUE
3780 CustomValue[x][y] = 0;
3783 InitField_WithBug2(x, y, FALSE);
3785 DrawLevelField(x, y);
3787 TestIfElementTouchesCustomElement(x, y);
3789 if (GFX_CRUMBLED(element))
3790 DrawLevelFieldCrumbledSandNeighbours(x, y);
3792 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3793 StorePlayer[x][y] = 0;
3795 if (ELEM_IS_PLAYER(element))
3796 RelocatePlayer(x, y, element);
3798 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3800 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3801 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3804 DrawLevelFieldCrumbledSand(x, y);
3806 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3808 DrawLevelElement(x, y, Back[x][y]);
3809 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3811 else if (IS_WALKABLE_UNDER(Back[x][y]))
3813 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3814 DrawLevelElementThruMask(x, y, Back[x][y]);
3816 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3817 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3821 void DynaExplode(int ex, int ey)
3824 int dynabomb_element = Feld[ex][ey];
3825 int dynabomb_size = 1;
3826 boolean dynabomb_xl = FALSE;
3827 struct PlayerInfo *player;
3828 static int xy[4][2] =
3836 if (IS_ACTIVE_BOMB(dynabomb_element))
3838 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3839 dynabomb_size = player->dynabomb_size;
3840 dynabomb_xl = player->dynabomb_xl;
3841 player->dynabombs_left++;
3844 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3846 for (i = 0; i < NUM_DIRECTIONS; i++)
3848 for (j = 1; j <= dynabomb_size; j++)
3850 int x = ex + j * xy[i][0];
3851 int y = ey + j * xy[i][1];
3854 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3857 element = Feld[x][y];
3859 /* do not restart explosions of fields with active bombs */
3860 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3863 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3865 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3866 !IS_DIGGABLE(element) && !dynabomb_xl)
3872 void Bang(int x, int y)
3874 int element = MovingOrBlocked2Element(x, y);
3875 int explosion_type = EX_TYPE_NORMAL;
3877 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3879 struct PlayerInfo *player = PLAYERINFO(x, y);
3881 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3882 player->element_nr);
3884 if (level.use_explosion_element[player->index_nr])
3886 int explosion_element = level.explosion_element[player->index_nr];
3888 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3889 explosion_type = EX_TYPE_CROSS;
3890 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3891 explosion_type = EX_TYPE_CENTER;
3899 case EL_BD_BUTTERFLY:
3902 case EL_DARK_YAMYAM:
3906 RaiseScoreElement(element);
3909 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3910 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3911 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3912 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3913 case EL_DYNABOMB_INCREASE_NUMBER:
3914 case EL_DYNABOMB_INCREASE_SIZE:
3915 case EL_DYNABOMB_INCREASE_POWER:
3916 explosion_type = EX_TYPE_DYNA;
3921 case EL_LAMP_ACTIVE:
3922 case EL_AMOEBA_TO_DIAMOND:
3923 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3924 explosion_type = EX_TYPE_CENTER;
3928 if (element_info[element].explosion_type == EXPLODES_CROSS)
3929 explosion_type = EX_TYPE_CROSS;
3930 else if (element_info[element].explosion_type == EXPLODES_1X1)
3931 explosion_type = EX_TYPE_CENTER;
3935 if (explosion_type == EX_TYPE_DYNA)
3938 Explode(x, y, EX_PHASE_START, explosion_type);
3940 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3943 void SplashAcid(int x, int y)
3945 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3946 (!IN_LEV_FIELD(x - 1, y - 2) ||
3947 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3948 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3950 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3951 (!IN_LEV_FIELD(x + 1, y - 2) ||
3952 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3953 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3955 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3958 static void InitBeltMovement()
3960 static int belt_base_element[4] =
3962 EL_CONVEYOR_BELT_1_LEFT,
3963 EL_CONVEYOR_BELT_2_LEFT,
3964 EL_CONVEYOR_BELT_3_LEFT,
3965 EL_CONVEYOR_BELT_4_LEFT
3967 static int belt_base_active_element[4] =
3969 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3970 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3971 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3972 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3977 /* set frame order for belt animation graphic according to belt direction */
3978 for (i = 0; i < NUM_BELTS; i++)
3982 for (j = 0; j < NUM_BELT_PARTS; j++)
3984 int element = belt_base_active_element[belt_nr] + j;
3985 int graphic = el2img(element);
3987 if (game.belt_dir[i] == MV_LEFT)
3988 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3990 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3994 SCAN_PLAYFIELD(x, y)
3996 int element = Feld[x][y];
3998 for (i = 0; i < NUM_BELTS; i++)
4000 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4002 int e_belt_nr = getBeltNrFromBeltElement(element);
4005 if (e_belt_nr == belt_nr)
4007 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4009 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4016 static void ToggleBeltSwitch(int x, int y)
4018 static int belt_base_element[4] =
4020 EL_CONVEYOR_BELT_1_LEFT,
4021 EL_CONVEYOR_BELT_2_LEFT,
4022 EL_CONVEYOR_BELT_3_LEFT,
4023 EL_CONVEYOR_BELT_4_LEFT
4025 static int belt_base_active_element[4] =
4027 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4028 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4029 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4030 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4032 static int belt_base_switch_element[4] =
4034 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4035 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4036 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4037 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4039 static int belt_move_dir[4] =
4047 int element = Feld[x][y];
4048 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4049 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4050 int belt_dir = belt_move_dir[belt_dir_nr];
4053 if (!IS_BELT_SWITCH(element))
4056 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4057 game.belt_dir[belt_nr] = belt_dir;
4059 if (belt_dir_nr == 3)
4062 /* set frame order for belt animation graphic according to belt direction */
4063 for (i = 0; i < NUM_BELT_PARTS; i++)
4065 int element = belt_base_active_element[belt_nr] + i;
4066 int graphic = el2img(element);
4068 if (belt_dir == MV_LEFT)
4069 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4071 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4074 SCAN_PLAYFIELD(xx, yy)
4076 int element = Feld[xx][yy];
4078 if (IS_BELT_SWITCH(element))
4080 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4082 if (e_belt_nr == belt_nr)
4084 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4085 DrawLevelField(xx, yy);
4088 else if (IS_BELT(element) && belt_dir != MV_NONE)
4090 int e_belt_nr = getBeltNrFromBeltElement(element);
4092 if (e_belt_nr == belt_nr)
4094 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4096 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4097 DrawLevelField(xx, yy);
4100 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4102 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4104 if (e_belt_nr == belt_nr)
4106 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4108 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4109 DrawLevelField(xx, yy);
4115 static void ToggleSwitchgateSwitch(int x, int y)
4119 game.switchgate_pos = !game.switchgate_pos;
4121 SCAN_PLAYFIELD(xx, yy)
4123 int element = Feld[xx][yy];
4125 #if !USE_BOTH_SWITCHGATE_SWITCHES
4126 if (element == EL_SWITCHGATE_SWITCH_UP ||
4127 element == EL_SWITCHGATE_SWITCH_DOWN)
4129 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4130 DrawLevelField(xx, yy);
4133 if (element == EL_SWITCHGATE_SWITCH_UP)
4135 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4136 DrawLevelField(xx, yy);
4138 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4140 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4141 DrawLevelField(xx, yy);
4144 else if (element == EL_SWITCHGATE_OPEN ||
4145 element == EL_SWITCHGATE_OPENING)
4147 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4149 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4151 else if (element == EL_SWITCHGATE_CLOSED ||
4152 element == EL_SWITCHGATE_CLOSING)
4154 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4156 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4161 static int getInvisibleActiveFromInvisibleElement(int element)
4163 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4164 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4165 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4169 static int getInvisibleFromInvisibleActiveElement(int element)
4171 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4172 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4173 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4177 static void RedrawAllLightSwitchesAndInvisibleElements()
4181 SCAN_PLAYFIELD(x, y)
4183 int element = Feld[x][y];
4185 if (element == EL_LIGHT_SWITCH &&
4186 game.light_time_left > 0)
4188 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4189 DrawLevelField(x, y);
4191 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4192 game.light_time_left == 0)
4194 Feld[x][y] = EL_LIGHT_SWITCH;
4195 DrawLevelField(x, y);
4197 else if (element == EL_EMC_DRIPPER &&
4198 game.light_time_left > 0)
4200 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4201 DrawLevelField(x, y);
4203 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4204 game.light_time_left == 0)
4206 Feld[x][y] = EL_EMC_DRIPPER;
4207 DrawLevelField(x, y);
4209 else if (element == EL_INVISIBLE_STEELWALL ||
4210 element == EL_INVISIBLE_WALL ||
4211 element == EL_INVISIBLE_SAND)
4213 if (game.light_time_left > 0)
4214 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4216 DrawLevelField(x, y);
4218 /* uncrumble neighbour fields, if needed */
4219 if (element == EL_INVISIBLE_SAND)
4220 DrawLevelFieldCrumbledSandNeighbours(x, y);
4222 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4223 element == EL_INVISIBLE_WALL_ACTIVE ||
4224 element == EL_INVISIBLE_SAND_ACTIVE)
4226 if (game.light_time_left == 0)
4227 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4229 DrawLevelField(x, y);
4231 /* re-crumble neighbour fields, if needed */
4232 if (element == EL_INVISIBLE_SAND)
4233 DrawLevelFieldCrumbledSandNeighbours(x, y);
4238 static void RedrawAllInvisibleElementsForLenses()
4242 SCAN_PLAYFIELD(x, y)
4244 int element = Feld[x][y];
4246 if (element == EL_EMC_DRIPPER &&
4247 game.lenses_time_left > 0)
4249 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4250 DrawLevelField(x, y);
4252 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4253 game.lenses_time_left == 0)
4255 Feld[x][y] = EL_EMC_DRIPPER;
4256 DrawLevelField(x, y);
4258 else if (element == EL_INVISIBLE_STEELWALL ||
4259 element == EL_INVISIBLE_WALL ||
4260 element == EL_INVISIBLE_SAND)
4262 if (game.lenses_time_left > 0)
4263 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4265 DrawLevelField(x, y);
4267 /* uncrumble neighbour fields, if needed */
4268 if (element == EL_INVISIBLE_SAND)
4269 DrawLevelFieldCrumbledSandNeighbours(x, y);
4271 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4272 element == EL_INVISIBLE_WALL_ACTIVE ||
4273 element == EL_INVISIBLE_SAND_ACTIVE)
4275 if (game.lenses_time_left == 0)
4276 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4278 DrawLevelField(x, y);
4280 /* re-crumble neighbour fields, if needed */
4281 if (element == EL_INVISIBLE_SAND)
4282 DrawLevelFieldCrumbledSandNeighbours(x, y);
4287 static void RedrawAllInvisibleElementsForMagnifier()
4291 SCAN_PLAYFIELD(x, y)
4293 int element = Feld[x][y];
4295 if (element == EL_EMC_FAKE_GRASS &&
4296 game.magnify_time_left > 0)
4298 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4299 DrawLevelField(x, y);
4301 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4302 game.magnify_time_left == 0)
4304 Feld[x][y] = EL_EMC_FAKE_GRASS;
4305 DrawLevelField(x, y);
4307 else if (IS_GATE_GRAY(element) &&
4308 game.magnify_time_left > 0)
4310 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4311 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4312 IS_EM_GATE_GRAY(element) ?
4313 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4314 IS_EMC_GATE_GRAY(element) ?
4315 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4317 DrawLevelField(x, y);
4319 else if (IS_GATE_GRAY_ACTIVE(element) &&
4320 game.magnify_time_left == 0)
4322 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4323 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4324 IS_EM_GATE_GRAY_ACTIVE(element) ?
4325 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4326 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4327 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4329 DrawLevelField(x, y);
4334 static void ToggleLightSwitch(int x, int y)
4336 int element = Feld[x][y];
4338 game.light_time_left =
4339 (element == EL_LIGHT_SWITCH ?
4340 level.time_light * FRAMES_PER_SECOND : 0);
4342 RedrawAllLightSwitchesAndInvisibleElements();
4345 static void ActivateTimegateSwitch(int x, int y)
4349 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4351 SCAN_PLAYFIELD(xx, yy)
4353 int element = Feld[xx][yy];
4355 if (element == EL_TIMEGATE_CLOSED ||
4356 element == EL_TIMEGATE_CLOSING)
4358 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4359 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4363 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4365 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4366 DrawLevelField(xx, yy);
4372 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4375 void Impact(int x, int y)
4377 boolean last_line = (y == lev_fieldy - 1);
4378 boolean object_hit = FALSE;
4379 boolean impact = (last_line || object_hit);
4380 int element = Feld[x][y];
4381 int smashed = EL_STEELWALL;
4383 if (!last_line) /* check if element below was hit */
4385 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4388 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4389 MovDir[x][y + 1] != MV_DOWN ||
4390 MovPos[x][y + 1] <= TILEY / 2));
4392 /* do not smash moving elements that left the smashed field in time */
4393 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4394 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4397 #if USE_QUICKSAND_IMPACT_BUGFIX
4398 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4400 RemoveMovingField(x, y + 1);
4401 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4402 Feld[x][y + 2] = EL_ROCK;
4403 DrawLevelField(x, y + 2);
4410 smashed = MovingOrBlocked2Element(x, y + 1);
4412 impact = (last_line || object_hit);
4415 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4417 SplashAcid(x, y + 1);
4421 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4422 /* only reset graphic animation if graphic really changes after impact */
4424 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4426 ResetGfxAnimation(x, y);
4427 DrawLevelField(x, y);
4430 if (impact && CAN_EXPLODE_IMPACT(element))
4435 else if (impact && element == EL_PEARL)
4437 ResetGfxAnimation(x, y);
4439 Feld[x][y] = EL_PEARL_BREAKING;
4440 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4443 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4445 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4450 if (impact && element == EL_AMOEBA_DROP)
4452 if (object_hit && IS_PLAYER(x, y + 1))
4453 KillPlayerUnlessEnemyProtected(x, y + 1);
4454 else if (object_hit && smashed == EL_PENGUIN)
4458 Feld[x][y] = EL_AMOEBA_GROWING;
4459 Store[x][y] = EL_AMOEBA_WET;
4461 ResetRandomAnimationValue(x, y);
4466 if (object_hit) /* check which object was hit */
4468 if (CAN_PASS_MAGIC_WALL(element) &&
4469 (smashed == EL_MAGIC_WALL ||
4470 smashed == EL_BD_MAGIC_WALL))
4473 int activated_magic_wall =
4474 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4475 EL_BD_MAGIC_WALL_ACTIVE);
4477 /* activate magic wall / mill */
4478 SCAN_PLAYFIELD(xx, yy)
4479 if (Feld[xx][yy] == smashed)
4480 Feld[xx][yy] = activated_magic_wall;
4482 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4483 game.magic_wall_active = TRUE;
4485 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4486 SND_MAGIC_WALL_ACTIVATING :
4487 SND_BD_MAGIC_WALL_ACTIVATING));
4490 if (IS_PLAYER(x, y + 1))
4492 if (CAN_SMASH_PLAYER(element))
4494 KillPlayerUnlessEnemyProtected(x, y + 1);
4498 else if (smashed == EL_PENGUIN)
4500 if (CAN_SMASH_PLAYER(element))
4506 else if (element == EL_BD_DIAMOND)
4508 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4514 else if (((element == EL_SP_INFOTRON ||
4515 element == EL_SP_ZONK) &&
4516 (smashed == EL_SP_SNIKSNAK ||
4517 smashed == EL_SP_ELECTRON ||
4518 smashed == EL_SP_DISK_ORANGE)) ||
4519 (element == EL_SP_INFOTRON &&
4520 smashed == EL_SP_DISK_YELLOW))
4525 else if (CAN_SMASH_EVERYTHING(element))
4527 if (IS_CLASSIC_ENEMY(smashed) ||
4528 CAN_EXPLODE_SMASHED(smashed))
4533 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4535 if (smashed == EL_LAMP ||
4536 smashed == EL_LAMP_ACTIVE)
4541 else if (smashed == EL_NUT)
4543 Feld[x][y + 1] = EL_NUT_BREAKING;
4544 PlayLevelSound(x, y, SND_NUT_BREAKING);
4545 RaiseScoreElement(EL_NUT);
4548 else if (smashed == EL_PEARL)
4550 ResetGfxAnimation(x, y);
4552 Feld[x][y + 1] = EL_PEARL_BREAKING;
4553 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4556 else if (smashed == EL_DIAMOND)
4558 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4559 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4562 else if (IS_BELT_SWITCH(smashed))
4564 ToggleBeltSwitch(x, y + 1);
4566 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4567 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4569 ToggleSwitchgateSwitch(x, y + 1);
4571 else if (smashed == EL_LIGHT_SWITCH ||
4572 smashed == EL_LIGHT_SWITCH_ACTIVE)
4574 ToggleLightSwitch(x, y + 1);
4579 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4582 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4584 CheckElementChangeBySide(x, y + 1, smashed, element,
4585 CE_SWITCHED, CH_SIDE_TOP);
4586 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4592 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4597 /* play sound of magic wall / mill */
4599 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4600 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4602 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4603 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4604 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4605 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4610 /* play sound of object that hits the ground */
4611 if (last_line || object_hit)
4612 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4615 inline static void TurnRoundExt(int x, int y)
4627 { 0, 0 }, { 0, 0 }, { 0, 0 },
4632 int left, right, back;
4636 { MV_DOWN, MV_UP, MV_RIGHT },
4637 { MV_UP, MV_DOWN, MV_LEFT },
4639 { MV_LEFT, MV_RIGHT, MV_DOWN },
4643 { MV_RIGHT, MV_LEFT, MV_UP }
4646 int element = Feld[x][y];
4647 int move_pattern = element_info[element].move_pattern;
4649 int old_move_dir = MovDir[x][y];
4650 int left_dir = turn[old_move_dir].left;
4651 int right_dir = turn[old_move_dir].right;
4652 int back_dir = turn[old_move_dir].back;
4654 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4655 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4656 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4657 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4659 int left_x = x + left_dx, left_y = y + left_dy;
4660 int right_x = x + right_dx, right_y = y + right_dy;
4661 int move_x = x + move_dx, move_y = y + move_dy;
4665 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4667 TestIfBadThingTouchesOtherBadThing(x, y);
4669 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4670 MovDir[x][y] = right_dir;
4671 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4672 MovDir[x][y] = left_dir;
4674 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4676 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4679 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4681 TestIfBadThingTouchesOtherBadThing(x, y);
4683 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4684 MovDir[x][y] = left_dir;
4685 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4686 MovDir[x][y] = right_dir;
4688 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4690 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4693 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4695 TestIfBadThingTouchesOtherBadThing(x, y);
4697 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4698 MovDir[x][y] = left_dir;
4699 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4700 MovDir[x][y] = right_dir;
4702 if (MovDir[x][y] != old_move_dir)
4705 else if (element == EL_YAMYAM)
4707 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4708 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4710 if (can_turn_left && can_turn_right)
4711 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4712 else if (can_turn_left)
4713 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4714 else if (can_turn_right)
4715 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4717 MovDir[x][y] = back_dir;
4719 MovDelay[x][y] = 16 + 16 * RND(3);
4721 else if (element == EL_DARK_YAMYAM)
4723 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4725 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4728 if (can_turn_left && can_turn_right)
4729 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4730 else if (can_turn_left)
4731 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4732 else if (can_turn_right)
4733 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4735 MovDir[x][y] = back_dir;
4737 MovDelay[x][y] = 16 + 16 * RND(3);
4739 else if (element == EL_PACMAN)
4741 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4742 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4744 if (can_turn_left && can_turn_right)
4745 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4746 else if (can_turn_left)
4747 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4748 else if (can_turn_right)
4749 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4751 MovDir[x][y] = back_dir;
4753 MovDelay[x][y] = 6 + RND(40);
4755 else if (element == EL_PIG)
4757 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4758 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4759 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4760 boolean should_turn_left, should_turn_right, should_move_on;
4762 int rnd = RND(rnd_value);
4764 should_turn_left = (can_turn_left &&
4766 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4767 y + back_dy + left_dy)));
4768 should_turn_right = (can_turn_right &&
4770 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4771 y + back_dy + right_dy)));
4772 should_move_on = (can_move_on &&
4775 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4776 y + move_dy + left_dy) ||
4777 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4778 y + move_dy + right_dy)));
4780 if (should_turn_left || should_turn_right || should_move_on)
4782 if (should_turn_left && should_turn_right && should_move_on)
4783 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4784 rnd < 2 * rnd_value / 3 ? right_dir :
4786 else if (should_turn_left && should_turn_right)
4787 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4788 else if (should_turn_left && should_move_on)
4789 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4790 else if (should_turn_right && should_move_on)
4791 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4792 else if (should_turn_left)
4793 MovDir[x][y] = left_dir;
4794 else if (should_turn_right)
4795 MovDir[x][y] = right_dir;
4796 else if (should_move_on)
4797 MovDir[x][y] = old_move_dir;
4799 else if (can_move_on && rnd > rnd_value / 8)
4800 MovDir[x][y] = old_move_dir;
4801 else if (can_turn_left && can_turn_right)
4802 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4803 else if (can_turn_left && rnd > rnd_value / 8)
4804 MovDir[x][y] = left_dir;
4805 else if (can_turn_right && rnd > rnd_value/8)
4806 MovDir[x][y] = right_dir;
4808 MovDir[x][y] = back_dir;
4810 xx = x + move_xy[MovDir[x][y]].dx;
4811 yy = y + move_xy[MovDir[x][y]].dy;
4813 if (!IN_LEV_FIELD(xx, yy) ||
4814 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4815 MovDir[x][y] = old_move_dir;
4819 else if (element == EL_DRAGON)
4821 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4822 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4823 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4825 int rnd = RND(rnd_value);
4827 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_AND_IS_FREE(xx, yy))
4842 MovDir[x][y] = old_move_dir;
4846 else if (element == EL_MOLE)
4848 boolean can_move_on =
4849 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4850 IS_AMOEBOID(Feld[move_x][move_y]) ||
4851 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4854 boolean can_turn_left =
4855 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4856 IS_AMOEBOID(Feld[left_x][left_y])));
4858 boolean can_turn_right =
4859 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4860 IS_AMOEBOID(Feld[right_x][right_y])));
4862 if (can_turn_left && can_turn_right)
4863 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4864 else if (can_turn_left)
4865 MovDir[x][y] = left_dir;
4867 MovDir[x][y] = right_dir;
4870 if (MovDir[x][y] != old_move_dir)
4873 else if (element == EL_BALLOON)
4875 MovDir[x][y] = game.wind_direction;
4878 else if (element == EL_SPRING)
4880 #if USE_NEW_SPRING_BUMPER
4881 if (MovDir[x][y] & MV_HORIZONTAL)
4883 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4884 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4886 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4887 ResetGfxAnimation(move_x, move_y);
4888 DrawLevelField(move_x, move_y);
4890 MovDir[x][y] = back_dir;
4892 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4893 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4894 MovDir[x][y] = MV_NONE;
4897 if (MovDir[x][y] & MV_HORIZONTAL &&
4898 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4899 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4900 MovDir[x][y] = MV_NONE;
4905 else if (element == EL_ROBOT ||
4906 element == EL_SATELLITE ||
4907 element == EL_PENGUIN ||
4908 element == EL_EMC_ANDROID)
4910 int attr_x = -1, attr_y = -1;
4921 for (i = 0; i < MAX_PLAYERS; i++)
4923 struct PlayerInfo *player = &stored_player[i];
4924 int jx = player->jx, jy = player->jy;
4926 if (!player->active)
4930 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4938 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4939 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4940 game.engine_version < VERSION_IDENT(3,1,0,0)))
4946 if (element == EL_PENGUIN)
4949 static int xy[4][2] =
4957 for (i = 0; i < NUM_DIRECTIONS; i++)
4959 int ex = x + xy[i][0];
4960 int ey = y + xy[i][1];
4962 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4971 MovDir[x][y] = MV_NONE;
4973 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4974 else if (attr_x > x)
4975 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4977 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4978 else if (attr_y > y)
4979 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4981 if (element == EL_ROBOT)
4985 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4986 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4987 Moving2Blocked(x, y, &newx, &newy);
4989 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4990 MovDelay[x][y] = 8 + 8 * !RND(3);
4992 MovDelay[x][y] = 16;
4994 else if (element == EL_PENGUIN)
5000 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5002 boolean first_horiz = RND(2);
5003 int new_move_dir = MovDir[x][y];
5006 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5007 Moving2Blocked(x, y, &newx, &newy);
5009 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5013 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5014 Moving2Blocked(x, y, &newx, &newy);
5016 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5019 MovDir[x][y] = old_move_dir;
5023 else if (element == EL_SATELLITE)
5029 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5031 boolean first_horiz = RND(2);
5032 int new_move_dir = MovDir[x][y];
5035 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5036 Moving2Blocked(x, y, &newx, &newy);
5038 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5042 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5043 Moving2Blocked(x, y, &newx, &newy);
5045 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5048 MovDir[x][y] = old_move_dir;
5052 else if (element == EL_EMC_ANDROID)
5054 static int check_pos[16] =
5056 -1, /* 0 => (invalid) */
5057 7, /* 1 => MV_LEFT */
5058 3, /* 2 => MV_RIGHT */
5059 -1, /* 3 => (invalid) */
5061 0, /* 5 => MV_LEFT | MV_UP */
5062 2, /* 6 => MV_RIGHT | MV_UP */
5063 -1, /* 7 => (invalid) */
5064 5, /* 8 => MV_DOWN */
5065 6, /* 9 => MV_LEFT | MV_DOWN */
5066 4, /* 10 => MV_RIGHT | MV_DOWN */
5067 -1, /* 11 => (invalid) */
5068 -1, /* 12 => (invalid) */
5069 -1, /* 13 => (invalid) */
5070 -1, /* 14 => (invalid) */
5071 -1, /* 15 => (invalid) */
5079 { -1, -1, MV_LEFT | MV_UP },
5081 { +1, -1, MV_RIGHT | MV_UP },
5082 { +1, 0, MV_RIGHT },
5083 { +1, +1, MV_RIGHT | MV_DOWN },
5085 { -1, +1, MV_LEFT | MV_DOWN },
5088 int start_pos, check_order;
5089 boolean can_clone = FALSE;
5092 /* check if there is any free field around current position */
5093 for (i = 0; i < 8; i++)
5095 int newx = x + check_xy[i].dx;
5096 int newy = y + check_xy[i].dy;
5098 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5106 if (can_clone) /* randomly find an element to clone */
5110 start_pos = check_pos[RND(8)];
5111 check_order = (RND(2) ? -1 : +1);
5113 for (i = 0; i < 8; i++)
5115 int pos_raw = start_pos + i * check_order;
5116 int pos = (pos_raw + 8) % 8;
5117 int newx = x + check_xy[pos].dx;
5118 int newy = y + check_xy[pos].dy;
5120 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5122 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5123 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5125 Store[x][y] = Feld[newx][newy];
5134 if (can_clone) /* randomly find a direction to move */
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;
5147 int new_move_dir = check_xy[pos].dir;
5149 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5151 MovDir[x][y] = new_move_dir;
5152 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5161 if (can_clone) /* cloning and moving successful */
5164 /* cannot clone -- try to move towards player */
5166 start_pos = check_pos[MovDir[x][y] & 0x0f];
5167 check_order = (RND(2) ? -1 : +1);
5169 for (i = 0; i < 3; i++)
5171 /* first check start_pos, then previous/next or (next/previous) pos */
5172 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5173 int pos = (pos_raw + 8) % 8;
5174 int newx = x + check_xy[pos].dx;
5175 int newy = y + check_xy[pos].dy;
5176 int new_move_dir = check_xy[pos].dir;
5178 if (IS_PLAYER(newx, newy))
5181 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5183 MovDir[x][y] = new_move_dir;
5184 MovDelay[x][y] = level.android_move_time * 8 + 1;
5191 else if (move_pattern == MV_TURNING_LEFT ||
5192 move_pattern == MV_TURNING_RIGHT ||
5193 move_pattern == MV_TURNING_LEFT_RIGHT ||
5194 move_pattern == MV_TURNING_RIGHT_LEFT ||
5195 move_pattern == MV_TURNING_RANDOM ||
5196 move_pattern == MV_ALL_DIRECTIONS)
5198 boolean can_turn_left =
5199 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5200 boolean can_turn_right =
5201 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5203 if (element_info[element].move_stepsize == 0) /* "not moving" */
5206 if (move_pattern == MV_TURNING_LEFT)
5207 MovDir[x][y] = left_dir;
5208 else if (move_pattern == MV_TURNING_RIGHT)
5209 MovDir[x][y] = right_dir;
5210 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5211 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5212 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5213 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5214 else if (move_pattern == MV_TURNING_RANDOM)
5215 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5216 can_turn_right && !can_turn_left ? right_dir :
5217 RND(2) ? left_dir : right_dir);
5218 else if (can_turn_left && can_turn_right)
5219 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5220 else if (can_turn_left)
5221 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5222 else if (can_turn_right)
5223 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5225 MovDir[x][y] = back_dir;
5227 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5229 else if (move_pattern == MV_HORIZONTAL ||
5230 move_pattern == MV_VERTICAL)
5232 if (move_pattern & old_move_dir)
5233 MovDir[x][y] = back_dir;
5234 else if (move_pattern == MV_HORIZONTAL)
5235 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5236 else if (move_pattern == MV_VERTICAL)
5237 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5239 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5241 else if (move_pattern & MV_ANY_DIRECTION)
5243 MovDir[x][y] = move_pattern;
5244 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5246 else if (move_pattern & MV_WIND_DIRECTION)
5248 MovDir[x][y] = game.wind_direction;
5249 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5251 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5253 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5254 MovDir[x][y] = left_dir;
5255 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5256 MovDir[x][y] = right_dir;
5258 if (MovDir[x][y] != old_move_dir)
5259 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5261 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5263 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5264 MovDir[x][y] = right_dir;
5265 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5266 MovDir[x][y] = left_dir;
5268 if (MovDir[x][y] != old_move_dir)
5269 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5271 else if (move_pattern == MV_TOWARDS_PLAYER ||
5272 move_pattern == MV_AWAY_FROM_PLAYER)
5274 int attr_x = -1, attr_y = -1;
5276 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5287 for (i = 0; i < MAX_PLAYERS; i++)
5289 struct PlayerInfo *player = &stored_player[i];
5290 int jx = player->jx, jy = player->jy;
5292 if (!player->active)
5296 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5304 MovDir[x][y] = MV_NONE;
5306 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5307 else if (attr_x > x)
5308 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5310 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5311 else if (attr_y > y)
5312 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5314 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5316 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5318 boolean first_horiz = RND(2);
5319 int new_move_dir = MovDir[x][y];
5321 if (element_info[element].move_stepsize == 0) /* "not moving" */
5323 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5324 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5330 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5331 Moving2Blocked(x, y, &newx, &newy);
5333 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5337 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5338 Moving2Blocked(x, y, &newx, &newy);
5340 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5343 MovDir[x][y] = old_move_dir;
5346 else if (move_pattern == MV_WHEN_PUSHED ||
5347 move_pattern == MV_WHEN_DROPPED)
5349 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5350 MovDir[x][y] = MV_NONE;
5354 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5356 static int test_xy[7][2] =
5366 static int test_dir[7] =
5376 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5377 int move_preference = -1000000; /* start with very low preference */
5378 int new_move_dir = MV_NONE;
5379 int start_test = RND(4);
5382 for (i = 0; i < NUM_DIRECTIONS; i++)
5384 int move_dir = test_dir[start_test + i];
5385 int move_dir_preference;
5387 xx = x + test_xy[start_test + i][0];
5388 yy = y + test_xy[start_test + i][1];
5390 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5391 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5393 new_move_dir = move_dir;
5398 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5401 move_dir_preference = -1 * RunnerVisit[xx][yy];
5402 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5403 move_dir_preference = PlayerVisit[xx][yy];
5405 if (move_dir_preference > move_preference)
5407 /* prefer field that has not been visited for the longest time */
5408 move_preference = move_dir_preference;
5409 new_move_dir = move_dir;
5411 else if (move_dir_preference == move_preference &&
5412 move_dir == old_move_dir)
5414 /* prefer last direction when all directions are preferred equally */
5415 move_preference = move_dir_preference;
5416 new_move_dir = move_dir;
5420 MovDir[x][y] = new_move_dir;
5421 if (old_move_dir != new_move_dir)
5422 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5426 static void TurnRound(int x, int y)
5428 int direction = MovDir[x][y];
5432 GfxDir[x][y] = MovDir[x][y];
5434 if (direction != MovDir[x][y])
5438 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5440 ResetGfxFrame(x, y, FALSE);
5443 static boolean JustBeingPushed(int x, int y)
5447 for (i = 0; i < MAX_PLAYERS; i++)
5449 struct PlayerInfo *player = &stored_player[i];
5451 if (player->active && player->is_pushing && player->MovPos)
5453 int next_jx = player->jx + (player->jx - player->last_jx);
5454 int next_jy = player->jy + (player->jy - player->last_jy);
5456 if (x == next_jx && y == next_jy)
5464 void StartMoving(int x, int y)
5466 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5467 int element = Feld[x][y];
5472 if (MovDelay[x][y] == 0)
5473 GfxAction[x][y] = ACTION_DEFAULT;
5475 if (CAN_FALL(element) && y < lev_fieldy - 1)
5477 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5478 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5479 if (JustBeingPushed(x, y))
5482 if (element == EL_QUICKSAND_FULL)
5484 if (IS_FREE(x, y + 1))
5486 InitMovingField(x, y, MV_DOWN);
5487 started_moving = TRUE;
5489 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5490 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5491 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5492 Store[x][y] = EL_ROCK;
5494 Store[x][y] = EL_ROCK;
5497 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5499 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5501 if (!MovDelay[x][y])
5502 MovDelay[x][y] = TILEY + 1;
5511 Feld[x][y] = EL_QUICKSAND_EMPTY;
5512 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5513 Store[x][y + 1] = Store[x][y];
5516 PlayLevelSoundAction(x, y, ACTION_FILLING);
5519 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5520 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5522 InitMovingField(x, y, MV_DOWN);
5523 started_moving = TRUE;
5525 Feld[x][y] = EL_QUICKSAND_FILLING;
5526 Store[x][y] = element;
5528 PlayLevelSoundAction(x, y, ACTION_FILLING);
5530 else if (element == EL_MAGIC_WALL_FULL)
5532 if (IS_FREE(x, y + 1))
5534 InitMovingField(x, y, MV_DOWN);
5535 started_moving = TRUE;
5537 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5538 Store[x][y] = EL_CHANGED(Store[x][y]);
5540 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5542 if (!MovDelay[x][y])
5543 MovDelay[x][y] = TILEY/4 + 1;
5552 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5553 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5554 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5558 else if (element == EL_BD_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_BD_MAGIC_WALL_EMPTYING;
5566 Store[x][y] = EL_CHANGED2(Store[x][y]);
5568 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5570 if (!MovDelay[x][y])
5571 MovDelay[x][y] = TILEY/4 + 1;
5580 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5581 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5582 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5586 else if (CAN_PASS_MAGIC_WALL(element) &&
5587 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5588 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5590 InitMovingField(x, y, MV_DOWN);
5591 started_moving = TRUE;
5594 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5595 EL_BD_MAGIC_WALL_FILLING);
5596 Store[x][y] = element;
5598 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5600 SplashAcid(x, y + 1);
5602 InitMovingField(x, y, MV_DOWN);
5603 started_moving = TRUE;
5605 Store[x][y] = EL_ACID;
5607 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5608 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5610 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5611 CAN_FALL(element) && WasJustFalling[x][y] &&
5612 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5614 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5615 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5616 (Feld[x][y + 1] == EL_BLOCKED)))
5618 /* this is needed for a special case not covered by calling "Impact()"
5619 from "ContinueMoving()": if an element moves to a tile directly below
5620 another element which was just falling on that tile (which was empty
5621 in the previous frame), the falling element above would just stop
5622 instead of smashing the element below (in previous version, the above
5623 element was just checked for "moving" instead of "falling", resulting
5624 in incorrect smashes caused by horizontal movement of the above
5625 element; also, the case of the player being the element to smash was
5626 simply not covered here... :-/ ) */
5628 CheckCollision[x][y] = 0;
5632 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5634 if (MovDir[x][y] == MV_NONE)
5636 InitMovingField(x, y, MV_DOWN);
5637 started_moving = TRUE;
5640 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5642 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5643 MovDir[x][y] = MV_DOWN;
5645 InitMovingField(x, y, MV_DOWN);
5646 started_moving = TRUE;
5648 else if (element == EL_AMOEBA_DROP)
5650 Feld[x][y] = EL_AMOEBA_GROWING;
5651 Store[x][y] = EL_AMOEBA_WET;
5653 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5654 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5655 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5656 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5658 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5659 (IS_FREE(x - 1, y + 1) ||
5660 Feld[x - 1][y + 1] == EL_ACID));
5661 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5662 (IS_FREE(x + 1, y + 1) ||
5663 Feld[x + 1][y + 1] == EL_ACID));
5664 boolean can_fall_any = (can_fall_left || can_fall_right);
5665 boolean can_fall_both = (can_fall_left && can_fall_right);
5666 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5668 #if USE_NEW_ALL_SLIPPERY
5669 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5671 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5672 can_fall_right = FALSE;
5673 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5674 can_fall_left = FALSE;
5675 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5676 can_fall_right = FALSE;
5677 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5678 can_fall_left = FALSE;
5680 can_fall_any = (can_fall_left || can_fall_right);
5681 can_fall_both = FALSE;
5684 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5686 if (slippery_type == SLIPPERY_ONLY_LEFT)
5687 can_fall_right = FALSE;
5688 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5689 can_fall_left = FALSE;
5690 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5691 can_fall_right = FALSE;
5692 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5693 can_fall_left = FALSE;
5695 can_fall_any = (can_fall_left || can_fall_right);
5696 can_fall_both = (can_fall_left && can_fall_right);
5700 #if USE_NEW_ALL_SLIPPERY
5702 #if USE_NEW_SP_SLIPPERY
5703 /* !!! better use the same properties as for custom elements here !!! */
5704 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5705 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5707 can_fall_right = FALSE; /* slip down on left side */
5708 can_fall_both = FALSE;
5713 #if USE_NEW_ALL_SLIPPERY
5716 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5717 can_fall_right = FALSE; /* slip down on left side */
5719 can_fall_left = !(can_fall_right = RND(2));
5721 can_fall_both = FALSE;
5726 if (game.emulation == EMU_BOULDERDASH ||
5727 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5728 can_fall_right = FALSE; /* slip down on left side */
5730 can_fall_left = !(can_fall_right = RND(2));
5732 can_fall_both = FALSE;
5738 /* if not determined otherwise, prefer left side for slipping down */
5739 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5740 started_moving = TRUE;
5744 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5746 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5749 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5750 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5751 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5752 int belt_dir = game.belt_dir[belt_nr];
5754 if ((belt_dir == MV_LEFT && left_is_free) ||
5755 (belt_dir == MV_RIGHT && right_is_free))
5757 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5759 InitMovingField(x, y, belt_dir);
5760 started_moving = TRUE;
5762 Pushed[x][y] = TRUE;
5763 Pushed[nextx][y] = TRUE;
5765 GfxAction[x][y] = ACTION_DEFAULT;
5769 MovDir[x][y] = 0; /* if element was moving, stop it */
5774 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5776 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5778 if (CAN_MOVE(element) && !started_moving)
5781 int move_pattern = element_info[element].move_pattern;
5786 if (MovDir[x][y] == MV_NONE)
5788 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5789 x, y, element, element_info[element].token_name);
5790 printf("StartMoving(): This should never happen!\n");
5795 Moving2Blocked(x, y, &newx, &newy);
5797 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5800 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5801 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5803 WasJustMoving[x][y] = 0;
5804 CheckCollision[x][y] = 0;
5806 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5808 if (Feld[x][y] != element) /* element has changed */
5812 if (!MovDelay[x][y]) /* start new movement phase */
5814 /* all objects that can change their move direction after each step
5815 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5817 if (element != EL_YAMYAM &&
5818 element != EL_DARK_YAMYAM &&
5819 element != EL_PACMAN &&
5820 !(move_pattern & MV_ANY_DIRECTION) &&
5821 move_pattern != MV_TURNING_LEFT &&
5822 move_pattern != MV_TURNING_RIGHT &&
5823 move_pattern != MV_TURNING_LEFT_RIGHT &&
5824 move_pattern != MV_TURNING_RIGHT_LEFT &&
5825 move_pattern != MV_TURNING_RANDOM)
5829 if (MovDelay[x][y] && (element == EL_BUG ||
5830 element == EL_SPACESHIP ||
5831 element == EL_SP_SNIKSNAK ||
5832 element == EL_SP_ELECTRON ||
5833 element == EL_MOLE))
5834 DrawLevelField(x, y);
5838 if (MovDelay[x][y]) /* wait some time before next movement */
5842 if (element == EL_ROBOT ||
5843 element == EL_YAMYAM ||
5844 element == EL_DARK_YAMYAM)
5846 DrawLevelElementAnimationIfNeeded(x, y, element);
5847 PlayLevelSoundAction(x, y, ACTION_WAITING);
5849 else if (element == EL_SP_ELECTRON)
5850 DrawLevelElementAnimationIfNeeded(x, y, element);
5851 else if (element == EL_DRAGON)
5854 int dir = MovDir[x][y];
5855 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5856 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5857 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5858 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5859 dir == MV_UP ? IMG_FLAMES_1_UP :
5860 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5861 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5863 GfxAction[x][y] = ACTION_ATTACKING;
5865 if (IS_PLAYER(x, y))
5866 DrawPlayerField(x, y);
5868 DrawLevelField(x, y);
5870 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5872 for (i = 1; i <= 3; i++)
5874 int xx = x + i * dx;
5875 int yy = y + i * dy;
5876 int sx = SCREENX(xx);
5877 int sy = SCREENY(yy);
5878 int flame_graphic = graphic + (i - 1);
5880 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5885 int flamed = MovingOrBlocked2Element(xx, yy);
5889 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5891 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5892 RemoveMovingField(xx, yy);
5894 RemoveField(xx, yy);
5896 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5899 RemoveMovingField(xx, yy);
5902 ChangeDelay[xx][yy] = 0;
5904 Feld[xx][yy] = EL_FLAMES;
5906 if (IN_SCR_FIELD(sx, sy))
5908 DrawLevelFieldCrumbledSand(xx, yy);
5909 DrawGraphic(sx, sy, flame_graphic, frame);
5914 if (Feld[xx][yy] == EL_FLAMES)
5915 Feld[xx][yy] = EL_EMPTY;
5916 DrawLevelField(xx, yy);
5921 if (MovDelay[x][y]) /* element still has to wait some time */
5923 PlayLevelSoundAction(x, y, ACTION_WAITING);
5929 /* now make next step */
5931 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5933 if (DONT_COLLIDE_WITH(element) &&
5934 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5935 !PLAYER_ENEMY_PROTECTED(newx, newy))
5937 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5942 else if (CAN_MOVE_INTO_ACID(element) &&
5943 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5944 !IS_MV_DIAGONAL(MovDir[x][y]) &&
5945 (MovDir[x][y] == MV_DOWN ||
5946 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5948 SplashAcid(newx, newy);
5949 Store[x][y] = EL_ACID;
5951 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5953 if (Feld[newx][newy] == EL_EXIT_OPEN)
5956 DrawLevelField(x, y);
5958 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5959 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5960 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5962 local_player->friends_still_needed--;
5963 if (!local_player->friends_still_needed &&
5964 !local_player->GameOver && AllPlayersGone)
5965 PlayerWins(local_player);
5969 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5971 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
5972 DrawLevelField(newx, newy);
5974 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5976 else if (!IS_FREE(newx, newy))
5978 GfxAction[x][y] = ACTION_WAITING;
5980 if (IS_PLAYER(x, y))
5981 DrawPlayerField(x, y);
5983 DrawLevelField(x, y);
5988 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5990 if (IS_FOOD_PIG(Feld[newx][newy]))
5992 if (IS_MOVING(newx, newy))
5993 RemoveMovingField(newx, newy);
5996 Feld[newx][newy] = EL_EMPTY;
5997 DrawLevelField(newx, newy);
6000 PlayLevelSound(x, y, SND_PIG_DIGGING);
6002 else if (!IS_FREE(newx, newy))
6004 if (IS_PLAYER(x, y))
6005 DrawPlayerField(x, y);
6007 DrawLevelField(x, y);
6012 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6014 if (Store[x][y] != EL_EMPTY)
6016 boolean can_clone = FALSE;
6019 /* check if element to clone is still there */
6020 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6022 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6030 /* cannot clone or target field not free anymore -- do not clone */
6031 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6032 Store[x][y] = EL_EMPTY;
6035 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6037 if (IS_MV_DIAGONAL(MovDir[x][y]))
6039 int diagonal_move_dir = MovDir[x][y];
6040 int stored = Store[x][y];
6041 int change_delay = 8;
6044 /* android is moving diagonally */
6046 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6048 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6049 GfxElement[x][y] = EL_EMC_ANDROID;
6050 GfxAction[x][y] = ACTION_SHRINKING;
6051 GfxDir[x][y] = diagonal_move_dir;
6052 ChangeDelay[x][y] = change_delay;
6054 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6057 DrawLevelGraphicAnimation(x, y, graphic);
6058 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6060 if (Feld[newx][newy] == EL_ACID)
6062 SplashAcid(newx, newy);
6067 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6069 Store[newx][newy] = EL_EMC_ANDROID;
6070 GfxElement[newx][newy] = EL_EMC_ANDROID;
6071 GfxAction[newx][newy] = ACTION_GROWING;
6072 GfxDir[newx][newy] = diagonal_move_dir;
6073 ChangeDelay[newx][newy] = change_delay;
6075 graphic = el_act_dir2img(GfxElement[newx][newy],
6076 GfxAction[newx][newy], GfxDir[newx][newy]);
6078 DrawLevelGraphicAnimation(newx, newy, graphic);
6079 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6085 Feld[newx][newy] = EL_EMPTY;
6086 DrawLevelField(newx, newy);
6088 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6091 else if (!IS_FREE(newx, newy))
6094 if (IS_PLAYER(x, y))
6095 DrawPlayerField(x, y);
6097 DrawLevelField(x, y);
6103 else if (IS_CUSTOM_ELEMENT(element) &&
6104 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6106 int new_element = Feld[newx][newy];
6108 if (!IS_FREE(newx, newy))
6110 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6111 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6114 /* no element can dig solid indestructible elements */
6115 if (IS_INDESTRUCTIBLE(new_element) &&
6116 !IS_DIGGABLE(new_element) &&
6117 !IS_COLLECTIBLE(new_element))
6120 if (AmoebaNr[newx][newy] &&
6121 (new_element == EL_AMOEBA_FULL ||
6122 new_element == EL_BD_AMOEBA ||
6123 new_element == EL_AMOEBA_GROWING))
6125 AmoebaCnt[AmoebaNr[newx][newy]]--;
6126 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6129 if (IS_MOVING(newx, newy))
6130 RemoveMovingField(newx, newy);
6133 RemoveField(newx, newy);
6134 DrawLevelField(newx, newy);
6137 /* if digged element was about to explode, prevent the explosion */
6138 ExplodeField[newx][newy] = EX_TYPE_NONE;
6140 PlayLevelSoundAction(x, y, action);
6143 Store[newx][newy] = EL_EMPTY;
6145 /* this makes it possible to leave the removed element again */
6146 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6147 Store[newx][newy] = new_element;
6149 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6151 int move_leave_element = element_info[element].move_leave_element;
6153 /* this makes it possible to leave the removed element again */
6154 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6155 new_element : move_leave_element);
6159 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6161 RunnerVisit[x][y] = FrameCounter;
6162 PlayerVisit[x][y] /= 8; /* expire player visit path */
6165 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6167 if (!IS_FREE(newx, newy))
6169 if (IS_PLAYER(x, y))
6170 DrawPlayerField(x, y);
6172 DrawLevelField(x, y);
6178 boolean wanna_flame = !RND(10);
6179 int dx = newx - x, dy = newy - y;
6180 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6181 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6182 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6183 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6184 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6185 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6188 IS_CLASSIC_ENEMY(element1) ||
6189 IS_CLASSIC_ENEMY(element2)) &&
6190 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6191 element1 != EL_FLAMES && element2 != EL_FLAMES)
6193 ResetGfxAnimation(x, y);
6194 GfxAction[x][y] = ACTION_ATTACKING;
6196 if (IS_PLAYER(x, y))
6197 DrawPlayerField(x, y);
6199 DrawLevelField(x, y);
6201 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6203 MovDelay[x][y] = 50;
6207 RemoveField(newx, newy);
6209 Feld[newx][newy] = EL_FLAMES;
6210 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6213 RemoveField(newx1, newy1);
6215 Feld[newx1][newy1] = EL_FLAMES;
6217 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6220 RemoveField(newx2, newy2);
6222 Feld[newx2][newy2] = EL_FLAMES;
6229 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6230 Feld[newx][newy] == EL_DIAMOND)
6232 if (IS_MOVING(newx, newy))
6233 RemoveMovingField(newx, newy);
6236 Feld[newx][newy] = EL_EMPTY;
6237 DrawLevelField(newx, newy);
6240 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6242 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6243 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6245 if (AmoebaNr[newx][newy])
6247 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6248 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6249 Feld[newx][newy] == EL_BD_AMOEBA)
6250 AmoebaCnt[AmoebaNr[newx][newy]]--;
6255 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6257 RemoveMovingField(newx, newy);
6260 if (IS_MOVING(newx, newy))
6262 RemoveMovingField(newx, newy);
6267 Feld[newx][newy] = EL_EMPTY;
6268 DrawLevelField(newx, newy);
6271 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6273 else if ((element == EL_PACMAN || element == EL_MOLE)
6274 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6276 if (AmoebaNr[newx][newy])
6278 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6279 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6280 Feld[newx][newy] == EL_BD_AMOEBA)
6281 AmoebaCnt[AmoebaNr[newx][newy]]--;
6284 if (element == EL_MOLE)
6286 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6287 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6289 ResetGfxAnimation(x, y);
6290 GfxAction[x][y] = ACTION_DIGGING;
6291 DrawLevelField(x, y);
6293 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6295 return; /* wait for shrinking amoeba */
6297 else /* element == EL_PACMAN */
6299 Feld[newx][newy] = EL_EMPTY;
6300 DrawLevelField(newx, newy);
6301 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6304 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6305 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6306 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6308 /* wait for shrinking amoeba to completely disappear */
6311 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6313 /* object was running against a wall */
6318 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6319 if (move_pattern & MV_ANY_DIRECTION &&
6320 move_pattern == MovDir[x][y])
6322 int blocking_element =
6323 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6325 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6328 element = Feld[x][y]; /* element might have changed */
6332 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6333 DrawLevelElementAnimation(x, y, element);
6335 if (DONT_TOUCH(element))
6336 TestIfBadThingTouchesPlayer(x, y);
6341 InitMovingField(x, y, MovDir[x][y]);
6343 PlayLevelSoundAction(x, y, ACTION_MOVING);
6347 ContinueMoving(x, y);
6350 void ContinueMoving(int x, int y)
6352 int element = Feld[x][y];
6353 struct ElementInfo *ei = &element_info[element];
6354 int direction = MovDir[x][y];
6355 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6356 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6357 int newx = x + dx, newy = y + dy;
6358 int stored = Store[x][y];
6359 int stored_new = Store[newx][newy];
6360 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6361 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6362 boolean last_line = (newy == lev_fieldy - 1);
6364 MovPos[x][y] += getElementMoveStepsize(x, y);
6366 if (pushed_by_player) /* special case: moving object pushed by player */
6367 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6369 if (ABS(MovPos[x][y]) < TILEX)
6371 DrawLevelField(x, y);
6373 return; /* element is still moving */
6376 /* element reached destination field */
6378 Feld[x][y] = EL_EMPTY;
6379 Feld[newx][newy] = element;
6380 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6382 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6384 element = Feld[newx][newy] = EL_ACID;
6386 else if (element == EL_MOLE)
6388 Feld[x][y] = EL_SAND;
6390 DrawLevelFieldCrumbledSandNeighbours(x, y);
6392 else if (element == EL_QUICKSAND_FILLING)
6394 element = Feld[newx][newy] = get_next_element(element);
6395 Store[newx][newy] = Store[x][y];
6397 else if (element == EL_QUICKSAND_EMPTYING)
6399 Feld[x][y] = get_next_element(element);
6400 element = Feld[newx][newy] = Store[x][y];
6402 else if (element == EL_MAGIC_WALL_FILLING)
6404 element = Feld[newx][newy] = get_next_element(element);
6405 if (!game.magic_wall_active)
6406 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6407 Store[newx][newy] = Store[x][y];
6409 else if (element == EL_MAGIC_WALL_EMPTYING)
6411 Feld[x][y] = get_next_element(element);
6412 if (!game.magic_wall_active)
6413 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6414 element = Feld[newx][newy] = Store[x][y];
6416 #if USE_NEW_CUSTOM_VALUE
6417 InitField(newx, newy, FALSE);
6420 else if (element == EL_BD_MAGIC_WALL_FILLING)
6422 element = Feld[newx][newy] = get_next_element(element);
6423 if (!game.magic_wall_active)
6424 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6425 Store[newx][newy] = Store[x][y];
6427 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6429 Feld[x][y] = get_next_element(element);
6430 if (!game.magic_wall_active)
6431 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6432 element = Feld[newx][newy] = Store[x][y];
6434 #if USE_NEW_CUSTOM_VALUE
6435 InitField(newx, newy, FALSE);
6438 else if (element == EL_AMOEBA_DROPPING)
6440 Feld[x][y] = get_next_element(element);
6441 element = Feld[newx][newy] = Store[x][y];
6443 else if (element == EL_SOKOBAN_OBJECT)
6446 Feld[x][y] = Back[x][y];
6448 if (Back[newx][newy])
6449 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6451 Back[x][y] = Back[newx][newy] = 0;
6454 Store[x][y] = EL_EMPTY;
6459 MovDelay[newx][newy] = 0;
6461 if (CAN_CHANGE_OR_HAS_ACTION(element))
6463 /* copy element change control values to new field */
6464 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6465 ChangePage[newx][newy] = ChangePage[x][y];
6466 ChangeCount[newx][newy] = ChangeCount[x][y];
6467 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6470 #if USE_NEW_CUSTOM_VALUE
6471 CustomValue[newx][newy] = CustomValue[x][y];
6474 ChangeDelay[x][y] = 0;
6475 ChangePage[x][y] = -1;
6476 ChangeCount[x][y] = 0;
6477 ChangeEvent[x][y] = -1;
6479 #if USE_NEW_CUSTOM_VALUE
6480 CustomValue[x][y] = 0;
6483 /* copy animation control values to new field */
6484 GfxFrame[newx][newy] = GfxFrame[x][y];
6485 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6486 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6487 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6489 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6491 /* some elements can leave other elements behind after moving */
6493 if (ei->move_leave_element != EL_EMPTY &&
6494 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6495 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6497 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6498 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6499 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6502 int move_leave_element = ei->move_leave_element;
6506 /* this makes it possible to leave the removed element again */
6507 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6508 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6510 /* this makes it possible to leave the removed element again */
6511 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6512 move_leave_element = stored;
6515 /* this makes it possible to leave the removed element again */
6516 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6517 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6518 move_leave_element = stored;
6521 Feld[x][y] = move_leave_element;
6523 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6524 MovDir[x][y] = direction;
6526 InitField(x, y, FALSE);
6528 if (GFX_CRUMBLED(Feld[x][y]))
6529 DrawLevelFieldCrumbledSandNeighbours(x, y);
6531 if (ELEM_IS_PLAYER(move_leave_element))
6532 RelocatePlayer(x, y, move_leave_element);
6535 /* do this after checking for left-behind element */
6536 ResetGfxAnimation(x, y); /* reset animation values for old field */
6538 if (!CAN_MOVE(element) ||
6539 (CAN_FALL(element) && direction == MV_DOWN &&
6540 (element == EL_SPRING ||
6541 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6542 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6543 GfxDir[x][y] = MovDir[newx][newy] = 0;
6545 DrawLevelField(x, y);
6546 DrawLevelField(newx, newy);
6548 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6550 /* prevent pushed element from moving on in pushed direction */
6551 if (pushed_by_player && CAN_MOVE(element) &&
6552 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6553 !(element_info[element].move_pattern & direction))
6554 TurnRound(newx, newy);
6556 /* prevent elements on conveyor belt from moving on in last direction */
6557 if (pushed_by_conveyor && CAN_FALL(element) &&
6558 direction & MV_HORIZONTAL)
6559 MovDir[newx][newy] = 0;
6561 if (!pushed_by_player)
6563 int nextx = newx + dx, nexty = newy + dy;
6564 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6566 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6568 if (CAN_FALL(element) && direction == MV_DOWN)
6569 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6571 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6572 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6575 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6577 TestIfBadThingTouchesPlayer(newx, newy);
6578 TestIfBadThingTouchesFriend(newx, newy);
6580 if (!IS_CUSTOM_ELEMENT(element))
6581 TestIfBadThingTouchesOtherBadThing(newx, newy);
6583 else if (element == EL_PENGUIN)
6584 TestIfFriendTouchesBadThing(newx, newy);
6586 /* give the player one last chance (one more frame) to move away */
6587 if (CAN_FALL(element) && direction == MV_DOWN &&
6588 (last_line || (!IS_FREE(x, newy + 1) &&
6589 (!IS_PLAYER(x, newy + 1) ||
6590 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6593 if (pushed_by_player && !game.use_change_when_pushing_bug)
6595 int push_side = MV_DIR_OPPOSITE(direction);
6596 struct PlayerInfo *player = PLAYERINFO(x, y);
6598 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6599 player->index_bit, push_side);
6600 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6601 player->index_bit, push_side);
6604 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6605 MovDelay[newx][newy] = 1;
6607 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6609 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6612 if (ChangePage[newx][newy] != -1) /* delayed change */
6614 int page = ChangePage[newx][newy];
6615 struct ElementChangeInfo *change = &ei->change_page[page];
6617 ChangePage[newx][newy] = -1;
6619 if (change->can_change)
6621 if (ChangeElement(newx, newy, element, page))
6623 if (change->post_change_function)
6624 change->post_change_function(newx, newy);
6628 if (change->has_action)
6629 ExecuteCustomElementAction(newx, newy, element, page);
6633 TestIfElementHitsCustomElement(newx, newy, direction);
6634 TestIfPlayerTouchesCustomElement(newx, newy);
6635 TestIfElementTouchesCustomElement(newx, newy);
6637 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6638 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6639 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6640 MV_DIR_OPPOSITE(direction));
6643 int AmoebeNachbarNr(int ax, int ay)
6646 int element = Feld[ax][ay];
6648 static int xy[4][2] =
6656 for (i = 0; i < NUM_DIRECTIONS; i++)
6658 int x = ax + xy[i][0];
6659 int y = ay + xy[i][1];
6661 if (!IN_LEV_FIELD(x, y))
6664 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6665 group_nr = AmoebaNr[x][y];
6671 void AmoebenVereinigen(int ax, int ay)
6673 int i, x, y, xx, yy;
6674 int new_group_nr = AmoebaNr[ax][ay];
6675 static int xy[4][2] =
6683 if (new_group_nr == 0)
6686 for (i = 0; i < NUM_DIRECTIONS; i++)
6691 if (!IN_LEV_FIELD(x, y))
6694 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6695 Feld[x][y] == EL_BD_AMOEBA ||
6696 Feld[x][y] == EL_AMOEBA_DEAD) &&
6697 AmoebaNr[x][y] != new_group_nr)
6699 int old_group_nr = AmoebaNr[x][y];
6701 if (old_group_nr == 0)
6704 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6705 AmoebaCnt[old_group_nr] = 0;
6706 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6707 AmoebaCnt2[old_group_nr] = 0;
6709 SCAN_PLAYFIELD(xx, yy)
6711 if (AmoebaNr[xx][yy] == old_group_nr)
6712 AmoebaNr[xx][yy] = new_group_nr;
6718 void AmoebeUmwandeln(int ax, int ay)
6722 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6724 int group_nr = AmoebaNr[ax][ay];
6729 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6730 printf("AmoebeUmwandeln(): This should never happen!\n");
6735 SCAN_PLAYFIELD(x, y)
6737 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6740 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6744 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6745 SND_AMOEBA_TURNING_TO_GEM :
6746 SND_AMOEBA_TURNING_TO_ROCK));
6751 static int xy[4][2] =
6759 for (i = 0; i < NUM_DIRECTIONS; i++)
6764 if (!IN_LEV_FIELD(x, y))
6767 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6769 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6770 SND_AMOEBA_TURNING_TO_GEM :
6771 SND_AMOEBA_TURNING_TO_ROCK));
6778 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6781 int group_nr = AmoebaNr[ax][ay];
6782 boolean done = FALSE;
6787 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6788 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6793 SCAN_PLAYFIELD(x, y)
6795 if (AmoebaNr[x][y] == group_nr &&
6796 (Feld[x][y] == EL_AMOEBA_DEAD ||
6797 Feld[x][y] == EL_BD_AMOEBA ||
6798 Feld[x][y] == EL_AMOEBA_GROWING))
6801 Feld[x][y] = new_element;
6802 InitField(x, y, FALSE);
6803 DrawLevelField(x, y);
6809 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6810 SND_BD_AMOEBA_TURNING_TO_ROCK :
6811 SND_BD_AMOEBA_TURNING_TO_GEM));
6814 void AmoebeWaechst(int x, int y)
6816 static unsigned long sound_delay = 0;
6817 static unsigned long sound_delay_value = 0;
6819 if (!MovDelay[x][y]) /* start new growing cycle */
6823 if (DelayReached(&sound_delay, sound_delay_value))
6825 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6826 sound_delay_value = 30;
6830 if (MovDelay[x][y]) /* wait some time before growing bigger */
6833 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6835 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6836 6 - MovDelay[x][y]);
6838 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6841 if (!MovDelay[x][y])
6843 Feld[x][y] = Store[x][y];
6845 DrawLevelField(x, y);
6850 void AmoebaDisappearing(int x, int y)
6852 static unsigned long sound_delay = 0;
6853 static unsigned long sound_delay_value = 0;
6855 if (!MovDelay[x][y]) /* start new shrinking cycle */
6859 if (DelayReached(&sound_delay, sound_delay_value))
6860 sound_delay_value = 30;
6863 if (MovDelay[x][y]) /* wait some time before shrinking */
6866 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6868 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6869 6 - MovDelay[x][y]);
6871 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6874 if (!MovDelay[x][y])
6876 Feld[x][y] = EL_EMPTY;
6877 DrawLevelField(x, y);
6879 /* don't let mole enter this field in this cycle;
6880 (give priority to objects falling to this field from above) */
6886 void AmoebeAbleger(int ax, int ay)
6889 int element = Feld[ax][ay];
6890 int graphic = el2img(element);
6891 int newax = ax, neway = ay;
6892 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6893 static int xy[4][2] =
6901 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6903 Feld[ax][ay] = EL_AMOEBA_DEAD;
6904 DrawLevelField(ax, ay);
6908 if (IS_ANIMATED(graphic))
6909 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6911 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6912 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6914 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6917 if (MovDelay[ax][ay])
6921 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6924 int x = ax + xy[start][0];
6925 int y = ay + xy[start][1];
6927 if (!IN_LEV_FIELD(x, y))
6930 if (IS_FREE(x, y) ||
6931 CAN_GROW_INTO(Feld[x][y]) ||
6932 Feld[x][y] == EL_QUICKSAND_EMPTY)
6938 if (newax == ax && neway == ay)
6941 else /* normal or "filled" (BD style) amoeba */
6944 boolean waiting_for_player = FALSE;
6946 for (i = 0; i < NUM_DIRECTIONS; i++)
6948 int j = (start + i) % 4;
6949 int x = ax + xy[j][0];
6950 int y = ay + xy[j][1];
6952 if (!IN_LEV_FIELD(x, y))
6955 if (IS_FREE(x, y) ||
6956 CAN_GROW_INTO(Feld[x][y]) ||
6957 Feld[x][y] == EL_QUICKSAND_EMPTY)
6963 else if (IS_PLAYER(x, y))
6964 waiting_for_player = TRUE;
6967 if (newax == ax && neway == ay) /* amoeba cannot grow */
6969 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6971 Feld[ax][ay] = EL_AMOEBA_DEAD;
6972 DrawLevelField(ax, ay);
6973 AmoebaCnt[AmoebaNr[ax][ay]]--;
6975 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6977 if (element == EL_AMOEBA_FULL)
6978 AmoebeUmwandeln(ax, ay);
6979 else if (element == EL_BD_AMOEBA)
6980 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6985 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6987 /* amoeba gets larger by growing in some direction */
6989 int new_group_nr = AmoebaNr[ax][ay];
6992 if (new_group_nr == 0)
6994 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6995 printf("AmoebeAbleger(): This should never happen!\n");
7000 AmoebaNr[newax][neway] = new_group_nr;
7001 AmoebaCnt[new_group_nr]++;
7002 AmoebaCnt2[new_group_nr]++;
7004 /* if amoeba touches other amoeba(s) after growing, unify them */
7005 AmoebenVereinigen(newax, neway);
7007 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7009 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7015 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7016 (neway == lev_fieldy - 1 && newax != ax))
7018 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7019 Store[newax][neway] = element;
7021 else if (neway == ay || element == EL_EMC_DRIPPER)
7023 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7025 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7029 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7030 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7031 Store[ax][ay] = EL_AMOEBA_DROP;
7032 ContinueMoving(ax, ay);
7036 DrawLevelField(newax, neway);
7039 void Life(int ax, int ay)
7043 int element = Feld[ax][ay];
7044 int graphic = el2img(element);
7045 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7047 boolean changed = FALSE;
7049 if (IS_ANIMATED(graphic))
7050 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7055 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7056 MovDelay[ax][ay] = life_time;
7058 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7061 if (MovDelay[ax][ay])
7065 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7067 int xx = ax+x1, yy = ay+y1;
7070 if (!IN_LEV_FIELD(xx, yy))
7073 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7075 int x = xx+x2, y = yy+y2;
7077 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7080 if (((Feld[x][y] == element ||
7081 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7083 (IS_FREE(x, y) && Stop[x][y]))
7087 if (xx == ax && yy == ay) /* field in the middle */
7089 if (nachbarn < life_parameter[0] ||
7090 nachbarn > life_parameter[1])
7092 Feld[xx][yy] = EL_EMPTY;
7094 DrawLevelField(xx, yy);
7095 Stop[xx][yy] = TRUE;
7099 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7100 { /* free border field */
7101 if (nachbarn >= life_parameter[2] &&
7102 nachbarn <= life_parameter[3])
7104 Feld[xx][yy] = element;
7105 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7107 DrawLevelField(xx, yy);
7108 Stop[xx][yy] = TRUE;
7115 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7116 SND_GAME_OF_LIFE_GROWING);
7119 static void InitRobotWheel(int x, int y)
7121 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7124 static void RunRobotWheel(int x, int y)
7126 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7129 static void StopRobotWheel(int x, int y)
7131 if (ZX == x && ZY == y)
7135 static void InitTimegateWheel(int x, int y)
7137 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7140 static void RunTimegateWheel(int x, int y)
7142 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7145 static void InitMagicBallDelay(int x, int y)
7148 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7150 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7154 static void ActivateMagicBall(int bx, int by)
7158 if (level.ball_random)
7160 int pos_border = RND(8); /* select one of the eight border elements */
7161 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7162 int xx = pos_content % 3;
7163 int yy = pos_content / 3;
7168 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7169 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7173 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7175 int xx = x - bx + 1;
7176 int yy = y - by + 1;
7178 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7179 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7183 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7186 void CheckExit(int x, int y)
7188 if (local_player->gems_still_needed > 0 ||
7189 local_player->sokobanfields_still_needed > 0 ||
7190 local_player->lights_still_needed > 0)
7192 int element = Feld[x][y];
7193 int graphic = el2img(element);
7195 if (IS_ANIMATED(graphic))
7196 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7201 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7204 Feld[x][y] = EL_EXIT_OPENING;
7206 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7209 void CheckExitSP(int x, int y)
7211 if (local_player->gems_still_needed > 0)
7213 int element = Feld[x][y];
7214 int graphic = el2img(element);
7216 if (IS_ANIMATED(graphic))
7217 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7222 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7225 Feld[x][y] = EL_SP_EXIT_OPENING;
7227 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7230 static void CloseAllOpenTimegates()
7234 SCAN_PLAYFIELD(x, y)
7236 int element = Feld[x][y];
7238 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7240 Feld[x][y] = EL_TIMEGATE_CLOSING;
7242 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7247 void DrawTwinkleOnField(int x, int y)
7249 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7252 if (Feld[x][y] == EL_BD_DIAMOND)
7255 if (MovDelay[x][y] == 0) /* next animation frame */
7256 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7258 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7262 if (setup.direct_draw && MovDelay[x][y])
7263 SetDrawtoField(DRAW_BUFFERED);
7265 DrawLevelElementAnimation(x, y, Feld[x][y]);
7267 if (MovDelay[x][y] != 0)
7269 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7270 10 - MovDelay[x][y]);
7272 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7274 if (setup.direct_draw)
7278 dest_x = FX + SCREENX(x) * TILEX;
7279 dest_y = FY + SCREENY(y) * TILEY;
7281 BlitBitmap(drawto_field, window,
7282 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7283 SetDrawtoField(DRAW_DIRECT);
7289 void MauerWaechst(int x, int y)
7293 if (!MovDelay[x][y]) /* next animation frame */
7294 MovDelay[x][y] = 3 * delay;
7296 if (MovDelay[x][y]) /* wait some time before next frame */
7300 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7302 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7303 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7305 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7308 if (!MovDelay[x][y])
7310 if (MovDir[x][y] == MV_LEFT)
7312 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7313 DrawLevelField(x - 1, y);
7315 else if (MovDir[x][y] == MV_RIGHT)
7317 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7318 DrawLevelField(x + 1, y);
7320 else if (MovDir[x][y] == MV_UP)
7322 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7323 DrawLevelField(x, y - 1);
7327 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7328 DrawLevelField(x, y + 1);
7331 Feld[x][y] = Store[x][y];
7333 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7334 DrawLevelField(x, y);
7339 void MauerAbleger(int ax, int ay)
7341 int element = Feld[ax][ay];
7342 int graphic = el2img(element);
7343 boolean oben_frei = FALSE, unten_frei = FALSE;
7344 boolean links_frei = FALSE, rechts_frei = FALSE;
7345 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7346 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7347 boolean new_wall = FALSE;
7349 if (IS_ANIMATED(graphic))
7350 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7352 if (!MovDelay[ax][ay]) /* start building new wall */
7353 MovDelay[ax][ay] = 6;
7355 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7358 if (MovDelay[ax][ay])
7362 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7364 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7366 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7368 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7371 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7372 element == EL_EXPANDABLE_WALL_ANY)
7376 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7377 Store[ax][ay-1] = element;
7378 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7379 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7380 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7381 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7386 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7387 Store[ax][ay+1] = element;
7388 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7389 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7390 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7391 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7396 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7397 element == EL_EXPANDABLE_WALL_ANY ||
7398 element == EL_EXPANDABLE_WALL ||
7399 element == EL_BD_EXPANDABLE_WALL)
7403 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7404 Store[ax-1][ay] = element;
7405 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7406 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7407 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7408 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7414 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7415 Store[ax+1][ay] = element;
7416 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7417 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7418 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7419 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7424 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7425 DrawLevelField(ax, ay);
7427 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7429 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7430 unten_massiv = TRUE;
7431 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7432 links_massiv = TRUE;
7433 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7434 rechts_massiv = TRUE;
7436 if (((oben_massiv && unten_massiv) ||
7437 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7438 element == EL_EXPANDABLE_WALL) &&
7439 ((links_massiv && rechts_massiv) ||
7440 element == EL_EXPANDABLE_WALL_VERTICAL))
7441 Feld[ax][ay] = EL_WALL;
7444 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7447 void CheckForDragon(int x, int y)
7450 boolean dragon_found = FALSE;
7451 static int xy[4][2] =
7459 for (i = 0; i < NUM_DIRECTIONS; i++)
7461 for (j = 0; j < 4; j++)
7463 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7465 if (IN_LEV_FIELD(xx, yy) &&
7466 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7468 if (Feld[xx][yy] == EL_DRAGON)
7469 dragon_found = TRUE;
7478 for (i = 0; i < NUM_DIRECTIONS; i++)
7480 for (j = 0; j < 3; j++)
7482 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7484 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7486 Feld[xx][yy] = EL_EMPTY;
7487 DrawLevelField(xx, yy);
7496 static void InitBuggyBase(int x, int y)
7498 int element = Feld[x][y];
7499 int activating_delay = FRAMES_PER_SECOND / 4;
7502 (element == EL_SP_BUGGY_BASE ?
7503 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7504 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7506 element == EL_SP_BUGGY_BASE_ACTIVE ?
7507 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7510 static void WarnBuggyBase(int x, int y)
7513 static int xy[4][2] =
7521 for (i = 0; i < NUM_DIRECTIONS; i++)
7523 int xx = x + xy[i][0];
7524 int yy = y + xy[i][1];
7526 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7528 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7535 static void InitTrap(int x, int y)
7537 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7540 static void ActivateTrap(int x, int y)
7542 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7545 static void ChangeActiveTrap(int x, int y)
7547 int graphic = IMG_TRAP_ACTIVE;
7549 /* if new animation frame was drawn, correct crumbled sand border */
7550 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7551 DrawLevelFieldCrumbledSand(x, y);
7554 static int getSpecialActionElement(int element, int number, int base_element)
7556 return (element != EL_EMPTY ? element :
7557 number != -1 ? base_element + number - 1 :
7561 static int getModifiedActionNumber(int value_old, int operator, int operand,
7562 int value_min, int value_max)
7564 int value_new = (operator == CA_MODE_SET ? operand :
7565 operator == CA_MODE_ADD ? value_old + operand :
7566 operator == CA_MODE_SUBTRACT ? value_old - operand :
7567 operator == CA_MODE_MULTIPLY ? value_old * operand :
7568 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7569 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7572 return (value_new < value_min ? value_min :
7573 value_new > value_max ? value_max :
7577 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7579 struct ElementInfo *ei = &element_info[element];
7580 struct ElementChangeInfo *change = &ei->change_page[page];
7581 int target_element = change->target_element;
7582 int action_type = change->action_type;
7583 int action_mode = change->action_mode;
7584 int action_arg = change->action_arg;
7587 if (!change->has_action)
7590 /* ---------- determine action paramater values -------------------------- */
7592 int level_time_value =
7593 (level.time > 0 ? TimeLeft :
7596 int action_arg_element =
7597 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7598 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7599 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7602 int action_arg_direction =
7603 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7604 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7605 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7606 change->actual_trigger_side :
7607 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7608 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7611 int action_arg_number_min =
7612 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7615 int action_arg_number_max =
7616 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7617 action_type == CA_SET_LEVEL_GEMS ? 999 :
7618 action_type == CA_SET_LEVEL_TIME ? 9999 :
7619 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7620 action_type == CA_SET_CE_VALUE ? 9999 :
7621 action_type == CA_SET_CE_SCORE ? 9999 :
7624 int action_arg_number_reset =
7625 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
7626 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7627 action_type == CA_SET_LEVEL_TIME ? level.time :
7628 action_type == CA_SET_LEVEL_SCORE ? 0 :
7629 #if USE_NEW_CUSTOM_VALUE
7630 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
7632 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7634 action_type == CA_SET_CE_SCORE ? 0 :
7637 int action_arg_number =
7638 (action_arg <= CA_ARG_MAX ? action_arg :
7639 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7640 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7641 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7642 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7643 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7644 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7645 #if USE_NEW_CUSTOM_VALUE
7646 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7648 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7650 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7651 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7652 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7653 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7654 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7655 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
7656 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7657 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
7658 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
7659 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7660 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7663 int action_arg_number_old =
7664 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7665 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7666 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7667 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7668 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7671 int action_arg_number_new =
7672 getModifiedActionNumber(action_arg_number_old,
7673 action_mode, action_arg_number,
7674 action_arg_number_min, action_arg_number_max);
7676 int trigger_player_bits =
7677 (change->actual_trigger_player >= EL_PLAYER_1 &&
7678 change->actual_trigger_player <= EL_PLAYER_4 ?
7679 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7682 int action_arg_player_bits =
7683 (action_arg >= CA_ARG_PLAYER_1 &&
7684 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7685 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7688 /* ---------- execute action -------------------------------------------- */
7697 /* ---------- level actions ------------------------------------------- */
7699 case CA_RESTART_LEVEL:
7701 game.restart_level = TRUE;
7706 case CA_SHOW_ENVELOPE:
7708 int element = getSpecialActionElement(action_arg_element,
7709 action_arg_number, EL_ENVELOPE_1);
7711 if (IS_ENVELOPE(element))
7712 local_player->show_envelope = element;
7717 case CA_SET_LEVEL_TIME:
7719 if (level.time > 0) /* only modify limited time value */
7721 TimeLeft = action_arg_number_new;
7723 DrawGameValue_Time(TimeLeft);
7725 if (!TimeLeft && setup.time_limit)
7726 for (i = 0; i < MAX_PLAYERS; i++)
7727 KillPlayer(&stored_player[i]);
7733 case CA_SET_LEVEL_SCORE:
7735 local_player->score = action_arg_number_new;
7737 DrawGameValue_Score(local_player->score);
7742 case CA_SET_LEVEL_GEMS:
7744 local_player->gems_still_needed = action_arg_number_new;
7746 DrawGameValue_Emeralds(local_player->gems_still_needed);
7751 #if !USE_PLAYER_GRAVITY
7752 case CA_SET_LEVEL_GRAVITY:
7754 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7755 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7756 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7762 case CA_SET_LEVEL_WIND:
7764 game.wind_direction = action_arg_direction;
7769 /* ---------- player actions ------------------------------------------ */
7771 case CA_MOVE_PLAYER:
7773 /* automatically move to the next field in specified direction */
7774 for (i = 0; i < MAX_PLAYERS; i++)
7775 if (trigger_player_bits & (1 << i))
7776 stored_player[i].programmed_action = action_arg_direction;
7781 case CA_EXIT_PLAYER:
7783 for (i = 0; i < MAX_PLAYERS; i++)
7784 if (action_arg_player_bits & (1 << i))
7785 PlayerWins(&stored_player[i]);
7790 case CA_KILL_PLAYER:
7792 for (i = 0; i < MAX_PLAYERS; i++)
7793 if (action_arg_player_bits & (1 << i))
7794 KillPlayer(&stored_player[i]);
7799 case CA_SET_PLAYER_KEYS:
7801 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7802 int element = getSpecialActionElement(action_arg_element,
7803 action_arg_number, EL_KEY_1);
7805 if (IS_KEY(element))
7807 for (i = 0; i < MAX_PLAYERS; i++)
7809 if (trigger_player_bits & (1 << i))
7811 stored_player[i].key[KEY_NR(element)] = key_state;
7813 DrawGameDoorValues();
7821 case CA_SET_PLAYER_SPEED:
7823 for (i = 0; i < MAX_PLAYERS; i++)
7825 if (trigger_player_bits & (1 << i))
7827 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7829 if (action_arg == CA_ARG_SPEED_FASTER &&
7830 stored_player[i].cannot_move)
7832 action_arg_number = STEPSIZE_VERY_SLOW;
7834 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7835 action_arg == CA_ARG_SPEED_FASTER)
7837 action_arg_number = 2;
7838 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7841 else if (action_arg == CA_ARG_NUMBER_RESET)
7843 action_arg_number = level.initial_player_stepsize[i];
7847 getModifiedActionNumber(move_stepsize,
7850 action_arg_number_min,
7851 action_arg_number_max);
7853 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7860 case CA_SET_PLAYER_SHIELD:
7862 for (i = 0; i < MAX_PLAYERS; i++)
7864 if (trigger_player_bits & (1 << i))
7866 if (action_arg == CA_ARG_SHIELD_OFF)
7868 stored_player[i].shield_normal_time_left = 0;
7869 stored_player[i].shield_deadly_time_left = 0;
7871 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7873 stored_player[i].shield_normal_time_left = 999999;
7875 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7877 stored_player[i].shield_normal_time_left = 999999;
7878 stored_player[i].shield_deadly_time_left = 999999;
7886 #if USE_PLAYER_GRAVITY
7887 case CA_SET_PLAYER_GRAVITY:
7889 for (i = 0; i < MAX_PLAYERS; i++)
7891 if (trigger_player_bits & (1 << i))
7893 stored_player[i].gravity =
7894 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7895 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7896 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
7897 stored_player[i].gravity);
7905 case CA_SET_PLAYER_ARTWORK:
7907 for (i = 0; i < MAX_PLAYERS; i++)
7909 if (trigger_player_bits & (1 << i))
7911 int artwork_element = action_arg_element;
7913 if (action_arg == CA_ARG_ELEMENT_RESET)
7915 (level.use_artwork_element[i] ? level.artwork_element[i] :
7916 stored_player[i].element_nr);
7918 #if USE_GFX_RESET_PLAYER_ARTWORK
7919 if (stored_player[i].artwork_element != artwork_element)
7920 stored_player[i].Frame = 0;
7923 stored_player[i].artwork_element = artwork_element;
7925 SetPlayerWaiting(&stored_player[i], FALSE);
7927 /* set number of special actions for bored and sleeping animation */
7928 stored_player[i].num_special_action_bored =
7929 get_num_special_action(artwork_element,
7930 ACTION_BORING_1, ACTION_BORING_LAST);
7931 stored_player[i].num_special_action_sleeping =
7932 get_num_special_action(artwork_element,
7933 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7940 /* ---------- CE actions ---------------------------------------------- */
7942 case CA_SET_CE_VALUE:
7944 #if USE_NEW_CUSTOM_VALUE
7945 int last_ce_value = CustomValue[x][y];
7947 CustomValue[x][y] = action_arg_number_new;
7949 if (CustomValue[x][y] != last_ce_value)
7951 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
7952 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
7954 if (CustomValue[x][y] == 0)
7956 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7957 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7965 case CA_SET_CE_SCORE:
7967 #if USE_NEW_CUSTOM_VALUE
7968 int last_ce_score = ei->collect_score;
7970 ei->collect_score = action_arg_number_new;
7972 if (ei->collect_score != last_ce_score)
7974 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
7975 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
7977 if (ei->collect_score == 0)
7981 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
7982 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
7985 This is a very special case that seems to be a mixture between
7986 CheckElementChange() and CheckTriggeredElementChange(): while
7987 the first one only affects single elements that are triggered
7988 directly, the second one affects multiple elements in the playfield
7989 that are triggered indirectly by another element. This is a third
7990 case: Changing the CE score always affects multiple identical CEs,
7991 so every affected CE must be checked, not only the single CE for
7992 which the CE score was changed in the first place (as every instance
7993 of that CE shares the same CE score, and therefore also can change)!
7995 SCAN_PLAYFIELD(xx, yy)
7997 if (Feld[xx][yy] == element)
7998 CheckElementChange(xx, yy, element, EL_UNDEFINED,
7999 CE_SCORE_GETS_ZERO);
8008 /* ---------- engine actions ------------------------------------------ */
8010 case CA_SET_ENGINE_SCAN_MODE:
8012 InitPlayfieldScanMode(action_arg);
8022 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8024 int old_element = Feld[x][y];
8025 int new_element = get_element_from_group_element(element);
8026 int previous_move_direction = MovDir[x][y];
8027 #if USE_NEW_CUSTOM_VALUE
8028 int last_ce_value = CustomValue[x][y];
8030 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8031 boolean add_player_onto_element = (new_element_is_player &&
8032 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8033 /* this breaks SnakeBite when a snake is
8034 halfway through a door that closes */
8035 /* NOW FIXED AT LEVEL INIT IN files.c */
8036 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8038 IS_WALKABLE(old_element));
8041 /* check if element under the player changes from accessible to unaccessible
8042 (needed for special case of dropping element which then changes) */
8043 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8044 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8052 if (!add_player_onto_element)
8054 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8055 RemoveMovingField(x, y);
8059 Feld[x][y] = new_element;
8061 #if !USE_GFX_RESET_GFX_ANIMATION
8062 ResetGfxAnimation(x, y);
8063 ResetRandomAnimationValue(x, y);
8066 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8067 MovDir[x][y] = previous_move_direction;
8069 #if USE_NEW_CUSTOM_VALUE
8070 if (element_info[new_element].use_last_ce_value)
8071 CustomValue[x][y] = last_ce_value;
8074 InitField_WithBug1(x, y, FALSE);
8076 new_element = Feld[x][y]; /* element may have changed */
8078 #if USE_GFX_RESET_GFX_ANIMATION
8079 ResetGfxAnimation(x, y);
8080 ResetRandomAnimationValue(x, y);
8083 DrawLevelField(x, y);
8085 if (GFX_CRUMBLED(new_element))
8086 DrawLevelFieldCrumbledSandNeighbours(x, y);
8090 /* check if element under the player changes from accessible to unaccessible
8091 (needed for special case of dropping element which then changes) */
8092 /* (must be checked after creating new element for walkable group elements) */
8093 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8094 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8102 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8103 if (new_element_is_player)
8104 RelocatePlayer(x, y, new_element);
8107 ChangeCount[x][y]++; /* count number of changes in the same frame */
8109 TestIfBadThingTouchesPlayer(x, y);
8110 TestIfPlayerTouchesCustomElement(x, y);
8111 TestIfElementTouchesCustomElement(x, y);
8114 static void CreateField(int x, int y, int element)
8116 CreateFieldExt(x, y, element, FALSE);
8119 static void CreateElementFromChange(int x, int y, int element)
8121 element = GET_VALID_RUNTIME_ELEMENT(element);
8123 #if USE_STOP_CHANGED_ELEMENTS
8124 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8126 int old_element = Feld[x][y];
8128 /* prevent changed element from moving in same engine frame
8129 unless both old and new element can either fall or move */
8130 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8131 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8136 CreateFieldExt(x, y, element, TRUE);
8139 static boolean ChangeElement(int x, int y, int element, int page)
8141 struct ElementInfo *ei = &element_info[element];
8142 struct ElementChangeInfo *change = &ei->change_page[page];
8143 int ce_value = CustomValue[x][y];
8144 int ce_score = ei->collect_score;
8146 int old_element = Feld[x][y];
8148 /* always use default change event to prevent running into a loop */
8149 if (ChangeEvent[x][y] == -1)
8150 ChangeEvent[x][y] = CE_DELAY;
8152 if (ChangeEvent[x][y] == CE_DELAY)
8154 /* reset actual trigger element, trigger player and action element */
8155 change->actual_trigger_element = EL_EMPTY;
8156 change->actual_trigger_player = EL_PLAYER_1;
8157 change->actual_trigger_side = CH_SIDE_NONE;
8158 change->actual_trigger_ce_value = 0;
8159 change->actual_trigger_ce_score = 0;
8162 /* do not change elements more than a specified maximum number of changes */
8163 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8166 ChangeCount[x][y]++; /* count number of changes in the same frame */
8168 if (change->explode)
8175 if (change->use_target_content)
8177 boolean complete_replace = TRUE;
8178 boolean can_replace[3][3];
8181 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8184 boolean is_walkable;
8185 boolean is_diggable;
8186 boolean is_collectible;
8187 boolean is_removable;
8188 boolean is_destructible;
8189 int ex = x + xx - 1;
8190 int ey = y + yy - 1;
8191 int content_element = change->target_content.e[xx][yy];
8194 can_replace[xx][yy] = TRUE;
8196 if (ex == x && ey == y) /* do not check changing element itself */
8199 if (content_element == EL_EMPTY_SPACE)
8201 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8206 if (!IN_LEV_FIELD(ex, ey))
8208 can_replace[xx][yy] = FALSE;
8209 complete_replace = FALSE;
8216 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8217 e = MovingOrBlocked2Element(ex, ey);
8219 is_empty = (IS_FREE(ex, ey) ||
8220 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8222 is_walkable = (is_empty || IS_WALKABLE(e));
8223 is_diggable = (is_empty || IS_DIGGABLE(e));
8224 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8225 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8226 is_removable = (is_diggable || is_collectible);
8228 can_replace[xx][yy] =
8229 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8230 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8231 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8232 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8233 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8234 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8235 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8237 if (!can_replace[xx][yy])
8238 complete_replace = FALSE;
8241 if (!change->only_if_complete || complete_replace)
8243 boolean something_has_changed = FALSE;
8245 if (change->only_if_complete && change->use_random_replace &&
8246 RND(100) < change->random_percentage)
8249 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8251 int ex = x + xx - 1;
8252 int ey = y + yy - 1;
8253 int content_element;
8255 if (can_replace[xx][yy] && (!change->use_random_replace ||
8256 RND(100) < change->random_percentage))
8258 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8259 RemoveMovingField(ex, ey);
8261 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8263 content_element = change->target_content.e[xx][yy];
8264 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8265 ce_value, ce_score);
8267 CreateElementFromChange(ex, ey, target_element);
8269 something_has_changed = TRUE;
8271 /* for symmetry reasons, freeze newly created border elements */
8272 if (ex != x || ey != y)
8273 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8277 if (something_has_changed)
8279 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8280 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8286 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
8287 ce_value, ce_score);
8289 if (element == EL_DIAGONAL_GROWING ||
8290 element == EL_DIAGONAL_SHRINKING)
8292 target_element = Store[x][y];
8294 Store[x][y] = EL_EMPTY;
8297 CreateElementFromChange(x, y, target_element);
8299 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8300 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8303 /* this uses direct change before indirect change */
8304 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8309 #if USE_NEW_DELAYED_ACTION
8311 static void HandleElementChange(int x, int y, int page)
8313 int element = MovingOrBlocked2Element(x, y);
8314 struct ElementInfo *ei = &element_info[element];
8315 struct ElementChangeInfo *change = &ei->change_page[page];
8318 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8319 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8322 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8323 x, y, element, element_info[element].token_name);
8324 printf("HandleElementChange(): This should never happen!\n");
8329 /* this can happen with classic bombs on walkable, changing elements */
8330 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8333 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8334 ChangeDelay[x][y] = 0;
8340 if (ChangeDelay[x][y] == 0) /* initialize element change */
8342 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8344 if (change->can_change)
8346 ResetGfxAnimation(x, y);
8347 ResetRandomAnimationValue(x, y);
8349 if (change->pre_change_function)
8350 change->pre_change_function(x, y);
8354 ChangeDelay[x][y]--;
8356 if (ChangeDelay[x][y] != 0) /* continue element change */
8358 if (change->can_change)
8360 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8362 if (IS_ANIMATED(graphic))
8363 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8365 if (change->change_function)
8366 change->change_function(x, y);
8369 else /* finish element change */
8371 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8373 page = ChangePage[x][y];
8374 ChangePage[x][y] = -1;
8376 change = &ei->change_page[page];
8379 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8381 ChangeDelay[x][y] = 1; /* try change after next move step */
8382 ChangePage[x][y] = page; /* remember page to use for change */
8387 if (change->can_change)
8389 if (ChangeElement(x, y, element, page))
8391 if (change->post_change_function)
8392 change->post_change_function(x, y);
8396 if (change->has_action)
8397 ExecuteCustomElementAction(x, y, element, page);
8403 static void HandleElementChange(int x, int y, int page)
8405 int element = MovingOrBlocked2Element(x, y);
8406 struct ElementInfo *ei = &element_info[element];
8407 struct ElementChangeInfo *change = &ei->change_page[page];
8410 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8413 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8414 x, y, element, element_info[element].token_name);
8415 printf("HandleElementChange(): This should never happen!\n");
8420 /* this can happen with classic bombs on walkable, changing elements */
8421 if (!CAN_CHANGE(element))
8424 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8425 ChangeDelay[x][y] = 0;
8431 if (ChangeDelay[x][y] == 0) /* initialize element change */
8433 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8435 ResetGfxAnimation(x, y);
8436 ResetRandomAnimationValue(x, y);
8438 if (change->pre_change_function)
8439 change->pre_change_function(x, y);
8442 ChangeDelay[x][y]--;
8444 if (ChangeDelay[x][y] != 0) /* continue element change */
8446 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8448 if (IS_ANIMATED(graphic))
8449 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8451 if (change->change_function)
8452 change->change_function(x, y);
8454 else /* finish element change */
8456 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8458 page = ChangePage[x][y];
8459 ChangePage[x][y] = -1;
8461 change = &ei->change_page[page];
8464 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8466 ChangeDelay[x][y] = 1; /* try change after next move step */
8467 ChangePage[x][y] = page; /* remember page to use for change */
8472 if (ChangeElement(x, y, element, page))
8474 if (change->post_change_function)
8475 change->post_change_function(x, y);
8482 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8483 int trigger_element,
8489 boolean change_done_any = FALSE;
8490 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8493 if (!(trigger_events[trigger_element][trigger_event]))
8496 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8498 int element = EL_CUSTOM_START + i;
8499 boolean change_done = FALSE;
8502 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8503 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8506 for (p = 0; p < element_info[element].num_change_pages; p++)
8508 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8510 if (change->can_change_or_has_action &&
8511 change->has_event[trigger_event] &&
8512 change->trigger_side & trigger_side &&
8513 change->trigger_player & trigger_player &&
8514 change->trigger_page & trigger_page_bits &&
8515 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8517 change->actual_trigger_element = trigger_element;
8518 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8519 change->actual_trigger_side = trigger_side;
8520 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8521 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8523 if ((change->can_change && !change_done) || change->has_action)
8527 SCAN_PLAYFIELD(x, y)
8529 if (Feld[x][y] == element)
8531 if (change->can_change && !change_done)
8533 ChangeDelay[x][y] = 1;
8534 ChangeEvent[x][y] = trigger_event;
8536 HandleElementChange(x, y, p);
8538 #if USE_NEW_DELAYED_ACTION
8539 else if (change->has_action)
8541 ExecuteCustomElementAction(x, y, element, p);
8542 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8545 if (change->has_action)
8547 ExecuteCustomElementAction(x, y, element, p);
8548 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8554 if (change->can_change)
8557 change_done_any = TRUE;
8564 return change_done_any;
8567 static boolean CheckElementChangeExt(int x, int y,
8569 int trigger_element,
8574 boolean change_done = FALSE;
8577 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8578 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8581 if (Feld[x][y] == EL_BLOCKED)
8583 Blocked2Moving(x, y, &x, &y);
8584 element = Feld[x][y];
8588 /* check if element has already changed */
8589 if (Feld[x][y] != element)
8592 /* check if element has already changed or is about to change after moving */
8593 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8594 Feld[x][y] != element) ||
8596 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8597 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8598 ChangePage[x][y] != -1)))
8602 for (p = 0; p < element_info[element].num_change_pages; p++)
8604 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8606 boolean check_trigger_element =
8607 (trigger_event == CE_TOUCHING_X ||
8608 trigger_event == CE_HITTING_X ||
8609 trigger_event == CE_HIT_BY_X);
8611 if (change->can_change_or_has_action &&
8612 change->has_event[trigger_event] &&
8613 change->trigger_side & trigger_side &&
8614 change->trigger_player & trigger_player &&
8615 (!check_trigger_element ||
8616 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8618 change->actual_trigger_element = trigger_element;
8619 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8620 change->actual_trigger_side = trigger_side;
8621 change->actual_trigger_ce_value = CustomValue[x][y];
8622 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8624 /* special case: trigger element not at (x,y) position for some events */
8625 if (check_trigger_element)
8637 { 0, 0 }, { 0, 0 }, { 0, 0 },
8641 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8642 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8644 change->actual_trigger_ce_value = CustomValue[xx][yy];
8645 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8648 if (change->can_change && !change_done)
8650 ChangeDelay[x][y] = 1;
8651 ChangeEvent[x][y] = trigger_event;
8653 HandleElementChange(x, y, p);
8657 #if USE_NEW_DELAYED_ACTION
8658 else if (change->has_action)
8660 ExecuteCustomElementAction(x, y, element, p);
8661 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8664 if (change->has_action)
8666 ExecuteCustomElementAction(x, y, element, p);
8667 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8676 static void PlayPlayerSound(struct PlayerInfo *player)
8678 int jx = player->jx, jy = player->jy;
8679 int sound_element = player->artwork_element;
8680 int last_action = player->last_action_waiting;
8681 int action = player->action_waiting;
8683 if (player->is_waiting)
8685 if (action != last_action)
8686 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8688 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8692 if (action != last_action)
8693 StopSound(element_info[sound_element].sound[last_action]);
8695 if (last_action == ACTION_SLEEPING)
8696 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8700 static void PlayAllPlayersSound()
8704 for (i = 0; i < MAX_PLAYERS; i++)
8705 if (stored_player[i].active)
8706 PlayPlayerSound(&stored_player[i]);
8709 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8711 boolean last_waiting = player->is_waiting;
8712 int move_dir = player->MovDir;
8714 player->dir_waiting = move_dir;
8715 player->last_action_waiting = player->action_waiting;
8719 if (!last_waiting) /* not waiting -> waiting */
8721 player->is_waiting = TRUE;
8723 player->frame_counter_bored =
8725 game.player_boring_delay_fixed +
8726 GetSimpleRandom(game.player_boring_delay_random);
8727 player->frame_counter_sleeping =
8729 game.player_sleeping_delay_fixed +
8730 GetSimpleRandom(game.player_sleeping_delay_random);
8732 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
8735 if (game.player_sleeping_delay_fixed +
8736 game.player_sleeping_delay_random > 0 &&
8737 player->anim_delay_counter == 0 &&
8738 player->post_delay_counter == 0 &&
8739 FrameCounter >= player->frame_counter_sleeping)
8740 player->is_sleeping = TRUE;
8741 else if (game.player_boring_delay_fixed +
8742 game.player_boring_delay_random > 0 &&
8743 FrameCounter >= player->frame_counter_bored)
8744 player->is_bored = TRUE;
8746 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8747 player->is_bored ? ACTION_BORING :
8750 if (player->is_sleeping && player->use_murphy)
8752 /* special case for sleeping Murphy when leaning against non-free tile */
8754 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
8755 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
8756 !IS_MOVING(player->jx - 1, player->jy)))
8758 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
8759 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
8760 !IS_MOVING(player->jx + 1, player->jy)))
8761 move_dir = MV_RIGHT;
8763 player->is_sleeping = FALSE;
8765 player->dir_waiting = move_dir;
8768 if (player->is_sleeping)
8770 if (player->num_special_action_sleeping > 0)
8772 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8774 int last_special_action = player->special_action_sleeping;
8775 int num_special_action = player->num_special_action_sleeping;
8776 int special_action =
8777 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8778 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8779 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8780 last_special_action + 1 : ACTION_SLEEPING);
8781 int special_graphic =
8782 el_act_dir2img(player->artwork_element, special_action, move_dir);
8784 player->anim_delay_counter =
8785 graphic_info[special_graphic].anim_delay_fixed +
8786 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8787 player->post_delay_counter =
8788 graphic_info[special_graphic].post_delay_fixed +
8789 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8791 player->special_action_sleeping = special_action;
8794 if (player->anim_delay_counter > 0)
8796 player->action_waiting = player->special_action_sleeping;
8797 player->anim_delay_counter--;
8799 else if (player->post_delay_counter > 0)
8801 player->post_delay_counter--;
8805 else if (player->is_bored)
8807 if (player->num_special_action_bored > 0)
8809 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8811 int special_action =
8812 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
8813 int special_graphic =
8814 el_act_dir2img(player->artwork_element, special_action, move_dir);
8816 player->anim_delay_counter =
8817 graphic_info[special_graphic].anim_delay_fixed +
8818 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8819 player->post_delay_counter =
8820 graphic_info[special_graphic].post_delay_fixed +
8821 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8823 player->special_action_bored = special_action;
8826 if (player->anim_delay_counter > 0)
8828 player->action_waiting = player->special_action_bored;
8829 player->anim_delay_counter--;
8831 else if (player->post_delay_counter > 0)
8833 player->post_delay_counter--;
8838 else if (last_waiting) /* waiting -> not waiting */
8840 player->is_waiting = FALSE;
8841 player->is_bored = FALSE;
8842 player->is_sleeping = FALSE;
8844 player->frame_counter_bored = -1;
8845 player->frame_counter_sleeping = -1;
8847 player->anim_delay_counter = 0;
8848 player->post_delay_counter = 0;
8850 player->dir_waiting = player->MovDir;
8851 player->action_waiting = ACTION_DEFAULT;
8853 player->special_action_bored = ACTION_DEFAULT;
8854 player->special_action_sleeping = ACTION_DEFAULT;
8858 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8860 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8861 int left = player_action & JOY_LEFT;
8862 int right = player_action & JOY_RIGHT;
8863 int up = player_action & JOY_UP;
8864 int down = player_action & JOY_DOWN;
8865 int button1 = player_action & JOY_BUTTON_1;
8866 int button2 = player_action & JOY_BUTTON_2;
8867 int dx = (left ? -1 : right ? 1 : 0);
8868 int dy = (up ? -1 : down ? 1 : 0);
8870 if (!player->active || tape.pausing)
8876 snapped = SnapField(player, dx, dy);
8880 dropped = DropElement(player);
8882 moved = MovePlayer(player, dx, dy);
8885 if (tape.single_step && tape.recording && !tape.pausing)
8887 if (button1 || (dropped && !moved))
8889 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8890 SnapField(player, 0, 0); /* stop snapping */
8894 SetPlayerWaiting(player, FALSE);
8896 return player_action;
8900 /* no actions for this player (no input at player's configured device) */
8902 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8903 SnapField(player, 0, 0);
8904 CheckGravityMovementWhenNotMoving(player);
8906 if (player->MovPos == 0)
8907 SetPlayerWaiting(player, TRUE);
8909 if (player->MovPos == 0) /* needed for tape.playing */
8910 player->is_moving = FALSE;
8912 player->is_dropping = FALSE;
8913 player->is_dropping_pressed = FALSE;
8914 player->drop_pressed_delay = 0;
8920 static void CheckLevelTime()
8924 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8926 if (level.native_em_level->lev->home == 0) /* all players at home */
8928 PlayerWins(local_player);
8930 AllPlayersGone = TRUE;
8932 level.native_em_level->lev->home = -1;
8935 if (level.native_em_level->ply[0]->alive == 0 &&
8936 level.native_em_level->ply[1]->alive == 0 &&
8937 level.native_em_level->ply[2]->alive == 0 &&
8938 level.native_em_level->ply[3]->alive == 0) /* all dead */
8939 AllPlayersGone = TRUE;
8942 if (TimeFrames >= FRAMES_PER_SECOND)
8947 for (i = 0; i < MAX_PLAYERS; i++)
8949 struct PlayerInfo *player = &stored_player[i];
8951 if (SHIELD_ON(player))
8953 player->shield_normal_time_left--;
8955 if (player->shield_deadly_time_left > 0)
8956 player->shield_deadly_time_left--;
8960 if (!local_player->LevelSolved && !level.use_step_counter)
8968 if (TimeLeft <= 10 && setup.time_limit)
8969 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
8971 DrawGameValue_Time(TimeLeft);
8973 if (!TimeLeft && setup.time_limit)
8975 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8976 level.native_em_level->lev->killed_out_of_time = TRUE;
8978 for (i = 0; i < MAX_PLAYERS; i++)
8979 KillPlayer(&stored_player[i]);
8982 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8983 DrawGameValue_Time(TimePlayed);
8985 level.native_em_level->lev->time =
8986 (level.time == 0 ? TimePlayed : TimeLeft);
8989 if (tape.recording || tape.playing)
8990 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8994 void AdvanceFrameAndPlayerCounters(int player_nr)
8998 /* advance frame counters (global frame counter and time frame counter) */
9002 /* advance player counters (counters for move delay, move animation etc.) */
9003 for (i = 0; i < MAX_PLAYERS; i++)
9005 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9006 int move_delay_value = stored_player[i].move_delay_value;
9007 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9009 if (!advance_player_counters) /* not all players may be affected */
9012 #if USE_NEW_PLAYER_ANIM
9013 if (move_frames == 0) /* less than one move per game frame */
9015 int stepsize = TILEX / move_delay_value;
9016 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9017 int count = (stored_player[i].is_moving ?
9018 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9020 if (count % delay == 0)
9025 stored_player[i].Frame += move_frames;
9027 if (stored_player[i].MovPos != 0)
9028 stored_player[i].StepFrame += move_frames;
9030 if (stored_player[i].move_delay > 0)
9031 stored_player[i].move_delay--;
9033 /* due to bugs in previous versions, counter must count up, not down */
9034 if (stored_player[i].push_delay != -1)
9035 stored_player[i].push_delay++;
9037 if (stored_player[i].drop_delay > 0)
9038 stored_player[i].drop_delay--;
9040 if (stored_player[i].is_dropping_pressed)
9041 stored_player[i].drop_pressed_delay++;
9045 void StartGameActions(boolean init_network_game, boolean record_tape,
9048 unsigned long new_random_seed = InitRND(random_seed);
9051 TapeStartRecording(new_random_seed);
9053 #if defined(NETWORK_AVALIABLE)
9054 if (init_network_game)
9056 SendToServer_StartPlaying();
9067 static unsigned long game_frame_delay = 0;
9068 unsigned long game_frame_delay_value;
9069 byte *recorded_player_action;
9070 byte summarized_player_action = 0;
9071 byte tape_action[MAX_PLAYERS];
9074 if (game.restart_level)
9075 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9077 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9079 if (level.native_em_level->lev->home == 0) /* all players at home */
9081 PlayerWins(local_player);
9083 AllPlayersGone = TRUE;
9085 level.native_em_level->lev->home = -1;
9088 if (level.native_em_level->ply[0]->alive == 0 &&
9089 level.native_em_level->ply[1]->alive == 0 &&
9090 level.native_em_level->ply[2]->alive == 0 &&
9091 level.native_em_level->ply[3]->alive == 0) /* all dead */
9092 AllPlayersGone = TRUE;
9095 if (local_player->LevelSolved)
9098 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9101 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9104 game_frame_delay_value =
9105 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9107 if (tape.playing && tape.warp_forward && !tape.pausing)
9108 game_frame_delay_value = 0;
9110 /* ---------- main game synchronization point ---------- */
9112 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9114 if (network_playing && !network_player_action_received)
9116 /* try to get network player actions in time */
9118 #if defined(NETWORK_AVALIABLE)
9119 /* last chance to get network player actions without main loop delay */
9123 /* game was quit by network peer */
9124 if (game_status != GAME_MODE_PLAYING)
9127 if (!network_player_action_received)
9128 return; /* failed to get network player actions in time */
9130 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9136 /* at this point we know that we really continue executing the game */
9138 network_player_action_received = FALSE;
9140 /* when playing tape, read previously recorded player input from tape data */
9141 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9144 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9149 if (tape.set_centered_player)
9151 game.centered_player_nr_next = tape.centered_player_nr_next;
9152 game.set_centered_player = TRUE;
9155 for (i = 0; i < MAX_PLAYERS; i++)
9157 summarized_player_action |= stored_player[i].action;
9159 if (!network_playing)
9160 stored_player[i].effective_action = stored_player[i].action;
9163 #if defined(NETWORK_AVALIABLE)
9164 if (network_playing)
9165 SendToServer_MovePlayer(summarized_player_action);
9168 if (!options.network && !setup.team_mode)
9169 local_player->effective_action = summarized_player_action;
9171 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9173 for (i = 0; i < MAX_PLAYERS; i++)
9174 stored_player[i].effective_action =
9175 (i == game.centered_player_nr ? summarized_player_action : 0);
9178 if (recorded_player_action != NULL)
9179 for (i = 0; i < MAX_PLAYERS; i++)
9180 stored_player[i].effective_action = recorded_player_action[i];
9182 for (i = 0; i < MAX_PLAYERS; i++)
9184 tape_action[i] = stored_player[i].effective_action;
9186 /* (this can only happen in the R'n'D game engine) */
9187 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9188 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9191 /* only record actions from input devices, but not programmed actions */
9193 TapeRecordAction(tape_action);
9195 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9197 GameActions_EM_Main();
9205 void GameActions_EM_Main()
9207 byte effective_action[MAX_PLAYERS];
9208 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9211 for (i = 0; i < MAX_PLAYERS; i++)
9212 effective_action[i] = stored_player[i].effective_action;
9214 GameActions_EM(effective_action, warp_mode);
9218 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9221 void GameActions_RND()
9223 int magic_wall_x = 0, magic_wall_y = 0;
9224 int i, x, y, element, graphic;
9226 InitPlayfieldScanModeVars();
9228 #if USE_ONE_MORE_CHANGE_PER_FRAME
9229 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9231 SCAN_PLAYFIELD(x, y)
9233 ChangeCount[x][y] = 0;
9234 ChangeEvent[x][y] = -1;
9239 if (game.set_centered_player)
9241 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9243 /* switching to "all players" only possible if all players fit to screen */
9244 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9246 game.centered_player_nr_next = game.centered_player_nr;
9247 game.set_centered_player = FALSE;
9250 /* do not switch focus to non-existing (or non-active) player */
9251 if (game.centered_player_nr_next >= 0 &&
9252 !stored_player[game.centered_player_nr_next].active)
9254 game.centered_player_nr_next = game.centered_player_nr;
9255 game.set_centered_player = FALSE;
9259 if (game.set_centered_player &&
9260 ScreenMovPos == 0) /* screen currently aligned at tile position */
9264 if (game.centered_player_nr_next == -1)
9266 setScreenCenteredToAllPlayers(&sx, &sy);
9270 sx = stored_player[game.centered_player_nr_next].jx;
9271 sy = stored_player[game.centered_player_nr_next].jy;
9274 game.centered_player_nr = game.centered_player_nr_next;
9275 game.set_centered_player = FALSE;
9277 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9278 DrawGameDoorValues();
9281 for (i = 0; i < MAX_PLAYERS; i++)
9283 int actual_player_action = stored_player[i].effective_action;
9286 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9287 - rnd_equinox_tetrachloride 048
9288 - rnd_equinox_tetrachloride_ii 096
9289 - rnd_emanuel_schmieg 002
9290 - doctor_sloan_ww 001, 020
9292 if (stored_player[i].MovPos == 0)
9293 CheckGravityMovement(&stored_player[i]);
9296 /* overwrite programmed action with tape action */
9297 if (stored_player[i].programmed_action)
9298 actual_player_action = stored_player[i].programmed_action;
9300 PlayerActions(&stored_player[i], actual_player_action);
9302 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9305 ScrollScreen(NULL, SCROLL_GO_ON);
9307 /* for backwards compatibility, the following code emulates a fixed bug that
9308 occured when pushing elements (causing elements that just made their last
9309 pushing step to already (if possible) make their first falling step in the
9310 same game frame, which is bad); this code is also needed to use the famous
9311 "spring push bug" which is used in older levels and might be wanted to be
9312 used also in newer levels, but in this case the buggy pushing code is only
9313 affecting the "spring" element and no other elements */
9315 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9317 for (i = 0; i < MAX_PLAYERS; i++)
9319 struct PlayerInfo *player = &stored_player[i];
9323 if (player->active && player->is_pushing && player->is_moving &&
9325 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9326 Feld[x][y] == EL_SPRING))
9328 ContinueMoving(x, y);
9330 /* continue moving after pushing (this is actually a bug) */
9331 if (!IS_MOVING(x, y))
9339 SCAN_PLAYFIELD(x, y)
9341 ChangeCount[x][y] = 0;
9342 ChangeEvent[x][y] = -1;
9344 /* this must be handled before main playfield loop */
9345 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9348 if (MovDelay[x][y] <= 0)
9352 #if USE_NEW_SNAP_DELAY
9353 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9356 if (MovDelay[x][y] <= 0)
9359 DrawLevelField(x, y);
9361 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9367 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9369 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9370 printf("GameActions(): This should never happen!\n");
9372 ChangePage[x][y] = -1;
9377 if (WasJustMoving[x][y] > 0)
9378 WasJustMoving[x][y]--;
9379 if (WasJustFalling[x][y] > 0)
9380 WasJustFalling[x][y]--;
9381 if (CheckCollision[x][y] > 0)
9382 CheckCollision[x][y]--;
9386 /* reset finished pushing action (not done in ContinueMoving() to allow
9387 continuous pushing animation for elements with zero push delay) */
9388 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9390 ResetGfxAnimation(x, y);
9391 DrawLevelField(x, y);
9395 if (IS_BLOCKED(x, y))
9399 Blocked2Moving(x, y, &oldx, &oldy);
9400 if (!IS_MOVING(oldx, oldy))
9402 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9403 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9404 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9405 printf("GameActions(): This should never happen!\n");
9411 SCAN_PLAYFIELD(x, y)
9413 element = Feld[x][y];
9414 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9416 ResetGfxFrame(x, y, TRUE);
9418 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9419 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9420 ResetRandomAnimationValue(x, y);
9422 SetRandomAnimationValue(x, y);
9424 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9426 if (IS_INACTIVE(element))
9428 if (IS_ANIMATED(graphic))
9429 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9434 /* this may take place after moving, so 'element' may have changed */
9435 if (IS_CHANGING(x, y) &&
9436 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9438 int page = element_info[element].event_page_nr[CE_DELAY];
9441 HandleElementChange(x, y, page);
9443 if (CAN_CHANGE(element))
9444 HandleElementChange(x, y, page);
9446 if (HAS_ACTION(element))
9447 ExecuteCustomElementAction(x, y, element, page);
9450 element = Feld[x][y];
9451 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9454 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9458 element = Feld[x][y];
9459 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9461 if (IS_ANIMATED(graphic) &&
9464 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9466 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9467 DrawTwinkleOnField(x, y);
9469 else if ((element == EL_ACID ||
9470 element == EL_EXIT_OPEN ||
9471 element == EL_SP_EXIT_OPEN ||
9472 element == EL_SP_TERMINAL ||
9473 element == EL_SP_TERMINAL_ACTIVE ||
9474 element == EL_EXTRA_TIME ||
9475 element == EL_SHIELD_NORMAL ||
9476 element == EL_SHIELD_DEADLY) &&
9477 IS_ANIMATED(graphic))
9478 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9479 else if (IS_MOVING(x, y))
9480 ContinueMoving(x, y);
9481 else if (IS_ACTIVE_BOMB(element))
9482 CheckDynamite(x, y);
9483 else if (element == EL_AMOEBA_GROWING)
9484 AmoebeWaechst(x, y);
9485 else if (element == EL_AMOEBA_SHRINKING)
9486 AmoebaDisappearing(x, y);
9488 #if !USE_NEW_AMOEBA_CODE
9489 else if (IS_AMOEBALIVE(element))
9490 AmoebeAbleger(x, y);
9493 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9495 else if (element == EL_EXIT_CLOSED)
9497 else if (element == EL_SP_EXIT_CLOSED)
9499 else if (element == EL_EXPANDABLE_WALL_GROWING)
9501 else if (element == EL_EXPANDABLE_WALL ||
9502 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9503 element == EL_EXPANDABLE_WALL_VERTICAL ||
9504 element == EL_EXPANDABLE_WALL_ANY ||
9505 element == EL_BD_EXPANDABLE_WALL)
9507 else if (element == EL_FLAMES)
9508 CheckForDragon(x, y);
9509 else if (element == EL_EXPLOSION)
9510 ; /* drawing of correct explosion animation is handled separately */
9511 else if (element == EL_ELEMENT_SNAPPING ||
9512 element == EL_DIAGONAL_SHRINKING ||
9513 element == EL_DIAGONAL_GROWING)
9515 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9517 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9519 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9520 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9522 if (IS_BELT_ACTIVE(element))
9523 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9525 if (game.magic_wall_active)
9527 int jx = local_player->jx, jy = local_player->jy;
9529 /* play the element sound at the position nearest to the player */
9530 if ((element == EL_MAGIC_WALL_FULL ||
9531 element == EL_MAGIC_WALL_ACTIVE ||
9532 element == EL_MAGIC_WALL_EMPTYING ||
9533 element == EL_BD_MAGIC_WALL_FULL ||
9534 element == EL_BD_MAGIC_WALL_ACTIVE ||
9535 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9536 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9544 #if USE_NEW_AMOEBA_CODE
9545 /* new experimental amoeba growth stuff */
9546 if (!(FrameCounter % 8))
9548 static unsigned long random = 1684108901;
9550 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9552 x = RND(lev_fieldx);
9553 y = RND(lev_fieldy);
9554 element = Feld[x][y];
9556 if (!IS_PLAYER(x,y) &&
9557 (element == EL_EMPTY ||
9558 CAN_GROW_INTO(element) ||
9559 element == EL_QUICKSAND_EMPTY ||
9560 element == EL_ACID_SPLASH_LEFT ||
9561 element == EL_ACID_SPLASH_RIGHT))
9563 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9564 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9565 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9566 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9567 Feld[x][y] = EL_AMOEBA_DROP;
9570 random = random * 129 + 1;
9576 if (game.explosions_delayed)
9579 game.explosions_delayed = FALSE;
9581 SCAN_PLAYFIELD(x, y)
9583 element = Feld[x][y];
9585 if (ExplodeField[x][y])
9586 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9587 else if (element == EL_EXPLOSION)
9588 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9590 ExplodeField[x][y] = EX_TYPE_NONE;
9593 game.explosions_delayed = TRUE;
9596 if (game.magic_wall_active)
9598 if (!(game.magic_wall_time_left % 4))
9600 int element = Feld[magic_wall_x][magic_wall_y];
9602 if (element == EL_BD_MAGIC_WALL_FULL ||
9603 element == EL_BD_MAGIC_WALL_ACTIVE ||
9604 element == EL_BD_MAGIC_WALL_EMPTYING)
9605 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9607 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9610 if (game.magic_wall_time_left > 0)
9612 game.magic_wall_time_left--;
9613 if (!game.magic_wall_time_left)
9615 SCAN_PLAYFIELD(x, y)
9617 element = Feld[x][y];
9619 if (element == EL_MAGIC_WALL_ACTIVE ||
9620 element == EL_MAGIC_WALL_FULL)
9622 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9623 DrawLevelField(x, y);
9625 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9626 element == EL_BD_MAGIC_WALL_FULL)
9628 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9629 DrawLevelField(x, y);
9633 game.magic_wall_active = FALSE;
9638 if (game.light_time_left > 0)
9640 game.light_time_left--;
9642 if (game.light_time_left == 0)
9643 RedrawAllLightSwitchesAndInvisibleElements();
9646 if (game.timegate_time_left > 0)
9648 game.timegate_time_left--;
9650 if (game.timegate_time_left == 0)
9651 CloseAllOpenTimegates();
9654 if (game.lenses_time_left > 0)
9656 game.lenses_time_left--;
9658 if (game.lenses_time_left == 0)
9659 RedrawAllInvisibleElementsForLenses();
9662 if (game.magnify_time_left > 0)
9664 game.magnify_time_left--;
9666 if (game.magnify_time_left == 0)
9667 RedrawAllInvisibleElementsForMagnifier();
9670 for (i = 0; i < MAX_PLAYERS; i++)
9672 struct PlayerInfo *player = &stored_player[i];
9674 if (SHIELD_ON(player))
9676 if (player->shield_deadly_time_left)
9677 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9678 else if (player->shield_normal_time_left)
9679 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9686 PlayAllPlayersSound();
9688 if (options.debug) /* calculate frames per second */
9690 static unsigned long fps_counter = 0;
9691 static int fps_frames = 0;
9692 unsigned long fps_delay_ms = Counter() - fps_counter;
9696 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9698 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9701 fps_counter = Counter();
9704 redraw_mask |= REDRAW_FPS;
9707 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9709 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9711 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9713 local_player->show_envelope = 0;
9716 /* use random number generator in every frame to make it less predictable */
9717 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9721 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9723 int min_x = x, min_y = y, max_x = x, max_y = y;
9726 for (i = 0; i < MAX_PLAYERS; i++)
9728 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9730 if (!stored_player[i].active || &stored_player[i] == player)
9733 min_x = MIN(min_x, jx);
9734 min_y = MIN(min_y, jy);
9735 max_x = MAX(max_x, jx);
9736 max_y = MAX(max_y, jy);
9739 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9742 static boolean AllPlayersInVisibleScreen()
9746 for (i = 0; i < MAX_PLAYERS; i++)
9748 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9750 if (!stored_player[i].active)
9753 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9760 void ScrollLevel(int dx, int dy)
9762 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9765 BlitBitmap(drawto_field, drawto_field,
9766 FX + TILEX * (dx == -1) - softscroll_offset,
9767 FY + TILEY * (dy == -1) - softscroll_offset,
9768 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9769 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9770 FX + TILEX * (dx == 1) - softscroll_offset,
9771 FY + TILEY * (dy == 1) - softscroll_offset);
9775 x = (dx == 1 ? BX1 : BX2);
9776 for (y = BY1; y <= BY2; y++)
9777 DrawScreenField(x, y);
9782 y = (dy == 1 ? BY1 : BY2);
9783 for (x = BX1; x <= BX2; x++)
9784 DrawScreenField(x, y);
9787 redraw_mask |= REDRAW_FIELD;
9790 static boolean canFallDown(struct PlayerInfo *player)
9792 int jx = player->jx, jy = player->jy;
9794 return (IN_LEV_FIELD(jx, jy + 1) &&
9795 (IS_FREE(jx, jy + 1) ||
9796 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9797 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9798 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9801 static boolean canPassField(int x, int y, int move_dir)
9803 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9804 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9805 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9808 int element = Feld[x][y];
9810 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9811 !CAN_MOVE(element) &&
9812 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9813 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9814 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9817 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9819 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9820 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9821 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9825 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9826 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9827 (IS_DIGGABLE(Feld[newx][newy]) ||
9828 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9829 canPassField(newx, newy, move_dir)));
9832 static void CheckGravityMovement(struct PlayerInfo *player)
9834 #if USE_PLAYER_GRAVITY
9835 if (player->gravity && !player->programmed_action)
9837 if (game.gravity && !player->programmed_action)
9840 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9841 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9842 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
9843 int jx = player->jx, jy = player->jy;
9844 boolean player_is_moving_to_valid_field =
9845 (!player_is_snapping &&
9846 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9847 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9848 boolean player_can_fall_down = canFallDown(player);
9850 if (player_can_fall_down &&
9851 !player_is_moving_to_valid_field)
9852 player->programmed_action = MV_DOWN;
9856 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9858 return CheckGravityMovement(player);
9860 #if USE_PLAYER_GRAVITY
9861 if (player->gravity && !player->programmed_action)
9863 if (game.gravity && !player->programmed_action)
9866 int jx = player->jx, jy = player->jy;
9867 boolean field_under_player_is_free =
9868 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9869 boolean player_is_standing_on_valid_field =
9870 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9871 (IS_WALKABLE(Feld[jx][jy]) &&
9872 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9874 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9875 player->programmed_action = MV_DOWN;
9881 -----------------------------------------------------------------------------
9882 dx, dy: direction (non-diagonal) to try to move the player to
9883 real_dx, real_dy: direction as read from input device (can be diagonal)
9886 boolean MovePlayerOneStep(struct PlayerInfo *player,
9887 int dx, int dy, int real_dx, int real_dy)
9889 int jx = player->jx, jy = player->jy;
9890 int new_jx = jx + dx, new_jy = jy + dy;
9891 #if !USE_FIXED_DONT_RUN_INTO
9895 boolean player_can_move = !player->cannot_move;
9897 if (!player->active || (!dx && !dy))
9898 return MP_NO_ACTION;
9900 player->MovDir = (dx < 0 ? MV_LEFT :
9903 dy > 0 ? MV_DOWN : MV_NONE);
9905 if (!IN_LEV_FIELD(new_jx, new_jy))
9906 return MP_NO_ACTION;
9908 if (!player_can_move)
9910 if (player->MovPos == 0)
9912 player->is_moving = FALSE;
9913 player->is_digging = FALSE;
9914 player->is_collecting = FALSE;
9915 player->is_snapping = FALSE;
9916 player->is_pushing = FALSE;
9921 if (!options.network && game.centered_player_nr == -1 &&
9922 !AllPlayersInSight(player, new_jx, new_jy))
9923 return MP_NO_ACTION;
9925 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9926 return MP_NO_ACTION;
9929 #if !USE_FIXED_DONT_RUN_INTO
9930 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9932 /* (moved to DigField()) */
9933 if (player_can_move && DONT_RUN_INTO(element))
9935 if (element == EL_ACID && dx == 0 && dy == 1)
9937 SplashAcid(new_jx, new_jy);
9938 Feld[jx][jy] = EL_PLAYER_1;
9939 InitMovingField(jx, jy, MV_DOWN);
9940 Store[jx][jy] = EL_ACID;
9941 ContinueMoving(jx, jy);
9945 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9951 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9952 if (can_move != MP_MOVING)
9955 /* check if DigField() has caused relocation of the player */
9956 if (player->jx != jx || player->jy != jy)
9957 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
9959 StorePlayer[jx][jy] = 0;
9960 player->last_jx = jx;
9961 player->last_jy = jy;
9962 player->jx = new_jx;
9963 player->jy = new_jy;
9964 StorePlayer[new_jx][new_jy] = player->element_nr;
9966 if (player->move_delay_value_next != -1)
9968 player->move_delay_value = player->move_delay_value_next;
9969 player->move_delay_value_next = -1;
9973 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9975 player->step_counter++;
9977 PlayerVisit[jx][jy] = FrameCounter;
9979 #if USE_UFAST_PLAYER_EXIT_BUGFIX
9980 player->is_moving = TRUE;
9984 /* should better be called in MovePlayer(), but this breaks some tapes */
9985 ScrollPlayer(player, SCROLL_INIT);
9991 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9993 int jx = player->jx, jy = player->jy;
9994 int old_jx = jx, old_jy = jy;
9995 int moved = MP_NO_ACTION;
9997 if (!player->active)
10002 if (player->MovPos == 0)
10004 player->is_moving = FALSE;
10005 player->is_digging = FALSE;
10006 player->is_collecting = FALSE;
10007 player->is_snapping = FALSE;
10008 player->is_pushing = FALSE;
10014 if (player->move_delay > 0)
10017 player->move_delay = -1; /* set to "uninitialized" value */
10019 /* store if player is automatically moved to next field */
10020 player->is_auto_moving = (player->programmed_action != MV_NONE);
10022 /* remove the last programmed player action */
10023 player->programmed_action = 0;
10025 if (player->MovPos)
10027 /* should only happen if pre-1.2 tape recordings are played */
10028 /* this is only for backward compatibility */
10030 int original_move_delay_value = player->move_delay_value;
10033 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10037 /* scroll remaining steps with finest movement resolution */
10038 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10040 while (player->MovPos)
10042 ScrollPlayer(player, SCROLL_GO_ON);
10043 ScrollScreen(NULL, SCROLL_GO_ON);
10045 AdvanceFrameAndPlayerCounters(player->index_nr);
10051 player->move_delay_value = original_move_delay_value;
10054 player->is_active = FALSE;
10056 if (player->last_move_dir & MV_HORIZONTAL)
10058 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10059 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10063 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10064 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10067 #if USE_FIXED_BORDER_RUNNING_GFX
10068 if (!moved && !player->is_active)
10070 player->is_moving = FALSE;
10071 player->is_digging = FALSE;
10072 player->is_collecting = FALSE;
10073 player->is_snapping = FALSE;
10074 player->is_pushing = FALSE;
10082 if (moved & MP_MOVING && !ScreenMovPos &&
10083 (player->index_nr == game.centered_player_nr ||
10084 game.centered_player_nr == -1))
10086 if (moved & MP_MOVING && !ScreenMovPos &&
10087 (player == local_player || !options.network))
10090 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10091 int offset = (setup.scroll_delay ? 3 : 0);
10093 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10095 /* actual player has left the screen -- scroll in that direction */
10096 if (jx != old_jx) /* player has moved horizontally */
10097 scroll_x += (jx - old_jx);
10098 else /* player has moved vertically */
10099 scroll_y += (jy - old_jy);
10103 if (jx != old_jx) /* player has moved horizontally */
10105 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10106 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10107 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10109 /* don't scroll over playfield boundaries */
10110 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10111 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10113 /* don't scroll more than one field at a time */
10114 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10116 /* don't scroll against the player's moving direction */
10117 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10118 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10119 scroll_x = old_scroll_x;
10121 else /* player has moved vertically */
10123 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10124 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10125 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10127 /* don't scroll over playfield boundaries */
10128 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10129 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10131 /* don't scroll more than one field at a time */
10132 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10134 /* don't scroll against the player's moving direction */
10135 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10136 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10137 scroll_y = old_scroll_y;
10141 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10144 if (!options.network && game.centered_player_nr == -1 &&
10145 !AllPlayersInVisibleScreen())
10147 scroll_x = old_scroll_x;
10148 scroll_y = old_scroll_y;
10152 if (!options.network && !AllPlayersInVisibleScreen())
10154 scroll_x = old_scroll_x;
10155 scroll_y = old_scroll_y;
10160 ScrollScreen(player, SCROLL_INIT);
10161 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10166 player->StepFrame = 0;
10168 if (moved & MP_MOVING)
10170 if (old_jx != jx && old_jy == jy)
10171 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10172 else if (old_jx == jx && old_jy != jy)
10173 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10175 DrawLevelField(jx, jy); /* for "crumbled sand" */
10177 player->last_move_dir = player->MovDir;
10178 player->is_moving = TRUE;
10179 player->is_snapping = FALSE;
10180 player->is_switching = FALSE;
10181 player->is_dropping = FALSE;
10182 player->is_dropping_pressed = FALSE;
10183 player->drop_pressed_delay = 0;
10186 /* should better be called here than above, but this breaks some tapes */
10187 ScrollPlayer(player, SCROLL_INIT);
10192 CheckGravityMovementWhenNotMoving(player);
10194 player->is_moving = FALSE;
10196 /* at this point, the player is allowed to move, but cannot move right now
10197 (e.g. because of something blocking the way) -- ensure that the player
10198 is also allowed to move in the next frame (in old versions before 3.1.1,
10199 the player was forced to wait again for eight frames before next try) */
10201 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10202 player->move_delay = 0; /* allow direct movement in the next frame */
10205 if (player->move_delay == -1) /* not yet initialized by DigField() */
10206 player->move_delay = player->move_delay_value;
10208 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10210 TestIfPlayerTouchesBadThing(jx, jy);
10211 TestIfPlayerTouchesCustomElement(jx, jy);
10214 if (!player->active)
10215 RemovePlayer(player);
10220 void ScrollPlayer(struct PlayerInfo *player, int mode)
10222 int jx = player->jx, jy = player->jy;
10223 int last_jx = player->last_jx, last_jy = player->last_jy;
10224 int move_stepsize = TILEX / player->move_delay_value;
10226 #if USE_NEW_PLAYER_SPEED
10227 if (!player->active)
10230 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10233 if (!player->active || player->MovPos == 0)
10237 if (mode == SCROLL_INIT)
10239 player->actual_frame_counter = FrameCounter;
10240 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10242 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10243 Feld[last_jx][last_jy] == EL_EMPTY)
10245 int last_field_block_delay = 0; /* start with no blocking at all */
10246 int block_delay_adjustment = player->block_delay_adjustment;
10248 /* if player blocks last field, add delay for exactly one move */
10249 if (player->block_last_field)
10251 last_field_block_delay += player->move_delay_value;
10253 /* when blocking enabled, prevent moving up despite gravity */
10254 #if USE_PLAYER_GRAVITY
10255 if (player->gravity && player->MovDir == MV_UP)
10256 block_delay_adjustment = -1;
10258 if (game.gravity && player->MovDir == MV_UP)
10259 block_delay_adjustment = -1;
10263 /* add block delay adjustment (also possible when not blocking) */
10264 last_field_block_delay += block_delay_adjustment;
10266 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10267 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10270 #if USE_NEW_PLAYER_SPEED
10271 if (player->MovPos != 0) /* player has not yet reached destination */
10277 else if (!FrameReached(&player->actual_frame_counter, 1))
10280 #if USE_NEW_PLAYER_SPEED
10281 if (player->MovPos != 0)
10283 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10284 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10286 /* before DrawPlayer() to draw correct player graphic for this case */
10287 if (player->MovPos == 0)
10288 CheckGravityMovement(player);
10291 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10292 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10294 /* before DrawPlayer() to draw correct player graphic for this case */
10295 if (player->MovPos == 0)
10296 CheckGravityMovement(player);
10299 if (player->MovPos == 0) /* player reached destination field */
10301 if (player->move_delay_reset_counter > 0)
10303 player->move_delay_reset_counter--;
10305 if (player->move_delay_reset_counter == 0)
10307 /* continue with normal speed after quickly moving through gate */
10308 HALVE_PLAYER_SPEED(player);
10310 /* be able to make the next move without delay */
10311 player->move_delay = 0;
10315 player->last_jx = jx;
10316 player->last_jy = jy;
10318 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10319 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10320 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10322 DrawPlayer(player); /* needed here only to cleanup last field */
10323 RemovePlayer(player);
10325 if (local_player->friends_still_needed == 0 ||
10326 IS_SP_ELEMENT(Feld[jx][jy]))
10327 PlayerWins(player);
10330 /* this breaks one level: "machine", level 000 */
10332 int move_direction = player->MovDir;
10333 int enter_side = MV_DIR_OPPOSITE(move_direction);
10334 int leave_side = move_direction;
10335 int old_jx = last_jx;
10336 int old_jy = last_jy;
10337 int old_element = Feld[old_jx][old_jy];
10338 int new_element = Feld[jx][jy];
10340 if (IS_CUSTOM_ELEMENT(old_element))
10341 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10343 player->index_bit, leave_side);
10345 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10346 CE_PLAYER_LEAVES_X,
10347 player->index_bit, leave_side);
10349 if (IS_CUSTOM_ELEMENT(new_element))
10350 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10351 player->index_bit, enter_side);
10353 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10354 CE_PLAYER_ENTERS_X,
10355 player->index_bit, enter_side);
10357 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10358 CE_MOVE_OF_X, move_direction);
10361 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10363 TestIfPlayerTouchesBadThing(jx, jy);
10364 TestIfPlayerTouchesCustomElement(jx, jy);
10366 /* needed because pushed element has not yet reached its destination,
10367 so it would trigger a change event at its previous field location */
10368 if (!player->is_pushing)
10369 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10371 if (!player->active)
10372 RemovePlayer(player);
10375 if (!local_player->LevelSolved && level.use_step_counter)
10385 if (TimeLeft <= 10 && setup.time_limit)
10386 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10388 DrawGameValue_Time(TimeLeft);
10390 if (!TimeLeft && setup.time_limit)
10391 for (i = 0; i < MAX_PLAYERS; i++)
10392 KillPlayer(&stored_player[i]);
10394 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10395 DrawGameValue_Time(TimePlayed);
10398 if (tape.single_step && tape.recording && !tape.pausing &&
10399 !player->programmed_action)
10400 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10404 void ScrollScreen(struct PlayerInfo *player, int mode)
10406 static unsigned long screen_frame_counter = 0;
10408 if (mode == SCROLL_INIT)
10410 /* set scrolling step size according to actual player's moving speed */
10411 ScrollStepSize = TILEX / player->move_delay_value;
10413 screen_frame_counter = FrameCounter;
10414 ScreenMovDir = player->MovDir;
10415 ScreenMovPos = player->MovPos;
10416 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10419 else if (!FrameReached(&screen_frame_counter, 1))
10424 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10425 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10426 redraw_mask |= REDRAW_FIELD;
10429 ScreenMovDir = MV_NONE;
10432 void TestIfPlayerTouchesCustomElement(int x, int y)
10434 static int xy[4][2] =
10441 static int trigger_sides[4][2] =
10443 /* center side border side */
10444 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10445 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10446 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10447 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10449 static int touch_dir[4] =
10451 MV_LEFT | MV_RIGHT,
10456 int center_element = Feld[x][y]; /* should always be non-moving! */
10459 for (i = 0; i < NUM_DIRECTIONS; i++)
10461 int xx = x + xy[i][0];
10462 int yy = y + xy[i][1];
10463 int center_side = trigger_sides[i][0];
10464 int border_side = trigger_sides[i][1];
10465 int border_element;
10467 if (!IN_LEV_FIELD(xx, yy))
10470 if (IS_PLAYER(x, y))
10472 struct PlayerInfo *player = PLAYERINFO(x, y);
10474 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10475 border_element = Feld[xx][yy]; /* may be moving! */
10476 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10477 border_element = Feld[xx][yy];
10478 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10479 border_element = MovingOrBlocked2Element(xx, yy);
10481 continue; /* center and border element do not touch */
10483 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10484 player->index_bit, border_side);
10485 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10486 CE_PLAYER_TOUCHES_X,
10487 player->index_bit, border_side);
10489 else if (IS_PLAYER(xx, yy))
10491 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10493 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10495 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10496 continue; /* center and border element do not touch */
10499 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10500 player->index_bit, center_side);
10501 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10502 CE_PLAYER_TOUCHES_X,
10503 player->index_bit, center_side);
10509 #if USE_ELEMENT_TOUCHING_BUGFIX
10511 void TestIfElementTouchesCustomElement(int x, int y)
10513 static int xy[4][2] =
10520 static int trigger_sides[4][2] =
10522 /* center side border side */
10523 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10524 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10525 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10526 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10528 static int touch_dir[4] =
10530 MV_LEFT | MV_RIGHT,
10535 boolean change_center_element = FALSE;
10536 int center_element = Feld[x][y]; /* should always be non-moving! */
10537 int border_element_old[NUM_DIRECTIONS];
10540 for (i = 0; i < NUM_DIRECTIONS; i++)
10542 int xx = x + xy[i][0];
10543 int yy = y + xy[i][1];
10544 int border_element;
10546 border_element_old[i] = -1;
10548 if (!IN_LEV_FIELD(xx, yy))
10551 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10552 border_element = Feld[xx][yy]; /* may be moving! */
10553 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10554 border_element = Feld[xx][yy];
10555 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10556 border_element = MovingOrBlocked2Element(xx, yy);
10558 continue; /* center and border element do not touch */
10560 border_element_old[i] = border_element;
10563 for (i = 0; i < NUM_DIRECTIONS; i++)
10565 int xx = x + xy[i][0];
10566 int yy = y + xy[i][1];
10567 int center_side = trigger_sides[i][0];
10568 int border_element = border_element_old[i];
10570 if (border_element == -1)
10573 /* check for change of border element */
10574 CheckElementChangeBySide(xx, yy, border_element, center_element,
10575 CE_TOUCHING_X, center_side);
10578 for (i = 0; i < NUM_DIRECTIONS; i++)
10580 int border_side = trigger_sides[i][1];
10581 int border_element = border_element_old[i];
10583 if (border_element == -1)
10586 /* check for change of center element (but change it only once) */
10587 if (!change_center_element)
10588 change_center_element =
10589 CheckElementChangeBySide(x, y, center_element, border_element,
10590 CE_TOUCHING_X, border_side);
10596 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10598 static int xy[4][2] =
10605 static int trigger_sides[4][2] =
10607 /* center side border side */
10608 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10609 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10610 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10611 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10613 static int touch_dir[4] =
10615 MV_LEFT | MV_RIGHT,
10620 boolean change_center_element = FALSE;
10621 int center_element = Feld[x][y]; /* should always be non-moving! */
10624 for (i = 0; i < NUM_DIRECTIONS; i++)
10626 int xx = x + xy[i][0];
10627 int yy = y + xy[i][1];
10628 int center_side = trigger_sides[i][0];
10629 int border_side = trigger_sides[i][1];
10630 int border_element;
10632 if (!IN_LEV_FIELD(xx, yy))
10635 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10636 border_element = Feld[xx][yy]; /* may be moving! */
10637 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10638 border_element = Feld[xx][yy];
10639 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10640 border_element = MovingOrBlocked2Element(xx, yy);
10642 continue; /* center and border element do not touch */
10644 /* check for change of center element (but change it only once) */
10645 if (!change_center_element)
10646 change_center_element =
10647 CheckElementChangeBySide(x, y, center_element, border_element,
10648 CE_TOUCHING_X, border_side);
10650 /* check for change of border element */
10651 CheckElementChangeBySide(xx, yy, border_element, center_element,
10652 CE_TOUCHING_X, center_side);
10658 void TestIfElementHitsCustomElement(int x, int y, int direction)
10660 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10661 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10662 int hitx = x + dx, hity = y + dy;
10663 int hitting_element = Feld[x][y];
10664 int touched_element;
10666 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10669 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10670 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10672 if (IN_LEV_FIELD(hitx, hity))
10674 int opposite_direction = MV_DIR_OPPOSITE(direction);
10675 int hitting_side = direction;
10676 int touched_side = opposite_direction;
10677 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10678 MovDir[hitx][hity] != direction ||
10679 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10685 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10686 CE_HITTING_X, touched_side);
10688 CheckElementChangeBySide(hitx, hity, touched_element,
10689 hitting_element, CE_HIT_BY_X, hitting_side);
10691 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10692 CE_HIT_BY_SOMETHING, opposite_direction);
10696 /* "hitting something" is also true when hitting the playfield border */
10697 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10698 CE_HITTING_SOMETHING, direction);
10702 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10704 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10705 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10706 int hitx = x + dx, hity = y + dy;
10707 int hitting_element = Feld[x][y];
10708 int touched_element;
10710 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10711 !IS_FREE(hitx, hity) &&
10712 (!IS_MOVING(hitx, hity) ||
10713 MovDir[hitx][hity] != direction ||
10714 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10717 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10721 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10725 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10726 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10728 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10729 EP_CAN_SMASH_EVERYTHING, direction);
10731 if (IN_LEV_FIELD(hitx, hity))
10733 int opposite_direction = MV_DIR_OPPOSITE(direction);
10734 int hitting_side = direction;
10735 int touched_side = opposite_direction;
10737 int touched_element = MovingOrBlocked2Element(hitx, hity);
10740 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10741 MovDir[hitx][hity] != direction ||
10742 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10751 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10752 CE_SMASHED_BY_SOMETHING, opposite_direction);
10754 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10755 CE_OTHER_IS_SMASHING, touched_side);
10757 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10758 CE_OTHER_GETS_SMASHED, hitting_side);
10764 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10766 int i, kill_x = -1, kill_y = -1;
10768 int bad_element = -1;
10769 static int test_xy[4][2] =
10776 static int test_dir[4] =
10784 for (i = 0; i < NUM_DIRECTIONS; i++)
10786 int test_x, test_y, test_move_dir, test_element;
10788 test_x = good_x + test_xy[i][0];
10789 test_y = good_y + test_xy[i][1];
10791 if (!IN_LEV_FIELD(test_x, test_y))
10795 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10797 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10799 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10800 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10802 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10803 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10807 bad_element = test_element;
10813 if (kill_x != -1 || kill_y != -1)
10815 if (IS_PLAYER(good_x, good_y))
10817 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10819 if (player->shield_deadly_time_left > 0 &&
10820 !IS_INDESTRUCTIBLE(bad_element))
10821 Bang(kill_x, kill_y);
10822 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10823 KillPlayer(player);
10826 Bang(good_x, good_y);
10830 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10832 int i, kill_x = -1, kill_y = -1;
10833 int bad_element = Feld[bad_x][bad_y];
10834 static int test_xy[4][2] =
10841 static int touch_dir[4] =
10843 MV_LEFT | MV_RIGHT,
10848 static int test_dir[4] =
10856 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10859 for (i = 0; i < NUM_DIRECTIONS; i++)
10861 int test_x, test_y, test_move_dir, test_element;
10863 test_x = bad_x + test_xy[i][0];
10864 test_y = bad_y + test_xy[i][1];
10865 if (!IN_LEV_FIELD(test_x, test_y))
10869 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10871 test_element = Feld[test_x][test_y];
10873 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10874 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10876 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10877 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10879 /* good thing is player or penguin that does not move away */
10880 if (IS_PLAYER(test_x, test_y))
10882 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10884 if (bad_element == EL_ROBOT && player->is_moving)
10885 continue; /* robot does not kill player if he is moving */
10887 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10889 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10890 continue; /* center and border element do not touch */
10897 else if (test_element == EL_PENGUIN)
10906 if (kill_x != -1 || kill_y != -1)
10908 if (IS_PLAYER(kill_x, kill_y))
10910 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10912 if (player->shield_deadly_time_left > 0 &&
10913 !IS_INDESTRUCTIBLE(bad_element))
10914 Bang(bad_x, bad_y);
10915 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10916 KillPlayer(player);
10919 Bang(kill_x, kill_y);
10923 void TestIfPlayerTouchesBadThing(int x, int y)
10925 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10928 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10930 TestIfGoodThingHitsBadThing(x, y, move_dir);
10933 void TestIfBadThingTouchesPlayer(int x, int y)
10935 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10938 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10940 TestIfBadThingHitsGoodThing(x, y, move_dir);
10943 void TestIfFriendTouchesBadThing(int x, int y)
10945 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10948 void TestIfBadThingTouchesFriend(int x, int y)
10950 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10953 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10955 int i, kill_x = bad_x, kill_y = bad_y;
10956 static int xy[4][2] =
10964 for (i = 0; i < NUM_DIRECTIONS; i++)
10968 x = bad_x + xy[i][0];
10969 y = bad_y + xy[i][1];
10970 if (!IN_LEV_FIELD(x, y))
10973 element = Feld[x][y];
10974 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10975 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10983 if (kill_x != bad_x || kill_y != bad_y)
10984 Bang(bad_x, bad_y);
10987 void KillPlayer(struct PlayerInfo *player)
10989 int jx = player->jx, jy = player->jy;
10991 if (!player->active)
10994 /* remove accessible field at the player's position */
10995 Feld[jx][jy] = EL_EMPTY;
10997 /* deactivate shield (else Bang()/Explode() would not work right) */
10998 player->shield_normal_time_left = 0;
10999 player->shield_deadly_time_left = 0;
11002 BuryPlayer(player);
11005 static void KillPlayerUnlessEnemyProtected(int x, int y)
11007 if (!PLAYER_ENEMY_PROTECTED(x, y))
11008 KillPlayer(PLAYERINFO(x, y));
11011 static void KillPlayerUnlessExplosionProtected(int x, int y)
11013 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11014 KillPlayer(PLAYERINFO(x, y));
11017 void BuryPlayer(struct PlayerInfo *player)
11019 int jx = player->jx, jy = player->jy;
11021 if (!player->active)
11024 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11025 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11027 player->GameOver = TRUE;
11028 RemovePlayer(player);
11031 void RemovePlayer(struct PlayerInfo *player)
11033 int jx = player->jx, jy = player->jy;
11034 int i, found = FALSE;
11036 player->present = FALSE;
11037 player->active = FALSE;
11039 if (!ExplodeField[jx][jy])
11040 StorePlayer[jx][jy] = 0;
11042 if (player->is_moving)
11043 DrawLevelField(player->last_jx, player->last_jy);
11045 for (i = 0; i < MAX_PLAYERS; i++)
11046 if (stored_player[i].active)
11050 AllPlayersGone = TRUE;
11056 #if USE_NEW_SNAP_DELAY
11057 static void setFieldForSnapping(int x, int y, int element, int direction)
11059 struct ElementInfo *ei = &element_info[element];
11060 int direction_bit = MV_DIR_TO_BIT(direction);
11061 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11062 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11063 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11065 Feld[x][y] = EL_ELEMENT_SNAPPING;
11066 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11068 ResetGfxAnimation(x, y);
11070 GfxElement[x][y] = element;
11071 GfxAction[x][y] = action;
11072 GfxDir[x][y] = direction;
11073 GfxFrame[x][y] = -1;
11078 =============================================================================
11079 checkDiagonalPushing()
11080 -----------------------------------------------------------------------------
11081 check if diagonal input device direction results in pushing of object
11082 (by checking if the alternative direction is walkable, diggable, ...)
11083 =============================================================================
11086 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11087 int x, int y, int real_dx, int real_dy)
11089 int jx, jy, dx, dy, xx, yy;
11091 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11094 /* diagonal direction: check alternative direction */
11099 xx = jx + (dx == 0 ? real_dx : 0);
11100 yy = jy + (dy == 0 ? real_dy : 0);
11102 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11106 =============================================================================
11108 -----------------------------------------------------------------------------
11109 x, y: field next to player (non-diagonal) to try to dig to
11110 real_dx, real_dy: direction as read from input device (can be diagonal)
11111 =============================================================================
11114 int DigField(struct PlayerInfo *player,
11115 int oldx, int oldy, int x, int y,
11116 int real_dx, int real_dy, int mode)
11118 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11119 boolean player_was_pushing = player->is_pushing;
11120 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11121 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11122 int jx = oldx, jy = oldy;
11123 int dx = x - jx, dy = y - jy;
11124 int nextx = x + dx, nexty = y + dy;
11125 int move_direction = (dx == -1 ? MV_LEFT :
11126 dx == +1 ? MV_RIGHT :
11128 dy == +1 ? MV_DOWN : MV_NONE);
11129 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11130 int dig_side = MV_DIR_OPPOSITE(move_direction);
11131 int old_element = Feld[jx][jy];
11132 #if USE_FIXED_DONT_RUN_INTO
11133 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11139 if (is_player) /* function can also be called by EL_PENGUIN */
11141 if (player->MovPos == 0)
11143 player->is_digging = FALSE;
11144 player->is_collecting = FALSE;
11147 if (player->MovPos == 0) /* last pushing move finished */
11148 player->is_pushing = FALSE;
11150 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11152 player->is_switching = FALSE;
11153 player->push_delay = -1;
11155 return MP_NO_ACTION;
11159 #if !USE_FIXED_DONT_RUN_INTO
11160 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11161 return MP_NO_ACTION;
11164 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11165 old_element = Back[jx][jy];
11167 /* in case of element dropped at player position, check background */
11168 else if (Back[jx][jy] != EL_EMPTY &&
11169 game.engine_version >= VERSION_IDENT(2,2,0,0))
11170 old_element = Back[jx][jy];
11172 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11173 return MP_NO_ACTION; /* field has no opening in this direction */
11175 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11176 return MP_NO_ACTION; /* field has no opening in this direction */
11178 #if USE_FIXED_DONT_RUN_INTO
11179 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11183 Feld[jx][jy] = player->artwork_element;
11184 InitMovingField(jx, jy, MV_DOWN);
11185 Store[jx][jy] = EL_ACID;
11186 ContinueMoving(jx, jy);
11187 BuryPlayer(player);
11189 return MP_DONT_RUN_INTO;
11193 #if USE_FIXED_DONT_RUN_INTO
11194 if (player_can_move && DONT_RUN_INTO(element))
11196 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11198 return MP_DONT_RUN_INTO;
11202 #if USE_FIXED_DONT_RUN_INTO
11203 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11204 return MP_NO_ACTION;
11207 #if !USE_FIXED_DONT_RUN_INTO
11208 element = Feld[x][y];
11211 collect_count = element_info[element].collect_count_initial;
11213 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11214 return MP_NO_ACTION;
11216 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11217 player_can_move = player_can_move_or_snap;
11219 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11220 game.engine_version >= VERSION_IDENT(2,2,0,0))
11222 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11223 player->index_bit, dig_side);
11224 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11225 player->index_bit, dig_side);
11227 if (Feld[x][y] != element) /* field changed by snapping */
11230 return MP_NO_ACTION;
11233 #if USE_PLAYER_GRAVITY
11234 if (player->gravity && is_player && !player->is_auto_moving &&
11235 canFallDown(player) && move_direction != MV_DOWN &&
11236 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11237 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11239 if (game.gravity && is_player && !player->is_auto_moving &&
11240 canFallDown(player) && move_direction != MV_DOWN &&
11241 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11242 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11245 if (player_can_move &&
11246 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11248 int sound_element = SND_ELEMENT(element);
11249 int sound_action = ACTION_WALKING;
11251 if (IS_RND_GATE(element))
11253 if (!player->key[RND_GATE_NR(element)])
11254 return MP_NO_ACTION;
11256 else if (IS_RND_GATE_GRAY(element))
11258 if (!player->key[RND_GATE_GRAY_NR(element)])
11259 return MP_NO_ACTION;
11261 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11263 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11264 return MP_NO_ACTION;
11266 else if (element == EL_EXIT_OPEN ||
11267 element == EL_SP_EXIT_OPEN ||
11268 element == EL_SP_EXIT_OPENING)
11270 sound_action = ACTION_PASSING; /* player is passing exit */
11272 else if (element == EL_EMPTY)
11274 sound_action = ACTION_MOVING; /* nothing to walk on */
11277 /* play sound from background or player, whatever is available */
11278 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11279 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11281 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11283 else if (player_can_move &&
11284 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11286 if (!ACCESS_FROM(element, opposite_direction))
11287 return MP_NO_ACTION; /* field not accessible from this direction */
11289 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11290 return MP_NO_ACTION;
11292 if (IS_EM_GATE(element))
11294 if (!player->key[EM_GATE_NR(element)])
11295 return MP_NO_ACTION;
11297 else if (IS_EM_GATE_GRAY(element))
11299 if (!player->key[EM_GATE_GRAY_NR(element)])
11300 return MP_NO_ACTION;
11302 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11304 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11305 return MP_NO_ACTION;
11307 else if (IS_EMC_GATE(element))
11309 if (!player->key[EMC_GATE_NR(element)])
11310 return MP_NO_ACTION;
11312 else if (IS_EMC_GATE_GRAY(element))
11314 if (!player->key[EMC_GATE_GRAY_NR(element)])
11315 return MP_NO_ACTION;
11317 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11319 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11320 return MP_NO_ACTION;
11322 else if (IS_SP_PORT(element))
11324 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11325 element == EL_SP_GRAVITY_PORT_RIGHT ||
11326 element == EL_SP_GRAVITY_PORT_UP ||
11327 element == EL_SP_GRAVITY_PORT_DOWN)
11328 #if USE_PLAYER_GRAVITY
11329 player->gravity = !player->gravity;
11331 game.gravity = !game.gravity;
11333 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11334 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11335 element == EL_SP_GRAVITY_ON_PORT_UP ||
11336 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11337 #if USE_PLAYER_GRAVITY
11338 player->gravity = TRUE;
11340 game.gravity = TRUE;
11342 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11343 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11344 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11345 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11346 #if USE_PLAYER_GRAVITY
11347 player->gravity = FALSE;
11349 game.gravity = FALSE;
11353 /* automatically move to the next field with double speed */
11354 player->programmed_action = move_direction;
11356 if (player->move_delay_reset_counter == 0)
11358 player->move_delay_reset_counter = 2; /* two double speed steps */
11360 DOUBLE_PLAYER_SPEED(player);
11363 PlayLevelSoundAction(x, y, ACTION_PASSING);
11365 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11369 if (mode != DF_SNAP)
11371 GfxElement[x][y] = GFX_ELEMENT(element);
11372 player->is_digging = TRUE;
11375 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11377 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11378 player->index_bit, dig_side);
11380 if (mode == DF_SNAP)
11382 #if USE_NEW_SNAP_DELAY
11383 if (level.block_snap_field)
11384 setFieldForSnapping(x, y, element, move_direction);
11386 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11388 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11391 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11392 player->index_bit, dig_side);
11395 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11399 if (is_player && mode != DF_SNAP)
11401 GfxElement[x][y] = element;
11402 player->is_collecting = TRUE;
11405 if (element == EL_SPEED_PILL)
11407 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11409 else if (element == EL_EXTRA_TIME && level.time > 0)
11411 TimeLeft += level.extra_time;
11412 DrawGameValue_Time(TimeLeft);
11414 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11416 player->shield_normal_time_left += level.shield_normal_time;
11417 if (element == EL_SHIELD_DEADLY)
11418 player->shield_deadly_time_left += level.shield_deadly_time;
11420 else if (element == EL_DYNAMITE ||
11421 element == EL_EM_DYNAMITE ||
11422 element == EL_SP_DISK_RED)
11424 if (player->inventory_size < MAX_INVENTORY_SIZE)
11425 player->inventory_element[player->inventory_size++] = element;
11427 DrawGameDoorValues();
11429 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11431 player->dynabomb_count++;
11432 player->dynabombs_left++;
11434 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11436 player->dynabomb_size++;
11438 else if (element == EL_DYNABOMB_INCREASE_POWER)
11440 player->dynabomb_xl = TRUE;
11442 else if (IS_KEY(element))
11444 player->key[KEY_NR(element)] = TRUE;
11446 DrawGameDoorValues();
11448 else if (IS_ENVELOPE(element))
11450 player->show_envelope = element;
11452 else if (element == EL_EMC_LENSES)
11454 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11456 RedrawAllInvisibleElementsForLenses();
11458 else if (element == EL_EMC_MAGNIFIER)
11460 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11462 RedrawAllInvisibleElementsForMagnifier();
11464 else if (IS_DROPPABLE(element) ||
11465 IS_THROWABLE(element)) /* can be collected and dropped */
11469 if (collect_count == 0)
11470 player->inventory_infinite_element = element;
11472 for (i = 0; i < collect_count; i++)
11473 if (player->inventory_size < MAX_INVENTORY_SIZE)
11474 player->inventory_element[player->inventory_size++] = element;
11476 DrawGameDoorValues();
11478 else if (collect_count > 0)
11480 local_player->gems_still_needed -= collect_count;
11481 if (local_player->gems_still_needed < 0)
11482 local_player->gems_still_needed = 0;
11484 DrawGameValue_Emeralds(local_player->gems_still_needed);
11487 RaiseScoreElement(element);
11488 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11491 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11492 player->index_bit, dig_side);
11494 if (mode == DF_SNAP)
11496 #if USE_NEW_SNAP_DELAY
11497 if (level.block_snap_field)
11498 setFieldForSnapping(x, y, element, move_direction);
11500 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11502 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11505 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11506 player->index_bit, dig_side);
11509 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11511 if (mode == DF_SNAP && element != EL_BD_ROCK)
11512 return MP_NO_ACTION;
11514 if (CAN_FALL(element) && dy)
11515 return MP_NO_ACTION;
11517 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11518 !(element == EL_SPRING && level.use_spring_bug))
11519 return MP_NO_ACTION;
11521 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11522 ((move_direction & MV_VERTICAL &&
11523 ((element_info[element].move_pattern & MV_LEFT &&
11524 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11525 (element_info[element].move_pattern & MV_RIGHT &&
11526 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11527 (move_direction & MV_HORIZONTAL &&
11528 ((element_info[element].move_pattern & MV_UP &&
11529 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11530 (element_info[element].move_pattern & MV_DOWN &&
11531 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11532 return MP_NO_ACTION;
11534 /* do not push elements already moving away faster than player */
11535 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11536 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11537 return MP_NO_ACTION;
11539 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11541 if (player->push_delay_value == -1 || !player_was_pushing)
11542 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11544 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11546 if (player->push_delay_value == -1)
11547 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11549 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11551 if (!player->is_pushing)
11552 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11555 player->is_pushing = TRUE;
11556 player->is_active = TRUE;
11558 if (!(IN_LEV_FIELD(nextx, nexty) &&
11559 (IS_FREE(nextx, nexty) ||
11560 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11561 IS_SB_ELEMENT(element)))))
11562 return MP_NO_ACTION;
11564 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11565 return MP_NO_ACTION;
11567 if (player->push_delay == -1) /* new pushing; restart delay */
11568 player->push_delay = 0;
11570 if (player->push_delay < player->push_delay_value &&
11571 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11572 element != EL_SPRING && element != EL_BALLOON)
11574 /* make sure that there is no move delay before next try to push */
11575 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11576 player->move_delay = 0;
11578 return MP_NO_ACTION;
11581 if (IS_SB_ELEMENT(element))
11583 if (element == EL_SOKOBAN_FIELD_FULL)
11585 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11586 local_player->sokobanfields_still_needed++;
11589 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11591 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11592 local_player->sokobanfields_still_needed--;
11595 Feld[x][y] = EL_SOKOBAN_OBJECT;
11597 if (Back[x][y] == Back[nextx][nexty])
11598 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11599 else if (Back[x][y] != 0)
11600 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11603 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11606 if (local_player->sokobanfields_still_needed == 0 &&
11607 game.emulation == EMU_SOKOBAN)
11609 PlayerWins(player);
11611 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11615 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11617 InitMovingField(x, y, move_direction);
11618 GfxAction[x][y] = ACTION_PUSHING;
11620 if (mode == DF_SNAP)
11621 ContinueMoving(x, y);
11623 MovPos[x][y] = (dx != 0 ? dx : dy);
11625 Pushed[x][y] = TRUE;
11626 Pushed[nextx][nexty] = TRUE;
11628 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11629 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11631 player->push_delay_value = -1; /* get new value later */
11633 /* check for element change _after_ element has been pushed */
11634 if (game.use_change_when_pushing_bug)
11636 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11637 player->index_bit, dig_side);
11638 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11639 player->index_bit, dig_side);
11642 else if (IS_SWITCHABLE(element))
11644 if (PLAYER_SWITCHING(player, x, y))
11646 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11647 player->index_bit, dig_side);
11652 player->is_switching = TRUE;
11653 player->switch_x = x;
11654 player->switch_y = y;
11656 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11658 if (element == EL_ROBOT_WHEEL)
11660 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11664 DrawLevelField(x, y);
11666 else if (element == EL_SP_TERMINAL)
11670 SCAN_PLAYFIELD(xx, yy)
11672 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11674 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11675 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11678 else if (IS_BELT_SWITCH(element))
11680 ToggleBeltSwitch(x, y);
11682 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11683 element == EL_SWITCHGATE_SWITCH_DOWN)
11685 ToggleSwitchgateSwitch(x, y);
11687 else if (element == EL_LIGHT_SWITCH ||
11688 element == EL_LIGHT_SWITCH_ACTIVE)
11690 ToggleLightSwitch(x, y);
11692 else if (element == EL_TIMEGATE_SWITCH)
11694 ActivateTimegateSwitch(x, y);
11696 else if (element == EL_BALLOON_SWITCH_LEFT ||
11697 element == EL_BALLOON_SWITCH_RIGHT ||
11698 element == EL_BALLOON_SWITCH_UP ||
11699 element == EL_BALLOON_SWITCH_DOWN ||
11700 element == EL_BALLOON_SWITCH_NONE ||
11701 element == EL_BALLOON_SWITCH_ANY)
11703 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11704 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11705 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11706 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11707 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11710 else if (element == EL_LAMP)
11712 Feld[x][y] = EL_LAMP_ACTIVE;
11713 local_player->lights_still_needed--;
11715 ResetGfxAnimation(x, y);
11716 DrawLevelField(x, y);
11718 else if (element == EL_TIME_ORB_FULL)
11720 Feld[x][y] = EL_TIME_ORB_EMPTY;
11722 if (level.time > 0 || level.use_time_orb_bug)
11724 TimeLeft += level.time_orb_time;
11725 DrawGameValue_Time(TimeLeft);
11728 ResetGfxAnimation(x, y);
11729 DrawLevelField(x, y);
11731 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11732 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11736 game.ball_state = !game.ball_state;
11738 SCAN_PLAYFIELD(xx, yy)
11740 int e = Feld[xx][yy];
11742 if (game.ball_state)
11744 if (e == EL_EMC_MAGIC_BALL)
11745 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11746 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11747 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11751 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11752 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11753 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11754 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11759 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11760 player->index_bit, dig_side);
11762 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11763 player->index_bit, dig_side);
11765 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11766 player->index_bit, dig_side);
11772 if (!PLAYER_SWITCHING(player, x, y))
11774 player->is_switching = TRUE;
11775 player->switch_x = x;
11776 player->switch_y = y;
11778 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11779 player->index_bit, dig_side);
11780 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11781 player->index_bit, dig_side);
11783 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11784 player->index_bit, dig_side);
11785 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11786 player->index_bit, dig_side);
11789 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11790 player->index_bit, dig_side);
11791 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11792 player->index_bit, dig_side);
11794 return MP_NO_ACTION;
11797 player->push_delay = -1;
11799 if (is_player) /* function can also be called by EL_PENGUIN */
11801 if (Feld[x][y] != element) /* really digged/collected something */
11803 player->is_collecting = !player->is_digging;
11804 player->is_active = TRUE;
11811 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11813 int jx = player->jx, jy = player->jy;
11814 int x = jx + dx, y = jy + dy;
11815 int snap_direction = (dx == -1 ? MV_LEFT :
11816 dx == +1 ? MV_RIGHT :
11818 dy == +1 ? MV_DOWN : MV_NONE);
11819 boolean can_continue_snapping = (level.continuous_snapping &&
11820 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
11822 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11825 if (!player->active || !IN_LEV_FIELD(x, y))
11833 if (player->MovPos == 0)
11834 player->is_pushing = FALSE;
11836 player->is_snapping = FALSE;
11838 if (player->MovPos == 0)
11840 player->is_moving = FALSE;
11841 player->is_digging = FALSE;
11842 player->is_collecting = FALSE;
11848 #if USE_NEW_CONTINUOUS_SNAPPING
11849 /* prevent snapping with already pressed snap key when not allowed */
11850 if (player->is_snapping && !can_continue_snapping)
11853 if (player->is_snapping)
11857 player->MovDir = snap_direction;
11859 if (player->MovPos == 0)
11861 player->is_moving = FALSE;
11862 player->is_digging = FALSE;
11863 player->is_collecting = FALSE;
11866 player->is_dropping = FALSE;
11867 player->is_dropping_pressed = FALSE;
11868 player->drop_pressed_delay = 0;
11870 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11873 player->is_snapping = TRUE;
11874 player->is_active = TRUE;
11876 if (player->MovPos == 0)
11878 player->is_moving = FALSE;
11879 player->is_digging = FALSE;
11880 player->is_collecting = FALSE;
11883 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11884 DrawLevelField(player->last_jx, player->last_jy);
11886 DrawLevelField(x, y);
11891 boolean DropElement(struct PlayerInfo *player)
11893 int old_element, new_element;
11894 int dropx = player->jx, dropy = player->jy;
11895 int drop_direction = player->MovDir;
11896 int drop_side = drop_direction;
11897 int drop_element = (player->inventory_size > 0 ?
11898 player->inventory_element[player->inventory_size - 1] :
11899 player->inventory_infinite_element != EL_UNDEFINED ?
11900 player->inventory_infinite_element :
11901 player->dynabombs_left > 0 ?
11902 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11905 player->is_dropping_pressed = TRUE;
11907 /* do not drop an element on top of another element; when holding drop key
11908 pressed without moving, dropped element must move away before the next
11909 element can be dropped (this is especially important if the next element
11910 is dynamite, which can be placed on background for historical reasons) */
11911 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11914 if (IS_THROWABLE(drop_element))
11916 dropx += GET_DX_FROM_DIR(drop_direction);
11917 dropy += GET_DY_FROM_DIR(drop_direction);
11919 if (!IN_LEV_FIELD(dropx, dropy))
11923 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11924 new_element = drop_element; /* default: no change when dropping */
11926 /* check if player is active, not moving and ready to drop */
11927 if (!player->active || player->MovPos || player->drop_delay > 0)
11930 /* check if player has anything that can be dropped */
11931 if (new_element == EL_UNDEFINED)
11934 /* check if drop key was pressed long enough for EM style dynamite */
11935 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
11938 /* check if anything can be dropped at the current position */
11939 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11942 /* collected custom elements can only be dropped on empty fields */
11943 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11946 if (old_element != EL_EMPTY)
11947 Back[dropx][dropy] = old_element; /* store old element on this field */
11949 ResetGfxAnimation(dropx, dropy);
11950 ResetRandomAnimationValue(dropx, dropy);
11952 if (player->inventory_size > 0 ||
11953 player->inventory_infinite_element != EL_UNDEFINED)
11955 if (player->inventory_size > 0)
11957 player->inventory_size--;
11959 DrawGameDoorValues();
11961 if (new_element == EL_DYNAMITE)
11962 new_element = EL_DYNAMITE_ACTIVE;
11963 else if (new_element == EL_EM_DYNAMITE)
11964 new_element = EL_EM_DYNAMITE_ACTIVE;
11965 else if (new_element == EL_SP_DISK_RED)
11966 new_element = EL_SP_DISK_RED_ACTIVE;
11969 Feld[dropx][dropy] = new_element;
11971 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11972 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11973 el2img(Feld[dropx][dropy]), 0);
11975 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11977 /* needed if previous element just changed to "empty" in the last frame */
11978 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11980 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11981 player->index_bit, drop_side);
11982 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11984 player->index_bit, drop_side);
11986 TestIfElementTouchesCustomElement(dropx, dropy);
11988 else /* player is dropping a dyna bomb */
11990 player->dynabombs_left--;
11992 Feld[dropx][dropy] = new_element;
11994 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11995 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11996 el2img(Feld[dropx][dropy]), 0);
11998 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12001 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12002 InitField_WithBug1(dropx, dropy, FALSE);
12004 new_element = Feld[dropx][dropy]; /* element might have changed */
12006 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12007 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12009 int move_direction, nextx, nexty;
12011 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12012 MovDir[dropx][dropy] = drop_direction;
12014 move_direction = MovDir[dropx][dropy];
12015 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12016 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12018 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12019 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12022 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12023 player->is_dropping = TRUE;
12025 player->drop_pressed_delay = 0;
12026 player->is_dropping_pressed = FALSE;
12028 player->drop_x = dropx;
12029 player->drop_y = dropy;
12034 /* ------------------------------------------------------------------------- */
12035 /* game sound playing functions */
12036 /* ------------------------------------------------------------------------- */
12038 static int *loop_sound_frame = NULL;
12039 static int *loop_sound_volume = NULL;
12041 void InitPlayLevelSound()
12043 int num_sounds = getSoundListSize();
12045 checked_free(loop_sound_frame);
12046 checked_free(loop_sound_volume);
12048 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12049 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12052 static void PlayLevelSound(int x, int y, int nr)
12054 int sx = SCREENX(x), sy = SCREENY(y);
12055 int volume, stereo_position;
12056 int max_distance = 8;
12057 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12059 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12060 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12063 if (!IN_LEV_FIELD(x, y) ||
12064 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12065 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12068 volume = SOUND_MAX_VOLUME;
12070 if (!IN_SCR_FIELD(sx, sy))
12072 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12073 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12075 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12078 stereo_position = (SOUND_MAX_LEFT +
12079 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12080 (SCR_FIELDX + 2 * max_distance));
12082 if (IS_LOOP_SOUND(nr))
12084 /* This assures that quieter loop sounds do not overwrite louder ones,
12085 while restarting sound volume comparison with each new game frame. */
12087 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12090 loop_sound_volume[nr] = volume;
12091 loop_sound_frame[nr] = FrameCounter;
12094 PlaySoundExt(nr, volume, stereo_position, type);
12097 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12099 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12100 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12101 y < LEVELY(BY1) ? LEVELY(BY1) :
12102 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12106 static void PlayLevelSoundAction(int x, int y, int action)
12108 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12111 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12113 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12115 if (sound_effect != SND_UNDEFINED)
12116 PlayLevelSound(x, y, sound_effect);
12119 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12122 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12124 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12125 PlayLevelSound(x, y, sound_effect);
12128 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12130 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12132 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12133 PlayLevelSound(x, y, sound_effect);
12136 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12138 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12140 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12141 StopSound(sound_effect);
12144 static void PlayLevelMusic()
12146 if (levelset.music[level_nr] != MUS_UNDEFINED)
12147 PlayMusic(levelset.music[level_nr]); /* from config file */
12149 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12152 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
12154 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12155 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
12156 int x = xx - 1 - offset;
12157 int y = yy - 1 - offset;
12162 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12166 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12170 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12174 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12178 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12182 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12186 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12189 case SAMPLE_android_clone:
12190 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12193 case SAMPLE_android_move:
12194 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12197 case SAMPLE_spring:
12198 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12202 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12206 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12209 case SAMPLE_eater_eat:
12210 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12214 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12217 case SAMPLE_collect:
12218 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12221 case SAMPLE_diamond:
12222 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12225 case SAMPLE_squash:
12226 /* !!! CHECK THIS !!! */
12228 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12230 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12234 case SAMPLE_wonderfall:
12235 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12239 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12243 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12247 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12251 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12255 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12259 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12262 case SAMPLE_wonder:
12263 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12267 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12270 case SAMPLE_exit_open:
12271 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12274 case SAMPLE_exit_leave:
12275 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12278 case SAMPLE_dynamite:
12279 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12283 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12287 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12291 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12295 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12299 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12303 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12307 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12313 void ChangeTime(int value)
12315 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
12319 /* EMC game engine uses value from time counter of RND game engine */
12320 level.native_em_level->lev->time = *time;
12322 DrawGameValue_Time(*time);
12325 void RaiseScore(int value)
12327 /* EMC game engine and RND game engine have separate score counters */
12328 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
12329 &level.native_em_level->lev->score : &local_player->score);
12333 DrawGameValue_Score(*score);
12337 void RaiseScore(int value)
12339 local_player->score += value;
12341 DrawGameValue_Score(local_player->score);
12344 void RaiseScoreElement(int element)
12349 case EL_BD_DIAMOND:
12350 case EL_EMERALD_YELLOW:
12351 case EL_EMERALD_RED:
12352 case EL_EMERALD_PURPLE:
12353 case EL_SP_INFOTRON:
12354 RaiseScore(level.score[SC_EMERALD]);
12357 RaiseScore(level.score[SC_DIAMOND]);
12360 RaiseScore(level.score[SC_CRYSTAL]);
12363 RaiseScore(level.score[SC_PEARL]);
12366 case EL_BD_BUTTERFLY:
12367 case EL_SP_ELECTRON:
12368 RaiseScore(level.score[SC_BUG]);
12371 case EL_BD_FIREFLY:
12372 case EL_SP_SNIKSNAK:
12373 RaiseScore(level.score[SC_SPACESHIP]);
12376 case EL_DARK_YAMYAM:
12377 RaiseScore(level.score[SC_YAMYAM]);
12380 RaiseScore(level.score[SC_ROBOT]);
12383 RaiseScore(level.score[SC_PACMAN]);
12386 RaiseScore(level.score[SC_NUT]);
12389 case EL_EM_DYNAMITE:
12390 case EL_SP_DISK_RED:
12391 case EL_DYNABOMB_INCREASE_NUMBER:
12392 case EL_DYNABOMB_INCREASE_SIZE:
12393 case EL_DYNABOMB_INCREASE_POWER:
12394 RaiseScore(level.score[SC_DYNAMITE]);
12396 case EL_SHIELD_NORMAL:
12397 case EL_SHIELD_DEADLY:
12398 RaiseScore(level.score[SC_SHIELD]);
12400 case EL_EXTRA_TIME:
12401 RaiseScore(level.extra_time_score);
12415 RaiseScore(level.score[SC_KEY]);
12418 RaiseScore(element_info[element].collect_score);
12423 void RequestQuitGame(boolean ask_if_really_quit)
12425 if (AllPlayersGone ||
12426 !ask_if_really_quit ||
12427 level_editor_test_game ||
12428 Request("Do you really want to quit the game ?",
12429 REQ_ASK | REQ_STAY_CLOSED))
12431 #if defined(NETWORK_AVALIABLE)
12432 if (options.network)
12433 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12437 if (!ask_if_really_quit || level_editor_test_game)
12439 game_status = GAME_MODE_MAIN;
12445 FadeOut(REDRAW_FIELD);
12447 game_status = GAME_MODE_MAIN;
12449 DrawAndFadeInMainMenu(REDRAW_FIELD);
12455 if (tape.playing && tape.deactivate_display)
12456 TapeDeactivateDisplayOff(TRUE);
12458 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12460 if (tape.playing && tape.deactivate_display)
12461 TapeDeactivateDisplayOn();
12466 /* ------------------------------------------------------------------------- */
12467 /* random generator functions */
12468 /* ------------------------------------------------------------------------- */
12470 unsigned int InitEngineRandom_RND(long seed)
12472 game.num_random_calls = 0;
12475 unsigned int rnd_seed = InitEngineRandom(seed);
12477 printf("::: START RND: %d\n", rnd_seed);
12482 return InitEngineRandom(seed);
12488 unsigned int RND(int max)
12492 game.num_random_calls++;
12494 return GetEngineRandom(max);
12501 /* ------------------------------------------------------------------------- */
12502 /* game engine snapshot handling functions */
12503 /* ------------------------------------------------------------------------- */
12505 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
12507 struct EngineSnapshotInfo
12509 /* runtime values for custom element collect score */
12510 int collect_score[NUM_CUSTOM_ELEMENTS];
12512 /* runtime values for group element choice position */
12513 int choice_pos[NUM_GROUP_ELEMENTS];
12515 /* runtime values for belt position animations */
12516 int belt_graphic[4 * NUM_BELT_PARTS];
12517 int belt_anim_mode[4 * NUM_BELT_PARTS];
12520 struct EngineSnapshotNodeInfo
12527 static struct EngineSnapshotInfo engine_snapshot_rnd;
12528 static ListNode *engine_snapshot_list = NULL;
12529 static char *snapshot_level_identifier = NULL;
12530 static int snapshot_level_nr = -1;
12532 void FreeEngineSnapshot()
12534 while (engine_snapshot_list != NULL)
12535 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
12538 setString(&snapshot_level_identifier, NULL);
12539 snapshot_level_nr = -1;
12542 static void SaveEngineSnapshotValues_RND()
12544 static int belt_base_active_element[4] =
12546 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
12547 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
12548 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
12549 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
12553 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12555 int element = EL_CUSTOM_START + i;
12557 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
12560 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12562 int element = EL_GROUP_START + i;
12564 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
12567 for (i = 0; i < 4; i++)
12569 for (j = 0; j < NUM_BELT_PARTS; j++)
12571 int element = belt_base_active_element[i] + j;
12572 int graphic = el2img(element);
12573 int anim_mode = graphic_info[graphic].anim_mode;
12575 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
12576 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
12581 static void LoadEngineSnapshotValues_RND()
12583 unsigned long num_random_calls = game.num_random_calls;
12586 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12588 int element = EL_CUSTOM_START + i;
12590 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
12593 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12595 int element = EL_GROUP_START + i;
12597 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
12600 for (i = 0; i < 4; i++)
12602 for (j = 0; j < NUM_BELT_PARTS; j++)
12604 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
12605 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
12607 graphic_info[graphic].anim_mode = anim_mode;
12611 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
12613 InitRND(tape.random_seed);
12614 for (i = 0; i < num_random_calls; i++)
12618 if (game.num_random_calls != num_random_calls)
12620 Error(ERR_RETURN, "number of random calls out of sync");
12621 Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
12622 Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
12623 Error(ERR_EXIT, "this should not happen -- please debug");
12627 static void SaveEngineSnapshotBuffer(void *buffer, int size)
12629 struct EngineSnapshotNodeInfo *bi =
12630 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
12632 bi->buffer_orig = buffer;
12633 bi->buffer_copy = checked_malloc(size);
12636 memcpy(bi->buffer_copy, buffer, size);
12638 addNodeToList(&engine_snapshot_list, NULL, bi);
12641 void SaveEngineSnapshot()
12643 FreeEngineSnapshot(); /* free previous snapshot, if needed */
12645 /* copy some special values to a structure better suited for the snapshot */
12647 SaveEngineSnapshotValues_RND();
12648 SaveEngineSnapshotValues_EM();
12650 /* save values stored in special snapshot structure */
12652 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
12653 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
12655 /* save further RND engine values */
12657 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
12658 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
12659 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
12661 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
12662 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
12663 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
12664 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
12666 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
12667 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
12668 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
12669 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
12670 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
12672 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
12673 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
12674 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
12676 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
12678 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
12680 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
12681 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
12683 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
12684 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
12685 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
12686 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
12687 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
12688 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
12689 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
12690 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
12691 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
12692 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
12693 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
12694 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
12695 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
12696 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
12697 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
12698 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
12699 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
12701 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
12702 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
12704 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
12705 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
12706 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
12708 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
12709 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
12711 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
12712 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
12713 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
12714 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
12715 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
12717 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
12718 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
12720 /* save level identification information */
12722 setString(&snapshot_level_identifier, leveldir_current->identifier);
12723 snapshot_level_nr = level_nr;
12726 ListNode *node = engine_snapshot_list;
12729 while (node != NULL)
12731 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
12736 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
12740 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
12742 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
12745 void LoadEngineSnapshot()
12747 ListNode *node = engine_snapshot_list;
12749 if (engine_snapshot_list == NULL)
12752 while (node != NULL)
12754 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
12759 /* restore special values from snapshot structure */
12761 LoadEngineSnapshotValues_RND();
12762 LoadEngineSnapshotValues_EM();
12765 boolean CheckEngineSnapshot()
12767 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
12768 snapshot_level_nr == level_nr);
12772 /* ---------- new game button stuff ---------------------------------------- */
12774 /* graphic position values for game buttons */
12775 #define GAME_BUTTON_XSIZE 30
12776 #define GAME_BUTTON_YSIZE 30
12777 #define GAME_BUTTON_XPOS 5
12778 #define GAME_BUTTON_YPOS 215
12779 #define SOUND_BUTTON_XPOS 5
12780 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12782 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12783 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12784 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12785 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12786 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12787 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12794 } gamebutton_info[NUM_GAME_BUTTONS] =
12797 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12802 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12803 GAME_CTRL_ID_PAUSE,
12807 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12812 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12813 SOUND_CTRL_ID_MUSIC,
12814 "background music on/off"
12817 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12818 SOUND_CTRL_ID_LOOPS,
12819 "sound loops on/off"
12822 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12823 SOUND_CTRL_ID_SIMPLE,
12824 "normal sounds on/off"
12828 void CreateGameButtons()
12832 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12834 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12835 struct GadgetInfo *gi;
12838 unsigned long event_mask;
12839 int gd_xoffset, gd_yoffset;
12840 int gd_x1, gd_x2, gd_y1, gd_y2;
12843 gd_xoffset = gamebutton_info[i].x;
12844 gd_yoffset = gamebutton_info[i].y;
12845 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12846 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12848 if (id == GAME_CTRL_ID_STOP ||
12849 id == GAME_CTRL_ID_PAUSE ||
12850 id == GAME_CTRL_ID_PLAY)
12852 button_type = GD_TYPE_NORMAL_BUTTON;
12854 event_mask = GD_EVENT_RELEASED;
12855 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12856 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12860 button_type = GD_TYPE_CHECK_BUTTON;
12862 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12863 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12864 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12865 event_mask = GD_EVENT_PRESSED;
12866 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12867 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12870 gi = CreateGadget(GDI_CUSTOM_ID, id,
12871 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12872 GDI_X, DX + gd_xoffset,
12873 GDI_Y, DY + gd_yoffset,
12874 GDI_WIDTH, GAME_BUTTON_XSIZE,
12875 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12876 GDI_TYPE, button_type,
12877 GDI_STATE, GD_BUTTON_UNPRESSED,
12878 GDI_CHECKED, checked,
12879 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12880 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12881 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12882 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12883 GDI_EVENT_MASK, event_mask,
12884 GDI_CALLBACK_ACTION, HandleGameButtons,
12888 Error(ERR_EXIT, "cannot create gadget");
12890 game_gadget[id] = gi;
12894 void FreeGameButtons()
12898 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12899 FreeGadget(game_gadget[i]);
12902 static void MapGameButtons()
12906 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12907 MapGadget(game_gadget[i]);
12910 void UnmapGameButtons()
12914 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12915 UnmapGadget(game_gadget[i]);
12918 static void HandleGameButtons(struct GadgetInfo *gi)
12920 int id = gi->custom_id;
12922 if (game_status != GAME_MODE_PLAYING)
12927 case GAME_CTRL_ID_STOP:
12931 RequestQuitGame(TRUE);
12934 case GAME_CTRL_ID_PAUSE:
12935 if (options.network)
12937 #if defined(NETWORK_AVALIABLE)
12939 SendToServer_ContinuePlaying();
12941 SendToServer_PausePlaying();
12945 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12948 case GAME_CTRL_ID_PLAY:
12951 #if defined(NETWORK_AVALIABLE)
12952 if (options.network)
12953 SendToServer_ContinuePlaying();
12957 tape.pausing = FALSE;
12958 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
12963 case SOUND_CTRL_ID_MUSIC:
12964 if (setup.sound_music)
12966 setup.sound_music = FALSE;
12969 else if (audio.music_available)
12971 setup.sound = setup.sound_music = TRUE;
12973 SetAudioMode(setup.sound);
12979 case SOUND_CTRL_ID_LOOPS:
12980 if (setup.sound_loops)
12981 setup.sound_loops = FALSE;
12982 else if (audio.loops_available)
12984 setup.sound = setup.sound_loops = TRUE;
12985 SetAudioMode(setup.sound);
12989 case SOUND_CTRL_ID_SIMPLE:
12990 if (setup.sound_simple)
12991 setup.sound_simple = FALSE;
12992 else if (audio.sound_available)
12994 setup.sound = setup.sound_simple = TRUE;
12995 SetAudioMode(setup.sound);