1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
68 /* for MovePlayer() */
69 #define MP_NO_ACTION 0
72 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
74 /* for ScrollPlayer() */
76 #define SCROLL_GO_ON 1
78 /* for Bang()/Explode() */
79 #define EX_PHASE_START 0
80 #define EX_TYPE_NONE 0
81 #define EX_TYPE_NORMAL (1 << 0)
82 #define EX_TYPE_CENTER (1 << 1)
83 #define EX_TYPE_BORDER (1 << 2)
84 #define EX_TYPE_CROSS (1 << 3)
85 #define EX_TYPE_DYNA (1 << 4)
86 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
88 #define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0)
90 /* special positions in the game control window (relative to control window) */
91 #define XX_LEVEL1 (game.panel.level.x)
92 #define XX_LEVEL2 (game.panel.level.x - 1)
93 #define YY_LEVEL (game.panel.level.y)
94 #define XX_EMERALDS (game.panel.gems.x)
95 #define YY_EMERALDS (game.panel.gems.y)
96 #define XX_DYNAMITE (game.panel.inventory.x)
97 #define YY_DYNAMITE (game.panel.inventory.y)
98 #define XX_KEYS (game.panel.keys.x)
99 #define YY_KEYS (game.panel.keys.y)
100 #define XX_SCORE (game.panel.score.x)
101 #define YY_SCORE (game.panel.score.y)
102 #define XX_TIME1 (game.panel.time.x)
103 #define XX_TIME2 (game.panel.time.x + 1)
104 #define YY_TIME (game.panel.time.y)
106 /* special positions in the game control window (relative to main window) */
107 #define DX_LEVEL1 (DX + XX_LEVEL1)
108 #define DX_LEVEL2 (DX + XX_LEVEL2)
109 #define DY_LEVEL (DY + YY_LEVEL)
110 #define DX_EMERALDS (DX + XX_EMERALDS)
111 #define DY_EMERALDS (DY + YY_EMERALDS)
112 #define DX_DYNAMITE (DX + XX_DYNAMITE)
113 #define DY_DYNAMITE (DY + YY_DYNAMITE)
114 #define DX_KEYS (DX + XX_KEYS)
115 #define DY_KEYS (DY + YY_KEYS)
116 #define DX_SCORE (DX + XX_SCORE)
117 #define DY_SCORE (DY + YY_SCORE)
118 #define DX_TIME1 (DX + XX_TIME1)
119 #define DX_TIME2 (DX + XX_TIME2)
120 #define DY_TIME (DY + YY_TIME)
122 /* values for delayed check of falling and moving elements and for collision */
123 #define CHECK_DELAY_MOVING 3
124 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
125 #define CHECK_DELAY_COLLISION 2
126 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
128 /* values for initial player move delay (initial delay counter value) */
129 #define INITIAL_MOVE_DELAY_OFF -1
130 #define INITIAL_MOVE_DELAY_ON 0
132 /* values for player movement speed (which is in fact a delay value) */
133 #define MOVE_DELAY_MIN_SPEED 32
134 #define MOVE_DELAY_NORMAL_SPEED 8
135 #define MOVE_DELAY_HIGH_SPEED 4
136 #define MOVE_DELAY_MAX_SPEED 1
138 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
139 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
141 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
142 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
144 /* values for other actions */
145 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
146 #define MOVE_STEPSIZE_MIN (1)
147 #define MOVE_STEPSIZE_MAX (TILEX)
149 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
150 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
152 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
154 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
155 RND(element_info[e].push_delay_random))
156 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
157 RND(element_info[e].drop_delay_random))
158 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
159 RND(element_info[e].move_delay_random))
160 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
161 (element_info[e].move_delay_random))
162 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
163 RND(element_info[e].ce_value_random_initial))
164 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
165 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
166 RND((c)->delay_random * (c)->delay_frames))
167 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
168 RND((c)->delay_random))
171 #define GET_VALID_RUNTIME_ELEMENT(e) \
172 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
174 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
175 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
176 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
177 (be) + (e) - EL_SELF)
179 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
180 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
181 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
182 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
183 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
184 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
185 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
186 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
187 RESOLVED_REFERENCE_ELEMENT(be, e) : \
190 #define CAN_GROW_INTO(e) \
191 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
193 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
194 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
197 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
198 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
199 (CAN_MOVE_INTO_ACID(e) && \
200 Feld[x][y] == EL_ACID) || \
203 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
204 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
205 (CAN_MOVE_INTO_ACID(e) && \
206 Feld[x][y] == EL_ACID) || \
209 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
210 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
212 (CAN_MOVE_INTO_ACID(e) && \
213 Feld[x][y] == EL_ACID) || \
214 (DONT_COLLIDE_WITH(e) && \
216 !PLAYER_ENEMY_PROTECTED(x, y))))
218 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
219 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
221 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
222 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
224 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
225 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
227 #define ANDROID_CAN_CLONE_FIELD(x, y) \
228 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
229 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
231 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
232 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
234 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
235 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
237 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
238 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
240 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
241 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
243 #define PIG_CAN_ENTER_FIELD(e, x, y) \
244 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
246 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
247 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
248 IS_FOOD_PENGUIN(Feld[x][y])))
249 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
250 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
252 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
253 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
255 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
256 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
258 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
259 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
260 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
262 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
264 #define CE_ENTER_FIELD_COND(e, x, y) \
265 (!IS_PLAYER(x, y) && \
266 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
268 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
269 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
271 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
272 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
274 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
275 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
276 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
277 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
279 /* game button identifiers */
280 #define GAME_CTRL_ID_STOP 0
281 #define GAME_CTRL_ID_PAUSE 1
282 #define GAME_CTRL_ID_PLAY 2
283 #define SOUND_CTRL_ID_MUSIC 3
284 #define SOUND_CTRL_ID_LOOPS 4
285 #define SOUND_CTRL_ID_SIMPLE 5
287 #define NUM_GAME_BUTTONS 6
290 /* forward declaration for internal use */
292 static void CreateField(int, int, int);
294 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
295 static void AdvanceFrameAndPlayerCounters(int);
297 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
298 static boolean MovePlayer(struct PlayerInfo *, int, int);
299 static void ScrollPlayer(struct PlayerInfo *, int);
300 static void ScrollScreen(struct PlayerInfo *, int);
302 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
304 static void InitBeltMovement(void);
305 static void CloseAllOpenTimegates(void);
306 static void CheckGravityMovement(struct PlayerInfo *);
307 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
308 static void KillPlayerUnlessEnemyProtected(int, int);
309 static void KillPlayerUnlessExplosionProtected(int, int);
311 static void TestIfPlayerTouchesCustomElement(int, int);
312 static void TestIfElementTouchesCustomElement(int, int);
313 static void TestIfElementHitsCustomElement(int, int, int);
315 static void TestIfElementSmashesCustomElement(int, int, int);
318 static void HandleElementChange(int, int, int);
319 static void ExecuteCustomElementAction(int, int, int, int);
320 static boolean ChangeElement(int, int, int, int);
322 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
323 #define CheckTriggeredElementChange(x, y, e, ev) \
324 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
325 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
326 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
327 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
328 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
329 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
330 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
332 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
333 #define CheckElementChange(x, y, e, te, ev) \
334 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
335 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
336 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
337 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
338 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
340 static void PlayLevelSound(int, int, int);
341 static void PlayLevelSoundNearest(int, int, int);
342 static void PlayLevelSoundAction(int, int, int);
343 static void PlayLevelSoundElementAction(int, int, int, int);
344 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
345 static void PlayLevelSoundActionIfLoop(int, int, int);
346 static void StopLevelSoundActionIfLoop(int, int, int);
347 static void PlayLevelMusic();
349 static void MapGameButtons();
350 static void HandleGameButtons(struct GadgetInfo *);
352 int AmoebeNachbarNr(int, int);
353 void AmoebeUmwandeln(int, int);
354 void ContinueMoving(int, int);
356 void InitMovDir(int, int);
357 void InitAmoebaNr(int, int);
358 int NewHiScore(void);
360 void TestIfGoodThingHitsBadThing(int, int, int);
361 void TestIfBadThingHitsGoodThing(int, int, int);
362 void TestIfPlayerTouchesBadThing(int, int);
363 void TestIfPlayerRunsIntoBadThing(int, int, int);
364 void TestIfBadThingTouchesPlayer(int, int);
365 void TestIfBadThingRunsIntoPlayer(int, int, int);
366 void TestIfFriendTouchesBadThing(int, int);
367 void TestIfBadThingTouchesFriend(int, int);
368 void TestIfBadThingTouchesOtherBadThing(int, int);
370 void KillPlayer(struct PlayerInfo *);
371 void BuryPlayer(struct PlayerInfo *);
372 void RemovePlayer(struct PlayerInfo *);
374 boolean SnapField(struct PlayerInfo *, int, int);
375 boolean DropElement(struct PlayerInfo *);
377 static int getInvisibleActiveFromInvisibleElement(int);
378 static int getInvisibleFromInvisibleActiveElement(int);
380 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
383 /* ------------------------------------------------------------------------- */
384 /* definition of elements that automatically change to other elements after */
385 /* a specified time, eventually calling a function when changing */
386 /* ------------------------------------------------------------------------- */
388 /* forward declaration for changer functions */
389 static void InitBuggyBase(int, int);
390 static void WarnBuggyBase(int, int);
392 static void InitTrap(int, int);
393 static void ActivateTrap(int, int);
394 static void ChangeActiveTrap(int, int);
396 static void InitRobotWheel(int, int);
397 static void RunRobotWheel(int, int);
398 static void StopRobotWheel(int, int);
400 static void InitTimegateWheel(int, int);
401 static void RunTimegateWheel(int, int);
403 static void InitMagicBallDelay(int, int);
404 static void ActivateMagicBall(int, int);
406 struct ChangingElementInfo
411 void (*pre_change_function)(int x, int y);
412 void (*change_function)(int x, int y);
413 void (*post_change_function)(int x, int y);
416 static struct ChangingElementInfo change_delay_list[] =
467 EL_SWITCHGATE_OPENING,
475 EL_SWITCHGATE_CLOSING,
476 EL_SWITCHGATE_CLOSED,
508 EL_ACID_SPLASH_RIGHT,
517 EL_SP_BUGGY_BASE_ACTIVATING,
524 EL_SP_BUGGY_BASE_ACTIVATING,
525 EL_SP_BUGGY_BASE_ACTIVE,
532 EL_SP_BUGGY_BASE_ACTIVE,
556 EL_ROBOT_WHEEL_ACTIVE,
564 EL_TIMEGATE_SWITCH_ACTIVE,
572 EL_EMC_MAGIC_BALL_ACTIVE,
573 EL_EMC_MAGIC_BALL_ACTIVE,
580 EL_EMC_SPRING_BUMPER_ACTIVE,
581 EL_EMC_SPRING_BUMPER,
588 EL_DIAGONAL_SHRINKING,
617 int push_delay_fixed, push_delay_random;
622 { EL_BALLOON, 0, 0 },
624 { EL_SOKOBAN_OBJECT, 2, 0 },
625 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
626 { EL_SATELLITE, 2, 0 },
627 { EL_SP_DISK_YELLOW, 2, 0 },
629 { EL_UNDEFINED, 0, 0 },
637 move_stepsize_list[] =
639 { EL_AMOEBA_DROP, 2 },
640 { EL_AMOEBA_DROPPING, 2 },
641 { EL_QUICKSAND_FILLING, 1 },
642 { EL_QUICKSAND_EMPTYING, 1 },
643 { EL_MAGIC_WALL_FILLING, 2 },
644 { EL_BD_MAGIC_WALL_FILLING, 2 },
645 { EL_MAGIC_WALL_EMPTYING, 2 },
646 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
656 collect_count_list[] =
659 { EL_BD_DIAMOND, 1 },
660 { EL_EMERALD_YELLOW, 1 },
661 { EL_EMERALD_RED, 1 },
662 { EL_EMERALD_PURPLE, 1 },
664 { EL_SP_INFOTRON, 1 },
676 access_direction_list[] =
678 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
679 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
680 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
681 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
682 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
683 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
684 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
685 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
686 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
687 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
688 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
690 { EL_SP_PORT_LEFT, MV_RIGHT },
691 { EL_SP_PORT_RIGHT, MV_LEFT },
692 { EL_SP_PORT_UP, MV_DOWN },
693 { EL_SP_PORT_DOWN, MV_UP },
694 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
695 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
696 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
697 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
698 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
699 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
700 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
701 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
702 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
703 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
704 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
705 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
706 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
707 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
708 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
710 { EL_UNDEFINED, MV_NONE }
713 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
715 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
716 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
717 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
718 IS_JUST_CHANGING(x, y))
720 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
722 /* static variables for playfield scan mode (scanning forward or backward) */
723 static int playfield_scan_start_x = 0;
724 static int playfield_scan_start_y = 0;
725 static int playfield_scan_delta_x = 1;
726 static int playfield_scan_delta_y = 1;
728 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
729 (y) >= 0 && (y) <= lev_fieldy - 1; \
730 (y) += playfield_scan_delta_y) \
731 for ((x) = playfield_scan_start_x; \
732 (x) >= 0 && (x) <= lev_fieldx - 1; \
733 (x) += playfield_scan_delta_x) \
736 void DEBUG_SetMaximumDynamite()
740 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
741 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
742 local_player->inventory_element[local_player->inventory_size++] =
747 static void InitPlayfieldScanModeVars()
749 if (game.use_reverse_scan_direction)
751 playfield_scan_start_x = lev_fieldx - 1;
752 playfield_scan_start_y = lev_fieldy - 1;
754 playfield_scan_delta_x = -1;
755 playfield_scan_delta_y = -1;
759 playfield_scan_start_x = 0;
760 playfield_scan_start_y = 0;
762 playfield_scan_delta_x = 1;
763 playfield_scan_delta_y = 1;
767 static void InitPlayfieldScanMode(int mode)
769 game.use_reverse_scan_direction =
770 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
772 InitPlayfieldScanModeVars();
775 static int get_move_delay_from_stepsize(int move_stepsize)
778 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
780 /* make sure that stepsize value is always a power of 2 */
781 move_stepsize = (1 << log_2(move_stepsize));
783 return TILEX / move_stepsize;
786 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
789 int player_nr = player->index_nr;
790 int move_delay = get_move_delay_from_stepsize(move_stepsize);
791 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
793 /* do no immediately change move delay -- the player might just be moving */
794 player->move_delay_value_next = move_delay;
796 /* information if player can move must be set separately */
797 player->cannot_move = cannot_move;
801 player->move_delay = game.initial_move_delay[player_nr];
802 player->move_delay_value = game.initial_move_delay_value[player_nr];
804 player->move_delay_value_next = -1;
806 player->move_delay_reset_counter = 0;
810 void GetPlayerConfig()
812 if (!audio.sound_available)
813 setup.sound_simple = FALSE;
815 if (!audio.loops_available)
816 setup.sound_loops = FALSE;
818 if (!audio.music_available)
819 setup.sound_music = FALSE;
821 if (!video.fullscreen_available)
822 setup.fullscreen = FALSE;
824 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
826 SetAudioMode(setup.sound);
830 static int getBeltNrFromBeltElement(int element)
832 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
833 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
834 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
837 static int getBeltNrFromBeltActiveElement(int element)
839 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
840 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
841 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
844 static int getBeltNrFromBeltSwitchElement(int element)
846 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
847 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
848 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
851 static int getBeltDirNrFromBeltSwitchElement(int element)
853 static int belt_base_element[4] =
855 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
856 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
857 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
858 EL_CONVEYOR_BELT_4_SWITCH_LEFT
861 int belt_nr = getBeltNrFromBeltSwitchElement(element);
862 int belt_dir_nr = element - belt_base_element[belt_nr];
864 return (belt_dir_nr % 3);
867 static int getBeltDirFromBeltSwitchElement(int element)
869 static int belt_move_dir[3] =
876 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
878 return belt_move_dir[belt_dir_nr];
881 static int get_element_from_group_element(int element)
883 if (IS_GROUP_ELEMENT(element))
885 struct ElementGroupInfo *group = element_info[element].group;
886 int last_anim_random_frame = gfx.anim_random_frame;
889 if (group->choice_mode == ANIM_RANDOM)
890 gfx.anim_random_frame = RND(group->num_elements_resolved);
892 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
893 group->choice_mode, 0,
896 if (group->choice_mode == ANIM_RANDOM)
897 gfx.anim_random_frame = last_anim_random_frame;
901 element = group->element_resolved[element_pos];
907 static void InitPlayerField(int x, int y, int element, boolean init_game)
909 if (element == EL_SP_MURPHY)
913 if (stored_player[0].present)
915 Feld[x][y] = EL_SP_MURPHY_CLONE;
921 stored_player[0].use_murphy = TRUE;
923 if (!level.use_artwork_element[0])
924 stored_player[0].artwork_element = EL_SP_MURPHY;
927 Feld[x][y] = EL_PLAYER_1;
933 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
934 int jx = player->jx, jy = player->jy;
936 player->present = TRUE;
938 player->block_last_field = (element == EL_SP_MURPHY ?
939 level.sp_block_last_field :
940 level.block_last_field);
942 /* ---------- initialize player's last field block delay --------------- */
944 /* always start with reliable default value (no adjustment needed) */
945 player->block_delay_adjustment = 0;
947 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
948 if (player->block_last_field && element == EL_SP_MURPHY)
949 player->block_delay_adjustment = 1;
951 /* special case 2: in game engines before 3.1.1, blocking was different */
952 if (game.use_block_last_field_bug)
953 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
955 if (!options.network || player->connected)
957 player->active = TRUE;
959 /* remove potentially duplicate players */
960 if (StorePlayer[jx][jy] == Feld[x][y])
961 StorePlayer[jx][jy] = 0;
963 StorePlayer[x][y] = Feld[x][y];
967 printf("Player %d activated.\n", player->element_nr);
968 printf("[Local player is %d and currently %s.]\n",
969 local_player->element_nr,
970 local_player->active ? "active" : "not active");
974 Feld[x][y] = EL_EMPTY;
976 player->jx = player->last_jx = x;
977 player->jy = player->last_jy = y;
981 static void InitField(int x, int y, boolean init_game)
983 int element = Feld[x][y];
992 InitPlayerField(x, y, element, init_game);
995 case EL_SOKOBAN_FIELD_PLAYER:
996 element = Feld[x][y] = EL_PLAYER_1;
997 InitField(x, y, init_game);
999 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1000 InitField(x, y, init_game);
1003 case EL_SOKOBAN_FIELD_EMPTY:
1004 local_player->sokobanfields_still_needed++;
1008 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1009 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1010 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1011 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1012 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1013 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1014 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1015 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1016 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1017 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1026 case EL_SPACESHIP_RIGHT:
1027 case EL_SPACESHIP_UP:
1028 case EL_SPACESHIP_LEFT:
1029 case EL_SPACESHIP_DOWN:
1030 case EL_BD_BUTTERFLY:
1031 case EL_BD_BUTTERFLY_RIGHT:
1032 case EL_BD_BUTTERFLY_UP:
1033 case EL_BD_BUTTERFLY_LEFT:
1034 case EL_BD_BUTTERFLY_DOWN:
1036 case EL_BD_FIREFLY_RIGHT:
1037 case EL_BD_FIREFLY_UP:
1038 case EL_BD_FIREFLY_LEFT:
1039 case EL_BD_FIREFLY_DOWN:
1040 case EL_PACMAN_RIGHT:
1042 case EL_PACMAN_LEFT:
1043 case EL_PACMAN_DOWN:
1045 case EL_YAMYAM_LEFT:
1046 case EL_YAMYAM_RIGHT:
1048 case EL_YAMYAM_DOWN:
1049 case EL_DARK_YAMYAM:
1052 case EL_SP_SNIKSNAK:
1053 case EL_SP_ELECTRON:
1062 case EL_AMOEBA_FULL:
1067 case EL_AMOEBA_DROP:
1068 if (y == lev_fieldy - 1)
1070 Feld[x][y] = EL_AMOEBA_GROWING;
1071 Store[x][y] = EL_AMOEBA_WET;
1075 case EL_DYNAMITE_ACTIVE:
1076 case EL_SP_DISK_RED_ACTIVE:
1077 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1078 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1079 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1080 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1081 MovDelay[x][y] = 96;
1084 case EL_EM_DYNAMITE_ACTIVE:
1085 MovDelay[x][y] = 32;
1089 local_player->lights_still_needed++;
1093 local_player->friends_still_needed++;
1098 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1101 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1102 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1103 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1104 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1105 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1106 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1107 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1108 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1109 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1110 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1111 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1112 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1115 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1116 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1117 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1119 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1121 game.belt_dir[belt_nr] = belt_dir;
1122 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1124 else /* more than one switch -- set it like the first switch */
1126 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1131 #if !USE_BOTH_SWITCHGATE_SWITCHES
1132 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1134 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1138 case EL_LIGHT_SWITCH_ACTIVE:
1140 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1143 case EL_INVISIBLE_STEELWALL:
1144 case EL_INVISIBLE_WALL:
1145 case EL_INVISIBLE_SAND:
1146 if (game.light_time_left > 0 ||
1147 game.lenses_time_left > 0)
1148 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1151 case EL_EMC_MAGIC_BALL:
1152 if (game.ball_state)
1153 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1156 case EL_EMC_MAGIC_BALL_SWITCH:
1157 if (game.ball_state)
1158 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1162 if (IS_CUSTOM_ELEMENT(element))
1164 if (CAN_MOVE(element))
1167 #if USE_NEW_CUSTOM_VALUE
1168 if (!element_info[element].use_last_ce_value || init_game)
1169 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1172 else if (IS_GROUP_ELEMENT(element))
1174 Feld[x][y] = get_element_from_group_element(element);
1176 InitField(x, y, init_game);
1183 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1186 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1188 InitField(x, y, init_game);
1190 /* not needed to call InitMovDir() -- already done by InitField()! */
1191 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1192 CAN_MOVE(Feld[x][y]))
1196 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1198 int old_element = Feld[x][y];
1200 InitField(x, y, init_game);
1202 /* not needed to call InitMovDir() -- already done by InitField()! */
1203 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1204 CAN_MOVE(old_element) &&
1205 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1208 /* this case is in fact a combination of not less than three bugs:
1209 first, it calls InitMovDir() for elements that can move, although this is
1210 already done by InitField(); then, it checks the element that was at this
1211 field _before_ the call to InitField() (which can change it); lastly, it
1212 was not called for "mole with direction" elements, which were treated as
1213 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1217 inline void DrawGameValue_Emeralds(int value)
1219 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1221 if (PANEL_DEACTIVATED(game.panel.gems))
1224 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1227 inline void DrawGameValue_Dynamite(int value)
1229 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1231 if (PANEL_DEACTIVATED(game.panel.inventory))
1234 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1237 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1239 int base_key_graphic = EL_KEY_1;
1242 if (PANEL_DEACTIVATED(game.panel.keys))
1245 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1246 base_key_graphic = EL_EM_KEY_1;
1248 /* currently only 4 of 8 possible keys are displayed */
1249 for (i = 0; i < STD_NUM_KEYS; i++)
1251 int x = XX_KEYS + i * MINI_TILEX;
1255 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1257 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1258 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1262 inline void DrawGameValue_Score(int value)
1264 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1266 if (PANEL_DEACTIVATED(game.panel.score))
1269 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1272 inline void DrawGameValue_Time(int value)
1274 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1275 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1277 if (PANEL_DEACTIVATED(game.panel.time))
1280 /* clear background if value just changed its size */
1281 if (value == 999 || value == 1000)
1282 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1285 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1287 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1290 inline void DrawGameValue_Level(int value)
1292 if (PANEL_DEACTIVATED(game.panel.level))
1296 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1298 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
1301 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1304 int key[MAX_NUM_KEYS];
1307 /* prevent EM engine from updating time/score values parallel to GameWon() */
1308 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1309 local_player->LevelSolved)
1312 for (i = 0; i < MAX_NUM_KEYS; i++)
1313 key[i] = key_bits & (1 << i);
1315 DrawGameValue_Level(level_nr);
1317 DrawGameValue_Emeralds(emeralds);
1318 DrawGameValue_Dynamite(dynamite);
1319 DrawGameValue_Score(score);
1320 DrawGameValue_Time(time);
1322 DrawGameValue_Keys(key);
1325 void DrawGameDoorValues()
1327 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1328 int dynamite_value = 0;
1329 int score_value = (local_player->LevelSolved ? local_player->score_final :
1330 local_player->score);
1331 int gems_value = local_player->gems_still_needed;
1335 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1337 DrawGameDoorValues_EM();
1342 if (game.centered_player_nr == -1)
1344 for (i = 0; i < MAX_PLAYERS; i++)
1346 for (j = 0; j < MAX_NUM_KEYS; j++)
1347 if (stored_player[i].key[j])
1348 key_bits |= (1 << j);
1350 dynamite_value += stored_player[i].inventory_size;
1355 int player_nr = game.centered_player_nr;
1357 for (i = 0; i < MAX_NUM_KEYS; i++)
1358 if (stored_player[player_nr].key[i])
1359 key_bits |= (1 << i);
1361 dynamite_value = stored_player[player_nr].inventory_size;
1364 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1370 =============================================================================
1372 -----------------------------------------------------------------------------
1373 initialize game engine due to level / tape version number
1374 =============================================================================
1377 static void InitGameEngine()
1379 int i, j, k, l, x, y;
1381 /* set game engine from tape file when re-playing, else from level file */
1382 game.engine_version = (tape.playing ? tape.engine_version :
1383 level.game_version);
1385 /* ---------------------------------------------------------------------- */
1386 /* set flags for bugs and changes according to active game engine version */
1387 /* ---------------------------------------------------------------------- */
1390 Summary of bugfix/change:
1391 Fixed handling for custom elements that change when pushed by the player.
1393 Fixed/changed in version:
1397 Before 3.1.0, custom elements that "change when pushing" changed directly
1398 after the player started pushing them (until then handled in "DigField()").
1399 Since 3.1.0, these custom elements are not changed until the "pushing"
1400 move of the element is finished (now handled in "ContinueMoving()").
1402 Affected levels/tapes:
1403 The first condition is generally needed for all levels/tapes before version
1404 3.1.0, which might use the old behaviour before it was changed; known tapes
1405 that are affected are some tapes from the level set "Walpurgis Gardens" by
1407 The second condition is an exception from the above case and is needed for
1408 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1409 above (including some development versions of 3.1.0), but before it was
1410 known that this change would break tapes like the above and was fixed in
1411 3.1.1, so that the changed behaviour was active although the engine version
1412 while recording maybe was before 3.1.0. There is at least one tape that is
1413 affected by this exception, which is the tape for the one-level set "Bug
1414 Machine" by Juergen Bonhagen.
1417 game.use_change_when_pushing_bug =
1418 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1420 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1421 tape.game_version < VERSION_IDENT(3,1,1,0)));
1424 Summary of bugfix/change:
1425 Fixed handling for blocking the field the player leaves when moving.
1427 Fixed/changed in version:
1431 Before 3.1.1, when "block last field when moving" was enabled, the field
1432 the player is leaving when moving was blocked for the time of the move,
1433 and was directly unblocked afterwards. This resulted in the last field
1434 being blocked for exactly one less than the number of frames of one player
1435 move. Additionally, even when blocking was disabled, the last field was
1436 blocked for exactly one frame.
1437 Since 3.1.1, due to changes in player movement handling, the last field
1438 is not blocked at all when blocking is disabled. When blocking is enabled,
1439 the last field is blocked for exactly the number of frames of one player
1440 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1441 last field is blocked for exactly one more than the number of frames of
1444 Affected levels/tapes:
1445 (!!! yet to be determined -- probably many !!!)
1448 game.use_block_last_field_bug =
1449 (game.engine_version < VERSION_IDENT(3,1,1,0));
1452 Summary of bugfix/change:
1453 Changed behaviour of CE changes with multiple changes per single frame.
1455 Fixed/changed in version:
1459 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1460 This resulted in race conditions where CEs seem to behave strange in some
1461 situations (where triggered CE changes were just skipped because there was
1462 already a CE change on that tile in the playfield in that engine frame).
1463 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1464 (The number of changes per frame must be limited in any case, because else
1465 it is easily possible to define CE changes that would result in an infinite
1466 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1467 should be set large enough so that it would only be reached in cases where
1468 the corresponding CE change conditions run into a loop. Therefore, it seems
1469 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1470 maximal number of change pages for custom elements.)
1472 Affected levels/tapes:
1476 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1477 game.max_num_changes_per_frame = 1;
1479 game.max_num_changes_per_frame =
1480 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1483 /* ---------------------------------------------------------------------- */
1485 /* default scan direction: scan playfield from top/left to bottom/right */
1486 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1488 /* dynamically adjust element properties according to game engine version */
1489 InitElementPropertiesEngine(game.engine_version);
1492 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1493 printf(" tape version == %06d [%s] [file: %06d]\n",
1494 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1496 printf(" => game.engine_version == %06d\n", game.engine_version);
1499 /* ---------- initialize player's initial move delay --------------------- */
1501 /* dynamically adjust player properties according to level information */
1502 for (i = 0; i < MAX_PLAYERS; i++)
1503 game.initial_move_delay_value[i] =
1504 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1506 /* dynamically adjust player properties according to game engine version */
1507 for (i = 0; i < MAX_PLAYERS; i++)
1508 game.initial_move_delay[i] =
1509 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1510 game.initial_move_delay_value[i] : 0);
1512 /* ---------- initialize player's initial push delay --------------------- */
1514 /* dynamically adjust player properties according to game engine version */
1515 game.initial_push_delay_value =
1516 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1518 /* ---------- initialize changing elements ------------------------------- */
1520 /* initialize changing elements information */
1521 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1523 struct ElementInfo *ei = &element_info[i];
1525 /* this pointer might have been changed in the level editor */
1526 ei->change = &ei->change_page[0];
1528 if (!IS_CUSTOM_ELEMENT(i))
1530 ei->change->target_element = EL_EMPTY_SPACE;
1531 ei->change->delay_fixed = 0;
1532 ei->change->delay_random = 0;
1533 ei->change->delay_frames = 1;
1536 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1538 ei->has_change_event[j] = FALSE;
1540 ei->event_page_nr[j] = 0;
1541 ei->event_page[j] = &ei->change_page[0];
1545 /* add changing elements from pre-defined list */
1546 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1548 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1549 struct ElementInfo *ei = &element_info[ch_delay->element];
1551 ei->change->target_element = ch_delay->target_element;
1552 ei->change->delay_fixed = ch_delay->change_delay;
1554 ei->change->pre_change_function = ch_delay->pre_change_function;
1555 ei->change->change_function = ch_delay->change_function;
1556 ei->change->post_change_function = ch_delay->post_change_function;
1558 ei->change->can_change = TRUE;
1559 ei->change->can_change_or_has_action = TRUE;
1561 ei->has_change_event[CE_DELAY] = TRUE;
1563 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1564 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1567 /* ---------- initialize internal run-time variables ------------- */
1569 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1571 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1573 for (j = 0; j < ei->num_change_pages; j++)
1575 ei->change_page[j].can_change_or_has_action =
1576 (ei->change_page[j].can_change |
1577 ei->change_page[j].has_action);
1581 /* add change events from custom element configuration */
1582 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1584 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1586 for (j = 0; j < ei->num_change_pages; j++)
1588 if (!ei->change_page[j].can_change_or_has_action)
1591 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1593 /* only add event page for the first page found with this event */
1594 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1596 ei->has_change_event[k] = TRUE;
1598 ei->event_page_nr[k] = j;
1599 ei->event_page[k] = &ei->change_page[j];
1605 /* ---------- initialize run-time trigger player and element ------------- */
1607 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1609 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1611 for (j = 0; j < ei->num_change_pages; j++)
1613 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1614 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1615 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1616 ei->change_page[j].actual_trigger_ce_value = 0;
1617 ei->change_page[j].actual_trigger_ce_score = 0;
1621 /* ---------- initialize trigger events ---------------------------------- */
1623 /* initialize trigger events information */
1624 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1625 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1626 trigger_events[i][j] = FALSE;
1628 /* add trigger events from element change event properties */
1629 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1631 struct ElementInfo *ei = &element_info[i];
1633 for (j = 0; j < ei->num_change_pages; j++)
1635 if (!ei->change_page[j].can_change_or_has_action)
1638 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1640 int trigger_element = ei->change_page[j].trigger_element;
1642 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1644 if (ei->change_page[j].has_event[k])
1646 if (IS_GROUP_ELEMENT(trigger_element))
1648 struct ElementGroupInfo *group =
1649 element_info[trigger_element].group;
1651 for (l = 0; l < group->num_elements_resolved; l++)
1652 trigger_events[group->element_resolved[l]][k] = TRUE;
1654 else if (trigger_element == EL_ANY_ELEMENT)
1655 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1656 trigger_events[l][k] = TRUE;
1658 trigger_events[trigger_element][k] = TRUE;
1665 /* ---------- initialize push delay -------------------------------------- */
1667 /* initialize push delay values to default */
1668 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1670 if (!IS_CUSTOM_ELEMENT(i))
1672 /* set default push delay values (corrected since version 3.0.7-1) */
1673 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1675 element_info[i].push_delay_fixed = 2;
1676 element_info[i].push_delay_random = 8;
1680 element_info[i].push_delay_fixed = 8;
1681 element_info[i].push_delay_random = 8;
1686 /* set push delay value for certain elements from pre-defined list */
1687 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1689 int e = push_delay_list[i].element;
1691 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1692 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1695 /* set push delay value for Supaplex elements for newer engine versions */
1696 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1698 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1700 if (IS_SP_ELEMENT(i))
1702 /* set SP push delay to just enough to push under a falling zonk */
1703 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1705 element_info[i].push_delay_fixed = delay;
1706 element_info[i].push_delay_random = 0;
1711 /* ---------- initialize move stepsize ----------------------------------- */
1713 /* initialize move stepsize values to default */
1714 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1715 if (!IS_CUSTOM_ELEMENT(i))
1716 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1718 /* set move stepsize value for certain elements from pre-defined list */
1719 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1721 int e = move_stepsize_list[i].element;
1723 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1726 /* ---------- initialize collect score ----------------------------------- */
1728 /* initialize collect score values for custom elements from initial value */
1729 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1730 if (IS_CUSTOM_ELEMENT(i))
1731 element_info[i].collect_score = element_info[i].collect_score_initial;
1733 /* ---------- initialize collect count ----------------------------------- */
1735 /* initialize collect count values for non-custom elements */
1736 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1737 if (!IS_CUSTOM_ELEMENT(i))
1738 element_info[i].collect_count_initial = 0;
1740 /* add collect count values for all elements from pre-defined list */
1741 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1742 element_info[collect_count_list[i].element].collect_count_initial =
1743 collect_count_list[i].count;
1745 /* ---------- initialize access direction -------------------------------- */
1747 /* initialize access direction values to default (access from every side) */
1748 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1749 if (!IS_CUSTOM_ELEMENT(i))
1750 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1752 /* set access direction value for certain elements from pre-defined list */
1753 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1754 element_info[access_direction_list[i].element].access_direction =
1755 access_direction_list[i].direction;
1757 /* ---------- initialize explosion content ------------------------------- */
1758 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1760 if (IS_CUSTOM_ELEMENT(i))
1763 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1765 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1767 element_info[i].content.e[x][y] =
1768 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1769 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1770 i == EL_PLAYER_3 ? EL_EMERALD :
1771 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1772 i == EL_MOLE ? EL_EMERALD_RED :
1773 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1774 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1775 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1776 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1777 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1778 i == EL_WALL_EMERALD ? EL_EMERALD :
1779 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1780 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1781 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1782 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1783 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1784 i == EL_WALL_PEARL ? EL_PEARL :
1785 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1791 int get_num_special_action(int element, int action_first, int action_last)
1793 int num_special_action = 0;
1796 for (i = action_first; i <= action_last; i++)
1798 boolean found = FALSE;
1800 for (j = 0; j < NUM_DIRECTIONS; j++)
1801 if (el_act_dir2img(element, i, j) !=
1802 el_act_dir2img(element, ACTION_DEFAULT, j))
1806 num_special_action++;
1811 return num_special_action;
1816 =============================================================================
1818 -----------------------------------------------------------------------------
1819 initialize and start new game
1820 =============================================================================
1825 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1826 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1827 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1828 boolean do_fading = (game_status == GAME_MODE_MAIN);
1831 game_status = GAME_MODE_PLAYING;
1835 /* don't play tapes over network */
1836 network_playing = (options.network && !tape.playing);
1838 for (i = 0; i < MAX_PLAYERS; i++)
1840 struct PlayerInfo *player = &stored_player[i];
1842 player->index_nr = i;
1843 player->index_bit = (1 << i);
1844 player->element_nr = EL_PLAYER_1 + i;
1846 player->present = FALSE;
1847 player->active = FALSE;
1848 player->killed = FALSE;
1851 player->effective_action = 0;
1852 player->programmed_action = 0;
1855 player->score_final = 0;
1857 player->gems_still_needed = level.gems_needed;
1858 player->sokobanfields_still_needed = 0;
1859 player->lights_still_needed = 0;
1860 player->friends_still_needed = 0;
1862 for (j = 0; j < MAX_NUM_KEYS; j++)
1863 player->key[j] = FALSE;
1865 player->dynabomb_count = 0;
1866 player->dynabomb_size = 1;
1867 player->dynabombs_left = 0;
1868 player->dynabomb_xl = FALSE;
1870 player->MovDir = MV_NONE;
1873 player->GfxDir = MV_NONE;
1874 player->GfxAction = ACTION_DEFAULT;
1876 player->StepFrame = 0;
1878 player->use_murphy = FALSE;
1879 player->artwork_element =
1880 (level.use_artwork_element[i] ? level.artwork_element[i] :
1881 player->element_nr);
1883 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1884 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1886 player->gravity = level.initial_player_gravity[i];
1888 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1890 player->actual_frame_counter = 0;
1892 player->step_counter = 0;
1894 player->last_move_dir = MV_NONE;
1896 player->is_active = FALSE;
1898 player->is_waiting = FALSE;
1899 player->is_moving = FALSE;
1900 player->is_auto_moving = FALSE;
1901 player->is_digging = FALSE;
1902 player->is_snapping = FALSE;
1903 player->is_collecting = FALSE;
1904 player->is_pushing = FALSE;
1905 player->is_switching = FALSE;
1906 player->is_dropping = FALSE;
1907 player->is_dropping_pressed = FALSE;
1909 player->is_bored = FALSE;
1910 player->is_sleeping = FALSE;
1912 player->frame_counter_bored = -1;
1913 player->frame_counter_sleeping = -1;
1915 player->anim_delay_counter = 0;
1916 player->post_delay_counter = 0;
1918 player->dir_waiting = MV_NONE;
1919 player->action_waiting = ACTION_DEFAULT;
1920 player->last_action_waiting = ACTION_DEFAULT;
1921 player->special_action_bored = ACTION_DEFAULT;
1922 player->special_action_sleeping = ACTION_DEFAULT;
1924 player->switch_x = -1;
1925 player->switch_y = -1;
1927 player->drop_x = -1;
1928 player->drop_y = -1;
1930 player->show_envelope = 0;
1932 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
1934 player->push_delay = -1; /* initialized when pushing starts */
1935 player->push_delay_value = game.initial_push_delay_value;
1937 player->drop_delay = 0;
1938 player->drop_pressed_delay = 0;
1940 player->last_jx = -1;
1941 player->last_jy = -1;
1945 player->shield_normal_time_left = 0;
1946 player->shield_deadly_time_left = 0;
1948 player->inventory_infinite_element = EL_UNDEFINED;
1949 player->inventory_size = 0;
1951 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1952 SnapField(player, 0, 0);
1954 player->LevelSolved = FALSE;
1955 player->GameOver = FALSE;
1957 player->LevelSolved_GameEnd = FALSE;
1958 player->LevelSolved_SaveTape = FALSE;
1959 player->LevelSolved_SaveScore = FALSE;
1962 network_player_action_received = FALSE;
1964 #if defined(NETWORK_AVALIABLE)
1965 /* initial null action */
1966 if (network_playing)
1967 SendToServer_MovePlayer(MV_NONE);
1976 TimeLeft = level.time;
1979 ScreenMovDir = MV_NONE;
1983 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1985 AllPlayersGone = FALSE;
1987 game.yamyam_content_nr = 0;
1988 game.magic_wall_active = FALSE;
1989 game.magic_wall_time_left = 0;
1990 game.light_time_left = 0;
1991 game.timegate_time_left = 0;
1992 game.switchgate_pos = 0;
1993 game.wind_direction = level.wind_direction_initial;
1995 #if !USE_PLAYER_GRAVITY
1996 game.gravity = FALSE;
1997 game.explosions_delayed = TRUE;
2000 game.lenses_time_left = 0;
2001 game.magnify_time_left = 0;
2003 game.ball_state = level.ball_state_initial;
2004 game.ball_content_nr = 0;
2006 game.envelope_active = FALSE;
2008 /* set focus to local player for network games, else to all players */
2009 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2010 game.centered_player_nr_next = game.centered_player_nr;
2011 game.set_centered_player = FALSE;
2013 if (network_playing && tape.recording)
2015 /* store client dependent player focus when recording network games */
2016 tape.centered_player_nr_next = game.centered_player_nr_next;
2017 tape.set_centered_player = TRUE;
2020 for (i = 0; i < NUM_BELTS; i++)
2022 game.belt_dir[i] = MV_NONE;
2023 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2026 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2027 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2029 SCAN_PLAYFIELD(x, y)
2031 Feld[x][y] = level.field[x][y];
2032 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2033 ChangeDelay[x][y] = 0;
2034 ChangePage[x][y] = -1;
2035 #if USE_NEW_CUSTOM_VALUE
2036 CustomValue[x][y] = 0; /* initialized in InitField() */
2038 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2040 WasJustMoving[x][y] = 0;
2041 WasJustFalling[x][y] = 0;
2042 CheckCollision[x][y] = 0;
2043 CheckImpact[x][y] = 0;
2045 Pushed[x][y] = FALSE;
2047 ChangeCount[x][y] = 0;
2048 ChangeEvent[x][y] = -1;
2050 ExplodePhase[x][y] = 0;
2051 ExplodeDelay[x][y] = 0;
2052 ExplodeField[x][y] = EX_TYPE_NONE;
2054 RunnerVisit[x][y] = 0;
2055 PlayerVisit[x][y] = 0;
2058 GfxRandom[x][y] = INIT_GFX_RANDOM();
2059 GfxElement[x][y] = EL_UNDEFINED;
2060 GfxAction[x][y] = ACTION_DEFAULT;
2061 GfxDir[x][y] = MV_NONE;
2064 SCAN_PLAYFIELD(x, y)
2066 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2068 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2070 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2073 InitField(x, y, TRUE);
2078 for (i = 0; i < MAX_PLAYERS; i++)
2080 struct PlayerInfo *player = &stored_player[i];
2082 /* set number of special actions for bored and sleeping animation */
2083 player->num_special_action_bored =
2084 get_num_special_action(player->artwork_element,
2085 ACTION_BORING_1, ACTION_BORING_LAST);
2086 player->num_special_action_sleeping =
2087 get_num_special_action(player->artwork_element,
2088 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2091 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2092 emulate_sb ? EMU_SOKOBAN :
2093 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2095 #if USE_NEW_ALL_SLIPPERY
2096 /* initialize type of slippery elements */
2097 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2099 if (!IS_CUSTOM_ELEMENT(i))
2101 /* default: elements slip down either to the left or right randomly */
2102 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2104 /* SP style elements prefer to slip down on the left side */
2105 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2106 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2108 /* BD style elements prefer to slip down on the left side */
2109 if (game.emulation == EMU_BOULDERDASH)
2110 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2115 /* initialize explosion and ignition delay */
2116 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2118 if (!IS_CUSTOM_ELEMENT(i))
2121 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2122 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2123 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2124 int last_phase = (num_phase + 1) * delay;
2125 int half_phase = (num_phase / 2) * delay;
2127 element_info[i].explosion_delay = last_phase - 1;
2128 element_info[i].ignition_delay = half_phase;
2130 if (i == EL_BLACK_ORB)
2131 element_info[i].ignition_delay = 1;
2135 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2136 element_info[i].explosion_delay = 1;
2138 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2139 element_info[i].ignition_delay = 1;
2143 /* correct non-moving belts to start moving left */
2144 for (i = 0; i < NUM_BELTS; i++)
2145 if (game.belt_dir[i] == MV_NONE)
2146 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2148 /* check if any connected player was not found in playfield */
2149 for (i = 0; i < MAX_PLAYERS; i++)
2151 struct PlayerInfo *player = &stored_player[i];
2153 if (player->connected && !player->present)
2155 for (j = 0; j < MAX_PLAYERS; j++)
2157 struct PlayerInfo *some_player = &stored_player[j];
2158 int jx = some_player->jx, jy = some_player->jy;
2160 /* assign first free player found that is present in the playfield */
2161 if (some_player->present && !some_player->connected)
2163 player->present = TRUE;
2164 player->active = TRUE;
2166 some_player->present = FALSE;
2167 some_player->active = FALSE;
2169 player->artwork_element = some_player->artwork_element;
2171 player->block_last_field = some_player->block_last_field;
2172 player->block_delay_adjustment = some_player->block_delay_adjustment;
2174 StorePlayer[jx][jy] = player->element_nr;
2175 player->jx = player->last_jx = jx;
2176 player->jy = player->last_jy = jy;
2186 /* when playing a tape, eliminate all players who do not participate */
2188 for (i = 0; i < MAX_PLAYERS; i++)
2190 if (stored_player[i].active && !tape.player_participates[i])
2192 struct PlayerInfo *player = &stored_player[i];
2193 int jx = player->jx, jy = player->jy;
2195 player->active = FALSE;
2196 StorePlayer[jx][jy] = 0;
2197 Feld[jx][jy] = EL_EMPTY;
2201 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2203 /* when in single player mode, eliminate all but the first active player */
2205 for (i = 0; i < MAX_PLAYERS; i++)
2207 if (stored_player[i].active)
2209 for (j = i + 1; j < MAX_PLAYERS; j++)
2211 if (stored_player[j].active)
2213 struct PlayerInfo *player = &stored_player[j];
2214 int jx = player->jx, jy = player->jy;
2216 player->active = FALSE;
2217 player->present = FALSE;
2219 StorePlayer[jx][jy] = 0;
2220 Feld[jx][jy] = EL_EMPTY;
2227 /* when recording the game, store which players take part in the game */
2230 for (i = 0; i < MAX_PLAYERS; i++)
2231 if (stored_player[i].active)
2232 tape.player_participates[i] = TRUE;
2237 for (i = 0; i < MAX_PLAYERS; i++)
2239 struct PlayerInfo *player = &stored_player[i];
2241 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2246 if (local_player == player)
2247 printf("Player %d is local player.\n", i+1);
2251 if (BorderElement == EL_EMPTY)
2254 SBX_Right = lev_fieldx - SCR_FIELDX;
2256 SBY_Lower = lev_fieldy - SCR_FIELDY;
2261 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2263 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2266 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2267 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2269 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2270 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2272 /* if local player not found, look for custom element that might create
2273 the player (make some assumptions about the right custom element) */
2274 if (!local_player->present)
2276 int start_x = 0, start_y = 0;
2277 int found_rating = 0;
2278 int found_element = EL_UNDEFINED;
2279 int player_nr = local_player->index_nr;
2281 SCAN_PLAYFIELD(x, y)
2283 int element = Feld[x][y];
2288 if (level.use_start_element[player_nr] &&
2289 level.start_element[player_nr] == element &&
2296 found_element = element;
2299 if (!IS_CUSTOM_ELEMENT(element))
2302 if (CAN_CHANGE(element))
2304 for (i = 0; i < element_info[element].num_change_pages; i++)
2306 /* check for player created from custom element as single target */
2307 content = element_info[element].change_page[i].target_element;
2308 is_player = ELEM_IS_PLAYER(content);
2310 if (is_player && (found_rating < 3 || element < found_element))
2316 found_element = element;
2321 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2323 /* check for player created from custom element as explosion content */
2324 content = element_info[element].content.e[xx][yy];
2325 is_player = ELEM_IS_PLAYER(content);
2327 if (is_player && (found_rating < 2 || element < found_element))
2329 start_x = x + xx - 1;
2330 start_y = y + yy - 1;
2333 found_element = element;
2336 if (!CAN_CHANGE(element))
2339 for (i = 0; i < element_info[element].num_change_pages; i++)
2341 /* check for player created from custom element as extended target */
2343 element_info[element].change_page[i].target_content.e[xx][yy];
2345 is_player = ELEM_IS_PLAYER(content);
2347 if (is_player && (found_rating < 1 || element < found_element))
2349 start_x = x + xx - 1;
2350 start_y = y + yy - 1;
2353 found_element = element;
2359 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2360 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2363 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2364 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2369 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2370 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2371 local_player->jx - MIDPOSX);
2373 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2374 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2375 local_player->jy - MIDPOSY);
2380 if (!game.restart_level)
2381 CloseDoor(DOOR_CLOSE_1);
2384 FadeOut(REDRAW_FIELD);
2386 /* !!! FIX THIS (START) !!! */
2387 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2389 InitGameEngine_EM();
2391 /* blit playfield from scroll buffer to normal back buffer for fading in */
2392 BlitScreenToBitmap_EM(backbuffer);
2399 /* after drawing the level, correct some elements */
2400 if (game.timegate_time_left == 0)
2401 CloseAllOpenTimegates();
2403 /* blit playfield from scroll buffer to normal back buffer for fading in */
2404 if (setup.soft_scrolling)
2405 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2407 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2409 /* !!! FIX THIS (END) !!! */
2412 FadeIn(REDRAW_FIELD);
2416 if (!game.restart_level)
2418 /* copy default game door content to main double buffer */
2419 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2420 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2423 SetPanelBackground();
2424 SetDrawBackgroundMask(REDRAW_DOOR_1);
2426 DrawGameDoorValues();
2428 if (!game.restart_level)
2432 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2433 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2434 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2438 /* copy actual game door content to door double buffer for OpenDoor() */
2439 BlitBitmap(drawto, bitmap_db_door,
2440 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2442 OpenDoor(DOOR_OPEN_ALL);
2444 PlaySound(SND_GAME_STARTING);
2446 if (setup.sound_music)
2449 KeyboardAutoRepeatOffUnlessAutoplay();
2453 for (i = 0; i < MAX_PLAYERS; i++)
2454 printf("Player %d %sactive.\n",
2455 i + 1, (stored_player[i].active ? "" : "not "));
2466 game.restart_level = FALSE;
2469 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2471 /* this is used for non-R'n'D game engines to update certain engine values */
2473 /* needed to determine if sounds are played within the visible screen area */
2474 scroll_x = actual_scroll_x;
2475 scroll_y = actual_scroll_y;
2478 void InitMovDir(int x, int y)
2480 int i, element = Feld[x][y];
2481 static int xy[4][2] =
2488 static int direction[3][4] =
2490 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2491 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2492 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2501 Feld[x][y] = EL_BUG;
2502 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2505 case EL_SPACESHIP_RIGHT:
2506 case EL_SPACESHIP_UP:
2507 case EL_SPACESHIP_LEFT:
2508 case EL_SPACESHIP_DOWN:
2509 Feld[x][y] = EL_SPACESHIP;
2510 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2513 case EL_BD_BUTTERFLY_RIGHT:
2514 case EL_BD_BUTTERFLY_UP:
2515 case EL_BD_BUTTERFLY_LEFT:
2516 case EL_BD_BUTTERFLY_DOWN:
2517 Feld[x][y] = EL_BD_BUTTERFLY;
2518 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2521 case EL_BD_FIREFLY_RIGHT:
2522 case EL_BD_FIREFLY_UP:
2523 case EL_BD_FIREFLY_LEFT:
2524 case EL_BD_FIREFLY_DOWN:
2525 Feld[x][y] = EL_BD_FIREFLY;
2526 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2529 case EL_PACMAN_RIGHT:
2531 case EL_PACMAN_LEFT:
2532 case EL_PACMAN_DOWN:
2533 Feld[x][y] = EL_PACMAN;
2534 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2537 case EL_YAMYAM_LEFT:
2538 case EL_YAMYAM_RIGHT:
2540 case EL_YAMYAM_DOWN:
2541 Feld[x][y] = EL_YAMYAM;
2542 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2545 case EL_SP_SNIKSNAK:
2546 MovDir[x][y] = MV_UP;
2549 case EL_SP_ELECTRON:
2550 MovDir[x][y] = MV_LEFT;
2557 Feld[x][y] = EL_MOLE;
2558 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2562 if (IS_CUSTOM_ELEMENT(element))
2564 struct ElementInfo *ei = &element_info[element];
2565 int move_direction_initial = ei->move_direction_initial;
2566 int move_pattern = ei->move_pattern;
2568 if (move_direction_initial == MV_START_PREVIOUS)
2570 if (MovDir[x][y] != MV_NONE)
2573 move_direction_initial = MV_START_AUTOMATIC;
2576 if (move_direction_initial == MV_START_RANDOM)
2577 MovDir[x][y] = 1 << RND(4);
2578 else if (move_direction_initial & MV_ANY_DIRECTION)
2579 MovDir[x][y] = move_direction_initial;
2580 else if (move_pattern == MV_ALL_DIRECTIONS ||
2581 move_pattern == MV_TURNING_LEFT ||
2582 move_pattern == MV_TURNING_RIGHT ||
2583 move_pattern == MV_TURNING_LEFT_RIGHT ||
2584 move_pattern == MV_TURNING_RIGHT_LEFT ||
2585 move_pattern == MV_TURNING_RANDOM)
2586 MovDir[x][y] = 1 << RND(4);
2587 else if (move_pattern == MV_HORIZONTAL)
2588 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2589 else if (move_pattern == MV_VERTICAL)
2590 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2591 else if (move_pattern & MV_ANY_DIRECTION)
2592 MovDir[x][y] = element_info[element].move_pattern;
2593 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2594 move_pattern == MV_ALONG_RIGHT_SIDE)
2596 /* use random direction as default start direction */
2597 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2598 MovDir[x][y] = 1 << RND(4);
2600 for (i = 0; i < NUM_DIRECTIONS; i++)
2602 int x1 = x + xy[i][0];
2603 int y1 = y + xy[i][1];
2605 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2607 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2608 MovDir[x][y] = direction[0][i];
2610 MovDir[x][y] = direction[1][i];
2619 MovDir[x][y] = 1 << RND(4);
2621 if (element != EL_BUG &&
2622 element != EL_SPACESHIP &&
2623 element != EL_BD_BUTTERFLY &&
2624 element != EL_BD_FIREFLY)
2627 for (i = 0; i < NUM_DIRECTIONS; i++)
2629 int x1 = x + xy[i][0];
2630 int y1 = y + xy[i][1];
2632 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2634 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2636 MovDir[x][y] = direction[0][i];
2639 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2640 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2642 MovDir[x][y] = direction[1][i];
2651 GfxDir[x][y] = MovDir[x][y];
2654 void InitAmoebaNr(int x, int y)
2657 int group_nr = AmoebeNachbarNr(x, y);
2661 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2663 if (AmoebaCnt[i] == 0)
2671 AmoebaNr[x][y] = group_nr;
2672 AmoebaCnt[group_nr]++;
2673 AmoebaCnt2[group_nr]++;
2676 static void PlayerWins(struct PlayerInfo *player)
2678 player->LevelSolved = TRUE;
2679 player->GameOver = TRUE;
2681 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2682 level.native_em_level->lev->score : player->score);
2687 static int time, time_final;
2688 static int score, score_final;
2689 static int game_over_delay = 0;
2690 int game_over_delay_value = 50;
2692 if (!local_player->LevelSolved_GameEnd)
2696 /* do not start end game actions before the player stops moving (to exit) */
2697 if (local_player->MovPos)
2700 local_player->LevelSolved_GameEnd = TRUE;
2701 local_player->LevelSolved_SaveTape = tape.recording;
2702 local_player->LevelSolved_SaveScore = !tape.playing;
2704 if (tape.auto_play) /* tape might already be stopped here */
2705 tape.auto_play_level_solved = TRUE;
2711 game_over_delay = game_over_delay_value;
2713 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
2714 score = score_final = local_player->score_final;
2719 score_final += TimeLeft * level.score[SC_TIME_BONUS];
2721 else if (level.time == 0 && TimePlayed < 999)
2724 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
2727 local_player->score_final = score_final;
2729 if (level_editor_test_game)
2732 score = score_final;
2734 DrawGameValue_Time(time);
2735 DrawGameValue_Score(score);
2738 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
2740 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
2742 /* close exit door after last player */
2743 if (AllPlayersGone &&
2744 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2745 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2747 int element = Feld[ExitX][ExitY];
2749 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2750 EL_SP_EXIT_CLOSING);
2752 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2755 /* player disappears */
2756 DrawLevelField(ExitX, ExitY);
2759 for (i = 0; i < MAX_PLAYERS; i++)
2761 struct PlayerInfo *player = &stored_player[i];
2763 if (player->present)
2765 RemovePlayer(player);
2767 /* player disappears */
2768 DrawLevelField(player->jx, player->jy);
2773 PlaySound(SND_GAME_WINNING);
2776 if (game_over_delay > 0)
2783 if (time != time_final)
2785 int time_to_go = ABS(time_final - time);
2786 int time_count_dir = (time < time_final ? +1 : -1);
2787 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
2789 time += time_count_steps * time_count_dir;
2790 score += time_count_steps * level.score[SC_TIME_BONUS];
2792 DrawGameValue_Time(time);
2793 DrawGameValue_Score(score);
2795 if (time == time_final)
2796 StopSound(SND_GAME_LEVELTIME_BONUS);
2797 else if (setup.sound_loops)
2798 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
2800 PlaySound(SND_GAME_LEVELTIME_BONUS);
2807 boolean raise_level = FALSE;
2809 CloseDoor(DOOR_CLOSE_1);
2811 if (local_player->LevelSolved_SaveTape)
2818 SaveTapeChecked(tape.level_nr); /* ask to save tape */
2820 SaveTape(tape.level_nr); /* ask to save tape */
2824 if (level_editor_test_game)
2826 game_status = GAME_MODE_MAIN;
2833 if (!local_player->LevelSolved_SaveScore)
2835 FadeOut(REDRAW_FIELD);
2837 game_status = GAME_MODE_MAIN;
2839 DrawAndFadeInMainMenu(REDRAW_FIELD);
2844 if (level_nr == leveldir_current->handicap_level)
2846 leveldir_current->handicap_level++;
2847 SaveLevelSetup_SeriesInfo();
2850 if (level_nr < leveldir_current->last_level)
2851 raise_level = TRUE; /* advance to next level */
2853 if ((hi_pos = NewHiScore()) >= 0)
2855 game_status = GAME_MODE_SCORES;
2857 DrawHallOfFame(hi_pos);
2867 FadeOut(REDRAW_FIELD);
2869 game_status = GAME_MODE_MAIN;
2877 DrawAndFadeInMainMenu(REDRAW_FIELD);
2886 LoadScore(level_nr);
2888 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2889 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
2892 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2894 if (local_player->score_final > highscore[k].Score)
2896 /* player has made it to the hall of fame */
2898 if (k < MAX_SCORE_ENTRIES - 1)
2900 int m = MAX_SCORE_ENTRIES - 1;
2903 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2904 if (strEqual(setup.player_name, highscore[l].Name))
2906 if (m == k) /* player's new highscore overwrites his old one */
2910 for (l = m; l > k; l--)
2912 strcpy(highscore[l].Name, highscore[l - 1].Name);
2913 highscore[l].Score = highscore[l - 1].Score;
2920 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2921 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2922 highscore[k].Score = local_player->score_final;
2928 else if (!strncmp(setup.player_name, highscore[k].Name,
2929 MAX_PLAYER_NAME_LEN))
2930 break; /* player already there with a higher score */
2936 SaveScore(level_nr);
2941 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
2943 int element = Feld[x][y];
2944 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2945 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2946 int horiz_move = (dx != 0);
2947 int sign = (horiz_move ? dx : dy);
2948 int step = sign * element_info[element].move_stepsize;
2950 /* special values for move stepsize for spring and things on conveyor belt */
2953 if (CAN_FALL(element) &&
2954 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2955 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2956 else if (element == EL_SPRING)
2957 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2963 inline static int getElementMoveStepsize(int x, int y)
2965 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
2968 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2970 if (player->GfxAction != action || player->GfxDir != dir)
2973 printf("Player frame reset! (%d => %d, %d => %d)\n",
2974 player->GfxAction, action, player->GfxDir, dir);
2977 player->GfxAction = action;
2978 player->GfxDir = dir;
2980 player->StepFrame = 0;
2984 #if USE_GFX_RESET_GFX_ANIMATION
2985 static void ResetGfxFrame(int x, int y, boolean redraw)
2987 int element = Feld[x][y];
2988 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2989 int last_gfx_frame = GfxFrame[x][y];
2991 if (graphic_info[graphic].anim_global_sync)
2992 GfxFrame[x][y] = FrameCounter;
2993 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2994 GfxFrame[x][y] = CustomValue[x][y];
2995 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2996 GfxFrame[x][y] = element_info[element].collect_score;
2997 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
2998 GfxFrame[x][y] = ChangeDelay[x][y];
3000 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3001 DrawLevelGraphicAnimation(x, y, graphic);
3005 static void ResetGfxAnimation(int x, int y)
3007 GfxAction[x][y] = ACTION_DEFAULT;
3008 GfxDir[x][y] = MovDir[x][y];
3011 #if USE_GFX_RESET_GFX_ANIMATION
3012 ResetGfxFrame(x, y, FALSE);
3016 static void ResetRandomAnimationValue(int x, int y)
3018 GfxRandom[x][y] = INIT_GFX_RANDOM();
3021 void InitMovingField(int x, int y, int direction)
3023 int element = Feld[x][y];
3024 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3025 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3028 boolean is_moving_before, is_moving_after;
3030 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3033 /* check if element was/is moving or being moved before/after mode change */
3035 is_moving_before = WasJustMoving[x][y];
3037 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3039 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3041 /* reset animation only for moving elements which change direction of moving
3042 or which just started or stopped moving
3043 (else CEs with property "can move" / "not moving" are reset each frame) */
3044 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3046 if (is_moving_before != is_moving_after ||
3047 direction != MovDir[x][y])
3048 ResetGfxAnimation(x, y);
3050 if ((is_moving_before || is_moving_after) && !continues_moving)
3051 ResetGfxAnimation(x, y);
3054 if (!continues_moving)
3055 ResetGfxAnimation(x, y);
3058 MovDir[x][y] = direction;
3059 GfxDir[x][y] = direction;
3061 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3062 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3063 direction == MV_DOWN && CAN_FALL(element) ?
3064 ACTION_FALLING : ACTION_MOVING);
3066 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3067 ACTION_FALLING : ACTION_MOVING);
3070 /* this is needed for CEs with property "can move" / "not moving" */
3072 if (is_moving_after)
3074 if (Feld[newx][newy] == EL_EMPTY)
3075 Feld[newx][newy] = EL_BLOCKED;
3077 MovDir[newx][newy] = MovDir[x][y];
3079 #if USE_NEW_CUSTOM_VALUE
3080 CustomValue[newx][newy] = CustomValue[x][y];
3083 GfxFrame[newx][newy] = GfxFrame[x][y];
3084 GfxRandom[newx][newy] = GfxRandom[x][y];
3085 GfxAction[newx][newy] = GfxAction[x][y];
3086 GfxDir[newx][newy] = GfxDir[x][y];
3090 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3092 int direction = MovDir[x][y];
3093 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3094 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3100 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3102 int oldx = x, oldy = y;
3103 int direction = MovDir[x][y];
3105 if (direction == MV_LEFT)
3107 else if (direction == MV_RIGHT)
3109 else if (direction == MV_UP)
3111 else if (direction == MV_DOWN)
3114 *comes_from_x = oldx;
3115 *comes_from_y = oldy;
3118 int MovingOrBlocked2Element(int x, int y)
3120 int element = Feld[x][y];
3122 if (element == EL_BLOCKED)
3126 Blocked2Moving(x, y, &oldx, &oldy);
3127 return Feld[oldx][oldy];
3133 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3135 /* like MovingOrBlocked2Element(), but if element is moving
3136 and (x,y) is the field the moving element is just leaving,
3137 return EL_BLOCKED instead of the element value */
3138 int element = Feld[x][y];
3140 if (IS_MOVING(x, y))
3142 if (element == EL_BLOCKED)
3146 Blocked2Moving(x, y, &oldx, &oldy);
3147 return Feld[oldx][oldy];
3156 static void RemoveField(int x, int y)
3158 Feld[x][y] = EL_EMPTY;
3164 #if USE_NEW_CUSTOM_VALUE
3165 CustomValue[x][y] = 0;
3169 ChangeDelay[x][y] = 0;
3170 ChangePage[x][y] = -1;
3171 Pushed[x][y] = FALSE;
3174 ExplodeField[x][y] = EX_TYPE_NONE;
3177 GfxElement[x][y] = EL_UNDEFINED;
3178 GfxAction[x][y] = ACTION_DEFAULT;
3179 GfxDir[x][y] = MV_NONE;
3182 void RemoveMovingField(int x, int y)
3184 int oldx = x, oldy = y, newx = x, newy = y;
3185 int element = Feld[x][y];
3186 int next_element = EL_UNDEFINED;
3188 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3191 if (IS_MOVING(x, y))
3193 Moving2Blocked(x, y, &newx, &newy);
3195 if (Feld[newx][newy] != EL_BLOCKED)
3197 /* element is moving, but target field is not free (blocked), but
3198 already occupied by something different (example: acid pool);
3199 in this case, only remove the moving field, but not the target */
3201 RemoveField(oldx, oldy);
3203 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3205 DrawLevelField(oldx, oldy);
3210 else if (element == EL_BLOCKED)
3212 Blocked2Moving(x, y, &oldx, &oldy);
3213 if (!IS_MOVING(oldx, oldy))
3217 if (element == EL_BLOCKED &&
3218 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3219 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3220 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3221 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3222 next_element = get_next_element(Feld[oldx][oldy]);
3224 RemoveField(oldx, oldy);
3225 RemoveField(newx, newy);
3227 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3229 if (next_element != EL_UNDEFINED)
3230 Feld[oldx][oldy] = next_element;
3232 DrawLevelField(oldx, oldy);
3233 DrawLevelField(newx, newy);
3236 void DrawDynamite(int x, int y)
3238 int sx = SCREENX(x), sy = SCREENY(y);
3239 int graphic = el2img(Feld[x][y]);
3242 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3245 if (IS_WALKABLE_INSIDE(Back[x][y]))
3249 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3250 else if (Store[x][y])
3251 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3253 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3255 if (Back[x][y] || Store[x][y])
3256 DrawGraphicThruMask(sx, sy, graphic, frame);
3258 DrawGraphic(sx, sy, graphic, frame);
3261 void CheckDynamite(int x, int y)
3263 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3267 if (MovDelay[x][y] != 0)
3270 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3276 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3281 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3283 boolean num_checked_players = 0;
3286 for (i = 0; i < MAX_PLAYERS; i++)
3288 if (stored_player[i].active)
3290 int sx = stored_player[i].jx;
3291 int sy = stored_player[i].jy;
3293 if (num_checked_players == 0)
3300 *sx1 = MIN(*sx1, sx);
3301 *sy1 = MIN(*sy1, sy);
3302 *sx2 = MAX(*sx2, sx);
3303 *sy2 = MAX(*sy2, sy);
3306 num_checked_players++;
3311 static boolean checkIfAllPlayersFitToScreen_RND()
3313 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3315 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3317 return (sx2 - sx1 < SCR_FIELDX &&
3318 sy2 - sy1 < SCR_FIELDY);
3321 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3323 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3325 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3327 *sx = (sx1 + sx2) / 2;
3328 *sy = (sy1 + sy2) / 2;
3331 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3332 boolean quick_relocation)
3334 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3335 boolean no_delay = (tape.warp_forward);
3336 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3337 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3339 if (quick_relocation)
3341 int offset = (setup.scroll_delay ? 3 : 0);
3343 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3345 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3346 x > SBX_Right + MIDPOSX ? SBX_Right :
3349 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3350 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3355 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3356 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3357 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3359 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3360 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3361 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3363 /* don't scroll over playfield boundaries */
3364 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3365 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3367 /* don't scroll over playfield boundaries */
3368 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3369 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3372 RedrawPlayfield(TRUE, 0,0,0,0);
3376 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3377 x > SBX_Right + MIDPOSX ? SBX_Right :
3380 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3381 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3384 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3386 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3389 int fx = FX, fy = FY;
3391 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3392 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3394 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3400 fx += dx * TILEX / 2;
3401 fy += dy * TILEY / 2;
3403 ScrollLevel(dx, dy);
3406 /* scroll in two steps of half tile size to make things smoother */
3407 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3409 Delay(wait_delay_value);
3411 /* scroll second step to align at full tile size */
3413 Delay(wait_delay_value);
3418 Delay(wait_delay_value);
3422 void RelocatePlayer(int jx, int jy, int el_player_raw)
3424 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3425 int player_nr = GET_PLAYER_NR(el_player);
3426 struct PlayerInfo *player = &stored_player[player_nr];
3427 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3428 boolean no_delay = (tape.warp_forward);
3429 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3430 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3431 int old_jx = player->jx;
3432 int old_jy = player->jy;
3433 int old_element = Feld[old_jx][old_jy];
3434 int element = Feld[jx][jy];
3435 boolean player_relocated = (old_jx != jx || old_jy != jy);
3437 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3438 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3439 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3440 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3441 int leave_side_horiz = move_dir_horiz;
3442 int leave_side_vert = move_dir_vert;
3443 int enter_side = enter_side_horiz | enter_side_vert;
3444 int leave_side = leave_side_horiz | leave_side_vert;
3446 if (player->GameOver) /* do not reanimate dead player */
3449 if (!player_relocated) /* no need to relocate the player */
3452 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3454 RemoveField(jx, jy); /* temporarily remove newly placed player */
3455 DrawLevelField(jx, jy);
3458 if (player->present)
3460 while (player->MovPos)
3462 ScrollPlayer(player, SCROLL_GO_ON);
3463 ScrollScreen(NULL, SCROLL_GO_ON);
3465 AdvanceFrameAndPlayerCounters(player->index_nr);
3470 Delay(wait_delay_value);
3473 DrawPlayer(player); /* needed here only to cleanup last field */
3474 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3476 player->is_moving = FALSE;
3479 if (IS_CUSTOM_ELEMENT(old_element))
3480 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3482 player->index_bit, leave_side);
3484 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3486 player->index_bit, leave_side);
3488 Feld[jx][jy] = el_player;
3489 InitPlayerField(jx, jy, el_player, TRUE);
3491 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3493 Feld[jx][jy] = element;
3494 InitField(jx, jy, FALSE);
3497 /* only visually relocate centered player */
3498 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3499 level.instant_relocation);
3501 TestIfPlayerTouchesBadThing(jx, jy);
3502 TestIfPlayerTouchesCustomElement(jx, jy);
3504 if (IS_CUSTOM_ELEMENT(element))
3505 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3506 player->index_bit, enter_side);
3508 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3509 player->index_bit, enter_side);
3512 void Explode(int ex, int ey, int phase, int mode)
3518 /* !!! eliminate this variable !!! */
3519 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3521 if (game.explosions_delayed)
3523 ExplodeField[ex][ey] = mode;
3527 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3529 int center_element = Feld[ex][ey];
3530 int artwork_element, explosion_element; /* set these values later */
3533 /* --- This is only really needed (and now handled) in "Impact()". --- */
3534 /* do not explode moving elements that left the explode field in time */
3535 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3536 center_element == EL_EMPTY &&
3537 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3542 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3543 if (mode == EX_TYPE_NORMAL ||
3544 mode == EX_TYPE_CENTER ||
3545 mode == EX_TYPE_CROSS)
3546 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3549 /* remove things displayed in background while burning dynamite */
3550 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3553 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3555 /* put moving element to center field (and let it explode there) */
3556 center_element = MovingOrBlocked2Element(ex, ey);
3557 RemoveMovingField(ex, ey);
3558 Feld[ex][ey] = center_element;
3561 /* now "center_element" is finally determined -- set related values now */
3562 artwork_element = center_element; /* for custom player artwork */
3563 explosion_element = center_element; /* for custom player artwork */
3565 if (IS_PLAYER(ex, ey))
3567 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3569 artwork_element = stored_player[player_nr].artwork_element;
3571 if (level.use_explosion_element[player_nr])
3573 explosion_element = level.explosion_element[player_nr];
3574 artwork_element = explosion_element;
3579 if (mode == EX_TYPE_NORMAL ||
3580 mode == EX_TYPE_CENTER ||
3581 mode == EX_TYPE_CROSS)
3582 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3585 last_phase = element_info[explosion_element].explosion_delay + 1;
3587 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3589 int xx = x - ex + 1;
3590 int yy = y - ey + 1;
3593 if (!IN_LEV_FIELD(x, y) ||
3594 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3595 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3598 element = Feld[x][y];
3600 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3602 element = MovingOrBlocked2Element(x, y);
3604 if (!IS_EXPLOSION_PROOF(element))
3605 RemoveMovingField(x, y);
3608 /* indestructible elements can only explode in center (but not flames) */
3609 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3610 mode == EX_TYPE_BORDER)) ||
3611 element == EL_FLAMES)
3614 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3615 behaviour, for example when touching a yamyam that explodes to rocks
3616 with active deadly shield, a rock is created under the player !!! */
3617 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3619 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3620 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3621 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3623 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3626 if (IS_ACTIVE_BOMB(element))
3628 /* re-activate things under the bomb like gate or penguin */
3629 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3636 /* save walkable background elements while explosion on same tile */
3637 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3638 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3639 Back[x][y] = element;
3641 /* ignite explodable elements reached by other explosion */
3642 if (element == EL_EXPLOSION)
3643 element = Store2[x][y];
3645 if (AmoebaNr[x][y] &&
3646 (element == EL_AMOEBA_FULL ||
3647 element == EL_BD_AMOEBA ||
3648 element == EL_AMOEBA_GROWING))
3650 AmoebaCnt[AmoebaNr[x][y]]--;
3651 AmoebaCnt2[AmoebaNr[x][y]]--;
3656 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3658 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3660 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3662 if (PLAYERINFO(ex, ey)->use_murphy)
3663 Store[x][y] = EL_EMPTY;
3666 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3667 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3668 else if (ELEM_IS_PLAYER(center_element))
3669 Store[x][y] = EL_EMPTY;
3670 else if (center_element == EL_YAMYAM)
3671 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3672 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3673 Store[x][y] = element_info[center_element].content.e[xx][yy];
3675 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3676 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3677 otherwise) -- FIX THIS !!! */
3678 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3679 Store[x][y] = element_info[element].content.e[1][1];
3681 else if (!CAN_EXPLODE(element))
3682 Store[x][y] = element_info[element].content.e[1][1];
3685 Store[x][y] = EL_EMPTY;
3687 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3688 center_element == EL_AMOEBA_TO_DIAMOND)
3689 Store2[x][y] = element;
3691 Feld[x][y] = EL_EXPLOSION;
3692 GfxElement[x][y] = artwork_element;
3694 ExplodePhase[x][y] = 1;
3695 ExplodeDelay[x][y] = last_phase;
3700 if (center_element == EL_YAMYAM)
3701 game.yamyam_content_nr =
3702 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3714 GfxFrame[x][y] = 0; /* restart explosion animation */
3716 last_phase = ExplodeDelay[x][y];
3718 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3722 /* activate this even in non-DEBUG version until cause for crash in
3723 getGraphicAnimationFrame() (see below) is found and eliminated */
3729 /* this can happen if the player leaves an explosion just in time */
3730 if (GfxElement[x][y] == EL_UNDEFINED)
3731 GfxElement[x][y] = EL_EMPTY;
3733 if (GfxElement[x][y] == EL_UNDEFINED)
3736 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3737 printf("Explode(): This should never happen!\n");
3740 GfxElement[x][y] = EL_EMPTY;
3746 border_element = Store2[x][y];
3747 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3748 border_element = StorePlayer[x][y];
3750 if (phase == element_info[border_element].ignition_delay ||
3751 phase == last_phase)
3753 boolean border_explosion = FALSE;
3755 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3756 !PLAYER_EXPLOSION_PROTECTED(x, y))
3758 KillPlayerUnlessExplosionProtected(x, y);
3759 border_explosion = TRUE;
3761 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3763 Feld[x][y] = Store2[x][y];
3766 border_explosion = TRUE;
3768 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3770 AmoebeUmwandeln(x, y);
3772 border_explosion = TRUE;
3775 /* if an element just explodes due to another explosion (chain-reaction),
3776 do not immediately end the new explosion when it was the last frame of
3777 the explosion (as it would be done in the following "if"-statement!) */
3778 if (border_explosion && phase == last_phase)
3782 if (phase == last_phase)
3786 element = Feld[x][y] = Store[x][y];
3787 Store[x][y] = Store2[x][y] = 0;
3788 GfxElement[x][y] = EL_UNDEFINED;
3790 /* player can escape from explosions and might therefore be still alive */
3791 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3792 element <= EL_PLAYER_IS_EXPLODING_4)
3794 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3795 int explosion_element = EL_PLAYER_1 + player_nr;
3796 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3797 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3799 if (level.use_explosion_element[player_nr])
3800 explosion_element = level.explosion_element[player_nr];
3802 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3803 element_info[explosion_element].content.e[xx][yy]);
3806 /* restore probably existing indestructible background element */
3807 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3808 element = Feld[x][y] = Back[x][y];
3811 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3812 GfxDir[x][y] = MV_NONE;
3813 ChangeDelay[x][y] = 0;
3814 ChangePage[x][y] = -1;
3816 #if USE_NEW_CUSTOM_VALUE
3817 CustomValue[x][y] = 0;
3820 InitField_WithBug2(x, y, FALSE);
3822 DrawLevelField(x, y);
3824 TestIfElementTouchesCustomElement(x, y);
3826 if (GFX_CRUMBLED(element))
3827 DrawLevelFieldCrumbledSandNeighbours(x, y);
3829 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3830 StorePlayer[x][y] = 0;
3832 if (ELEM_IS_PLAYER(element))
3833 RelocatePlayer(x, y, element);
3835 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3837 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3838 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3841 DrawLevelFieldCrumbledSand(x, y);
3843 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3845 DrawLevelElement(x, y, Back[x][y]);
3846 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3848 else if (IS_WALKABLE_UNDER(Back[x][y]))
3850 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3851 DrawLevelElementThruMask(x, y, Back[x][y]);
3853 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3854 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3858 void DynaExplode(int ex, int ey)
3861 int dynabomb_element = Feld[ex][ey];
3862 int dynabomb_size = 1;
3863 boolean dynabomb_xl = FALSE;
3864 struct PlayerInfo *player;
3865 static int xy[4][2] =
3873 if (IS_ACTIVE_BOMB(dynabomb_element))
3875 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3876 dynabomb_size = player->dynabomb_size;
3877 dynabomb_xl = player->dynabomb_xl;
3878 player->dynabombs_left++;
3881 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3883 for (i = 0; i < NUM_DIRECTIONS; i++)
3885 for (j = 1; j <= dynabomb_size; j++)
3887 int x = ex + j * xy[i][0];
3888 int y = ey + j * xy[i][1];
3891 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3894 element = Feld[x][y];
3896 /* do not restart explosions of fields with active bombs */
3897 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3900 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3902 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3903 !IS_DIGGABLE(element) && !dynabomb_xl)
3909 void Bang(int x, int y)
3911 int element = MovingOrBlocked2Element(x, y);
3912 int explosion_type = EX_TYPE_NORMAL;
3914 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3916 struct PlayerInfo *player = PLAYERINFO(x, y);
3918 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3919 player->element_nr);
3921 if (level.use_explosion_element[player->index_nr])
3923 int explosion_element = level.explosion_element[player->index_nr];
3925 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3926 explosion_type = EX_TYPE_CROSS;
3927 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3928 explosion_type = EX_TYPE_CENTER;
3936 case EL_BD_BUTTERFLY:
3939 case EL_DARK_YAMYAM:
3943 RaiseScoreElement(element);
3946 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3947 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3948 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3949 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3950 case EL_DYNABOMB_INCREASE_NUMBER:
3951 case EL_DYNABOMB_INCREASE_SIZE:
3952 case EL_DYNABOMB_INCREASE_POWER:
3953 explosion_type = EX_TYPE_DYNA;
3958 case EL_LAMP_ACTIVE:
3959 case EL_AMOEBA_TO_DIAMOND:
3960 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3961 explosion_type = EX_TYPE_CENTER;
3965 if (element_info[element].explosion_type == EXPLODES_CROSS)
3966 explosion_type = EX_TYPE_CROSS;
3967 else if (element_info[element].explosion_type == EXPLODES_1X1)
3968 explosion_type = EX_TYPE_CENTER;
3972 if (explosion_type == EX_TYPE_DYNA)
3975 Explode(x, y, EX_PHASE_START, explosion_type);
3977 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3980 void SplashAcid(int x, int y)
3982 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3983 (!IN_LEV_FIELD(x - 1, y - 2) ||
3984 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3985 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3987 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3988 (!IN_LEV_FIELD(x + 1, y - 2) ||
3989 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3990 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3992 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3995 static void InitBeltMovement()
3997 static int belt_base_element[4] =
3999 EL_CONVEYOR_BELT_1_LEFT,
4000 EL_CONVEYOR_BELT_2_LEFT,
4001 EL_CONVEYOR_BELT_3_LEFT,
4002 EL_CONVEYOR_BELT_4_LEFT
4004 static int belt_base_active_element[4] =
4006 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4007 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4008 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4009 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4014 /* set frame order for belt animation graphic according to belt direction */
4015 for (i = 0; i < NUM_BELTS; i++)
4019 for (j = 0; j < NUM_BELT_PARTS; j++)
4021 int element = belt_base_active_element[belt_nr] + j;
4022 int graphic = el2img(element);
4024 if (game.belt_dir[i] == MV_LEFT)
4025 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4027 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4031 SCAN_PLAYFIELD(x, y)
4033 int element = Feld[x][y];
4035 for (i = 0; i < NUM_BELTS; i++)
4037 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4039 int e_belt_nr = getBeltNrFromBeltElement(element);
4042 if (e_belt_nr == belt_nr)
4044 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4046 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4053 static void ToggleBeltSwitch(int x, int y)
4055 static int belt_base_element[4] =
4057 EL_CONVEYOR_BELT_1_LEFT,
4058 EL_CONVEYOR_BELT_2_LEFT,
4059 EL_CONVEYOR_BELT_3_LEFT,
4060 EL_CONVEYOR_BELT_4_LEFT
4062 static int belt_base_active_element[4] =
4064 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4065 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4066 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4067 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4069 static int belt_base_switch_element[4] =
4071 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4072 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4073 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4074 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4076 static int belt_move_dir[4] =
4084 int element = Feld[x][y];
4085 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4086 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4087 int belt_dir = belt_move_dir[belt_dir_nr];
4090 if (!IS_BELT_SWITCH(element))
4093 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4094 game.belt_dir[belt_nr] = belt_dir;
4096 if (belt_dir_nr == 3)
4099 /* set frame order for belt animation graphic according to belt direction */
4100 for (i = 0; i < NUM_BELT_PARTS; i++)
4102 int element = belt_base_active_element[belt_nr] + i;
4103 int graphic = el2img(element);
4105 if (belt_dir == MV_LEFT)
4106 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4108 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4111 SCAN_PLAYFIELD(xx, yy)
4113 int element = Feld[xx][yy];
4115 if (IS_BELT_SWITCH(element))
4117 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4119 if (e_belt_nr == belt_nr)
4121 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4122 DrawLevelField(xx, yy);
4125 else if (IS_BELT(element) && belt_dir != MV_NONE)
4127 int e_belt_nr = getBeltNrFromBeltElement(element);
4129 if (e_belt_nr == belt_nr)
4131 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4133 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4134 DrawLevelField(xx, yy);
4137 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4139 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4141 if (e_belt_nr == belt_nr)
4143 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4145 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4146 DrawLevelField(xx, yy);
4152 static void ToggleSwitchgateSwitch(int x, int y)
4156 game.switchgate_pos = !game.switchgate_pos;
4158 SCAN_PLAYFIELD(xx, yy)
4160 int element = Feld[xx][yy];
4162 #if !USE_BOTH_SWITCHGATE_SWITCHES
4163 if (element == EL_SWITCHGATE_SWITCH_UP ||
4164 element == EL_SWITCHGATE_SWITCH_DOWN)
4166 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4167 DrawLevelField(xx, yy);
4170 if (element == EL_SWITCHGATE_SWITCH_UP)
4172 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4173 DrawLevelField(xx, yy);
4175 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4177 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4178 DrawLevelField(xx, yy);
4181 else if (element == EL_SWITCHGATE_OPEN ||
4182 element == EL_SWITCHGATE_OPENING)
4184 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4186 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4188 else if (element == EL_SWITCHGATE_CLOSED ||
4189 element == EL_SWITCHGATE_CLOSING)
4191 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4193 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4198 static int getInvisibleActiveFromInvisibleElement(int element)
4200 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4201 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4202 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4206 static int getInvisibleFromInvisibleActiveElement(int element)
4208 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4209 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4210 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4214 static void RedrawAllLightSwitchesAndInvisibleElements()
4218 SCAN_PLAYFIELD(x, y)
4220 int element = Feld[x][y];
4222 if (element == EL_LIGHT_SWITCH &&
4223 game.light_time_left > 0)
4225 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4226 DrawLevelField(x, y);
4228 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4229 game.light_time_left == 0)
4231 Feld[x][y] = EL_LIGHT_SWITCH;
4232 DrawLevelField(x, y);
4234 else if (element == EL_EMC_DRIPPER &&
4235 game.light_time_left > 0)
4237 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4238 DrawLevelField(x, y);
4240 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4241 game.light_time_left == 0)
4243 Feld[x][y] = EL_EMC_DRIPPER;
4244 DrawLevelField(x, y);
4246 else if (element == EL_INVISIBLE_STEELWALL ||
4247 element == EL_INVISIBLE_WALL ||
4248 element == EL_INVISIBLE_SAND)
4250 if (game.light_time_left > 0)
4251 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4253 DrawLevelField(x, y);
4255 /* uncrumble neighbour fields, if needed */
4256 if (element == EL_INVISIBLE_SAND)
4257 DrawLevelFieldCrumbledSandNeighbours(x, y);
4259 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4260 element == EL_INVISIBLE_WALL_ACTIVE ||
4261 element == EL_INVISIBLE_SAND_ACTIVE)
4263 if (game.light_time_left == 0)
4264 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4266 DrawLevelField(x, y);
4268 /* re-crumble neighbour fields, if needed */
4269 if (element == EL_INVISIBLE_SAND)
4270 DrawLevelFieldCrumbledSandNeighbours(x, y);
4275 static void RedrawAllInvisibleElementsForLenses()
4279 SCAN_PLAYFIELD(x, y)
4281 int element = Feld[x][y];
4283 if (element == EL_EMC_DRIPPER &&
4284 game.lenses_time_left > 0)
4286 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4287 DrawLevelField(x, y);
4289 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4290 game.lenses_time_left == 0)
4292 Feld[x][y] = EL_EMC_DRIPPER;
4293 DrawLevelField(x, y);
4295 else if (element == EL_INVISIBLE_STEELWALL ||
4296 element == EL_INVISIBLE_WALL ||
4297 element == EL_INVISIBLE_SAND)
4299 if (game.lenses_time_left > 0)
4300 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4302 DrawLevelField(x, y);
4304 /* uncrumble neighbour fields, if needed */
4305 if (element == EL_INVISIBLE_SAND)
4306 DrawLevelFieldCrumbledSandNeighbours(x, y);
4308 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4309 element == EL_INVISIBLE_WALL_ACTIVE ||
4310 element == EL_INVISIBLE_SAND_ACTIVE)
4312 if (game.lenses_time_left == 0)
4313 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4315 DrawLevelField(x, y);
4317 /* re-crumble neighbour fields, if needed */
4318 if (element == EL_INVISIBLE_SAND)
4319 DrawLevelFieldCrumbledSandNeighbours(x, y);
4324 static void RedrawAllInvisibleElementsForMagnifier()
4328 SCAN_PLAYFIELD(x, y)
4330 int element = Feld[x][y];
4332 if (element == EL_EMC_FAKE_GRASS &&
4333 game.magnify_time_left > 0)
4335 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4336 DrawLevelField(x, y);
4338 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4339 game.magnify_time_left == 0)
4341 Feld[x][y] = EL_EMC_FAKE_GRASS;
4342 DrawLevelField(x, y);
4344 else if (IS_GATE_GRAY(element) &&
4345 game.magnify_time_left > 0)
4347 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4348 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4349 IS_EM_GATE_GRAY(element) ?
4350 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4351 IS_EMC_GATE_GRAY(element) ?
4352 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4354 DrawLevelField(x, y);
4356 else if (IS_GATE_GRAY_ACTIVE(element) &&
4357 game.magnify_time_left == 0)
4359 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4360 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4361 IS_EM_GATE_GRAY_ACTIVE(element) ?
4362 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4363 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4364 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4366 DrawLevelField(x, y);
4371 static void ToggleLightSwitch(int x, int y)
4373 int element = Feld[x][y];
4375 game.light_time_left =
4376 (element == EL_LIGHT_SWITCH ?
4377 level.time_light * FRAMES_PER_SECOND : 0);
4379 RedrawAllLightSwitchesAndInvisibleElements();
4382 static void ActivateTimegateSwitch(int x, int y)
4386 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4388 SCAN_PLAYFIELD(xx, yy)
4390 int element = Feld[xx][yy];
4392 if (element == EL_TIMEGATE_CLOSED ||
4393 element == EL_TIMEGATE_CLOSING)
4395 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4396 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4400 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4402 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4403 DrawLevelField(xx, yy);
4409 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4412 void Impact(int x, int y)
4414 boolean last_line = (y == lev_fieldy - 1);
4415 boolean object_hit = FALSE;
4416 boolean impact = (last_line || object_hit);
4417 int element = Feld[x][y];
4418 int smashed = EL_STEELWALL;
4420 if (!last_line) /* check if element below was hit */
4422 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4425 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4426 MovDir[x][y + 1] != MV_DOWN ||
4427 MovPos[x][y + 1] <= TILEY / 2));
4429 /* do not smash moving elements that left the smashed field in time */
4430 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4431 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4434 #if USE_QUICKSAND_IMPACT_BUGFIX
4435 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4437 RemoveMovingField(x, y + 1);
4438 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4439 Feld[x][y + 2] = EL_ROCK;
4440 DrawLevelField(x, y + 2);
4447 smashed = MovingOrBlocked2Element(x, y + 1);
4449 impact = (last_line || object_hit);
4452 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4454 SplashAcid(x, y + 1);
4458 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4459 /* only reset graphic animation if graphic really changes after impact */
4461 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4463 ResetGfxAnimation(x, y);
4464 DrawLevelField(x, y);
4467 if (impact && CAN_EXPLODE_IMPACT(element))
4472 else if (impact && element == EL_PEARL)
4474 ResetGfxAnimation(x, y);
4476 Feld[x][y] = EL_PEARL_BREAKING;
4477 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4480 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4482 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4487 if (impact && element == EL_AMOEBA_DROP)
4489 if (object_hit && IS_PLAYER(x, y + 1))
4490 KillPlayerUnlessEnemyProtected(x, y + 1);
4491 else if (object_hit && smashed == EL_PENGUIN)
4495 Feld[x][y] = EL_AMOEBA_GROWING;
4496 Store[x][y] = EL_AMOEBA_WET;
4498 ResetRandomAnimationValue(x, y);
4503 if (object_hit) /* check which object was hit */
4505 if (CAN_PASS_MAGIC_WALL(element) &&
4506 (smashed == EL_MAGIC_WALL ||
4507 smashed == EL_BD_MAGIC_WALL))
4510 int activated_magic_wall =
4511 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4512 EL_BD_MAGIC_WALL_ACTIVE);
4514 /* activate magic wall / mill */
4515 SCAN_PLAYFIELD(xx, yy)
4516 if (Feld[xx][yy] == smashed)
4517 Feld[xx][yy] = activated_magic_wall;
4519 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4520 game.magic_wall_active = TRUE;
4522 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4523 SND_MAGIC_WALL_ACTIVATING :
4524 SND_BD_MAGIC_WALL_ACTIVATING));
4527 if (IS_PLAYER(x, y + 1))
4529 if (CAN_SMASH_PLAYER(element))
4531 KillPlayerUnlessEnemyProtected(x, y + 1);
4535 else if (smashed == EL_PENGUIN)
4537 if (CAN_SMASH_PLAYER(element))
4543 else if (element == EL_BD_DIAMOND)
4545 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4551 else if (((element == EL_SP_INFOTRON ||
4552 element == EL_SP_ZONK) &&
4553 (smashed == EL_SP_SNIKSNAK ||
4554 smashed == EL_SP_ELECTRON ||
4555 smashed == EL_SP_DISK_ORANGE)) ||
4556 (element == EL_SP_INFOTRON &&
4557 smashed == EL_SP_DISK_YELLOW))
4562 else if (CAN_SMASH_EVERYTHING(element))
4564 if (IS_CLASSIC_ENEMY(smashed) ||
4565 CAN_EXPLODE_SMASHED(smashed))
4570 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4572 if (smashed == EL_LAMP ||
4573 smashed == EL_LAMP_ACTIVE)
4578 else if (smashed == EL_NUT)
4580 Feld[x][y + 1] = EL_NUT_BREAKING;
4581 PlayLevelSound(x, y, SND_NUT_BREAKING);
4582 RaiseScoreElement(EL_NUT);
4585 else if (smashed == EL_PEARL)
4587 ResetGfxAnimation(x, y);
4589 Feld[x][y + 1] = EL_PEARL_BREAKING;
4590 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4593 else if (smashed == EL_DIAMOND)
4595 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4596 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4599 else if (IS_BELT_SWITCH(smashed))
4601 ToggleBeltSwitch(x, y + 1);
4603 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4604 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4606 ToggleSwitchgateSwitch(x, y + 1);
4608 else if (smashed == EL_LIGHT_SWITCH ||
4609 smashed == EL_LIGHT_SWITCH_ACTIVE)
4611 ToggleLightSwitch(x, y + 1);
4616 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4619 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4621 CheckElementChangeBySide(x, y + 1, smashed, element,
4622 CE_SWITCHED, CH_SIDE_TOP);
4623 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4629 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4634 /* play sound of magic wall / mill */
4636 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4637 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4639 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4640 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4641 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4642 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4647 /* play sound of object that hits the ground */
4648 if (last_line || object_hit)
4649 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4652 inline static void TurnRoundExt(int x, int y)
4664 { 0, 0 }, { 0, 0 }, { 0, 0 },
4669 int left, right, back;
4673 { MV_DOWN, MV_UP, MV_RIGHT },
4674 { MV_UP, MV_DOWN, MV_LEFT },
4676 { MV_LEFT, MV_RIGHT, MV_DOWN },
4680 { MV_RIGHT, MV_LEFT, MV_UP }
4683 int element = Feld[x][y];
4684 int move_pattern = element_info[element].move_pattern;
4686 int old_move_dir = MovDir[x][y];
4687 int left_dir = turn[old_move_dir].left;
4688 int right_dir = turn[old_move_dir].right;
4689 int back_dir = turn[old_move_dir].back;
4691 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4692 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4693 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4694 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4696 int left_x = x + left_dx, left_y = y + left_dy;
4697 int right_x = x + right_dx, right_y = y + right_dy;
4698 int move_x = x + move_dx, move_y = y + move_dy;
4702 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4704 TestIfBadThingTouchesOtherBadThing(x, y);
4706 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4707 MovDir[x][y] = right_dir;
4708 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4709 MovDir[x][y] = left_dir;
4711 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4713 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4716 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4718 TestIfBadThingTouchesOtherBadThing(x, y);
4720 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4721 MovDir[x][y] = left_dir;
4722 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4723 MovDir[x][y] = right_dir;
4725 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4727 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4730 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4732 TestIfBadThingTouchesOtherBadThing(x, y);
4734 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4735 MovDir[x][y] = left_dir;
4736 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4737 MovDir[x][y] = right_dir;
4739 if (MovDir[x][y] != old_move_dir)
4742 else if (element == EL_YAMYAM)
4744 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4745 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4747 if (can_turn_left && can_turn_right)
4748 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4749 else if (can_turn_left)
4750 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4751 else if (can_turn_right)
4752 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4754 MovDir[x][y] = back_dir;
4756 MovDelay[x][y] = 16 + 16 * RND(3);
4758 else if (element == EL_DARK_YAMYAM)
4760 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4762 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4765 if (can_turn_left && can_turn_right)
4766 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4767 else if (can_turn_left)
4768 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4769 else if (can_turn_right)
4770 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4772 MovDir[x][y] = back_dir;
4774 MovDelay[x][y] = 16 + 16 * RND(3);
4776 else if (element == EL_PACMAN)
4778 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4779 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4781 if (can_turn_left && can_turn_right)
4782 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4783 else if (can_turn_left)
4784 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4785 else if (can_turn_right)
4786 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4788 MovDir[x][y] = back_dir;
4790 MovDelay[x][y] = 6 + RND(40);
4792 else if (element == EL_PIG)
4794 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4795 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4796 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4797 boolean should_turn_left, should_turn_right, should_move_on;
4799 int rnd = RND(rnd_value);
4801 should_turn_left = (can_turn_left &&
4803 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4804 y + back_dy + left_dy)));
4805 should_turn_right = (can_turn_right &&
4807 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4808 y + back_dy + right_dy)));
4809 should_move_on = (can_move_on &&
4812 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4813 y + move_dy + left_dy) ||
4814 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4815 y + move_dy + right_dy)));
4817 if (should_turn_left || should_turn_right || should_move_on)
4819 if (should_turn_left && should_turn_right && should_move_on)
4820 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4821 rnd < 2 * rnd_value / 3 ? right_dir :
4823 else if (should_turn_left && should_turn_right)
4824 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4825 else if (should_turn_left && should_move_on)
4826 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4827 else if (should_turn_right && should_move_on)
4828 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4829 else if (should_turn_left)
4830 MovDir[x][y] = left_dir;
4831 else if (should_turn_right)
4832 MovDir[x][y] = right_dir;
4833 else if (should_move_on)
4834 MovDir[x][y] = old_move_dir;
4836 else if (can_move_on && rnd > rnd_value / 8)
4837 MovDir[x][y] = old_move_dir;
4838 else if (can_turn_left && can_turn_right)
4839 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4840 else if (can_turn_left && rnd > rnd_value / 8)
4841 MovDir[x][y] = left_dir;
4842 else if (can_turn_right && rnd > rnd_value/8)
4843 MovDir[x][y] = right_dir;
4845 MovDir[x][y] = back_dir;
4847 xx = x + move_xy[MovDir[x][y]].dx;
4848 yy = y + move_xy[MovDir[x][y]].dy;
4850 if (!IN_LEV_FIELD(xx, yy) ||
4851 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4852 MovDir[x][y] = old_move_dir;
4856 else if (element == EL_DRAGON)
4858 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4859 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4860 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4862 int rnd = RND(rnd_value);
4864 if (can_move_on && rnd > rnd_value / 8)
4865 MovDir[x][y] = old_move_dir;
4866 else if (can_turn_left && can_turn_right)
4867 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4868 else if (can_turn_left && rnd > rnd_value / 8)
4869 MovDir[x][y] = left_dir;
4870 else if (can_turn_right && rnd > rnd_value / 8)
4871 MovDir[x][y] = right_dir;
4873 MovDir[x][y] = back_dir;
4875 xx = x + move_xy[MovDir[x][y]].dx;
4876 yy = y + move_xy[MovDir[x][y]].dy;
4878 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4879 MovDir[x][y] = old_move_dir;
4883 else if (element == EL_MOLE)
4885 boolean can_move_on =
4886 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4887 IS_AMOEBOID(Feld[move_x][move_y]) ||
4888 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4891 boolean can_turn_left =
4892 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4893 IS_AMOEBOID(Feld[left_x][left_y])));
4895 boolean can_turn_right =
4896 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4897 IS_AMOEBOID(Feld[right_x][right_y])));
4899 if (can_turn_left && can_turn_right)
4900 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4901 else if (can_turn_left)
4902 MovDir[x][y] = left_dir;
4904 MovDir[x][y] = right_dir;
4907 if (MovDir[x][y] != old_move_dir)
4910 else if (element == EL_BALLOON)
4912 MovDir[x][y] = game.wind_direction;
4915 else if (element == EL_SPRING)
4917 #if USE_NEW_SPRING_BUMPER
4918 if (MovDir[x][y] & MV_HORIZONTAL)
4920 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4921 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4923 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4924 ResetGfxAnimation(move_x, move_y);
4925 DrawLevelField(move_x, move_y);
4927 MovDir[x][y] = back_dir;
4929 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4930 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4931 MovDir[x][y] = MV_NONE;
4934 if (MovDir[x][y] & MV_HORIZONTAL &&
4935 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4936 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4937 MovDir[x][y] = MV_NONE;
4942 else if (element == EL_ROBOT ||
4943 element == EL_SATELLITE ||
4944 element == EL_PENGUIN ||
4945 element == EL_EMC_ANDROID)
4947 int attr_x = -1, attr_y = -1;
4958 for (i = 0; i < MAX_PLAYERS; i++)
4960 struct PlayerInfo *player = &stored_player[i];
4961 int jx = player->jx, jy = player->jy;
4963 if (!player->active)
4967 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4975 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4976 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4977 game.engine_version < VERSION_IDENT(3,1,0,0)))
4983 if (element == EL_PENGUIN)
4986 static int xy[4][2] =
4994 for (i = 0; i < NUM_DIRECTIONS; i++)
4996 int ex = x + xy[i][0];
4997 int ey = y + xy[i][1];
4999 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5008 MovDir[x][y] = MV_NONE;
5010 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5011 else if (attr_x > x)
5012 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5014 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5015 else if (attr_y > y)
5016 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5018 if (element == EL_ROBOT)
5022 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5023 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5024 Moving2Blocked(x, y, &newx, &newy);
5026 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5027 MovDelay[x][y] = 8 + 8 * !RND(3);
5029 MovDelay[x][y] = 16;
5031 else if (element == EL_PENGUIN)
5037 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5039 boolean first_horiz = RND(2);
5040 int new_move_dir = MovDir[x][y];
5043 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5044 Moving2Blocked(x, y, &newx, &newy);
5046 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5050 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5051 Moving2Blocked(x, y, &newx, &newy);
5053 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5056 MovDir[x][y] = old_move_dir;
5060 else if (element == EL_SATELLITE)
5066 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5068 boolean first_horiz = RND(2);
5069 int new_move_dir = MovDir[x][y];
5072 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5073 Moving2Blocked(x, y, &newx, &newy);
5075 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5079 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5080 Moving2Blocked(x, y, &newx, &newy);
5082 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5085 MovDir[x][y] = old_move_dir;
5089 else if (element == EL_EMC_ANDROID)
5091 static int check_pos[16] =
5093 -1, /* 0 => (invalid) */
5094 7, /* 1 => MV_LEFT */
5095 3, /* 2 => MV_RIGHT */
5096 -1, /* 3 => (invalid) */
5098 0, /* 5 => MV_LEFT | MV_UP */
5099 2, /* 6 => MV_RIGHT | MV_UP */
5100 -1, /* 7 => (invalid) */
5101 5, /* 8 => MV_DOWN */
5102 6, /* 9 => MV_LEFT | MV_DOWN */
5103 4, /* 10 => MV_RIGHT | MV_DOWN */
5104 -1, /* 11 => (invalid) */
5105 -1, /* 12 => (invalid) */
5106 -1, /* 13 => (invalid) */
5107 -1, /* 14 => (invalid) */
5108 -1, /* 15 => (invalid) */
5116 { -1, -1, MV_LEFT | MV_UP },
5118 { +1, -1, MV_RIGHT | MV_UP },
5119 { +1, 0, MV_RIGHT },
5120 { +1, +1, MV_RIGHT | MV_DOWN },
5122 { -1, +1, MV_LEFT | MV_DOWN },
5125 int start_pos, check_order;
5126 boolean can_clone = FALSE;
5129 /* check if there is any free field around current position */
5130 for (i = 0; i < 8; i++)
5132 int newx = x + check_xy[i].dx;
5133 int newy = y + check_xy[i].dy;
5135 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5143 if (can_clone) /* randomly find an element to clone */
5147 start_pos = check_pos[RND(8)];
5148 check_order = (RND(2) ? -1 : +1);
5150 for (i = 0; i < 8; i++)
5152 int pos_raw = start_pos + i * check_order;
5153 int pos = (pos_raw + 8) % 8;
5154 int newx = x + check_xy[pos].dx;
5155 int newy = y + check_xy[pos].dy;
5157 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5159 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5160 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5162 Store[x][y] = Feld[newx][newy];
5171 if (can_clone) /* randomly find a direction to move */
5175 start_pos = check_pos[RND(8)];
5176 check_order = (RND(2) ? -1 : +1);
5178 for (i = 0; i < 8; i++)
5180 int pos_raw = start_pos + i * check_order;
5181 int pos = (pos_raw + 8) % 8;
5182 int newx = x + check_xy[pos].dx;
5183 int newy = y + check_xy[pos].dy;
5184 int new_move_dir = check_xy[pos].dir;
5186 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5188 MovDir[x][y] = new_move_dir;
5189 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5198 if (can_clone) /* cloning and moving successful */
5201 /* cannot clone -- try to move towards player */
5203 start_pos = check_pos[MovDir[x][y] & 0x0f];
5204 check_order = (RND(2) ? -1 : +1);
5206 for (i = 0; i < 3; i++)
5208 /* first check start_pos, then previous/next or (next/previous) pos */
5209 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5210 int pos = (pos_raw + 8) % 8;
5211 int newx = x + check_xy[pos].dx;
5212 int newy = y + check_xy[pos].dy;
5213 int new_move_dir = check_xy[pos].dir;
5215 if (IS_PLAYER(newx, newy))
5218 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5220 MovDir[x][y] = new_move_dir;
5221 MovDelay[x][y] = level.android_move_time * 8 + 1;
5228 else if (move_pattern == MV_TURNING_LEFT ||
5229 move_pattern == MV_TURNING_RIGHT ||
5230 move_pattern == MV_TURNING_LEFT_RIGHT ||
5231 move_pattern == MV_TURNING_RIGHT_LEFT ||
5232 move_pattern == MV_TURNING_RANDOM ||
5233 move_pattern == MV_ALL_DIRECTIONS)
5235 boolean can_turn_left =
5236 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5237 boolean can_turn_right =
5238 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5240 if (element_info[element].move_stepsize == 0) /* "not moving" */
5243 if (move_pattern == MV_TURNING_LEFT)
5244 MovDir[x][y] = left_dir;
5245 else if (move_pattern == MV_TURNING_RIGHT)
5246 MovDir[x][y] = right_dir;
5247 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5248 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5249 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5250 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5251 else if (move_pattern == MV_TURNING_RANDOM)
5252 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5253 can_turn_right && !can_turn_left ? right_dir :
5254 RND(2) ? left_dir : right_dir);
5255 else if (can_turn_left && can_turn_right)
5256 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5257 else if (can_turn_left)
5258 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5259 else if (can_turn_right)
5260 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5262 MovDir[x][y] = back_dir;
5264 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5266 else if (move_pattern == MV_HORIZONTAL ||
5267 move_pattern == MV_VERTICAL)
5269 if (move_pattern & old_move_dir)
5270 MovDir[x][y] = back_dir;
5271 else if (move_pattern == MV_HORIZONTAL)
5272 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5273 else if (move_pattern == MV_VERTICAL)
5274 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5276 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5278 else if (move_pattern & MV_ANY_DIRECTION)
5280 MovDir[x][y] = move_pattern;
5281 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5283 else if (move_pattern & MV_WIND_DIRECTION)
5285 MovDir[x][y] = game.wind_direction;
5286 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5288 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5290 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5291 MovDir[x][y] = left_dir;
5292 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5293 MovDir[x][y] = right_dir;
5295 if (MovDir[x][y] != old_move_dir)
5296 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5298 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5300 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5301 MovDir[x][y] = right_dir;
5302 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5303 MovDir[x][y] = left_dir;
5305 if (MovDir[x][y] != old_move_dir)
5306 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5308 else if (move_pattern == MV_TOWARDS_PLAYER ||
5309 move_pattern == MV_AWAY_FROM_PLAYER)
5311 int attr_x = -1, attr_y = -1;
5313 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5324 for (i = 0; i < MAX_PLAYERS; i++)
5326 struct PlayerInfo *player = &stored_player[i];
5327 int jx = player->jx, jy = player->jy;
5329 if (!player->active)
5333 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5341 MovDir[x][y] = MV_NONE;
5343 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5344 else if (attr_x > x)
5345 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5347 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5348 else if (attr_y > y)
5349 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5351 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5353 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5355 boolean first_horiz = RND(2);
5356 int new_move_dir = MovDir[x][y];
5358 if (element_info[element].move_stepsize == 0) /* "not moving" */
5360 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5361 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5367 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5368 Moving2Blocked(x, y, &newx, &newy);
5370 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5374 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5375 Moving2Blocked(x, y, &newx, &newy);
5377 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5380 MovDir[x][y] = old_move_dir;
5383 else if (move_pattern == MV_WHEN_PUSHED ||
5384 move_pattern == MV_WHEN_DROPPED)
5386 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5387 MovDir[x][y] = MV_NONE;
5391 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5393 static int test_xy[7][2] =
5403 static int test_dir[7] =
5413 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5414 int move_preference = -1000000; /* start with very low preference */
5415 int new_move_dir = MV_NONE;
5416 int start_test = RND(4);
5419 for (i = 0; i < NUM_DIRECTIONS; i++)
5421 int move_dir = test_dir[start_test + i];
5422 int move_dir_preference;
5424 xx = x + test_xy[start_test + i][0];
5425 yy = y + test_xy[start_test + i][1];
5427 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5428 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5430 new_move_dir = move_dir;
5435 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5438 move_dir_preference = -1 * RunnerVisit[xx][yy];
5439 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5440 move_dir_preference = PlayerVisit[xx][yy];
5442 if (move_dir_preference > move_preference)
5444 /* prefer field that has not been visited for the longest time */
5445 move_preference = move_dir_preference;
5446 new_move_dir = move_dir;
5448 else if (move_dir_preference == move_preference &&
5449 move_dir == old_move_dir)
5451 /* prefer last direction when all directions are preferred equally */
5452 move_preference = move_dir_preference;
5453 new_move_dir = move_dir;
5457 MovDir[x][y] = new_move_dir;
5458 if (old_move_dir != new_move_dir)
5459 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5463 static void TurnRound(int x, int y)
5465 int direction = MovDir[x][y];
5469 GfxDir[x][y] = MovDir[x][y];
5471 if (direction != MovDir[x][y])
5475 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5477 ResetGfxFrame(x, y, FALSE);
5480 static boolean JustBeingPushed(int x, int y)
5484 for (i = 0; i < MAX_PLAYERS; i++)
5486 struct PlayerInfo *player = &stored_player[i];
5488 if (player->active && player->is_pushing && player->MovPos)
5490 int next_jx = player->jx + (player->jx - player->last_jx);
5491 int next_jy = player->jy + (player->jy - player->last_jy);
5493 if (x == next_jx && y == next_jy)
5501 void StartMoving(int x, int y)
5503 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5504 int element = Feld[x][y];
5509 if (MovDelay[x][y] == 0)
5510 GfxAction[x][y] = ACTION_DEFAULT;
5512 if (CAN_FALL(element) && y < lev_fieldy - 1)
5514 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5515 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5516 if (JustBeingPushed(x, y))
5519 if (element == EL_QUICKSAND_FULL)
5521 if (IS_FREE(x, y + 1))
5523 InitMovingField(x, y, MV_DOWN);
5524 started_moving = TRUE;
5526 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5527 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5528 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5529 Store[x][y] = EL_ROCK;
5531 Store[x][y] = EL_ROCK;
5534 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5536 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5538 if (!MovDelay[x][y])
5539 MovDelay[x][y] = TILEY + 1;
5548 Feld[x][y] = EL_QUICKSAND_EMPTY;
5549 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5550 Store[x][y + 1] = Store[x][y];
5553 PlayLevelSoundAction(x, y, ACTION_FILLING);
5556 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5557 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5559 InitMovingField(x, y, MV_DOWN);
5560 started_moving = TRUE;
5562 Feld[x][y] = EL_QUICKSAND_FILLING;
5563 Store[x][y] = element;
5565 PlayLevelSoundAction(x, y, ACTION_FILLING);
5567 else if (element == EL_MAGIC_WALL_FULL)
5569 if (IS_FREE(x, y + 1))
5571 InitMovingField(x, y, MV_DOWN);
5572 started_moving = TRUE;
5574 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5575 Store[x][y] = EL_CHANGED(Store[x][y]);
5577 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5579 if (!MovDelay[x][y])
5580 MovDelay[x][y] = TILEY/4 + 1;
5589 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5590 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5591 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5595 else if (element == EL_BD_MAGIC_WALL_FULL)
5597 if (IS_FREE(x, y + 1))
5599 InitMovingField(x, y, MV_DOWN);
5600 started_moving = TRUE;
5602 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5603 Store[x][y] = EL_CHANGED2(Store[x][y]);
5605 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5607 if (!MovDelay[x][y])
5608 MovDelay[x][y] = TILEY/4 + 1;
5617 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5618 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5619 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5623 else if (CAN_PASS_MAGIC_WALL(element) &&
5624 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5625 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5627 InitMovingField(x, y, MV_DOWN);
5628 started_moving = TRUE;
5631 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5632 EL_BD_MAGIC_WALL_FILLING);
5633 Store[x][y] = element;
5635 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5637 SplashAcid(x, y + 1);
5639 InitMovingField(x, y, MV_DOWN);
5640 started_moving = TRUE;
5642 Store[x][y] = EL_ACID;
5645 #if USE_FIX_IMPACT_COLLISION
5646 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5647 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
5649 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5650 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5652 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5653 CAN_FALL(element) && WasJustFalling[x][y] &&
5654 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5656 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5657 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5658 (Feld[x][y + 1] == EL_BLOCKED)))
5660 /* this is needed for a special case not covered by calling "Impact()"
5661 from "ContinueMoving()": if an element moves to a tile directly below
5662 another element which was just falling on that tile (which was empty
5663 in the previous frame), the falling element above would just stop
5664 instead of smashing the element below (in previous version, the above
5665 element was just checked for "moving" instead of "falling", resulting
5666 in incorrect smashes caused by horizontal movement of the above
5667 element; also, the case of the player being the element to smash was
5668 simply not covered here... :-/ ) */
5670 CheckCollision[x][y] = 0;
5671 CheckImpact[x][y] = 0;
5675 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5677 if (MovDir[x][y] == MV_NONE)
5679 InitMovingField(x, y, MV_DOWN);
5680 started_moving = TRUE;
5683 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5685 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5686 MovDir[x][y] = MV_DOWN;
5688 InitMovingField(x, y, MV_DOWN);
5689 started_moving = TRUE;
5691 else if (element == EL_AMOEBA_DROP)
5693 Feld[x][y] = EL_AMOEBA_GROWING;
5694 Store[x][y] = EL_AMOEBA_WET;
5696 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5697 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5698 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5699 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5701 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5702 (IS_FREE(x - 1, y + 1) ||
5703 Feld[x - 1][y + 1] == EL_ACID));
5704 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5705 (IS_FREE(x + 1, y + 1) ||
5706 Feld[x + 1][y + 1] == EL_ACID));
5707 boolean can_fall_any = (can_fall_left || can_fall_right);
5708 boolean can_fall_both = (can_fall_left && can_fall_right);
5709 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5711 #if USE_NEW_ALL_SLIPPERY
5712 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5714 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5715 can_fall_right = FALSE;
5716 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5717 can_fall_left = FALSE;
5718 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5719 can_fall_right = FALSE;
5720 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5721 can_fall_left = FALSE;
5723 can_fall_any = (can_fall_left || can_fall_right);
5724 can_fall_both = FALSE;
5727 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5729 if (slippery_type == SLIPPERY_ONLY_LEFT)
5730 can_fall_right = FALSE;
5731 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5732 can_fall_left = FALSE;
5733 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5734 can_fall_right = FALSE;
5735 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5736 can_fall_left = FALSE;
5738 can_fall_any = (can_fall_left || can_fall_right);
5739 can_fall_both = (can_fall_left && can_fall_right);
5743 #if USE_NEW_ALL_SLIPPERY
5745 #if USE_NEW_SP_SLIPPERY
5746 /* !!! better use the same properties as for custom elements here !!! */
5747 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5748 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5750 can_fall_right = FALSE; /* slip down on left side */
5751 can_fall_both = FALSE;
5756 #if USE_NEW_ALL_SLIPPERY
5759 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5760 can_fall_right = FALSE; /* slip down on left side */
5762 can_fall_left = !(can_fall_right = RND(2));
5764 can_fall_both = FALSE;
5769 if (game.emulation == EMU_BOULDERDASH ||
5770 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5771 can_fall_right = FALSE; /* slip down on left side */
5773 can_fall_left = !(can_fall_right = RND(2));
5775 can_fall_both = FALSE;
5781 /* if not determined otherwise, prefer left side for slipping down */
5782 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5783 started_moving = TRUE;
5787 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5789 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5792 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5793 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5794 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5795 int belt_dir = game.belt_dir[belt_nr];
5797 if ((belt_dir == MV_LEFT && left_is_free) ||
5798 (belt_dir == MV_RIGHT && right_is_free))
5800 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5802 InitMovingField(x, y, belt_dir);
5803 started_moving = TRUE;
5805 Pushed[x][y] = TRUE;
5806 Pushed[nextx][y] = TRUE;
5808 GfxAction[x][y] = ACTION_DEFAULT;
5812 MovDir[x][y] = 0; /* if element was moving, stop it */
5817 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5819 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5821 if (CAN_MOVE(element) && !started_moving)
5824 int move_pattern = element_info[element].move_pattern;
5829 if (MovDir[x][y] == MV_NONE)
5831 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5832 x, y, element, element_info[element].token_name);
5833 printf("StartMoving(): This should never happen!\n");
5838 Moving2Blocked(x, y, &newx, &newy);
5840 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5843 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5844 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5846 WasJustMoving[x][y] = 0;
5847 CheckCollision[x][y] = 0;
5849 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5851 if (Feld[x][y] != element) /* element has changed */
5855 if (!MovDelay[x][y]) /* start new movement phase */
5857 /* all objects that can change their move direction after each step
5858 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5860 if (element != EL_YAMYAM &&
5861 element != EL_DARK_YAMYAM &&
5862 element != EL_PACMAN &&
5863 !(move_pattern & MV_ANY_DIRECTION) &&
5864 move_pattern != MV_TURNING_LEFT &&
5865 move_pattern != MV_TURNING_RIGHT &&
5866 move_pattern != MV_TURNING_LEFT_RIGHT &&
5867 move_pattern != MV_TURNING_RIGHT_LEFT &&
5868 move_pattern != MV_TURNING_RANDOM)
5872 if (MovDelay[x][y] && (element == EL_BUG ||
5873 element == EL_SPACESHIP ||
5874 element == EL_SP_SNIKSNAK ||
5875 element == EL_SP_ELECTRON ||
5876 element == EL_MOLE))
5877 DrawLevelField(x, y);
5881 if (MovDelay[x][y]) /* wait some time before next movement */
5885 if (element == EL_ROBOT ||
5886 element == EL_YAMYAM ||
5887 element == EL_DARK_YAMYAM)
5889 DrawLevelElementAnimationIfNeeded(x, y, element);
5890 PlayLevelSoundAction(x, y, ACTION_WAITING);
5892 else if (element == EL_SP_ELECTRON)
5893 DrawLevelElementAnimationIfNeeded(x, y, element);
5894 else if (element == EL_DRAGON)
5897 int dir = MovDir[x][y];
5898 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5899 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5900 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5901 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5902 dir == MV_UP ? IMG_FLAMES_1_UP :
5903 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5904 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5906 GfxAction[x][y] = ACTION_ATTACKING;
5908 if (IS_PLAYER(x, y))
5909 DrawPlayerField(x, y);
5911 DrawLevelField(x, y);
5913 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5915 for (i = 1; i <= 3; i++)
5917 int xx = x + i * dx;
5918 int yy = y + i * dy;
5919 int sx = SCREENX(xx);
5920 int sy = SCREENY(yy);
5921 int flame_graphic = graphic + (i - 1);
5923 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5928 int flamed = MovingOrBlocked2Element(xx, yy);
5932 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5934 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5935 RemoveMovingField(xx, yy);
5937 RemoveField(xx, yy);
5939 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5942 RemoveMovingField(xx, yy);
5945 ChangeDelay[xx][yy] = 0;
5947 Feld[xx][yy] = EL_FLAMES;
5949 if (IN_SCR_FIELD(sx, sy))
5951 DrawLevelFieldCrumbledSand(xx, yy);
5952 DrawGraphic(sx, sy, flame_graphic, frame);
5957 if (Feld[xx][yy] == EL_FLAMES)
5958 Feld[xx][yy] = EL_EMPTY;
5959 DrawLevelField(xx, yy);
5964 if (MovDelay[x][y]) /* element still has to wait some time */
5966 PlayLevelSoundAction(x, y, ACTION_WAITING);
5972 /* now make next step */
5974 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5976 if (DONT_COLLIDE_WITH(element) &&
5977 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5978 !PLAYER_ENEMY_PROTECTED(newx, newy))
5980 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5985 else if (CAN_MOVE_INTO_ACID(element) &&
5986 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5987 !IS_MV_DIAGONAL(MovDir[x][y]) &&
5988 (MovDir[x][y] == MV_DOWN ||
5989 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5991 SplashAcid(newx, newy);
5992 Store[x][y] = EL_ACID;
5994 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5996 if (Feld[newx][newy] == EL_EXIT_OPEN)
5999 DrawLevelField(x, y);
6001 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6002 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6003 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6005 local_player->friends_still_needed--;
6006 if (!local_player->friends_still_needed &&
6007 !local_player->GameOver && AllPlayersGone)
6008 PlayerWins(local_player);
6012 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6014 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6015 DrawLevelField(newx, newy);
6017 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6019 else if (!IS_FREE(newx, newy))
6021 GfxAction[x][y] = ACTION_WAITING;
6023 if (IS_PLAYER(x, y))
6024 DrawPlayerField(x, y);
6026 DrawLevelField(x, y);
6031 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6033 if (IS_FOOD_PIG(Feld[newx][newy]))
6035 if (IS_MOVING(newx, newy))
6036 RemoveMovingField(newx, newy);
6039 Feld[newx][newy] = EL_EMPTY;
6040 DrawLevelField(newx, newy);
6043 PlayLevelSound(x, y, SND_PIG_DIGGING);
6045 else if (!IS_FREE(newx, newy))
6047 if (IS_PLAYER(x, y))
6048 DrawPlayerField(x, y);
6050 DrawLevelField(x, y);
6055 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6057 if (Store[x][y] != EL_EMPTY)
6059 boolean can_clone = FALSE;
6062 /* check if element to clone is still there */
6063 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6065 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6073 /* cannot clone or target field not free anymore -- do not clone */
6074 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6075 Store[x][y] = EL_EMPTY;
6078 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6080 if (IS_MV_DIAGONAL(MovDir[x][y]))
6082 int diagonal_move_dir = MovDir[x][y];
6083 int stored = Store[x][y];
6084 int change_delay = 8;
6087 /* android is moving diagonally */
6089 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6091 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6092 GfxElement[x][y] = EL_EMC_ANDROID;
6093 GfxAction[x][y] = ACTION_SHRINKING;
6094 GfxDir[x][y] = diagonal_move_dir;
6095 ChangeDelay[x][y] = change_delay;
6097 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6100 DrawLevelGraphicAnimation(x, y, graphic);
6101 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6103 if (Feld[newx][newy] == EL_ACID)
6105 SplashAcid(newx, newy);
6110 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6112 Store[newx][newy] = EL_EMC_ANDROID;
6113 GfxElement[newx][newy] = EL_EMC_ANDROID;
6114 GfxAction[newx][newy] = ACTION_GROWING;
6115 GfxDir[newx][newy] = diagonal_move_dir;
6116 ChangeDelay[newx][newy] = change_delay;
6118 graphic = el_act_dir2img(GfxElement[newx][newy],
6119 GfxAction[newx][newy], GfxDir[newx][newy]);
6121 DrawLevelGraphicAnimation(newx, newy, graphic);
6122 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6128 Feld[newx][newy] = EL_EMPTY;
6129 DrawLevelField(newx, newy);
6131 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6134 else if (!IS_FREE(newx, newy))
6137 if (IS_PLAYER(x, y))
6138 DrawPlayerField(x, y);
6140 DrawLevelField(x, y);
6146 else if (IS_CUSTOM_ELEMENT(element) &&
6147 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6149 int new_element = Feld[newx][newy];
6151 if (!IS_FREE(newx, newy))
6153 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6154 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6157 /* no element can dig solid indestructible elements */
6158 if (IS_INDESTRUCTIBLE(new_element) &&
6159 !IS_DIGGABLE(new_element) &&
6160 !IS_COLLECTIBLE(new_element))
6163 if (AmoebaNr[newx][newy] &&
6164 (new_element == EL_AMOEBA_FULL ||
6165 new_element == EL_BD_AMOEBA ||
6166 new_element == EL_AMOEBA_GROWING))
6168 AmoebaCnt[AmoebaNr[newx][newy]]--;
6169 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6172 if (IS_MOVING(newx, newy))
6173 RemoveMovingField(newx, newy);
6176 RemoveField(newx, newy);
6177 DrawLevelField(newx, newy);
6180 /* if digged element was about to explode, prevent the explosion */
6181 ExplodeField[newx][newy] = EX_TYPE_NONE;
6183 PlayLevelSoundAction(x, y, action);
6186 Store[newx][newy] = EL_EMPTY;
6188 /* this makes it possible to leave the removed element again */
6189 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6190 Store[newx][newy] = new_element;
6192 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6194 int move_leave_element = element_info[element].move_leave_element;
6196 /* this makes it possible to leave the removed element again */
6197 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6198 new_element : move_leave_element);
6202 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6204 RunnerVisit[x][y] = FrameCounter;
6205 PlayerVisit[x][y] /= 8; /* expire player visit path */
6208 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6210 if (!IS_FREE(newx, newy))
6212 if (IS_PLAYER(x, y))
6213 DrawPlayerField(x, y);
6215 DrawLevelField(x, y);
6221 boolean wanna_flame = !RND(10);
6222 int dx = newx - x, dy = newy - y;
6223 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6224 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6225 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6226 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6227 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6228 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6231 IS_CLASSIC_ENEMY(element1) ||
6232 IS_CLASSIC_ENEMY(element2)) &&
6233 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6234 element1 != EL_FLAMES && element2 != EL_FLAMES)
6236 ResetGfxAnimation(x, y);
6237 GfxAction[x][y] = ACTION_ATTACKING;
6239 if (IS_PLAYER(x, y))
6240 DrawPlayerField(x, y);
6242 DrawLevelField(x, y);
6244 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6246 MovDelay[x][y] = 50;
6250 RemoveField(newx, newy);
6252 Feld[newx][newy] = EL_FLAMES;
6253 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6256 RemoveField(newx1, newy1);
6258 Feld[newx1][newy1] = EL_FLAMES;
6260 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6263 RemoveField(newx2, newy2);
6265 Feld[newx2][newy2] = EL_FLAMES;
6272 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6273 Feld[newx][newy] == EL_DIAMOND)
6275 if (IS_MOVING(newx, newy))
6276 RemoveMovingField(newx, newy);
6279 Feld[newx][newy] = EL_EMPTY;
6280 DrawLevelField(newx, newy);
6283 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6285 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6286 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6288 if (AmoebaNr[newx][newy])
6290 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6291 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6292 Feld[newx][newy] == EL_BD_AMOEBA)
6293 AmoebaCnt[AmoebaNr[newx][newy]]--;
6298 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6300 RemoveMovingField(newx, newy);
6303 if (IS_MOVING(newx, newy))
6305 RemoveMovingField(newx, newy);
6310 Feld[newx][newy] = EL_EMPTY;
6311 DrawLevelField(newx, newy);
6314 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6316 else if ((element == EL_PACMAN || element == EL_MOLE)
6317 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6319 if (AmoebaNr[newx][newy])
6321 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6322 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6323 Feld[newx][newy] == EL_BD_AMOEBA)
6324 AmoebaCnt[AmoebaNr[newx][newy]]--;
6327 if (element == EL_MOLE)
6329 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6330 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6332 ResetGfxAnimation(x, y);
6333 GfxAction[x][y] = ACTION_DIGGING;
6334 DrawLevelField(x, y);
6336 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6338 return; /* wait for shrinking amoeba */
6340 else /* element == EL_PACMAN */
6342 Feld[newx][newy] = EL_EMPTY;
6343 DrawLevelField(newx, newy);
6344 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6347 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6348 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6349 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6351 /* wait for shrinking amoeba to completely disappear */
6354 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6356 /* object was running against a wall */
6361 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6362 if (move_pattern & MV_ANY_DIRECTION &&
6363 move_pattern == MovDir[x][y])
6365 int blocking_element =
6366 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6368 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6371 element = Feld[x][y]; /* element might have changed */
6375 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6376 DrawLevelElementAnimation(x, y, element);
6378 if (DONT_TOUCH(element))
6379 TestIfBadThingTouchesPlayer(x, y);
6384 InitMovingField(x, y, MovDir[x][y]);
6386 PlayLevelSoundAction(x, y, ACTION_MOVING);
6390 ContinueMoving(x, y);
6393 void ContinueMoving(int x, int y)
6395 int element = Feld[x][y];
6396 struct ElementInfo *ei = &element_info[element];
6397 int direction = MovDir[x][y];
6398 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6399 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6400 int newx = x + dx, newy = y + dy;
6401 int stored = Store[x][y];
6402 int stored_new = Store[newx][newy];
6403 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6404 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6405 boolean last_line = (newy == lev_fieldy - 1);
6407 MovPos[x][y] += getElementMoveStepsize(x, y);
6409 if (pushed_by_player) /* special case: moving object pushed by player */
6410 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6412 if (ABS(MovPos[x][y]) < TILEX)
6414 DrawLevelField(x, y);
6416 return; /* element is still moving */
6419 /* element reached destination field */
6421 Feld[x][y] = EL_EMPTY;
6422 Feld[newx][newy] = element;
6423 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6425 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6427 element = Feld[newx][newy] = EL_ACID;
6429 else if (element == EL_MOLE)
6431 Feld[x][y] = EL_SAND;
6433 DrawLevelFieldCrumbledSandNeighbours(x, y);
6435 else if (element == EL_QUICKSAND_FILLING)
6437 element = Feld[newx][newy] = get_next_element(element);
6438 Store[newx][newy] = Store[x][y];
6440 else if (element == EL_QUICKSAND_EMPTYING)
6442 Feld[x][y] = get_next_element(element);
6443 element = Feld[newx][newy] = Store[x][y];
6445 else if (element == EL_MAGIC_WALL_FILLING)
6447 element = Feld[newx][newy] = get_next_element(element);
6448 if (!game.magic_wall_active)
6449 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6450 Store[newx][newy] = Store[x][y];
6452 else if (element == EL_MAGIC_WALL_EMPTYING)
6454 Feld[x][y] = get_next_element(element);
6455 if (!game.magic_wall_active)
6456 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6457 element = Feld[newx][newy] = Store[x][y];
6459 #if USE_NEW_CUSTOM_VALUE
6460 InitField(newx, newy, FALSE);
6463 else if (element == EL_BD_MAGIC_WALL_FILLING)
6465 element = Feld[newx][newy] = get_next_element(element);
6466 if (!game.magic_wall_active)
6467 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6468 Store[newx][newy] = Store[x][y];
6470 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6472 Feld[x][y] = get_next_element(element);
6473 if (!game.magic_wall_active)
6474 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6475 element = Feld[newx][newy] = Store[x][y];
6477 #if USE_NEW_CUSTOM_VALUE
6478 InitField(newx, newy, FALSE);
6481 else if (element == EL_AMOEBA_DROPPING)
6483 Feld[x][y] = get_next_element(element);
6484 element = Feld[newx][newy] = Store[x][y];
6486 else if (element == EL_SOKOBAN_OBJECT)
6489 Feld[x][y] = Back[x][y];
6491 if (Back[newx][newy])
6492 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6494 Back[x][y] = Back[newx][newy] = 0;
6497 Store[x][y] = EL_EMPTY;
6502 MovDelay[newx][newy] = 0;
6504 if (CAN_CHANGE_OR_HAS_ACTION(element))
6506 /* copy element change control values to new field */
6507 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6508 ChangePage[newx][newy] = ChangePage[x][y];
6509 ChangeCount[newx][newy] = ChangeCount[x][y];
6510 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6513 #if USE_NEW_CUSTOM_VALUE
6514 CustomValue[newx][newy] = CustomValue[x][y];
6517 ChangeDelay[x][y] = 0;
6518 ChangePage[x][y] = -1;
6519 ChangeCount[x][y] = 0;
6520 ChangeEvent[x][y] = -1;
6522 #if USE_NEW_CUSTOM_VALUE
6523 CustomValue[x][y] = 0;
6526 /* copy animation control values to new field */
6527 GfxFrame[newx][newy] = GfxFrame[x][y];
6528 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6529 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6530 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6532 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6534 /* some elements can leave other elements behind after moving */
6536 if (ei->move_leave_element != EL_EMPTY &&
6537 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6538 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6540 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6541 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6542 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6545 int move_leave_element = ei->move_leave_element;
6549 /* this makes it possible to leave the removed element again */
6550 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6551 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6553 /* this makes it possible to leave the removed element again */
6554 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6555 move_leave_element = stored;
6558 /* this makes it possible to leave the removed element again */
6559 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6560 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6561 move_leave_element = stored;
6564 Feld[x][y] = move_leave_element;
6566 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6567 MovDir[x][y] = direction;
6569 InitField(x, y, FALSE);
6571 if (GFX_CRUMBLED(Feld[x][y]))
6572 DrawLevelFieldCrumbledSandNeighbours(x, y);
6574 if (ELEM_IS_PLAYER(move_leave_element))
6575 RelocatePlayer(x, y, move_leave_element);
6578 /* do this after checking for left-behind element */
6579 ResetGfxAnimation(x, y); /* reset animation values for old field */
6581 if (!CAN_MOVE(element) ||
6582 (CAN_FALL(element) && direction == MV_DOWN &&
6583 (element == EL_SPRING ||
6584 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6585 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6586 GfxDir[x][y] = MovDir[newx][newy] = 0;
6588 DrawLevelField(x, y);
6589 DrawLevelField(newx, newy);
6591 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6593 /* prevent pushed element from moving on in pushed direction */
6594 if (pushed_by_player && CAN_MOVE(element) &&
6595 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6596 !(element_info[element].move_pattern & direction))
6597 TurnRound(newx, newy);
6599 /* prevent elements on conveyor belt from moving on in last direction */
6600 if (pushed_by_conveyor && CAN_FALL(element) &&
6601 direction & MV_HORIZONTAL)
6602 MovDir[newx][newy] = 0;
6604 if (!pushed_by_player)
6606 int nextx = newx + dx, nexty = newy + dy;
6607 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6609 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6611 if (CAN_FALL(element) && direction == MV_DOWN)
6612 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6614 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6615 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6617 #if USE_FIX_IMPACT_COLLISION
6618 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
6619 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
6623 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6625 TestIfBadThingTouchesPlayer(newx, newy);
6626 TestIfBadThingTouchesFriend(newx, newy);
6628 if (!IS_CUSTOM_ELEMENT(element))
6629 TestIfBadThingTouchesOtherBadThing(newx, newy);
6631 else if (element == EL_PENGUIN)
6632 TestIfFriendTouchesBadThing(newx, newy);
6634 /* give the player one last chance (one more frame) to move away */
6635 if (CAN_FALL(element) && direction == MV_DOWN &&
6636 (last_line || (!IS_FREE(x, newy + 1) &&
6637 (!IS_PLAYER(x, newy + 1) ||
6638 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6641 if (pushed_by_player && !game.use_change_when_pushing_bug)
6643 int push_side = MV_DIR_OPPOSITE(direction);
6644 struct PlayerInfo *player = PLAYERINFO(x, y);
6646 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6647 player->index_bit, push_side);
6648 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6649 player->index_bit, push_side);
6652 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6653 MovDelay[newx][newy] = 1;
6655 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6657 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6660 if (ChangePage[newx][newy] != -1) /* delayed change */
6662 int page = ChangePage[newx][newy];
6663 struct ElementChangeInfo *change = &ei->change_page[page];
6665 ChangePage[newx][newy] = -1;
6667 if (change->can_change)
6669 if (ChangeElement(newx, newy, element, page))
6671 if (change->post_change_function)
6672 change->post_change_function(newx, newy);
6676 if (change->has_action)
6677 ExecuteCustomElementAction(newx, newy, element, page);
6681 TestIfElementHitsCustomElement(newx, newy, direction);
6682 TestIfPlayerTouchesCustomElement(newx, newy);
6683 TestIfElementTouchesCustomElement(newx, newy);
6685 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6686 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6687 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6688 MV_DIR_OPPOSITE(direction));
6691 int AmoebeNachbarNr(int ax, int ay)
6694 int element = Feld[ax][ay];
6696 static int xy[4][2] =
6704 for (i = 0; i < NUM_DIRECTIONS; i++)
6706 int x = ax + xy[i][0];
6707 int y = ay + xy[i][1];
6709 if (!IN_LEV_FIELD(x, y))
6712 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6713 group_nr = AmoebaNr[x][y];
6719 void AmoebenVereinigen(int ax, int ay)
6721 int i, x, y, xx, yy;
6722 int new_group_nr = AmoebaNr[ax][ay];
6723 static int xy[4][2] =
6731 if (new_group_nr == 0)
6734 for (i = 0; i < NUM_DIRECTIONS; i++)
6739 if (!IN_LEV_FIELD(x, y))
6742 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6743 Feld[x][y] == EL_BD_AMOEBA ||
6744 Feld[x][y] == EL_AMOEBA_DEAD) &&
6745 AmoebaNr[x][y] != new_group_nr)
6747 int old_group_nr = AmoebaNr[x][y];
6749 if (old_group_nr == 0)
6752 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6753 AmoebaCnt[old_group_nr] = 0;
6754 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6755 AmoebaCnt2[old_group_nr] = 0;
6757 SCAN_PLAYFIELD(xx, yy)
6759 if (AmoebaNr[xx][yy] == old_group_nr)
6760 AmoebaNr[xx][yy] = new_group_nr;
6766 void AmoebeUmwandeln(int ax, int ay)
6770 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6772 int group_nr = AmoebaNr[ax][ay];
6777 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6778 printf("AmoebeUmwandeln(): This should never happen!\n");
6783 SCAN_PLAYFIELD(x, y)
6785 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6788 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6792 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6793 SND_AMOEBA_TURNING_TO_GEM :
6794 SND_AMOEBA_TURNING_TO_ROCK));
6799 static int xy[4][2] =
6807 for (i = 0; i < NUM_DIRECTIONS; i++)
6812 if (!IN_LEV_FIELD(x, y))
6815 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6817 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6818 SND_AMOEBA_TURNING_TO_GEM :
6819 SND_AMOEBA_TURNING_TO_ROCK));
6826 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6829 int group_nr = AmoebaNr[ax][ay];
6830 boolean done = FALSE;
6835 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6836 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6841 SCAN_PLAYFIELD(x, y)
6843 if (AmoebaNr[x][y] == group_nr &&
6844 (Feld[x][y] == EL_AMOEBA_DEAD ||
6845 Feld[x][y] == EL_BD_AMOEBA ||
6846 Feld[x][y] == EL_AMOEBA_GROWING))
6849 Feld[x][y] = new_element;
6850 InitField(x, y, FALSE);
6851 DrawLevelField(x, y);
6857 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6858 SND_BD_AMOEBA_TURNING_TO_ROCK :
6859 SND_BD_AMOEBA_TURNING_TO_GEM));
6862 void AmoebeWaechst(int x, int y)
6864 static unsigned long sound_delay = 0;
6865 static unsigned long sound_delay_value = 0;
6867 if (!MovDelay[x][y]) /* start new growing cycle */
6871 if (DelayReached(&sound_delay, sound_delay_value))
6873 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6874 sound_delay_value = 30;
6878 if (MovDelay[x][y]) /* wait some time before growing bigger */
6881 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6883 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6884 6 - MovDelay[x][y]);
6886 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6889 if (!MovDelay[x][y])
6891 Feld[x][y] = Store[x][y];
6893 DrawLevelField(x, y);
6898 void AmoebaDisappearing(int x, int y)
6900 static unsigned long sound_delay = 0;
6901 static unsigned long sound_delay_value = 0;
6903 if (!MovDelay[x][y]) /* start new shrinking cycle */
6907 if (DelayReached(&sound_delay, sound_delay_value))
6908 sound_delay_value = 30;
6911 if (MovDelay[x][y]) /* wait some time before shrinking */
6914 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6916 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6917 6 - MovDelay[x][y]);
6919 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6922 if (!MovDelay[x][y])
6924 Feld[x][y] = EL_EMPTY;
6925 DrawLevelField(x, y);
6927 /* don't let mole enter this field in this cycle;
6928 (give priority to objects falling to this field from above) */
6934 void AmoebeAbleger(int ax, int ay)
6937 int element = Feld[ax][ay];
6938 int graphic = el2img(element);
6939 int newax = ax, neway = ay;
6940 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6941 static int xy[4][2] =
6949 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6951 Feld[ax][ay] = EL_AMOEBA_DEAD;
6952 DrawLevelField(ax, ay);
6956 if (IS_ANIMATED(graphic))
6957 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6959 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6960 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6962 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6965 if (MovDelay[ax][ay])
6969 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6972 int x = ax + xy[start][0];
6973 int y = ay + xy[start][1];
6975 if (!IN_LEV_FIELD(x, y))
6978 if (IS_FREE(x, y) ||
6979 CAN_GROW_INTO(Feld[x][y]) ||
6980 Feld[x][y] == EL_QUICKSAND_EMPTY)
6986 if (newax == ax && neway == ay)
6989 else /* normal or "filled" (BD style) amoeba */
6992 boolean waiting_for_player = FALSE;
6994 for (i = 0; i < NUM_DIRECTIONS; i++)
6996 int j = (start + i) % 4;
6997 int x = ax + xy[j][0];
6998 int y = ay + xy[j][1];
7000 if (!IN_LEV_FIELD(x, y))
7003 if (IS_FREE(x, y) ||
7004 CAN_GROW_INTO(Feld[x][y]) ||
7005 Feld[x][y] == EL_QUICKSAND_EMPTY)
7011 else if (IS_PLAYER(x, y))
7012 waiting_for_player = TRUE;
7015 if (newax == ax && neway == ay) /* amoeba cannot grow */
7017 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7019 Feld[ax][ay] = EL_AMOEBA_DEAD;
7020 DrawLevelField(ax, ay);
7021 AmoebaCnt[AmoebaNr[ax][ay]]--;
7023 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7025 if (element == EL_AMOEBA_FULL)
7026 AmoebeUmwandeln(ax, ay);
7027 else if (element == EL_BD_AMOEBA)
7028 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7033 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7035 /* amoeba gets larger by growing in some direction */
7037 int new_group_nr = AmoebaNr[ax][ay];
7040 if (new_group_nr == 0)
7042 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7043 printf("AmoebeAbleger(): This should never happen!\n");
7048 AmoebaNr[newax][neway] = new_group_nr;
7049 AmoebaCnt[new_group_nr]++;
7050 AmoebaCnt2[new_group_nr]++;
7052 /* if amoeba touches other amoeba(s) after growing, unify them */
7053 AmoebenVereinigen(newax, neway);
7055 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7057 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7063 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7064 (neway == lev_fieldy - 1 && newax != ax))
7066 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7067 Store[newax][neway] = element;
7069 else if (neway == ay || element == EL_EMC_DRIPPER)
7071 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7073 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7077 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7078 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7079 Store[ax][ay] = EL_AMOEBA_DROP;
7080 ContinueMoving(ax, ay);
7084 DrawLevelField(newax, neway);
7087 void Life(int ax, int ay)
7091 int element = Feld[ax][ay];
7092 int graphic = el2img(element);
7093 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7095 boolean changed = FALSE;
7097 if (IS_ANIMATED(graphic))
7098 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7103 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7104 MovDelay[ax][ay] = life_time;
7106 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7109 if (MovDelay[ax][ay])
7113 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7115 int xx = ax+x1, yy = ay+y1;
7118 if (!IN_LEV_FIELD(xx, yy))
7121 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7123 int x = xx+x2, y = yy+y2;
7125 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7128 if (((Feld[x][y] == element ||
7129 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7131 (IS_FREE(x, y) && Stop[x][y]))
7135 if (xx == ax && yy == ay) /* field in the middle */
7137 if (nachbarn < life_parameter[0] ||
7138 nachbarn > life_parameter[1])
7140 Feld[xx][yy] = EL_EMPTY;
7142 DrawLevelField(xx, yy);
7143 Stop[xx][yy] = TRUE;
7147 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7148 { /* free border field */
7149 if (nachbarn >= life_parameter[2] &&
7150 nachbarn <= life_parameter[3])
7152 Feld[xx][yy] = element;
7153 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7155 DrawLevelField(xx, yy);
7156 Stop[xx][yy] = TRUE;
7163 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7164 SND_GAME_OF_LIFE_GROWING);
7167 static void InitRobotWheel(int x, int y)
7169 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7172 static void RunRobotWheel(int x, int y)
7174 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7177 static void StopRobotWheel(int x, int y)
7179 if (ZX == x && ZY == y)
7183 static void InitTimegateWheel(int x, int y)
7185 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7188 static void RunTimegateWheel(int x, int y)
7190 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7193 static void InitMagicBallDelay(int x, int y)
7196 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7198 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7202 static void ActivateMagicBall(int bx, int by)
7206 if (level.ball_random)
7208 int pos_border = RND(8); /* select one of the eight border elements */
7209 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7210 int xx = pos_content % 3;
7211 int yy = pos_content / 3;
7216 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7217 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7221 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7223 int xx = x - bx + 1;
7224 int yy = y - by + 1;
7226 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7227 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7231 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7234 void CheckExit(int x, int y)
7236 if (local_player->gems_still_needed > 0 ||
7237 local_player->sokobanfields_still_needed > 0 ||
7238 local_player->lights_still_needed > 0)
7240 int element = Feld[x][y];
7241 int graphic = el2img(element);
7243 if (IS_ANIMATED(graphic))
7244 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7249 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7252 Feld[x][y] = EL_EXIT_OPENING;
7254 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7257 void CheckExitSP(int x, int y)
7259 if (local_player->gems_still_needed > 0)
7261 int element = Feld[x][y];
7262 int graphic = el2img(element);
7264 if (IS_ANIMATED(graphic))
7265 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7270 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7273 Feld[x][y] = EL_SP_EXIT_OPENING;
7275 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7278 static void CloseAllOpenTimegates()
7282 SCAN_PLAYFIELD(x, y)
7284 int element = Feld[x][y];
7286 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7288 Feld[x][y] = EL_TIMEGATE_CLOSING;
7290 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7295 void DrawTwinkleOnField(int x, int y)
7297 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7300 if (Feld[x][y] == EL_BD_DIAMOND)
7303 if (MovDelay[x][y] == 0) /* next animation frame */
7304 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7306 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7310 if (setup.direct_draw && MovDelay[x][y])
7311 SetDrawtoField(DRAW_BUFFERED);
7313 DrawLevelElementAnimation(x, y, Feld[x][y]);
7315 if (MovDelay[x][y] != 0)
7317 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7318 10 - MovDelay[x][y]);
7320 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7322 if (setup.direct_draw)
7326 dest_x = FX + SCREENX(x) * TILEX;
7327 dest_y = FY + SCREENY(y) * TILEY;
7329 BlitBitmap(drawto_field, window,
7330 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7331 SetDrawtoField(DRAW_DIRECT);
7337 void MauerWaechst(int x, int y)
7341 if (!MovDelay[x][y]) /* next animation frame */
7342 MovDelay[x][y] = 3 * delay;
7344 if (MovDelay[x][y]) /* wait some time before next frame */
7348 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7350 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7351 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7353 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7356 if (!MovDelay[x][y])
7358 if (MovDir[x][y] == MV_LEFT)
7360 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7361 DrawLevelField(x - 1, y);
7363 else if (MovDir[x][y] == MV_RIGHT)
7365 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7366 DrawLevelField(x + 1, y);
7368 else if (MovDir[x][y] == MV_UP)
7370 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7371 DrawLevelField(x, y - 1);
7375 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7376 DrawLevelField(x, y + 1);
7379 Feld[x][y] = Store[x][y];
7381 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7382 DrawLevelField(x, y);
7387 void MauerAbleger(int ax, int ay)
7389 int element = Feld[ax][ay];
7390 int graphic = el2img(element);
7391 boolean oben_frei = FALSE, unten_frei = FALSE;
7392 boolean links_frei = FALSE, rechts_frei = FALSE;
7393 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7394 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7395 boolean new_wall = FALSE;
7397 if (IS_ANIMATED(graphic))
7398 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7400 if (!MovDelay[ax][ay]) /* start building new wall */
7401 MovDelay[ax][ay] = 6;
7403 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7406 if (MovDelay[ax][ay])
7410 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7412 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7414 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7416 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7419 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7420 element == EL_EXPANDABLE_WALL_ANY)
7424 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7425 Store[ax][ay-1] = element;
7426 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7427 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7428 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7429 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7434 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7435 Store[ax][ay+1] = element;
7436 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7437 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7438 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7439 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7444 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7445 element == EL_EXPANDABLE_WALL_ANY ||
7446 element == EL_EXPANDABLE_WALL ||
7447 element == EL_BD_EXPANDABLE_WALL)
7451 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7452 Store[ax-1][ay] = element;
7453 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7454 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7455 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7456 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7462 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7463 Store[ax+1][ay] = element;
7464 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7465 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7466 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7467 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7472 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7473 DrawLevelField(ax, ay);
7475 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7477 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7478 unten_massiv = TRUE;
7479 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7480 links_massiv = TRUE;
7481 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7482 rechts_massiv = TRUE;
7484 if (((oben_massiv && unten_massiv) ||
7485 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7486 element == EL_EXPANDABLE_WALL) &&
7487 ((links_massiv && rechts_massiv) ||
7488 element == EL_EXPANDABLE_WALL_VERTICAL))
7489 Feld[ax][ay] = EL_WALL;
7492 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7495 void CheckForDragon(int x, int y)
7498 boolean dragon_found = FALSE;
7499 static int xy[4][2] =
7507 for (i = 0; i < NUM_DIRECTIONS; i++)
7509 for (j = 0; j < 4; j++)
7511 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7513 if (IN_LEV_FIELD(xx, yy) &&
7514 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7516 if (Feld[xx][yy] == EL_DRAGON)
7517 dragon_found = TRUE;
7526 for (i = 0; i < NUM_DIRECTIONS; i++)
7528 for (j = 0; j < 3; j++)
7530 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7532 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7534 Feld[xx][yy] = EL_EMPTY;
7535 DrawLevelField(xx, yy);
7544 static void InitBuggyBase(int x, int y)
7546 int element = Feld[x][y];
7547 int activating_delay = FRAMES_PER_SECOND / 4;
7550 (element == EL_SP_BUGGY_BASE ?
7551 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7552 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7554 element == EL_SP_BUGGY_BASE_ACTIVE ?
7555 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7558 static void WarnBuggyBase(int x, int y)
7561 static int xy[4][2] =
7569 for (i = 0; i < NUM_DIRECTIONS; i++)
7571 int xx = x + xy[i][0];
7572 int yy = y + xy[i][1];
7574 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7576 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7583 static void InitTrap(int x, int y)
7585 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7588 static void ActivateTrap(int x, int y)
7590 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7593 static void ChangeActiveTrap(int x, int y)
7595 int graphic = IMG_TRAP_ACTIVE;
7597 /* if new animation frame was drawn, correct crumbled sand border */
7598 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7599 DrawLevelFieldCrumbledSand(x, y);
7602 static int getSpecialActionElement(int element, int number, int base_element)
7604 return (element != EL_EMPTY ? element :
7605 number != -1 ? base_element + number - 1 :
7609 static int getModifiedActionNumber(int value_old, int operator, int operand,
7610 int value_min, int value_max)
7612 int value_new = (operator == CA_MODE_SET ? operand :
7613 operator == CA_MODE_ADD ? value_old + operand :
7614 operator == CA_MODE_SUBTRACT ? value_old - operand :
7615 operator == CA_MODE_MULTIPLY ? value_old * operand :
7616 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7617 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7620 return (value_new < value_min ? value_min :
7621 value_new > value_max ? value_max :
7625 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7627 struct ElementInfo *ei = &element_info[element];
7628 struct ElementChangeInfo *change = &ei->change_page[page];
7629 int target_element = change->target_element;
7630 int action_type = change->action_type;
7631 int action_mode = change->action_mode;
7632 int action_arg = change->action_arg;
7635 if (!change->has_action)
7638 /* ---------- determine action paramater values -------------------------- */
7640 int level_time_value =
7641 (level.time > 0 ? TimeLeft :
7644 int action_arg_element =
7645 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7646 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7647 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7650 int action_arg_direction =
7651 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7652 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7653 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7654 change->actual_trigger_side :
7655 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7656 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7659 int action_arg_number_min =
7660 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7663 int action_arg_number_max =
7664 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7665 action_type == CA_SET_LEVEL_GEMS ? 999 :
7666 action_type == CA_SET_LEVEL_TIME ? 9999 :
7667 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7668 action_type == CA_SET_CE_VALUE ? 9999 :
7669 action_type == CA_SET_CE_SCORE ? 9999 :
7672 int action_arg_number_reset =
7673 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
7674 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7675 action_type == CA_SET_LEVEL_TIME ? level.time :
7676 action_type == CA_SET_LEVEL_SCORE ? 0 :
7677 #if USE_NEW_CUSTOM_VALUE
7678 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
7680 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7682 action_type == CA_SET_CE_SCORE ? 0 :
7685 int action_arg_number =
7686 (action_arg <= CA_ARG_MAX ? action_arg :
7687 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7688 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7689 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7690 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7691 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7692 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7693 #if USE_NEW_CUSTOM_VALUE
7694 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7696 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7698 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7699 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7700 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7701 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7702 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7703 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
7704 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7705 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
7706 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
7707 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7708 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7711 int action_arg_number_old =
7712 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7713 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7714 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7715 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7716 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7719 int action_arg_number_new =
7720 getModifiedActionNumber(action_arg_number_old,
7721 action_mode, action_arg_number,
7722 action_arg_number_min, action_arg_number_max);
7724 int trigger_player_bits =
7725 (change->actual_trigger_player >= EL_PLAYER_1 &&
7726 change->actual_trigger_player <= EL_PLAYER_4 ?
7727 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7730 int action_arg_player_bits =
7731 (action_arg >= CA_ARG_PLAYER_1 &&
7732 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7733 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7736 /* ---------- execute action -------------------------------------------- */
7738 switch (action_type)
7745 /* ---------- level actions ------------------------------------------- */
7747 case CA_RESTART_LEVEL:
7749 game.restart_level = TRUE;
7754 case CA_SHOW_ENVELOPE:
7756 int element = getSpecialActionElement(action_arg_element,
7757 action_arg_number, EL_ENVELOPE_1);
7759 if (IS_ENVELOPE(element))
7760 local_player->show_envelope = element;
7765 case CA_SET_LEVEL_TIME:
7767 if (level.time > 0) /* only modify limited time value */
7769 TimeLeft = action_arg_number_new;
7771 DrawGameValue_Time(TimeLeft);
7773 if (!TimeLeft && setup.time_limit)
7774 for (i = 0; i < MAX_PLAYERS; i++)
7775 KillPlayer(&stored_player[i]);
7781 case CA_SET_LEVEL_SCORE:
7783 local_player->score = action_arg_number_new;
7785 DrawGameValue_Score(local_player->score);
7790 case CA_SET_LEVEL_GEMS:
7792 local_player->gems_still_needed = action_arg_number_new;
7794 DrawGameValue_Emeralds(local_player->gems_still_needed);
7799 #if !USE_PLAYER_GRAVITY
7800 case CA_SET_LEVEL_GRAVITY:
7802 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7803 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7804 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7810 case CA_SET_LEVEL_WIND:
7812 game.wind_direction = action_arg_direction;
7817 /* ---------- player actions ------------------------------------------ */
7819 case CA_MOVE_PLAYER:
7821 /* automatically move to the next field in specified direction */
7822 for (i = 0; i < MAX_PLAYERS; i++)
7823 if (trigger_player_bits & (1 << i))
7824 stored_player[i].programmed_action = action_arg_direction;
7829 case CA_EXIT_PLAYER:
7831 for (i = 0; i < MAX_PLAYERS; i++)
7832 if (action_arg_player_bits & (1 << i))
7833 PlayerWins(&stored_player[i]);
7838 case CA_KILL_PLAYER:
7840 for (i = 0; i < MAX_PLAYERS; i++)
7841 if (action_arg_player_bits & (1 << i))
7842 KillPlayer(&stored_player[i]);
7847 case CA_SET_PLAYER_KEYS:
7849 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7850 int element = getSpecialActionElement(action_arg_element,
7851 action_arg_number, EL_KEY_1);
7853 if (IS_KEY(element))
7855 for (i = 0; i < MAX_PLAYERS; i++)
7857 if (trigger_player_bits & (1 << i))
7859 stored_player[i].key[KEY_NR(element)] = key_state;
7861 DrawGameDoorValues();
7869 case CA_SET_PLAYER_SPEED:
7871 for (i = 0; i < MAX_PLAYERS; i++)
7873 if (trigger_player_bits & (1 << i))
7875 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7877 if (action_arg == CA_ARG_SPEED_FASTER &&
7878 stored_player[i].cannot_move)
7880 action_arg_number = STEPSIZE_VERY_SLOW;
7882 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7883 action_arg == CA_ARG_SPEED_FASTER)
7885 action_arg_number = 2;
7886 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7889 else if (action_arg == CA_ARG_NUMBER_RESET)
7891 action_arg_number = level.initial_player_stepsize[i];
7895 getModifiedActionNumber(move_stepsize,
7898 action_arg_number_min,
7899 action_arg_number_max);
7901 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7908 case CA_SET_PLAYER_SHIELD:
7910 for (i = 0; i < MAX_PLAYERS; i++)
7912 if (trigger_player_bits & (1 << i))
7914 if (action_arg == CA_ARG_SHIELD_OFF)
7916 stored_player[i].shield_normal_time_left = 0;
7917 stored_player[i].shield_deadly_time_left = 0;
7919 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7921 stored_player[i].shield_normal_time_left = 999999;
7923 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7925 stored_player[i].shield_normal_time_left = 999999;
7926 stored_player[i].shield_deadly_time_left = 999999;
7934 #if USE_PLAYER_GRAVITY
7935 case CA_SET_PLAYER_GRAVITY:
7937 for (i = 0; i < MAX_PLAYERS; i++)
7939 if (trigger_player_bits & (1 << i))
7941 stored_player[i].gravity =
7942 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7943 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7944 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
7945 stored_player[i].gravity);
7953 case CA_SET_PLAYER_ARTWORK:
7955 for (i = 0; i < MAX_PLAYERS; i++)
7957 if (trigger_player_bits & (1 << i))
7959 int artwork_element = action_arg_element;
7961 if (action_arg == CA_ARG_ELEMENT_RESET)
7963 (level.use_artwork_element[i] ? level.artwork_element[i] :
7964 stored_player[i].element_nr);
7966 #if USE_GFX_RESET_PLAYER_ARTWORK
7967 if (stored_player[i].artwork_element != artwork_element)
7968 stored_player[i].Frame = 0;
7971 stored_player[i].artwork_element = artwork_element;
7973 SetPlayerWaiting(&stored_player[i], FALSE);
7975 /* set number of special actions for bored and sleeping animation */
7976 stored_player[i].num_special_action_bored =
7977 get_num_special_action(artwork_element,
7978 ACTION_BORING_1, ACTION_BORING_LAST);
7979 stored_player[i].num_special_action_sleeping =
7980 get_num_special_action(artwork_element,
7981 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7988 /* ---------- CE actions ---------------------------------------------- */
7990 case CA_SET_CE_VALUE:
7992 #if USE_NEW_CUSTOM_VALUE
7993 int last_ce_value = CustomValue[x][y];
7995 CustomValue[x][y] = action_arg_number_new;
7997 if (CustomValue[x][y] != last_ce_value)
7999 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8000 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8002 if (CustomValue[x][y] == 0)
8004 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8005 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8013 case CA_SET_CE_SCORE:
8015 #if USE_NEW_CUSTOM_VALUE
8016 int last_ce_score = ei->collect_score;
8018 ei->collect_score = action_arg_number_new;
8020 if (ei->collect_score != last_ce_score)
8022 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8023 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8025 if (ei->collect_score == 0)
8029 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8030 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8033 This is a very special case that seems to be a mixture between
8034 CheckElementChange() and CheckTriggeredElementChange(): while
8035 the first one only affects single elements that are triggered
8036 directly, the second one affects multiple elements in the playfield
8037 that are triggered indirectly by another element. This is a third
8038 case: Changing the CE score always affects multiple identical CEs,
8039 so every affected CE must be checked, not only the single CE for
8040 which the CE score was changed in the first place (as every instance
8041 of that CE shares the same CE score, and therefore also can change)!
8043 SCAN_PLAYFIELD(xx, yy)
8045 if (Feld[xx][yy] == element)
8046 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8047 CE_SCORE_GETS_ZERO);
8056 /* ---------- engine actions ------------------------------------------ */
8058 case CA_SET_ENGINE_SCAN_MODE:
8060 InitPlayfieldScanMode(action_arg);
8070 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8072 int old_element = Feld[x][y];
8073 int new_element = get_element_from_group_element(element);
8074 int previous_move_direction = MovDir[x][y];
8075 #if USE_NEW_CUSTOM_VALUE
8076 int last_ce_value = CustomValue[x][y];
8078 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8079 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8080 boolean add_player_onto_element = (new_element_is_player &&
8081 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8082 /* this breaks SnakeBite when a snake is
8083 halfway through a door that closes */
8084 /* NOW FIXED AT LEVEL INIT IN files.c */
8085 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8087 IS_WALKABLE(old_element));
8090 /* check if element under the player changes from accessible to unaccessible
8091 (needed for special case of dropping element which then changes) */
8092 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8093 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8101 if (!add_player_onto_element)
8103 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8104 RemoveMovingField(x, y);
8108 Feld[x][y] = new_element;
8110 #if !USE_GFX_RESET_GFX_ANIMATION
8111 ResetGfxAnimation(x, y);
8112 ResetRandomAnimationValue(x, y);
8115 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8116 MovDir[x][y] = previous_move_direction;
8118 #if USE_NEW_CUSTOM_VALUE
8119 if (element_info[new_element].use_last_ce_value)
8120 CustomValue[x][y] = last_ce_value;
8123 InitField_WithBug1(x, y, FALSE);
8125 new_element = Feld[x][y]; /* element may have changed */
8127 #if USE_GFX_RESET_GFX_ANIMATION
8128 ResetGfxAnimation(x, y);
8129 ResetRandomAnimationValue(x, y);
8132 DrawLevelField(x, y);
8134 if (GFX_CRUMBLED(new_element))
8135 DrawLevelFieldCrumbledSandNeighbours(x, y);
8139 /* check if element under the player changes from accessible to unaccessible
8140 (needed for special case of dropping element which then changes) */
8141 /* (must be checked after creating new element for walkable group elements) */
8142 #if USE_FIX_KILLED_BY_NON_WALKABLE
8143 if (IS_PLAYER(x, y) && !player_explosion_protected &&
8144 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8151 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8152 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8161 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8162 if (new_element_is_player)
8163 RelocatePlayer(x, y, new_element);
8166 ChangeCount[x][y]++; /* count number of changes in the same frame */
8168 TestIfBadThingTouchesPlayer(x, y);
8169 TestIfPlayerTouchesCustomElement(x, y);
8170 TestIfElementTouchesCustomElement(x, y);
8173 static void CreateField(int x, int y, int element)
8175 CreateFieldExt(x, y, element, FALSE);
8178 static void CreateElementFromChange(int x, int y, int element)
8180 element = GET_VALID_RUNTIME_ELEMENT(element);
8182 #if USE_STOP_CHANGED_ELEMENTS
8183 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8185 int old_element = Feld[x][y];
8187 /* prevent changed element from moving in same engine frame
8188 unless both old and new element can either fall or move */
8189 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8190 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8195 CreateFieldExt(x, y, element, TRUE);
8198 static boolean ChangeElement(int x, int y, int element, int page)
8200 struct ElementInfo *ei = &element_info[element];
8201 struct ElementChangeInfo *change = &ei->change_page[page];
8202 int ce_value = CustomValue[x][y];
8203 int ce_score = ei->collect_score;
8205 int old_element = Feld[x][y];
8207 /* always use default change event to prevent running into a loop */
8208 if (ChangeEvent[x][y] == -1)
8209 ChangeEvent[x][y] = CE_DELAY;
8211 if (ChangeEvent[x][y] == CE_DELAY)
8213 /* reset actual trigger element, trigger player and action element */
8214 change->actual_trigger_element = EL_EMPTY;
8215 change->actual_trigger_player = EL_PLAYER_1;
8216 change->actual_trigger_side = CH_SIDE_NONE;
8217 change->actual_trigger_ce_value = 0;
8218 change->actual_trigger_ce_score = 0;
8221 /* do not change elements more than a specified maximum number of changes */
8222 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8225 ChangeCount[x][y]++; /* count number of changes in the same frame */
8227 if (change->explode)
8234 if (change->use_target_content)
8236 boolean complete_replace = TRUE;
8237 boolean can_replace[3][3];
8240 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8243 boolean is_walkable;
8244 boolean is_diggable;
8245 boolean is_collectible;
8246 boolean is_removable;
8247 boolean is_destructible;
8248 int ex = x + xx - 1;
8249 int ey = y + yy - 1;
8250 int content_element = change->target_content.e[xx][yy];
8253 can_replace[xx][yy] = TRUE;
8255 if (ex == x && ey == y) /* do not check changing element itself */
8258 if (content_element == EL_EMPTY_SPACE)
8260 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8265 if (!IN_LEV_FIELD(ex, ey))
8267 can_replace[xx][yy] = FALSE;
8268 complete_replace = FALSE;
8275 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8276 e = MovingOrBlocked2Element(ex, ey);
8278 is_empty = (IS_FREE(ex, ey) ||
8279 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8281 is_walkable = (is_empty || IS_WALKABLE(e));
8282 is_diggable = (is_empty || IS_DIGGABLE(e));
8283 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8284 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8285 is_removable = (is_diggable || is_collectible);
8287 can_replace[xx][yy] =
8288 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8289 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8290 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8291 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8292 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8293 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8294 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8296 if (!can_replace[xx][yy])
8297 complete_replace = FALSE;
8300 if (!change->only_if_complete || complete_replace)
8302 boolean something_has_changed = FALSE;
8304 if (change->only_if_complete && change->use_random_replace &&
8305 RND(100) < change->random_percentage)
8308 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8310 int ex = x + xx - 1;
8311 int ey = y + yy - 1;
8312 int content_element;
8314 if (can_replace[xx][yy] && (!change->use_random_replace ||
8315 RND(100) < change->random_percentage))
8317 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8318 RemoveMovingField(ex, ey);
8320 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8322 content_element = change->target_content.e[xx][yy];
8323 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8324 ce_value, ce_score);
8326 CreateElementFromChange(ex, ey, target_element);
8328 something_has_changed = TRUE;
8330 /* for symmetry reasons, freeze newly created border elements */
8331 if (ex != x || ey != y)
8332 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8336 if (something_has_changed)
8338 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8339 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8345 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
8346 ce_value, ce_score);
8348 if (element == EL_DIAGONAL_GROWING ||
8349 element == EL_DIAGONAL_SHRINKING)
8351 target_element = Store[x][y];
8353 Store[x][y] = EL_EMPTY;
8356 CreateElementFromChange(x, y, target_element);
8358 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8359 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8362 /* this uses direct change before indirect change */
8363 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8368 #if USE_NEW_DELAYED_ACTION
8370 static void HandleElementChange(int x, int y, int page)
8372 int element = MovingOrBlocked2Element(x, y);
8373 struct ElementInfo *ei = &element_info[element];
8374 struct ElementChangeInfo *change = &ei->change_page[page];
8377 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8378 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8381 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8382 x, y, element, element_info[element].token_name);
8383 printf("HandleElementChange(): This should never happen!\n");
8388 /* this can happen with classic bombs on walkable, changing elements */
8389 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8392 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8393 ChangeDelay[x][y] = 0;
8399 if (ChangeDelay[x][y] == 0) /* initialize element change */
8401 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8403 if (change->can_change)
8405 ResetGfxAnimation(x, y);
8406 ResetRandomAnimationValue(x, y);
8408 if (change->pre_change_function)
8409 change->pre_change_function(x, y);
8413 ChangeDelay[x][y]--;
8415 if (ChangeDelay[x][y] != 0) /* continue element change */
8417 if (change->can_change)
8419 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8421 if (IS_ANIMATED(graphic))
8422 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8424 if (change->change_function)
8425 change->change_function(x, y);
8428 else /* finish element change */
8430 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8432 page = ChangePage[x][y];
8433 ChangePage[x][y] = -1;
8435 change = &ei->change_page[page];
8438 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8440 ChangeDelay[x][y] = 1; /* try change after next move step */
8441 ChangePage[x][y] = page; /* remember page to use for change */
8446 if (change->can_change)
8448 if (ChangeElement(x, y, element, page))
8450 if (change->post_change_function)
8451 change->post_change_function(x, y);
8455 if (change->has_action)
8456 ExecuteCustomElementAction(x, y, element, page);
8462 static void HandleElementChange(int x, int y, int page)
8464 int element = MovingOrBlocked2Element(x, y);
8465 struct ElementInfo *ei = &element_info[element];
8466 struct ElementChangeInfo *change = &ei->change_page[page];
8469 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8472 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8473 x, y, element, element_info[element].token_name);
8474 printf("HandleElementChange(): This should never happen!\n");
8479 /* this can happen with classic bombs on walkable, changing elements */
8480 if (!CAN_CHANGE(element))
8483 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8484 ChangeDelay[x][y] = 0;
8490 if (ChangeDelay[x][y] == 0) /* initialize element change */
8492 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8494 ResetGfxAnimation(x, y);
8495 ResetRandomAnimationValue(x, y);
8497 if (change->pre_change_function)
8498 change->pre_change_function(x, y);
8501 ChangeDelay[x][y]--;
8503 if (ChangeDelay[x][y] != 0) /* continue element change */
8505 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8507 if (IS_ANIMATED(graphic))
8508 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8510 if (change->change_function)
8511 change->change_function(x, y);
8513 else /* finish element change */
8515 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8517 page = ChangePage[x][y];
8518 ChangePage[x][y] = -1;
8520 change = &ei->change_page[page];
8523 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8525 ChangeDelay[x][y] = 1; /* try change after next move step */
8526 ChangePage[x][y] = page; /* remember page to use for change */
8531 if (ChangeElement(x, y, element, page))
8533 if (change->post_change_function)
8534 change->post_change_function(x, y);
8541 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8542 int trigger_element,
8548 boolean change_done_any = FALSE;
8549 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8552 if (!(trigger_events[trigger_element][trigger_event]))
8555 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8557 int element = EL_CUSTOM_START + i;
8558 boolean change_done = FALSE;
8561 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8562 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8565 for (p = 0; p < element_info[element].num_change_pages; p++)
8567 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8569 if (change->can_change_or_has_action &&
8570 change->has_event[trigger_event] &&
8571 change->trigger_side & trigger_side &&
8572 change->trigger_player & trigger_player &&
8573 change->trigger_page & trigger_page_bits &&
8574 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8576 change->actual_trigger_element = trigger_element;
8577 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8578 change->actual_trigger_side = trigger_side;
8579 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8580 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8582 if ((change->can_change && !change_done) || change->has_action)
8586 SCAN_PLAYFIELD(x, y)
8588 if (Feld[x][y] == element)
8590 if (change->can_change && !change_done)
8592 ChangeDelay[x][y] = 1;
8593 ChangeEvent[x][y] = trigger_event;
8595 HandleElementChange(x, y, p);
8597 #if USE_NEW_DELAYED_ACTION
8598 else if (change->has_action)
8600 ExecuteCustomElementAction(x, y, element, p);
8601 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8604 if (change->has_action)
8606 ExecuteCustomElementAction(x, y, element, p);
8607 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8613 if (change->can_change)
8616 change_done_any = TRUE;
8623 return change_done_any;
8626 static boolean CheckElementChangeExt(int x, int y,
8628 int trigger_element,
8633 boolean change_done = FALSE;
8636 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8637 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8640 if (Feld[x][y] == EL_BLOCKED)
8642 Blocked2Moving(x, y, &x, &y);
8643 element = Feld[x][y];
8647 /* check if element has already changed */
8648 if (Feld[x][y] != element)
8651 /* check if element has already changed or is about to change after moving */
8652 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8653 Feld[x][y] != element) ||
8655 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8656 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8657 ChangePage[x][y] != -1)))
8661 for (p = 0; p < element_info[element].num_change_pages; p++)
8663 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8665 /* check trigger element for all events where the element that is checked
8666 for changing interacts with a directly adjacent element -- this is
8667 different to element changes that affect other elements to change on the
8668 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
8669 boolean check_trigger_element =
8670 (trigger_event == CE_TOUCHING_X ||
8671 trigger_event == CE_HITTING_X ||
8672 trigger_event == CE_HIT_BY_X ||
8674 /* this one was forgotten until 3.2.3 */
8675 trigger_event == CE_DIGGING_X);
8678 if (change->can_change_or_has_action &&
8679 change->has_event[trigger_event] &&
8680 change->trigger_side & trigger_side &&
8681 change->trigger_player & trigger_player &&
8682 (!check_trigger_element ||
8683 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8685 change->actual_trigger_element = trigger_element;
8686 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8687 change->actual_trigger_side = trigger_side;
8688 change->actual_trigger_ce_value = CustomValue[x][y];
8689 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8691 /* special case: trigger element not at (x,y) position for some events */
8692 if (check_trigger_element)
8704 { 0, 0 }, { 0, 0 }, { 0, 0 },
8708 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8709 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8711 change->actual_trigger_ce_value = CustomValue[xx][yy];
8712 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8715 if (change->can_change && !change_done)
8717 ChangeDelay[x][y] = 1;
8718 ChangeEvent[x][y] = trigger_event;
8720 HandleElementChange(x, y, p);
8724 #if USE_NEW_DELAYED_ACTION
8725 else if (change->has_action)
8727 ExecuteCustomElementAction(x, y, element, p);
8728 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8731 if (change->has_action)
8733 ExecuteCustomElementAction(x, y, element, p);
8734 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8743 static void PlayPlayerSound(struct PlayerInfo *player)
8745 int jx = player->jx, jy = player->jy;
8746 int sound_element = player->artwork_element;
8747 int last_action = player->last_action_waiting;
8748 int action = player->action_waiting;
8750 if (player->is_waiting)
8752 if (action != last_action)
8753 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8755 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8759 if (action != last_action)
8760 StopSound(element_info[sound_element].sound[last_action]);
8762 if (last_action == ACTION_SLEEPING)
8763 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8767 static void PlayAllPlayersSound()
8771 for (i = 0; i < MAX_PLAYERS; i++)
8772 if (stored_player[i].active)
8773 PlayPlayerSound(&stored_player[i]);
8776 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8778 boolean last_waiting = player->is_waiting;
8779 int move_dir = player->MovDir;
8781 player->dir_waiting = move_dir;
8782 player->last_action_waiting = player->action_waiting;
8786 if (!last_waiting) /* not waiting -> waiting */
8788 player->is_waiting = TRUE;
8790 player->frame_counter_bored =
8792 game.player_boring_delay_fixed +
8793 GetSimpleRandom(game.player_boring_delay_random);
8794 player->frame_counter_sleeping =
8796 game.player_sleeping_delay_fixed +
8797 GetSimpleRandom(game.player_sleeping_delay_random);
8799 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
8802 if (game.player_sleeping_delay_fixed +
8803 game.player_sleeping_delay_random > 0 &&
8804 player->anim_delay_counter == 0 &&
8805 player->post_delay_counter == 0 &&
8806 FrameCounter >= player->frame_counter_sleeping)
8807 player->is_sleeping = TRUE;
8808 else if (game.player_boring_delay_fixed +
8809 game.player_boring_delay_random > 0 &&
8810 FrameCounter >= player->frame_counter_bored)
8811 player->is_bored = TRUE;
8813 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8814 player->is_bored ? ACTION_BORING :
8817 if (player->is_sleeping && player->use_murphy)
8819 /* special case for sleeping Murphy when leaning against non-free tile */
8821 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
8822 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
8823 !IS_MOVING(player->jx - 1, player->jy)))
8825 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
8826 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
8827 !IS_MOVING(player->jx + 1, player->jy)))
8828 move_dir = MV_RIGHT;
8830 player->is_sleeping = FALSE;
8832 player->dir_waiting = move_dir;
8835 if (player->is_sleeping)
8837 if (player->num_special_action_sleeping > 0)
8839 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8841 int last_special_action = player->special_action_sleeping;
8842 int num_special_action = player->num_special_action_sleeping;
8843 int special_action =
8844 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8845 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8846 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8847 last_special_action + 1 : ACTION_SLEEPING);
8848 int special_graphic =
8849 el_act_dir2img(player->artwork_element, special_action, move_dir);
8851 player->anim_delay_counter =
8852 graphic_info[special_graphic].anim_delay_fixed +
8853 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8854 player->post_delay_counter =
8855 graphic_info[special_graphic].post_delay_fixed +
8856 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8858 player->special_action_sleeping = special_action;
8861 if (player->anim_delay_counter > 0)
8863 player->action_waiting = player->special_action_sleeping;
8864 player->anim_delay_counter--;
8866 else if (player->post_delay_counter > 0)
8868 player->post_delay_counter--;
8872 else if (player->is_bored)
8874 if (player->num_special_action_bored > 0)
8876 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8878 int special_action =
8879 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
8880 int special_graphic =
8881 el_act_dir2img(player->artwork_element, special_action, move_dir);
8883 player->anim_delay_counter =
8884 graphic_info[special_graphic].anim_delay_fixed +
8885 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8886 player->post_delay_counter =
8887 graphic_info[special_graphic].post_delay_fixed +
8888 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8890 player->special_action_bored = special_action;
8893 if (player->anim_delay_counter > 0)
8895 player->action_waiting = player->special_action_bored;
8896 player->anim_delay_counter--;
8898 else if (player->post_delay_counter > 0)
8900 player->post_delay_counter--;
8905 else if (last_waiting) /* waiting -> not waiting */
8907 player->is_waiting = FALSE;
8908 player->is_bored = FALSE;
8909 player->is_sleeping = FALSE;
8911 player->frame_counter_bored = -1;
8912 player->frame_counter_sleeping = -1;
8914 player->anim_delay_counter = 0;
8915 player->post_delay_counter = 0;
8917 player->dir_waiting = player->MovDir;
8918 player->action_waiting = ACTION_DEFAULT;
8920 player->special_action_bored = ACTION_DEFAULT;
8921 player->special_action_sleeping = ACTION_DEFAULT;
8925 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8927 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8928 int left = player_action & JOY_LEFT;
8929 int right = player_action & JOY_RIGHT;
8930 int up = player_action & JOY_UP;
8931 int down = player_action & JOY_DOWN;
8932 int button1 = player_action & JOY_BUTTON_1;
8933 int button2 = player_action & JOY_BUTTON_2;
8934 int dx = (left ? -1 : right ? 1 : 0);
8935 int dy = (up ? -1 : down ? 1 : 0);
8937 if (!player->active || tape.pausing)
8943 snapped = SnapField(player, dx, dy);
8947 dropped = DropElement(player);
8949 moved = MovePlayer(player, dx, dy);
8952 if (tape.single_step && tape.recording && !tape.pausing)
8954 if (button1 || (dropped && !moved))
8956 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8957 SnapField(player, 0, 0); /* stop snapping */
8961 SetPlayerWaiting(player, FALSE);
8963 return player_action;
8967 /* no actions for this player (no input at player's configured device) */
8969 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8970 SnapField(player, 0, 0);
8971 CheckGravityMovementWhenNotMoving(player);
8973 if (player->MovPos == 0)
8974 SetPlayerWaiting(player, TRUE);
8976 if (player->MovPos == 0) /* needed for tape.playing */
8977 player->is_moving = FALSE;
8979 player->is_dropping = FALSE;
8980 player->is_dropping_pressed = FALSE;
8981 player->drop_pressed_delay = 0;
8987 static void CheckLevelTime()
8991 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8993 if (level.native_em_level->lev->home == 0) /* all players at home */
8995 PlayerWins(local_player);
8997 AllPlayersGone = TRUE;
8999 level.native_em_level->lev->home = -1;
9002 if (level.native_em_level->ply[0]->alive == 0 &&
9003 level.native_em_level->ply[1]->alive == 0 &&
9004 level.native_em_level->ply[2]->alive == 0 &&
9005 level.native_em_level->ply[3]->alive == 0) /* all dead */
9006 AllPlayersGone = TRUE;
9009 if (TimeFrames >= FRAMES_PER_SECOND)
9014 for (i = 0; i < MAX_PLAYERS; i++)
9016 struct PlayerInfo *player = &stored_player[i];
9018 if (SHIELD_ON(player))
9020 player->shield_normal_time_left--;
9022 if (player->shield_deadly_time_left > 0)
9023 player->shield_deadly_time_left--;
9027 if (!local_player->LevelSolved && !level.use_step_counter)
9035 if (TimeLeft <= 10 && setup.time_limit)
9036 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9038 DrawGameValue_Time(TimeLeft);
9040 if (!TimeLeft && setup.time_limit)
9042 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9043 level.native_em_level->lev->killed_out_of_time = TRUE;
9045 for (i = 0; i < MAX_PLAYERS; i++)
9046 KillPlayer(&stored_player[i]);
9049 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9050 DrawGameValue_Time(TimePlayed);
9052 level.native_em_level->lev->time =
9053 (level.time == 0 ? TimePlayed : TimeLeft);
9056 if (tape.recording || tape.playing)
9057 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9061 void AdvanceFrameAndPlayerCounters(int player_nr)
9065 /* advance frame counters (global frame counter and time frame counter) */
9069 /* advance player counters (counters for move delay, move animation etc.) */
9070 for (i = 0; i < MAX_PLAYERS; i++)
9072 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9073 int move_delay_value = stored_player[i].move_delay_value;
9074 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9076 if (!advance_player_counters) /* not all players may be affected */
9079 #if USE_NEW_PLAYER_ANIM
9080 if (move_frames == 0) /* less than one move per game frame */
9082 int stepsize = TILEX / move_delay_value;
9083 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9084 int count = (stored_player[i].is_moving ?
9085 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9087 if (count % delay == 0)
9092 stored_player[i].Frame += move_frames;
9094 if (stored_player[i].MovPos != 0)
9095 stored_player[i].StepFrame += move_frames;
9097 if (stored_player[i].move_delay > 0)
9098 stored_player[i].move_delay--;
9100 /* due to bugs in previous versions, counter must count up, not down */
9101 if (stored_player[i].push_delay != -1)
9102 stored_player[i].push_delay++;
9104 if (stored_player[i].drop_delay > 0)
9105 stored_player[i].drop_delay--;
9107 if (stored_player[i].is_dropping_pressed)
9108 stored_player[i].drop_pressed_delay++;
9112 void StartGameActions(boolean init_network_game, boolean record_tape,
9115 unsigned long new_random_seed = InitRND(random_seed);
9118 TapeStartRecording(new_random_seed);
9120 #if defined(NETWORK_AVALIABLE)
9121 if (init_network_game)
9123 SendToServer_StartPlaying();
9134 static unsigned long game_frame_delay = 0;
9135 unsigned long game_frame_delay_value;
9136 byte *recorded_player_action;
9137 byte summarized_player_action = 0;
9138 byte tape_action[MAX_PLAYERS];
9141 if (game.restart_level)
9142 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9144 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9146 if (level.native_em_level->lev->home == 0) /* all players at home */
9148 PlayerWins(local_player);
9150 AllPlayersGone = TRUE;
9152 level.native_em_level->lev->home = -1;
9155 if (level.native_em_level->ply[0]->alive == 0 &&
9156 level.native_em_level->ply[1]->alive == 0 &&
9157 level.native_em_level->ply[2]->alive == 0 &&
9158 level.native_em_level->ply[3]->alive == 0) /* all dead */
9159 AllPlayersGone = TRUE;
9162 if (local_player->LevelSolved)
9165 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9168 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9171 game_frame_delay_value =
9172 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9174 if (tape.playing && tape.warp_forward && !tape.pausing)
9175 game_frame_delay_value = 0;
9177 /* ---------- main game synchronization point ---------- */
9179 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9181 if (network_playing && !network_player_action_received)
9183 /* try to get network player actions in time */
9185 #if defined(NETWORK_AVALIABLE)
9186 /* last chance to get network player actions without main loop delay */
9190 /* game was quit by network peer */
9191 if (game_status != GAME_MODE_PLAYING)
9194 if (!network_player_action_received)
9195 return; /* failed to get network player actions in time */
9197 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9203 /* at this point we know that we really continue executing the game */
9205 network_player_action_received = FALSE;
9207 /* when playing tape, read previously recorded player input from tape data */
9208 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9211 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9216 if (tape.set_centered_player)
9218 game.centered_player_nr_next = tape.centered_player_nr_next;
9219 game.set_centered_player = TRUE;
9222 for (i = 0; i < MAX_PLAYERS; i++)
9224 summarized_player_action |= stored_player[i].action;
9226 if (!network_playing)
9227 stored_player[i].effective_action = stored_player[i].action;
9230 #if defined(NETWORK_AVALIABLE)
9231 if (network_playing)
9232 SendToServer_MovePlayer(summarized_player_action);
9235 if (!options.network && !setup.team_mode)
9236 local_player->effective_action = summarized_player_action;
9238 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9240 for (i = 0; i < MAX_PLAYERS; i++)
9241 stored_player[i].effective_action =
9242 (i == game.centered_player_nr ? summarized_player_action : 0);
9245 if (recorded_player_action != NULL)
9246 for (i = 0; i < MAX_PLAYERS; i++)
9247 stored_player[i].effective_action = recorded_player_action[i];
9249 for (i = 0; i < MAX_PLAYERS; i++)
9251 tape_action[i] = stored_player[i].effective_action;
9253 /* (this can only happen in the R'n'D game engine) */
9254 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9255 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9258 /* only record actions from input devices, but not programmed actions */
9260 TapeRecordAction(tape_action);
9262 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9264 GameActions_EM_Main();
9272 void GameActions_EM_Main()
9274 byte effective_action[MAX_PLAYERS];
9275 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9278 for (i = 0; i < MAX_PLAYERS; i++)
9279 effective_action[i] = stored_player[i].effective_action;
9281 GameActions_EM(effective_action, warp_mode);
9285 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9288 void GameActions_RND()
9290 int magic_wall_x = 0, magic_wall_y = 0;
9291 int i, x, y, element, graphic;
9293 InitPlayfieldScanModeVars();
9295 #if USE_ONE_MORE_CHANGE_PER_FRAME
9296 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9298 SCAN_PLAYFIELD(x, y)
9300 ChangeCount[x][y] = 0;
9301 ChangeEvent[x][y] = -1;
9306 if (game.set_centered_player)
9308 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9310 /* switching to "all players" only possible if all players fit to screen */
9311 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9313 game.centered_player_nr_next = game.centered_player_nr;
9314 game.set_centered_player = FALSE;
9317 /* do not switch focus to non-existing (or non-active) player */
9318 if (game.centered_player_nr_next >= 0 &&
9319 !stored_player[game.centered_player_nr_next].active)
9321 game.centered_player_nr_next = game.centered_player_nr;
9322 game.set_centered_player = FALSE;
9326 if (game.set_centered_player &&
9327 ScreenMovPos == 0) /* screen currently aligned at tile position */
9331 if (game.centered_player_nr_next == -1)
9333 setScreenCenteredToAllPlayers(&sx, &sy);
9337 sx = stored_player[game.centered_player_nr_next].jx;
9338 sy = stored_player[game.centered_player_nr_next].jy;
9341 game.centered_player_nr = game.centered_player_nr_next;
9342 game.set_centered_player = FALSE;
9344 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9345 DrawGameDoorValues();
9348 for (i = 0; i < MAX_PLAYERS; i++)
9350 int actual_player_action = stored_player[i].effective_action;
9353 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9354 - rnd_equinox_tetrachloride 048
9355 - rnd_equinox_tetrachloride_ii 096
9356 - rnd_emanuel_schmieg 002
9357 - doctor_sloan_ww 001, 020
9359 if (stored_player[i].MovPos == 0)
9360 CheckGravityMovement(&stored_player[i]);
9363 /* overwrite programmed action with tape action */
9364 if (stored_player[i].programmed_action)
9365 actual_player_action = stored_player[i].programmed_action;
9367 PlayerActions(&stored_player[i], actual_player_action);
9369 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9372 ScrollScreen(NULL, SCROLL_GO_ON);
9374 /* for backwards compatibility, the following code emulates a fixed bug that
9375 occured when pushing elements (causing elements that just made their last
9376 pushing step to already (if possible) make their first falling step in the
9377 same game frame, which is bad); this code is also needed to use the famous
9378 "spring push bug" which is used in older levels and might be wanted to be
9379 used also in newer levels, but in this case the buggy pushing code is only
9380 affecting the "spring" element and no other elements */
9382 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9384 for (i = 0; i < MAX_PLAYERS; i++)
9386 struct PlayerInfo *player = &stored_player[i];
9390 if (player->active && player->is_pushing && player->is_moving &&
9392 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9393 Feld[x][y] == EL_SPRING))
9395 ContinueMoving(x, y);
9397 /* continue moving after pushing (this is actually a bug) */
9398 if (!IS_MOVING(x, y))
9406 SCAN_PLAYFIELD(x, y)
9408 ChangeCount[x][y] = 0;
9409 ChangeEvent[x][y] = -1;
9411 /* this must be handled before main playfield loop */
9412 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9415 if (MovDelay[x][y] <= 0)
9419 #if USE_NEW_SNAP_DELAY
9420 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9423 if (MovDelay[x][y] <= 0)
9426 DrawLevelField(x, y);
9428 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9434 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9436 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9437 printf("GameActions(): This should never happen!\n");
9439 ChangePage[x][y] = -1;
9444 if (WasJustMoving[x][y] > 0)
9445 WasJustMoving[x][y]--;
9446 if (WasJustFalling[x][y] > 0)
9447 WasJustFalling[x][y]--;
9448 if (CheckCollision[x][y] > 0)
9449 CheckCollision[x][y]--;
9450 if (CheckImpact[x][y] > 0)
9451 CheckImpact[x][y]--;
9455 /* reset finished pushing action (not done in ContinueMoving() to allow
9456 continuous pushing animation for elements with zero push delay) */
9457 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9459 ResetGfxAnimation(x, y);
9460 DrawLevelField(x, y);
9464 if (IS_BLOCKED(x, y))
9468 Blocked2Moving(x, y, &oldx, &oldy);
9469 if (!IS_MOVING(oldx, oldy))
9471 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9472 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9473 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9474 printf("GameActions(): This should never happen!\n");
9480 SCAN_PLAYFIELD(x, y)
9482 element = Feld[x][y];
9483 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9485 ResetGfxFrame(x, y, TRUE);
9487 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9488 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9489 ResetRandomAnimationValue(x, y);
9491 SetRandomAnimationValue(x, y);
9493 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9495 if (IS_INACTIVE(element))
9497 if (IS_ANIMATED(graphic))
9498 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9503 /* this may take place after moving, so 'element' may have changed */
9504 if (IS_CHANGING(x, y) &&
9505 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9507 int page = element_info[element].event_page_nr[CE_DELAY];
9510 HandleElementChange(x, y, page);
9512 if (CAN_CHANGE(element))
9513 HandleElementChange(x, y, page);
9515 if (HAS_ACTION(element))
9516 ExecuteCustomElementAction(x, y, element, page);
9519 element = Feld[x][y];
9520 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9523 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9527 element = Feld[x][y];
9528 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9530 if (IS_ANIMATED(graphic) &&
9533 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9535 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9536 DrawTwinkleOnField(x, y);
9538 else if ((element == EL_ACID ||
9539 element == EL_EXIT_OPEN ||
9540 element == EL_SP_EXIT_OPEN ||
9541 element == EL_SP_TERMINAL ||
9542 element == EL_SP_TERMINAL_ACTIVE ||
9543 element == EL_EXTRA_TIME ||
9544 element == EL_SHIELD_NORMAL ||
9545 element == EL_SHIELD_DEADLY) &&
9546 IS_ANIMATED(graphic))
9547 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9548 else if (IS_MOVING(x, y))
9549 ContinueMoving(x, y);
9550 else if (IS_ACTIVE_BOMB(element))
9551 CheckDynamite(x, y);
9552 else if (element == EL_AMOEBA_GROWING)
9553 AmoebeWaechst(x, y);
9554 else if (element == EL_AMOEBA_SHRINKING)
9555 AmoebaDisappearing(x, y);
9557 #if !USE_NEW_AMOEBA_CODE
9558 else if (IS_AMOEBALIVE(element))
9559 AmoebeAbleger(x, y);
9562 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9564 else if (element == EL_EXIT_CLOSED)
9566 else if (element == EL_SP_EXIT_CLOSED)
9568 else if (element == EL_EXPANDABLE_WALL_GROWING)
9570 else if (element == EL_EXPANDABLE_WALL ||
9571 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9572 element == EL_EXPANDABLE_WALL_VERTICAL ||
9573 element == EL_EXPANDABLE_WALL_ANY ||
9574 element == EL_BD_EXPANDABLE_WALL)
9576 else if (element == EL_FLAMES)
9577 CheckForDragon(x, y);
9578 else if (element == EL_EXPLOSION)
9579 ; /* drawing of correct explosion animation is handled separately */
9580 else if (element == EL_ELEMENT_SNAPPING ||
9581 element == EL_DIAGONAL_SHRINKING ||
9582 element == EL_DIAGONAL_GROWING)
9584 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9586 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9588 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9589 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9591 if (IS_BELT_ACTIVE(element))
9592 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9594 if (game.magic_wall_active)
9596 int jx = local_player->jx, jy = local_player->jy;
9598 /* play the element sound at the position nearest to the player */
9599 if ((element == EL_MAGIC_WALL_FULL ||
9600 element == EL_MAGIC_WALL_ACTIVE ||
9601 element == EL_MAGIC_WALL_EMPTYING ||
9602 element == EL_BD_MAGIC_WALL_FULL ||
9603 element == EL_BD_MAGIC_WALL_ACTIVE ||
9604 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9605 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9613 #if USE_NEW_AMOEBA_CODE
9614 /* new experimental amoeba growth stuff */
9615 if (!(FrameCounter % 8))
9617 static unsigned long random = 1684108901;
9619 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9621 x = RND(lev_fieldx);
9622 y = RND(lev_fieldy);
9623 element = Feld[x][y];
9625 if (!IS_PLAYER(x,y) &&
9626 (element == EL_EMPTY ||
9627 CAN_GROW_INTO(element) ||
9628 element == EL_QUICKSAND_EMPTY ||
9629 element == EL_ACID_SPLASH_LEFT ||
9630 element == EL_ACID_SPLASH_RIGHT))
9632 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9633 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9634 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9635 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9636 Feld[x][y] = EL_AMOEBA_DROP;
9639 random = random * 129 + 1;
9645 if (game.explosions_delayed)
9648 game.explosions_delayed = FALSE;
9650 SCAN_PLAYFIELD(x, y)
9652 element = Feld[x][y];
9654 if (ExplodeField[x][y])
9655 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9656 else if (element == EL_EXPLOSION)
9657 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9659 ExplodeField[x][y] = EX_TYPE_NONE;
9662 game.explosions_delayed = TRUE;
9665 if (game.magic_wall_active)
9667 if (!(game.magic_wall_time_left % 4))
9669 int element = Feld[magic_wall_x][magic_wall_y];
9671 if (element == EL_BD_MAGIC_WALL_FULL ||
9672 element == EL_BD_MAGIC_WALL_ACTIVE ||
9673 element == EL_BD_MAGIC_WALL_EMPTYING)
9674 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9676 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9679 if (game.magic_wall_time_left > 0)
9681 game.magic_wall_time_left--;
9682 if (!game.magic_wall_time_left)
9684 SCAN_PLAYFIELD(x, y)
9686 element = Feld[x][y];
9688 if (element == EL_MAGIC_WALL_ACTIVE ||
9689 element == EL_MAGIC_WALL_FULL)
9691 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9692 DrawLevelField(x, y);
9694 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9695 element == EL_BD_MAGIC_WALL_FULL)
9697 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9698 DrawLevelField(x, y);
9702 game.magic_wall_active = FALSE;
9707 if (game.light_time_left > 0)
9709 game.light_time_left--;
9711 if (game.light_time_left == 0)
9712 RedrawAllLightSwitchesAndInvisibleElements();
9715 if (game.timegate_time_left > 0)
9717 game.timegate_time_left--;
9719 if (game.timegate_time_left == 0)
9720 CloseAllOpenTimegates();
9723 if (game.lenses_time_left > 0)
9725 game.lenses_time_left--;
9727 if (game.lenses_time_left == 0)
9728 RedrawAllInvisibleElementsForLenses();
9731 if (game.magnify_time_left > 0)
9733 game.magnify_time_left--;
9735 if (game.magnify_time_left == 0)
9736 RedrawAllInvisibleElementsForMagnifier();
9739 for (i = 0; i < MAX_PLAYERS; i++)
9741 struct PlayerInfo *player = &stored_player[i];
9743 if (SHIELD_ON(player))
9745 if (player->shield_deadly_time_left)
9746 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9747 else if (player->shield_normal_time_left)
9748 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9755 PlayAllPlayersSound();
9757 if (options.debug) /* calculate frames per second */
9759 static unsigned long fps_counter = 0;
9760 static int fps_frames = 0;
9761 unsigned long fps_delay_ms = Counter() - fps_counter;
9765 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9767 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9770 fps_counter = Counter();
9773 redraw_mask |= REDRAW_FPS;
9776 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9778 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9780 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9782 local_player->show_envelope = 0;
9785 /* use random number generator in every frame to make it less predictable */
9786 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9790 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9792 int min_x = x, min_y = y, max_x = x, max_y = y;
9795 for (i = 0; i < MAX_PLAYERS; i++)
9797 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9799 if (!stored_player[i].active || &stored_player[i] == player)
9802 min_x = MIN(min_x, jx);
9803 min_y = MIN(min_y, jy);
9804 max_x = MAX(max_x, jx);
9805 max_y = MAX(max_y, jy);
9808 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9811 static boolean AllPlayersInVisibleScreen()
9815 for (i = 0; i < MAX_PLAYERS; i++)
9817 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9819 if (!stored_player[i].active)
9822 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9829 void ScrollLevel(int dx, int dy)
9831 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9834 BlitBitmap(drawto_field, drawto_field,
9835 FX + TILEX * (dx == -1) - softscroll_offset,
9836 FY + TILEY * (dy == -1) - softscroll_offset,
9837 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9838 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9839 FX + TILEX * (dx == 1) - softscroll_offset,
9840 FY + TILEY * (dy == 1) - softscroll_offset);
9844 x = (dx == 1 ? BX1 : BX2);
9845 for (y = BY1; y <= BY2; y++)
9846 DrawScreenField(x, y);
9851 y = (dy == 1 ? BY1 : BY2);
9852 for (x = BX1; x <= BX2; x++)
9853 DrawScreenField(x, y);
9856 redraw_mask |= REDRAW_FIELD;
9859 static boolean canFallDown(struct PlayerInfo *player)
9861 int jx = player->jx, jy = player->jy;
9863 return (IN_LEV_FIELD(jx, jy + 1) &&
9864 (IS_FREE(jx, jy + 1) ||
9865 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9866 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9867 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9870 static boolean canPassField(int x, int y, int move_dir)
9872 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9873 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9874 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9877 int element = Feld[x][y];
9879 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9880 !CAN_MOVE(element) &&
9881 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9882 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9883 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9886 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9888 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9889 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9890 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9894 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9895 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9896 (IS_DIGGABLE(Feld[newx][newy]) ||
9897 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9898 canPassField(newx, newy, move_dir)));
9901 static void CheckGravityMovement(struct PlayerInfo *player)
9903 #if USE_PLAYER_GRAVITY
9904 if (player->gravity && !player->programmed_action)
9906 if (game.gravity && !player->programmed_action)
9909 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9910 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9911 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
9912 int jx = player->jx, jy = player->jy;
9913 boolean player_is_moving_to_valid_field =
9914 (!player_is_snapping &&
9915 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9916 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9917 boolean player_can_fall_down = canFallDown(player);
9919 if (player_can_fall_down &&
9920 !player_is_moving_to_valid_field)
9921 player->programmed_action = MV_DOWN;
9925 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9927 return CheckGravityMovement(player);
9929 #if USE_PLAYER_GRAVITY
9930 if (player->gravity && !player->programmed_action)
9932 if (game.gravity && !player->programmed_action)
9935 int jx = player->jx, jy = player->jy;
9936 boolean field_under_player_is_free =
9937 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9938 boolean player_is_standing_on_valid_field =
9939 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9940 (IS_WALKABLE(Feld[jx][jy]) &&
9941 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9943 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9944 player->programmed_action = MV_DOWN;
9950 -----------------------------------------------------------------------------
9951 dx, dy: direction (non-diagonal) to try to move the player to
9952 real_dx, real_dy: direction as read from input device (can be diagonal)
9955 boolean MovePlayerOneStep(struct PlayerInfo *player,
9956 int dx, int dy, int real_dx, int real_dy)
9958 int jx = player->jx, jy = player->jy;
9959 int new_jx = jx + dx, new_jy = jy + dy;
9960 #if !USE_FIXED_DONT_RUN_INTO
9964 boolean player_can_move = !player->cannot_move;
9966 if (!player->active || (!dx && !dy))
9967 return MP_NO_ACTION;
9969 player->MovDir = (dx < 0 ? MV_LEFT :
9972 dy > 0 ? MV_DOWN : MV_NONE);
9974 if (!IN_LEV_FIELD(new_jx, new_jy))
9975 return MP_NO_ACTION;
9977 if (!player_can_move)
9979 if (player->MovPos == 0)
9981 player->is_moving = FALSE;
9982 player->is_digging = FALSE;
9983 player->is_collecting = FALSE;
9984 player->is_snapping = FALSE;
9985 player->is_pushing = FALSE;
9990 if (!options.network && game.centered_player_nr == -1 &&
9991 !AllPlayersInSight(player, new_jx, new_jy))
9992 return MP_NO_ACTION;
9994 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9995 return MP_NO_ACTION;
9998 #if !USE_FIXED_DONT_RUN_INTO
9999 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10001 /* (moved to DigField()) */
10002 if (player_can_move && DONT_RUN_INTO(element))
10004 if (element == EL_ACID && dx == 0 && dy == 1)
10006 SplashAcid(new_jx, new_jy);
10007 Feld[jx][jy] = EL_PLAYER_1;
10008 InitMovingField(jx, jy, MV_DOWN);
10009 Store[jx][jy] = EL_ACID;
10010 ContinueMoving(jx, jy);
10011 BuryPlayer(player);
10014 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10020 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10021 if (can_move != MP_MOVING)
10024 /* check if DigField() has caused relocation of the player */
10025 if (player->jx != jx || player->jy != jy)
10026 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10028 StorePlayer[jx][jy] = 0;
10029 player->last_jx = jx;
10030 player->last_jy = jy;
10031 player->jx = new_jx;
10032 player->jy = new_jy;
10033 StorePlayer[new_jx][new_jy] = player->element_nr;
10035 if (player->move_delay_value_next != -1)
10037 player->move_delay_value = player->move_delay_value_next;
10038 player->move_delay_value_next = -1;
10042 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10044 player->step_counter++;
10046 PlayerVisit[jx][jy] = FrameCounter;
10048 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10049 player->is_moving = TRUE;
10053 /* should better be called in MovePlayer(), but this breaks some tapes */
10054 ScrollPlayer(player, SCROLL_INIT);
10060 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10062 int jx = player->jx, jy = player->jy;
10063 int old_jx = jx, old_jy = jy;
10064 int moved = MP_NO_ACTION;
10066 if (!player->active)
10071 if (player->MovPos == 0)
10073 player->is_moving = FALSE;
10074 player->is_digging = FALSE;
10075 player->is_collecting = FALSE;
10076 player->is_snapping = FALSE;
10077 player->is_pushing = FALSE;
10083 if (player->move_delay > 0)
10086 player->move_delay = -1; /* set to "uninitialized" value */
10088 /* store if player is automatically moved to next field */
10089 player->is_auto_moving = (player->programmed_action != MV_NONE);
10091 /* remove the last programmed player action */
10092 player->programmed_action = 0;
10094 if (player->MovPos)
10096 /* should only happen if pre-1.2 tape recordings are played */
10097 /* this is only for backward compatibility */
10099 int original_move_delay_value = player->move_delay_value;
10102 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10106 /* scroll remaining steps with finest movement resolution */
10107 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10109 while (player->MovPos)
10111 ScrollPlayer(player, SCROLL_GO_ON);
10112 ScrollScreen(NULL, SCROLL_GO_ON);
10114 AdvanceFrameAndPlayerCounters(player->index_nr);
10120 player->move_delay_value = original_move_delay_value;
10123 player->is_active = FALSE;
10125 if (player->last_move_dir & MV_HORIZONTAL)
10127 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10128 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10132 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10133 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10136 #if USE_FIXED_BORDER_RUNNING_GFX
10137 if (!moved && !player->is_active)
10139 player->is_moving = FALSE;
10140 player->is_digging = FALSE;
10141 player->is_collecting = FALSE;
10142 player->is_snapping = FALSE;
10143 player->is_pushing = FALSE;
10151 if (moved & MP_MOVING && !ScreenMovPos &&
10152 (player->index_nr == game.centered_player_nr ||
10153 game.centered_player_nr == -1))
10155 if (moved & MP_MOVING && !ScreenMovPos &&
10156 (player == local_player || !options.network))
10159 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10160 int offset = (setup.scroll_delay ? 3 : 0);
10162 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10164 /* actual player has left the screen -- scroll in that direction */
10165 if (jx != old_jx) /* player has moved horizontally */
10166 scroll_x += (jx - old_jx);
10167 else /* player has moved vertically */
10168 scroll_y += (jy - old_jy);
10172 if (jx != old_jx) /* player has moved horizontally */
10174 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10175 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10176 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10178 /* don't scroll over playfield boundaries */
10179 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10180 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10182 /* don't scroll more than one field at a time */
10183 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10185 /* don't scroll against the player's moving direction */
10186 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10187 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10188 scroll_x = old_scroll_x;
10190 else /* player has moved vertically */
10192 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10193 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10194 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10196 /* don't scroll over playfield boundaries */
10197 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10198 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10200 /* don't scroll more than one field at a time */
10201 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10203 /* don't scroll against the player's moving direction */
10204 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10205 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10206 scroll_y = old_scroll_y;
10210 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10213 if (!options.network && game.centered_player_nr == -1 &&
10214 !AllPlayersInVisibleScreen())
10216 scroll_x = old_scroll_x;
10217 scroll_y = old_scroll_y;
10221 if (!options.network && !AllPlayersInVisibleScreen())
10223 scroll_x = old_scroll_x;
10224 scroll_y = old_scroll_y;
10229 ScrollScreen(player, SCROLL_INIT);
10230 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10235 player->StepFrame = 0;
10237 if (moved & MP_MOVING)
10239 if (old_jx != jx && old_jy == jy)
10240 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10241 else if (old_jx == jx && old_jy != jy)
10242 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10244 DrawLevelField(jx, jy); /* for "crumbled sand" */
10246 player->last_move_dir = player->MovDir;
10247 player->is_moving = TRUE;
10248 player->is_snapping = FALSE;
10249 player->is_switching = FALSE;
10250 player->is_dropping = FALSE;
10251 player->is_dropping_pressed = FALSE;
10252 player->drop_pressed_delay = 0;
10255 /* should better be called here than above, but this breaks some tapes */
10256 ScrollPlayer(player, SCROLL_INIT);
10261 CheckGravityMovementWhenNotMoving(player);
10263 player->is_moving = FALSE;
10265 /* at this point, the player is allowed to move, but cannot move right now
10266 (e.g. because of something blocking the way) -- ensure that the player
10267 is also allowed to move in the next frame (in old versions before 3.1.1,
10268 the player was forced to wait again for eight frames before next try) */
10270 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10271 player->move_delay = 0; /* allow direct movement in the next frame */
10274 if (player->move_delay == -1) /* not yet initialized by DigField() */
10275 player->move_delay = player->move_delay_value;
10277 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10279 TestIfPlayerTouchesBadThing(jx, jy);
10280 TestIfPlayerTouchesCustomElement(jx, jy);
10283 if (!player->active)
10284 RemovePlayer(player);
10289 void ScrollPlayer(struct PlayerInfo *player, int mode)
10291 int jx = player->jx, jy = player->jy;
10292 int last_jx = player->last_jx, last_jy = player->last_jy;
10293 int move_stepsize = TILEX / player->move_delay_value;
10295 #if USE_NEW_PLAYER_SPEED
10296 if (!player->active)
10299 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10302 if (!player->active || player->MovPos == 0)
10306 if (mode == SCROLL_INIT)
10308 player->actual_frame_counter = FrameCounter;
10309 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10311 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10312 Feld[last_jx][last_jy] == EL_EMPTY)
10314 int last_field_block_delay = 0; /* start with no blocking at all */
10315 int block_delay_adjustment = player->block_delay_adjustment;
10317 /* if player blocks last field, add delay for exactly one move */
10318 if (player->block_last_field)
10320 last_field_block_delay += player->move_delay_value;
10322 /* when blocking enabled, prevent moving up despite gravity */
10323 #if USE_PLAYER_GRAVITY
10324 if (player->gravity && player->MovDir == MV_UP)
10325 block_delay_adjustment = -1;
10327 if (game.gravity && player->MovDir == MV_UP)
10328 block_delay_adjustment = -1;
10332 /* add block delay adjustment (also possible when not blocking) */
10333 last_field_block_delay += block_delay_adjustment;
10335 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10336 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10339 #if USE_NEW_PLAYER_SPEED
10340 if (player->MovPos != 0) /* player has not yet reached destination */
10346 else if (!FrameReached(&player->actual_frame_counter, 1))
10349 #if USE_NEW_PLAYER_SPEED
10350 if (player->MovPos != 0)
10352 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10353 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10355 /* before DrawPlayer() to draw correct player graphic for this case */
10356 if (player->MovPos == 0)
10357 CheckGravityMovement(player);
10360 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10361 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10363 /* before DrawPlayer() to draw correct player graphic for this case */
10364 if (player->MovPos == 0)
10365 CheckGravityMovement(player);
10368 if (player->MovPos == 0) /* player reached destination field */
10370 if (player->move_delay_reset_counter > 0)
10372 player->move_delay_reset_counter--;
10374 if (player->move_delay_reset_counter == 0)
10376 /* continue with normal speed after quickly moving through gate */
10377 HALVE_PLAYER_SPEED(player);
10379 /* be able to make the next move without delay */
10380 player->move_delay = 0;
10384 player->last_jx = jx;
10385 player->last_jy = jy;
10387 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10388 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10389 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10391 DrawPlayer(player); /* needed here only to cleanup last field */
10392 RemovePlayer(player);
10394 if (local_player->friends_still_needed == 0 ||
10395 IS_SP_ELEMENT(Feld[jx][jy]))
10396 PlayerWins(player);
10399 /* this breaks one level: "machine", level 000 */
10401 int move_direction = player->MovDir;
10402 int enter_side = MV_DIR_OPPOSITE(move_direction);
10403 int leave_side = move_direction;
10404 int old_jx = last_jx;
10405 int old_jy = last_jy;
10406 int old_element = Feld[old_jx][old_jy];
10407 int new_element = Feld[jx][jy];
10409 if (IS_CUSTOM_ELEMENT(old_element))
10410 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10412 player->index_bit, leave_side);
10414 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10415 CE_PLAYER_LEAVES_X,
10416 player->index_bit, leave_side);
10418 if (IS_CUSTOM_ELEMENT(new_element))
10419 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10420 player->index_bit, enter_side);
10422 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10423 CE_PLAYER_ENTERS_X,
10424 player->index_bit, enter_side);
10426 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10427 CE_MOVE_OF_X, move_direction);
10430 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10432 TestIfPlayerTouchesBadThing(jx, jy);
10433 TestIfPlayerTouchesCustomElement(jx, jy);
10435 /* needed because pushed element has not yet reached its destination,
10436 so it would trigger a change event at its previous field location */
10437 if (!player->is_pushing)
10438 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10440 if (!player->active)
10441 RemovePlayer(player);
10444 if (!local_player->LevelSolved && level.use_step_counter)
10454 if (TimeLeft <= 10 && setup.time_limit)
10455 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10457 DrawGameValue_Time(TimeLeft);
10459 if (!TimeLeft && setup.time_limit)
10460 for (i = 0; i < MAX_PLAYERS; i++)
10461 KillPlayer(&stored_player[i]);
10463 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10464 DrawGameValue_Time(TimePlayed);
10467 if (tape.single_step && tape.recording && !tape.pausing &&
10468 !player->programmed_action)
10469 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10473 void ScrollScreen(struct PlayerInfo *player, int mode)
10475 static unsigned long screen_frame_counter = 0;
10477 if (mode == SCROLL_INIT)
10479 /* set scrolling step size according to actual player's moving speed */
10480 ScrollStepSize = TILEX / player->move_delay_value;
10482 screen_frame_counter = FrameCounter;
10483 ScreenMovDir = player->MovDir;
10484 ScreenMovPos = player->MovPos;
10485 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10488 else if (!FrameReached(&screen_frame_counter, 1))
10493 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10494 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10495 redraw_mask |= REDRAW_FIELD;
10498 ScreenMovDir = MV_NONE;
10501 void TestIfPlayerTouchesCustomElement(int x, int y)
10503 static int xy[4][2] =
10510 static int trigger_sides[4][2] =
10512 /* center side border side */
10513 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10514 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10515 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10516 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10518 static int touch_dir[4] =
10520 MV_LEFT | MV_RIGHT,
10525 int center_element = Feld[x][y]; /* should always be non-moving! */
10528 for (i = 0; i < NUM_DIRECTIONS; i++)
10530 int xx = x + xy[i][0];
10531 int yy = y + xy[i][1];
10532 int center_side = trigger_sides[i][0];
10533 int border_side = trigger_sides[i][1];
10534 int border_element;
10536 if (!IN_LEV_FIELD(xx, yy))
10539 if (IS_PLAYER(x, y))
10541 struct PlayerInfo *player = PLAYERINFO(x, y);
10543 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10544 border_element = Feld[xx][yy]; /* may be moving! */
10545 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10546 border_element = Feld[xx][yy];
10547 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10548 border_element = MovingOrBlocked2Element(xx, yy);
10550 continue; /* center and border element do not touch */
10552 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10553 player->index_bit, border_side);
10554 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10555 CE_PLAYER_TOUCHES_X,
10556 player->index_bit, border_side);
10558 else if (IS_PLAYER(xx, yy))
10560 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10562 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10564 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10565 continue; /* center and border element do not touch */
10568 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10569 player->index_bit, center_side);
10570 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10571 CE_PLAYER_TOUCHES_X,
10572 player->index_bit, center_side);
10578 #if USE_ELEMENT_TOUCHING_BUGFIX
10580 void TestIfElementTouchesCustomElement(int x, int y)
10582 static int xy[4][2] =
10589 static int trigger_sides[4][2] =
10591 /* center side border side */
10592 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10593 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10594 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10595 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10597 static int touch_dir[4] =
10599 MV_LEFT | MV_RIGHT,
10604 boolean change_center_element = FALSE;
10605 int center_element = Feld[x][y]; /* should always be non-moving! */
10606 int border_element_old[NUM_DIRECTIONS];
10609 for (i = 0; i < NUM_DIRECTIONS; i++)
10611 int xx = x + xy[i][0];
10612 int yy = y + xy[i][1];
10613 int border_element;
10615 border_element_old[i] = -1;
10617 if (!IN_LEV_FIELD(xx, yy))
10620 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10621 border_element = Feld[xx][yy]; /* may be moving! */
10622 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10623 border_element = Feld[xx][yy];
10624 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10625 border_element = MovingOrBlocked2Element(xx, yy);
10627 continue; /* center and border element do not touch */
10629 border_element_old[i] = border_element;
10632 for (i = 0; i < NUM_DIRECTIONS; i++)
10634 int xx = x + xy[i][0];
10635 int yy = y + xy[i][1];
10636 int center_side = trigger_sides[i][0];
10637 int border_element = border_element_old[i];
10639 if (border_element == -1)
10642 /* check for change of border element */
10643 CheckElementChangeBySide(xx, yy, border_element, center_element,
10644 CE_TOUCHING_X, center_side);
10647 for (i = 0; i < NUM_DIRECTIONS; i++)
10649 int border_side = trigger_sides[i][1];
10650 int border_element = border_element_old[i];
10652 if (border_element == -1)
10655 /* check for change of center element (but change it only once) */
10656 if (!change_center_element)
10657 change_center_element =
10658 CheckElementChangeBySide(x, y, center_element, border_element,
10659 CE_TOUCHING_X, border_side);
10665 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10667 static int xy[4][2] =
10674 static int trigger_sides[4][2] =
10676 /* center side border side */
10677 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10678 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10679 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10680 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10682 static int touch_dir[4] =
10684 MV_LEFT | MV_RIGHT,
10689 boolean change_center_element = FALSE;
10690 int center_element = Feld[x][y]; /* should always be non-moving! */
10693 for (i = 0; i < NUM_DIRECTIONS; i++)
10695 int xx = x + xy[i][0];
10696 int yy = y + xy[i][1];
10697 int center_side = trigger_sides[i][0];
10698 int border_side = trigger_sides[i][1];
10699 int border_element;
10701 if (!IN_LEV_FIELD(xx, yy))
10704 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10705 border_element = Feld[xx][yy]; /* may be moving! */
10706 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10707 border_element = Feld[xx][yy];
10708 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10709 border_element = MovingOrBlocked2Element(xx, yy);
10711 continue; /* center and border element do not touch */
10713 /* check for change of center element (but change it only once) */
10714 if (!change_center_element)
10715 change_center_element =
10716 CheckElementChangeBySide(x, y, center_element, border_element,
10717 CE_TOUCHING_X, border_side);
10719 /* check for change of border element */
10720 CheckElementChangeBySide(xx, yy, border_element, center_element,
10721 CE_TOUCHING_X, center_side);
10727 void TestIfElementHitsCustomElement(int x, int y, int direction)
10729 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10730 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10731 int hitx = x + dx, hity = y + dy;
10732 int hitting_element = Feld[x][y];
10733 int touched_element;
10735 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10738 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10739 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10741 if (IN_LEV_FIELD(hitx, hity))
10743 int opposite_direction = MV_DIR_OPPOSITE(direction);
10744 int hitting_side = direction;
10745 int touched_side = opposite_direction;
10746 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10747 MovDir[hitx][hity] != direction ||
10748 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10754 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10755 CE_HITTING_X, touched_side);
10757 CheckElementChangeBySide(hitx, hity, touched_element,
10758 hitting_element, CE_HIT_BY_X, hitting_side);
10760 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10761 CE_HIT_BY_SOMETHING, opposite_direction);
10765 /* "hitting something" is also true when hitting the playfield border */
10766 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10767 CE_HITTING_SOMETHING, direction);
10771 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10773 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10774 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10775 int hitx = x + dx, hity = y + dy;
10776 int hitting_element = Feld[x][y];
10777 int touched_element;
10779 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10780 !IS_FREE(hitx, hity) &&
10781 (!IS_MOVING(hitx, hity) ||
10782 MovDir[hitx][hity] != direction ||
10783 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10786 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10790 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10794 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10795 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10797 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10798 EP_CAN_SMASH_EVERYTHING, direction);
10800 if (IN_LEV_FIELD(hitx, hity))
10802 int opposite_direction = MV_DIR_OPPOSITE(direction);
10803 int hitting_side = direction;
10804 int touched_side = opposite_direction;
10806 int touched_element = MovingOrBlocked2Element(hitx, hity);
10809 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10810 MovDir[hitx][hity] != direction ||
10811 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10820 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10821 CE_SMASHED_BY_SOMETHING, opposite_direction);
10823 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10824 CE_OTHER_IS_SMASHING, touched_side);
10826 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10827 CE_OTHER_GETS_SMASHED, hitting_side);
10833 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10835 int i, kill_x = -1, kill_y = -1;
10837 int bad_element = -1;
10838 static int test_xy[4][2] =
10845 static int test_dir[4] =
10853 for (i = 0; i < NUM_DIRECTIONS; i++)
10855 int test_x, test_y, test_move_dir, test_element;
10857 test_x = good_x + test_xy[i][0];
10858 test_y = good_y + test_xy[i][1];
10860 if (!IN_LEV_FIELD(test_x, test_y))
10864 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10866 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10868 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10869 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10871 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10872 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10876 bad_element = test_element;
10882 if (kill_x != -1 || kill_y != -1)
10884 if (IS_PLAYER(good_x, good_y))
10886 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10888 if (player->shield_deadly_time_left > 0 &&
10889 !IS_INDESTRUCTIBLE(bad_element))
10890 Bang(kill_x, kill_y);
10891 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10892 KillPlayer(player);
10895 Bang(good_x, good_y);
10899 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10901 int i, kill_x = -1, kill_y = -1;
10902 int bad_element = Feld[bad_x][bad_y];
10903 static int test_xy[4][2] =
10910 static int touch_dir[4] =
10912 MV_LEFT | MV_RIGHT,
10917 static int test_dir[4] =
10925 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10928 for (i = 0; i < NUM_DIRECTIONS; i++)
10930 int test_x, test_y, test_move_dir, test_element;
10932 test_x = bad_x + test_xy[i][0];
10933 test_y = bad_y + test_xy[i][1];
10934 if (!IN_LEV_FIELD(test_x, test_y))
10938 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10940 test_element = Feld[test_x][test_y];
10942 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10943 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10945 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10946 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10948 /* good thing is player or penguin that does not move away */
10949 if (IS_PLAYER(test_x, test_y))
10951 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10953 if (bad_element == EL_ROBOT && player->is_moving)
10954 continue; /* robot does not kill player if he is moving */
10956 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10958 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10959 continue; /* center and border element do not touch */
10966 else if (test_element == EL_PENGUIN)
10975 if (kill_x != -1 || kill_y != -1)
10977 if (IS_PLAYER(kill_x, kill_y))
10979 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10981 if (player->shield_deadly_time_left > 0 &&
10982 !IS_INDESTRUCTIBLE(bad_element))
10983 Bang(bad_x, bad_y);
10984 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10985 KillPlayer(player);
10988 Bang(kill_x, kill_y);
10992 void TestIfPlayerTouchesBadThing(int x, int y)
10994 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10997 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10999 TestIfGoodThingHitsBadThing(x, y, move_dir);
11002 void TestIfBadThingTouchesPlayer(int x, int y)
11004 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11007 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11009 TestIfBadThingHitsGoodThing(x, y, move_dir);
11012 void TestIfFriendTouchesBadThing(int x, int y)
11014 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11017 void TestIfBadThingTouchesFriend(int x, int y)
11019 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11022 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11024 int i, kill_x = bad_x, kill_y = bad_y;
11025 static int xy[4][2] =
11033 for (i = 0; i < NUM_DIRECTIONS; i++)
11037 x = bad_x + xy[i][0];
11038 y = bad_y + xy[i][1];
11039 if (!IN_LEV_FIELD(x, y))
11042 element = Feld[x][y];
11043 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11044 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11052 if (kill_x != bad_x || kill_y != bad_y)
11053 Bang(bad_x, bad_y);
11056 void KillPlayer(struct PlayerInfo *player)
11058 int jx = player->jx, jy = player->jy;
11060 if (!player->active)
11063 /* the following code was introduced to prevent an infinite loop when calling
11065 -> CheckTriggeredElementChangeExt()
11066 -> ExecuteCustomElementAction()
11068 -> (infinitely repeating the above sequence of function calls)
11069 which occurs when killing the player while having a CE with the setting
11070 "kill player X when explosion of <player X>"; the solution using a new
11071 field "player->killed" was chosen for backwards compatibility, although
11072 clever use of the fields "player->active" etc. would probably also work */
11073 if (player->killed)
11076 player->killed = TRUE;
11078 /* remove accessible field at the player's position */
11079 Feld[jx][jy] = EL_EMPTY;
11081 /* deactivate shield (else Bang()/Explode() would not work right) */
11082 player->shield_normal_time_left = 0;
11083 player->shield_deadly_time_left = 0;
11086 BuryPlayer(player);
11089 static void KillPlayerUnlessEnemyProtected(int x, int y)
11091 if (!PLAYER_ENEMY_PROTECTED(x, y))
11092 KillPlayer(PLAYERINFO(x, y));
11095 static void KillPlayerUnlessExplosionProtected(int x, int y)
11097 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11098 KillPlayer(PLAYERINFO(x, y));
11101 void BuryPlayer(struct PlayerInfo *player)
11103 int jx = player->jx, jy = player->jy;
11105 if (!player->active)
11108 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11109 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11111 player->GameOver = TRUE;
11112 RemovePlayer(player);
11115 void RemovePlayer(struct PlayerInfo *player)
11117 int jx = player->jx, jy = player->jy;
11118 int i, found = FALSE;
11120 player->present = FALSE;
11121 player->active = FALSE;
11123 if (!ExplodeField[jx][jy])
11124 StorePlayer[jx][jy] = 0;
11126 if (player->is_moving)
11127 DrawLevelField(player->last_jx, player->last_jy);
11129 for (i = 0; i < MAX_PLAYERS; i++)
11130 if (stored_player[i].active)
11134 AllPlayersGone = TRUE;
11140 #if USE_NEW_SNAP_DELAY
11141 static void setFieldForSnapping(int x, int y, int element, int direction)
11143 struct ElementInfo *ei = &element_info[element];
11144 int direction_bit = MV_DIR_TO_BIT(direction);
11145 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11146 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11147 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11149 Feld[x][y] = EL_ELEMENT_SNAPPING;
11150 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11152 ResetGfxAnimation(x, y);
11154 GfxElement[x][y] = element;
11155 GfxAction[x][y] = action;
11156 GfxDir[x][y] = direction;
11157 GfxFrame[x][y] = -1;
11162 =============================================================================
11163 checkDiagonalPushing()
11164 -----------------------------------------------------------------------------
11165 check if diagonal input device direction results in pushing of object
11166 (by checking if the alternative direction is walkable, diggable, ...)
11167 =============================================================================
11170 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11171 int x, int y, int real_dx, int real_dy)
11173 int jx, jy, dx, dy, xx, yy;
11175 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11178 /* diagonal direction: check alternative direction */
11183 xx = jx + (dx == 0 ? real_dx : 0);
11184 yy = jy + (dy == 0 ? real_dy : 0);
11186 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11190 =============================================================================
11192 -----------------------------------------------------------------------------
11193 x, y: field next to player (non-diagonal) to try to dig to
11194 real_dx, real_dy: direction as read from input device (can be diagonal)
11195 =============================================================================
11198 int DigField(struct PlayerInfo *player,
11199 int oldx, int oldy, int x, int y,
11200 int real_dx, int real_dy, int mode)
11202 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11203 boolean player_was_pushing = player->is_pushing;
11204 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11205 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11206 int jx = oldx, jy = oldy;
11207 int dx = x - jx, dy = y - jy;
11208 int nextx = x + dx, nexty = y + dy;
11209 int move_direction = (dx == -1 ? MV_LEFT :
11210 dx == +1 ? MV_RIGHT :
11212 dy == +1 ? MV_DOWN : MV_NONE);
11213 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11214 int dig_side = MV_DIR_OPPOSITE(move_direction);
11215 int old_element = Feld[jx][jy];
11216 #if USE_FIXED_DONT_RUN_INTO
11217 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11223 if (is_player) /* function can also be called by EL_PENGUIN */
11225 if (player->MovPos == 0)
11227 player->is_digging = FALSE;
11228 player->is_collecting = FALSE;
11231 if (player->MovPos == 0) /* last pushing move finished */
11232 player->is_pushing = FALSE;
11234 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11236 player->is_switching = FALSE;
11237 player->push_delay = -1;
11239 return MP_NO_ACTION;
11243 #if !USE_FIXED_DONT_RUN_INTO
11244 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11245 return MP_NO_ACTION;
11248 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11249 old_element = Back[jx][jy];
11251 /* in case of element dropped at player position, check background */
11252 else if (Back[jx][jy] != EL_EMPTY &&
11253 game.engine_version >= VERSION_IDENT(2,2,0,0))
11254 old_element = Back[jx][jy];
11256 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11257 return MP_NO_ACTION; /* field has no opening in this direction */
11259 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11260 return MP_NO_ACTION; /* field has no opening in this direction */
11262 #if USE_FIXED_DONT_RUN_INTO
11263 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11267 Feld[jx][jy] = player->artwork_element;
11268 InitMovingField(jx, jy, MV_DOWN);
11269 Store[jx][jy] = EL_ACID;
11270 ContinueMoving(jx, jy);
11271 BuryPlayer(player);
11273 return MP_DONT_RUN_INTO;
11277 #if USE_FIXED_DONT_RUN_INTO
11278 if (player_can_move && DONT_RUN_INTO(element))
11280 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11282 return MP_DONT_RUN_INTO;
11286 #if USE_FIXED_DONT_RUN_INTO
11287 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11288 return MP_NO_ACTION;
11291 #if !USE_FIXED_DONT_RUN_INTO
11292 element = Feld[x][y];
11295 collect_count = element_info[element].collect_count_initial;
11297 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11298 return MP_NO_ACTION;
11300 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11301 player_can_move = player_can_move_or_snap;
11303 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11304 game.engine_version >= VERSION_IDENT(2,2,0,0))
11306 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11307 player->index_bit, dig_side);
11308 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11309 player->index_bit, dig_side);
11311 if (Feld[x][y] != element) /* field changed by snapping */
11314 return MP_NO_ACTION;
11317 #if USE_PLAYER_GRAVITY
11318 if (player->gravity && is_player && !player->is_auto_moving &&
11319 canFallDown(player) && move_direction != MV_DOWN &&
11320 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11321 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11323 if (game.gravity && is_player && !player->is_auto_moving &&
11324 canFallDown(player) && move_direction != MV_DOWN &&
11325 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11326 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11329 if (player_can_move &&
11330 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11332 int sound_element = SND_ELEMENT(element);
11333 int sound_action = ACTION_WALKING;
11335 if (IS_RND_GATE(element))
11337 if (!player->key[RND_GATE_NR(element)])
11338 return MP_NO_ACTION;
11340 else if (IS_RND_GATE_GRAY(element))
11342 if (!player->key[RND_GATE_GRAY_NR(element)])
11343 return MP_NO_ACTION;
11345 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11347 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11348 return MP_NO_ACTION;
11350 else if (element == EL_EXIT_OPEN ||
11351 element == EL_SP_EXIT_OPEN ||
11352 element == EL_SP_EXIT_OPENING)
11354 sound_action = ACTION_PASSING; /* player is passing exit */
11356 else if (element == EL_EMPTY)
11358 sound_action = ACTION_MOVING; /* nothing to walk on */
11361 /* play sound from background or player, whatever is available */
11362 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11363 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11365 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11367 else if (player_can_move &&
11368 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11370 if (!ACCESS_FROM(element, opposite_direction))
11371 return MP_NO_ACTION; /* field not accessible from this direction */
11373 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11374 return MP_NO_ACTION;
11376 if (IS_EM_GATE(element))
11378 if (!player->key[EM_GATE_NR(element)])
11379 return MP_NO_ACTION;
11381 else if (IS_EM_GATE_GRAY(element))
11383 if (!player->key[EM_GATE_GRAY_NR(element)])
11384 return MP_NO_ACTION;
11386 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11388 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11389 return MP_NO_ACTION;
11391 else if (IS_EMC_GATE(element))
11393 if (!player->key[EMC_GATE_NR(element)])
11394 return MP_NO_ACTION;
11396 else if (IS_EMC_GATE_GRAY(element))
11398 if (!player->key[EMC_GATE_GRAY_NR(element)])
11399 return MP_NO_ACTION;
11401 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11403 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11404 return MP_NO_ACTION;
11406 else if (IS_SP_PORT(element))
11408 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11409 element == EL_SP_GRAVITY_PORT_RIGHT ||
11410 element == EL_SP_GRAVITY_PORT_UP ||
11411 element == EL_SP_GRAVITY_PORT_DOWN)
11412 #if USE_PLAYER_GRAVITY
11413 player->gravity = !player->gravity;
11415 game.gravity = !game.gravity;
11417 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11418 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11419 element == EL_SP_GRAVITY_ON_PORT_UP ||
11420 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11421 #if USE_PLAYER_GRAVITY
11422 player->gravity = TRUE;
11424 game.gravity = TRUE;
11426 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11427 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11428 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11429 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11430 #if USE_PLAYER_GRAVITY
11431 player->gravity = FALSE;
11433 game.gravity = FALSE;
11437 /* automatically move to the next field with double speed */
11438 player->programmed_action = move_direction;
11440 if (player->move_delay_reset_counter == 0)
11442 player->move_delay_reset_counter = 2; /* two double speed steps */
11444 DOUBLE_PLAYER_SPEED(player);
11447 PlayLevelSoundAction(x, y, ACTION_PASSING);
11449 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11453 if (mode != DF_SNAP)
11455 GfxElement[x][y] = GFX_ELEMENT(element);
11456 player->is_digging = TRUE;
11459 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11461 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11462 player->index_bit, dig_side);
11464 if (mode == DF_SNAP)
11466 #if USE_NEW_SNAP_DELAY
11467 if (level.block_snap_field)
11468 setFieldForSnapping(x, y, element, move_direction);
11470 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11472 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11475 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11476 player->index_bit, dig_side);
11479 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11483 if (is_player && mode != DF_SNAP)
11485 GfxElement[x][y] = element;
11486 player->is_collecting = TRUE;
11489 if (element == EL_SPEED_PILL)
11491 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11493 else if (element == EL_EXTRA_TIME && level.time > 0)
11495 TimeLeft += level.extra_time;
11496 DrawGameValue_Time(TimeLeft);
11498 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11500 player->shield_normal_time_left += level.shield_normal_time;
11501 if (element == EL_SHIELD_DEADLY)
11502 player->shield_deadly_time_left += level.shield_deadly_time;
11504 else if (element == EL_DYNAMITE ||
11505 element == EL_EM_DYNAMITE ||
11506 element == EL_SP_DISK_RED)
11508 if (player->inventory_size < MAX_INVENTORY_SIZE)
11509 player->inventory_element[player->inventory_size++] = element;
11511 DrawGameDoorValues();
11513 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11515 player->dynabomb_count++;
11516 player->dynabombs_left++;
11518 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11520 player->dynabomb_size++;
11522 else if (element == EL_DYNABOMB_INCREASE_POWER)
11524 player->dynabomb_xl = TRUE;
11526 else if (IS_KEY(element))
11528 player->key[KEY_NR(element)] = TRUE;
11530 DrawGameDoorValues();
11532 else if (IS_ENVELOPE(element))
11534 player->show_envelope = element;
11536 else if (element == EL_EMC_LENSES)
11538 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11540 RedrawAllInvisibleElementsForLenses();
11542 else if (element == EL_EMC_MAGNIFIER)
11544 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11546 RedrawAllInvisibleElementsForMagnifier();
11548 else if (IS_DROPPABLE(element) ||
11549 IS_THROWABLE(element)) /* can be collected and dropped */
11553 if (collect_count == 0)
11554 player->inventory_infinite_element = element;
11556 for (i = 0; i < collect_count; i++)
11557 if (player->inventory_size < MAX_INVENTORY_SIZE)
11558 player->inventory_element[player->inventory_size++] = element;
11560 DrawGameDoorValues();
11562 else if (collect_count > 0)
11564 local_player->gems_still_needed -= collect_count;
11565 if (local_player->gems_still_needed < 0)
11566 local_player->gems_still_needed = 0;
11568 DrawGameValue_Emeralds(local_player->gems_still_needed);
11571 RaiseScoreElement(element);
11572 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11575 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11576 player->index_bit, dig_side);
11578 if (mode == DF_SNAP)
11580 #if USE_NEW_SNAP_DELAY
11581 if (level.block_snap_field)
11582 setFieldForSnapping(x, y, element, move_direction);
11584 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11586 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11589 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11590 player->index_bit, dig_side);
11593 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11595 if (mode == DF_SNAP && element != EL_BD_ROCK)
11596 return MP_NO_ACTION;
11598 if (CAN_FALL(element) && dy)
11599 return MP_NO_ACTION;
11601 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11602 !(element == EL_SPRING && level.use_spring_bug))
11603 return MP_NO_ACTION;
11605 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11606 ((move_direction & MV_VERTICAL &&
11607 ((element_info[element].move_pattern & MV_LEFT &&
11608 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11609 (element_info[element].move_pattern & MV_RIGHT &&
11610 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11611 (move_direction & MV_HORIZONTAL &&
11612 ((element_info[element].move_pattern & MV_UP &&
11613 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11614 (element_info[element].move_pattern & MV_DOWN &&
11615 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11616 return MP_NO_ACTION;
11618 /* do not push elements already moving away faster than player */
11619 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11620 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11621 return MP_NO_ACTION;
11623 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11625 if (player->push_delay_value == -1 || !player_was_pushing)
11626 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11628 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11630 if (player->push_delay_value == -1)
11631 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11633 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11635 if (!player->is_pushing)
11636 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11639 player->is_pushing = TRUE;
11640 player->is_active = TRUE;
11642 if (!(IN_LEV_FIELD(nextx, nexty) &&
11643 (IS_FREE(nextx, nexty) ||
11644 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11645 IS_SB_ELEMENT(element)))))
11646 return MP_NO_ACTION;
11648 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11649 return MP_NO_ACTION;
11651 if (player->push_delay == -1) /* new pushing; restart delay */
11652 player->push_delay = 0;
11654 if (player->push_delay < player->push_delay_value &&
11655 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11656 element != EL_SPRING && element != EL_BALLOON)
11658 /* make sure that there is no move delay before next try to push */
11659 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11660 player->move_delay = 0;
11662 return MP_NO_ACTION;
11665 if (IS_SB_ELEMENT(element))
11667 if (element == EL_SOKOBAN_FIELD_FULL)
11669 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11670 local_player->sokobanfields_still_needed++;
11673 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11675 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11676 local_player->sokobanfields_still_needed--;
11679 Feld[x][y] = EL_SOKOBAN_OBJECT;
11681 if (Back[x][y] == Back[nextx][nexty])
11682 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11683 else if (Back[x][y] != 0)
11684 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11687 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11690 if (local_player->sokobanfields_still_needed == 0 &&
11691 game.emulation == EMU_SOKOBAN)
11693 PlayerWins(player);
11695 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11699 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11701 InitMovingField(x, y, move_direction);
11702 GfxAction[x][y] = ACTION_PUSHING;
11704 if (mode == DF_SNAP)
11705 ContinueMoving(x, y);
11707 MovPos[x][y] = (dx != 0 ? dx : dy);
11709 Pushed[x][y] = TRUE;
11710 Pushed[nextx][nexty] = TRUE;
11712 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11713 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11715 player->push_delay_value = -1; /* get new value later */
11717 /* check for element change _after_ element has been pushed */
11718 if (game.use_change_when_pushing_bug)
11720 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11721 player->index_bit, dig_side);
11722 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11723 player->index_bit, dig_side);
11726 else if (IS_SWITCHABLE(element))
11728 if (PLAYER_SWITCHING(player, x, y))
11730 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11731 player->index_bit, dig_side);
11736 player->is_switching = TRUE;
11737 player->switch_x = x;
11738 player->switch_y = y;
11740 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11742 if (element == EL_ROBOT_WHEEL)
11744 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11748 DrawLevelField(x, y);
11750 else if (element == EL_SP_TERMINAL)
11754 SCAN_PLAYFIELD(xx, yy)
11756 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11758 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11759 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11762 else if (IS_BELT_SWITCH(element))
11764 ToggleBeltSwitch(x, y);
11766 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11767 element == EL_SWITCHGATE_SWITCH_DOWN)
11769 ToggleSwitchgateSwitch(x, y);
11771 else if (element == EL_LIGHT_SWITCH ||
11772 element == EL_LIGHT_SWITCH_ACTIVE)
11774 ToggleLightSwitch(x, y);
11776 else if (element == EL_TIMEGATE_SWITCH)
11778 ActivateTimegateSwitch(x, y);
11780 else if (element == EL_BALLOON_SWITCH_LEFT ||
11781 element == EL_BALLOON_SWITCH_RIGHT ||
11782 element == EL_BALLOON_SWITCH_UP ||
11783 element == EL_BALLOON_SWITCH_DOWN ||
11784 element == EL_BALLOON_SWITCH_NONE ||
11785 element == EL_BALLOON_SWITCH_ANY)
11787 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11788 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11789 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11790 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11791 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11794 else if (element == EL_LAMP)
11796 Feld[x][y] = EL_LAMP_ACTIVE;
11797 local_player->lights_still_needed--;
11799 ResetGfxAnimation(x, y);
11800 DrawLevelField(x, y);
11802 else if (element == EL_TIME_ORB_FULL)
11804 Feld[x][y] = EL_TIME_ORB_EMPTY;
11806 if (level.time > 0 || level.use_time_orb_bug)
11808 TimeLeft += level.time_orb_time;
11809 DrawGameValue_Time(TimeLeft);
11812 ResetGfxAnimation(x, y);
11813 DrawLevelField(x, y);
11815 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11816 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11820 game.ball_state = !game.ball_state;
11822 SCAN_PLAYFIELD(xx, yy)
11824 int e = Feld[xx][yy];
11826 if (game.ball_state)
11828 if (e == EL_EMC_MAGIC_BALL)
11829 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11830 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11831 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11835 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11836 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11837 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11838 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11843 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11844 player->index_bit, dig_side);
11846 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11847 player->index_bit, dig_side);
11849 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11850 player->index_bit, dig_side);
11856 if (!PLAYER_SWITCHING(player, x, y))
11858 player->is_switching = TRUE;
11859 player->switch_x = x;
11860 player->switch_y = y;
11862 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11863 player->index_bit, dig_side);
11864 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11865 player->index_bit, dig_side);
11867 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11868 player->index_bit, dig_side);
11869 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11870 player->index_bit, dig_side);
11873 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11874 player->index_bit, dig_side);
11875 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11876 player->index_bit, dig_side);
11878 return MP_NO_ACTION;
11881 player->push_delay = -1;
11883 if (is_player) /* function can also be called by EL_PENGUIN */
11885 if (Feld[x][y] != element) /* really digged/collected something */
11887 player->is_collecting = !player->is_digging;
11888 player->is_active = TRUE;
11895 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11897 int jx = player->jx, jy = player->jy;
11898 int x = jx + dx, y = jy + dy;
11899 int snap_direction = (dx == -1 ? MV_LEFT :
11900 dx == +1 ? MV_RIGHT :
11902 dy == +1 ? MV_DOWN : MV_NONE);
11903 boolean can_continue_snapping = (level.continuous_snapping &&
11904 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
11906 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11909 if (!player->active || !IN_LEV_FIELD(x, y))
11917 if (player->MovPos == 0)
11918 player->is_pushing = FALSE;
11920 player->is_snapping = FALSE;
11922 if (player->MovPos == 0)
11924 player->is_moving = FALSE;
11925 player->is_digging = FALSE;
11926 player->is_collecting = FALSE;
11932 #if USE_NEW_CONTINUOUS_SNAPPING
11933 /* prevent snapping with already pressed snap key when not allowed */
11934 if (player->is_snapping && !can_continue_snapping)
11937 if (player->is_snapping)
11941 player->MovDir = snap_direction;
11943 if (player->MovPos == 0)
11945 player->is_moving = FALSE;
11946 player->is_digging = FALSE;
11947 player->is_collecting = FALSE;
11950 player->is_dropping = FALSE;
11951 player->is_dropping_pressed = FALSE;
11952 player->drop_pressed_delay = 0;
11954 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11957 player->is_snapping = TRUE;
11958 player->is_active = TRUE;
11960 if (player->MovPos == 0)
11962 player->is_moving = FALSE;
11963 player->is_digging = FALSE;
11964 player->is_collecting = FALSE;
11967 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11968 DrawLevelField(player->last_jx, player->last_jy);
11970 DrawLevelField(x, y);
11975 boolean DropElement(struct PlayerInfo *player)
11977 int old_element, new_element;
11978 int dropx = player->jx, dropy = player->jy;
11979 int drop_direction = player->MovDir;
11980 int drop_side = drop_direction;
11981 int drop_element = (player->inventory_size > 0 ?
11982 player->inventory_element[player->inventory_size - 1] :
11983 player->inventory_infinite_element != EL_UNDEFINED ?
11984 player->inventory_infinite_element :
11985 player->dynabombs_left > 0 ?
11986 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11989 player->is_dropping_pressed = TRUE;
11991 /* do not drop an element on top of another element; when holding drop key
11992 pressed without moving, dropped element must move away before the next
11993 element can be dropped (this is especially important if the next element
11994 is dynamite, which can be placed on background for historical reasons) */
11995 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11998 if (IS_THROWABLE(drop_element))
12000 dropx += GET_DX_FROM_DIR(drop_direction);
12001 dropy += GET_DY_FROM_DIR(drop_direction);
12003 if (!IN_LEV_FIELD(dropx, dropy))
12007 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12008 new_element = drop_element; /* default: no change when dropping */
12010 /* check if player is active, not moving and ready to drop */
12011 if (!player->active || player->MovPos || player->drop_delay > 0)
12014 /* check if player has anything that can be dropped */
12015 if (new_element == EL_UNDEFINED)
12018 /* check if drop key was pressed long enough for EM style dynamite */
12019 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12022 /* check if anything can be dropped at the current position */
12023 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12026 /* collected custom elements can only be dropped on empty fields */
12027 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12030 if (old_element != EL_EMPTY)
12031 Back[dropx][dropy] = old_element; /* store old element on this field */
12033 ResetGfxAnimation(dropx, dropy);
12034 ResetRandomAnimationValue(dropx, dropy);
12036 if (player->inventory_size > 0 ||
12037 player->inventory_infinite_element != EL_UNDEFINED)
12039 if (player->inventory_size > 0)
12041 player->inventory_size--;
12043 DrawGameDoorValues();
12045 if (new_element == EL_DYNAMITE)
12046 new_element = EL_DYNAMITE_ACTIVE;
12047 else if (new_element == EL_EM_DYNAMITE)
12048 new_element = EL_EM_DYNAMITE_ACTIVE;
12049 else if (new_element == EL_SP_DISK_RED)
12050 new_element = EL_SP_DISK_RED_ACTIVE;
12053 Feld[dropx][dropy] = new_element;
12055 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12056 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12057 el2img(Feld[dropx][dropy]), 0);
12059 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12061 /* needed if previous element just changed to "empty" in the last frame */
12062 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12064 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12065 player->index_bit, drop_side);
12066 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12068 player->index_bit, drop_side);
12070 TestIfElementTouchesCustomElement(dropx, dropy);
12072 else /* player is dropping a dyna bomb */
12074 player->dynabombs_left--;
12076 Feld[dropx][dropy] = new_element;
12078 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12079 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12080 el2img(Feld[dropx][dropy]), 0);
12082 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12085 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12086 InitField_WithBug1(dropx, dropy, FALSE);
12088 new_element = Feld[dropx][dropy]; /* element might have changed */
12090 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12091 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12093 int move_direction, nextx, nexty;
12095 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12096 MovDir[dropx][dropy] = drop_direction;
12098 move_direction = MovDir[dropx][dropy];
12099 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12100 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12102 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12104 #if USE_FIX_IMPACT_COLLISION
12105 /* do not cause impact style collision by dropping elements that can fall */
12106 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12108 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12112 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12113 player->is_dropping = TRUE;
12115 player->drop_pressed_delay = 0;
12116 player->is_dropping_pressed = FALSE;
12118 player->drop_x = dropx;
12119 player->drop_y = dropy;
12124 /* ------------------------------------------------------------------------- */
12125 /* game sound playing functions */
12126 /* ------------------------------------------------------------------------- */
12128 static int *loop_sound_frame = NULL;
12129 static int *loop_sound_volume = NULL;
12131 void InitPlayLevelSound()
12133 int num_sounds = getSoundListSize();
12135 checked_free(loop_sound_frame);
12136 checked_free(loop_sound_volume);
12138 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12139 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12142 static void PlayLevelSound(int x, int y, int nr)
12144 int sx = SCREENX(x), sy = SCREENY(y);
12145 int volume, stereo_position;
12146 int max_distance = 8;
12147 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12149 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12150 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12153 if (!IN_LEV_FIELD(x, y) ||
12154 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12155 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12158 volume = SOUND_MAX_VOLUME;
12160 if (!IN_SCR_FIELD(sx, sy))
12162 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12163 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12165 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12168 stereo_position = (SOUND_MAX_LEFT +
12169 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12170 (SCR_FIELDX + 2 * max_distance));
12172 if (IS_LOOP_SOUND(nr))
12174 /* This assures that quieter loop sounds do not overwrite louder ones,
12175 while restarting sound volume comparison with each new game frame. */
12177 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12180 loop_sound_volume[nr] = volume;
12181 loop_sound_frame[nr] = FrameCounter;
12184 PlaySoundExt(nr, volume, stereo_position, type);
12187 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12189 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12190 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12191 y < LEVELY(BY1) ? LEVELY(BY1) :
12192 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12196 static void PlayLevelSoundAction(int x, int y, int action)
12198 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12201 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12203 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12205 if (sound_effect != SND_UNDEFINED)
12206 PlayLevelSound(x, y, sound_effect);
12209 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12212 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12214 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12215 PlayLevelSound(x, y, sound_effect);
12218 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12220 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12222 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12223 PlayLevelSound(x, y, sound_effect);
12226 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12228 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12230 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12231 StopSound(sound_effect);
12234 static void PlayLevelMusic()
12236 if (levelset.music[level_nr] != MUS_UNDEFINED)
12237 PlayMusic(levelset.music[level_nr]); /* from config file */
12239 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12242 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
12244 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12245 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
12246 int x = xx - 1 - offset;
12247 int y = yy - 1 - offset;
12252 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12256 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12260 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12264 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12268 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12272 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12276 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12279 case SAMPLE_android_clone:
12280 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12283 case SAMPLE_android_move:
12284 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12287 case SAMPLE_spring:
12288 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12292 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12296 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12299 case SAMPLE_eater_eat:
12300 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12304 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12307 case SAMPLE_collect:
12308 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12311 case SAMPLE_diamond:
12312 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12315 case SAMPLE_squash:
12316 /* !!! CHECK THIS !!! */
12318 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12320 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12324 case SAMPLE_wonderfall:
12325 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12329 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12333 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12337 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12341 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12345 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12349 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12352 case SAMPLE_wonder:
12353 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12357 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12360 case SAMPLE_exit_open:
12361 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12364 case SAMPLE_exit_leave:
12365 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12368 case SAMPLE_dynamite:
12369 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12373 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12377 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12381 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12385 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12389 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12393 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12397 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12403 void ChangeTime(int value)
12405 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
12409 /* EMC game engine uses value from time counter of RND game engine */
12410 level.native_em_level->lev->time = *time;
12412 DrawGameValue_Time(*time);
12415 void RaiseScore(int value)
12417 /* EMC game engine and RND game engine have separate score counters */
12418 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
12419 &level.native_em_level->lev->score : &local_player->score);
12423 DrawGameValue_Score(*score);
12427 void RaiseScore(int value)
12429 local_player->score += value;
12431 DrawGameValue_Score(local_player->score);
12434 void RaiseScoreElement(int element)
12439 case EL_BD_DIAMOND:
12440 case EL_EMERALD_YELLOW:
12441 case EL_EMERALD_RED:
12442 case EL_EMERALD_PURPLE:
12443 case EL_SP_INFOTRON:
12444 RaiseScore(level.score[SC_EMERALD]);
12447 RaiseScore(level.score[SC_DIAMOND]);
12450 RaiseScore(level.score[SC_CRYSTAL]);
12453 RaiseScore(level.score[SC_PEARL]);
12456 case EL_BD_BUTTERFLY:
12457 case EL_SP_ELECTRON:
12458 RaiseScore(level.score[SC_BUG]);
12461 case EL_BD_FIREFLY:
12462 case EL_SP_SNIKSNAK:
12463 RaiseScore(level.score[SC_SPACESHIP]);
12466 case EL_DARK_YAMYAM:
12467 RaiseScore(level.score[SC_YAMYAM]);
12470 RaiseScore(level.score[SC_ROBOT]);
12473 RaiseScore(level.score[SC_PACMAN]);
12476 RaiseScore(level.score[SC_NUT]);
12479 case EL_EM_DYNAMITE:
12480 case EL_SP_DISK_RED:
12481 case EL_DYNABOMB_INCREASE_NUMBER:
12482 case EL_DYNABOMB_INCREASE_SIZE:
12483 case EL_DYNABOMB_INCREASE_POWER:
12484 RaiseScore(level.score[SC_DYNAMITE]);
12486 case EL_SHIELD_NORMAL:
12487 case EL_SHIELD_DEADLY:
12488 RaiseScore(level.score[SC_SHIELD]);
12490 case EL_EXTRA_TIME:
12491 RaiseScore(level.extra_time_score);
12505 RaiseScore(level.score[SC_KEY]);
12508 RaiseScore(element_info[element].collect_score);
12513 void RequestQuitGame(boolean ask_if_really_quit)
12515 if (AllPlayersGone ||
12516 !ask_if_really_quit ||
12517 level_editor_test_game ||
12518 Request("Do you really want to quit the game ?",
12519 REQ_ASK | REQ_STAY_CLOSED))
12521 #if defined(NETWORK_AVALIABLE)
12522 if (options.network)
12523 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12527 if (!ask_if_really_quit || level_editor_test_game)
12529 game_status = GAME_MODE_MAIN;
12535 FadeOut(REDRAW_FIELD);
12537 game_status = GAME_MODE_MAIN;
12539 DrawAndFadeInMainMenu(REDRAW_FIELD);
12545 if (tape.playing && tape.deactivate_display)
12546 TapeDeactivateDisplayOff(TRUE);
12548 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12550 if (tape.playing && tape.deactivate_display)
12551 TapeDeactivateDisplayOn();
12556 /* ------------------------------------------------------------------------- */
12557 /* random generator functions */
12558 /* ------------------------------------------------------------------------- */
12560 unsigned int InitEngineRandom_RND(long seed)
12562 game.num_random_calls = 0;
12565 unsigned int rnd_seed = InitEngineRandom(seed);
12567 printf("::: START RND: %d\n", rnd_seed);
12572 return InitEngineRandom(seed);
12578 unsigned int RND(int max)
12582 game.num_random_calls++;
12584 return GetEngineRandom(max);
12591 /* ------------------------------------------------------------------------- */
12592 /* game engine snapshot handling functions */
12593 /* ------------------------------------------------------------------------- */
12595 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
12597 struct EngineSnapshotInfo
12599 /* runtime values for custom element collect score */
12600 int collect_score[NUM_CUSTOM_ELEMENTS];
12602 /* runtime values for group element choice position */
12603 int choice_pos[NUM_GROUP_ELEMENTS];
12605 /* runtime values for belt position animations */
12606 int belt_graphic[4 * NUM_BELT_PARTS];
12607 int belt_anim_mode[4 * NUM_BELT_PARTS];
12610 struct EngineSnapshotNodeInfo
12617 static struct EngineSnapshotInfo engine_snapshot_rnd;
12618 static ListNode *engine_snapshot_list = NULL;
12619 static char *snapshot_level_identifier = NULL;
12620 static int snapshot_level_nr = -1;
12622 void FreeEngineSnapshot()
12624 while (engine_snapshot_list != NULL)
12625 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
12628 setString(&snapshot_level_identifier, NULL);
12629 snapshot_level_nr = -1;
12632 static void SaveEngineSnapshotValues_RND()
12634 static int belt_base_active_element[4] =
12636 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
12637 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
12638 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
12639 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
12643 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12645 int element = EL_CUSTOM_START + i;
12647 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
12650 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12652 int element = EL_GROUP_START + i;
12654 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
12657 for (i = 0; i < 4; i++)
12659 for (j = 0; j < NUM_BELT_PARTS; j++)
12661 int element = belt_base_active_element[i] + j;
12662 int graphic = el2img(element);
12663 int anim_mode = graphic_info[graphic].anim_mode;
12665 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
12666 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
12671 static void LoadEngineSnapshotValues_RND()
12673 unsigned long num_random_calls = game.num_random_calls;
12676 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12678 int element = EL_CUSTOM_START + i;
12680 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
12683 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12685 int element = EL_GROUP_START + i;
12687 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
12690 for (i = 0; i < 4; i++)
12692 for (j = 0; j < NUM_BELT_PARTS; j++)
12694 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
12695 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
12697 graphic_info[graphic].anim_mode = anim_mode;
12701 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
12703 InitRND(tape.random_seed);
12704 for (i = 0; i < num_random_calls; i++)
12708 if (game.num_random_calls != num_random_calls)
12710 Error(ERR_RETURN, "number of random calls out of sync");
12711 Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
12712 Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
12713 Error(ERR_EXIT, "this should not happen -- please debug");
12717 static void SaveEngineSnapshotBuffer(void *buffer, int size)
12719 struct EngineSnapshotNodeInfo *bi =
12720 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
12722 bi->buffer_orig = buffer;
12723 bi->buffer_copy = checked_malloc(size);
12726 memcpy(bi->buffer_copy, buffer, size);
12728 addNodeToList(&engine_snapshot_list, NULL, bi);
12731 void SaveEngineSnapshot()
12733 FreeEngineSnapshot(); /* free previous snapshot, if needed */
12735 if (level_editor_test_game) /* do not save snapshots from editor */
12738 /* copy some special values to a structure better suited for the snapshot */
12740 SaveEngineSnapshotValues_RND();
12741 SaveEngineSnapshotValues_EM();
12743 /* save values stored in special snapshot structure */
12745 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
12746 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
12748 /* save further RND engine values */
12750 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
12751 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
12752 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
12754 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
12755 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
12756 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
12757 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
12759 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
12760 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
12761 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
12762 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
12763 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
12765 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
12766 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
12767 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
12769 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
12771 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
12773 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
12774 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
12776 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
12777 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
12778 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
12779 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
12780 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
12781 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
12782 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
12783 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
12784 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
12785 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
12786 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
12787 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
12788 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
12789 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
12790 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
12791 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
12792 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
12793 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
12795 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
12796 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
12798 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
12799 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
12800 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
12802 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
12803 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
12805 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
12806 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
12807 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
12808 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
12809 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
12811 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
12812 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
12814 /* save level identification information */
12816 setString(&snapshot_level_identifier, leveldir_current->identifier);
12817 snapshot_level_nr = level_nr;
12820 ListNode *node = engine_snapshot_list;
12823 while (node != NULL)
12825 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
12830 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
12834 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
12836 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
12839 void LoadEngineSnapshot()
12841 ListNode *node = engine_snapshot_list;
12843 if (engine_snapshot_list == NULL)
12846 while (node != NULL)
12848 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
12853 /* restore special values from snapshot structure */
12855 LoadEngineSnapshotValues_RND();
12856 LoadEngineSnapshotValues_EM();
12859 boolean CheckEngineSnapshot()
12861 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
12862 snapshot_level_nr == level_nr);
12866 /* ---------- new game button stuff ---------------------------------------- */
12868 /* graphic position values for game buttons */
12869 #define GAME_BUTTON_XSIZE 30
12870 #define GAME_BUTTON_YSIZE 30
12871 #define GAME_BUTTON_XPOS 5
12872 #define GAME_BUTTON_YPOS 215
12873 #define SOUND_BUTTON_XPOS 5
12874 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12876 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12877 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12878 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12879 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12880 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12881 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12888 } gamebutton_info[NUM_GAME_BUTTONS] =
12891 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12896 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12897 GAME_CTRL_ID_PAUSE,
12901 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12906 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12907 SOUND_CTRL_ID_MUSIC,
12908 "background music on/off"
12911 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12912 SOUND_CTRL_ID_LOOPS,
12913 "sound loops on/off"
12916 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12917 SOUND_CTRL_ID_SIMPLE,
12918 "normal sounds on/off"
12922 void CreateGameButtons()
12926 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12928 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12929 struct GadgetInfo *gi;
12932 unsigned long event_mask;
12933 int gd_xoffset, gd_yoffset;
12934 int gd_x1, gd_x2, gd_y1, gd_y2;
12937 gd_xoffset = gamebutton_info[i].x;
12938 gd_yoffset = gamebutton_info[i].y;
12939 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12940 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12942 if (id == GAME_CTRL_ID_STOP ||
12943 id == GAME_CTRL_ID_PAUSE ||
12944 id == GAME_CTRL_ID_PLAY)
12946 button_type = GD_TYPE_NORMAL_BUTTON;
12948 event_mask = GD_EVENT_RELEASED;
12949 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12950 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12954 button_type = GD_TYPE_CHECK_BUTTON;
12956 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12957 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12958 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12959 event_mask = GD_EVENT_PRESSED;
12960 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12961 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12964 gi = CreateGadget(GDI_CUSTOM_ID, id,
12965 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12966 GDI_X, DX + gd_xoffset,
12967 GDI_Y, DY + gd_yoffset,
12968 GDI_WIDTH, GAME_BUTTON_XSIZE,
12969 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12970 GDI_TYPE, button_type,
12971 GDI_STATE, GD_BUTTON_UNPRESSED,
12972 GDI_CHECKED, checked,
12973 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12974 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12975 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12976 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12977 GDI_EVENT_MASK, event_mask,
12978 GDI_CALLBACK_ACTION, HandleGameButtons,
12982 Error(ERR_EXIT, "cannot create gadget");
12984 game_gadget[id] = gi;
12988 void FreeGameButtons()
12992 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12993 FreeGadget(game_gadget[i]);
12996 static void MapGameButtons()
13000 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13001 MapGadget(game_gadget[i]);
13004 void UnmapGameButtons()
13008 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13009 UnmapGadget(game_gadget[i]);
13012 static void HandleGameButtons(struct GadgetInfo *gi)
13014 int id = gi->custom_id;
13016 if (game_status != GAME_MODE_PLAYING)
13021 case GAME_CTRL_ID_STOP:
13025 RequestQuitGame(TRUE);
13028 case GAME_CTRL_ID_PAUSE:
13029 if (options.network)
13031 #if defined(NETWORK_AVALIABLE)
13033 SendToServer_ContinuePlaying();
13035 SendToServer_PausePlaying();
13039 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13042 case GAME_CTRL_ID_PLAY:
13045 #if defined(NETWORK_AVALIABLE)
13046 if (options.network)
13047 SendToServer_ContinuePlaying();
13051 tape.pausing = FALSE;
13052 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13057 case SOUND_CTRL_ID_MUSIC:
13058 if (setup.sound_music)
13060 setup.sound_music = FALSE;
13063 else if (audio.music_available)
13065 setup.sound = setup.sound_music = TRUE;
13067 SetAudioMode(setup.sound);
13073 case SOUND_CTRL_ID_LOOPS:
13074 if (setup.sound_loops)
13075 setup.sound_loops = FALSE;
13076 else if (audio.loops_available)
13078 setup.sound = setup.sound_loops = TRUE;
13079 SetAudioMode(setup.sound);
13083 case SOUND_CTRL_ID_SIMPLE:
13084 if (setup.sound_simple)
13085 setup.sound_simple = FALSE;
13086 else if (audio.sound_available)
13088 setup.sound = setup.sound_simple = TRUE;
13089 SetAudioMode(setup.sound);