1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
35 /* for MovePlayer() */
36 #define MF_NO_ACTION 0
40 /* for ScrollPlayer() */
42 #define SCROLL_GO_ON 1
45 #define EX_PHASE_START 0
46 #define EX_TYPE_NONE 0
47 #define EX_TYPE_NORMAL (1 << 0)
48 #define EX_TYPE_CENTER (1 << 1)
49 #define EX_TYPE_BORDER (1 << 2)
50 #define EX_TYPE_CROSS (1 << 3)
51 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
53 /* special positions in the game control window (relative to control window) */
56 #define XX_EMERALDS 29
57 #define YY_EMERALDS 54
58 #define XX_DYNAMITE 29
59 #define YY_DYNAMITE 89
68 /* special positions in the game control window (relative to main window) */
69 #define DX_LEVEL (DX + XX_LEVEL)
70 #define DY_LEVEL (DY + YY_LEVEL)
71 #define DX_EMERALDS (DX + XX_EMERALDS)
72 #define DY_EMERALDS (DY + YY_EMERALDS)
73 #define DX_DYNAMITE (DX + XX_DYNAMITE)
74 #define DY_DYNAMITE (DY + YY_DYNAMITE)
75 #define DX_KEYS (DX + XX_KEYS)
76 #define DY_KEYS (DY + YY_KEYS)
77 #define DX_SCORE (DX + XX_SCORE)
78 #define DY_SCORE (DY + YY_SCORE)
79 #define DX_TIME1 (DX + XX_TIME1)
80 #define DX_TIME2 (DX + XX_TIME2)
81 #define DY_TIME (DY + YY_TIME)
83 /* values for initial player move delay (initial delay counter value) */
84 #define INITIAL_MOVE_DELAY_OFF -1
85 #define INITIAL_MOVE_DELAY_ON 0
87 /* values for player movement speed (which is in fact a delay value) */
88 #define MOVE_DELAY_NORMAL_SPEED 8
89 #define MOVE_DELAY_HIGH_SPEED 4
91 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
92 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
93 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
94 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
96 /* values for other actions */
97 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
99 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
100 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
102 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
104 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
105 RND(element_info[e].push_delay_random))
106 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
107 RND(element_info[e].drop_delay_random))
108 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
109 RND(element_info[e].move_delay_random))
110 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
111 (element_info[e].move_delay_random))
113 #define GET_TARGET_ELEMENT(e, ch) \
114 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
115 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
117 #define CAN_GROW_INTO(e) \
118 (e == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
120 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
121 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
124 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
125 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
126 (CAN_MOVE_INTO_ACID(e) && \
127 Feld[x][y] == EL_ACID) || \
130 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
131 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
132 (CAN_MOVE_INTO_ACID(e) && \
133 Feld[x][y] == EL_ACID) || \
136 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
137 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
139 (CAN_MOVE_INTO_ACID(e) && \
140 Feld[x][y] == EL_ACID) || \
141 (DONT_COLLIDE_WITH(e) && \
143 !PLAYER_ENEMY_PROTECTED(x, y))))
146 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
147 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
149 (DONT_COLLIDE_WITH(e) && \
151 !PLAYER_ENEMY_PROTECTED(x, y))))
154 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
155 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
158 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
159 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
161 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
162 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
166 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
169 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
170 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
174 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
175 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
177 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
178 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
180 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
181 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
183 #define PIG_CAN_ENTER_FIELD(e, x, y) \
184 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
186 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
187 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
188 IS_FOOD_PENGUIN(Feld[x][y])))
189 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
192 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
193 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
195 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
196 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
200 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
201 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
202 (CAN_MOVE_INTO_ACID(e) && \
203 Feld[x][y] == EL_ACID) || \
204 Feld[x][y] == EL_DIAMOND))
206 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
207 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
208 (CAN_MOVE_INTO_ACID(e) && \
209 Feld[x][y] == EL_ACID) || \
210 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
212 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
213 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
214 (CAN_MOVE_INTO_ACID(e) && \
215 Feld[x][y] == EL_ACID) || \
216 IS_AMOEBOID(Feld[x][y])))
218 #define PIG_CAN_ENTER_FIELD(e, x, y) \
219 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
220 (CAN_MOVE_INTO_ACID(e) && \
221 Feld[x][y] == EL_ACID) || \
222 IS_FOOD_PIG(Feld[x][y])))
224 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
225 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
226 (CAN_MOVE_INTO_ACID(e) && \
227 Feld[x][y] == EL_ACID) || \
228 IS_FOOD_PENGUIN(Feld[x][y]) || \
229 Feld[x][y] == EL_EXIT_OPEN))
231 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
232 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
233 (CAN_MOVE_INTO_ACID(e) && \
234 Feld[x][y] == EL_ACID)))
236 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
237 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
238 (CAN_MOVE_INTO_ACID(e) && \
239 Feld[x][y] == EL_ACID) || \
242 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
243 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
244 (CAN_MOVE_INTO_ACID(e) && \
245 Feld[x][y] == EL_ACID)))
249 #define GROUP_NR(e) ((e) - EL_GROUP_START)
250 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
251 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
252 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
254 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
255 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
258 #define CE_ENTER_FIELD_COND(e, x, y) \
259 (!IS_PLAYER(x, y) && \
260 (Feld[x][y] == EL_ACID || \
261 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
263 #define CE_ENTER_FIELD_COND(e, x, y) \
264 (!IS_PLAYER(x, y) && \
265 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 boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
293 static boolean MovePlayer(struct PlayerInfo *, int, int);
294 static void ScrollPlayer(struct PlayerInfo *, int);
295 static void ScrollScreen(struct PlayerInfo *, int);
297 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
299 static void InitBeltMovement(void);
300 static void CloseAllOpenTimegates(void);
301 static void CheckGravityMovement(struct PlayerInfo *);
302 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
303 static void KillHeroUnlessEnemyProtected(int, int);
304 static void KillHeroUnlessExplosionProtected(int, int);
306 static void TestIfPlayerTouchesCustomElement(int, int);
307 static void TestIfElementTouchesCustomElement(int, int);
308 static void TestIfElementHitsCustomElement(int, int, int);
310 static void TestIfElementSmashesCustomElement(int, int, int);
313 static void ChangeElement(int, int, int);
315 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
316 #define CheckTriggeredElementChange(x, y, e, ev) \
317 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
319 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
320 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
321 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
322 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
323 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
324 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
327 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
328 #define CheckElementChange(x, y, e, te, ev) \
329 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
330 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
331 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
332 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
333 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
334 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
335 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
337 static void PlayLevelSound(int, int, int);
338 static void PlayLevelSoundNearest(int, int, int);
339 static void PlayLevelSoundAction(int, int, int);
340 static void PlayLevelSoundElementAction(int, int, int, int);
341 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
342 static void PlayLevelSoundActionIfLoop(int, int, int);
343 static void StopLevelSoundActionIfLoop(int, int, int);
344 static void PlayLevelMusic();
346 static void MapGameButtons();
347 static void HandleGameButtons(struct GadgetInfo *);
349 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
352 /* ------------------------------------------------------------------------- */
353 /* definition of elements that automatically change to other elements after */
354 /* a specified time, eventually calling a function when changing */
355 /* ------------------------------------------------------------------------- */
357 /* forward declaration for changer functions */
358 static void InitBuggyBase(int x, int y);
359 static void WarnBuggyBase(int x, int y);
361 static void InitTrap(int x, int y);
362 static void ActivateTrap(int x, int y);
363 static void ChangeActiveTrap(int x, int y);
365 static void InitRobotWheel(int x, int y);
366 static void RunRobotWheel(int x, int y);
367 static void StopRobotWheel(int x, int y);
369 static void InitTimegateWheel(int x, int y);
370 static void RunTimegateWheel(int x, int y);
372 struct ChangingElementInfo
377 void (*pre_change_function)(int x, int y);
378 void (*change_function)(int x, int y);
379 void (*post_change_function)(int x, int y);
382 static struct ChangingElementInfo change_delay_list[] =
433 EL_SWITCHGATE_OPENING,
441 EL_SWITCHGATE_CLOSING,
442 EL_SWITCHGATE_CLOSED,
474 EL_ACID_SPLASH_RIGHT,
483 EL_SP_BUGGY_BASE_ACTIVATING,
490 EL_SP_BUGGY_BASE_ACTIVATING,
491 EL_SP_BUGGY_BASE_ACTIVE,
498 EL_SP_BUGGY_BASE_ACTIVE,
522 EL_ROBOT_WHEEL_ACTIVE,
530 EL_TIMEGATE_SWITCH_ACTIVE,
551 int push_delay_fixed, push_delay_random;
556 { EL_BALLOON, 0, 0 },
558 { EL_SOKOBAN_OBJECT, 2, 0 },
559 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
560 { EL_SATELLITE, 2, 0 },
561 { EL_SP_DISK_YELLOW, 2, 0 },
563 { EL_UNDEFINED, 0, 0 },
571 move_stepsize_list[] =
573 { EL_AMOEBA_DROP, 2 },
574 { EL_AMOEBA_DROPPING, 2 },
575 { EL_QUICKSAND_FILLING, 1 },
576 { EL_QUICKSAND_EMPTYING, 1 },
577 { EL_MAGIC_WALL_FILLING, 2 },
578 { EL_BD_MAGIC_WALL_FILLING, 2 },
579 { EL_MAGIC_WALL_EMPTYING, 2 },
580 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
590 collect_count_list[] =
593 { EL_BD_DIAMOND, 1 },
594 { EL_EMERALD_YELLOW, 1 },
595 { EL_EMERALD_RED, 1 },
596 { EL_EMERALD_PURPLE, 1 },
598 { EL_SP_INFOTRON, 1 },
610 access_direction_list[] =
612 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
613 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
614 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
615 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
616 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
617 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
618 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
619 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
620 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
621 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
622 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
624 { EL_SP_PORT_LEFT, MV_RIGHT },
625 { EL_SP_PORT_RIGHT, MV_LEFT },
626 { EL_SP_PORT_UP, MV_DOWN },
627 { EL_SP_PORT_DOWN, MV_UP },
628 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
629 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
630 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
631 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
632 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
633 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
634 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
636 { EL_UNDEFINED, MV_NO_MOVING }
639 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
641 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
642 CH_EVENT_BIT(CE_DELAY))
643 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
644 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
645 IS_JUST_CHANGING(x, y))
647 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
650 void GetPlayerConfig()
652 if (!audio.sound_available)
653 setup.sound_simple = FALSE;
655 if (!audio.loops_available)
656 setup.sound_loops = FALSE;
658 if (!audio.music_available)
659 setup.sound_music = FALSE;
661 if (!video.fullscreen_available)
662 setup.fullscreen = FALSE;
664 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
666 SetAudioMode(setup.sound);
670 static int getBeltNrFromBeltElement(int element)
672 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
673 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
674 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
677 static int getBeltNrFromBeltActiveElement(int element)
679 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
680 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
681 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
684 static int getBeltNrFromBeltSwitchElement(int element)
686 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
687 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
688 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
691 static int getBeltDirNrFromBeltSwitchElement(int element)
693 static int belt_base_element[4] =
695 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
696 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
697 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
698 EL_CONVEYOR_BELT_4_SWITCH_LEFT
701 int belt_nr = getBeltNrFromBeltSwitchElement(element);
702 int belt_dir_nr = element - belt_base_element[belt_nr];
704 return (belt_dir_nr % 3);
707 static int getBeltDirFromBeltSwitchElement(int element)
709 static int belt_move_dir[3] =
716 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
718 return belt_move_dir[belt_dir_nr];
721 static void InitPlayerField(int x, int y, int element, boolean init_game)
723 if (element == EL_SP_MURPHY)
727 if (stored_player[0].present)
729 Feld[x][y] = EL_SP_MURPHY_CLONE;
735 stored_player[0].use_murphy_graphic = TRUE;
738 Feld[x][y] = EL_PLAYER_1;
744 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
745 int jx = player->jx, jy = player->jy;
747 player->present = TRUE;
749 player->block_last_field = (element == EL_SP_MURPHY ?
750 level.sp_block_last_field :
751 level.block_last_field);
753 if (!options.network || player->connected)
755 player->active = TRUE;
757 /* remove potentially duplicate players */
758 if (StorePlayer[jx][jy] == Feld[x][y])
759 StorePlayer[jx][jy] = 0;
761 StorePlayer[x][y] = Feld[x][y];
765 printf("Player %d activated.\n", player->element_nr);
766 printf("[Local player is %d and currently %s.]\n",
767 local_player->element_nr,
768 local_player->active ? "active" : "not active");
772 Feld[x][y] = EL_EMPTY;
774 player->jx = player->last_jx = x;
775 player->jy = player->last_jy = y;
779 static void InitField(int x, int y, boolean init_game)
781 int element = Feld[x][y];
790 InitPlayerField(x, y, element, init_game);
793 case EL_SOKOBAN_FIELD_PLAYER:
794 element = Feld[x][y] = EL_PLAYER_1;
795 InitField(x, y, init_game);
797 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
798 InitField(x, y, init_game);
801 case EL_SOKOBAN_FIELD_EMPTY:
802 local_player->sokobanfields_still_needed++;
806 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
807 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
808 else if (x > 0 && Feld[x-1][y] == EL_ACID)
809 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
810 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
811 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
812 else if (y > 0 && Feld[x][y-1] == EL_ACID)
813 Feld[x][y] = EL_ACID_POOL_BOTTOM;
814 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
815 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
823 case EL_SPACESHIP_RIGHT:
824 case EL_SPACESHIP_UP:
825 case EL_SPACESHIP_LEFT:
826 case EL_SPACESHIP_DOWN:
828 case EL_BD_BUTTERFLY_RIGHT:
829 case EL_BD_BUTTERFLY_UP:
830 case EL_BD_BUTTERFLY_LEFT:
831 case EL_BD_BUTTERFLY_DOWN:
832 case EL_BD_BUTTERFLY:
833 case EL_BD_FIREFLY_RIGHT:
834 case EL_BD_FIREFLY_UP:
835 case EL_BD_FIREFLY_LEFT:
836 case EL_BD_FIREFLY_DOWN:
838 case EL_PACMAN_RIGHT:
862 if (y == lev_fieldy - 1)
864 Feld[x][y] = EL_AMOEBA_GROWING;
865 Store[x][y] = EL_AMOEBA_WET;
869 case EL_DYNAMITE_ACTIVE:
870 case EL_SP_DISK_RED_ACTIVE:
871 case EL_DYNABOMB_PLAYER_1_ACTIVE:
872 case EL_DYNABOMB_PLAYER_2_ACTIVE:
873 case EL_DYNABOMB_PLAYER_3_ACTIVE:
874 case EL_DYNABOMB_PLAYER_4_ACTIVE:
879 local_player->lights_still_needed++;
883 local_player->friends_still_needed++;
888 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
893 Feld[x][y] = EL_EMPTY;
898 case EL_EM_KEY_1_FILE:
899 Feld[x][y] = EL_EM_KEY_1;
901 case EL_EM_KEY_2_FILE:
902 Feld[x][y] = EL_EM_KEY_2;
904 case EL_EM_KEY_3_FILE:
905 Feld[x][y] = EL_EM_KEY_3;
907 case EL_EM_KEY_4_FILE:
908 Feld[x][y] = EL_EM_KEY_4;
912 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
913 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
914 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
915 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
916 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
917 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
918 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
919 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
920 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
921 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
922 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
923 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
926 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
927 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
928 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
930 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
932 game.belt_dir[belt_nr] = belt_dir;
933 game.belt_dir_nr[belt_nr] = belt_dir_nr;
935 else /* more than one switch -- set it like the first switch */
937 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
942 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
944 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
947 case EL_LIGHT_SWITCH_ACTIVE:
949 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
953 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
955 else if (IS_GROUP_ELEMENT(element))
957 struct ElementGroupInfo *group = element_info[element].group;
958 int last_anim_random_frame = gfx.anim_random_frame;
961 if (group->choice_mode == ANIM_RANDOM)
962 gfx.anim_random_frame = RND(group->num_elements_resolved);
964 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
965 group->choice_mode, 0,
968 if (group->choice_mode == ANIM_RANDOM)
969 gfx.anim_random_frame = last_anim_random_frame;
973 Feld[x][y] = group->element_resolved[element_pos];
975 InitField(x, y, init_game);
981 static inline void InitField_WithBug1(int x, int y, boolean init_game)
983 InitField(x, y, init_game);
985 /* not needed to call InitMovDir() -- already done by InitField()! */
986 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
987 CAN_MOVE(Feld[x][y]))
991 static inline void InitField_WithBug2(int x, int y, boolean init_game)
993 int old_element = Feld[x][y];
995 InitField(x, y, init_game);
997 /* not needed to call InitMovDir() -- already done by InitField()! */
998 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
999 CAN_MOVE(old_element) &&
1000 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1003 /* this case is in fact a combination of not less than three bugs:
1004 first, it calls InitMovDir() for elements that can move, although this is
1005 already done by InitField(); then, it checks the element that was at this
1006 field _before_ the call to InitField() (which can change it); lastly, it
1007 was not called for "mole with direction" elements, which were treated as
1008 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1012 inline void DrawGameValue_Emeralds(int value)
1014 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1017 inline void DrawGameValue_Dynamite(int value)
1019 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1022 inline void DrawGameValue_Keys(struct PlayerInfo *player)
1026 for (i = 0; i < MAX_KEYS; i++)
1028 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1029 el2edimg(EL_KEY_1 + i));
1032 inline void DrawGameValue_Score(int value)
1034 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1037 inline void DrawGameValue_Time(int value)
1040 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1042 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1045 inline void DrawGameValue_Level(int value)
1048 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1051 /* misuse area for displaying emeralds to draw bigger level number */
1052 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1053 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1055 /* now copy it to the area for displaying level number */
1056 BlitBitmap(drawto, drawto,
1057 DX_EMERALDS, DY_EMERALDS + 1,
1058 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1059 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1060 DX_LEVEL - 1, DY_LEVEL + 1);
1062 /* restore the area for displaying emeralds */
1063 DrawGameValue_Emeralds(local_player->gems_still_needed);
1065 /* yes, this is all really ugly :-) */
1069 void DrawGameDoorValues()
1073 DrawGameValue_Level(level_nr);
1075 for (i = 0; i < MAX_PLAYERS; i++)
1076 DrawGameValue_Keys(&stored_player[i]);
1078 DrawGameValue_Emeralds(local_player->gems_still_needed);
1079 DrawGameValue_Dynamite(local_player->inventory_size);
1080 DrawGameValue_Score(local_player->score);
1081 DrawGameValue_Time(TimeLeft);
1084 static void resolve_group_element(int group_element, int recursion_depth)
1086 static int group_nr;
1087 static struct ElementGroupInfo *group;
1088 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1091 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1093 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1094 group_element - EL_GROUP_START + 1);
1096 /* replace element which caused too deep recursion by question mark */
1097 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1102 if (recursion_depth == 0) /* initialization */
1104 group = element_info[group_element].group;
1105 group_nr = group_element - EL_GROUP_START;
1107 group->num_elements_resolved = 0;
1108 group->choice_pos = 0;
1111 for (i = 0; i < actual_group->num_elements; i++)
1113 int element = actual_group->element[i];
1115 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1118 if (IS_GROUP_ELEMENT(element))
1119 resolve_group_element(element, recursion_depth + 1);
1122 group->element_resolved[group->num_elements_resolved++] = element;
1123 element_info[element].in_group[group_nr] = TRUE;
1128 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1130 printf("::: group %d: %d resolved elements\n",
1131 group_element - EL_GROUP_START, group->num_elements_resolved);
1132 for (i = 0; i < group->num_elements_resolved; i++)
1133 printf("::: - %d ['%s']\n", group->element_resolved[i],
1134 element_info[group->element_resolved[i]].token_name);
1141 =============================================================================
1143 -----------------------------------------------------------------------------
1144 initialize game engine due to level / tape version number
1145 =============================================================================
1148 static void InitGameEngine()
1152 /* set game engine from tape file when re-playing, else from level file */
1153 game.engine_version = (tape.playing ? tape.engine_version :
1154 level.game_version);
1156 /* dynamically adjust element properties according to game engine version */
1157 InitElementPropertiesEngine(game.engine_version);
1160 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1161 printf(" tape version == %06d [%s] [file: %06d]\n",
1162 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1164 printf(" => game.engine_version == %06d\n", game.engine_version);
1167 /* ---------- recursively resolve group elements ------------------------- */
1169 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1170 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1171 element_info[i].in_group[j] = FALSE;
1173 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1174 resolve_group_element(EL_GROUP_START + i, 0);
1176 /* ---------- initialize player's initial move delay --------------------- */
1178 /* dynamically adjust player properties according to game engine version */
1179 game.initial_move_delay =
1180 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1181 INITIAL_MOVE_DELAY_OFF);
1183 /* dynamically adjust player properties according to level information */
1184 game.initial_move_delay_value =
1185 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1187 /* ---------- initialize player's initial push delay --------------------- */
1189 /* dynamically adjust player properties according to game engine version */
1190 game.initial_push_delay_value =
1191 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1193 /* ---------- initialize changing elements ------------------------------- */
1195 /* initialize changing elements information */
1196 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1198 struct ElementInfo *ei = &element_info[i];
1200 /* this pointer might have been changed in the level editor */
1201 ei->change = &ei->change_page[0];
1203 if (!IS_CUSTOM_ELEMENT(i))
1205 ei->change->target_element = EL_EMPTY_SPACE;
1206 ei->change->delay_fixed = 0;
1207 ei->change->delay_random = 0;
1208 ei->change->delay_frames = 1;
1211 ei->change_events = CE_BITMASK_DEFAULT;
1212 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1214 ei->event_page_nr[j] = 0;
1215 ei->event_page[j] = &ei->change_page[0];
1219 /* add changing elements from pre-defined list */
1220 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1222 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1223 struct ElementInfo *ei = &element_info[ch_delay->element];
1225 ei->change->target_element = ch_delay->target_element;
1226 ei->change->delay_fixed = ch_delay->change_delay;
1228 ei->change->pre_change_function = ch_delay->pre_change_function;
1229 ei->change->change_function = ch_delay->change_function;
1230 ei->change->post_change_function = ch_delay->post_change_function;
1232 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1235 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1240 /* add change events from custom element configuration */
1241 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1243 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1245 for (j = 0; j < ei->num_change_pages; j++)
1247 if (!ei->change_page[j].can_change)
1250 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1252 /* only add event page for the first page found with this event */
1253 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1254 !(ei->change_events & CH_EVENT_BIT(k)))
1256 ei->change_events |= CH_EVENT_BIT(k);
1257 ei->event_page_nr[k] = j;
1258 ei->event_page[k] = &ei->change_page[j];
1266 /* add change events from custom element configuration */
1267 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1269 int element = EL_CUSTOM_START + i;
1271 /* only add custom elements that change after fixed/random frame delay */
1272 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1273 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1277 /* ---------- initialize run-time trigger player and element ------------- */
1279 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1281 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1283 for (j = 0; j < ei->num_change_pages; j++)
1285 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1286 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1290 /* ---------- initialize trigger events ---------------------------------- */
1292 /* initialize trigger events information */
1293 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1294 trigger_events[i] = EP_BITMASK_DEFAULT;
1297 /* add trigger events from element change event properties */
1298 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1300 struct ElementInfo *ei = &element_info[i];
1302 for (j = 0; j < ei->num_change_pages; j++)
1304 if (!ei->change_page[j].can_change)
1307 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1309 int trigger_element = ei->change_page[j].trigger_element;
1311 if (IS_GROUP_ELEMENT(trigger_element))
1313 struct ElementGroupInfo *group = element_info[trigger_element].group;
1315 for (k = 0; k < group->num_elements_resolved; k++)
1316 trigger_events[group->element_resolved[k]]
1317 |= ei->change_page[j].events;
1320 trigger_events[trigger_element] |= ei->change_page[j].events;
1325 /* add trigger events from element change event properties */
1326 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1327 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1328 trigger_events[element_info[i].change->trigger_element] |=
1329 element_info[i].change->events;
1332 /* ---------- initialize push delay -------------------------------------- */
1334 /* initialize push delay values to default */
1335 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1337 if (!IS_CUSTOM_ELEMENT(i))
1339 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1340 element_info[i].push_delay_random = game.default_push_delay_random;
1344 /* set push delay value for certain elements from pre-defined list */
1345 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1347 int e = push_delay_list[i].element;
1349 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1350 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1353 /* set push delay value for Supaplex elements for newer engine versions */
1354 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1356 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1358 if (IS_SP_ELEMENT(i))
1360 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1361 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1366 /* ---------- initialize move stepsize ----------------------------------- */
1368 /* initialize move stepsize values to default */
1369 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1370 if (!IS_CUSTOM_ELEMENT(i))
1371 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1373 /* set move stepsize value for certain elements from pre-defined list */
1374 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1376 int e = move_stepsize_list[i].element;
1378 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1382 /* ---------- initialize move dig/leave ---------------------------------- */
1384 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1386 element_info[i].can_leave_element = FALSE;
1387 element_info[i].can_leave_element_last = FALSE;
1391 /* ---------- initialize gem count --------------------------------------- */
1393 /* initialize gem count values for each element */
1394 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1395 if (!IS_CUSTOM_ELEMENT(i))
1396 element_info[i].collect_count = 0;
1398 /* add gem count values for all elements from pre-defined list */
1399 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1400 element_info[collect_count_list[i].element].collect_count =
1401 collect_count_list[i].count;
1403 /* ---------- initialize access direction -------------------------------- */
1405 /* initialize access direction values to default (access from every side) */
1406 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1407 if (!IS_CUSTOM_ELEMENT(i))
1408 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1410 /* set access direction value for certain elements from pre-defined list */
1411 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1412 element_info[access_direction_list[i].element].access_direction =
1413 access_direction_list[i].direction;
1418 =============================================================================
1420 -----------------------------------------------------------------------------
1421 initialize and start new game
1422 =============================================================================
1427 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1428 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1429 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1436 #if USE_NEW_AMOEBA_CODE
1437 printf("Using new amoeba code.\n");
1439 printf("Using old amoeba code.\n");
1444 /* don't play tapes over network */
1445 network_playing = (options.network && !tape.playing);
1447 for (i = 0; i < MAX_PLAYERS; i++)
1449 struct PlayerInfo *player = &stored_player[i];
1451 player->index_nr = i;
1452 player->index_bit = (1 << i);
1453 player->element_nr = EL_PLAYER_1 + i;
1455 player->present = FALSE;
1456 player->active = FALSE;
1459 player->effective_action = 0;
1460 player->programmed_action = 0;
1463 player->gems_still_needed = level.gems_needed;
1464 player->sokobanfields_still_needed = 0;
1465 player->lights_still_needed = 0;
1466 player->friends_still_needed = 0;
1468 for (j = 0; j < MAX_KEYS; j++)
1469 player->key[j] = FALSE;
1471 player->dynabomb_count = 0;
1472 player->dynabomb_size = 1;
1473 player->dynabombs_left = 0;
1474 player->dynabomb_xl = FALSE;
1476 player->MovDir = MV_NO_MOVING;
1479 player->GfxDir = MV_NO_MOVING;
1480 player->GfxAction = ACTION_DEFAULT;
1482 player->StepFrame = 0;
1484 player->use_murphy_graphic = FALSE;
1486 player->block_last_field = FALSE;
1487 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1489 player->actual_frame_counter = 0;
1491 player->step_counter = 0;
1493 player->last_move_dir = MV_NO_MOVING;
1495 player->is_waiting = FALSE;
1496 player->is_moving = FALSE;
1497 player->is_auto_moving = FALSE;
1498 player->is_digging = FALSE;
1499 player->is_snapping = FALSE;
1500 player->is_collecting = FALSE;
1501 player->is_pushing = FALSE;
1502 player->is_switching = FALSE;
1503 player->is_dropping = FALSE;
1505 player->is_bored = FALSE;
1506 player->is_sleeping = FALSE;
1508 player->frame_counter_bored = -1;
1509 player->frame_counter_sleeping = -1;
1511 player->anim_delay_counter = 0;
1512 player->post_delay_counter = 0;
1514 player->action_waiting = ACTION_DEFAULT;
1515 player->last_action_waiting = ACTION_DEFAULT;
1516 player->special_action_bored = ACTION_DEFAULT;
1517 player->special_action_sleeping = ACTION_DEFAULT;
1519 player->num_special_action_bored = 0;
1520 player->num_special_action_sleeping = 0;
1522 /* determine number of special actions for bored and sleeping animation */
1523 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1525 boolean found = FALSE;
1527 for (k = 0; k < NUM_DIRECTIONS; k++)
1528 if (el_act_dir2img(player->element_nr, j, k) !=
1529 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1533 player->num_special_action_bored++;
1537 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1539 boolean found = FALSE;
1541 for (k = 0; k < NUM_DIRECTIONS; k++)
1542 if (el_act_dir2img(player->element_nr, j, k) !=
1543 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1547 player->num_special_action_sleeping++;
1552 player->switch_x = -1;
1553 player->switch_y = -1;
1555 player->show_envelope = 0;
1557 player->move_delay = game.initial_move_delay;
1558 player->move_delay_value = game.initial_move_delay_value;
1560 player->move_delay_reset_counter = 0;
1562 player->push_delay = 0;
1563 player->push_delay_value = game.initial_push_delay_value;
1565 player->drop_delay = 0;
1567 player->last_jx = player->last_jy = 0;
1568 player->jx = player->jy = 0;
1570 player->shield_normal_time_left = 0;
1571 player->shield_deadly_time_left = 0;
1573 player->inventory_infinite_element = EL_UNDEFINED;
1574 player->inventory_size = 0;
1576 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1577 SnapField(player, 0, 0);
1579 player->LevelSolved = FALSE;
1580 player->GameOver = FALSE;
1583 network_player_action_received = FALSE;
1585 #if defined(NETWORK_AVALIABLE)
1586 /* initial null action */
1587 if (network_playing)
1588 SendToServer_MovePlayer(MV_NO_MOVING);
1596 TimeLeft = level.time;
1599 ScreenMovDir = MV_NO_MOVING;
1603 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1605 AllPlayersGone = FALSE;
1607 game.yamyam_content_nr = 0;
1608 game.magic_wall_active = FALSE;
1609 game.magic_wall_time_left = 0;
1610 game.light_time_left = 0;
1611 game.timegate_time_left = 0;
1612 game.switchgate_pos = 0;
1613 game.balloon_dir = MV_NO_MOVING;
1614 game.gravity = level.initial_gravity;
1615 game.explosions_delayed = TRUE;
1617 game.envelope_active = FALSE;
1619 for (i = 0; i < NUM_BELTS; i++)
1621 game.belt_dir[i] = MV_NO_MOVING;
1622 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1625 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1626 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1628 for (x = 0; x < lev_fieldx; x++)
1630 for (y = 0; y < lev_fieldy; y++)
1632 Feld[x][y] = level.field[x][y];
1633 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1634 ChangeDelay[x][y] = 0;
1635 ChangePage[x][y] = -1;
1636 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1638 WasJustMoving[x][y] = 0;
1639 WasJustFalling[x][y] = 0;
1640 CheckCollision[x][y] = 0;
1642 Pushed[x][y] = FALSE;
1644 Changed[x][y] = CE_BITMASK_DEFAULT;
1645 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1647 ExplodePhase[x][y] = 0;
1648 ExplodeDelay[x][y] = 0;
1649 ExplodeField[x][y] = EX_TYPE_NONE;
1651 RunnerVisit[x][y] = 0;
1652 PlayerVisit[x][y] = 0;
1655 GfxRandom[x][y] = INIT_GFX_RANDOM();
1656 GfxElement[x][y] = EL_UNDEFINED;
1657 GfxAction[x][y] = ACTION_DEFAULT;
1658 GfxDir[x][y] = MV_NO_MOVING;
1662 for (y = 0; y < lev_fieldy; y++)
1664 for (x = 0; x < lev_fieldx; x++)
1666 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1668 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1670 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1673 InitField(x, y, TRUE);
1679 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1680 emulate_sb ? EMU_SOKOBAN :
1681 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1683 /* initialize explosion and ignition delay */
1684 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1686 if (!IS_CUSTOM_ELEMENT(i))
1689 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1690 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1691 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1692 int last_phase = (num_phase + 1) * delay;
1693 int half_phase = (num_phase / 2) * delay;
1695 element_info[i].explosion_delay = last_phase - 1;
1696 element_info[i].ignition_delay = half_phase;
1699 if (i == EL_BLACK_ORB)
1700 element_info[i].ignition_delay = 0;
1702 if (i == EL_BLACK_ORB)
1703 element_info[i].ignition_delay = 1;
1708 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1709 element_info[i].explosion_delay = 1;
1711 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1712 element_info[i].ignition_delay = 1;
1716 /* correct non-moving belts to start moving left */
1717 for (i = 0; i < NUM_BELTS; i++)
1718 if (game.belt_dir[i] == MV_NO_MOVING)
1719 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1721 /* check if any connected player was not found in playfield */
1722 for (i = 0; i < MAX_PLAYERS; i++)
1724 struct PlayerInfo *player = &stored_player[i];
1726 if (player->connected && !player->present)
1728 for (j = 0; j < MAX_PLAYERS; j++)
1730 struct PlayerInfo *some_player = &stored_player[j];
1731 int jx = some_player->jx, jy = some_player->jy;
1733 /* assign first free player found that is present in the playfield */
1734 if (some_player->present && !some_player->connected)
1736 player->present = TRUE;
1737 player->active = TRUE;
1739 some_player->present = FALSE;
1740 some_player->active = FALSE;
1743 player->element_nr = some_player->element_nr;
1746 StorePlayer[jx][jy] = player->element_nr;
1747 player->jx = player->last_jx = jx;
1748 player->jy = player->last_jy = jy;
1758 /* when playing a tape, eliminate all players which do not participate */
1760 for (i = 0; i < MAX_PLAYERS; i++)
1762 if (stored_player[i].active && !tape.player_participates[i])
1764 struct PlayerInfo *player = &stored_player[i];
1765 int jx = player->jx, jy = player->jy;
1767 player->active = FALSE;
1768 StorePlayer[jx][jy] = 0;
1769 Feld[jx][jy] = EL_EMPTY;
1773 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1775 /* when in single player mode, eliminate all but the first active player */
1777 for (i = 0; i < MAX_PLAYERS; i++)
1779 if (stored_player[i].active)
1781 for (j = i + 1; j < MAX_PLAYERS; j++)
1783 if (stored_player[j].active)
1785 struct PlayerInfo *player = &stored_player[j];
1786 int jx = player->jx, jy = player->jy;
1788 player->active = FALSE;
1789 player->present = FALSE;
1791 StorePlayer[jx][jy] = 0;
1792 Feld[jx][jy] = EL_EMPTY;
1799 /* when recording the game, store which players take part in the game */
1802 for (i = 0; i < MAX_PLAYERS; i++)
1803 if (stored_player[i].active)
1804 tape.player_participates[i] = TRUE;
1809 for (i = 0; i < MAX_PLAYERS; i++)
1811 struct PlayerInfo *player = &stored_player[i];
1813 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1818 if (local_player == player)
1819 printf("Player %d is local player.\n", i+1);
1823 if (BorderElement == EL_EMPTY)
1826 SBX_Right = lev_fieldx - SCR_FIELDX;
1828 SBY_Lower = lev_fieldy - SCR_FIELDY;
1833 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1835 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1838 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1839 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1841 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1842 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1844 /* if local player not found, look for custom element that might create
1845 the player (make some assumptions about the right custom element) */
1846 if (!local_player->present)
1848 int start_x = 0, start_y = 0;
1849 int found_rating = 0;
1850 int found_element = EL_UNDEFINED;
1852 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1854 int element = Feld[x][y];
1859 if (!IS_CUSTOM_ELEMENT(element))
1862 if (CAN_CHANGE(element))
1864 for (i = 0; i < element_info[element].num_change_pages; i++)
1866 content = element_info[element].change_page[i].target_element;
1867 is_player = ELEM_IS_PLAYER(content);
1869 if (is_player && (found_rating < 3 || element < found_element))
1875 found_element = element;
1880 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1882 content = element_info[element].content[xx][yy];
1883 is_player = ELEM_IS_PLAYER(content);
1885 if (is_player && (found_rating < 2 || element < found_element))
1887 start_x = x + xx - 1;
1888 start_y = y + yy - 1;
1891 found_element = element;
1894 if (!CAN_CHANGE(element))
1897 for (i = 0; i < element_info[element].num_change_pages; i++)
1899 content= element_info[element].change_page[i].target_content[xx][yy];
1900 is_player = ELEM_IS_PLAYER(content);
1902 if (is_player && (found_rating < 1 || element < found_element))
1904 start_x = x + xx - 1;
1905 start_y = y + yy - 1;
1908 found_element = element;
1914 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1915 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1918 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1919 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1925 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1926 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1927 local_player->jx - MIDPOSX);
1929 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1930 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1931 local_player->jy - MIDPOSY);
1933 scroll_x = SBX_Left;
1934 scroll_y = SBY_Upper;
1935 if (local_player->jx >= SBX_Left + MIDPOSX)
1936 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1937 local_player->jx - MIDPOSX :
1939 if (local_player->jy >= SBY_Upper + MIDPOSY)
1940 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1941 local_player->jy - MIDPOSY :
1946 CloseDoor(DOOR_CLOSE_1);
1951 /* after drawing the level, correct some elements */
1952 if (game.timegate_time_left == 0)
1953 CloseAllOpenTimegates();
1955 if (setup.soft_scrolling)
1956 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1958 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1961 /* copy default game door content to main double buffer */
1962 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1963 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1965 DrawGameDoorValues();
1969 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1970 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1971 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1975 /* copy actual game door content to door double buffer for OpenDoor() */
1976 BlitBitmap(drawto, bitmap_db_door,
1977 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1979 OpenDoor(DOOR_OPEN_ALL);
1981 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1983 if (setup.sound_music)
1986 KeyboardAutoRepeatOffUnlessAutoplay();
1990 for (i = 0; i < MAX_PLAYERS; i++)
1991 printf("Player %d %sactive.\n",
1992 i + 1, (stored_player[i].active ? "" : "not "));
1996 printf("::: starting game [%d]\n", FrameCounter);
2000 void InitMovDir(int x, int y)
2002 int i, element = Feld[x][y];
2003 static int xy[4][2] =
2010 static int direction[3][4] =
2012 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2013 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2014 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2023 Feld[x][y] = EL_BUG;
2024 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2027 case EL_SPACESHIP_RIGHT:
2028 case EL_SPACESHIP_UP:
2029 case EL_SPACESHIP_LEFT:
2030 case EL_SPACESHIP_DOWN:
2031 Feld[x][y] = EL_SPACESHIP;
2032 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2035 case EL_BD_BUTTERFLY_RIGHT:
2036 case EL_BD_BUTTERFLY_UP:
2037 case EL_BD_BUTTERFLY_LEFT:
2038 case EL_BD_BUTTERFLY_DOWN:
2039 Feld[x][y] = EL_BD_BUTTERFLY;
2040 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2043 case EL_BD_FIREFLY_RIGHT:
2044 case EL_BD_FIREFLY_UP:
2045 case EL_BD_FIREFLY_LEFT:
2046 case EL_BD_FIREFLY_DOWN:
2047 Feld[x][y] = EL_BD_FIREFLY;
2048 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2051 case EL_PACMAN_RIGHT:
2053 case EL_PACMAN_LEFT:
2054 case EL_PACMAN_DOWN:
2055 Feld[x][y] = EL_PACMAN;
2056 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2059 case EL_SP_SNIKSNAK:
2060 MovDir[x][y] = MV_UP;
2063 case EL_SP_ELECTRON:
2064 MovDir[x][y] = MV_LEFT;
2071 Feld[x][y] = EL_MOLE;
2072 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2076 if (IS_CUSTOM_ELEMENT(element))
2078 struct ElementInfo *ei = &element_info[element];
2079 int move_direction_initial = ei->move_direction_initial;
2080 int move_pattern = ei->move_pattern;
2082 if (move_direction_initial == MV_START_PREVIOUS)
2084 if (MovDir[x][y] != MV_NO_MOVING)
2087 move_direction_initial = MV_START_AUTOMATIC;
2090 if (move_direction_initial == MV_START_RANDOM)
2091 MovDir[x][y] = 1 << RND(4);
2092 else if (move_direction_initial & MV_ANY_DIRECTION)
2093 MovDir[x][y] = move_direction_initial;
2094 else if (move_pattern == MV_ALL_DIRECTIONS ||
2095 move_pattern == MV_TURNING_LEFT ||
2096 move_pattern == MV_TURNING_RIGHT ||
2097 move_pattern == MV_TURNING_LEFT_RIGHT ||
2098 move_pattern == MV_TURNING_RIGHT_LEFT ||
2099 move_pattern == MV_TURNING_RANDOM)
2100 MovDir[x][y] = 1 << RND(4);
2101 else if (move_pattern == MV_HORIZONTAL)
2102 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2103 else if (move_pattern == MV_VERTICAL)
2104 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2105 else if (move_pattern & MV_ANY_DIRECTION)
2106 MovDir[x][y] = element_info[element].move_pattern;
2107 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2108 move_pattern == MV_ALONG_RIGHT_SIDE)
2111 /* use random direction as default start direction */
2112 if (game.engine_version >= VERSION_IDENT(3,1,0,2))
2113 MovDir[x][y] = 1 << RND(4);
2116 for (i = 0; i < NUM_DIRECTIONS; i++)
2118 int x1 = x + xy[i][0];
2119 int y1 = y + xy[i][1];
2121 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2123 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2124 MovDir[x][y] = direction[0][i];
2126 MovDir[x][y] = direction[1][i];
2135 MovDir[x][y] = 1 << RND(4);
2137 if (element != EL_BUG &&
2138 element != EL_SPACESHIP &&
2139 element != EL_BD_BUTTERFLY &&
2140 element != EL_BD_FIREFLY)
2143 for (i = 0; i < NUM_DIRECTIONS; i++)
2145 int x1 = x + xy[i][0];
2146 int y1 = y + xy[i][1];
2148 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2150 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2152 MovDir[x][y] = direction[0][i];
2155 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2156 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2158 MovDir[x][y] = direction[1][i];
2167 GfxDir[x][y] = MovDir[x][y];
2170 void InitAmoebaNr(int x, int y)
2173 int group_nr = AmoebeNachbarNr(x, y);
2177 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2179 if (AmoebaCnt[i] == 0)
2187 AmoebaNr[x][y] = group_nr;
2188 AmoebaCnt[group_nr]++;
2189 AmoebaCnt2[group_nr]++;
2195 boolean raise_level = FALSE;
2197 if (local_player->MovPos)
2201 if (tape.auto_play) /* tape might already be stopped here */
2202 tape.auto_play_level_solved = TRUE;
2204 if (tape.playing && tape.auto_play)
2205 tape.auto_play_level_solved = TRUE;
2208 local_player->LevelSolved = FALSE;
2210 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2214 if (!tape.playing && setup.sound_loops)
2215 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2216 SND_CTRL_PLAY_LOOP);
2218 while (TimeLeft > 0)
2220 if (!tape.playing && !setup.sound_loops)
2221 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2222 if (TimeLeft > 0 && !(TimeLeft % 10))
2223 RaiseScore(level.score[SC_TIME_BONUS]);
2224 if (TimeLeft > 100 && !(TimeLeft % 10))
2229 DrawGameValue_Time(TimeLeft);
2237 if (!tape.playing && setup.sound_loops)
2238 StopSound(SND_GAME_LEVELTIME_BONUS);
2240 else if (level.time == 0) /* level without time limit */
2242 if (!tape.playing && setup.sound_loops)
2243 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2244 SND_CTRL_PLAY_LOOP);
2246 while (TimePlayed < 999)
2248 if (!tape.playing && !setup.sound_loops)
2249 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2250 if (TimePlayed < 999 && !(TimePlayed % 10))
2251 RaiseScore(level.score[SC_TIME_BONUS]);
2252 if (TimePlayed < 900 && !(TimePlayed % 10))
2257 DrawGameValue_Time(TimePlayed);
2265 if (!tape.playing && setup.sound_loops)
2266 StopSound(SND_GAME_LEVELTIME_BONUS);
2269 /* close exit door after last player */
2270 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2271 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2273 int element = Feld[ExitX][ExitY];
2275 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2276 EL_SP_EXIT_CLOSING);
2278 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2281 /* Hero disappears */
2282 DrawLevelField(ExitX, ExitY);
2288 CloseDoor(DOOR_CLOSE_1);
2293 SaveTape(tape.level_nr); /* Ask to save tape */
2296 if (level_nr == leveldir_current->handicap_level)
2298 leveldir_current->handicap_level++;
2299 SaveLevelSetup_SeriesInfo();
2302 if (level_editor_test_game)
2303 local_player->score = -1; /* no highscore when playing from editor */
2304 else if (level_nr < leveldir_current->last_level)
2305 raise_level = TRUE; /* advance to next level */
2307 if ((hi_pos = NewHiScore()) >= 0)
2309 game_status = GAME_MODE_SCORES;
2310 DrawHallOfFame(hi_pos);
2319 game_status = GAME_MODE_MAIN;
2336 LoadScore(level_nr);
2338 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2339 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2342 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2344 if (local_player->score > highscore[k].Score)
2346 /* player has made it to the hall of fame */
2348 if (k < MAX_SCORE_ENTRIES - 1)
2350 int m = MAX_SCORE_ENTRIES - 1;
2353 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2354 if (!strcmp(setup.player_name, highscore[l].Name))
2356 if (m == k) /* player's new highscore overwrites his old one */
2360 for (l = m; l > k; l--)
2362 strcpy(highscore[l].Name, highscore[l - 1].Name);
2363 highscore[l].Score = highscore[l - 1].Score;
2370 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2371 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2372 highscore[k].Score = local_player->score;
2378 else if (!strncmp(setup.player_name, highscore[k].Name,
2379 MAX_PLAYER_NAME_LEN))
2380 break; /* player already there with a higher score */
2386 SaveScore(level_nr);
2391 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2393 if (player->GfxAction != action || player->GfxDir != dir)
2396 printf("Player frame reset! (%d => %d, %d => %d)\n",
2397 player->GfxAction, action, player->GfxDir, dir);
2400 player->GfxAction = action;
2401 player->GfxDir = dir;
2403 player->StepFrame = 0;
2407 static void ResetRandomAnimationValue(int x, int y)
2409 GfxRandom[x][y] = INIT_GFX_RANDOM();
2412 static void ResetGfxAnimation(int x, int y)
2415 GfxAction[x][y] = ACTION_DEFAULT;
2416 GfxDir[x][y] = MovDir[x][y];
2419 void InitMovingField(int x, int y, int direction)
2421 int element = Feld[x][y];
2422 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2423 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2427 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2428 ResetGfxAnimation(x, y);
2430 MovDir[newx][newy] = MovDir[x][y] = direction;
2431 GfxDir[x][y] = direction;
2433 if (Feld[newx][newy] == EL_EMPTY)
2434 Feld[newx][newy] = EL_BLOCKED;
2436 if (direction == MV_DOWN && CAN_FALL(element))
2437 GfxAction[x][y] = ACTION_FALLING;
2439 GfxAction[x][y] = ACTION_MOVING;
2441 GfxFrame[newx][newy] = GfxFrame[x][y];
2442 GfxRandom[newx][newy] = GfxRandom[x][y];
2443 GfxAction[newx][newy] = GfxAction[x][y];
2444 GfxDir[newx][newy] = GfxDir[x][y];
2447 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2449 int direction = MovDir[x][y];
2450 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2451 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2457 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2459 int oldx = x, oldy = y;
2460 int direction = MovDir[x][y];
2462 if (direction == MV_LEFT)
2464 else if (direction == MV_RIGHT)
2466 else if (direction == MV_UP)
2468 else if (direction == MV_DOWN)
2471 *comes_from_x = oldx;
2472 *comes_from_y = oldy;
2475 int MovingOrBlocked2Element(int x, int y)
2477 int element = Feld[x][y];
2479 if (element == EL_BLOCKED)
2483 Blocked2Moving(x, y, &oldx, &oldy);
2484 return Feld[oldx][oldy];
2490 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2492 /* like MovingOrBlocked2Element(), but if element is moving
2493 and (x,y) is the field the moving element is just leaving,
2494 return EL_BLOCKED instead of the element value */
2495 int element = Feld[x][y];
2497 if (IS_MOVING(x, y))
2499 if (element == EL_BLOCKED)
2503 Blocked2Moving(x, y, &oldx, &oldy);
2504 return Feld[oldx][oldy];
2513 static void RemoveField(int x, int y)
2515 Feld[x][y] = EL_EMPTY;
2522 ChangeDelay[x][y] = 0;
2523 ChangePage[x][y] = -1;
2524 Pushed[x][y] = FALSE;
2527 ExplodeField[x][y] = EX_TYPE_NONE;
2530 GfxElement[x][y] = EL_UNDEFINED;
2531 GfxAction[x][y] = ACTION_DEFAULT;
2532 GfxDir[x][y] = MV_NO_MOVING;
2535 void RemoveMovingField(int x, int y)
2537 int oldx = x, oldy = y, newx = x, newy = y;
2538 int element = Feld[x][y];
2539 int next_element = EL_UNDEFINED;
2541 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2544 if (IS_MOVING(x, y))
2546 Moving2Blocked(x, y, &newx, &newy);
2548 if (Feld[newx][newy] != EL_BLOCKED)
2551 if (Feld[newx][newy] != EL_BLOCKED)
2553 /* element is moving, but target field is not free (blocked), but
2554 already occupied by something different (example: acid pool);
2555 in this case, only remove the moving field, but not the target */
2557 RemoveField(oldx, oldy);
2559 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2561 DrawLevelField(oldx, oldy);
2567 else if (element == EL_BLOCKED)
2569 Blocked2Moving(x, y, &oldx, &oldy);
2570 if (!IS_MOVING(oldx, oldy))
2574 if (element == EL_BLOCKED &&
2575 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2576 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2577 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2578 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2579 next_element = get_next_element(Feld[oldx][oldy]);
2581 RemoveField(oldx, oldy);
2582 RemoveField(newx, newy);
2584 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2586 if (next_element != EL_UNDEFINED)
2587 Feld[oldx][oldy] = next_element;
2589 DrawLevelField(oldx, oldy);
2590 DrawLevelField(newx, newy);
2593 void DrawDynamite(int x, int y)
2595 int sx = SCREENX(x), sy = SCREENY(y);
2596 int graphic = el2img(Feld[x][y]);
2599 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2602 if (IS_WALKABLE_INSIDE(Back[x][y]))
2606 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2607 else if (Store[x][y])
2608 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2610 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2613 if (Back[x][y] || Store[x][y])
2614 DrawGraphicThruMask(sx, sy, graphic, frame);
2616 DrawGraphic(sx, sy, graphic, frame);
2618 if (game.emulation == EMU_SUPAPLEX)
2619 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2620 else if (Store[x][y])
2621 DrawGraphicThruMask(sx, sy, graphic, frame);
2623 DrawGraphic(sx, sy, graphic, frame);
2627 void CheckDynamite(int x, int y)
2629 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2633 if (MovDelay[x][y] != 0)
2636 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2643 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2645 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2646 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2647 StopSound(SND_DYNAMITE_ACTIVE);
2649 StopSound(SND_DYNABOMB_ACTIVE);
2655 void DrawRelocatePlayer(struct PlayerInfo *player)
2657 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2658 boolean no_delay = (tape.warp_forward);
2659 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2660 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2661 int jx = player->jx;
2662 int jy = player->jy;
2664 if (level.instant_relocation)
2667 int offset = (setup.scroll_delay ? 3 : 0);
2669 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2671 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2672 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2673 local_player->jx - MIDPOSX);
2675 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2676 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2677 local_player->jy - MIDPOSY);
2681 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2682 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2683 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2685 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2686 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2687 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2689 /* don't scroll over playfield boundaries */
2690 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2691 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2693 /* don't scroll over playfield boundaries */
2694 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2695 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2698 scroll_x += (local_player->jx - old_jx);
2699 scroll_y += (local_player->jy - old_jy);
2701 /* don't scroll over playfield boundaries */
2702 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2703 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2705 /* don't scroll over playfield boundaries */
2706 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2707 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2710 RedrawPlayfield(TRUE, 0,0,0,0);
2716 int offset = (setup.scroll_delay ? 3 : 0);
2718 int scroll_xx = -999, scroll_yy = -999;
2720 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2722 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2725 int fx = FX, fy = FY;
2727 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2728 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2729 local_player->jx - MIDPOSX);
2731 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2732 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2733 local_player->jy - MIDPOSY);
2735 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2736 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2739 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2742 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2749 fx += dx * TILEX / 2;
2750 fy += dy * TILEY / 2;
2752 ScrollLevel(dx, dy);
2755 /* scroll in two steps of half tile size to make things smoother */
2756 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2758 Delay(wait_delay_value);
2760 /* scroll second step to align at full tile size */
2762 Delay(wait_delay_value);
2765 int scroll_xx = -999, scroll_yy = -999;
2767 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2769 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2772 int fx = FX, fy = FY;
2774 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2775 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2776 local_player->jx - MIDPOSX);
2778 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2779 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2780 local_player->jy - MIDPOSY);
2782 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2783 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2786 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2789 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2796 fx += dx * TILEX / 2;
2797 fy += dy * TILEY / 2;
2799 ScrollLevel(dx, dy);
2802 /* scroll in two steps of half tile size to make things smoother */
2803 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2805 Delay(wait_delay_value);
2807 /* scroll second step to align at full tile size */
2809 Delay(wait_delay_value);
2815 Delay(wait_delay_value);
2819 void RelocatePlayer(int jx, int jy, int el_player_raw)
2821 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
2822 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2823 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2824 boolean no_delay = (tape.warp_forward);
2825 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2826 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2827 int old_jx = player->jx;
2828 int old_jy = player->jy;
2829 int old_element = Feld[old_jx][old_jy];
2830 int element = Feld[jx][jy];
2831 boolean player_relocated = (old_jx != jx || old_jy != jy);
2833 static int trigger_sides[4][2] =
2835 /* enter side leave side */
2836 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
2837 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
2838 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
2839 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
2841 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2842 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2843 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
2844 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
2845 int enter_side = enter_side_horiz | enter_side_vert;
2846 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
2847 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
2848 int leave_side = leave_side_horiz | leave_side_vert;
2850 if (player->GameOver) /* do not reanimate dead player */
2853 if (!player_relocated) /* no need to relocate the player */
2856 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2858 RemoveField(jx, jy); /* temporarily remove newly placed player */
2859 DrawLevelField(jx, jy);
2862 if (player->present)
2864 while (player->MovPos)
2866 ScrollPlayer(player, SCROLL_GO_ON);
2867 ScrollScreen(NULL, SCROLL_GO_ON);
2873 Delay(wait_delay_value);
2876 DrawPlayer(player); /* needed here only to cleanup last field */
2877 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2879 player->is_moving = FALSE;
2883 if (IS_CUSTOM_ELEMENT(old_element))
2884 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
2886 player->index_bit, leave_side);
2888 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
2890 player->index_bit, leave_side);
2893 Feld[jx][jy] = el_player;
2894 InitPlayerField(jx, jy, el_player, TRUE);
2896 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
2898 Feld[jx][jy] = element;
2899 InitField(jx, jy, FALSE);
2903 if (player == local_player) /* only visually relocate local player */
2904 DrawRelocatePlayer(player);
2908 TestIfHeroTouchesBadThing(jx, jy);
2909 TestIfPlayerTouchesCustomElement(jx, jy);
2913 /* needed to allow change of walkable custom element by entering player */
2914 Changed[jx][jy] = 0; /* allow another change */
2918 if (IS_CUSTOM_ELEMENT(element))
2919 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
2920 player->index_bit, enter_side);
2922 CheckTriggeredElementChangeByPlayer(jx, jy, element,
2923 CE_OTHER_GETS_ENTERED,
2924 player->index_bit, enter_side);
2928 void Explode(int ex, int ey, int phase, int mode)
2935 /* !!! eliminate this variable !!! */
2936 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2941 int last_phase = num_phase * delay;
2942 int half_phase = (num_phase / 2) * delay;
2943 int first_phase_after_start = EX_PHASE_START + 1;
2947 if (game.explosions_delayed)
2949 ExplodeField[ex][ey] = mode;
2953 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2955 int center_element = Feld[ex][ey];
2958 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
2962 /* --- This is only really needed (and now handled) in "Impact()". --- */
2963 /* do not explode moving elements that left the explode field in time */
2964 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2965 center_element == EL_EMPTY &&
2966 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
2970 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
2971 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2973 /* remove things displayed in background while burning dynamite */
2974 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2977 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2979 /* put moving element to center field (and let it explode there) */
2980 center_element = MovingOrBlocked2Element(ex, ey);
2981 RemoveMovingField(ex, ey);
2982 Feld[ex][ey] = center_element;
2988 last_phase = element_info[center_element].explosion_delay + 1;
2990 last_phase = element_info[center_element].explosion_delay;
2994 printf("::: %d -> %d\n", center_element, last_phase);
2998 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3000 int xx = x - ex + 1;
3001 int yy = y - ey + 1;
3006 if (!IN_LEV_FIELD(x, y) ||
3007 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3008 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3011 if (!IN_LEV_FIELD(x, y) ||
3012 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3016 if (!IN_LEV_FIELD(x, y) ||
3017 ((mode != EX_TYPE_NORMAL ||
3018 center_element == EL_AMOEBA_TO_DIAMOND) &&
3019 (x != ex || y != ey)))
3023 element = Feld[x][y];
3025 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3027 element = MovingOrBlocked2Element(x, y);
3029 if (!IS_EXPLOSION_PROOF(element))
3030 RemoveMovingField(x, y);
3036 if (IS_EXPLOSION_PROOF(element))
3039 /* indestructible elements can only explode in center (but not flames) */
3040 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3041 element == EL_FLAMES)
3046 if ((IS_INDESTRUCTIBLE(element) &&
3047 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3048 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3049 element == EL_FLAMES)
3054 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3055 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3056 (x == ex && y == ey)))
3058 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3061 if (IS_ACTIVE_BOMB(element))
3063 /* re-activate things under the bomb like gate or penguin */
3065 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3068 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3076 /* save walkable background elements while explosion on same tile */
3078 if (IS_INDESTRUCTIBLE(element))
3079 Back[x][y] = element;
3082 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3083 (x != ex || y != ey))
3084 Back[x][y] = element;
3086 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3087 Back[x][y] = element;
3091 /* ignite explodable elements reached by other explosion */
3092 if (element == EL_EXPLOSION)
3093 element = Store2[x][y];
3096 if (AmoebaNr[x][y] &&
3097 (element == EL_AMOEBA_FULL ||
3098 element == EL_BD_AMOEBA ||
3099 element == EL_AMOEBA_GROWING))
3101 AmoebaCnt[AmoebaNr[x][y]]--;
3102 AmoebaCnt2[AmoebaNr[x][y]]--;
3108 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3110 switch(StorePlayer[ex][ey])
3113 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3116 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3119 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3123 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3128 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3129 Store[x][y] = EL_EMPTY;
3131 if (game.emulation == EMU_SUPAPLEX)
3132 Store[x][y] = EL_EMPTY;
3135 else if (center_element == EL_MOLE)
3136 Store[x][y] = EL_EMERALD_RED;
3137 else if (center_element == EL_PENGUIN)
3138 Store[x][y] = EL_EMERALD_PURPLE;
3139 else if (center_element == EL_BUG)
3140 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3141 else if (center_element == EL_BD_BUTTERFLY)
3142 Store[x][y] = EL_BD_DIAMOND;
3143 else if (center_element == EL_SP_ELECTRON)
3144 Store[x][y] = EL_SP_INFOTRON;
3145 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3146 Store[x][y] = level.amoeba_content;
3147 else if (center_element == EL_YAMYAM)
3148 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3149 else if (IS_CUSTOM_ELEMENT(center_element) &&
3150 element_info[center_element].content[xx][yy] != EL_EMPTY)
3151 Store[x][y] = element_info[center_element].content[xx][yy];
3152 else if (element == EL_WALL_EMERALD)
3153 Store[x][y] = EL_EMERALD;
3154 else if (element == EL_WALL_DIAMOND)
3155 Store[x][y] = EL_DIAMOND;
3156 else if (element == EL_WALL_BD_DIAMOND)
3157 Store[x][y] = EL_BD_DIAMOND;
3158 else if (element == EL_WALL_EMERALD_YELLOW)
3159 Store[x][y] = EL_EMERALD_YELLOW;
3160 else if (element == EL_WALL_EMERALD_RED)
3161 Store[x][y] = EL_EMERALD_RED;
3162 else if (element == EL_WALL_EMERALD_PURPLE)
3163 Store[x][y] = EL_EMERALD_PURPLE;
3164 else if (element == EL_WALL_PEARL)
3165 Store[x][y] = EL_PEARL;
3166 else if (element == EL_WALL_CRYSTAL)
3167 Store[x][y] = EL_CRYSTAL;
3168 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3169 Store[x][y] = element_info[element].content[1][1];
3171 Store[x][y] = EL_EMPTY;
3173 if (x != ex || y != ey ||
3174 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_TYPE_BORDER)
3175 Store2[x][y] = element;
3178 if (AmoebaNr[x][y] &&
3179 (element == EL_AMOEBA_FULL ||
3180 element == EL_BD_AMOEBA ||
3181 element == EL_AMOEBA_GROWING))
3183 AmoebaCnt[AmoebaNr[x][y]]--;
3184 AmoebaCnt2[AmoebaNr[x][y]]--;
3190 MovDir[x][y] = MovPos[x][y] = 0;
3191 GfxDir[x][y] = MovDir[x][y];
3196 Feld[x][y] = EL_EXPLOSION;
3198 GfxElement[x][y] = center_element;
3200 GfxElement[x][y] = EL_UNDEFINED;
3203 ExplodePhase[x][y] = 1;
3205 ExplodeDelay[x][y] = last_phase;
3210 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3212 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3219 if (center_element == EL_YAMYAM)
3220 game.yamyam_content_nr =
3221 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3234 GfxFrame[x][y] = 0; /* restart explosion animation */
3238 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3242 last_phase = ExplodeDelay[x][y];
3245 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3249 /* activate this even in non-DEBUG version until cause for crash in
3250 getGraphicAnimationFrame() (see below) is found and eliminated */
3254 if (GfxElement[x][y] == EL_UNDEFINED)
3257 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3258 printf("Explode(): This should never happen!\n");
3261 GfxElement[x][y] = EL_EMPTY;
3267 border_element = Store2[x][y];
3269 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3270 border_element = StorePlayer[x][y];
3272 if (IS_PLAYER(x, y))
3273 border_element = StorePlayer[x][y];
3277 printf("::: phase == %d\n", phase);
3280 if (phase == element_info[border_element].ignition_delay ||
3281 phase == last_phase)
3283 boolean border_explosion = FALSE;
3287 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3288 !PLAYER_EXPLOSION_PROTECTED(x, y))
3290 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3293 if (IS_PLAYER(x, y))
3296 KillHeroUnlessExplosionProtected(x, y);
3297 border_explosion = TRUE;
3300 if (phase == last_phase)
3301 printf("::: IS_PLAYER\n");
3304 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3306 Feld[x][y] = Store2[x][y];
3309 border_explosion = TRUE;
3312 if (phase == last_phase)
3313 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3316 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3318 AmoebeUmwandeln(x, y);
3320 border_explosion = TRUE;
3323 if (phase == last_phase)
3324 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3325 element_info[border_element].explosion_delay,
3326 element_info[border_element].ignition_delay,
3332 /* if an element just explodes due to another explosion (chain-reaction),
3333 do not immediately end the new explosion when it was the last frame of
3334 the explosion (as it would be done in the following "if"-statement!) */
3335 if (border_explosion && phase == last_phase)
3342 if (phase == first_phase_after_start)
3344 int element = Store2[x][y];
3346 if (element == EL_BLACK_ORB)
3348 Feld[x][y] = Store2[x][y];
3353 else if (phase == half_phase)
3355 int element = Store2[x][y];
3357 if (IS_PLAYER(x, y))
3358 KillHeroUnlessExplosionProtected(x, y);
3359 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3361 Feld[x][y] = Store2[x][y];
3365 else if (element == EL_AMOEBA_TO_DIAMOND)
3366 AmoebeUmwandeln(x, y);
3370 if (phase == last_phase)
3375 printf("::: done: phase == %d\n", phase);
3379 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3382 element = Feld[x][y] = Store[x][y];
3383 Store[x][y] = Store2[x][y] = 0;
3384 GfxElement[x][y] = EL_UNDEFINED;
3386 /* player can escape from explosions and might therefore be still alive */
3387 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3388 element <= EL_PLAYER_IS_EXPLODING_4)
3389 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3391 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3392 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3393 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3396 /* restore probably existing indestructible background element */
3397 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3398 element = Feld[x][y] = Back[x][y];
3401 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3402 GfxDir[x][y] = MV_NO_MOVING;
3403 ChangeDelay[x][y] = 0;
3404 ChangePage[x][y] = -1;
3407 InitField_WithBug2(x, y, FALSE);
3409 InitField(x, y, FALSE);
3411 /* !!! not needed !!! */
3413 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3414 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3417 if (CAN_MOVE(element))
3422 DrawLevelField(x, y);
3424 TestIfElementTouchesCustomElement(x, y);
3426 if (GFX_CRUMBLED(element))
3427 DrawLevelFieldCrumbledSandNeighbours(x, y);
3429 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3430 StorePlayer[x][y] = 0;
3432 if (ELEM_IS_PLAYER(element))
3433 RelocatePlayer(x, y, element);
3436 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3438 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3442 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3444 int stored = Store[x][y];
3445 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3446 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3450 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3452 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3456 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3460 printf("::: %d / %d [%d - %d]\n",
3461 GfxFrame[x][y], phase - delay, phase, delay);
3465 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3466 element_info[GfxElement[x][y]].token_name,
3471 DrawLevelFieldCrumbledSand(x, y);
3473 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3475 DrawLevelElement(x, y, Back[x][y]);
3476 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3478 else if (IS_WALKABLE_UNDER(Back[x][y]))
3480 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3481 DrawLevelElementThruMask(x, y, Back[x][y]);
3483 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3484 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3488 void DynaExplode(int ex, int ey)
3491 int dynabomb_element = Feld[ex][ey];
3492 int dynabomb_size = 1;
3493 boolean dynabomb_xl = FALSE;
3494 struct PlayerInfo *player;
3495 static int xy[4][2] =
3503 if (IS_ACTIVE_BOMB(dynabomb_element))
3505 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3506 dynabomb_size = player->dynabomb_size;
3507 dynabomb_xl = player->dynabomb_xl;
3508 player->dynabombs_left++;
3511 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3513 for (i = 0; i < NUM_DIRECTIONS; i++)
3515 for (j = 1; j <= dynabomb_size; j++)
3517 int x = ex + j * xy[i][0];
3518 int y = ey + j * xy[i][1];
3521 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3524 element = Feld[x][y];
3526 /* do not restart explosions of fields with active bombs */
3527 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3530 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3534 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3535 !IS_DIGGABLE(element) && !dynabomb_xl)
3538 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3539 !CAN_GROW_INTO(element) && !dynabomb_xl)
3543 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3544 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3545 element != EL_SAND && !dynabomb_xl)
3552 void Bang(int x, int y)
3555 int element = MovingOrBlocked2Element(x, y);
3557 int element = Feld[x][y];
3561 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3563 if (IS_PLAYER(x, y))
3566 struct PlayerInfo *player = PLAYERINFO(x, y);
3568 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3569 player->element_nr);
3574 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3576 if (game.emulation == EMU_SUPAPLEX)
3577 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3579 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3584 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3592 case EL_BD_BUTTERFLY:
3595 case EL_DARK_YAMYAM:
3599 RaiseScoreElement(element);
3600 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3602 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3603 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3604 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3605 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3606 case EL_DYNABOMB_INCREASE_NUMBER:
3607 case EL_DYNABOMB_INCREASE_SIZE:
3608 case EL_DYNABOMB_INCREASE_POWER:
3613 case EL_LAMP_ACTIVE:
3615 case EL_AMOEBA_TO_DIAMOND:
3617 if (IS_PLAYER(x, y))
3618 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3620 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3624 if (element_info[element].explosion_type == EXPLODES_CROSS)
3626 if (CAN_EXPLODE_CROSS(element))
3629 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3634 else if (element_info[element].explosion_type == EXPLODES_1X1)
3636 else if (CAN_EXPLODE_1X1(element))
3638 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3640 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3644 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3647 void SplashAcid(int x, int y)
3650 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3651 (!IN_LEV_FIELD(x - 1, y - 2) ||
3652 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3653 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3655 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3656 (!IN_LEV_FIELD(x + 1, y - 2) ||
3657 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3658 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3660 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3662 /* input: position of element entering acid (obsolete) */
3664 int element = Feld[x][y];
3666 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3669 if (element != EL_ACID_SPLASH_LEFT &&
3670 element != EL_ACID_SPLASH_RIGHT)
3672 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3674 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3675 (!IN_LEV_FIELD(x - 1, y - 1) ||
3676 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3677 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3679 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3680 (!IN_LEV_FIELD(x + 1, y - 1) ||
3681 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3682 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3687 static void InitBeltMovement()
3689 static int belt_base_element[4] =
3691 EL_CONVEYOR_BELT_1_LEFT,
3692 EL_CONVEYOR_BELT_2_LEFT,
3693 EL_CONVEYOR_BELT_3_LEFT,
3694 EL_CONVEYOR_BELT_4_LEFT
3696 static int belt_base_active_element[4] =
3698 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3699 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3700 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3701 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3706 /* set frame order for belt animation graphic according to belt direction */
3707 for (i = 0; i < NUM_BELTS; i++)
3711 for (j = 0; j < NUM_BELT_PARTS; j++)
3713 int element = belt_base_active_element[belt_nr] + j;
3714 int graphic = el2img(element);
3716 if (game.belt_dir[i] == MV_LEFT)
3717 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3719 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3723 for (y = 0; y < lev_fieldy; y++)
3725 for (x = 0; x < lev_fieldx; x++)
3727 int element = Feld[x][y];
3729 for (i = 0; i < NUM_BELTS; i++)
3731 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3733 int e_belt_nr = getBeltNrFromBeltElement(element);
3736 if (e_belt_nr == belt_nr)
3738 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3740 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3748 static void ToggleBeltSwitch(int x, int y)
3750 static int belt_base_element[4] =
3752 EL_CONVEYOR_BELT_1_LEFT,
3753 EL_CONVEYOR_BELT_2_LEFT,
3754 EL_CONVEYOR_BELT_3_LEFT,
3755 EL_CONVEYOR_BELT_4_LEFT
3757 static int belt_base_active_element[4] =
3759 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3760 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3761 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3762 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3764 static int belt_base_switch_element[4] =
3766 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3767 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3768 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3769 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3771 static int belt_move_dir[4] =
3779 int element = Feld[x][y];
3780 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3781 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3782 int belt_dir = belt_move_dir[belt_dir_nr];
3785 if (!IS_BELT_SWITCH(element))
3788 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3789 game.belt_dir[belt_nr] = belt_dir;
3791 if (belt_dir_nr == 3)
3794 /* set frame order for belt animation graphic according to belt direction */
3795 for (i = 0; i < NUM_BELT_PARTS; i++)
3797 int element = belt_base_active_element[belt_nr] + i;
3798 int graphic = el2img(element);
3800 if (belt_dir == MV_LEFT)
3801 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3803 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3806 for (yy = 0; yy < lev_fieldy; yy++)
3808 for (xx = 0; xx < lev_fieldx; xx++)
3810 int element = Feld[xx][yy];
3812 if (IS_BELT_SWITCH(element))
3814 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3816 if (e_belt_nr == belt_nr)
3818 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3819 DrawLevelField(xx, yy);
3822 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3824 int e_belt_nr = getBeltNrFromBeltElement(element);
3826 if (e_belt_nr == belt_nr)
3828 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3830 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3831 DrawLevelField(xx, yy);
3834 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3836 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3838 if (e_belt_nr == belt_nr)
3840 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3842 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3843 DrawLevelField(xx, yy);
3850 static void ToggleSwitchgateSwitch(int x, int y)
3854 game.switchgate_pos = !game.switchgate_pos;
3856 for (yy = 0; yy < lev_fieldy; yy++)
3858 for (xx = 0; xx < lev_fieldx; xx++)
3860 int element = Feld[xx][yy];
3862 if (element == EL_SWITCHGATE_SWITCH_UP ||
3863 element == EL_SWITCHGATE_SWITCH_DOWN)
3865 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3866 DrawLevelField(xx, yy);
3868 else if (element == EL_SWITCHGATE_OPEN ||
3869 element == EL_SWITCHGATE_OPENING)
3871 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3873 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3875 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3878 else if (element == EL_SWITCHGATE_CLOSED ||
3879 element == EL_SWITCHGATE_CLOSING)
3881 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3883 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3885 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3892 static int getInvisibleActiveFromInvisibleElement(int element)
3894 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3895 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3896 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3900 static int getInvisibleFromInvisibleActiveElement(int element)
3902 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3903 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3904 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3908 static void RedrawAllLightSwitchesAndInvisibleElements()
3912 for (y = 0; y < lev_fieldy; y++)
3914 for (x = 0; x < lev_fieldx; x++)
3916 int element = Feld[x][y];
3918 if (element == EL_LIGHT_SWITCH &&
3919 game.light_time_left > 0)
3921 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3922 DrawLevelField(x, y);
3924 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3925 game.light_time_left == 0)
3927 Feld[x][y] = EL_LIGHT_SWITCH;
3928 DrawLevelField(x, y);
3930 else if (element == EL_INVISIBLE_STEELWALL ||
3931 element == EL_INVISIBLE_WALL ||
3932 element == EL_INVISIBLE_SAND)
3934 if (game.light_time_left > 0)
3935 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3937 DrawLevelField(x, y);
3939 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3940 element == EL_INVISIBLE_WALL_ACTIVE ||
3941 element == EL_INVISIBLE_SAND_ACTIVE)
3943 if (game.light_time_left == 0)
3944 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3946 DrawLevelField(x, y);
3952 static void ToggleLightSwitch(int x, int y)
3954 int element = Feld[x][y];
3956 game.light_time_left =
3957 (element == EL_LIGHT_SWITCH ?
3958 level.time_light * FRAMES_PER_SECOND : 0);
3960 RedrawAllLightSwitchesAndInvisibleElements();
3963 static void ActivateTimegateSwitch(int x, int y)
3967 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3969 for (yy = 0; yy < lev_fieldy; yy++)
3971 for (xx = 0; xx < lev_fieldx; xx++)
3973 int element = Feld[xx][yy];
3975 if (element == EL_TIMEGATE_CLOSED ||
3976 element == EL_TIMEGATE_CLOSING)
3978 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3979 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3983 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3985 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3986 DrawLevelField(xx, yy);
3993 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3996 inline static int getElementMoveStepsize(int x, int y)
3998 int element = Feld[x][y];
3999 int direction = MovDir[x][y];
4000 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4001 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4002 int horiz_move = (dx != 0);
4003 int sign = (horiz_move ? dx : dy);
4004 int step = sign * element_info[element].move_stepsize;
4006 /* special values for move stepsize for spring and things on conveyor belt */
4010 if (element == EL_SPRING)
4011 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4012 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
4013 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4014 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4016 if (CAN_FALL(element) &&
4017 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4018 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4019 else if (element == EL_SPRING)
4020 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4027 void Impact(int x, int y)
4029 boolean lastline = (y == lev_fieldy-1);
4030 boolean object_hit = FALSE;
4031 boolean impact = (lastline || object_hit);
4032 int element = Feld[x][y];
4033 int smashed = EL_STEELWALL;
4035 if (!lastline) /* check if element below was hit */
4037 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4040 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4041 MovDir[x][y + 1] != MV_DOWN ||
4042 MovPos[x][y + 1] <= TILEY / 2));
4045 object_hit = !IS_FREE(x, y + 1);
4048 /* do not smash moving elements that left the smashed field in time */
4049 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4050 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4054 smashed = MovingOrBlocked2Element(x, y + 1);
4056 impact = (lastline || object_hit);
4059 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4061 SplashAcid(x, y + 1);
4065 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4066 /* only reset graphic animation if graphic really changes after impact */
4068 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4070 ResetGfxAnimation(x, y);
4071 DrawLevelField(x, y);
4074 if (impact && CAN_EXPLODE_IMPACT(element))
4079 else if (impact && element == EL_PEARL)
4081 ResetGfxAnimation(x, y);
4083 Feld[x][y] = EL_PEARL_BREAKING;
4084 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4087 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4089 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4094 if (impact && element == EL_AMOEBA_DROP)
4096 if (object_hit && IS_PLAYER(x, y + 1))
4097 KillHeroUnlessEnemyProtected(x, y + 1);
4098 else if (object_hit && smashed == EL_PENGUIN)
4102 Feld[x][y] = EL_AMOEBA_GROWING;
4103 Store[x][y] = EL_AMOEBA_WET;
4105 ResetRandomAnimationValue(x, y);
4110 if (object_hit) /* check which object was hit */
4112 if (CAN_PASS_MAGIC_WALL(element) &&
4113 (smashed == EL_MAGIC_WALL ||
4114 smashed == EL_BD_MAGIC_WALL))
4117 int activated_magic_wall =
4118 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4119 EL_BD_MAGIC_WALL_ACTIVE);
4121 /* activate magic wall / mill */
4122 for (yy = 0; yy < lev_fieldy; yy++)
4123 for (xx = 0; xx < lev_fieldx; xx++)
4124 if (Feld[xx][yy] == smashed)
4125 Feld[xx][yy] = activated_magic_wall;
4127 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4128 game.magic_wall_active = TRUE;
4130 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4131 SND_MAGIC_WALL_ACTIVATING :
4132 SND_BD_MAGIC_WALL_ACTIVATING));
4135 if (IS_PLAYER(x, y + 1))
4137 if (CAN_SMASH_PLAYER(element))
4139 KillHeroUnlessEnemyProtected(x, y + 1);
4143 else if (smashed == EL_PENGUIN)
4145 if (CAN_SMASH_PLAYER(element))
4151 else if (element == EL_BD_DIAMOND)
4153 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4159 else if (((element == EL_SP_INFOTRON ||
4160 element == EL_SP_ZONK) &&
4161 (smashed == EL_SP_SNIKSNAK ||
4162 smashed == EL_SP_ELECTRON ||
4163 smashed == EL_SP_DISK_ORANGE)) ||
4164 (element == EL_SP_INFOTRON &&
4165 smashed == EL_SP_DISK_YELLOW))
4171 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4177 else if (CAN_SMASH_EVERYTHING(element))
4179 if (IS_CLASSIC_ENEMY(smashed) ||
4180 CAN_EXPLODE_SMASHED(smashed))
4185 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4187 if (smashed == EL_LAMP ||
4188 smashed == EL_LAMP_ACTIVE)
4193 else if (smashed == EL_NUT)
4195 Feld[x][y + 1] = EL_NUT_BREAKING;
4196 PlayLevelSound(x, y, SND_NUT_BREAKING);
4197 RaiseScoreElement(EL_NUT);
4200 else if (smashed == EL_PEARL)
4202 ResetGfxAnimation(x, y);
4204 Feld[x][y + 1] = EL_PEARL_BREAKING;
4205 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4208 else if (smashed == EL_DIAMOND)
4210 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4211 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4214 else if (IS_BELT_SWITCH(smashed))
4216 ToggleBeltSwitch(x, y + 1);
4218 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4219 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4221 ToggleSwitchgateSwitch(x, y + 1);
4223 else if (smashed == EL_LIGHT_SWITCH ||
4224 smashed == EL_LIGHT_SWITCH_ACTIVE)
4226 ToggleLightSwitch(x, y + 1);
4231 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4234 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4237 /* !!! TEST ONLY !!! */
4238 CheckElementChangeBySide(x, y + 1, smashed, element,
4239 CE_SWITCHED, CH_SIDE_TOP);
4240 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4241 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4243 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4244 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4245 CheckElementChangeBySide(x, y + 1, smashed, element,
4246 CE_SWITCHED, CH_SIDE_TOP);
4252 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4257 /* play sound of magic wall / mill */
4259 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4260 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4262 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4263 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4264 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4265 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4270 /* play sound of object that hits the ground */
4271 if (lastline || object_hit)
4272 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4275 inline static void TurnRoundExt(int x, int y)
4287 { 0, 0 }, { 0, 0 }, { 0, 0 },
4292 int left, right, back;
4296 { MV_DOWN, MV_UP, MV_RIGHT },
4297 { MV_UP, MV_DOWN, MV_LEFT },
4299 { MV_LEFT, MV_RIGHT, MV_DOWN },
4303 { MV_RIGHT, MV_LEFT, MV_UP }
4306 int element = Feld[x][y];
4307 int move_pattern = element_info[element].move_pattern;
4309 int old_move_dir = MovDir[x][y];
4310 int left_dir = turn[old_move_dir].left;
4311 int right_dir = turn[old_move_dir].right;
4312 int back_dir = turn[old_move_dir].back;
4314 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4315 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4316 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4317 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4319 int left_x = x + left_dx, left_y = y + left_dy;
4320 int right_x = x + right_dx, right_y = y + right_dy;
4321 int move_x = x + move_dx, move_y = y + move_dy;
4325 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4327 TestIfBadThingTouchesOtherBadThing(x, y);
4329 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4330 MovDir[x][y] = right_dir;
4331 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4332 MovDir[x][y] = left_dir;
4334 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4336 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4340 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4341 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4343 TestIfBadThingTouchesOtherBadThing(x, y);
4345 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4346 MovDir[x][y] = left_dir;
4347 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4348 MovDir[x][y] = right_dir;
4350 if ((element == EL_SPACESHIP ||
4351 element == EL_SP_SNIKSNAK ||
4352 element == EL_SP_ELECTRON)
4353 && MovDir[x][y] != old_move_dir)
4355 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4359 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4361 TestIfBadThingTouchesOtherBadThing(x, y);
4363 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4364 MovDir[x][y] = left_dir;
4365 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4366 MovDir[x][y] = right_dir;
4368 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4370 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4373 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4375 TestIfBadThingTouchesOtherBadThing(x, y);
4377 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4378 MovDir[x][y] = left_dir;
4379 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4380 MovDir[x][y] = right_dir;
4382 if (MovDir[x][y] != old_move_dir)
4386 else if (element == EL_YAMYAM)
4388 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4389 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4391 if (can_turn_left && can_turn_right)
4392 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4393 else if (can_turn_left)
4394 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4395 else if (can_turn_right)
4396 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4398 MovDir[x][y] = back_dir;
4400 MovDelay[x][y] = 16 + 16 * RND(3);
4402 else if (element == EL_DARK_YAMYAM)
4404 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4406 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4409 if (can_turn_left && can_turn_right)
4410 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4411 else if (can_turn_left)
4412 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4413 else if (can_turn_right)
4414 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4416 MovDir[x][y] = back_dir;
4418 MovDelay[x][y] = 16 + 16 * RND(3);
4420 else if (element == EL_PACMAN)
4422 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4423 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4425 if (can_turn_left && can_turn_right)
4426 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4427 else if (can_turn_left)
4428 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4429 else if (can_turn_right)
4430 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4432 MovDir[x][y] = back_dir;
4434 MovDelay[x][y] = 6 + RND(40);
4436 else if (element == EL_PIG)
4438 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4439 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4440 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4441 boolean should_turn_left, should_turn_right, should_move_on;
4443 int rnd = RND(rnd_value);
4445 should_turn_left = (can_turn_left &&
4447 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4448 y + back_dy + left_dy)));
4449 should_turn_right = (can_turn_right &&
4451 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4452 y + back_dy + right_dy)));
4453 should_move_on = (can_move_on &&
4456 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4457 y + move_dy + left_dy) ||
4458 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4459 y + move_dy + right_dy)));
4461 if (should_turn_left || should_turn_right || should_move_on)
4463 if (should_turn_left && should_turn_right && should_move_on)
4464 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4465 rnd < 2 * rnd_value / 3 ? right_dir :
4467 else if (should_turn_left && should_turn_right)
4468 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4469 else if (should_turn_left && should_move_on)
4470 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4471 else if (should_turn_right && should_move_on)
4472 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4473 else if (should_turn_left)
4474 MovDir[x][y] = left_dir;
4475 else if (should_turn_right)
4476 MovDir[x][y] = right_dir;
4477 else if (should_move_on)
4478 MovDir[x][y] = old_move_dir;
4480 else if (can_move_on && rnd > rnd_value / 8)
4481 MovDir[x][y] = old_move_dir;
4482 else if (can_turn_left && can_turn_right)
4483 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4484 else if (can_turn_left && rnd > rnd_value / 8)
4485 MovDir[x][y] = left_dir;
4486 else if (can_turn_right && rnd > rnd_value/8)
4487 MovDir[x][y] = right_dir;
4489 MovDir[x][y] = back_dir;
4491 xx = x + move_xy[MovDir[x][y]].x;
4492 yy = y + move_xy[MovDir[x][y]].y;
4494 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4495 MovDir[x][y] = old_move_dir;
4499 else if (element == EL_DRAGON)
4501 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4502 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4503 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4505 int rnd = RND(rnd_value);
4508 if (FrameCounter < 1 && x == 0 && y == 29)
4509 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4512 if (can_move_on && rnd > rnd_value / 8)
4513 MovDir[x][y] = old_move_dir;
4514 else if (can_turn_left && can_turn_right)
4515 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4516 else if (can_turn_left && rnd > rnd_value / 8)
4517 MovDir[x][y] = left_dir;
4518 else if (can_turn_right && rnd > rnd_value / 8)
4519 MovDir[x][y] = right_dir;
4521 MovDir[x][y] = back_dir;
4523 xx = x + move_xy[MovDir[x][y]].x;
4524 yy = y + move_xy[MovDir[x][y]].y;
4527 if (FrameCounter < 1 && x == 0 && y == 29)
4528 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4529 xx, yy, Feld[xx][yy],
4534 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4535 MovDir[x][y] = old_move_dir;
4537 if (!IS_FREE(xx, yy))
4538 MovDir[x][y] = old_move_dir;
4542 if (FrameCounter < 1 && x == 0 && y == 29)
4543 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4548 else if (element == EL_MOLE)
4550 boolean can_move_on =
4551 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4552 IS_AMOEBOID(Feld[move_x][move_y]) ||
4553 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4556 boolean can_turn_left =
4557 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4558 IS_AMOEBOID(Feld[left_x][left_y])));
4560 boolean can_turn_right =
4561 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4562 IS_AMOEBOID(Feld[right_x][right_y])));
4564 if (can_turn_left && can_turn_right)
4565 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4566 else if (can_turn_left)
4567 MovDir[x][y] = left_dir;
4569 MovDir[x][y] = right_dir;
4572 if (MovDir[x][y] != old_move_dir)
4575 else if (element == EL_BALLOON)
4577 MovDir[x][y] = game.balloon_dir;
4580 else if (element == EL_SPRING)
4583 if (MovDir[x][y] & MV_HORIZONTAL &&
4584 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4585 MovDir[x][y] = MV_NO_MOVING;
4587 if (MovDir[x][y] & MV_HORIZONTAL &&
4588 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4589 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4590 MovDir[x][y] = MV_NO_MOVING;
4595 else if (element == EL_ROBOT ||
4596 element == EL_SATELLITE ||
4597 element == EL_PENGUIN)
4599 int attr_x = -1, attr_y = -1;
4610 for (i = 0; i < MAX_PLAYERS; i++)
4612 struct PlayerInfo *player = &stored_player[i];
4613 int jx = player->jx, jy = player->jy;
4615 if (!player->active)
4619 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4628 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4629 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4630 game.engine_version < VERSION_IDENT(3,1,0,0)))
4632 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4639 if (element == EL_PENGUIN)
4642 static int xy[4][2] =
4650 for (i = 0; i < NUM_DIRECTIONS; i++)
4652 int ex = x + xy[i][0];
4653 int ey = y + xy[i][1];
4655 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4664 MovDir[x][y] = MV_NO_MOVING;
4666 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4667 else if (attr_x > x)
4668 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4670 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4671 else if (attr_y > y)
4672 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4674 if (element == EL_ROBOT)
4678 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4679 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4680 Moving2Blocked(x, y, &newx, &newy);
4682 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4683 MovDelay[x][y] = 8 + 8 * !RND(3);
4685 MovDelay[x][y] = 16;
4687 else if (element == EL_PENGUIN)
4693 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4695 boolean first_horiz = RND(2);
4696 int new_move_dir = MovDir[x][y];
4699 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4700 Moving2Blocked(x, y, &newx, &newy);
4702 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4706 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4707 Moving2Blocked(x, y, &newx, &newy);
4709 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4712 MovDir[x][y] = old_move_dir;
4716 else /* (element == EL_SATELLITE) */
4722 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4724 boolean first_horiz = RND(2);
4725 int new_move_dir = MovDir[x][y];
4728 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4729 Moving2Blocked(x, y, &newx, &newy);
4731 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4735 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4736 Moving2Blocked(x, y, &newx, &newy);
4738 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4741 MovDir[x][y] = old_move_dir;
4746 else if (move_pattern == MV_TURNING_LEFT ||
4747 move_pattern == MV_TURNING_RIGHT ||
4748 move_pattern == MV_TURNING_LEFT_RIGHT ||
4749 move_pattern == MV_TURNING_RIGHT_LEFT ||
4750 move_pattern == MV_TURNING_RANDOM ||
4751 move_pattern == MV_ALL_DIRECTIONS)
4753 boolean can_turn_left =
4754 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4755 boolean can_turn_right =
4756 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4758 if (move_pattern == MV_TURNING_LEFT)
4759 MovDir[x][y] = left_dir;
4760 else if (move_pattern == MV_TURNING_RIGHT)
4761 MovDir[x][y] = right_dir;
4762 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4763 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4764 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4765 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4766 else if (move_pattern == MV_TURNING_RANDOM)
4767 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4768 can_turn_right && !can_turn_left ? right_dir :
4769 RND(2) ? left_dir : right_dir);
4770 else if (can_turn_left && can_turn_right)
4771 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4772 else if (can_turn_left)
4773 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4774 else if (can_turn_right)
4775 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4777 MovDir[x][y] = back_dir;
4779 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4781 else if (move_pattern == MV_HORIZONTAL ||
4782 move_pattern == MV_VERTICAL)
4784 if (move_pattern & old_move_dir)
4785 MovDir[x][y] = back_dir;
4786 else if (move_pattern == MV_HORIZONTAL)
4787 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4788 else if (move_pattern == MV_VERTICAL)
4789 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4791 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4793 else if (move_pattern & MV_ANY_DIRECTION)
4795 MovDir[x][y] = move_pattern;
4796 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4798 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4800 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4801 MovDir[x][y] = left_dir;
4802 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4803 MovDir[x][y] = right_dir;
4805 if (MovDir[x][y] != old_move_dir)
4806 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4808 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4810 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4811 MovDir[x][y] = right_dir;
4812 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4813 MovDir[x][y] = left_dir;
4815 if (MovDir[x][y] != old_move_dir)
4816 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4818 else if (move_pattern == MV_TOWARDS_PLAYER ||
4819 move_pattern == MV_AWAY_FROM_PLAYER)
4821 int attr_x = -1, attr_y = -1;
4823 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4834 for (i = 0; i < MAX_PLAYERS; i++)
4836 struct PlayerInfo *player = &stored_player[i];
4837 int jx = player->jx, jy = player->jy;
4839 if (!player->active)
4843 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4851 MovDir[x][y] = MV_NO_MOVING;
4853 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4854 else if (attr_x > x)
4855 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4857 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4858 else if (attr_y > y)
4859 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4861 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4863 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4865 boolean first_horiz = RND(2);
4866 int new_move_dir = MovDir[x][y];
4869 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4870 Moving2Blocked(x, y, &newx, &newy);
4872 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4876 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4877 Moving2Blocked(x, y, &newx, &newy);
4879 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4882 MovDir[x][y] = old_move_dir;
4885 else if (move_pattern == MV_WHEN_PUSHED ||
4886 move_pattern == MV_WHEN_DROPPED)
4888 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4889 MovDir[x][y] = MV_NO_MOVING;
4893 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4895 static int test_xy[7][2] =
4905 static int test_dir[7] =
4915 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4916 int move_preference = -1000000; /* start with very low preference */
4917 int new_move_dir = MV_NO_MOVING;
4918 int start_test = RND(4);
4921 for (i = 0; i < NUM_DIRECTIONS; i++)
4923 int move_dir = test_dir[start_test + i];
4924 int move_dir_preference;
4926 xx = x + test_xy[start_test + i][0];
4927 yy = y + test_xy[start_test + i][1];
4929 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4930 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4932 new_move_dir = move_dir;
4937 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4940 move_dir_preference = -1 * RunnerVisit[xx][yy];
4941 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4942 move_dir_preference = PlayerVisit[xx][yy];
4944 if (move_dir_preference > move_preference)
4946 /* prefer field that has not been visited for the longest time */
4947 move_preference = move_dir_preference;
4948 new_move_dir = move_dir;
4950 else if (move_dir_preference == move_preference &&
4951 move_dir == old_move_dir)
4953 /* prefer last direction when all directions are preferred equally */
4954 move_preference = move_dir_preference;
4955 new_move_dir = move_dir;
4959 MovDir[x][y] = new_move_dir;
4960 if (old_move_dir != new_move_dir)
4965 static void TurnRound(int x, int y)
4967 int direction = MovDir[x][y];
4970 GfxDir[x][y] = MovDir[x][y];
4976 GfxDir[x][y] = MovDir[x][y];
4979 if (direction != MovDir[x][y])
4984 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4987 GfxAction[x][y] = ACTION_WAITING;
4991 static boolean JustBeingPushed(int x, int y)
4995 for (i = 0; i < MAX_PLAYERS; i++)
4997 struct PlayerInfo *player = &stored_player[i];
4999 if (player->active && player->is_pushing && player->MovPos)
5001 int next_jx = player->jx + (player->jx - player->last_jx);
5002 int next_jy = player->jy + (player->jy - player->last_jy);
5004 if (x == next_jx && y == next_jy)
5012 void StartMoving(int x, int y)
5015 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5017 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5018 int element = Feld[x][y];
5024 if (MovDelay[x][y] == 0)
5025 GfxAction[x][y] = ACTION_DEFAULT;
5027 /* !!! this should be handled more generic (not only for mole) !!! */
5028 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5029 GfxAction[x][y] = ACTION_DEFAULT;
5032 if (CAN_FALL(element) && y < lev_fieldy - 1)
5034 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5035 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5036 if (JustBeingPushed(x, y))
5039 if (element == EL_QUICKSAND_FULL)
5041 if (IS_FREE(x, y + 1))
5043 InitMovingField(x, y, MV_DOWN);
5044 started_moving = TRUE;
5046 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5047 Store[x][y] = EL_ROCK;
5049 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5051 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5054 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5056 if (!MovDelay[x][y])
5057 MovDelay[x][y] = TILEY + 1;
5066 Feld[x][y] = EL_QUICKSAND_EMPTY;
5067 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5068 Store[x][y + 1] = Store[x][y];
5071 PlayLevelSoundAction(x, y, ACTION_FILLING);
5073 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5077 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5078 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5080 InitMovingField(x, y, MV_DOWN);
5081 started_moving = TRUE;
5083 Feld[x][y] = EL_QUICKSAND_FILLING;
5084 Store[x][y] = element;
5086 PlayLevelSoundAction(x, y, ACTION_FILLING);
5088 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5091 else if (element == EL_MAGIC_WALL_FULL)
5093 if (IS_FREE(x, y + 1))
5095 InitMovingField(x, y, MV_DOWN);
5096 started_moving = TRUE;
5098 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5099 Store[x][y] = EL_CHANGED(Store[x][y]);
5101 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5103 if (!MovDelay[x][y])
5104 MovDelay[x][y] = TILEY/4 + 1;
5113 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5114 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5115 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5119 else if (element == EL_BD_MAGIC_WALL_FULL)
5121 if (IS_FREE(x, y + 1))
5123 InitMovingField(x, y, MV_DOWN);
5124 started_moving = TRUE;
5126 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5127 Store[x][y] = EL_CHANGED2(Store[x][y]);
5129 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5131 if (!MovDelay[x][y])
5132 MovDelay[x][y] = TILEY/4 + 1;
5141 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5142 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5143 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5147 else if (CAN_PASS_MAGIC_WALL(element) &&
5148 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5149 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5151 InitMovingField(x, y, MV_DOWN);
5152 started_moving = TRUE;
5155 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5156 EL_BD_MAGIC_WALL_FILLING);
5157 Store[x][y] = element;
5160 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5162 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5165 SplashAcid(x, y + 1);
5167 InitMovingField(x, y, MV_DOWN);
5168 started_moving = TRUE;
5170 Store[x][y] = EL_ACID;
5172 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5173 GfxAction[x][y + 1] = ACTION_ACTIVE;
5177 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5178 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5180 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5181 CAN_SMASH(element) && WasJustFalling[x][y] &&
5182 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5184 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5185 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5186 (Feld[x][y + 1] == EL_BLOCKED)))
5190 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5191 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5192 WasJustMoving[x][y] && !Pushed[x][y + 1])
5194 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5195 WasJustMoving[x][y])
5200 /* this is needed for a special case not covered by calling "Impact()"
5201 from "ContinueMoving()": if an element moves to a tile directly below
5202 another element which was just falling on that tile (which was empty
5203 in the previous frame), the falling element above would just stop
5204 instead of smashing the element below (in previous version, the above
5205 element was just checked for "moving" instead of "falling", resulting
5206 in incorrect smashes caused by horizontal movement of the above
5207 element; also, the case of the player being the element to smash was
5208 simply not covered here... :-/ ) */
5211 WasJustMoving[x][y] = 0;
5212 WasJustFalling[x][y] = 0;
5215 CheckCollision[x][y] = 0;
5219 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5221 if (MovDir[x][y] == MV_NO_MOVING)
5223 InitMovingField(x, y, MV_DOWN);
5224 started_moving = TRUE;
5227 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5229 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5230 MovDir[x][y] = MV_DOWN;
5232 InitMovingField(x, y, MV_DOWN);
5233 started_moving = TRUE;
5235 else if (element == EL_AMOEBA_DROP)
5237 Feld[x][y] = EL_AMOEBA_GROWING;
5238 Store[x][y] = EL_AMOEBA_WET;
5240 /* Store[x][y + 1] must be zero, because:
5241 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5244 #if OLD_GAME_BEHAVIOUR
5245 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5247 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5248 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5249 element != EL_DX_SUPABOMB)
5252 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5253 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5254 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5255 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5258 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5259 (IS_FREE(x - 1, y + 1) ||
5260 Feld[x - 1][y + 1] == EL_ACID));
5261 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5262 (IS_FREE(x + 1, y + 1) ||
5263 Feld[x + 1][y + 1] == EL_ACID));
5264 boolean can_fall_any = (can_fall_left || can_fall_right);
5265 boolean can_fall_both = (can_fall_left && can_fall_right);
5267 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5269 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5271 if (slippery_type == SLIPPERY_ONLY_LEFT)
5272 can_fall_right = FALSE;
5273 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5274 can_fall_left = FALSE;
5275 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5276 can_fall_right = FALSE;
5277 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5278 can_fall_left = FALSE;
5280 can_fall_any = (can_fall_left || can_fall_right);
5281 can_fall_both = (can_fall_left && can_fall_right);
5286 if (can_fall_both &&
5287 (game.emulation != EMU_BOULDERDASH &&
5288 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5289 can_fall_left = !(can_fall_right = RND(2));
5291 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5292 started_moving = TRUE;
5296 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5298 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5301 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5302 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5303 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5304 int belt_dir = game.belt_dir[belt_nr];
5306 if ((belt_dir == MV_LEFT && left_is_free) ||
5307 (belt_dir == MV_RIGHT && right_is_free))
5310 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5313 InitMovingField(x, y, belt_dir);
5314 started_moving = TRUE;
5317 Pushed[x][y] = TRUE;
5318 Pushed[nextx][y] = TRUE;
5321 GfxAction[x][y] = ACTION_DEFAULT;
5325 MovDir[x][y] = 0; /* if element was moving, stop it */
5330 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5332 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5334 if (CAN_MOVE(element) && !started_moving)
5337 int move_pattern = element_info[element].move_pattern;
5342 if (MovDir[x][y] == MV_NO_MOVING)
5344 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5345 x, y, element, element_info[element].token_name);
5346 printf("StartMoving(): This should never happen!\n");
5351 Moving2Blocked(x, y, &newx, &newy);
5354 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5357 if ((element == EL_SATELLITE ||
5358 element == EL_BALLOON ||
5359 element == EL_SPRING)
5360 && JustBeingPushed(x, y))
5367 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5368 CheckCollision[x][y] && IN_LEV_FIELD_AND_NOT_FREE(newx, newy))
5370 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5371 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5372 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5376 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5377 element, element_info[element].token_name,
5378 WasJustMoving[x][y],
5379 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5380 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5381 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5382 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5386 WasJustMoving[x][y] = 0;
5389 CheckCollision[x][y] = 0;
5391 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5394 if (Feld[x][y] != element) /* element has changed */
5396 element = Feld[x][y];
5397 move_pattern = element_info[element].move_pattern;
5399 if (!CAN_MOVE(element))
5403 if (Feld[x][y] != element) /* element has changed */
5411 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5412 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5414 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5416 Moving2Blocked(x, y, &newx, &newy);
5417 if (Feld[newx][newy] == EL_BLOCKED)
5418 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5424 if (FrameCounter < 1 && x == 0 && y == 29)
5425 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5428 if (!MovDelay[x][y]) /* start new movement phase */
5430 /* all objects that can change their move direction after each step
5431 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5433 if (element != EL_YAMYAM &&
5434 element != EL_DARK_YAMYAM &&
5435 element != EL_PACMAN &&
5436 !(move_pattern & MV_ANY_DIRECTION) &&
5437 move_pattern != MV_TURNING_LEFT &&
5438 move_pattern != MV_TURNING_RIGHT &&
5439 move_pattern != MV_TURNING_LEFT_RIGHT &&
5440 move_pattern != MV_TURNING_RIGHT_LEFT &&
5441 move_pattern != MV_TURNING_RANDOM)
5446 if (FrameCounter < 1 && x == 0 && y == 29)
5447 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5450 if (MovDelay[x][y] && (element == EL_BUG ||
5451 element == EL_SPACESHIP ||
5452 element == EL_SP_SNIKSNAK ||
5453 element == EL_SP_ELECTRON ||
5454 element == EL_MOLE))
5455 DrawLevelField(x, y);
5459 if (MovDelay[x][y]) /* wait some time before next movement */
5464 if (element == EL_YAMYAM)
5467 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5468 DrawLevelElementAnimation(x, y, element);
5472 if (MovDelay[x][y]) /* element still has to wait some time */
5475 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5476 ResetGfxAnimation(x, y);
5480 if (GfxAction[x][y] != ACTION_WAITING)
5481 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5483 GfxAction[x][y] = ACTION_WAITING;
5487 if (element == EL_ROBOT ||
5489 element == EL_PACMAN ||
5491 element == EL_YAMYAM ||
5492 element == EL_DARK_YAMYAM)
5495 DrawLevelElementAnimation(x, y, element);
5497 DrawLevelElementAnimationIfNeeded(x, y, element);
5499 PlayLevelSoundAction(x, y, ACTION_WAITING);
5501 else if (element == EL_SP_ELECTRON)
5502 DrawLevelElementAnimationIfNeeded(x, y, element);
5503 else if (element == EL_DRAGON)
5506 int dir = MovDir[x][y];
5507 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5508 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5509 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5510 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5511 dir == MV_UP ? IMG_FLAMES_1_UP :
5512 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5513 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5516 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5519 GfxAction[x][y] = ACTION_ATTACKING;
5521 if (IS_PLAYER(x, y))
5522 DrawPlayerField(x, y);
5524 DrawLevelField(x, y);
5526 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5528 for (i = 1; i <= 3; i++)
5530 int xx = x + i * dx;
5531 int yy = y + i * dy;
5532 int sx = SCREENX(xx);
5533 int sy = SCREENY(yy);
5534 int flame_graphic = graphic + (i - 1);
5536 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5541 int flamed = MovingOrBlocked2Element(xx, yy);
5545 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5547 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5548 RemoveMovingField(xx, yy);
5550 RemoveField(xx, yy);
5552 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5555 RemoveMovingField(xx, yy);
5559 if (ChangeDelay[xx][yy])
5560 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5561 Feld[xx][yy] == EL_BLOCKED));
5565 ChangeDelay[xx][yy] = 0;
5567 Feld[xx][yy] = EL_FLAMES;
5568 if (IN_SCR_FIELD(sx, sy))
5570 DrawLevelFieldCrumbledSand(xx, yy);
5571 DrawGraphic(sx, sy, flame_graphic, frame);
5576 if (Feld[xx][yy] == EL_FLAMES)
5577 Feld[xx][yy] = EL_EMPTY;
5578 DrawLevelField(xx, yy);
5583 if (MovDelay[x][y]) /* element still has to wait some time */
5585 PlayLevelSoundAction(x, y, ACTION_WAITING);
5591 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5592 for all other elements GfxAction will be set by InitMovingField() */
5593 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5594 GfxAction[x][y] = ACTION_MOVING;
5598 /* now make next step */
5600 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5602 if (DONT_COLLIDE_WITH(element) &&
5603 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5604 !PLAYER_ENEMY_PROTECTED(newx, newy))
5607 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5611 /* player killed by element which is deadly when colliding with */
5613 KillHero(PLAYERINFO(newx, newy));
5620 else if (CAN_MOVE_INTO_ACID(element) &&
5621 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5622 (MovDir[x][y] == MV_DOWN ||
5623 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5625 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5626 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5630 else if ((element == EL_PENGUIN ||
5631 element == EL_ROBOT ||
5632 element == EL_SATELLITE ||
5633 element == EL_BALLOON ||
5634 IS_CUSTOM_ELEMENT(element)) &&
5635 IN_LEV_FIELD(newx, newy) &&
5636 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5639 SplashAcid(newx, newy);
5640 Store[x][y] = EL_ACID;
5642 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5644 if (Feld[newx][newy] == EL_EXIT_OPEN)
5648 DrawLevelField(x, y);
5650 Feld[x][y] = EL_EMPTY;
5651 DrawLevelField(x, y);
5654 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5655 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5656 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5658 local_player->friends_still_needed--;
5659 if (!local_player->friends_still_needed &&
5660 !local_player->GameOver && AllPlayersGone)
5661 local_player->LevelSolved = local_player->GameOver = TRUE;
5665 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5667 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5668 DrawLevelField(newx, newy);
5670 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5672 else if (!IS_FREE(newx, newy))
5674 GfxAction[x][y] = ACTION_WAITING;
5676 if (IS_PLAYER(x, y))
5677 DrawPlayerField(x, y);
5679 DrawLevelField(x, y);
5684 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5686 if (IS_FOOD_PIG(Feld[newx][newy]))
5688 if (IS_MOVING(newx, newy))
5689 RemoveMovingField(newx, newy);
5692 Feld[newx][newy] = EL_EMPTY;
5693 DrawLevelField(newx, newy);
5696 PlayLevelSound(x, y, SND_PIG_DIGGING);
5698 else if (!IS_FREE(newx, newy))
5700 if (IS_PLAYER(x, y))
5701 DrawPlayerField(x, y);
5703 DrawLevelField(x, y);
5712 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5715 else if (IS_CUSTOM_ELEMENT(element) &&
5716 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5720 !IS_FREE(newx, newy)
5725 int new_element = Feld[newx][newy];
5728 printf("::: '%s' digs '%s' [%d]\n",
5729 element_info[element].token_name,
5730 element_info[Feld[newx][newy]].token_name,
5731 StorePlayer[newx][newy]);
5734 if (!IS_FREE(newx, newy))
5736 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5737 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5740 /* no element can dig solid indestructible elements */
5741 if (IS_INDESTRUCTIBLE(new_element) &&
5742 !IS_DIGGABLE(new_element) &&
5743 !IS_COLLECTIBLE(new_element))
5746 if (AmoebaNr[newx][newy] &&
5747 (new_element == EL_AMOEBA_FULL ||
5748 new_element == EL_BD_AMOEBA ||
5749 new_element == EL_AMOEBA_GROWING))
5751 AmoebaCnt[AmoebaNr[newx][newy]]--;
5752 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5755 if (IS_MOVING(newx, newy))
5756 RemoveMovingField(newx, newy);
5759 RemoveField(newx, newy);
5760 DrawLevelField(newx, newy);
5763 /* if digged element was about to explode, prevent the explosion */
5764 ExplodeField[newx][newy] = EX_TYPE_NONE;
5766 PlayLevelSoundAction(x, y, action);
5771 Store[newx][newy] = EL_EMPTY;
5772 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5773 Store[newx][newy] = element_info[element].move_leave_element;
5775 Store[newx][newy] = EL_EMPTY;
5776 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5777 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5778 Store[newx][newy] = element_info[element].move_leave_element;
5781 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5782 element_info[element].can_leave_element = TRUE;
5785 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5787 RunnerVisit[x][y] = FrameCounter;
5788 PlayerVisit[x][y] /= 8; /* expire player visit path */
5794 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5796 if (!IS_FREE(newx, newy))
5798 if (IS_PLAYER(x, y))
5799 DrawPlayerField(x, y);
5801 DrawLevelField(x, y);
5807 boolean wanna_flame = !RND(10);
5808 int dx = newx - x, dy = newy - y;
5809 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5810 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5811 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5812 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5813 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5814 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5817 IS_CLASSIC_ENEMY(element1) ||
5818 IS_CLASSIC_ENEMY(element2)) &&
5819 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5820 element1 != EL_FLAMES && element2 != EL_FLAMES)
5823 ResetGfxAnimation(x, y);
5824 GfxAction[x][y] = ACTION_ATTACKING;
5827 if (IS_PLAYER(x, y))
5828 DrawPlayerField(x, y);
5830 DrawLevelField(x, y);
5832 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5834 MovDelay[x][y] = 50;
5838 RemoveField(newx, newy);
5840 Feld[newx][newy] = EL_FLAMES;
5841 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5844 RemoveField(newx1, newy1);
5846 Feld[newx1][newy1] = EL_FLAMES;
5848 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5851 RemoveField(newx2, newy2);
5853 Feld[newx2][newy2] = EL_FLAMES;
5860 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5861 Feld[newx][newy] == EL_DIAMOND)
5863 if (IS_MOVING(newx, newy))
5864 RemoveMovingField(newx, newy);
5867 Feld[newx][newy] = EL_EMPTY;
5868 DrawLevelField(newx, newy);
5871 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5873 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5874 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5876 if (AmoebaNr[newx][newy])
5878 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5879 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5880 Feld[newx][newy] == EL_BD_AMOEBA)
5881 AmoebaCnt[AmoebaNr[newx][newy]]--;
5886 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5888 if (IS_MOVING(newx, newy))
5891 RemoveMovingField(newx, newy);
5895 Feld[newx][newy] = EL_EMPTY;
5896 DrawLevelField(newx, newy);
5899 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5901 else if ((element == EL_PACMAN || element == EL_MOLE)
5902 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5904 if (AmoebaNr[newx][newy])
5906 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5907 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5908 Feld[newx][newy] == EL_BD_AMOEBA)
5909 AmoebaCnt[AmoebaNr[newx][newy]]--;
5912 if (element == EL_MOLE)
5914 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5915 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5917 ResetGfxAnimation(x, y);
5918 GfxAction[x][y] = ACTION_DIGGING;
5919 DrawLevelField(x, y);
5921 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5923 return; /* wait for shrinking amoeba */
5925 else /* element == EL_PACMAN */
5927 Feld[newx][newy] = EL_EMPTY;
5928 DrawLevelField(newx, newy);
5929 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5932 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5933 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5934 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5936 /* wait for shrinking amoeba to completely disappear */
5939 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5941 /* object was running against a wall */
5946 if (move_pattern & MV_ANY_DIRECTION &&
5947 move_pattern == MovDir[x][y])
5949 int blocking_element =
5950 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5953 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
5954 element_info[element].token_name,
5955 element_info[blocking_element].token_name,
5959 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5962 element = Feld[x][y]; /* element might have changed */
5967 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5968 DrawLevelElementAnimation(x, y, element);
5970 if (element == EL_BUG ||
5971 element == EL_SPACESHIP ||
5972 element == EL_SP_SNIKSNAK)
5973 DrawLevelField(x, y);
5974 else if (element == EL_MOLE)
5975 DrawLevelField(x, y);
5976 else if (element == EL_BD_BUTTERFLY ||
5977 element == EL_BD_FIREFLY)
5978 DrawLevelElementAnimationIfNeeded(x, y, element);
5979 else if (element == EL_SATELLITE)
5980 DrawLevelElementAnimationIfNeeded(x, y, element);
5981 else if (element == EL_SP_ELECTRON)
5982 DrawLevelElementAnimationIfNeeded(x, y, element);
5985 if (DONT_TOUCH(element))
5986 TestIfBadThingTouchesHero(x, y);
5989 PlayLevelSoundAction(x, y, ACTION_WAITING);
5995 InitMovingField(x, y, MovDir[x][y]);
5997 PlayLevelSoundAction(x, y, ACTION_MOVING);
6001 ContinueMoving(x, y);
6004 void ContinueMoving(int x, int y)
6006 int element = Feld[x][y];
6007 int stored = Store[x][y];
6008 struct ElementInfo *ei = &element_info[element];
6009 int direction = MovDir[x][y];
6010 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6011 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6012 int newx = x + dx, newy = y + dy;
6014 int nextx = newx + dx, nexty = newy + dy;
6017 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6018 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6020 boolean pushed_by_player = Pushed[x][y];
6023 MovPos[x][y] += getElementMoveStepsize(x, y);
6026 if (pushed_by_player && IS_PLAYER(x, y))
6028 /* special case: moving object pushed by player */
6029 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6032 if (pushed_by_player) /* special case: moving object pushed by player */
6033 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6036 if (ABS(MovPos[x][y]) < TILEX)
6038 DrawLevelField(x, y);
6040 return; /* element is still moving */
6043 /* element reached destination field */
6045 Feld[x][y] = EL_EMPTY;
6046 Feld[newx][newy] = element;
6047 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6050 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6052 element = Feld[newx][newy] = EL_ACID;
6055 else if (element == EL_MOLE)
6057 Feld[x][y] = EL_SAND;
6059 DrawLevelFieldCrumbledSandNeighbours(x, y);
6061 else if (element == EL_QUICKSAND_FILLING)
6063 element = Feld[newx][newy] = get_next_element(element);
6064 Store[newx][newy] = Store[x][y];
6066 else if (element == EL_QUICKSAND_EMPTYING)
6068 Feld[x][y] = get_next_element(element);
6069 element = Feld[newx][newy] = Store[x][y];
6071 else if (element == EL_MAGIC_WALL_FILLING)
6073 element = Feld[newx][newy] = get_next_element(element);
6074 if (!game.magic_wall_active)
6075 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6076 Store[newx][newy] = Store[x][y];
6078 else if (element == EL_MAGIC_WALL_EMPTYING)
6080 Feld[x][y] = get_next_element(element);
6081 if (!game.magic_wall_active)
6082 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6083 element = Feld[newx][newy] = Store[x][y];
6085 else if (element == EL_BD_MAGIC_WALL_FILLING)
6087 element = Feld[newx][newy] = get_next_element(element);
6088 if (!game.magic_wall_active)
6089 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6090 Store[newx][newy] = Store[x][y];
6092 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6094 Feld[x][y] = get_next_element(element);
6095 if (!game.magic_wall_active)
6096 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6097 element = Feld[newx][newy] = Store[x][y];
6099 else if (element == EL_AMOEBA_DROPPING)
6101 Feld[x][y] = get_next_element(element);
6102 element = Feld[newx][newy] = Store[x][y];
6104 else if (element == EL_SOKOBAN_OBJECT)
6107 Feld[x][y] = Back[x][y];
6109 if (Back[newx][newy])
6110 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6112 Back[x][y] = Back[newx][newy] = 0;
6115 else if (Store[x][y] == EL_ACID)
6117 element = Feld[newx][newy] = EL_ACID;
6121 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6122 ei->move_leave_element != EL_EMPTY &&
6123 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6124 Store[x][y] != EL_EMPTY))
6126 /* some elements can leave other elements behind after moving */
6128 Feld[x][y] = ei->move_leave_element;
6129 InitField(x, y, FALSE);
6131 if (GFX_CRUMBLED(Feld[x][y]))
6132 DrawLevelFieldCrumbledSandNeighbours(x, y);
6136 Store[x][y] = EL_EMPTY;
6137 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6138 MovDelay[newx][newy] = 0;
6140 if (CAN_CHANGE(element))
6142 /* copy element change control values to new field */
6143 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6144 ChangePage[newx][newy] = ChangePage[x][y];
6145 Changed[newx][newy] = Changed[x][y];
6146 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6149 ChangeDelay[x][y] = 0;
6150 ChangePage[x][y] = -1;
6151 Changed[x][y] = CE_BITMASK_DEFAULT;
6152 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6154 /* copy animation control values to new field */
6155 GfxFrame[newx][newy] = GfxFrame[x][y];
6156 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6157 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6158 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6160 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6162 ResetGfxAnimation(x, y); /* reset animation values for old field */
6165 /* some elements can leave other elements behind after moving */
6167 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6168 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6169 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6171 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6172 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6176 int move_leave_element = ei->move_leave_element;
6178 Feld[x][y] = move_leave_element;
6179 InitField(x, y, FALSE);
6181 if (GFX_CRUMBLED(Feld[x][y]))
6182 DrawLevelFieldCrumbledSandNeighbours(x, y);
6184 if (ELEM_IS_PLAYER(move_leave_element))
6185 RelocatePlayer(x, y, move_leave_element);
6190 /* some elements can leave other elements behind after moving */
6191 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6192 ei->move_leave_element != EL_EMPTY &&
6193 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6194 ei->can_leave_element_last))
6196 Feld[x][y] = ei->move_leave_element;
6197 InitField(x, y, FALSE);
6199 if (GFX_CRUMBLED(Feld[x][y]))
6200 DrawLevelFieldCrumbledSandNeighbours(x, y);
6203 ei->can_leave_element_last = ei->can_leave_element;
6204 ei->can_leave_element = FALSE;
6208 /* 2.1.1 (does not work correctly for spring) */
6209 if (!CAN_MOVE(element))
6210 MovDir[newx][newy] = 0;
6214 /* (does not work for falling objects that slide horizontally) */
6215 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6216 MovDir[newx][newy] = 0;
6219 if (!CAN_MOVE(element) ||
6220 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6221 MovDir[newx][newy] = 0;
6225 if (!CAN_MOVE(element) ||
6226 (CAN_FALL(element) && direction == MV_DOWN))
6227 GfxDir[x][y] = MovDir[newx][newy] = 0;
6229 if (!CAN_MOVE(element) ||
6230 (CAN_FALL(element) && direction == MV_DOWN &&
6231 (element == EL_SPRING ||
6232 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6233 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6234 GfxDir[x][y] = MovDir[newx][newy] = 0;
6240 DrawLevelField(x, y);
6241 DrawLevelField(newx, newy);
6243 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6245 /* prevent pushed element from moving on in pushed direction */
6246 if (pushed_by_player && CAN_MOVE(element) &&
6247 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6248 !(element_info[element].move_pattern & direction))
6249 TurnRound(newx, newy);
6252 /* prevent elements on conveyor belt from moving on in last direction */
6253 if (pushed_by_conveyor && CAN_FALL(element) &&
6254 direction & MV_HORIZONTAL)
6257 if (CAN_MOVE(element))
6258 InitMovDir(newx, newy);
6260 MovDir[newx][newy] = 0;
6262 MovDir[newx][newy] = 0;
6267 if (!pushed_by_player)
6269 int nextx = newx + dx, nexty = newy + dy;
6270 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6272 WasJustMoving[newx][newy] = 3;
6274 if (CAN_FALL(element) && direction == MV_DOWN)
6275 WasJustFalling[newx][newy] = 3;
6277 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6278 CheckCollision[newx][newy] = 2;
6281 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6283 TestIfBadThingTouchesHero(newx, newy);
6284 TestIfBadThingTouchesFriend(newx, newy);
6286 if (!IS_CUSTOM_ELEMENT(element))
6287 TestIfBadThingTouchesOtherBadThing(newx, newy);
6289 else if (element == EL_PENGUIN)
6290 TestIfFriendTouchesBadThing(newx, newy);
6292 if (CAN_FALL(element) && direction == MV_DOWN &&
6293 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6297 if (pushed_by_player)
6299 static int trigger_sides[4] =
6301 CH_SIDE_RIGHT, /* moving left */
6302 CH_SIDE_LEFT, /* moving right */
6303 CH_SIDE_BOTTOM, /* moving up */
6304 CH_SIDE_TOP, /* moving down */
6306 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6307 struct PlayerInfo *player = PLAYERINFO(x, y);
6309 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6310 player->index_bit, dig_side);
6311 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6312 player->index_bit, dig_side);
6317 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6321 if (ChangePage[newx][newy] != -1) /* delayed change */
6322 ChangeElement(newx, newy, ChangePage[newx][newy]);
6327 TestIfElementHitsCustomElement(newx, newy, direction);
6331 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6333 int hitting_element = Feld[newx][newy];
6335 /* !!! fix side (direction) orientation here and elsewhere !!! */
6336 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6340 if (IN_LEV_FIELD(nextx, nexty))
6342 int opposite_direction = MV_DIR_OPPOSITE(direction);
6343 int hitting_side = direction;
6344 int touched_side = opposite_direction;
6345 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6346 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6347 MovDir[nextx][nexty] != direction ||
6348 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6354 CheckElementChangeBySide(nextx, nexty, touched_element,
6355 CE_HIT_BY_SOMETHING, opposite_direction);
6357 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6358 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6360 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6362 struct ElementChangeInfo *change =
6363 &element_info[hitting_element].change_page[i];
6365 if (change->can_change &&
6366 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6367 change->trigger_side & touched_side &&
6368 change->trigger_element == touched_element)
6370 CheckElementChangeByPage(newx, newy, hitting_element,
6371 touched_element, CE_OTHER_IS_HITTING,i);
6377 if (IS_CUSTOM_ELEMENT(touched_element) &&
6378 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6380 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6382 struct ElementChangeInfo *change =
6383 &element_info[touched_element].change_page[i];
6385 if (change->can_change &&
6386 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6387 change->trigger_side & hitting_side &&
6388 change->trigger_element == hitting_element)
6390 CheckElementChangeByPage(nextx, nexty, touched_element,
6391 hitting_element, CE_OTHER_GETS_HIT, i);
6402 TestIfPlayerTouchesCustomElement(newx, newy);
6403 TestIfElementTouchesCustomElement(newx, newy);
6406 int AmoebeNachbarNr(int ax, int ay)
6409 int element = Feld[ax][ay];
6411 static int xy[4][2] =
6419 for (i = 0; i < NUM_DIRECTIONS; i++)
6421 int x = ax + xy[i][0];
6422 int y = ay + xy[i][1];
6424 if (!IN_LEV_FIELD(x, y))
6427 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6428 group_nr = AmoebaNr[x][y];
6434 void AmoebenVereinigen(int ax, int ay)
6436 int i, x, y, xx, yy;
6437 int new_group_nr = AmoebaNr[ax][ay];
6438 static int xy[4][2] =
6446 if (new_group_nr == 0)
6449 for (i = 0; i < NUM_DIRECTIONS; i++)
6454 if (!IN_LEV_FIELD(x, y))
6457 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6458 Feld[x][y] == EL_BD_AMOEBA ||
6459 Feld[x][y] == EL_AMOEBA_DEAD) &&
6460 AmoebaNr[x][y] != new_group_nr)
6462 int old_group_nr = AmoebaNr[x][y];
6464 if (old_group_nr == 0)
6467 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6468 AmoebaCnt[old_group_nr] = 0;
6469 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6470 AmoebaCnt2[old_group_nr] = 0;
6472 for (yy = 0; yy < lev_fieldy; yy++)
6474 for (xx = 0; xx < lev_fieldx; xx++)
6476 if (AmoebaNr[xx][yy] == old_group_nr)
6477 AmoebaNr[xx][yy] = new_group_nr;
6484 void AmoebeUmwandeln(int ax, int ay)
6488 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6490 int group_nr = AmoebaNr[ax][ay];
6495 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6496 printf("AmoebeUmwandeln(): This should never happen!\n");
6501 for (y = 0; y < lev_fieldy; y++)
6503 for (x = 0; x < lev_fieldx; x++)
6505 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6508 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6512 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6513 SND_AMOEBA_TURNING_TO_GEM :
6514 SND_AMOEBA_TURNING_TO_ROCK));
6519 static int xy[4][2] =
6527 for (i = 0; i < NUM_DIRECTIONS; i++)
6532 if (!IN_LEV_FIELD(x, y))
6535 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6537 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6538 SND_AMOEBA_TURNING_TO_GEM :
6539 SND_AMOEBA_TURNING_TO_ROCK));
6546 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6549 int group_nr = AmoebaNr[ax][ay];
6550 boolean done = FALSE;
6555 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6556 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6561 for (y = 0; y < lev_fieldy; y++)
6563 for (x = 0; x < lev_fieldx; x++)
6565 if (AmoebaNr[x][y] == group_nr &&
6566 (Feld[x][y] == EL_AMOEBA_DEAD ||
6567 Feld[x][y] == EL_BD_AMOEBA ||
6568 Feld[x][y] == EL_AMOEBA_GROWING))
6571 Feld[x][y] = new_element;
6572 InitField(x, y, FALSE);
6573 DrawLevelField(x, y);
6580 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6581 SND_BD_AMOEBA_TURNING_TO_ROCK :
6582 SND_BD_AMOEBA_TURNING_TO_GEM));
6585 void AmoebeWaechst(int x, int y)
6587 static unsigned long sound_delay = 0;
6588 static unsigned long sound_delay_value = 0;
6590 if (!MovDelay[x][y]) /* start new growing cycle */
6594 if (DelayReached(&sound_delay, sound_delay_value))
6597 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6599 if (Store[x][y] == EL_BD_AMOEBA)
6600 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6602 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6604 sound_delay_value = 30;
6608 if (MovDelay[x][y]) /* wait some time before growing bigger */
6611 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6613 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6614 6 - MovDelay[x][y]);
6616 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6619 if (!MovDelay[x][y])
6621 Feld[x][y] = Store[x][y];
6623 DrawLevelField(x, y);
6628 void AmoebaDisappearing(int x, int y)
6630 static unsigned long sound_delay = 0;
6631 static unsigned long sound_delay_value = 0;
6633 if (!MovDelay[x][y]) /* start new shrinking cycle */
6637 if (DelayReached(&sound_delay, sound_delay_value))
6638 sound_delay_value = 30;
6641 if (MovDelay[x][y]) /* wait some time before shrinking */
6644 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6646 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6647 6 - MovDelay[x][y]);
6649 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6652 if (!MovDelay[x][y])
6654 Feld[x][y] = EL_EMPTY;
6655 DrawLevelField(x, y);
6657 /* don't let mole enter this field in this cycle;
6658 (give priority to objects falling to this field from above) */
6664 void AmoebeAbleger(int ax, int ay)
6667 int element = Feld[ax][ay];
6668 int graphic = el2img(element);
6669 int newax = ax, neway = ay;
6670 static int xy[4][2] =
6678 if (!level.amoeba_speed)
6680 Feld[ax][ay] = EL_AMOEBA_DEAD;
6681 DrawLevelField(ax, ay);
6685 if (IS_ANIMATED(graphic))
6686 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6688 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6689 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6691 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6694 if (MovDelay[ax][ay])
6698 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6701 int x = ax + xy[start][0];
6702 int y = ay + xy[start][1];
6704 if (!IN_LEV_FIELD(x, y))
6708 if (IS_FREE(x, y) ||
6709 CAN_GROW_INTO(Feld[x][y]) ||
6710 Feld[x][y] == EL_QUICKSAND_EMPTY)
6716 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6717 if (IS_FREE(x, y) ||
6718 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6725 if (newax == ax && neway == ay)
6728 else /* normal or "filled" (BD style) amoeba */
6731 boolean waiting_for_player = FALSE;
6733 for (i = 0; i < NUM_DIRECTIONS; i++)
6735 int j = (start + i) % 4;
6736 int x = ax + xy[j][0];
6737 int y = ay + xy[j][1];
6739 if (!IN_LEV_FIELD(x, y))
6743 if (IS_FREE(x, y) ||
6744 CAN_GROW_INTO(Feld[x][y]) ||
6745 Feld[x][y] == EL_QUICKSAND_EMPTY)
6752 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6753 if (IS_FREE(x, y) ||
6754 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6761 else if (IS_PLAYER(x, y))
6762 waiting_for_player = TRUE;
6765 if (newax == ax && neway == ay) /* amoeba cannot grow */
6768 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6770 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6773 Feld[ax][ay] = EL_AMOEBA_DEAD;
6774 DrawLevelField(ax, ay);
6775 AmoebaCnt[AmoebaNr[ax][ay]]--;
6777 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6779 if (element == EL_AMOEBA_FULL)
6780 AmoebeUmwandeln(ax, ay);
6781 else if (element == EL_BD_AMOEBA)
6782 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6787 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6789 /* amoeba gets larger by growing in some direction */
6791 int new_group_nr = AmoebaNr[ax][ay];
6794 if (new_group_nr == 0)
6796 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6797 printf("AmoebeAbleger(): This should never happen!\n");
6802 AmoebaNr[newax][neway] = new_group_nr;
6803 AmoebaCnt[new_group_nr]++;
6804 AmoebaCnt2[new_group_nr]++;
6806 /* if amoeba touches other amoeba(s) after growing, unify them */
6807 AmoebenVereinigen(newax, neway);
6809 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6811 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6817 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6818 (neway == lev_fieldy - 1 && newax != ax))
6820 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6821 Store[newax][neway] = element;
6823 else if (neway == ay)
6825 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6827 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6829 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6834 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6835 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6836 Store[ax][ay] = EL_AMOEBA_DROP;
6837 ContinueMoving(ax, ay);
6841 DrawLevelField(newax, neway);
6844 void Life(int ax, int ay)
6847 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6849 int element = Feld[ax][ay];
6850 int graphic = el2img(element);
6851 boolean changed = FALSE;
6853 if (IS_ANIMATED(graphic))
6854 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6859 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6860 MovDelay[ax][ay] = life_time;
6862 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6865 if (MovDelay[ax][ay])
6869 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6871 int xx = ax+x1, yy = ay+y1;
6874 if (!IN_LEV_FIELD(xx, yy))
6877 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6879 int x = xx+x2, y = yy+y2;
6881 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6884 if (((Feld[x][y] == element ||
6885 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6887 (IS_FREE(x, y) && Stop[x][y]))
6891 if (xx == ax && yy == ay) /* field in the middle */
6893 if (nachbarn < life[0] || nachbarn > life[1])
6895 Feld[xx][yy] = EL_EMPTY;
6897 DrawLevelField(xx, yy);
6898 Stop[xx][yy] = TRUE;
6903 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6904 { /* free border field */
6905 if (nachbarn >= life[2] && nachbarn <= life[3])
6907 Feld[xx][yy] = element;
6908 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6910 DrawLevelField(xx, yy);
6911 Stop[xx][yy] = TRUE;
6916 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6917 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6918 { /* free border field */
6919 if (nachbarn >= life[2] && nachbarn <= life[3])
6921 Feld[xx][yy] = element;
6922 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6924 DrawLevelField(xx, yy);
6925 Stop[xx][yy] = TRUE;
6933 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6934 SND_GAME_OF_LIFE_GROWING);
6937 static void InitRobotWheel(int x, int y)
6939 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6942 static void RunRobotWheel(int x, int y)
6944 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6947 static void StopRobotWheel(int x, int y)
6949 if (ZX == x && ZY == y)
6953 static void InitTimegateWheel(int x, int y)
6956 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6958 /* another brainless, "type style" bug ... :-( */
6959 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6963 static void RunTimegateWheel(int x, int y)
6965 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6968 void CheckExit(int x, int y)
6970 if (local_player->gems_still_needed > 0 ||
6971 local_player->sokobanfields_still_needed > 0 ||
6972 local_player->lights_still_needed > 0)
6974 int element = Feld[x][y];
6975 int graphic = el2img(element);
6977 if (IS_ANIMATED(graphic))
6978 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6983 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6986 Feld[x][y] = EL_EXIT_OPENING;
6988 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6991 void CheckExitSP(int x, int y)
6993 if (local_player->gems_still_needed > 0)
6995 int element = Feld[x][y];
6996 int graphic = el2img(element);
6998 if (IS_ANIMATED(graphic))
6999 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7004 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7007 Feld[x][y] = EL_SP_EXIT_OPENING;
7009 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7012 static void CloseAllOpenTimegates()
7016 for (y = 0; y < lev_fieldy; y++)
7018 for (x = 0; x < lev_fieldx; x++)
7020 int element = Feld[x][y];
7022 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7024 Feld[x][y] = EL_TIMEGATE_CLOSING;
7026 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7028 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7035 void EdelsteinFunkeln(int x, int y)
7037 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7040 if (Feld[x][y] == EL_BD_DIAMOND)
7043 if (MovDelay[x][y] == 0) /* next animation frame */
7044 MovDelay[x][y] = 11 * !SimpleRND(500);
7046 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7050 if (setup.direct_draw && MovDelay[x][y])
7051 SetDrawtoField(DRAW_BUFFERED);
7053 DrawLevelElementAnimation(x, y, Feld[x][y]);
7055 if (MovDelay[x][y] != 0)
7057 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7058 10 - MovDelay[x][y]);
7060 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7062 if (setup.direct_draw)
7066 dest_x = FX + SCREENX(x) * TILEX;
7067 dest_y = FY + SCREENY(y) * TILEY;
7069 BlitBitmap(drawto_field, window,
7070 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7071 SetDrawtoField(DRAW_DIRECT);
7077 void MauerWaechst(int x, int y)
7081 if (!MovDelay[x][y]) /* next animation frame */
7082 MovDelay[x][y] = 3 * delay;
7084 if (MovDelay[x][y]) /* wait some time before next frame */
7088 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7090 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7091 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7093 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7096 if (!MovDelay[x][y])
7098 if (MovDir[x][y] == MV_LEFT)
7100 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7101 DrawLevelField(x - 1, y);
7103 else if (MovDir[x][y] == MV_RIGHT)
7105 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7106 DrawLevelField(x + 1, y);
7108 else if (MovDir[x][y] == MV_UP)
7110 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7111 DrawLevelField(x, y - 1);
7115 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7116 DrawLevelField(x, y + 1);
7119 Feld[x][y] = Store[x][y];
7121 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7122 DrawLevelField(x, y);
7127 void MauerAbleger(int ax, int ay)
7129 int element = Feld[ax][ay];
7130 int graphic = el2img(element);
7131 boolean oben_frei = FALSE, unten_frei = FALSE;
7132 boolean links_frei = FALSE, rechts_frei = FALSE;
7133 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7134 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7135 boolean new_wall = FALSE;
7137 if (IS_ANIMATED(graphic))
7138 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7140 if (!MovDelay[ax][ay]) /* start building new wall */
7141 MovDelay[ax][ay] = 6;
7143 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7146 if (MovDelay[ax][ay])
7150 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7152 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7154 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7156 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7159 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7160 element == EL_EXPANDABLE_WALL_ANY)
7164 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7165 Store[ax][ay-1] = element;
7166 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7167 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7168 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7169 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7174 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7175 Store[ax][ay+1] = element;
7176 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7177 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7178 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7179 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7184 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7185 element == EL_EXPANDABLE_WALL_ANY ||
7186 element == EL_EXPANDABLE_WALL)
7190 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7191 Store[ax-1][ay] = element;
7192 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7193 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7194 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7195 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7201 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7202 Store[ax+1][ay] = element;
7203 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7204 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7205 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7206 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7211 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7212 DrawLevelField(ax, ay);
7214 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7216 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7217 unten_massiv = TRUE;
7218 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7219 links_massiv = TRUE;
7220 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7221 rechts_massiv = TRUE;
7223 if (((oben_massiv && unten_massiv) ||
7224 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7225 element == EL_EXPANDABLE_WALL) &&
7226 ((links_massiv && rechts_massiv) ||
7227 element == EL_EXPANDABLE_WALL_VERTICAL))
7228 Feld[ax][ay] = EL_WALL;
7232 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7234 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7238 void CheckForDragon(int x, int y)
7241 boolean dragon_found = FALSE;
7242 static int xy[4][2] =
7250 for (i = 0; i < NUM_DIRECTIONS; i++)
7252 for (j = 0; j < 4; j++)
7254 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7256 if (IN_LEV_FIELD(xx, yy) &&
7257 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7259 if (Feld[xx][yy] == EL_DRAGON)
7260 dragon_found = TRUE;
7269 for (i = 0; i < NUM_DIRECTIONS; i++)
7271 for (j = 0; j < 3; j++)
7273 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7275 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7277 Feld[xx][yy] = EL_EMPTY;
7278 DrawLevelField(xx, yy);
7287 static void InitBuggyBase(int x, int y)
7289 int element = Feld[x][y];
7290 int activating_delay = FRAMES_PER_SECOND / 4;
7293 (element == EL_SP_BUGGY_BASE ?
7294 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7295 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7297 element == EL_SP_BUGGY_BASE_ACTIVE ?
7298 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7301 static void WarnBuggyBase(int x, int y)
7304 static int xy[4][2] =
7312 for (i = 0; i < NUM_DIRECTIONS; i++)
7314 int xx = x + xy[i][0], yy = y + xy[i][1];
7316 if (IS_PLAYER(xx, yy))
7318 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7325 static void InitTrap(int x, int y)
7327 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7330 static void ActivateTrap(int x, int y)
7332 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7335 static void ChangeActiveTrap(int x, int y)
7337 int graphic = IMG_TRAP_ACTIVE;
7339 /* if new animation frame was drawn, correct crumbled sand border */
7340 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7341 DrawLevelFieldCrumbledSand(x, y);
7344 static void ChangeElementNowExt(int x, int y, int target_element)
7346 int previous_move_direction = MovDir[x][y];
7348 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7349 IS_WALKABLE(Feld[x][y]));
7351 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7352 IS_WALKABLE(Feld[x][y]) &&
7356 /* check if element under player changes from accessible to unaccessible
7357 (needed for special case of dropping element which then changes) */
7358 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7359 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7370 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7371 RemoveMovingField(x, y);
7375 Feld[x][y] = target_element;
7378 Feld[x][y] = target_element;
7381 ResetGfxAnimation(x, y);
7382 ResetRandomAnimationValue(x, y);
7384 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7385 MovDir[x][y] = previous_move_direction;
7388 InitField_WithBug1(x, y, FALSE);
7390 InitField(x, y, FALSE);
7391 if (CAN_MOVE(Feld[x][y]))
7395 DrawLevelField(x, y);
7397 if (GFX_CRUMBLED(Feld[x][y]))
7398 DrawLevelFieldCrumbledSandNeighbours(x, y);
7401 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7404 TestIfBadThingTouchesHero(x, y);
7405 TestIfPlayerTouchesCustomElement(x, y);
7406 TestIfElementTouchesCustomElement(x, y);
7409 if (ELEM_IS_PLAYER(target_element))
7410 RelocatePlayer(x, y, target_element);
7413 TestIfBadThingTouchesHero(x, y);
7414 TestIfPlayerTouchesCustomElement(x, y);
7415 TestIfElementTouchesCustomElement(x, y);
7419 static boolean ChangeElementNow(int x, int y, int element, int page)
7421 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7423 int old_element = Feld[x][y];
7425 /* always use default change event to prevent running into a loop */
7426 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7427 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7429 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7431 /* reset actual trigger element and player */
7432 change->actual_trigger_element = EL_EMPTY;
7433 change->actual_trigger_player = EL_PLAYER_1;
7436 /* do not change already changed elements with same change event */
7438 if (Changed[x][y] & ChangeEvent[x][y])
7445 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7448 /* !!! indirect change before direct change !!! */
7449 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7452 if (change->explode)
7459 if (change->use_target_content)
7461 boolean complete_replace = TRUE;
7462 boolean can_replace[3][3];
7465 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7468 boolean is_walkable;
7469 boolean is_diggable;
7470 boolean is_collectible;
7471 boolean is_removable;
7472 boolean is_destructible;
7473 int ex = x + xx - 1;
7474 int ey = y + yy - 1;
7475 int content_element = change->target_content[xx][yy];
7478 can_replace[xx][yy] = TRUE;
7480 if (ex == x && ey == y) /* do not check changing element itself */
7483 if (content_element == EL_EMPTY_SPACE)
7485 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7490 if (!IN_LEV_FIELD(ex, ey))
7492 can_replace[xx][yy] = FALSE;
7493 complete_replace = FALSE;
7500 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7501 e = MovingOrBlocked2Element(ex, ey);
7506 is_empty = (IS_FREE(ex, ey) ||
7507 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7508 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7509 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7511 is_empty = (IS_FREE(ex, ey) ||
7512 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7514 is_walkable = (is_empty || IS_WALKABLE(e));
7515 is_diggable = (is_empty || IS_DIGGABLE(e));
7516 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7517 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7518 is_removable = (is_diggable || is_collectible);
7520 can_replace[xx][yy] =
7521 ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7522 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7523 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7524 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7525 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7526 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7528 if (!can_replace[xx][yy])
7529 complete_replace = FALSE;
7531 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7532 IS_WALKABLE(content_element)));
7534 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7536 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7539 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7540 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7541 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7543 can_replace[xx][yy] = FALSE;
7544 complete_replace = FALSE;
7549 if (!change->only_if_complete || complete_replace)
7551 boolean something_has_changed = FALSE;
7553 if (change->only_if_complete && change->use_random_replace &&
7554 RND(100) < change->random_percentage)
7557 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7559 int ex = x + xx - 1;
7560 int ey = y + yy - 1;
7561 int content_element;
7563 if (can_replace[xx][yy] && (!change->use_random_replace ||
7564 RND(100) < change->random_percentage))
7566 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7567 RemoveMovingField(ex, ey);
7569 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7571 content_element = change->target_content[xx][yy];
7572 target_element = GET_TARGET_ELEMENT(content_element, change);
7574 ChangeElementNowExt(ex, ey, target_element);
7576 something_has_changed = TRUE;
7578 /* for symmetry reasons, freeze newly created border elements */
7579 if (ex != x || ey != y)
7580 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7584 if (something_has_changed)
7585 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7590 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7592 ChangeElementNowExt(x, y, target_element);
7594 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7598 /* this uses direct change before indirect change */
7599 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7605 static void ChangeElement(int x, int y, int page)
7607 int element = MovingOrBlocked2Element(x, y);
7608 struct ElementInfo *ei = &element_info[element];
7609 struct ElementChangeInfo *change = &ei->change_page[page];
7612 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7615 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7616 x, y, element, element_info[element].token_name);
7617 printf("ChangeElement(): This should never happen!\n");
7622 /* this can happen with classic bombs on walkable, changing elements */
7623 if (!CAN_CHANGE(element))
7626 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7627 ChangeDelay[x][y] = 0;
7633 if (ChangeDelay[x][y] == 0) /* initialize element change */
7635 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7636 RND(change->delay_random * change->delay_frames)) + 1;
7638 ResetGfxAnimation(x, y);
7639 ResetRandomAnimationValue(x, y);
7641 if (change->pre_change_function)
7642 change->pre_change_function(x, y);
7645 ChangeDelay[x][y]--;
7647 if (ChangeDelay[x][y] != 0) /* continue element change */
7649 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7651 if (IS_ANIMATED(graphic))
7652 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7654 if (change->change_function)
7655 change->change_function(x, y);
7657 else /* finish element change */
7659 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7661 page = ChangePage[x][y];
7662 ChangePage[x][y] = -1;
7664 change = &ei->change_page[page];
7668 if (IS_MOVING(x, y) && !change->explode)
7670 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7673 ChangeDelay[x][y] = 1; /* try change after next move step */
7674 ChangePage[x][y] = page; /* remember page to use for change */
7679 if (ChangeElementNow(x, y, element, page))
7681 if (change->post_change_function)
7682 change->post_change_function(x, y);
7687 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7688 int trigger_element,
7695 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7697 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7700 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7702 int element = EL_CUSTOM_START + i;
7704 boolean change_element = FALSE;
7707 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7710 for (j = 0; j < element_info[element].num_change_pages; j++)
7712 struct ElementChangeInfo *change = &element_info[element].change_page[j];
7714 if (change->can_change &&
7715 change->events & CH_EVENT_BIT(trigger_event) &&
7716 change->trigger_side & trigger_side &&
7717 change->trigger_player & trigger_player &&
7718 change->trigger_page & trigger_page_bits &&
7719 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7722 if (!(change->events & CH_EVENT_BIT(trigger_event)))
7723 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7724 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7727 change_element = TRUE;
7730 change->actual_trigger_element = trigger_element;
7731 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7737 if (!change_element)
7740 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7743 if (x == lx && y == ly) /* do not change trigger element itself */
7747 if (Feld[x][y] == element)
7749 ChangeDelay[x][y] = 1;
7750 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7751 ChangeElement(x, y, page);
7759 static boolean CheckElementChangeExt(int x, int y,
7761 int trigger_element,
7767 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7770 if (Feld[x][y] == EL_BLOCKED)
7772 Blocked2Moving(x, y, &x, &y);
7773 element = Feld[x][y];
7777 if (Feld[x][y] != element) /* check if element has already changed */
7780 printf("::: %d ('%s') != %d ('%s') [%d]\n",
7781 Feld[x][y], element_info[Feld[x][y]].token_name,
7782 element, element_info[element].token_name,
7791 if (trigger_page < 0)
7793 boolean change_element = FALSE;
7796 for (i = 0; i < element_info[element].num_change_pages; i++)
7798 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7800 if (change->can_change &&
7801 change->events & CH_EVENT_BIT(trigger_event) &&
7802 change->trigger_side & trigger_side &&
7803 change->trigger_player & trigger_player)
7805 change_element = TRUE;
7808 change->actual_trigger_element = trigger_element;
7809 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7815 if (!change_element)
7820 struct ElementInfo *ei = &element_info[element];
7821 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7823 change->actual_trigger_element = trigger_element;
7824 change->actual_trigger_player = EL_PLAYER_1; /* unused */
7829 /* !!! this check misses pages with same event, but different side !!! */
7831 if (trigger_page < 0)
7832 trigger_page = element_info[element].event_page_nr[trigger_event];
7834 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7838 ChangeDelay[x][y] = 1;
7839 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7840 ChangeElement(x, y, trigger_page);
7845 static void PlayPlayerSound(struct PlayerInfo *player)
7847 int jx = player->jx, jy = player->jy;
7848 int element = player->element_nr;
7849 int last_action = player->last_action_waiting;
7850 int action = player->action_waiting;
7852 if (player->is_waiting)
7854 if (action != last_action)
7855 PlayLevelSoundElementAction(jx, jy, element, action);
7857 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7861 if (action != last_action)
7862 StopSound(element_info[element].sound[last_action]);
7864 if (last_action == ACTION_SLEEPING)
7865 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7869 static void PlayAllPlayersSound()
7873 for (i = 0; i < MAX_PLAYERS; i++)
7874 if (stored_player[i].active)
7875 PlayPlayerSound(&stored_player[i]);
7878 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7880 boolean last_waiting = player->is_waiting;
7881 int move_dir = player->MovDir;
7883 player->last_action_waiting = player->action_waiting;
7887 if (!last_waiting) /* not waiting -> waiting */
7889 player->is_waiting = TRUE;
7891 player->frame_counter_bored =
7893 game.player_boring_delay_fixed +
7894 SimpleRND(game.player_boring_delay_random);
7895 player->frame_counter_sleeping =
7897 game.player_sleeping_delay_fixed +
7898 SimpleRND(game.player_sleeping_delay_random);
7900 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7903 if (game.player_sleeping_delay_fixed +
7904 game.player_sleeping_delay_random > 0 &&
7905 player->anim_delay_counter == 0 &&
7906 player->post_delay_counter == 0 &&
7907 FrameCounter >= player->frame_counter_sleeping)
7908 player->is_sleeping = TRUE;
7909 else if (game.player_boring_delay_fixed +
7910 game.player_boring_delay_random > 0 &&
7911 FrameCounter >= player->frame_counter_bored)
7912 player->is_bored = TRUE;
7914 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7915 player->is_bored ? ACTION_BORING :
7918 if (player->is_sleeping)
7920 if (player->num_special_action_sleeping > 0)
7922 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7924 int last_special_action = player->special_action_sleeping;
7925 int num_special_action = player->num_special_action_sleeping;
7926 int special_action =
7927 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7928 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7929 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7930 last_special_action + 1 : ACTION_SLEEPING);
7931 int special_graphic =
7932 el_act_dir2img(player->element_nr, special_action, move_dir);
7934 player->anim_delay_counter =
7935 graphic_info[special_graphic].anim_delay_fixed +
7936 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7937 player->post_delay_counter =
7938 graphic_info[special_graphic].post_delay_fixed +
7939 SimpleRND(graphic_info[special_graphic].post_delay_random);
7941 player->special_action_sleeping = special_action;
7944 if (player->anim_delay_counter > 0)
7946 player->action_waiting = player->special_action_sleeping;
7947 player->anim_delay_counter--;
7949 else if (player->post_delay_counter > 0)
7951 player->post_delay_counter--;
7955 else if (player->is_bored)
7957 if (player->num_special_action_bored > 0)
7959 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7961 int special_action =
7962 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7963 int special_graphic =
7964 el_act_dir2img(player->element_nr, special_action, move_dir);
7966 player->anim_delay_counter =
7967 graphic_info[special_graphic].anim_delay_fixed +
7968 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7969 player->post_delay_counter =
7970 graphic_info[special_graphic].post_delay_fixed +
7971 SimpleRND(graphic_info[special_graphic].post_delay_random);
7973 player->special_action_bored = special_action;
7976 if (player->anim_delay_counter > 0)
7978 player->action_waiting = player->special_action_bored;
7979 player->anim_delay_counter--;
7981 else if (player->post_delay_counter > 0)
7983 player->post_delay_counter--;
7988 else if (last_waiting) /* waiting -> not waiting */
7990 player->is_waiting = FALSE;
7991 player->is_bored = FALSE;
7992 player->is_sleeping = FALSE;
7994 player->frame_counter_bored = -1;
7995 player->frame_counter_sleeping = -1;
7997 player->anim_delay_counter = 0;
7998 player->post_delay_counter = 0;
8000 player->action_waiting = ACTION_DEFAULT;
8002 player->special_action_bored = ACTION_DEFAULT;
8003 player->special_action_sleeping = ACTION_DEFAULT;
8008 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8011 static byte stored_player_action[MAX_PLAYERS];
8012 static int num_stored_actions = 0;
8014 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8015 int left = player_action & JOY_LEFT;
8016 int right = player_action & JOY_RIGHT;
8017 int up = player_action & JOY_UP;
8018 int down = player_action & JOY_DOWN;
8019 int button1 = player_action & JOY_BUTTON_1;
8020 int button2 = player_action & JOY_BUTTON_2;
8021 int dx = (left ? -1 : right ? 1 : 0);
8022 int dy = (up ? -1 : down ? 1 : 0);
8025 stored_player_action[player->index_nr] = 0;
8026 num_stored_actions++;
8030 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8033 if (!player->active || tape.pausing)
8037 printf("::: [%d %d %d %d] [%d %d]\n",
8038 left, right, up, down, button1, button2);
8044 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8049 if (player->MovPos == 0)
8050 CheckGravityMovement(player);
8053 snapped = SnapField(player, dx, dy);
8057 dropped = DropElement(player);
8059 moved = MovePlayer(player, dx, dy);
8062 if (tape.single_step && tape.recording && !tape.pausing)
8064 if (button1 || (dropped && !moved))
8066 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8067 SnapField(player, 0, 0); /* stop snapping */
8071 SetPlayerWaiting(player, FALSE);
8074 return player_action;
8076 stored_player_action[player->index_nr] = player_action;
8082 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8085 /* no actions for this player (no input at player's configured device) */
8087 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8088 SnapField(player, 0, 0);
8089 CheckGravityMovementWhenNotMoving(player);
8091 if (player->MovPos == 0)
8092 SetPlayerWaiting(player, TRUE);
8094 if (player->MovPos == 0) /* needed for tape.playing */
8095 player->is_moving = FALSE;
8097 player->is_dropping = FALSE;
8103 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8105 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8107 TapeRecordAction(stored_player_action);
8108 num_stored_actions = 0;
8115 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8117 static byte stored_player_action[MAX_PLAYERS];
8118 static int num_stored_actions = 0;
8119 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8120 int left = player_action & JOY_LEFT;
8121 int right = player_action & JOY_RIGHT;
8122 int up = player_action & JOY_UP;
8123 int down = player_action & JOY_DOWN;
8124 int button1 = player_action & JOY_BUTTON_1;
8125 int button2 = player_action & JOY_BUTTON_2;
8126 int dx = (left ? -1 : right ? 1 : 0);
8127 int dy = (up ? -1 : down ? 1 : 0);
8129 stored_player_action[player->index_nr] = 0;
8130 num_stored_actions++;
8132 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8134 if (!player->active || tape.pausing)
8139 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8142 snapped = SnapField(player, dx, dy);
8146 dropped = DropElement(player);
8148 moved = MovePlayer(player, dx, dy);
8151 if (tape.single_step && tape.recording && !tape.pausing)
8153 if (button1 || (dropped && !moved))
8155 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8156 SnapField(player, 0, 0); /* stop snapping */
8160 stored_player_action[player->index_nr] = player_action;
8164 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8166 /* no actions for this player (no input at player's configured device) */
8168 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8169 SnapField(player, 0, 0);
8170 CheckGravityMovementWhenNotMoving(player);
8172 if (player->MovPos == 0)
8173 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8175 if (player->MovPos == 0) /* needed for tape.playing */
8176 player->is_moving = FALSE;
8179 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8181 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8183 TapeRecordAction(stored_player_action);
8184 num_stored_actions = 0;
8191 static unsigned long action_delay = 0;
8192 unsigned long action_delay_value;
8193 int magic_wall_x = 0, magic_wall_y = 0;
8194 int i, x, y, element, graphic;
8195 byte *recorded_player_action;
8196 byte summarized_player_action = 0;
8198 byte tape_action[MAX_PLAYERS];
8201 if (game_status != GAME_MODE_PLAYING)
8204 action_delay_value =
8205 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8207 if (tape.playing && tape.warp_forward && !tape.pausing)
8208 action_delay_value = 0;
8210 /* ---------- main game synchronization point ---------- */
8212 WaitUntilDelayReached(&action_delay, action_delay_value);
8214 if (network_playing && !network_player_action_received)
8218 printf("DEBUG: try to get network player actions in time\n");
8222 #if defined(NETWORK_AVALIABLE)
8223 /* last chance to get network player actions without main loop delay */
8227 if (game_status != GAME_MODE_PLAYING)
8230 if (!network_player_action_received)
8234 printf("DEBUG: failed to get network player actions in time\n");
8245 printf("::: getting new tape action [%d]\n", FrameCounter);
8248 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8251 if (recorded_player_action == NULL && tape.pausing)
8256 printf("::: %d\n", stored_player[0].action);
8260 if (recorded_player_action != NULL)
8261 for (i = 0; i < MAX_PLAYERS; i++)
8262 stored_player[i].action = recorded_player_action[i];
8265 for (i = 0; i < MAX_PLAYERS; i++)
8267 summarized_player_action |= stored_player[i].action;
8269 if (!network_playing)
8270 stored_player[i].effective_action = stored_player[i].action;
8273 #if defined(NETWORK_AVALIABLE)
8274 if (network_playing)
8275 SendToServer_MovePlayer(summarized_player_action);
8278 if (!options.network && !setup.team_mode)
8279 local_player->effective_action = summarized_player_action;
8282 if (recorded_player_action != NULL)
8283 for (i = 0; i < MAX_PLAYERS; i++)
8284 stored_player[i].effective_action = recorded_player_action[i];
8288 for (i = 0; i < MAX_PLAYERS; i++)
8290 tape_action[i] = stored_player[i].effective_action;
8292 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8293 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8296 /* only save actions from input devices, but not programmed actions */
8298 TapeRecordAction(tape_action);
8301 for (i = 0; i < MAX_PLAYERS; i++)
8303 int actual_player_action = stored_player[i].effective_action;
8306 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8307 - rnd_equinox_tetrachloride 048
8308 - rnd_equinox_tetrachloride_ii 096
8309 - rnd_emanuel_schmieg 002
8310 - doctor_sloan_ww 001, 020
8312 if (stored_player[i].MovPos == 0)
8313 CheckGravityMovement(&stored_player[i]);
8317 /* overwrite programmed action with tape action */
8318 if (stored_player[i].programmed_action)
8319 actual_player_action = stored_player[i].programmed_action;
8323 if (stored_player[i].programmed_action)
8324 printf("::: %d\n", stored_player[i].programmed_action);
8327 if (recorded_player_action)
8330 if (stored_player[i].programmed_action &&
8331 stored_player[i].programmed_action != recorded_player_action[i])
8332 printf("::: %d: %d <-> %d\n", i,
8333 stored_player[i].programmed_action, recorded_player_action[i]);
8337 actual_player_action = recorded_player_action[i];
8342 /* overwrite tape action with programmed action */
8343 if (stored_player[i].programmed_action)
8344 actual_player_action = stored_player[i].programmed_action;
8349 printf("::: action: %d: %x [%d]\n",
8350 stored_player[i].MovPos, actual_player_action, FrameCounter);
8354 PlayerActions(&stored_player[i], actual_player_action);
8356 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8358 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8359 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8362 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8367 TapeRecordAction(tape_action);
8370 network_player_action_received = FALSE;
8372 ScrollScreen(NULL, SCROLL_GO_ON);
8378 for (i = 0; i < MAX_PLAYERS; i++)
8379 stored_player[i].Frame++;
8383 /* for downwards compatibility, the following code emulates a fixed bug that
8384 occured when pushing elements (causing elements that just made their last
8385 pushing step to already (if possible) make their first falling step in the
8386 same game frame, which is bad); this code is also needed to use the famous
8387 "spring push bug" which is used in older levels and might be wanted to be
8388 used also in newer levels, but in this case the buggy pushing code is only
8389 affecting the "spring" element and no other elements */
8392 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8394 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8397 for (i = 0; i < MAX_PLAYERS; i++)
8399 struct PlayerInfo *player = &stored_player[i];
8404 if (player->active && player->is_pushing && player->is_moving &&
8406 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8407 Feld[x][y] == EL_SPRING))
8409 if (player->active && player->is_pushing && player->is_moving &&
8413 ContinueMoving(x, y);
8415 /* continue moving after pushing (this is actually a bug) */
8416 if (!IS_MOVING(x, y))
8425 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8427 Changed[x][y] = CE_BITMASK_DEFAULT;
8428 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8431 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8433 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8434 printf("GameActions(): This should never happen!\n");
8436 ChangePage[x][y] = -1;
8441 if (WasJustMoving[x][y] > 0)
8442 WasJustMoving[x][y]--;
8443 if (WasJustFalling[x][y] > 0)
8444 WasJustFalling[x][y]--;
8445 if (CheckCollision[x][y] > 0)
8446 CheckCollision[x][y]--;
8451 /* reset finished pushing action (not done in ContinueMoving() to allow
8452 continous pushing animation for elements with zero push delay) */
8453 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8455 ResetGfxAnimation(x, y);
8456 DrawLevelField(x, y);
8461 if (IS_BLOCKED(x, y))
8465 Blocked2Moving(x, y, &oldx, &oldy);
8466 if (!IS_MOVING(oldx, oldy))
8468 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8469 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8470 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8471 printf("GameActions(): This should never happen!\n");
8477 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8479 element = Feld[x][y];
8481 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8483 graphic = el2img(element);
8489 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8491 element = graphic = 0;
8495 if (graphic_info[graphic].anim_global_sync)
8496 GfxFrame[x][y] = FrameCounter;
8498 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8499 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8500 ResetRandomAnimationValue(x, y);
8502 SetRandomAnimationValue(x, y);
8505 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8508 if (IS_INACTIVE(element))
8510 if (IS_ANIMATED(graphic))
8511 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8517 /* this may take place after moving, so 'element' may have changed */
8519 if (IS_CHANGING(x, y))
8521 if (IS_CHANGING(x, y) &&
8522 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8526 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8527 element_info[element].event_page_nr[CE_DELAY]);
8529 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8532 element = Feld[x][y];
8533 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8537 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8542 element = Feld[x][y];
8543 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8545 if (element == EL_MOLE)
8546 printf("::: %d, %d, %d [%d]\n",
8547 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8551 if (element == EL_YAMYAM)
8552 printf("::: %d, %d, %d\n",
8553 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8557 if (IS_ANIMATED(graphic) &&
8561 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8564 if (element == EL_BUG)
8565 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8569 if (element == EL_MOLE)
8570 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8574 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8575 EdelsteinFunkeln(x, y);
8577 else if ((element == EL_ACID ||
8578 element == EL_EXIT_OPEN ||
8579 element == EL_SP_EXIT_OPEN ||
8580 element == EL_SP_TERMINAL ||
8581 element == EL_SP_TERMINAL_ACTIVE ||
8582 element == EL_EXTRA_TIME ||
8583 element == EL_SHIELD_NORMAL ||
8584 element == EL_SHIELD_DEADLY) &&
8585 IS_ANIMATED(graphic))
8586 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8587 else if (IS_MOVING(x, y))
8588 ContinueMoving(x, y);
8589 else if (IS_ACTIVE_BOMB(element))
8590 CheckDynamite(x, y);
8592 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8593 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8595 else if (element == EL_AMOEBA_GROWING)
8596 AmoebeWaechst(x, y);
8597 else if (element == EL_AMOEBA_SHRINKING)
8598 AmoebaDisappearing(x, y);
8600 #if !USE_NEW_AMOEBA_CODE
8601 else if (IS_AMOEBALIVE(element))
8602 AmoebeAbleger(x, y);
8605 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8607 else if (element == EL_EXIT_CLOSED)
8609 else if (element == EL_SP_EXIT_CLOSED)
8611 else if (element == EL_EXPANDABLE_WALL_GROWING)
8613 else if (element == EL_EXPANDABLE_WALL ||
8614 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8615 element == EL_EXPANDABLE_WALL_VERTICAL ||
8616 element == EL_EXPANDABLE_WALL_ANY)
8618 else if (element == EL_FLAMES)
8619 CheckForDragon(x, y);
8621 else if (IS_AUTO_CHANGING(element))
8622 ChangeElement(x, y);
8624 else if (element == EL_EXPLOSION)
8625 ; /* drawing of correct explosion animation is handled separately */
8626 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8627 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8630 /* this may take place after moving, so 'element' may have changed */
8631 if (IS_AUTO_CHANGING(Feld[x][y]))
8632 ChangeElement(x, y);
8635 if (IS_BELT_ACTIVE(element))
8636 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8638 if (game.magic_wall_active)
8640 int jx = local_player->jx, jy = local_player->jy;
8642 /* play the element sound at the position nearest to the player */
8643 if ((element == EL_MAGIC_WALL_FULL ||
8644 element == EL_MAGIC_WALL_ACTIVE ||
8645 element == EL_MAGIC_WALL_EMPTYING ||
8646 element == EL_BD_MAGIC_WALL_FULL ||
8647 element == EL_BD_MAGIC_WALL_ACTIVE ||
8648 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8649 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8657 #if USE_NEW_AMOEBA_CODE
8658 /* new experimental amoeba growth stuff */
8660 if (!(FrameCounter % 8))
8663 static unsigned long random = 1684108901;
8665 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8668 x = (random >> 10) % lev_fieldx;
8669 y = (random >> 20) % lev_fieldy;
8671 x = RND(lev_fieldx);
8672 y = RND(lev_fieldy);
8674 element = Feld[x][y];
8677 if (!IS_PLAYER(x,y) &&
8678 (element == EL_EMPTY ||
8679 CAN_GROW_INTO(element) ||
8680 element == EL_QUICKSAND_EMPTY ||
8681 element == EL_ACID_SPLASH_LEFT ||
8682 element == EL_ACID_SPLASH_RIGHT))
8684 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8685 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8686 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8687 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8688 Feld[x][y] = EL_AMOEBA_DROP;
8691 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8692 if (!IS_PLAYER(x,y) &&
8693 (element == EL_EMPTY ||
8694 element == EL_SAND ||
8695 element == EL_QUICKSAND_EMPTY ||
8696 element == EL_ACID_SPLASH_LEFT ||
8697 element == EL_ACID_SPLASH_RIGHT))
8699 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8700 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8701 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8702 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8703 Feld[x][y] = EL_AMOEBA_DROP;
8707 random = random * 129 + 1;
8713 if (game.explosions_delayed)
8716 game.explosions_delayed = FALSE;
8718 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8720 element = Feld[x][y];
8722 if (ExplodeField[x][y])
8723 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8724 else if (element == EL_EXPLOSION)
8725 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8727 ExplodeField[x][y] = EX_TYPE_NONE;
8730 game.explosions_delayed = TRUE;
8733 if (game.magic_wall_active)
8735 if (!(game.magic_wall_time_left % 4))
8737 int element = Feld[magic_wall_x][magic_wall_y];
8739 if (element == EL_BD_MAGIC_WALL_FULL ||
8740 element == EL_BD_MAGIC_WALL_ACTIVE ||
8741 element == EL_BD_MAGIC_WALL_EMPTYING)
8742 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8744 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8747 if (game.magic_wall_time_left > 0)
8749 game.magic_wall_time_left--;
8750 if (!game.magic_wall_time_left)
8752 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8754 element = Feld[x][y];
8756 if (element == EL_MAGIC_WALL_ACTIVE ||
8757 element == EL_MAGIC_WALL_FULL)
8759 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8760 DrawLevelField(x, y);
8762 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8763 element == EL_BD_MAGIC_WALL_FULL)
8765 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8766 DrawLevelField(x, y);
8770 game.magic_wall_active = FALSE;
8775 if (game.light_time_left > 0)
8777 game.light_time_left--;
8779 if (game.light_time_left == 0)
8780 RedrawAllLightSwitchesAndInvisibleElements();
8783 if (game.timegate_time_left > 0)
8785 game.timegate_time_left--;
8787 if (game.timegate_time_left == 0)
8788 CloseAllOpenTimegates();
8791 for (i = 0; i < MAX_PLAYERS; i++)
8793 struct PlayerInfo *player = &stored_player[i];
8795 if (SHIELD_ON(player))
8797 if (player->shield_deadly_time_left)
8798 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8799 else if (player->shield_normal_time_left)
8800 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8804 if (TimeFrames >= FRAMES_PER_SECOND)
8809 for (i = 0; i < MAX_PLAYERS; i++)
8811 struct PlayerInfo *player = &stored_player[i];
8813 if (SHIELD_ON(player))
8815 player->shield_normal_time_left--;
8817 if (player->shield_deadly_time_left > 0)
8818 player->shield_deadly_time_left--;
8822 if (!level.use_step_counter)
8830 if (TimeLeft <= 10 && setup.time_limit)
8831 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8833 DrawGameValue_Time(TimeLeft);
8835 if (!TimeLeft && setup.time_limit)
8836 for (i = 0; i < MAX_PLAYERS; i++)
8837 KillHero(&stored_player[i]);
8839 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8840 DrawGameValue_Time(TimePlayed);
8843 if (tape.recording || tape.playing)
8844 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8848 PlayAllPlayersSound();
8850 if (options.debug) /* calculate frames per second */
8852 static unsigned long fps_counter = 0;
8853 static int fps_frames = 0;
8854 unsigned long fps_delay_ms = Counter() - fps_counter;
8858 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8860 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8863 fps_counter = Counter();
8866 redraw_mask |= REDRAW_FPS;
8870 if (stored_player[0].jx != stored_player[0].last_jx ||
8871 stored_player[0].jy != stored_player[0].last_jy)
8872 printf("::: %d, %d, %d, %d, %d\n",
8873 stored_player[0].MovDir,
8874 stored_player[0].MovPos,
8875 stored_player[0].GfxPos,
8876 stored_player[0].Frame,
8877 stored_player[0].StepFrame);
8884 for (i = 0; i < MAX_PLAYERS; i++)
8887 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8889 stored_player[i].Frame += move_frames;
8891 if (stored_player[i].MovPos != 0)
8892 stored_player[i].StepFrame += move_frames;
8894 if (stored_player[i].drop_delay > 0)
8895 stored_player[i].drop_delay--;
8900 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8902 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8904 local_player->show_envelope = 0;
8909 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8911 int min_x = x, min_y = y, max_x = x, max_y = y;
8914 for (i = 0; i < MAX_PLAYERS; i++)
8916 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8918 if (!stored_player[i].active || &stored_player[i] == player)
8921 min_x = MIN(min_x, jx);
8922 min_y = MIN(min_y, jy);
8923 max_x = MAX(max_x, jx);
8924 max_y = MAX(max_y, jy);
8927 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8930 static boolean AllPlayersInVisibleScreen()
8934 for (i = 0; i < MAX_PLAYERS; i++)
8936 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8938 if (!stored_player[i].active)
8941 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8948 void ScrollLevel(int dx, int dy)
8950 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8953 BlitBitmap(drawto_field, drawto_field,
8954 FX + TILEX * (dx == -1) - softscroll_offset,
8955 FY + TILEY * (dy == -1) - softscroll_offset,
8956 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8957 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8958 FX + TILEX * (dx == 1) - softscroll_offset,
8959 FY + TILEY * (dy == 1) - softscroll_offset);
8963 x = (dx == 1 ? BX1 : BX2);
8964 for (y = BY1; y <= BY2; y++)
8965 DrawScreenField(x, y);
8970 y = (dy == 1 ? BY1 : BY2);
8971 for (x = BX1; x <= BX2; x++)
8972 DrawScreenField(x, y);
8975 redraw_mask |= REDRAW_FIELD;
8979 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8981 int nextx = x + dx, nexty = y + dy;
8982 int element = Feld[x][y];
8985 element != EL_SP_PORT_LEFT &&
8986 element != EL_SP_GRAVITY_PORT_LEFT &&
8987 element != EL_SP_PORT_HORIZONTAL &&
8988 element != EL_SP_PORT_ANY) ||
8990 element != EL_SP_PORT_RIGHT &&
8991 element != EL_SP_GRAVITY_PORT_RIGHT &&
8992 element != EL_SP_PORT_HORIZONTAL &&
8993 element != EL_SP_PORT_ANY) ||
8995 element != EL_SP_PORT_UP &&
8996 element != EL_SP_GRAVITY_PORT_UP &&
8997 element != EL_SP_PORT_VERTICAL &&
8998 element != EL_SP_PORT_ANY) ||
9000 element != EL_SP_PORT_DOWN &&
9001 element != EL_SP_GRAVITY_PORT_DOWN &&
9002 element != EL_SP_PORT_VERTICAL &&
9003 element != EL_SP_PORT_ANY) ||
9004 !IN_LEV_FIELD(nextx, nexty) ||
9005 !IS_FREE(nextx, nexty))
9012 static boolean canFallDown(struct PlayerInfo *player)
9014 int jx = player->jx, jy = player->jy;
9016 return (IN_LEV_FIELD(jx, jy + 1) &&
9017 (IS_FREE(jx, jy + 1) ||
9018 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9019 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9020 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9023 static boolean canPassField(int x, int y, int move_dir)
9025 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9026 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9027 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9030 int element = Feld[x][y];
9032 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9033 !CAN_MOVE(element) &&
9034 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9035 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9036 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9039 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9041 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9042 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9043 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9047 int nextx = newx + dx;
9048 int nexty = newy + dy;
9052 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9053 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9054 (IS_DIGGABLE(Feld[newx][newy]) ||
9055 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9056 canPassField(newx, newy, move_dir)));
9059 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9060 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9061 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9062 canPassField(newx, newy, move_dir)));
9064 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9065 (IS_DIGGABLE(Feld[newx][newy]) ||
9066 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9067 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9068 !CAN_MOVE(Feld[newx][newy]) &&
9069 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9070 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9071 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9076 static void CheckGravityMovement(struct PlayerInfo *player)
9078 if (game.gravity && !player->programmed_action)
9081 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9082 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9084 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9085 int move_dir_vertical = player->action & MV_VERTICAL;
9089 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9091 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9094 int jx = player->jx, jy = player->jy;
9096 boolean player_is_moving_to_valid_field =
9097 (!player_is_snapping &&
9098 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9099 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9103 (player->last_move_dir & MV_HORIZONTAL ?
9104 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9105 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9109 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9110 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9111 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9112 int new_jx = jx + dx, new_jy = jy + dy;
9113 int nextx = new_jx + dx, nexty = new_jy + dy;
9119 boolean player_can_fall_down = canFallDown(player);
9121 boolean player_can_fall_down =
9122 (IN_LEV_FIELD(jx, jy + 1) &&
9123 (IS_FREE(jx, jy + 1) ||
9124 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9128 boolean player_can_fall_down =
9129 (IN_LEV_FIELD(jx, jy + 1) &&
9130 (IS_FREE(jx, jy + 1)));
9134 boolean player_is_moving_to_valid_field =
9137 !player_is_snapping &&
9141 IN_LEV_FIELD(new_jx, new_jy) &&
9142 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9143 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9144 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9145 IN_LEV_FIELD(nextx, nexty) &&
9146 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9148 IN_LEV_FIELD(new_jx, new_jy) &&
9149 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9150 Feld[new_jx][new_jy] == EL_SAND ||
9151 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9152 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9153 /* !!! extend EL_SAND to anything diggable !!! */
9159 boolean player_is_standing_on_valid_field =
9160 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9161 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9165 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9166 player_can_fall_down,
9167 player_is_standing_on_valid_field,
9168 player_is_moving_to_valid_field,
9169 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9170 player->effective_action,
9171 player->can_fall_into_acid);
9174 if (player_can_fall_down &&
9176 !player_is_standing_on_valid_field &&
9178 !player_is_moving_to_valid_field)
9181 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9182 jx, jy, FrameCounter);
9185 player->programmed_action = MV_DOWN;
9190 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9193 return CheckGravityMovement(player);
9196 if (game.gravity && !player->programmed_action)
9198 int jx = player->jx, jy = player->jy;
9199 boolean field_under_player_is_free =
9200 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9201 boolean player_is_standing_on_valid_field =
9202 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9203 (IS_WALKABLE(Feld[jx][jy]) &&
9204 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9206 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9207 player->programmed_action = MV_DOWN;
9213 -----------------------------------------------------------------------------
9214 dx, dy: direction (non-diagonal) to try to move the player to
9215 real_dx, real_dy: direction as read from input device (can be diagonal)
9218 boolean MovePlayerOneStep(struct PlayerInfo *player,
9219 int dx, int dy, int real_dx, int real_dy)
9222 static int trigger_sides[4][2] =
9224 /* enter side leave side */
9225 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9226 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9227 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9228 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9230 int move_direction = (dx == -1 ? MV_LEFT :
9231 dx == +1 ? MV_RIGHT :
9233 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9234 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9235 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9237 int jx = player->jx, jy = player->jy;
9238 int new_jx = jx + dx, new_jy = jy + dy;
9242 if (!player->active || (!dx && !dy))
9243 return MF_NO_ACTION;
9245 player->MovDir = (dx < 0 ? MV_LEFT :
9248 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9250 if (!IN_LEV_FIELD(new_jx, new_jy))
9251 return MF_NO_ACTION;
9253 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9254 return MF_NO_ACTION;
9257 element = MovingOrBlocked2Element(new_jx, new_jy);
9259 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9262 if (DONT_RUN_INTO(element))
9264 if (element == EL_ACID && dx == 0 && dy == 1)
9266 SplashAcid(new_jx, new_jy);
9267 Feld[jx][jy] = EL_PLAYER_1;
9268 InitMovingField(jx, jy, MV_DOWN);
9269 Store[jx][jy] = EL_ACID;
9270 ContinueMoving(jx, jy);
9274 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9279 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9280 if (can_move != MF_MOVING)
9283 /* check if DigField() has caused relocation of the player */
9284 if (player->jx != jx || player->jy != jy)
9285 return MF_NO_ACTION;
9287 StorePlayer[jx][jy] = 0;
9288 player->last_jx = jx;
9289 player->last_jy = jy;
9290 player->jx = new_jx;
9291 player->jy = new_jy;
9292 StorePlayer[new_jx][new_jy] = player->element_nr;
9295 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9297 player->step_counter++;
9300 player->drop_delay = 0;
9303 PlayerVisit[jx][jy] = FrameCounter;
9305 ScrollPlayer(player, SCROLL_INIT);
9308 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9310 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9312 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9315 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9317 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9318 CE_OTHER_GETS_ENTERED, enter_side);
9319 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9320 CE_ENTERED_BY_PLAYER, enter_side);
9327 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9329 int jx = player->jx, jy = player->jy;
9330 int old_jx = jx, old_jy = jy;
9331 int moved = MF_NO_ACTION;
9334 if (!player->active)
9339 if (player->MovPos == 0)
9341 player->is_moving = FALSE;
9342 player->is_digging = FALSE;
9343 player->is_collecting = FALSE;
9344 player->is_snapping = FALSE;
9345 player->is_pushing = FALSE;
9351 if (!player->active || (!dx && !dy))
9356 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9362 if (!FrameReached(&player->move_delay, player->move_delay_value))
9365 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9366 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9372 /* store if player is automatically moved to next field */
9373 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9375 /* remove the last programmed player action */
9376 player->programmed_action = 0;
9380 /* should only happen if pre-1.2 tape recordings are played */
9381 /* this is only for backward compatibility */
9383 int original_move_delay_value = player->move_delay_value;
9386 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9390 /* scroll remaining steps with finest movement resolution */
9391 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9393 while (player->MovPos)
9395 ScrollPlayer(player, SCROLL_GO_ON);
9396 ScrollScreen(NULL, SCROLL_GO_ON);
9402 player->move_delay_value = original_move_delay_value;
9405 if (player->last_move_dir & MV_HORIZONTAL)
9407 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9408 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9412 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9413 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9419 if (moved & MF_MOVING && !ScreenMovPos &&
9420 (player == local_player || !options.network))
9422 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9423 int offset = (setup.scroll_delay ? 3 : 0);
9425 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9427 /* actual player has left the screen -- scroll in that direction */
9428 if (jx != old_jx) /* player has moved horizontally */
9429 scroll_x += (jx - old_jx);
9430 else /* player has moved vertically */
9431 scroll_y += (jy - old_jy);
9435 if (jx != old_jx) /* player has moved horizontally */
9437 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9438 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9439 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9441 /* don't scroll over playfield boundaries */
9442 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9443 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9445 /* don't scroll more than one field at a time */
9446 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9448 /* don't scroll against the player's moving direction */
9449 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9450 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9451 scroll_x = old_scroll_x;
9453 else /* player has moved vertically */
9455 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9456 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9457 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9459 /* don't scroll over playfield boundaries */
9460 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9461 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9463 /* don't scroll more than one field at a time */
9464 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9466 /* don't scroll against the player's moving direction */
9467 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9468 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9469 scroll_y = old_scroll_y;
9473 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9475 if (!options.network && !AllPlayersInVisibleScreen())
9477 scroll_x = old_scroll_x;
9478 scroll_y = old_scroll_y;
9482 ScrollScreen(player, SCROLL_INIT);
9483 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9490 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9492 if (!(moved & MF_MOVING) && !player->is_pushing)
9497 player->StepFrame = 0;
9499 if (moved & MF_MOVING)
9501 if (old_jx != jx && old_jy == jy)
9502 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9503 else if (old_jx == jx && old_jy != jy)
9504 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9506 DrawLevelField(jx, jy); /* for "crumbled sand" */
9508 player->last_move_dir = player->MovDir;
9509 player->is_moving = TRUE;
9511 player->is_snapping = FALSE;
9515 player->is_switching = FALSE;
9518 player->is_dropping = FALSE;
9522 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9525 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9528 static int trigger_sides[4][2] =
9530 /* enter side leave side */
9531 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9532 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9533 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9534 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9536 int move_direction = player->MovDir;
9537 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9538 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9539 int old_element = Feld[old_jx][old_jy];
9540 int new_element = Feld[jx][jy];
9543 /* !!! TEST ONLY !!! */
9544 if (IS_CUSTOM_ELEMENT(old_element))
9545 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9547 player->index_bit, leave_side);
9549 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9551 player->index_bit, leave_side);
9553 if (IS_CUSTOM_ELEMENT(new_element))
9554 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9555 player->index_bit, enter_side);
9557 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9558 CE_OTHER_GETS_ENTERED,
9559 player->index_bit, enter_side);
9569 CheckGravityMovementWhenNotMoving(player);
9572 player->last_move_dir = MV_NO_MOVING;
9574 player->is_moving = FALSE;
9577 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9579 TestIfHeroTouchesBadThing(jx, jy);
9580 TestIfPlayerTouchesCustomElement(jx, jy);
9583 if (!player->active)
9589 void ScrollPlayer(struct PlayerInfo *player, int mode)
9591 int jx = player->jx, jy = player->jy;
9592 int last_jx = player->last_jx, last_jy = player->last_jy;
9593 int move_stepsize = TILEX / player->move_delay_value;
9595 if (!player->active || !player->MovPos)
9598 if (mode == SCROLL_INIT)
9600 player->actual_frame_counter = FrameCounter;
9601 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9603 if (Feld[last_jx][last_jy] == EL_EMPTY)
9604 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9612 else if (!FrameReached(&player->actual_frame_counter, 1))
9615 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9616 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9618 if (!player->block_last_field &&
9619 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9620 Feld[last_jx][last_jy] = EL_EMPTY;
9622 /* before DrawPlayer() to draw correct player graphic for this case */
9623 if (player->MovPos == 0)
9624 CheckGravityMovement(player);
9627 DrawPlayer(player); /* needed here only to cleanup last field */
9630 if (player->MovPos == 0) /* player reached destination field */
9633 if (player->move_delay_reset_counter > 0)
9635 player->move_delay_reset_counter--;
9637 if (player->move_delay_reset_counter == 0)
9639 /* continue with normal speed after quickly moving through gate */
9640 HALVE_PLAYER_SPEED(player);
9642 /* be able to make the next move without delay */
9643 player->move_delay = 0;
9647 if (IS_PASSABLE(Feld[last_jx][last_jy]))
9649 /* continue with normal speed after quickly moving through gate */
9650 HALVE_PLAYER_SPEED(player);
9652 /* be able to make the next move without delay */
9653 player->move_delay = 0;
9657 if (player->block_last_field &&
9658 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9659 Feld[last_jx][last_jy] = EL_EMPTY;
9661 player->last_jx = jx;
9662 player->last_jy = jy;
9664 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9665 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9666 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9668 DrawPlayer(player); /* needed here only to cleanup last field */
9671 if (local_player->friends_still_needed == 0 ||
9672 IS_SP_ELEMENT(Feld[jx][jy]))
9673 player->LevelSolved = player->GameOver = TRUE;
9677 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9678 /* this breaks one level: "machine", level 000 */
9680 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9683 static int trigger_sides[4][2] =
9685 /* enter side leave side */
9686 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9687 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9688 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9689 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9691 int move_direction = player->MovDir;
9692 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9693 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9694 int old_jx = last_jx;
9695 int old_jy = last_jy;
9696 int old_element = Feld[old_jx][old_jy];
9697 int new_element = Feld[jx][jy];
9700 /* !!! TEST ONLY !!! */
9701 if (IS_CUSTOM_ELEMENT(old_element))
9702 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9704 player->index_bit, leave_side);
9706 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9708 player->index_bit, leave_side);
9710 if (IS_CUSTOM_ELEMENT(new_element))
9711 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9712 player->index_bit, enter_side);
9714 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9715 CE_OTHER_GETS_ENTERED,
9716 player->index_bit, enter_side);
9722 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9724 TestIfHeroTouchesBadThing(jx, jy);
9725 TestIfPlayerTouchesCustomElement(jx, jy);
9727 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9730 if (!player->active)
9734 if (level.use_step_counter)
9744 if (TimeLeft <= 10 && setup.time_limit)
9745 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9747 DrawGameValue_Time(TimeLeft);
9749 if (!TimeLeft && setup.time_limit)
9750 for (i = 0; i < MAX_PLAYERS; i++)
9751 KillHero(&stored_player[i]);
9753 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9754 DrawGameValue_Time(TimePlayed);
9757 if (tape.single_step && tape.recording && !tape.pausing &&
9758 !player->programmed_action)
9759 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9763 void ScrollScreen(struct PlayerInfo *player, int mode)
9765 static unsigned long screen_frame_counter = 0;
9767 if (mode == SCROLL_INIT)
9769 /* set scrolling step size according to actual player's moving speed */
9770 ScrollStepSize = TILEX / player->move_delay_value;
9772 screen_frame_counter = FrameCounter;
9773 ScreenMovDir = player->MovDir;
9774 ScreenMovPos = player->MovPos;
9775 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9778 else if (!FrameReached(&screen_frame_counter, 1))
9783 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9784 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9785 redraw_mask |= REDRAW_FIELD;
9788 ScreenMovDir = MV_NO_MOVING;
9791 void TestIfPlayerTouchesCustomElement(int x, int y)
9793 static int xy[4][2] =
9800 static int trigger_sides[4][2] =
9802 /* center side border side */
9803 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9804 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9805 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9806 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9808 static int touch_dir[4] =
9815 int center_element = Feld[x][y]; /* should always be non-moving! */
9818 for (i = 0; i < NUM_DIRECTIONS; i++)
9820 int xx = x + xy[i][0];
9821 int yy = y + xy[i][1];
9822 int center_side = trigger_sides[i][0];
9823 int border_side = trigger_sides[i][1];
9826 if (!IN_LEV_FIELD(xx, yy))
9829 if (IS_PLAYER(x, y))
9831 struct PlayerInfo *player = PLAYERINFO(x, y);
9833 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9834 border_element = Feld[xx][yy]; /* may be moving! */
9835 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9836 border_element = Feld[xx][yy];
9837 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9838 border_element = MovingOrBlocked2Element(xx, yy);
9840 continue; /* center and border element do not touch */
9843 /* !!! TEST ONLY !!! */
9844 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9845 player->index_bit, border_side);
9846 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9847 CE_OTHER_GETS_TOUCHED,
9848 player->index_bit, border_side);
9850 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9851 CE_OTHER_GETS_TOUCHED,
9852 player->index_bit, border_side);
9853 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9854 player->index_bit, border_side);
9857 else if (IS_PLAYER(xx, yy))
9859 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9861 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9863 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9864 continue; /* center and border element do not touch */
9868 /* !!! TEST ONLY !!! */
9869 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9870 player->index_bit, center_side);
9871 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9872 CE_OTHER_GETS_TOUCHED,
9873 player->index_bit, center_side);
9875 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9876 CE_OTHER_GETS_TOUCHED,
9877 player->index_bit, center_side);
9878 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9879 player->index_bit, center_side);
9887 void TestIfElementTouchesCustomElement(int x, int y)
9889 static int xy[4][2] =
9896 static int trigger_sides[4][2] =
9898 /* center side border side */
9899 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9900 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9901 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9902 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9904 static int touch_dir[4] =
9911 boolean change_center_element = FALSE;
9912 int center_element_change_page = 0;
9913 int center_element = Feld[x][y]; /* should always be non-moving! */
9914 int border_trigger_element;
9917 for (i = 0; i < NUM_DIRECTIONS; i++)
9919 int xx = x + xy[i][0];
9920 int yy = y + xy[i][1];
9921 int center_side = trigger_sides[i][0];
9922 int border_side = trigger_sides[i][1];
9925 if (!IN_LEV_FIELD(xx, yy))
9928 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9929 border_element = Feld[xx][yy]; /* may be moving! */
9930 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9931 border_element = Feld[xx][yy];
9932 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9933 border_element = MovingOrBlocked2Element(xx, yy);
9935 continue; /* center and border element do not touch */
9937 /* check for change of center element (but change it only once) */
9938 if (IS_CUSTOM_ELEMENT(center_element) &&
9939 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9940 !change_center_element)
9942 for (j = 0; j < element_info[center_element].num_change_pages; j++)
9944 struct ElementChangeInfo *change =
9945 &element_info[center_element].change_page[j];
9947 if (change->can_change &&
9948 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9949 change->trigger_side & border_side &&
9951 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9953 change->trigger_element == border_element
9957 change_center_element = TRUE;
9958 center_element_change_page = j;
9959 border_trigger_element = border_element;
9966 /* check for change of border element */
9967 if (IS_CUSTOM_ELEMENT(border_element) &&
9968 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9970 for (j = 0; j < element_info[border_element].num_change_pages; j++)
9972 struct ElementChangeInfo *change =
9973 &element_info[border_element].change_page[j];
9975 if (change->can_change &&
9976 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9977 change->trigger_side & center_side &&
9979 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9981 change->trigger_element == center_element
9986 printf("::: border_element %d, %d\n", x, y);
9989 CheckElementChangeByPage(xx, yy, border_element, center_element,
9990 CE_OTHER_IS_TOUCHING, j);
9997 if (change_center_element)
10000 printf("::: center_element %d, %d\n", x, y);
10003 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10004 CE_OTHER_IS_TOUCHING, center_element_change_page);
10008 void TestIfElementHitsCustomElement(int x, int y, int direction)
10010 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10011 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10012 int hitx = x + dx, hity = y + dy;
10013 int hitting_element = Feld[x][y];
10014 int touched_element;
10016 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10017 !IS_FREE(hitx, hity) &&
10018 (!IS_MOVING(hitx, hity) ||
10019 MovDir[hitx][hity] != direction ||
10020 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10023 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10027 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10031 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10032 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10034 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10035 CE_HITTING_SOMETHING, direction);
10037 if (IN_LEV_FIELD(hitx, hity))
10039 int opposite_direction = MV_DIR_OPPOSITE(direction);
10040 int hitting_side = direction;
10041 int touched_side = opposite_direction;
10043 int touched_element = MovingOrBlocked2Element(hitx, hity);
10046 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10047 MovDir[hitx][hity] != direction ||
10048 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10057 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10058 CE_HIT_BY_SOMETHING, opposite_direction);
10060 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10061 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10063 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10065 struct ElementChangeInfo *change =
10066 &element_info[hitting_element].change_page[i];
10068 if (change->can_change &&
10069 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10070 change->trigger_side & touched_side &&
10073 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10075 change->trigger_element == touched_element
10079 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10080 CE_OTHER_IS_HITTING, i);
10086 if (IS_CUSTOM_ELEMENT(touched_element) &&
10087 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10089 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10091 struct ElementChangeInfo *change =
10092 &element_info[touched_element].change_page[i];
10094 if (change->can_change &&
10095 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10096 change->trigger_side & hitting_side &&
10098 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10100 change->trigger_element == hitting_element
10104 CheckElementChangeByPage(hitx, hity, touched_element,
10105 hitting_element, CE_OTHER_GETS_HIT, i);
10115 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10117 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10118 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10119 int hitx = x + dx, hity = y + dy;
10120 int hitting_element = Feld[x][y];
10121 int touched_element;
10123 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10124 !IS_FREE(hitx, hity) &&
10125 (!IS_MOVING(hitx, hity) ||
10126 MovDir[hitx][hity] != direction ||
10127 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10130 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10134 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10138 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10139 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10141 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10142 EP_CAN_SMASH_EVERYTHING, direction);
10144 if (IN_LEV_FIELD(hitx, hity))
10146 int opposite_direction = MV_DIR_OPPOSITE(direction);
10147 int hitting_side = direction;
10148 int touched_side = opposite_direction;
10150 int touched_element = MovingOrBlocked2Element(hitx, hity);
10153 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10154 MovDir[hitx][hity] != direction ||
10155 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10164 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10165 CE_SMASHED_BY_SOMETHING, opposite_direction);
10167 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10168 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10170 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10172 struct ElementChangeInfo *change =
10173 &element_info[hitting_element].change_page[i];
10175 if (change->can_change &&
10176 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10177 change->trigger_side & touched_side &&
10180 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10182 change->trigger_element == touched_element
10186 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10187 CE_OTHER_IS_SMASHING, i);
10193 if (IS_CUSTOM_ELEMENT(touched_element) &&
10194 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10196 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10198 struct ElementChangeInfo *change =
10199 &element_info[touched_element].change_page[i];
10201 if (change->can_change &&
10202 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10203 change->trigger_side & hitting_side &&
10205 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10207 change->trigger_element == hitting_element
10211 CheckElementChangeByPage(hitx, hity, touched_element,
10212 hitting_element, CE_OTHER_GETS_SMASHED,i);
10222 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10224 int i, kill_x = -1, kill_y = -1;
10225 int bad_element = -1;
10226 static int test_xy[4][2] =
10233 static int test_dir[4] =
10241 for (i = 0; i < NUM_DIRECTIONS; i++)
10243 int test_x, test_y, test_move_dir, test_element;
10245 test_x = good_x + test_xy[i][0];
10246 test_y = good_y + test_xy[i][1];
10248 if (!IN_LEV_FIELD(test_x, test_y))
10252 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10255 test_element = Feld[test_x][test_y];
10257 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10260 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10261 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10263 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10264 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10268 bad_element = test_element;
10274 if (kill_x != -1 || kill_y != -1)
10276 if (IS_PLAYER(good_x, good_y))
10278 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10281 if (player->shield_deadly_time_left > 0 &&
10282 !IS_INDESTRUCTIBLE(bad_element))
10283 Bang(kill_x, kill_y);
10284 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10287 if (player->shield_deadly_time_left > 0)
10288 Bang(kill_x, kill_y);
10289 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10294 Bang(good_x, good_y);
10298 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10300 int i, kill_x = -1, kill_y = -1;
10301 int bad_element = Feld[bad_x][bad_y];
10302 static int test_xy[4][2] =
10309 static int touch_dir[4] =
10311 MV_LEFT | MV_RIGHT,
10316 static int test_dir[4] =
10324 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10327 for (i = 0; i < NUM_DIRECTIONS; i++)
10329 int test_x, test_y, test_move_dir, test_element;
10331 test_x = bad_x + test_xy[i][0];
10332 test_y = bad_y + test_xy[i][1];
10333 if (!IN_LEV_FIELD(test_x, test_y))
10337 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10339 test_element = Feld[test_x][test_y];
10341 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10342 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10344 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10345 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10347 /* good thing is player or penguin that does not move away */
10348 if (IS_PLAYER(test_x, test_y))
10350 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10352 if (bad_element == EL_ROBOT && player->is_moving)
10353 continue; /* robot does not kill player if he is moving */
10355 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10357 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10358 continue; /* center and border element do not touch */
10365 else if (test_element == EL_PENGUIN)
10374 if (kill_x != -1 || kill_y != -1)
10376 if (IS_PLAYER(kill_x, kill_y))
10378 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10381 if (player->shield_deadly_time_left > 0 &&
10382 !IS_INDESTRUCTIBLE(bad_element))
10383 Bang(bad_x, bad_y);
10384 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10387 if (player->shield_deadly_time_left > 0)
10388 Bang(bad_x, bad_y);
10389 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10394 Bang(kill_x, kill_y);
10398 void TestIfHeroTouchesBadThing(int x, int y)
10400 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10403 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10405 TestIfGoodThingHitsBadThing(x, y, move_dir);
10408 void TestIfBadThingTouchesHero(int x, int y)
10410 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10413 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10415 TestIfBadThingHitsGoodThing(x, y, move_dir);
10418 void TestIfFriendTouchesBadThing(int x, int y)
10420 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10423 void TestIfBadThingTouchesFriend(int x, int y)
10425 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10428 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10430 int i, kill_x = bad_x, kill_y = bad_y;
10431 static int xy[4][2] =
10439 for (i = 0; i < NUM_DIRECTIONS; i++)
10443 x = bad_x + xy[i][0];
10444 y = bad_y + xy[i][1];
10445 if (!IN_LEV_FIELD(x, y))
10448 element = Feld[x][y];
10449 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10450 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10458 if (kill_x != bad_x || kill_y != bad_y)
10459 Bang(bad_x, bad_y);
10462 void KillHero(struct PlayerInfo *player)
10464 int jx = player->jx, jy = player->jy;
10466 if (!player->active)
10469 /* remove accessible field at the player's position */
10470 Feld[jx][jy] = EL_EMPTY;
10472 /* deactivate shield (else Bang()/Explode() would not work right) */
10473 player->shield_normal_time_left = 0;
10474 player->shield_deadly_time_left = 0;
10480 static void KillHeroUnlessEnemyProtected(int x, int y)
10482 if (!PLAYER_ENEMY_PROTECTED(x, y))
10483 KillHero(PLAYERINFO(x, y));
10486 static void KillHeroUnlessExplosionProtected(int x, int y)
10488 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10489 KillHero(PLAYERINFO(x, y));
10492 void BuryHero(struct PlayerInfo *player)
10494 int jx = player->jx, jy = player->jy;
10496 if (!player->active)
10500 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10502 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10504 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10506 player->GameOver = TRUE;
10507 RemoveHero(player);
10510 void RemoveHero(struct PlayerInfo *player)
10512 int jx = player->jx, jy = player->jy;
10513 int i, found = FALSE;
10515 player->present = FALSE;
10516 player->active = FALSE;
10518 if (!ExplodeField[jx][jy])
10519 StorePlayer[jx][jy] = 0;
10521 for (i = 0; i < MAX_PLAYERS; i++)
10522 if (stored_player[i].active)
10526 AllPlayersGone = TRUE;
10533 =============================================================================
10534 checkDiagonalPushing()
10535 -----------------------------------------------------------------------------
10536 check if diagonal input device direction results in pushing of object
10537 (by checking if the alternative direction is walkable, diggable, ...)
10538 =============================================================================
10541 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10542 int x, int y, int real_dx, int real_dy)
10544 int jx, jy, dx, dy, xx, yy;
10546 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10549 /* diagonal direction: check alternative direction */
10554 xx = jx + (dx == 0 ? real_dx : 0);
10555 yy = jy + (dy == 0 ? real_dy : 0);
10557 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10561 =============================================================================
10563 -----------------------------------------------------------------------------
10564 x, y: field next to player (non-diagonal) to try to dig to
10565 real_dx, real_dy: direction as read from input device (can be diagonal)
10566 =============================================================================
10569 int DigField(struct PlayerInfo *player,
10570 int oldx, int oldy, int x, int y,
10571 int real_dx, int real_dy, int mode)
10573 static int trigger_sides[4] =
10575 CH_SIDE_RIGHT, /* moving left */
10576 CH_SIDE_LEFT, /* moving right */
10577 CH_SIDE_BOTTOM, /* moving up */
10578 CH_SIDE_TOP, /* moving down */
10581 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10583 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10584 boolean player_was_pushing = player->is_pushing;
10585 int jx = oldx, jy = oldy;
10586 int dx = x - jx, dy = y - jy;
10587 int nextx = x + dx, nexty = y + dy;
10588 int move_direction = (dx == -1 ? MV_LEFT :
10589 dx == +1 ? MV_RIGHT :
10591 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10592 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10593 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10594 int old_element = Feld[jx][jy];
10597 if (is_player) /* function can also be called by EL_PENGUIN */
10599 if (player->MovPos == 0)
10601 player->is_digging = FALSE;
10602 player->is_collecting = FALSE;
10605 if (player->MovPos == 0) /* last pushing move finished */
10606 player->is_pushing = FALSE;
10608 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10610 player->is_switching = FALSE;
10611 player->push_delay = 0;
10613 return MF_NO_ACTION;
10617 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10618 return MF_NO_ACTION;
10623 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10625 if (IS_TUBE(Feld[jx][jy]) ||
10626 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10630 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10631 int tube_leave_directions[][2] =
10633 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10634 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10635 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10636 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
10637 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
10638 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
10639 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
10640 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
10641 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
10642 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
10643 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
10644 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10647 while (tube_leave_directions[i][0] != tube_element)
10650 if (tube_leave_directions[i][0] == -1) /* should not happen */
10654 if (!(tube_leave_directions[i][1] & move_direction))
10655 return MF_NO_ACTION; /* tube has no opening in this direction */
10660 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10661 old_element = Back[jx][jy];
10665 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10666 return MF_NO_ACTION; /* field has no opening in this direction */
10668 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10669 return MF_NO_ACTION; /* field has no opening in this direction */
10671 element = Feld[x][y];
10673 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
10674 return MF_NO_ACTION;
10676 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10677 game.engine_version >= VERSION_IDENT(2,2,0,0))
10678 return MF_NO_ACTION;
10681 if (game.gravity && is_player && !player->is_auto_moving &&
10682 canFallDown(player) && move_direction != MV_DOWN &&
10683 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10684 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10688 if (element == EL_EMPTY_SPACE &&
10689 game.gravity && !player->is_auto_moving &&
10690 canFallDown(player) && move_direction != MV_DOWN)
10691 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10697 case EL_SP_PORT_LEFT:
10698 case EL_SP_PORT_RIGHT:
10699 case EL_SP_PORT_UP:
10700 case EL_SP_PORT_DOWN:
10701 case EL_SP_PORT_HORIZONTAL:
10702 case EL_SP_PORT_VERTICAL:
10703 case EL_SP_PORT_ANY:
10704 case EL_SP_GRAVITY_PORT_LEFT:
10705 case EL_SP_GRAVITY_PORT_RIGHT:
10706 case EL_SP_GRAVITY_PORT_UP:
10707 case EL_SP_GRAVITY_PORT_DOWN:
10709 if (!canEnterSupaplexPort(x, y, dx, dy))
10710 return MF_NO_ACTION;
10713 element != EL_SP_PORT_LEFT &&
10714 element != EL_SP_GRAVITY_PORT_LEFT &&
10715 element != EL_SP_PORT_HORIZONTAL &&
10716 element != EL_SP_PORT_ANY) ||
10718 element != EL_SP_PORT_RIGHT &&
10719 element != EL_SP_GRAVITY_PORT_RIGHT &&
10720 element != EL_SP_PORT_HORIZONTAL &&
10721 element != EL_SP_PORT_ANY) ||
10723 element != EL_SP_PORT_UP &&
10724 element != EL_SP_GRAVITY_PORT_UP &&
10725 element != EL_SP_PORT_VERTICAL &&
10726 element != EL_SP_PORT_ANY) ||
10728 element != EL_SP_PORT_DOWN &&
10729 element != EL_SP_GRAVITY_PORT_DOWN &&
10730 element != EL_SP_PORT_VERTICAL &&
10731 element != EL_SP_PORT_ANY) ||
10732 !IN_LEV_FIELD(nextx, nexty) ||
10733 !IS_FREE(nextx, nexty))
10734 return MF_NO_ACTION;
10737 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10738 element == EL_SP_GRAVITY_PORT_RIGHT ||
10739 element == EL_SP_GRAVITY_PORT_UP ||
10740 element == EL_SP_GRAVITY_PORT_DOWN)
10741 game.gravity = !game.gravity;
10743 /* automatically move to the next field with double speed */
10744 player->programmed_action = move_direction;
10746 if (player->move_delay_reset_counter == 0)
10748 player->move_delay_reset_counter = 2; /* two double speed steps */
10750 DOUBLE_PLAYER_SPEED(player);
10753 player->move_delay_reset_counter = 2;
10755 DOUBLE_PLAYER_SPEED(player);
10759 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10762 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10768 case EL_TUBE_VERTICAL:
10769 case EL_TUBE_HORIZONTAL:
10770 case EL_TUBE_VERTICAL_LEFT:
10771 case EL_TUBE_VERTICAL_RIGHT:
10772 case EL_TUBE_HORIZONTAL_UP:
10773 case EL_TUBE_HORIZONTAL_DOWN:
10774 case EL_TUBE_LEFT_UP:
10775 case EL_TUBE_LEFT_DOWN:
10776 case EL_TUBE_RIGHT_UP:
10777 case EL_TUBE_RIGHT_DOWN:
10780 int tube_enter_directions[][2] =
10782 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10783 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10784 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10785 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
10786 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
10787 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
10788 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
10789 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
10790 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
10791 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
10792 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
10793 { -1, MV_NO_MOVING }
10796 while (tube_enter_directions[i][0] != element)
10799 if (tube_enter_directions[i][0] == -1) /* should not happen */
10803 if (!(tube_enter_directions[i][1] & move_direction))
10804 return MF_NO_ACTION; /* tube has no opening in this direction */
10806 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10814 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10816 if (IS_WALKABLE(element))
10819 int sound_element = SND_ELEMENT(element);
10820 int sound_action = ACTION_WALKING;
10823 if (!ACCESS_FROM(element, opposite_direction))
10824 return MF_NO_ACTION; /* field not accessible from this direction */
10828 if (element == EL_EMPTY_SPACE &&
10829 game.gravity && !player->is_auto_moving &&
10830 canFallDown(player) && move_direction != MV_DOWN)
10831 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10834 if (IS_GATE(element))
10836 if (!player->key[element - EL_GATE_1])
10837 return MF_NO_ACTION;
10839 else if (IS_GATE_GRAY(element))
10841 if (!player->key[element - EL_GATE_1_GRAY])
10842 return MF_NO_ACTION;
10844 else if (element == EL_EXIT_OPEN ||
10845 element == EL_SP_EXIT_OPEN ||
10846 element == EL_SP_EXIT_OPENING)
10848 sound_action = ACTION_PASSING; /* player is passing exit */
10850 else if (element == EL_EMPTY)
10852 sound_action = ACTION_MOVING; /* nothing to walk on */
10855 /* play sound from background or player, whatever is available */
10856 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
10857 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
10859 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10864 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
10866 else if (IS_PASSABLE(element))
10870 if (!canPassField(x, y, move_direction))
10871 return MF_NO_ACTION;
10876 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
10877 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
10878 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
10879 return MF_NO_ACTION;
10881 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10882 return MF_NO_ACTION;
10887 if (!ACCESS_FROM(element, opposite_direction))
10888 return MF_NO_ACTION; /* field not accessible from this direction */
10890 if (IS_CUSTOM_ELEMENT(element) &&
10891 !ACCESS_FROM(element, opposite_direction))
10892 return MF_NO_ACTION; /* field not accessible from this direction */
10896 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10897 return MF_NO_ACTION;
10902 if (IS_EM_GATE(element))
10904 if (!player->key[element - EL_EM_GATE_1])
10905 return MF_NO_ACTION;
10907 else if (IS_EM_GATE_GRAY(element))
10909 if (!player->key[element - EL_EM_GATE_1_GRAY])
10910 return MF_NO_ACTION;
10912 else if (IS_SP_PORT(element))
10914 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10915 element == EL_SP_GRAVITY_PORT_RIGHT ||
10916 element == EL_SP_GRAVITY_PORT_UP ||
10917 element == EL_SP_GRAVITY_PORT_DOWN)
10918 game.gravity = !game.gravity;
10921 /* automatically move to the next field with double speed */
10922 player->programmed_action = move_direction;
10924 if (player->move_delay_reset_counter == 0)
10926 player->move_delay_reset_counter = 2; /* two double speed steps */
10928 DOUBLE_PLAYER_SPEED(player);
10931 player->move_delay_reset_counter = 2;
10933 DOUBLE_PLAYER_SPEED(player);
10936 PlayLevelSoundAction(x, y, ACTION_PASSING);
10940 else if (IS_DIGGABLE(element))
10944 if (mode != DF_SNAP)
10947 GfxElement[x][y] = GFX_ELEMENT(element);
10950 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
10952 player->is_digging = TRUE;
10955 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10957 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
10958 player->index_bit, dig_side);
10961 if (mode == DF_SNAP)
10962 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10967 else if (IS_COLLECTIBLE(element))
10971 if (is_player && mode != DF_SNAP)
10973 GfxElement[x][y] = element;
10974 player->is_collecting = TRUE;
10977 if (element == EL_SPEED_PILL)
10978 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10979 else if (element == EL_EXTRA_TIME && level.time > 0)
10982 DrawGameValue_Time(TimeLeft);
10984 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10986 player->shield_normal_time_left += 10;
10987 if (element == EL_SHIELD_DEADLY)
10988 player->shield_deadly_time_left += 10;
10990 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10992 if (player->inventory_size < MAX_INVENTORY_SIZE)
10993 player->inventory_element[player->inventory_size++] = element;
10995 DrawGameValue_Dynamite(local_player->inventory_size);
10997 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10999 player->dynabomb_count++;
11000 player->dynabombs_left++;
11002 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11004 player->dynabomb_size++;
11006 else if (element == EL_DYNABOMB_INCREASE_POWER)
11008 player->dynabomb_xl = TRUE;
11010 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
11011 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
11013 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
11014 element - EL_KEY_1 : element - EL_EM_KEY_1);
11016 player->key[key_nr] = TRUE;
11018 DrawGameValue_Keys(player);
11020 redraw_mask |= REDRAW_DOOR_1;
11022 else if (IS_ENVELOPE(element))
11025 player->show_envelope = element;
11027 ShowEnvelope(element - EL_ENVELOPE_1);
11030 else if (IS_DROPPABLE(element) ||
11031 IS_THROWABLE(element)) /* can be collected and dropped */
11035 if (element_info[element].collect_count == 0)
11036 player->inventory_infinite_element = element;
11038 for (i = 0; i < element_info[element].collect_count; i++)
11039 if (player->inventory_size < MAX_INVENTORY_SIZE)
11040 player->inventory_element[player->inventory_size++] = element;
11042 DrawGameValue_Dynamite(local_player->inventory_size);
11044 else if (element_info[element].collect_count > 0)
11046 local_player->gems_still_needed -=
11047 element_info[element].collect_count;
11048 if (local_player->gems_still_needed < 0)
11049 local_player->gems_still_needed = 0;
11051 DrawGameValue_Emeralds(local_player->gems_still_needed);
11054 RaiseScoreElement(element);
11055 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11058 CheckTriggeredElementChangeByPlayer(x, y, element,
11059 CE_OTHER_GETS_COLLECTED,
11060 player->index_bit, dig_side);
11063 if (mode == DF_SNAP)
11064 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11069 else if (IS_PUSHABLE(element))
11071 if (mode == DF_SNAP && element != EL_BD_ROCK)
11072 return MF_NO_ACTION;
11074 if (CAN_FALL(element) && dy)
11075 return MF_NO_ACTION;
11077 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11078 !(element == EL_SPRING && level.use_spring_bug))
11079 return MF_NO_ACTION;
11082 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11083 ((move_direction & MV_VERTICAL &&
11084 ((element_info[element].move_pattern & MV_LEFT &&
11085 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11086 (element_info[element].move_pattern & MV_RIGHT &&
11087 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11088 (move_direction & MV_HORIZONTAL &&
11089 ((element_info[element].move_pattern & MV_UP &&
11090 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11091 (element_info[element].move_pattern & MV_DOWN &&
11092 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11093 return MF_NO_ACTION;
11097 /* do not push elements already moving away faster than player */
11098 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11099 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11100 return MF_NO_ACTION;
11102 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11103 return MF_NO_ACTION;
11109 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11111 if (player->push_delay_value == -1 || !player_was_pushing)
11112 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11114 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11116 if (player->push_delay_value == -1)
11117 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11120 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11122 if (player->push_delay_value == -1 || !player_was_pushing)
11123 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11126 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11128 if (!player->is_pushing)
11129 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11133 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11134 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11135 !player_is_pushing))
11136 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11139 if (!player->is_pushing &&
11140 game.engine_version >= VERSION_IDENT(2,2,0,7))
11141 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11145 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11146 player->push_delay, player->push_delay_value,
11147 FrameCounter, game.engine_version,
11148 player_was_pushing, player->is_pushing,
11149 element, element_info[element].token_name,
11150 GET_NEW_PUSH_DELAY(element));
11153 player->is_pushing = TRUE;
11155 if (!(IN_LEV_FIELD(nextx, nexty) &&
11156 (IS_FREE(nextx, nexty) ||
11157 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11158 IS_SB_ELEMENT(element)))))
11159 return MF_NO_ACTION;
11161 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11162 return MF_NO_ACTION;
11164 if (player->push_delay == 0) /* new pushing; restart delay */
11165 player->push_delay = FrameCounter;
11167 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11168 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11169 element != EL_SPRING && element != EL_BALLOON)
11171 /* make sure that there is no move delay before next try to push */
11172 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11173 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11175 return MF_NO_ACTION;
11179 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11182 if (IS_SB_ELEMENT(element))
11184 if (element == EL_SOKOBAN_FIELD_FULL)
11186 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11187 local_player->sokobanfields_still_needed++;
11190 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11192 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11193 local_player->sokobanfields_still_needed--;
11196 Feld[x][y] = EL_SOKOBAN_OBJECT;
11198 if (Back[x][y] == Back[nextx][nexty])
11199 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11200 else if (Back[x][y] != 0)
11201 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11204 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11207 if (local_player->sokobanfields_still_needed == 0 &&
11208 game.emulation == EMU_SOKOBAN)
11210 player->LevelSolved = player->GameOver = TRUE;
11211 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11215 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11217 InitMovingField(x, y, move_direction);
11218 GfxAction[x][y] = ACTION_PUSHING;
11220 if (mode == DF_SNAP)
11221 ContinueMoving(x, y);
11223 MovPos[x][y] = (dx != 0 ? dx : dy);
11225 Pushed[x][y] = TRUE;
11226 Pushed[nextx][nexty] = TRUE;
11228 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11229 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11231 player->push_delay_value = -1; /* get new value later */
11234 /* check for element change _after_ element has been pushed! */
11238 /* !!! TEST ONLY !!! */
11239 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11240 player->index_bit, dig_side);
11241 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11242 player->index_bit, dig_side);
11244 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11245 player->index_bit, dig_side);
11246 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11247 player->index_bit, dig_side);
11253 else if (IS_SWITCHABLE(element))
11255 if (PLAYER_SWITCHING(player, x, y))
11258 player->is_switching = TRUE;
11259 player->switch_x = x;
11260 player->switch_y = y;
11262 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11264 if (element == EL_ROBOT_WHEEL)
11266 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11270 DrawLevelField(x, y);
11272 else if (element == EL_SP_TERMINAL)
11276 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11278 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11280 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11281 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11284 else if (IS_BELT_SWITCH(element))
11286 ToggleBeltSwitch(x, y);
11288 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11289 element == EL_SWITCHGATE_SWITCH_DOWN)
11291 ToggleSwitchgateSwitch(x, y);
11293 else if (element == EL_LIGHT_SWITCH ||
11294 element == EL_LIGHT_SWITCH_ACTIVE)
11296 ToggleLightSwitch(x, y);
11299 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11300 SND_LIGHT_SWITCH_ACTIVATING :
11301 SND_LIGHT_SWITCH_DEACTIVATING);
11304 else if (element == EL_TIMEGATE_SWITCH)
11306 ActivateTimegateSwitch(x, y);
11308 else if (element == EL_BALLOON_SWITCH_LEFT ||
11309 element == EL_BALLOON_SWITCH_RIGHT ||
11310 element == EL_BALLOON_SWITCH_UP ||
11311 element == EL_BALLOON_SWITCH_DOWN ||
11312 element == EL_BALLOON_SWITCH_ANY)
11314 if (element == EL_BALLOON_SWITCH_ANY)
11315 game.balloon_dir = move_direction;
11317 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11318 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11319 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11320 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11323 else if (element == EL_LAMP)
11325 Feld[x][y] = EL_LAMP_ACTIVE;
11326 local_player->lights_still_needed--;
11328 DrawLevelField(x, y);
11330 else if (element == EL_TIME_ORB_FULL)
11332 Feld[x][y] = EL_TIME_ORB_EMPTY;
11334 DrawGameValue_Time(TimeLeft);
11336 DrawLevelField(x, y);
11339 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11347 if (!PLAYER_SWITCHING(player, x, y))
11349 player->is_switching = TRUE;
11350 player->switch_x = x;
11351 player->switch_y = y;
11354 /* !!! TEST ONLY !!! */
11355 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11356 player->index_bit, dig_side);
11357 CheckTriggeredElementChangeByPlayer(x, y, element,
11358 CE_OTHER_IS_SWITCHING,
11359 player->index_bit, dig_side);
11361 CheckTriggeredElementChangeByPlayer(x, y, element,
11362 CE_OTHER_IS_SWITCHING,
11363 player->index_bit, dig_side);
11364 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11365 player->index_bit, dig_side);
11370 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11371 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11372 player->index_bit, dig_side);
11373 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11374 player->index_bit, dig_side);
11376 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11377 player->index_bit, dig_side);
11378 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11379 player->index_bit, dig_side);
11383 return MF_NO_ACTION;
11386 player->push_delay = 0;
11388 if (Feld[x][y] != element) /* really digged/collected something */
11389 player->is_collecting = !player->is_digging;
11394 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11396 int jx = player->jx, jy = player->jy;
11397 int x = jx + dx, y = jy + dy;
11398 int snap_direction = (dx == -1 ? MV_LEFT :
11399 dx == +1 ? MV_RIGHT :
11401 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11404 if (player->MovPos != 0)
11407 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11411 if (!player->active || !IN_LEV_FIELD(x, y))
11419 if (player->MovPos == 0)
11420 player->is_pushing = FALSE;
11422 player->is_snapping = FALSE;
11424 if (player->MovPos == 0)
11426 player->is_moving = FALSE;
11427 player->is_digging = FALSE;
11428 player->is_collecting = FALSE;
11434 if (player->is_snapping)
11437 player->MovDir = snap_direction;
11440 if (player->MovPos == 0)
11443 player->is_moving = FALSE;
11444 player->is_digging = FALSE;
11445 player->is_collecting = FALSE;
11448 player->is_dropping = FALSE;
11450 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11453 player->is_snapping = TRUE;
11456 if (player->MovPos == 0)
11459 player->is_moving = FALSE;
11460 player->is_digging = FALSE;
11461 player->is_collecting = FALSE;
11465 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11466 DrawLevelField(player->last_jx, player->last_jy);
11469 DrawLevelField(x, y);
11478 boolean DropElement(struct PlayerInfo *player)
11480 static int trigger_sides[4] =
11482 CH_SIDE_LEFT, /* dropping left */
11483 CH_SIDE_RIGHT, /* dropping right */
11484 CH_SIDE_TOP, /* dropping up */
11485 CH_SIDE_BOTTOM, /* dropping down */
11487 int old_element, new_element;
11488 int dropx = player->jx, dropy = player->jy;
11489 int drop_direction = player->MovDir;
11490 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11491 int drop_element = (player->inventory_size > 0 ?
11492 player->inventory_element[player->inventory_size - 1] :
11493 player->inventory_infinite_element != EL_UNDEFINED ?
11494 player->inventory_infinite_element :
11495 player->dynabombs_left > 0 ?
11496 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11499 if (IS_THROWABLE(drop_element))
11501 dropx += GET_DX_FROM_DIR(drop_direction);
11502 dropy += GET_DY_FROM_DIR(drop_direction);
11504 if (!IN_LEV_FIELD(dropx, dropy))
11508 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11509 new_element = drop_element; /* default: no change when dropping */
11511 /* check if player is active, not moving and ready to drop */
11512 if (!player->active || player->MovPos || player->drop_delay > 0)
11515 /* check if player has anything that can be dropped */
11517 if (new_element == EL_UNDEFINED)
11520 if (player->inventory_size == 0 &&
11521 player->inventory_infinite_element == EL_UNDEFINED &&
11522 player->dynabombs_left == 0)
11526 /* check if anything can be dropped at the current position */
11527 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11530 /* collected custom elements can only be dropped on empty fields */
11532 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11535 if (player->inventory_size > 0 &&
11536 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11537 && old_element != EL_EMPTY)
11541 if (old_element != EL_EMPTY)
11542 Back[dropx][dropy] = old_element; /* store old element on this field */
11544 ResetGfxAnimation(dropx, dropy);
11545 ResetRandomAnimationValue(dropx, dropy);
11547 if (player->inventory_size > 0 ||
11548 player->inventory_infinite_element != EL_UNDEFINED)
11550 if (player->inventory_size > 0)
11552 player->inventory_size--;
11555 new_element = player->inventory_element[player->inventory_size];
11558 DrawGameValue_Dynamite(local_player->inventory_size);
11560 if (new_element == EL_DYNAMITE)
11561 new_element = EL_DYNAMITE_ACTIVE;
11562 else if (new_element == EL_SP_DISK_RED)
11563 new_element = EL_SP_DISK_RED_ACTIVE;
11566 Feld[dropx][dropy] = new_element;
11568 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11569 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11570 el2img(Feld[dropx][dropy]), 0);
11572 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11575 /* needed if previous element just changed to "empty" in the last frame */
11576 Changed[dropx][dropy] = 0; /* allow another change */
11580 /* !!! TEST ONLY !!! */
11581 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11582 player->index_bit, drop_side);
11583 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11584 CE_OTHER_GETS_DROPPED,
11585 player->index_bit, drop_side);
11587 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11588 CE_OTHER_GETS_DROPPED,
11589 player->index_bit, drop_side);
11590 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11591 player->index_bit, drop_side);
11594 TestIfElementTouchesCustomElement(dropx, dropy);
11596 else /* player is dropping a dyna bomb */
11598 player->dynabombs_left--;
11601 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
11604 Feld[dropx][dropy] = new_element;
11606 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11607 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11608 el2img(Feld[dropx][dropy]), 0);
11610 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11617 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11620 InitField_WithBug1(dropx, dropy, FALSE);
11622 InitField(dropx, dropy, FALSE);
11623 if (CAN_MOVE(Feld[dropx][dropy]))
11624 InitMovDir(dropx, dropy);
11628 new_element = Feld[dropx][dropy]; /* element might have changed */
11630 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11631 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11634 int move_stepsize = element_info[new_element].move_stepsize;
11636 int move_direction, nextx, nexty;
11638 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11639 MovDir[dropx][dropy] = drop_direction;
11641 move_direction = MovDir[dropx][dropy];
11642 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11643 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11646 Changed[dropx][dropy] = 0; /* allow another change */
11647 CheckCollision[dropx][dropy] = 2;
11650 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
11653 WasJustMoving[dropx][dropy] = 3;
11656 InitMovingField(dropx, dropy, move_direction);
11657 ContinueMoving(dropx, dropy);
11664 Changed[dropx][dropy] = 0; /* allow another change */
11667 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
11669 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
11670 CE_HITTING_SOMETHING, move_direction);
11678 player->drop_delay = 2 * TILEX / move_stepsize + 1;
11683 player->drop_delay = 8 + 8 + 8;
11687 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11692 player->is_dropping = TRUE;
11698 /* ------------------------------------------------------------------------- */
11699 /* game sound playing functions */
11700 /* ------------------------------------------------------------------------- */
11702 static int *loop_sound_frame = NULL;
11703 static int *loop_sound_volume = NULL;
11705 void InitPlayLevelSound()
11707 int num_sounds = getSoundListSize();
11709 checked_free(loop_sound_frame);
11710 checked_free(loop_sound_volume);
11712 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11713 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11716 static void PlayLevelSound(int x, int y, int nr)
11718 int sx = SCREENX(x), sy = SCREENY(y);
11719 int volume, stereo_position;
11720 int max_distance = 8;
11721 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11723 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11724 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11727 if (!IN_LEV_FIELD(x, y) ||
11728 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11729 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11732 volume = SOUND_MAX_VOLUME;
11734 if (!IN_SCR_FIELD(sx, sy))
11736 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11737 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11739 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11742 stereo_position = (SOUND_MAX_LEFT +
11743 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11744 (SCR_FIELDX + 2 * max_distance));
11746 if (IS_LOOP_SOUND(nr))
11748 /* This assures that quieter loop sounds do not overwrite louder ones,
11749 while restarting sound volume comparison with each new game frame. */
11751 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11754 loop_sound_volume[nr] = volume;
11755 loop_sound_frame[nr] = FrameCounter;
11758 PlaySoundExt(nr, volume, stereo_position, type);
11761 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11763 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11764 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11765 y < LEVELY(BY1) ? LEVELY(BY1) :
11766 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11770 static void PlayLevelSoundAction(int x, int y, int action)
11772 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11775 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11777 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11779 if (sound_effect != SND_UNDEFINED)
11780 PlayLevelSound(x, y, sound_effect);
11783 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11786 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11788 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11789 PlayLevelSound(x, y, sound_effect);
11792 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11794 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11796 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11797 PlayLevelSound(x, y, sound_effect);
11800 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11802 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11804 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11805 StopSound(sound_effect);
11808 static void PlayLevelMusic()
11810 if (levelset.music[level_nr] != MUS_UNDEFINED)
11811 PlayMusic(levelset.music[level_nr]); /* from config file */
11813 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11816 void RaiseScore(int value)
11818 local_player->score += value;
11820 DrawGameValue_Score(local_player->score);
11823 void RaiseScoreElement(int element)
11828 case EL_BD_DIAMOND:
11829 case EL_EMERALD_YELLOW:
11830 case EL_EMERALD_RED:
11831 case EL_EMERALD_PURPLE:
11832 case EL_SP_INFOTRON:
11833 RaiseScore(level.score[SC_EMERALD]);
11836 RaiseScore(level.score[SC_DIAMOND]);
11839 RaiseScore(level.score[SC_CRYSTAL]);
11842 RaiseScore(level.score[SC_PEARL]);
11845 case EL_BD_BUTTERFLY:
11846 case EL_SP_ELECTRON:
11847 RaiseScore(level.score[SC_BUG]);
11850 case EL_BD_FIREFLY:
11851 case EL_SP_SNIKSNAK:
11852 RaiseScore(level.score[SC_SPACESHIP]);
11855 case EL_DARK_YAMYAM:
11856 RaiseScore(level.score[SC_YAMYAM]);
11859 RaiseScore(level.score[SC_ROBOT]);
11862 RaiseScore(level.score[SC_PACMAN]);
11865 RaiseScore(level.score[SC_NUT]);
11868 case EL_SP_DISK_RED:
11869 case EL_DYNABOMB_INCREASE_NUMBER:
11870 case EL_DYNABOMB_INCREASE_SIZE:
11871 case EL_DYNABOMB_INCREASE_POWER:
11872 RaiseScore(level.score[SC_DYNAMITE]);
11874 case EL_SHIELD_NORMAL:
11875 case EL_SHIELD_DEADLY:
11876 RaiseScore(level.score[SC_SHIELD]);
11878 case EL_EXTRA_TIME:
11879 RaiseScore(level.score[SC_TIME_BONUS]);
11885 RaiseScore(level.score[SC_KEY]);
11888 RaiseScore(element_info[element].collect_score);
11893 void RequestQuitGame(boolean ask_if_really_quit)
11895 if (AllPlayersGone ||
11896 !ask_if_really_quit ||
11897 level_editor_test_game ||
11898 Request("Do you really want to quit the game ?",
11899 REQ_ASK | REQ_STAY_CLOSED))
11901 #if defined(NETWORK_AVALIABLE)
11902 if (options.network)
11903 SendToServer_StopPlaying();
11907 game_status = GAME_MODE_MAIN;
11915 if (tape.playing && tape.deactivate_display)
11916 TapeDeactivateDisplayOff(TRUE);
11919 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11922 if (tape.playing && tape.deactivate_display)
11923 TapeDeactivateDisplayOn();
11930 /* ---------- new game button stuff ---------------------------------------- */
11932 /* graphic position values for game buttons */
11933 #define GAME_BUTTON_XSIZE 30
11934 #define GAME_BUTTON_YSIZE 30
11935 #define GAME_BUTTON_XPOS 5
11936 #define GAME_BUTTON_YPOS 215
11937 #define SOUND_BUTTON_XPOS 5
11938 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11940 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11941 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11942 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11943 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11944 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11945 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11952 } gamebutton_info[NUM_GAME_BUTTONS] =
11955 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11960 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11961 GAME_CTRL_ID_PAUSE,
11965 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11970 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11971 SOUND_CTRL_ID_MUSIC,
11972 "background music on/off"
11975 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11976 SOUND_CTRL_ID_LOOPS,
11977 "sound loops on/off"
11980 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11981 SOUND_CTRL_ID_SIMPLE,
11982 "normal sounds on/off"
11986 void CreateGameButtons()
11990 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11992 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11993 struct GadgetInfo *gi;
11996 unsigned long event_mask;
11997 int gd_xoffset, gd_yoffset;
11998 int gd_x1, gd_x2, gd_y1, gd_y2;
12001 gd_xoffset = gamebutton_info[i].x;
12002 gd_yoffset = gamebutton_info[i].y;
12003 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12004 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12006 if (id == GAME_CTRL_ID_STOP ||
12007 id == GAME_CTRL_ID_PAUSE ||
12008 id == GAME_CTRL_ID_PLAY)
12010 button_type = GD_TYPE_NORMAL_BUTTON;
12012 event_mask = GD_EVENT_RELEASED;
12013 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12014 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12018 button_type = GD_TYPE_CHECK_BUTTON;
12020 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12021 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12022 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12023 event_mask = GD_EVENT_PRESSED;
12024 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12025 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12028 gi = CreateGadget(GDI_CUSTOM_ID, id,
12029 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12030 GDI_X, DX + gd_xoffset,
12031 GDI_Y, DY + gd_yoffset,
12032 GDI_WIDTH, GAME_BUTTON_XSIZE,
12033 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12034 GDI_TYPE, button_type,
12035 GDI_STATE, GD_BUTTON_UNPRESSED,
12036 GDI_CHECKED, checked,
12037 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12038 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12039 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12040 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12041 GDI_EVENT_MASK, event_mask,
12042 GDI_CALLBACK_ACTION, HandleGameButtons,
12046 Error(ERR_EXIT, "cannot create gadget");
12048 game_gadget[id] = gi;
12052 void FreeGameButtons()
12056 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12057 FreeGadget(game_gadget[i]);
12060 static void MapGameButtons()
12064 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12065 MapGadget(game_gadget[i]);
12068 void UnmapGameButtons()
12072 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12073 UnmapGadget(game_gadget[i]);
12076 static void HandleGameButtons(struct GadgetInfo *gi)
12078 int id = gi->custom_id;
12080 if (game_status != GAME_MODE_PLAYING)
12085 case GAME_CTRL_ID_STOP:
12086 RequestQuitGame(TRUE);
12089 case GAME_CTRL_ID_PAUSE:
12090 if (options.network)
12092 #if defined(NETWORK_AVALIABLE)
12094 SendToServer_ContinuePlaying();
12096 SendToServer_PausePlaying();
12100 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12103 case GAME_CTRL_ID_PLAY:
12106 #if defined(NETWORK_AVALIABLE)
12107 if (options.network)
12108 SendToServer_ContinuePlaying();
12112 tape.pausing = FALSE;
12113 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12118 case SOUND_CTRL_ID_MUSIC:
12119 if (setup.sound_music)
12121 setup.sound_music = FALSE;
12124 else if (audio.music_available)
12126 setup.sound = setup.sound_music = TRUE;
12128 SetAudioMode(setup.sound);
12134 case SOUND_CTRL_ID_LOOPS:
12135 if (setup.sound_loops)
12136 setup.sound_loops = FALSE;
12137 else if (audio.loops_available)
12139 setup.sound = setup.sound_loops = TRUE;
12140 SetAudioMode(setup.sound);
12144 case SOUND_CTRL_ID_SIMPLE:
12145 if (setup.sound_simple)
12146 setup.sound_simple = FALSE;
12147 else if (audio.sound_available)
12149 setup.sound = setup.sound_simple = TRUE;
12150 SetAudioMode(setup.sound);