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 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2834 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2836 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2837 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2838 int leave_side_horiz = move_dir_horiz;
2839 int leave_side_vert = move_dir_vert;
2841 static int trigger_sides[4][2] =
2843 /* enter side leave side */
2844 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
2845 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
2846 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
2847 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
2849 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
2850 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
2851 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
2852 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
2854 int enter_side = enter_side_horiz | enter_side_vert;
2855 int leave_side = leave_side_horiz | leave_side_vert;
2857 if (player->GameOver) /* do not reanimate dead player */
2860 if (!player_relocated) /* no need to relocate the player */
2863 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2865 RemoveField(jx, jy); /* temporarily remove newly placed player */
2866 DrawLevelField(jx, jy);
2869 if (player->present)
2871 while (player->MovPos)
2873 ScrollPlayer(player, SCROLL_GO_ON);
2874 ScrollScreen(NULL, SCROLL_GO_ON);
2880 Delay(wait_delay_value);
2883 DrawPlayer(player); /* needed here only to cleanup last field */
2884 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2886 player->is_moving = FALSE;
2890 if (IS_CUSTOM_ELEMENT(old_element))
2891 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
2893 player->index_bit, leave_side);
2895 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
2897 player->index_bit, leave_side);
2900 Feld[jx][jy] = el_player;
2901 InitPlayerField(jx, jy, el_player, TRUE);
2903 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
2905 Feld[jx][jy] = element;
2906 InitField(jx, jy, FALSE);
2910 if (player == local_player) /* only visually relocate local player */
2911 DrawRelocatePlayer(player);
2915 TestIfHeroTouchesBadThing(jx, jy);
2916 TestIfPlayerTouchesCustomElement(jx, jy);
2920 /* needed to allow change of walkable custom element by entering player */
2921 Changed[jx][jy] = 0; /* allow another change */
2925 printf("::: player entering %d, %d from %s ...\n", jx, jy,
2926 enter_side == MV_LEFT ? "left" :
2927 enter_side == MV_RIGHT ? "right" :
2928 enter_side == MV_UP ? "top" :
2929 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
2933 if (IS_CUSTOM_ELEMENT(element))
2934 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
2935 player->index_bit, enter_side);
2937 CheckTriggeredElementChangeByPlayer(jx, jy, element,
2938 CE_OTHER_GETS_ENTERED,
2939 player->index_bit, enter_side);
2943 void Explode(int ex, int ey, int phase, int mode)
2950 /* !!! eliminate this variable !!! */
2951 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2956 int last_phase = num_phase * delay;
2957 int half_phase = (num_phase / 2) * delay;
2958 int first_phase_after_start = EX_PHASE_START + 1;
2962 if (game.explosions_delayed)
2964 ExplodeField[ex][ey] = mode;
2968 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2970 int center_element = Feld[ex][ey];
2973 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
2977 /* --- This is only really needed (and now handled) in "Impact()". --- */
2978 /* do not explode moving elements that left the explode field in time */
2979 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2980 center_element == EL_EMPTY &&
2981 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
2985 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
2986 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2988 /* remove things displayed in background while burning dynamite */
2989 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2992 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2994 /* put moving element to center field (and let it explode there) */
2995 center_element = MovingOrBlocked2Element(ex, ey);
2996 RemoveMovingField(ex, ey);
2997 Feld[ex][ey] = center_element;
3003 last_phase = element_info[center_element].explosion_delay + 1;
3005 last_phase = element_info[center_element].explosion_delay;
3009 printf("::: %d -> %d\n", center_element, last_phase);
3013 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3015 int xx = x - ex + 1;
3016 int yy = y - ey + 1;
3021 if (!IN_LEV_FIELD(x, y) ||
3022 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3023 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3026 if (!IN_LEV_FIELD(x, y) ||
3027 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3031 if (!IN_LEV_FIELD(x, y) ||
3032 ((mode != EX_TYPE_NORMAL ||
3033 center_element == EL_AMOEBA_TO_DIAMOND) &&
3034 (x != ex || y != ey)))
3038 element = Feld[x][y];
3040 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3042 element = MovingOrBlocked2Element(x, y);
3044 if (!IS_EXPLOSION_PROOF(element))
3045 RemoveMovingField(x, y);
3051 if (IS_EXPLOSION_PROOF(element))
3054 /* indestructible elements can only explode in center (but not flames) */
3055 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3056 element == EL_FLAMES)
3061 if ((IS_INDESTRUCTIBLE(element) &&
3062 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3063 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3064 element == EL_FLAMES)
3069 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3070 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3071 (x == ex && y == ey)))
3073 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3076 if (IS_ACTIVE_BOMB(element))
3078 /* re-activate things under the bomb like gate or penguin */
3080 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3083 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3091 /* save walkable background elements while explosion on same tile */
3093 if (IS_INDESTRUCTIBLE(element))
3094 Back[x][y] = element;
3097 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3098 (x != ex || y != ey))
3099 Back[x][y] = element;
3101 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3102 Back[x][y] = element;
3106 /* ignite explodable elements reached by other explosion */
3107 if (element == EL_EXPLOSION)
3108 element = Store2[x][y];
3111 if (AmoebaNr[x][y] &&
3112 (element == EL_AMOEBA_FULL ||
3113 element == EL_BD_AMOEBA ||
3114 element == EL_AMOEBA_GROWING))
3116 AmoebaCnt[AmoebaNr[x][y]]--;
3117 AmoebaCnt2[AmoebaNr[x][y]]--;
3123 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3125 switch(StorePlayer[ex][ey])
3128 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3131 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3134 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3138 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3143 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3144 Store[x][y] = EL_EMPTY;
3146 if (game.emulation == EMU_SUPAPLEX)
3147 Store[x][y] = EL_EMPTY;
3150 else if (center_element == EL_MOLE)
3151 Store[x][y] = EL_EMERALD_RED;
3152 else if (center_element == EL_PENGUIN)
3153 Store[x][y] = EL_EMERALD_PURPLE;
3154 else if (center_element == EL_BUG)
3155 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3156 else if (center_element == EL_BD_BUTTERFLY)
3157 Store[x][y] = EL_BD_DIAMOND;
3158 else if (center_element == EL_SP_ELECTRON)
3159 Store[x][y] = EL_SP_INFOTRON;
3160 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3161 Store[x][y] = level.amoeba_content;
3162 else if (center_element == EL_YAMYAM)
3163 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3164 else if (IS_CUSTOM_ELEMENT(center_element) &&
3165 element_info[center_element].content[xx][yy] != EL_EMPTY)
3166 Store[x][y] = element_info[center_element].content[xx][yy];
3167 else if (element == EL_WALL_EMERALD)
3168 Store[x][y] = EL_EMERALD;
3169 else if (element == EL_WALL_DIAMOND)
3170 Store[x][y] = EL_DIAMOND;
3171 else if (element == EL_WALL_BD_DIAMOND)
3172 Store[x][y] = EL_BD_DIAMOND;
3173 else if (element == EL_WALL_EMERALD_YELLOW)
3174 Store[x][y] = EL_EMERALD_YELLOW;
3175 else if (element == EL_WALL_EMERALD_RED)
3176 Store[x][y] = EL_EMERALD_RED;
3177 else if (element == EL_WALL_EMERALD_PURPLE)
3178 Store[x][y] = EL_EMERALD_PURPLE;
3179 else if (element == EL_WALL_PEARL)
3180 Store[x][y] = EL_PEARL;
3181 else if (element == EL_WALL_CRYSTAL)
3182 Store[x][y] = EL_CRYSTAL;
3183 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3184 Store[x][y] = element_info[element].content[1][1];
3186 Store[x][y] = EL_EMPTY;
3188 if (x != ex || y != ey ||
3189 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_TYPE_BORDER)
3190 Store2[x][y] = element;
3193 if (AmoebaNr[x][y] &&
3194 (element == EL_AMOEBA_FULL ||
3195 element == EL_BD_AMOEBA ||
3196 element == EL_AMOEBA_GROWING))
3198 AmoebaCnt[AmoebaNr[x][y]]--;
3199 AmoebaCnt2[AmoebaNr[x][y]]--;
3205 MovDir[x][y] = MovPos[x][y] = 0;
3206 GfxDir[x][y] = MovDir[x][y];
3211 Feld[x][y] = EL_EXPLOSION;
3213 GfxElement[x][y] = center_element;
3215 GfxElement[x][y] = EL_UNDEFINED;
3218 ExplodePhase[x][y] = 1;
3220 ExplodeDelay[x][y] = last_phase;
3225 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3227 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3234 if (center_element == EL_YAMYAM)
3235 game.yamyam_content_nr =
3236 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3249 GfxFrame[x][y] = 0; /* restart explosion animation */
3253 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3257 last_phase = ExplodeDelay[x][y];
3260 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3264 /* activate this even in non-DEBUG version until cause for crash in
3265 getGraphicAnimationFrame() (see below) is found and eliminated */
3269 if (GfxElement[x][y] == EL_UNDEFINED)
3272 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3273 printf("Explode(): This should never happen!\n");
3276 GfxElement[x][y] = EL_EMPTY;
3282 border_element = Store2[x][y];
3284 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3285 border_element = StorePlayer[x][y];
3287 if (IS_PLAYER(x, y))
3288 border_element = StorePlayer[x][y];
3292 printf("::: phase == %d\n", phase);
3295 if (phase == element_info[border_element].ignition_delay ||
3296 phase == last_phase)
3298 boolean border_explosion = FALSE;
3302 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3303 !PLAYER_EXPLOSION_PROTECTED(x, y))
3305 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3308 if (IS_PLAYER(x, y))
3311 KillHeroUnlessExplosionProtected(x, y);
3312 border_explosion = TRUE;
3315 if (phase == last_phase)
3316 printf("::: IS_PLAYER\n");
3319 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3321 Feld[x][y] = Store2[x][y];
3324 border_explosion = TRUE;
3327 if (phase == last_phase)
3328 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3331 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3333 AmoebeUmwandeln(x, y);
3335 border_explosion = TRUE;
3338 if (phase == last_phase)
3339 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3340 element_info[border_element].explosion_delay,
3341 element_info[border_element].ignition_delay,
3347 /* if an element just explodes due to another explosion (chain-reaction),
3348 do not immediately end the new explosion when it was the last frame of
3349 the explosion (as it would be done in the following "if"-statement!) */
3350 if (border_explosion && phase == last_phase)
3357 if (phase == first_phase_after_start)
3359 int element = Store2[x][y];
3361 if (element == EL_BLACK_ORB)
3363 Feld[x][y] = Store2[x][y];
3368 else if (phase == half_phase)
3370 int element = Store2[x][y];
3372 if (IS_PLAYER(x, y))
3373 KillHeroUnlessExplosionProtected(x, y);
3374 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3376 Feld[x][y] = Store2[x][y];
3380 else if (element == EL_AMOEBA_TO_DIAMOND)
3381 AmoebeUmwandeln(x, y);
3385 if (phase == last_phase)
3390 printf("::: done: phase == %d\n", phase);
3394 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3397 element = Feld[x][y] = Store[x][y];
3398 Store[x][y] = Store2[x][y] = 0;
3399 GfxElement[x][y] = EL_UNDEFINED;
3401 /* player can escape from explosions and might therefore be still alive */
3402 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3403 element <= EL_PLAYER_IS_EXPLODING_4)
3404 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3406 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3407 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3408 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3411 /* restore probably existing indestructible background element */
3412 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3413 element = Feld[x][y] = Back[x][y];
3416 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3417 GfxDir[x][y] = MV_NO_MOVING;
3418 ChangeDelay[x][y] = 0;
3419 ChangePage[x][y] = -1;
3422 InitField_WithBug2(x, y, FALSE);
3424 InitField(x, y, FALSE);
3426 /* !!! not needed !!! */
3428 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3429 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3432 if (CAN_MOVE(element))
3437 DrawLevelField(x, y);
3439 TestIfElementTouchesCustomElement(x, y);
3441 if (GFX_CRUMBLED(element))
3442 DrawLevelFieldCrumbledSandNeighbours(x, y);
3444 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3445 StorePlayer[x][y] = 0;
3447 if (ELEM_IS_PLAYER(element))
3448 RelocatePlayer(x, y, element);
3451 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3453 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3457 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3459 int stored = Store[x][y];
3460 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3461 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3465 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3467 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3471 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3475 printf("::: %d / %d [%d - %d]\n",
3476 GfxFrame[x][y], phase - delay, phase, delay);
3480 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3481 element_info[GfxElement[x][y]].token_name,
3486 DrawLevelFieldCrumbledSand(x, y);
3488 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3490 DrawLevelElement(x, y, Back[x][y]);
3491 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3493 else if (IS_WALKABLE_UNDER(Back[x][y]))
3495 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3496 DrawLevelElementThruMask(x, y, Back[x][y]);
3498 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3499 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3503 void DynaExplode(int ex, int ey)
3506 int dynabomb_element = Feld[ex][ey];
3507 int dynabomb_size = 1;
3508 boolean dynabomb_xl = FALSE;
3509 struct PlayerInfo *player;
3510 static int xy[4][2] =
3518 if (IS_ACTIVE_BOMB(dynabomb_element))
3520 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3521 dynabomb_size = player->dynabomb_size;
3522 dynabomb_xl = player->dynabomb_xl;
3523 player->dynabombs_left++;
3526 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3528 for (i = 0; i < NUM_DIRECTIONS; i++)
3530 for (j = 1; j <= dynabomb_size; j++)
3532 int x = ex + j * xy[i][0];
3533 int y = ey + j * xy[i][1];
3536 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3539 element = Feld[x][y];
3541 /* do not restart explosions of fields with active bombs */
3542 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3545 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3549 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3550 !IS_DIGGABLE(element) && !dynabomb_xl)
3553 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3554 !CAN_GROW_INTO(element) && !dynabomb_xl)
3558 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3559 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3560 element != EL_SAND && !dynabomb_xl)
3567 void Bang(int x, int y)
3570 int element = MovingOrBlocked2Element(x, y);
3572 int element = Feld[x][y];
3576 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3578 if (IS_PLAYER(x, y))
3581 struct PlayerInfo *player = PLAYERINFO(x, y);
3583 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3584 player->element_nr);
3589 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3591 if (game.emulation == EMU_SUPAPLEX)
3592 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3594 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3599 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3607 case EL_BD_BUTTERFLY:
3610 case EL_DARK_YAMYAM:
3614 RaiseScoreElement(element);
3615 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3617 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3618 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3619 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3620 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3621 case EL_DYNABOMB_INCREASE_NUMBER:
3622 case EL_DYNABOMB_INCREASE_SIZE:
3623 case EL_DYNABOMB_INCREASE_POWER:
3628 case EL_LAMP_ACTIVE:
3630 case EL_AMOEBA_TO_DIAMOND:
3632 if (IS_PLAYER(x, y))
3633 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3635 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3639 if (element_info[element].explosion_type == EXPLODES_CROSS)
3641 if (CAN_EXPLODE_CROSS(element))
3644 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3649 else if (element_info[element].explosion_type == EXPLODES_1X1)
3651 else if (CAN_EXPLODE_1X1(element))
3653 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3655 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3659 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3662 void SplashAcid(int x, int y)
3665 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3666 (!IN_LEV_FIELD(x - 1, y - 2) ||
3667 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3668 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3670 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3671 (!IN_LEV_FIELD(x + 1, y - 2) ||
3672 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3673 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3675 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3677 /* input: position of element entering acid (obsolete) */
3679 int element = Feld[x][y];
3681 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3684 if (element != EL_ACID_SPLASH_LEFT &&
3685 element != EL_ACID_SPLASH_RIGHT)
3687 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3689 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3690 (!IN_LEV_FIELD(x - 1, y - 1) ||
3691 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3692 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3694 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3695 (!IN_LEV_FIELD(x + 1, y - 1) ||
3696 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3697 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3702 static void InitBeltMovement()
3704 static int belt_base_element[4] =
3706 EL_CONVEYOR_BELT_1_LEFT,
3707 EL_CONVEYOR_BELT_2_LEFT,
3708 EL_CONVEYOR_BELT_3_LEFT,
3709 EL_CONVEYOR_BELT_4_LEFT
3711 static int belt_base_active_element[4] =
3713 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3714 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3715 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3716 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3721 /* set frame order for belt animation graphic according to belt direction */
3722 for (i = 0; i < NUM_BELTS; i++)
3726 for (j = 0; j < NUM_BELT_PARTS; j++)
3728 int element = belt_base_active_element[belt_nr] + j;
3729 int graphic = el2img(element);
3731 if (game.belt_dir[i] == MV_LEFT)
3732 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3734 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3738 for (y = 0; y < lev_fieldy; y++)
3740 for (x = 0; x < lev_fieldx; x++)
3742 int element = Feld[x][y];
3744 for (i = 0; i < NUM_BELTS; i++)
3746 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3748 int e_belt_nr = getBeltNrFromBeltElement(element);
3751 if (e_belt_nr == belt_nr)
3753 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3755 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3763 static void ToggleBeltSwitch(int x, int y)
3765 static int belt_base_element[4] =
3767 EL_CONVEYOR_BELT_1_LEFT,
3768 EL_CONVEYOR_BELT_2_LEFT,
3769 EL_CONVEYOR_BELT_3_LEFT,
3770 EL_CONVEYOR_BELT_4_LEFT
3772 static int belt_base_active_element[4] =
3774 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3775 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3776 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3777 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3779 static int belt_base_switch_element[4] =
3781 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3782 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3783 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3784 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3786 static int belt_move_dir[4] =
3794 int element = Feld[x][y];
3795 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3796 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3797 int belt_dir = belt_move_dir[belt_dir_nr];
3800 if (!IS_BELT_SWITCH(element))
3803 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3804 game.belt_dir[belt_nr] = belt_dir;
3806 if (belt_dir_nr == 3)
3809 /* set frame order for belt animation graphic according to belt direction */
3810 for (i = 0; i < NUM_BELT_PARTS; i++)
3812 int element = belt_base_active_element[belt_nr] + i;
3813 int graphic = el2img(element);
3815 if (belt_dir == MV_LEFT)
3816 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3818 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3821 for (yy = 0; yy < lev_fieldy; yy++)
3823 for (xx = 0; xx < lev_fieldx; xx++)
3825 int element = Feld[xx][yy];
3827 if (IS_BELT_SWITCH(element))
3829 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3831 if (e_belt_nr == belt_nr)
3833 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3834 DrawLevelField(xx, yy);
3837 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3839 int e_belt_nr = getBeltNrFromBeltElement(element);
3841 if (e_belt_nr == belt_nr)
3843 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3845 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3846 DrawLevelField(xx, yy);
3849 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3851 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3853 if (e_belt_nr == belt_nr)
3855 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3857 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3858 DrawLevelField(xx, yy);
3865 static void ToggleSwitchgateSwitch(int x, int y)
3869 game.switchgate_pos = !game.switchgate_pos;
3871 for (yy = 0; yy < lev_fieldy; yy++)
3873 for (xx = 0; xx < lev_fieldx; xx++)
3875 int element = Feld[xx][yy];
3877 if (element == EL_SWITCHGATE_SWITCH_UP ||
3878 element == EL_SWITCHGATE_SWITCH_DOWN)
3880 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3881 DrawLevelField(xx, yy);
3883 else if (element == EL_SWITCHGATE_OPEN ||
3884 element == EL_SWITCHGATE_OPENING)
3886 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3888 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3890 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3893 else if (element == EL_SWITCHGATE_CLOSED ||
3894 element == EL_SWITCHGATE_CLOSING)
3896 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3898 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3900 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3907 static int getInvisibleActiveFromInvisibleElement(int element)
3909 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3910 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3911 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3915 static int getInvisibleFromInvisibleActiveElement(int element)
3917 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3918 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3919 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3923 static void RedrawAllLightSwitchesAndInvisibleElements()
3927 for (y = 0; y < lev_fieldy; y++)
3929 for (x = 0; x < lev_fieldx; x++)
3931 int element = Feld[x][y];
3933 if (element == EL_LIGHT_SWITCH &&
3934 game.light_time_left > 0)
3936 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3937 DrawLevelField(x, y);
3939 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3940 game.light_time_left == 0)
3942 Feld[x][y] = EL_LIGHT_SWITCH;
3943 DrawLevelField(x, y);
3945 else if (element == EL_INVISIBLE_STEELWALL ||
3946 element == EL_INVISIBLE_WALL ||
3947 element == EL_INVISIBLE_SAND)
3949 if (game.light_time_left > 0)
3950 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3952 DrawLevelField(x, y);
3954 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3955 element == EL_INVISIBLE_WALL_ACTIVE ||
3956 element == EL_INVISIBLE_SAND_ACTIVE)
3958 if (game.light_time_left == 0)
3959 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3961 DrawLevelField(x, y);
3967 static void ToggleLightSwitch(int x, int y)
3969 int element = Feld[x][y];
3971 game.light_time_left =
3972 (element == EL_LIGHT_SWITCH ?
3973 level.time_light * FRAMES_PER_SECOND : 0);
3975 RedrawAllLightSwitchesAndInvisibleElements();
3978 static void ActivateTimegateSwitch(int x, int y)
3982 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3984 for (yy = 0; yy < lev_fieldy; yy++)
3986 for (xx = 0; xx < lev_fieldx; xx++)
3988 int element = Feld[xx][yy];
3990 if (element == EL_TIMEGATE_CLOSED ||
3991 element == EL_TIMEGATE_CLOSING)
3993 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3994 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3998 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4000 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4001 DrawLevelField(xx, yy);
4008 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4011 inline static int getElementMoveStepsize(int x, int y)
4013 int element = Feld[x][y];
4014 int direction = MovDir[x][y];
4015 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4016 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4017 int horiz_move = (dx != 0);
4018 int sign = (horiz_move ? dx : dy);
4019 int step = sign * element_info[element].move_stepsize;
4021 /* special values for move stepsize for spring and things on conveyor belt */
4025 if (element == EL_SPRING)
4026 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4027 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
4028 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4029 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4031 if (CAN_FALL(element) &&
4032 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4033 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4034 else if (element == EL_SPRING)
4035 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4042 void Impact(int x, int y)
4044 boolean lastline = (y == lev_fieldy-1);
4045 boolean object_hit = FALSE;
4046 boolean impact = (lastline || object_hit);
4047 int element = Feld[x][y];
4048 int smashed = EL_STEELWALL;
4050 if (!lastline) /* check if element below was hit */
4052 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4055 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4056 MovDir[x][y + 1] != MV_DOWN ||
4057 MovPos[x][y + 1] <= TILEY / 2));
4060 object_hit = !IS_FREE(x, y + 1);
4063 /* do not smash moving elements that left the smashed field in time */
4064 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4065 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4069 smashed = MovingOrBlocked2Element(x, y + 1);
4071 impact = (lastline || object_hit);
4074 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4076 SplashAcid(x, y + 1);
4080 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4081 /* only reset graphic animation if graphic really changes after impact */
4083 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4085 ResetGfxAnimation(x, y);
4086 DrawLevelField(x, y);
4089 if (impact && CAN_EXPLODE_IMPACT(element))
4094 else if (impact && element == EL_PEARL)
4096 ResetGfxAnimation(x, y);
4098 Feld[x][y] = EL_PEARL_BREAKING;
4099 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4102 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4104 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4109 if (impact && element == EL_AMOEBA_DROP)
4111 if (object_hit && IS_PLAYER(x, y + 1))
4112 KillHeroUnlessEnemyProtected(x, y + 1);
4113 else if (object_hit && smashed == EL_PENGUIN)
4117 Feld[x][y] = EL_AMOEBA_GROWING;
4118 Store[x][y] = EL_AMOEBA_WET;
4120 ResetRandomAnimationValue(x, y);
4125 if (object_hit) /* check which object was hit */
4127 if (CAN_PASS_MAGIC_WALL(element) &&
4128 (smashed == EL_MAGIC_WALL ||
4129 smashed == EL_BD_MAGIC_WALL))
4132 int activated_magic_wall =
4133 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4134 EL_BD_MAGIC_WALL_ACTIVE);
4136 /* activate magic wall / mill */
4137 for (yy = 0; yy < lev_fieldy; yy++)
4138 for (xx = 0; xx < lev_fieldx; xx++)
4139 if (Feld[xx][yy] == smashed)
4140 Feld[xx][yy] = activated_magic_wall;
4142 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4143 game.magic_wall_active = TRUE;
4145 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4146 SND_MAGIC_WALL_ACTIVATING :
4147 SND_BD_MAGIC_WALL_ACTIVATING));
4150 if (IS_PLAYER(x, y + 1))
4152 if (CAN_SMASH_PLAYER(element))
4154 KillHeroUnlessEnemyProtected(x, y + 1);
4158 else if (smashed == EL_PENGUIN)
4160 if (CAN_SMASH_PLAYER(element))
4166 else if (element == EL_BD_DIAMOND)
4168 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4174 else if (((element == EL_SP_INFOTRON ||
4175 element == EL_SP_ZONK) &&
4176 (smashed == EL_SP_SNIKSNAK ||
4177 smashed == EL_SP_ELECTRON ||
4178 smashed == EL_SP_DISK_ORANGE)) ||
4179 (element == EL_SP_INFOTRON &&
4180 smashed == EL_SP_DISK_YELLOW))
4186 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4192 else if (CAN_SMASH_EVERYTHING(element))
4194 if (IS_CLASSIC_ENEMY(smashed) ||
4195 CAN_EXPLODE_SMASHED(smashed))
4200 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4202 if (smashed == EL_LAMP ||
4203 smashed == EL_LAMP_ACTIVE)
4208 else if (smashed == EL_NUT)
4210 Feld[x][y + 1] = EL_NUT_BREAKING;
4211 PlayLevelSound(x, y, SND_NUT_BREAKING);
4212 RaiseScoreElement(EL_NUT);
4215 else if (smashed == EL_PEARL)
4217 ResetGfxAnimation(x, y);
4219 Feld[x][y + 1] = EL_PEARL_BREAKING;
4220 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4223 else if (smashed == EL_DIAMOND)
4225 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4226 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4229 else if (IS_BELT_SWITCH(smashed))
4231 ToggleBeltSwitch(x, y + 1);
4233 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4234 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4236 ToggleSwitchgateSwitch(x, y + 1);
4238 else if (smashed == EL_LIGHT_SWITCH ||
4239 smashed == EL_LIGHT_SWITCH_ACTIVE)
4241 ToggleLightSwitch(x, y + 1);
4246 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4249 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4252 /* !!! TEST ONLY !!! */
4253 CheckElementChangeBySide(x, y + 1, smashed, element,
4254 CE_SWITCHED, CH_SIDE_TOP);
4255 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4256 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4258 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4259 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4260 CheckElementChangeBySide(x, y + 1, smashed, element,
4261 CE_SWITCHED, CH_SIDE_TOP);
4267 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4272 /* play sound of magic wall / mill */
4274 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4275 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4277 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4278 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4279 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4280 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4285 /* play sound of object that hits the ground */
4286 if (lastline || object_hit)
4287 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4290 inline static void TurnRoundExt(int x, int y)
4302 { 0, 0 }, { 0, 0 }, { 0, 0 },
4307 int left, right, back;
4311 { MV_DOWN, MV_UP, MV_RIGHT },
4312 { MV_UP, MV_DOWN, MV_LEFT },
4314 { MV_LEFT, MV_RIGHT, MV_DOWN },
4318 { MV_RIGHT, MV_LEFT, MV_UP }
4321 int element = Feld[x][y];
4322 int move_pattern = element_info[element].move_pattern;
4324 int old_move_dir = MovDir[x][y];
4325 int left_dir = turn[old_move_dir].left;
4326 int right_dir = turn[old_move_dir].right;
4327 int back_dir = turn[old_move_dir].back;
4329 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4330 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4331 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4332 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4334 int left_x = x + left_dx, left_y = y + left_dy;
4335 int right_x = x + right_dx, right_y = y + right_dy;
4336 int move_x = x + move_dx, move_y = y + move_dy;
4340 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4342 TestIfBadThingTouchesOtherBadThing(x, y);
4344 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4345 MovDir[x][y] = right_dir;
4346 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4347 MovDir[x][y] = left_dir;
4349 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4351 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4355 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4356 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4358 TestIfBadThingTouchesOtherBadThing(x, y);
4360 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4361 MovDir[x][y] = left_dir;
4362 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4363 MovDir[x][y] = right_dir;
4365 if ((element == EL_SPACESHIP ||
4366 element == EL_SP_SNIKSNAK ||
4367 element == EL_SP_ELECTRON)
4368 && MovDir[x][y] != old_move_dir)
4370 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4374 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4376 TestIfBadThingTouchesOtherBadThing(x, y);
4378 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4379 MovDir[x][y] = left_dir;
4380 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4381 MovDir[x][y] = right_dir;
4383 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4385 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4388 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4390 TestIfBadThingTouchesOtherBadThing(x, y);
4392 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4393 MovDir[x][y] = left_dir;
4394 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4395 MovDir[x][y] = right_dir;
4397 if (MovDir[x][y] != old_move_dir)
4401 else if (element == EL_YAMYAM)
4403 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4404 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4406 if (can_turn_left && can_turn_right)
4407 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4408 else if (can_turn_left)
4409 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4410 else if (can_turn_right)
4411 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4413 MovDir[x][y] = back_dir;
4415 MovDelay[x][y] = 16 + 16 * RND(3);
4417 else if (element == EL_DARK_YAMYAM)
4419 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4421 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4424 if (can_turn_left && can_turn_right)
4425 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4426 else if (can_turn_left)
4427 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4428 else if (can_turn_right)
4429 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4431 MovDir[x][y] = back_dir;
4433 MovDelay[x][y] = 16 + 16 * RND(3);
4435 else if (element == EL_PACMAN)
4437 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4438 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4440 if (can_turn_left && can_turn_right)
4441 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4442 else if (can_turn_left)
4443 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4444 else if (can_turn_right)
4445 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4447 MovDir[x][y] = back_dir;
4449 MovDelay[x][y] = 6 + RND(40);
4451 else if (element == EL_PIG)
4453 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4454 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4455 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4456 boolean should_turn_left, should_turn_right, should_move_on;
4458 int rnd = RND(rnd_value);
4460 should_turn_left = (can_turn_left &&
4462 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4463 y + back_dy + left_dy)));
4464 should_turn_right = (can_turn_right &&
4466 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4467 y + back_dy + right_dy)));
4468 should_move_on = (can_move_on &&
4471 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4472 y + move_dy + left_dy) ||
4473 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4474 y + move_dy + right_dy)));
4476 if (should_turn_left || should_turn_right || should_move_on)
4478 if (should_turn_left && should_turn_right && should_move_on)
4479 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4480 rnd < 2 * rnd_value / 3 ? right_dir :
4482 else if (should_turn_left && should_turn_right)
4483 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4484 else if (should_turn_left && should_move_on)
4485 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4486 else if (should_turn_right && should_move_on)
4487 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4488 else if (should_turn_left)
4489 MovDir[x][y] = left_dir;
4490 else if (should_turn_right)
4491 MovDir[x][y] = right_dir;
4492 else if (should_move_on)
4493 MovDir[x][y] = old_move_dir;
4495 else if (can_move_on && rnd > rnd_value / 8)
4496 MovDir[x][y] = old_move_dir;
4497 else if (can_turn_left && can_turn_right)
4498 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4499 else if (can_turn_left && rnd > rnd_value / 8)
4500 MovDir[x][y] = left_dir;
4501 else if (can_turn_right && rnd > rnd_value/8)
4502 MovDir[x][y] = right_dir;
4504 MovDir[x][y] = back_dir;
4506 xx = x + move_xy[MovDir[x][y]].x;
4507 yy = y + move_xy[MovDir[x][y]].y;
4509 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4510 MovDir[x][y] = old_move_dir;
4514 else if (element == EL_DRAGON)
4516 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4517 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4518 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4520 int rnd = RND(rnd_value);
4523 if (FrameCounter < 1 && x == 0 && y == 29)
4524 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4527 if (can_move_on && rnd > rnd_value / 8)
4528 MovDir[x][y] = old_move_dir;
4529 else if (can_turn_left && can_turn_right)
4530 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4531 else if (can_turn_left && rnd > rnd_value / 8)
4532 MovDir[x][y] = left_dir;
4533 else if (can_turn_right && rnd > rnd_value / 8)
4534 MovDir[x][y] = right_dir;
4536 MovDir[x][y] = back_dir;
4538 xx = x + move_xy[MovDir[x][y]].x;
4539 yy = y + move_xy[MovDir[x][y]].y;
4542 if (FrameCounter < 1 && x == 0 && y == 29)
4543 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4544 xx, yy, Feld[xx][yy],
4549 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4550 MovDir[x][y] = old_move_dir;
4552 if (!IS_FREE(xx, yy))
4553 MovDir[x][y] = old_move_dir;
4557 if (FrameCounter < 1 && x == 0 && y == 29)
4558 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4563 else if (element == EL_MOLE)
4565 boolean can_move_on =
4566 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4567 IS_AMOEBOID(Feld[move_x][move_y]) ||
4568 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4571 boolean can_turn_left =
4572 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4573 IS_AMOEBOID(Feld[left_x][left_y])));
4575 boolean can_turn_right =
4576 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4577 IS_AMOEBOID(Feld[right_x][right_y])));
4579 if (can_turn_left && can_turn_right)
4580 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4581 else if (can_turn_left)
4582 MovDir[x][y] = left_dir;
4584 MovDir[x][y] = right_dir;
4587 if (MovDir[x][y] != old_move_dir)
4590 else if (element == EL_BALLOON)
4592 MovDir[x][y] = game.balloon_dir;
4595 else if (element == EL_SPRING)
4598 if (MovDir[x][y] & MV_HORIZONTAL &&
4599 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4600 MovDir[x][y] = MV_NO_MOVING;
4602 if (MovDir[x][y] & MV_HORIZONTAL &&
4603 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4604 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4605 MovDir[x][y] = MV_NO_MOVING;
4610 else if (element == EL_ROBOT ||
4611 element == EL_SATELLITE ||
4612 element == EL_PENGUIN)
4614 int attr_x = -1, attr_y = -1;
4625 for (i = 0; i < MAX_PLAYERS; i++)
4627 struct PlayerInfo *player = &stored_player[i];
4628 int jx = player->jx, jy = player->jy;
4630 if (!player->active)
4634 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4643 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4644 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4645 game.engine_version < VERSION_IDENT(3,1,0,0)))
4647 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4654 if (element == EL_PENGUIN)
4657 static int xy[4][2] =
4665 for (i = 0; i < NUM_DIRECTIONS; i++)
4667 int ex = x + xy[i][0];
4668 int ey = y + xy[i][1];
4670 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4679 MovDir[x][y] = MV_NO_MOVING;
4681 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4682 else if (attr_x > x)
4683 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4685 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4686 else if (attr_y > y)
4687 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4689 if (element == EL_ROBOT)
4693 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4694 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4695 Moving2Blocked(x, y, &newx, &newy);
4697 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4698 MovDelay[x][y] = 8 + 8 * !RND(3);
4700 MovDelay[x][y] = 16;
4702 else if (element == EL_PENGUIN)
4708 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4710 boolean first_horiz = RND(2);
4711 int new_move_dir = MovDir[x][y];
4714 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4715 Moving2Blocked(x, y, &newx, &newy);
4717 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4721 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4722 Moving2Blocked(x, y, &newx, &newy);
4724 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4727 MovDir[x][y] = old_move_dir;
4731 else /* (element == EL_SATELLITE) */
4737 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4739 boolean first_horiz = RND(2);
4740 int new_move_dir = MovDir[x][y];
4743 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4744 Moving2Blocked(x, y, &newx, &newy);
4746 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4750 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4751 Moving2Blocked(x, y, &newx, &newy);
4753 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4756 MovDir[x][y] = old_move_dir;
4761 else if (move_pattern == MV_TURNING_LEFT ||
4762 move_pattern == MV_TURNING_RIGHT ||
4763 move_pattern == MV_TURNING_LEFT_RIGHT ||
4764 move_pattern == MV_TURNING_RIGHT_LEFT ||
4765 move_pattern == MV_TURNING_RANDOM ||
4766 move_pattern == MV_ALL_DIRECTIONS)
4768 boolean can_turn_left =
4769 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4770 boolean can_turn_right =
4771 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4773 if (move_pattern == MV_TURNING_LEFT)
4774 MovDir[x][y] = left_dir;
4775 else if (move_pattern == MV_TURNING_RIGHT)
4776 MovDir[x][y] = right_dir;
4777 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4778 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4779 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4780 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4781 else if (move_pattern == MV_TURNING_RANDOM)
4782 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4783 can_turn_right && !can_turn_left ? right_dir :
4784 RND(2) ? left_dir : right_dir);
4785 else if (can_turn_left && can_turn_right)
4786 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4787 else if (can_turn_left)
4788 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4789 else if (can_turn_right)
4790 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4792 MovDir[x][y] = back_dir;
4794 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4796 else if (move_pattern == MV_HORIZONTAL ||
4797 move_pattern == MV_VERTICAL)
4799 if (move_pattern & old_move_dir)
4800 MovDir[x][y] = back_dir;
4801 else if (move_pattern == MV_HORIZONTAL)
4802 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4803 else if (move_pattern == MV_VERTICAL)
4804 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4806 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4808 else if (move_pattern & MV_ANY_DIRECTION)
4810 MovDir[x][y] = move_pattern;
4811 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4813 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4815 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4816 MovDir[x][y] = left_dir;
4817 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4818 MovDir[x][y] = right_dir;
4820 if (MovDir[x][y] != old_move_dir)
4821 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4823 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4825 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4826 MovDir[x][y] = right_dir;
4827 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4828 MovDir[x][y] = left_dir;
4830 if (MovDir[x][y] != old_move_dir)
4831 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4833 else if (move_pattern == MV_TOWARDS_PLAYER ||
4834 move_pattern == MV_AWAY_FROM_PLAYER)
4836 int attr_x = -1, attr_y = -1;
4838 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4849 for (i = 0; i < MAX_PLAYERS; i++)
4851 struct PlayerInfo *player = &stored_player[i];
4852 int jx = player->jx, jy = player->jy;
4854 if (!player->active)
4858 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4866 MovDir[x][y] = MV_NO_MOVING;
4868 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4869 else if (attr_x > x)
4870 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4872 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4873 else if (attr_y > y)
4874 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4876 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4878 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4880 boolean first_horiz = RND(2);
4881 int new_move_dir = MovDir[x][y];
4884 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4885 Moving2Blocked(x, y, &newx, &newy);
4887 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4891 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4892 Moving2Blocked(x, y, &newx, &newy);
4894 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4897 MovDir[x][y] = old_move_dir;
4900 else if (move_pattern == MV_WHEN_PUSHED ||
4901 move_pattern == MV_WHEN_DROPPED)
4903 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4904 MovDir[x][y] = MV_NO_MOVING;
4908 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4910 static int test_xy[7][2] =
4920 static int test_dir[7] =
4930 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4931 int move_preference = -1000000; /* start with very low preference */
4932 int new_move_dir = MV_NO_MOVING;
4933 int start_test = RND(4);
4936 for (i = 0; i < NUM_DIRECTIONS; i++)
4938 int move_dir = test_dir[start_test + i];
4939 int move_dir_preference;
4941 xx = x + test_xy[start_test + i][0];
4942 yy = y + test_xy[start_test + i][1];
4944 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4945 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4947 new_move_dir = move_dir;
4952 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4955 move_dir_preference = -1 * RunnerVisit[xx][yy];
4956 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4957 move_dir_preference = PlayerVisit[xx][yy];
4959 if (move_dir_preference > move_preference)
4961 /* prefer field that has not been visited for the longest time */
4962 move_preference = move_dir_preference;
4963 new_move_dir = move_dir;
4965 else if (move_dir_preference == move_preference &&
4966 move_dir == old_move_dir)
4968 /* prefer last direction when all directions are preferred equally */
4969 move_preference = move_dir_preference;
4970 new_move_dir = move_dir;
4974 MovDir[x][y] = new_move_dir;
4975 if (old_move_dir != new_move_dir)
4980 static void TurnRound(int x, int y)
4982 int direction = MovDir[x][y];
4985 GfxDir[x][y] = MovDir[x][y];
4991 GfxDir[x][y] = MovDir[x][y];
4994 if (direction != MovDir[x][y])
4999 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5002 GfxAction[x][y] = ACTION_WAITING;
5006 static boolean JustBeingPushed(int x, int y)
5010 for (i = 0; i < MAX_PLAYERS; i++)
5012 struct PlayerInfo *player = &stored_player[i];
5014 if (player->active && player->is_pushing && player->MovPos)
5016 int next_jx = player->jx + (player->jx - player->last_jx);
5017 int next_jy = player->jy + (player->jy - player->last_jy);
5019 if (x == next_jx && y == next_jy)
5027 void StartMoving(int x, int y)
5030 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5032 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5033 int element = Feld[x][y];
5039 if (MovDelay[x][y] == 0)
5040 GfxAction[x][y] = ACTION_DEFAULT;
5042 /* !!! this should be handled more generic (not only for mole) !!! */
5043 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5044 GfxAction[x][y] = ACTION_DEFAULT;
5047 if (CAN_FALL(element) && y < lev_fieldy - 1)
5049 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5050 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5051 if (JustBeingPushed(x, y))
5054 if (element == EL_QUICKSAND_FULL)
5056 if (IS_FREE(x, y + 1))
5058 InitMovingField(x, y, MV_DOWN);
5059 started_moving = TRUE;
5061 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5062 Store[x][y] = EL_ROCK;
5064 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5066 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5069 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5071 if (!MovDelay[x][y])
5072 MovDelay[x][y] = TILEY + 1;
5081 Feld[x][y] = EL_QUICKSAND_EMPTY;
5082 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5083 Store[x][y + 1] = Store[x][y];
5086 PlayLevelSoundAction(x, y, ACTION_FILLING);
5088 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5092 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5093 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5095 InitMovingField(x, y, MV_DOWN);
5096 started_moving = TRUE;
5098 Feld[x][y] = EL_QUICKSAND_FILLING;
5099 Store[x][y] = element;
5101 PlayLevelSoundAction(x, y, ACTION_FILLING);
5103 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5106 else if (element == EL_MAGIC_WALL_FULL)
5108 if (IS_FREE(x, y + 1))
5110 InitMovingField(x, y, MV_DOWN);
5111 started_moving = TRUE;
5113 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5114 Store[x][y] = EL_CHANGED(Store[x][y]);
5116 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5118 if (!MovDelay[x][y])
5119 MovDelay[x][y] = TILEY/4 + 1;
5128 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5129 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5130 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5134 else if (element == EL_BD_MAGIC_WALL_FULL)
5136 if (IS_FREE(x, y + 1))
5138 InitMovingField(x, y, MV_DOWN);
5139 started_moving = TRUE;
5141 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5142 Store[x][y] = EL_CHANGED2(Store[x][y]);
5144 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5146 if (!MovDelay[x][y])
5147 MovDelay[x][y] = TILEY/4 + 1;
5156 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5157 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5158 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5162 else if (CAN_PASS_MAGIC_WALL(element) &&
5163 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5164 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5166 InitMovingField(x, y, MV_DOWN);
5167 started_moving = TRUE;
5170 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5171 EL_BD_MAGIC_WALL_FILLING);
5172 Store[x][y] = element;
5175 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5177 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5180 SplashAcid(x, y + 1);
5182 InitMovingField(x, y, MV_DOWN);
5183 started_moving = TRUE;
5185 Store[x][y] = EL_ACID;
5187 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5188 GfxAction[x][y + 1] = ACTION_ACTIVE;
5192 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5193 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5195 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5196 CAN_SMASH(element) && WasJustFalling[x][y] &&
5197 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5199 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5200 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5201 (Feld[x][y + 1] == EL_BLOCKED)))
5205 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5206 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5207 WasJustMoving[x][y] && !Pushed[x][y + 1])
5209 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5210 WasJustMoving[x][y])
5215 /* this is needed for a special case not covered by calling "Impact()"
5216 from "ContinueMoving()": if an element moves to a tile directly below
5217 another element which was just falling on that tile (which was empty
5218 in the previous frame), the falling element above would just stop
5219 instead of smashing the element below (in previous version, the above
5220 element was just checked for "moving" instead of "falling", resulting
5221 in incorrect smashes caused by horizontal movement of the above
5222 element; also, the case of the player being the element to smash was
5223 simply not covered here... :-/ ) */
5226 WasJustMoving[x][y] = 0;
5227 WasJustFalling[x][y] = 0;
5230 CheckCollision[x][y] = 0;
5234 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5236 if (MovDir[x][y] == MV_NO_MOVING)
5238 InitMovingField(x, y, MV_DOWN);
5239 started_moving = TRUE;
5242 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5244 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5245 MovDir[x][y] = MV_DOWN;
5247 InitMovingField(x, y, MV_DOWN);
5248 started_moving = TRUE;
5250 else if (element == EL_AMOEBA_DROP)
5252 Feld[x][y] = EL_AMOEBA_GROWING;
5253 Store[x][y] = EL_AMOEBA_WET;
5255 /* Store[x][y + 1] must be zero, because:
5256 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5259 #if OLD_GAME_BEHAVIOUR
5260 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5262 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5263 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5264 element != EL_DX_SUPABOMB)
5267 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5268 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5269 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5270 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5273 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5274 (IS_FREE(x - 1, y + 1) ||
5275 Feld[x - 1][y + 1] == EL_ACID));
5276 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5277 (IS_FREE(x + 1, y + 1) ||
5278 Feld[x + 1][y + 1] == EL_ACID));
5279 boolean can_fall_any = (can_fall_left || can_fall_right);
5280 boolean can_fall_both = (can_fall_left && can_fall_right);
5282 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5284 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5286 if (slippery_type == SLIPPERY_ONLY_LEFT)
5287 can_fall_right = FALSE;
5288 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5289 can_fall_left = FALSE;
5290 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5291 can_fall_right = FALSE;
5292 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5293 can_fall_left = FALSE;
5295 can_fall_any = (can_fall_left || can_fall_right);
5296 can_fall_both = (can_fall_left && can_fall_right);
5301 if (can_fall_both &&
5302 (game.emulation != EMU_BOULDERDASH &&
5303 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5304 can_fall_left = !(can_fall_right = RND(2));
5306 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5307 started_moving = TRUE;
5311 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5313 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5316 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5317 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5318 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5319 int belt_dir = game.belt_dir[belt_nr];
5321 if ((belt_dir == MV_LEFT && left_is_free) ||
5322 (belt_dir == MV_RIGHT && right_is_free))
5325 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5328 InitMovingField(x, y, belt_dir);
5329 started_moving = TRUE;
5332 Pushed[x][y] = TRUE;
5333 Pushed[nextx][y] = TRUE;
5336 GfxAction[x][y] = ACTION_DEFAULT;
5340 MovDir[x][y] = 0; /* if element was moving, stop it */
5345 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5347 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5349 if (CAN_MOVE(element) && !started_moving)
5352 int move_pattern = element_info[element].move_pattern;
5357 if (MovDir[x][y] == MV_NO_MOVING)
5359 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5360 x, y, element, element_info[element].token_name);
5361 printf("StartMoving(): This should never happen!\n");
5366 Moving2Blocked(x, y, &newx, &newy);
5369 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5372 if ((element == EL_SATELLITE ||
5373 element == EL_BALLOON ||
5374 element == EL_SPRING)
5375 && JustBeingPushed(x, y))
5382 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5383 CheckCollision[x][y] && IN_LEV_FIELD_AND_NOT_FREE(newx, newy))
5385 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5386 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5387 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5391 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5392 element, element_info[element].token_name,
5393 WasJustMoving[x][y],
5394 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5395 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5396 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5397 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5401 WasJustMoving[x][y] = 0;
5404 CheckCollision[x][y] = 0;
5406 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5409 if (Feld[x][y] != element) /* element has changed */
5411 element = Feld[x][y];
5412 move_pattern = element_info[element].move_pattern;
5414 if (!CAN_MOVE(element))
5418 if (Feld[x][y] != element) /* element has changed */
5426 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5427 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5429 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5431 Moving2Blocked(x, y, &newx, &newy);
5432 if (Feld[newx][newy] == EL_BLOCKED)
5433 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5439 if (FrameCounter < 1 && x == 0 && y == 29)
5440 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5443 if (!MovDelay[x][y]) /* start new movement phase */
5445 /* all objects that can change their move direction after each step
5446 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5448 if (element != EL_YAMYAM &&
5449 element != EL_DARK_YAMYAM &&
5450 element != EL_PACMAN &&
5451 !(move_pattern & MV_ANY_DIRECTION) &&
5452 move_pattern != MV_TURNING_LEFT &&
5453 move_pattern != MV_TURNING_RIGHT &&
5454 move_pattern != MV_TURNING_LEFT_RIGHT &&
5455 move_pattern != MV_TURNING_RIGHT_LEFT &&
5456 move_pattern != MV_TURNING_RANDOM)
5461 if (FrameCounter < 1 && x == 0 && y == 29)
5462 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5465 if (MovDelay[x][y] && (element == EL_BUG ||
5466 element == EL_SPACESHIP ||
5467 element == EL_SP_SNIKSNAK ||
5468 element == EL_SP_ELECTRON ||
5469 element == EL_MOLE))
5470 DrawLevelField(x, y);
5474 if (MovDelay[x][y]) /* wait some time before next movement */
5479 if (element == EL_YAMYAM)
5482 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5483 DrawLevelElementAnimation(x, y, element);
5487 if (MovDelay[x][y]) /* element still has to wait some time */
5490 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5491 ResetGfxAnimation(x, y);
5495 if (GfxAction[x][y] != ACTION_WAITING)
5496 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5498 GfxAction[x][y] = ACTION_WAITING;
5502 if (element == EL_ROBOT ||
5504 element == EL_PACMAN ||
5506 element == EL_YAMYAM ||
5507 element == EL_DARK_YAMYAM)
5510 DrawLevelElementAnimation(x, y, element);
5512 DrawLevelElementAnimationIfNeeded(x, y, element);
5514 PlayLevelSoundAction(x, y, ACTION_WAITING);
5516 else if (element == EL_SP_ELECTRON)
5517 DrawLevelElementAnimationIfNeeded(x, y, element);
5518 else if (element == EL_DRAGON)
5521 int dir = MovDir[x][y];
5522 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5523 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5524 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5525 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5526 dir == MV_UP ? IMG_FLAMES_1_UP :
5527 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5528 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5531 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5534 GfxAction[x][y] = ACTION_ATTACKING;
5536 if (IS_PLAYER(x, y))
5537 DrawPlayerField(x, y);
5539 DrawLevelField(x, y);
5541 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5543 for (i = 1; i <= 3; i++)
5545 int xx = x + i * dx;
5546 int yy = y + i * dy;
5547 int sx = SCREENX(xx);
5548 int sy = SCREENY(yy);
5549 int flame_graphic = graphic + (i - 1);
5551 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5556 int flamed = MovingOrBlocked2Element(xx, yy);
5560 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5562 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5563 RemoveMovingField(xx, yy);
5565 RemoveField(xx, yy);
5567 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5570 RemoveMovingField(xx, yy);
5574 if (ChangeDelay[xx][yy])
5575 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5576 Feld[xx][yy] == EL_BLOCKED));
5580 ChangeDelay[xx][yy] = 0;
5582 Feld[xx][yy] = EL_FLAMES;
5583 if (IN_SCR_FIELD(sx, sy))
5585 DrawLevelFieldCrumbledSand(xx, yy);
5586 DrawGraphic(sx, sy, flame_graphic, frame);
5591 if (Feld[xx][yy] == EL_FLAMES)
5592 Feld[xx][yy] = EL_EMPTY;
5593 DrawLevelField(xx, yy);
5598 if (MovDelay[x][y]) /* element still has to wait some time */
5600 PlayLevelSoundAction(x, y, ACTION_WAITING);
5606 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5607 for all other elements GfxAction will be set by InitMovingField() */
5608 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5609 GfxAction[x][y] = ACTION_MOVING;
5613 /* now make next step */
5615 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5617 if (DONT_COLLIDE_WITH(element) &&
5618 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5619 !PLAYER_ENEMY_PROTECTED(newx, newy))
5622 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5626 /* player killed by element which is deadly when colliding with */
5628 KillHero(PLAYERINFO(newx, newy));
5635 else if (CAN_MOVE_INTO_ACID(element) &&
5636 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5637 (MovDir[x][y] == MV_DOWN ||
5638 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5640 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5641 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5645 else if ((element == EL_PENGUIN ||
5646 element == EL_ROBOT ||
5647 element == EL_SATELLITE ||
5648 element == EL_BALLOON ||
5649 IS_CUSTOM_ELEMENT(element)) &&
5650 IN_LEV_FIELD(newx, newy) &&
5651 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5654 SplashAcid(newx, newy);
5655 Store[x][y] = EL_ACID;
5657 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5659 if (Feld[newx][newy] == EL_EXIT_OPEN)
5663 DrawLevelField(x, y);
5665 Feld[x][y] = EL_EMPTY;
5666 DrawLevelField(x, y);
5669 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5670 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5671 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5673 local_player->friends_still_needed--;
5674 if (!local_player->friends_still_needed &&
5675 !local_player->GameOver && AllPlayersGone)
5676 local_player->LevelSolved = local_player->GameOver = TRUE;
5680 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5682 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5683 DrawLevelField(newx, newy);
5685 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5687 else if (!IS_FREE(newx, newy))
5689 GfxAction[x][y] = ACTION_WAITING;
5691 if (IS_PLAYER(x, y))
5692 DrawPlayerField(x, y);
5694 DrawLevelField(x, y);
5699 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5701 if (IS_FOOD_PIG(Feld[newx][newy]))
5703 if (IS_MOVING(newx, newy))
5704 RemoveMovingField(newx, newy);
5707 Feld[newx][newy] = EL_EMPTY;
5708 DrawLevelField(newx, newy);
5711 PlayLevelSound(x, y, SND_PIG_DIGGING);
5713 else if (!IS_FREE(newx, newy))
5715 if (IS_PLAYER(x, y))
5716 DrawPlayerField(x, y);
5718 DrawLevelField(x, y);
5727 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5730 else if (IS_CUSTOM_ELEMENT(element) &&
5731 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5735 !IS_FREE(newx, newy)
5740 int new_element = Feld[newx][newy];
5743 printf("::: '%s' digs '%s' [%d]\n",
5744 element_info[element].token_name,
5745 element_info[Feld[newx][newy]].token_name,
5746 StorePlayer[newx][newy]);
5749 if (!IS_FREE(newx, newy))
5751 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5752 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5755 /* no element can dig solid indestructible elements */
5756 if (IS_INDESTRUCTIBLE(new_element) &&
5757 !IS_DIGGABLE(new_element) &&
5758 !IS_COLLECTIBLE(new_element))
5761 if (AmoebaNr[newx][newy] &&
5762 (new_element == EL_AMOEBA_FULL ||
5763 new_element == EL_BD_AMOEBA ||
5764 new_element == EL_AMOEBA_GROWING))
5766 AmoebaCnt[AmoebaNr[newx][newy]]--;
5767 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5770 if (IS_MOVING(newx, newy))
5771 RemoveMovingField(newx, newy);
5774 RemoveField(newx, newy);
5775 DrawLevelField(newx, newy);
5778 /* if digged element was about to explode, prevent the explosion */
5779 ExplodeField[newx][newy] = EX_TYPE_NONE;
5781 PlayLevelSoundAction(x, y, action);
5786 Store[newx][newy] = EL_EMPTY;
5787 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5788 Store[newx][newy] = element_info[element].move_leave_element;
5790 Store[newx][newy] = EL_EMPTY;
5791 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5792 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5793 Store[newx][newy] = element_info[element].move_leave_element;
5796 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5797 element_info[element].can_leave_element = TRUE;
5800 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5802 RunnerVisit[x][y] = FrameCounter;
5803 PlayerVisit[x][y] /= 8; /* expire player visit path */
5809 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5811 if (!IS_FREE(newx, newy))
5813 if (IS_PLAYER(x, y))
5814 DrawPlayerField(x, y);
5816 DrawLevelField(x, y);
5822 boolean wanna_flame = !RND(10);
5823 int dx = newx - x, dy = newy - y;
5824 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5825 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5826 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5827 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5828 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5829 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5832 IS_CLASSIC_ENEMY(element1) ||
5833 IS_CLASSIC_ENEMY(element2)) &&
5834 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5835 element1 != EL_FLAMES && element2 != EL_FLAMES)
5838 ResetGfxAnimation(x, y);
5839 GfxAction[x][y] = ACTION_ATTACKING;
5842 if (IS_PLAYER(x, y))
5843 DrawPlayerField(x, y);
5845 DrawLevelField(x, y);
5847 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5849 MovDelay[x][y] = 50;
5853 RemoveField(newx, newy);
5855 Feld[newx][newy] = EL_FLAMES;
5856 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5859 RemoveField(newx1, newy1);
5861 Feld[newx1][newy1] = EL_FLAMES;
5863 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5866 RemoveField(newx2, newy2);
5868 Feld[newx2][newy2] = EL_FLAMES;
5875 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5876 Feld[newx][newy] == EL_DIAMOND)
5878 if (IS_MOVING(newx, newy))
5879 RemoveMovingField(newx, newy);
5882 Feld[newx][newy] = EL_EMPTY;
5883 DrawLevelField(newx, newy);
5886 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5888 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5889 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5891 if (AmoebaNr[newx][newy])
5893 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5894 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5895 Feld[newx][newy] == EL_BD_AMOEBA)
5896 AmoebaCnt[AmoebaNr[newx][newy]]--;
5901 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5903 if (IS_MOVING(newx, newy))
5906 RemoveMovingField(newx, newy);
5910 Feld[newx][newy] = EL_EMPTY;
5911 DrawLevelField(newx, newy);
5914 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5916 else if ((element == EL_PACMAN || element == EL_MOLE)
5917 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5919 if (AmoebaNr[newx][newy])
5921 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5922 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5923 Feld[newx][newy] == EL_BD_AMOEBA)
5924 AmoebaCnt[AmoebaNr[newx][newy]]--;
5927 if (element == EL_MOLE)
5929 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5930 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5932 ResetGfxAnimation(x, y);
5933 GfxAction[x][y] = ACTION_DIGGING;
5934 DrawLevelField(x, y);
5936 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5938 return; /* wait for shrinking amoeba */
5940 else /* element == EL_PACMAN */
5942 Feld[newx][newy] = EL_EMPTY;
5943 DrawLevelField(newx, newy);
5944 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5947 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5948 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5949 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5951 /* wait for shrinking amoeba to completely disappear */
5954 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5956 /* object was running against a wall */
5961 if (move_pattern & MV_ANY_DIRECTION &&
5962 move_pattern == MovDir[x][y])
5964 int blocking_element =
5965 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5968 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
5969 element_info[element].token_name,
5970 element_info[blocking_element].token_name,
5974 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5977 element = Feld[x][y]; /* element might have changed */
5982 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5983 DrawLevelElementAnimation(x, y, element);
5985 if (element == EL_BUG ||
5986 element == EL_SPACESHIP ||
5987 element == EL_SP_SNIKSNAK)
5988 DrawLevelField(x, y);
5989 else if (element == EL_MOLE)
5990 DrawLevelField(x, y);
5991 else if (element == EL_BD_BUTTERFLY ||
5992 element == EL_BD_FIREFLY)
5993 DrawLevelElementAnimationIfNeeded(x, y, element);
5994 else if (element == EL_SATELLITE)
5995 DrawLevelElementAnimationIfNeeded(x, y, element);
5996 else if (element == EL_SP_ELECTRON)
5997 DrawLevelElementAnimationIfNeeded(x, y, element);
6000 if (DONT_TOUCH(element))
6001 TestIfBadThingTouchesHero(x, y);
6004 PlayLevelSoundAction(x, y, ACTION_WAITING);
6010 InitMovingField(x, y, MovDir[x][y]);
6012 PlayLevelSoundAction(x, y, ACTION_MOVING);
6016 ContinueMoving(x, y);
6019 void ContinueMoving(int x, int y)
6021 int element = Feld[x][y];
6022 int stored = Store[x][y];
6023 struct ElementInfo *ei = &element_info[element];
6024 int direction = MovDir[x][y];
6025 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6026 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6027 int newx = x + dx, newy = y + dy;
6029 int nextx = newx + dx, nexty = newy + dy;
6032 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6033 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6035 boolean pushed_by_player = Pushed[x][y];
6038 MovPos[x][y] += getElementMoveStepsize(x, y);
6041 if (pushed_by_player && IS_PLAYER(x, y))
6043 /* special case: moving object pushed by player */
6044 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6047 if (pushed_by_player) /* special case: moving object pushed by player */
6048 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6051 if (ABS(MovPos[x][y]) < TILEX)
6053 DrawLevelField(x, y);
6055 return; /* element is still moving */
6058 /* element reached destination field */
6060 Feld[x][y] = EL_EMPTY;
6061 Feld[newx][newy] = element;
6062 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6065 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6067 element = Feld[newx][newy] = EL_ACID;
6070 else if (element == EL_MOLE)
6072 Feld[x][y] = EL_SAND;
6074 DrawLevelFieldCrumbledSandNeighbours(x, y);
6076 else if (element == EL_QUICKSAND_FILLING)
6078 element = Feld[newx][newy] = get_next_element(element);
6079 Store[newx][newy] = Store[x][y];
6081 else if (element == EL_QUICKSAND_EMPTYING)
6083 Feld[x][y] = get_next_element(element);
6084 element = Feld[newx][newy] = Store[x][y];
6086 else if (element == EL_MAGIC_WALL_FILLING)
6088 element = Feld[newx][newy] = get_next_element(element);
6089 if (!game.magic_wall_active)
6090 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6091 Store[newx][newy] = Store[x][y];
6093 else if (element == EL_MAGIC_WALL_EMPTYING)
6095 Feld[x][y] = get_next_element(element);
6096 if (!game.magic_wall_active)
6097 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6098 element = Feld[newx][newy] = Store[x][y];
6100 else if (element == EL_BD_MAGIC_WALL_FILLING)
6102 element = Feld[newx][newy] = get_next_element(element);
6103 if (!game.magic_wall_active)
6104 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6105 Store[newx][newy] = Store[x][y];
6107 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6109 Feld[x][y] = get_next_element(element);
6110 if (!game.magic_wall_active)
6111 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6112 element = Feld[newx][newy] = Store[x][y];
6114 else if (element == EL_AMOEBA_DROPPING)
6116 Feld[x][y] = get_next_element(element);
6117 element = Feld[newx][newy] = Store[x][y];
6119 else if (element == EL_SOKOBAN_OBJECT)
6122 Feld[x][y] = Back[x][y];
6124 if (Back[newx][newy])
6125 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6127 Back[x][y] = Back[newx][newy] = 0;
6130 else if (Store[x][y] == EL_ACID)
6132 element = Feld[newx][newy] = EL_ACID;
6136 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6137 ei->move_leave_element != EL_EMPTY &&
6138 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6139 Store[x][y] != EL_EMPTY))
6141 /* some elements can leave other elements behind after moving */
6143 Feld[x][y] = ei->move_leave_element;
6144 InitField(x, y, FALSE);
6146 if (GFX_CRUMBLED(Feld[x][y]))
6147 DrawLevelFieldCrumbledSandNeighbours(x, y);
6151 Store[x][y] = EL_EMPTY;
6152 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6153 MovDelay[newx][newy] = 0;
6155 if (CAN_CHANGE(element))
6157 /* copy element change control values to new field */
6158 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6159 ChangePage[newx][newy] = ChangePage[x][y];
6160 Changed[newx][newy] = Changed[x][y];
6161 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6164 ChangeDelay[x][y] = 0;
6165 ChangePage[x][y] = -1;
6166 Changed[x][y] = CE_BITMASK_DEFAULT;
6167 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6169 /* copy animation control values to new field */
6170 GfxFrame[newx][newy] = GfxFrame[x][y];
6171 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6172 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6173 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6175 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6177 ResetGfxAnimation(x, y); /* reset animation values for old field */
6180 /* some elements can leave other elements behind after moving */
6182 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6183 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6184 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6186 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6187 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6191 int move_leave_element = ei->move_leave_element;
6193 Feld[x][y] = move_leave_element;
6194 InitField(x, y, FALSE);
6196 if (GFX_CRUMBLED(Feld[x][y]))
6197 DrawLevelFieldCrumbledSandNeighbours(x, y);
6199 if (ELEM_IS_PLAYER(move_leave_element))
6200 RelocatePlayer(x, y, move_leave_element);
6205 /* some elements can leave other elements behind after moving */
6206 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6207 ei->move_leave_element != EL_EMPTY &&
6208 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6209 ei->can_leave_element_last))
6211 Feld[x][y] = ei->move_leave_element;
6212 InitField(x, y, FALSE);
6214 if (GFX_CRUMBLED(Feld[x][y]))
6215 DrawLevelFieldCrumbledSandNeighbours(x, y);
6218 ei->can_leave_element_last = ei->can_leave_element;
6219 ei->can_leave_element = FALSE;
6223 /* 2.1.1 (does not work correctly for spring) */
6224 if (!CAN_MOVE(element))
6225 MovDir[newx][newy] = 0;
6229 /* (does not work for falling objects that slide horizontally) */
6230 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6231 MovDir[newx][newy] = 0;
6234 if (!CAN_MOVE(element) ||
6235 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6236 MovDir[newx][newy] = 0;
6240 if (!CAN_MOVE(element) ||
6241 (CAN_FALL(element) && direction == MV_DOWN))
6242 GfxDir[x][y] = MovDir[newx][newy] = 0;
6244 if (!CAN_MOVE(element) ||
6245 (CAN_FALL(element) && direction == MV_DOWN &&
6246 (element == EL_SPRING ||
6247 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6248 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6249 GfxDir[x][y] = MovDir[newx][newy] = 0;
6255 DrawLevelField(x, y);
6256 DrawLevelField(newx, newy);
6258 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6260 /* prevent pushed element from moving on in pushed direction */
6261 if (pushed_by_player && CAN_MOVE(element) &&
6262 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6263 !(element_info[element].move_pattern & direction))
6264 TurnRound(newx, newy);
6267 /* prevent elements on conveyor belt from moving on in last direction */
6268 if (pushed_by_conveyor && CAN_FALL(element) &&
6269 direction & MV_HORIZONTAL)
6272 if (CAN_MOVE(element))
6273 InitMovDir(newx, newy);
6275 MovDir[newx][newy] = 0;
6277 MovDir[newx][newy] = 0;
6282 if (!pushed_by_player)
6284 int nextx = newx + dx, nexty = newy + dy;
6285 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6287 WasJustMoving[newx][newy] = 3;
6289 if (CAN_FALL(element) && direction == MV_DOWN)
6290 WasJustFalling[newx][newy] = 3;
6292 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6293 CheckCollision[newx][newy] = 2;
6296 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6298 TestIfBadThingTouchesHero(newx, newy);
6299 TestIfBadThingTouchesFriend(newx, newy);
6301 if (!IS_CUSTOM_ELEMENT(element))
6302 TestIfBadThingTouchesOtherBadThing(newx, newy);
6304 else if (element == EL_PENGUIN)
6305 TestIfFriendTouchesBadThing(newx, newy);
6307 if (CAN_FALL(element) && direction == MV_DOWN &&
6308 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6312 if (pushed_by_player)
6315 int dig_side = MV_DIR_OPPOSITE(direction);
6317 static int trigger_sides[4] =
6319 CH_SIDE_RIGHT, /* moving left */
6320 CH_SIDE_LEFT, /* moving right */
6321 CH_SIDE_BOTTOM, /* moving up */
6322 CH_SIDE_TOP, /* moving down */
6324 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6326 struct PlayerInfo *player = PLAYERINFO(x, y);
6328 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6329 player->index_bit, dig_side);
6330 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6331 player->index_bit, dig_side);
6336 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6340 if (ChangePage[newx][newy] != -1) /* delayed change */
6341 ChangeElement(newx, newy, ChangePage[newx][newy]);
6346 TestIfElementHitsCustomElement(newx, newy, direction);
6350 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6352 int hitting_element = Feld[newx][newy];
6354 /* !!! fix side (direction) orientation here and elsewhere !!! */
6355 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6359 if (IN_LEV_FIELD(nextx, nexty))
6361 int opposite_direction = MV_DIR_OPPOSITE(direction);
6362 int hitting_side = direction;
6363 int touched_side = opposite_direction;
6364 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6365 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6366 MovDir[nextx][nexty] != direction ||
6367 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6373 CheckElementChangeBySide(nextx, nexty, touched_element,
6374 CE_HIT_BY_SOMETHING, opposite_direction);
6376 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6377 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6379 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6381 struct ElementChangeInfo *change =
6382 &element_info[hitting_element].change_page[i];
6384 if (change->can_change &&
6385 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6386 change->trigger_side & touched_side &&
6387 change->trigger_element == touched_element)
6389 CheckElementChangeByPage(newx, newy, hitting_element,
6390 touched_element, CE_OTHER_IS_HITTING,i);
6396 if (IS_CUSTOM_ELEMENT(touched_element) &&
6397 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6399 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6401 struct ElementChangeInfo *change =
6402 &element_info[touched_element].change_page[i];
6404 if (change->can_change &&
6405 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6406 change->trigger_side & hitting_side &&
6407 change->trigger_element == hitting_element)
6409 CheckElementChangeByPage(nextx, nexty, touched_element,
6410 hitting_element, CE_OTHER_GETS_HIT, i);
6421 TestIfPlayerTouchesCustomElement(newx, newy);
6422 TestIfElementTouchesCustomElement(newx, newy);
6425 int AmoebeNachbarNr(int ax, int ay)
6428 int element = Feld[ax][ay];
6430 static int xy[4][2] =
6438 for (i = 0; i < NUM_DIRECTIONS; i++)
6440 int x = ax + xy[i][0];
6441 int y = ay + xy[i][1];
6443 if (!IN_LEV_FIELD(x, y))
6446 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6447 group_nr = AmoebaNr[x][y];
6453 void AmoebenVereinigen(int ax, int ay)
6455 int i, x, y, xx, yy;
6456 int new_group_nr = AmoebaNr[ax][ay];
6457 static int xy[4][2] =
6465 if (new_group_nr == 0)
6468 for (i = 0; i < NUM_DIRECTIONS; i++)
6473 if (!IN_LEV_FIELD(x, y))
6476 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6477 Feld[x][y] == EL_BD_AMOEBA ||
6478 Feld[x][y] == EL_AMOEBA_DEAD) &&
6479 AmoebaNr[x][y] != new_group_nr)
6481 int old_group_nr = AmoebaNr[x][y];
6483 if (old_group_nr == 0)
6486 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6487 AmoebaCnt[old_group_nr] = 0;
6488 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6489 AmoebaCnt2[old_group_nr] = 0;
6491 for (yy = 0; yy < lev_fieldy; yy++)
6493 for (xx = 0; xx < lev_fieldx; xx++)
6495 if (AmoebaNr[xx][yy] == old_group_nr)
6496 AmoebaNr[xx][yy] = new_group_nr;
6503 void AmoebeUmwandeln(int ax, int ay)
6507 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6509 int group_nr = AmoebaNr[ax][ay];
6514 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6515 printf("AmoebeUmwandeln(): This should never happen!\n");
6520 for (y = 0; y < lev_fieldy; y++)
6522 for (x = 0; x < lev_fieldx; x++)
6524 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6527 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6531 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6532 SND_AMOEBA_TURNING_TO_GEM :
6533 SND_AMOEBA_TURNING_TO_ROCK));
6538 static int xy[4][2] =
6546 for (i = 0; i < NUM_DIRECTIONS; i++)
6551 if (!IN_LEV_FIELD(x, y))
6554 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6556 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6557 SND_AMOEBA_TURNING_TO_GEM :
6558 SND_AMOEBA_TURNING_TO_ROCK));
6565 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6568 int group_nr = AmoebaNr[ax][ay];
6569 boolean done = FALSE;
6574 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6575 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6580 for (y = 0; y < lev_fieldy; y++)
6582 for (x = 0; x < lev_fieldx; x++)
6584 if (AmoebaNr[x][y] == group_nr &&
6585 (Feld[x][y] == EL_AMOEBA_DEAD ||
6586 Feld[x][y] == EL_BD_AMOEBA ||
6587 Feld[x][y] == EL_AMOEBA_GROWING))
6590 Feld[x][y] = new_element;
6591 InitField(x, y, FALSE);
6592 DrawLevelField(x, y);
6599 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6600 SND_BD_AMOEBA_TURNING_TO_ROCK :
6601 SND_BD_AMOEBA_TURNING_TO_GEM));
6604 void AmoebeWaechst(int x, int y)
6606 static unsigned long sound_delay = 0;
6607 static unsigned long sound_delay_value = 0;
6609 if (!MovDelay[x][y]) /* start new growing cycle */
6613 if (DelayReached(&sound_delay, sound_delay_value))
6616 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6618 if (Store[x][y] == EL_BD_AMOEBA)
6619 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6621 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6623 sound_delay_value = 30;
6627 if (MovDelay[x][y]) /* wait some time before growing bigger */
6630 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6632 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6633 6 - MovDelay[x][y]);
6635 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6638 if (!MovDelay[x][y])
6640 Feld[x][y] = Store[x][y];
6642 DrawLevelField(x, y);
6647 void AmoebaDisappearing(int x, int y)
6649 static unsigned long sound_delay = 0;
6650 static unsigned long sound_delay_value = 0;
6652 if (!MovDelay[x][y]) /* start new shrinking cycle */
6656 if (DelayReached(&sound_delay, sound_delay_value))
6657 sound_delay_value = 30;
6660 if (MovDelay[x][y]) /* wait some time before shrinking */
6663 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6665 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6666 6 - MovDelay[x][y]);
6668 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6671 if (!MovDelay[x][y])
6673 Feld[x][y] = EL_EMPTY;
6674 DrawLevelField(x, y);
6676 /* don't let mole enter this field in this cycle;
6677 (give priority to objects falling to this field from above) */
6683 void AmoebeAbleger(int ax, int ay)
6686 int element = Feld[ax][ay];
6687 int graphic = el2img(element);
6688 int newax = ax, neway = ay;
6689 static int xy[4][2] =
6697 if (!level.amoeba_speed)
6699 Feld[ax][ay] = EL_AMOEBA_DEAD;
6700 DrawLevelField(ax, ay);
6704 if (IS_ANIMATED(graphic))
6705 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6707 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6708 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6710 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6713 if (MovDelay[ax][ay])
6717 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6720 int x = ax + xy[start][0];
6721 int y = ay + xy[start][1];
6723 if (!IN_LEV_FIELD(x, y))
6727 if (IS_FREE(x, y) ||
6728 CAN_GROW_INTO(Feld[x][y]) ||
6729 Feld[x][y] == EL_QUICKSAND_EMPTY)
6735 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6736 if (IS_FREE(x, y) ||
6737 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6744 if (newax == ax && neway == ay)
6747 else /* normal or "filled" (BD style) amoeba */
6750 boolean waiting_for_player = FALSE;
6752 for (i = 0; i < NUM_DIRECTIONS; i++)
6754 int j = (start + i) % 4;
6755 int x = ax + xy[j][0];
6756 int y = ay + xy[j][1];
6758 if (!IN_LEV_FIELD(x, y))
6762 if (IS_FREE(x, y) ||
6763 CAN_GROW_INTO(Feld[x][y]) ||
6764 Feld[x][y] == EL_QUICKSAND_EMPTY)
6771 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6772 if (IS_FREE(x, y) ||
6773 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6780 else if (IS_PLAYER(x, y))
6781 waiting_for_player = TRUE;
6784 if (newax == ax && neway == ay) /* amoeba cannot grow */
6787 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6789 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6792 Feld[ax][ay] = EL_AMOEBA_DEAD;
6793 DrawLevelField(ax, ay);
6794 AmoebaCnt[AmoebaNr[ax][ay]]--;
6796 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6798 if (element == EL_AMOEBA_FULL)
6799 AmoebeUmwandeln(ax, ay);
6800 else if (element == EL_BD_AMOEBA)
6801 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6806 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6808 /* amoeba gets larger by growing in some direction */
6810 int new_group_nr = AmoebaNr[ax][ay];
6813 if (new_group_nr == 0)
6815 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6816 printf("AmoebeAbleger(): This should never happen!\n");
6821 AmoebaNr[newax][neway] = new_group_nr;
6822 AmoebaCnt[new_group_nr]++;
6823 AmoebaCnt2[new_group_nr]++;
6825 /* if amoeba touches other amoeba(s) after growing, unify them */
6826 AmoebenVereinigen(newax, neway);
6828 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6830 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6836 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6837 (neway == lev_fieldy - 1 && newax != ax))
6839 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6840 Store[newax][neway] = element;
6842 else if (neway == ay)
6844 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6846 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6848 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6853 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6854 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6855 Store[ax][ay] = EL_AMOEBA_DROP;
6856 ContinueMoving(ax, ay);
6860 DrawLevelField(newax, neway);
6863 void Life(int ax, int ay)
6866 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6868 int element = Feld[ax][ay];
6869 int graphic = el2img(element);
6870 boolean changed = FALSE;
6872 if (IS_ANIMATED(graphic))
6873 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6878 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6879 MovDelay[ax][ay] = life_time;
6881 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6884 if (MovDelay[ax][ay])
6888 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6890 int xx = ax+x1, yy = ay+y1;
6893 if (!IN_LEV_FIELD(xx, yy))
6896 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6898 int x = xx+x2, y = yy+y2;
6900 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6903 if (((Feld[x][y] == element ||
6904 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6906 (IS_FREE(x, y) && Stop[x][y]))
6910 if (xx == ax && yy == ay) /* field in the middle */
6912 if (nachbarn < life[0] || nachbarn > life[1])
6914 Feld[xx][yy] = EL_EMPTY;
6916 DrawLevelField(xx, yy);
6917 Stop[xx][yy] = TRUE;
6922 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6923 { /* free border field */
6924 if (nachbarn >= life[2] && nachbarn <= life[3])
6926 Feld[xx][yy] = element;
6927 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6929 DrawLevelField(xx, yy);
6930 Stop[xx][yy] = TRUE;
6935 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6936 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6937 { /* free border field */
6938 if (nachbarn >= life[2] && nachbarn <= life[3])
6940 Feld[xx][yy] = element;
6941 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6943 DrawLevelField(xx, yy);
6944 Stop[xx][yy] = TRUE;
6952 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6953 SND_GAME_OF_LIFE_GROWING);
6956 static void InitRobotWheel(int x, int y)
6958 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6961 static void RunRobotWheel(int x, int y)
6963 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6966 static void StopRobotWheel(int x, int y)
6968 if (ZX == x && ZY == y)
6972 static void InitTimegateWheel(int x, int y)
6975 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6977 /* another brainless, "type style" bug ... :-( */
6978 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6982 static void RunTimegateWheel(int x, int y)
6984 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6987 void CheckExit(int x, int y)
6989 if (local_player->gems_still_needed > 0 ||
6990 local_player->sokobanfields_still_needed > 0 ||
6991 local_player->lights_still_needed > 0)
6993 int element = Feld[x][y];
6994 int graphic = el2img(element);
6996 if (IS_ANIMATED(graphic))
6997 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7002 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7005 Feld[x][y] = EL_EXIT_OPENING;
7007 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7010 void CheckExitSP(int x, int y)
7012 if (local_player->gems_still_needed > 0)
7014 int element = Feld[x][y];
7015 int graphic = el2img(element);
7017 if (IS_ANIMATED(graphic))
7018 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7023 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7026 Feld[x][y] = EL_SP_EXIT_OPENING;
7028 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7031 static void CloseAllOpenTimegates()
7035 for (y = 0; y < lev_fieldy; y++)
7037 for (x = 0; x < lev_fieldx; x++)
7039 int element = Feld[x][y];
7041 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7043 Feld[x][y] = EL_TIMEGATE_CLOSING;
7045 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7047 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7054 void EdelsteinFunkeln(int x, int y)
7056 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7059 if (Feld[x][y] == EL_BD_DIAMOND)
7062 if (MovDelay[x][y] == 0) /* next animation frame */
7063 MovDelay[x][y] = 11 * !SimpleRND(500);
7065 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7069 if (setup.direct_draw && MovDelay[x][y])
7070 SetDrawtoField(DRAW_BUFFERED);
7072 DrawLevelElementAnimation(x, y, Feld[x][y]);
7074 if (MovDelay[x][y] != 0)
7076 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7077 10 - MovDelay[x][y]);
7079 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7081 if (setup.direct_draw)
7085 dest_x = FX + SCREENX(x) * TILEX;
7086 dest_y = FY + SCREENY(y) * TILEY;
7088 BlitBitmap(drawto_field, window,
7089 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7090 SetDrawtoField(DRAW_DIRECT);
7096 void MauerWaechst(int x, int y)
7100 if (!MovDelay[x][y]) /* next animation frame */
7101 MovDelay[x][y] = 3 * delay;
7103 if (MovDelay[x][y]) /* wait some time before next frame */
7107 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7109 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7110 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7112 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7115 if (!MovDelay[x][y])
7117 if (MovDir[x][y] == MV_LEFT)
7119 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7120 DrawLevelField(x - 1, y);
7122 else if (MovDir[x][y] == MV_RIGHT)
7124 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7125 DrawLevelField(x + 1, y);
7127 else if (MovDir[x][y] == MV_UP)
7129 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7130 DrawLevelField(x, y - 1);
7134 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7135 DrawLevelField(x, y + 1);
7138 Feld[x][y] = Store[x][y];
7140 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7141 DrawLevelField(x, y);
7146 void MauerAbleger(int ax, int ay)
7148 int element = Feld[ax][ay];
7149 int graphic = el2img(element);
7150 boolean oben_frei = FALSE, unten_frei = FALSE;
7151 boolean links_frei = FALSE, rechts_frei = FALSE;
7152 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7153 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7154 boolean new_wall = FALSE;
7156 if (IS_ANIMATED(graphic))
7157 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7159 if (!MovDelay[ax][ay]) /* start building new wall */
7160 MovDelay[ax][ay] = 6;
7162 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7165 if (MovDelay[ax][ay])
7169 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7171 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7173 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7175 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7178 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7179 element == EL_EXPANDABLE_WALL_ANY)
7183 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7184 Store[ax][ay-1] = element;
7185 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7186 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7187 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7188 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7193 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7194 Store[ax][ay+1] = element;
7195 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7196 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7197 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7198 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7203 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7204 element == EL_EXPANDABLE_WALL_ANY ||
7205 element == EL_EXPANDABLE_WALL)
7209 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7210 Store[ax-1][ay] = element;
7211 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7212 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7213 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7214 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7220 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7221 Store[ax+1][ay] = element;
7222 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7223 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7224 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7225 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7230 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7231 DrawLevelField(ax, ay);
7233 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7235 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7236 unten_massiv = TRUE;
7237 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7238 links_massiv = TRUE;
7239 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7240 rechts_massiv = TRUE;
7242 if (((oben_massiv && unten_massiv) ||
7243 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7244 element == EL_EXPANDABLE_WALL) &&
7245 ((links_massiv && rechts_massiv) ||
7246 element == EL_EXPANDABLE_WALL_VERTICAL))
7247 Feld[ax][ay] = EL_WALL;
7251 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7253 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7257 void CheckForDragon(int x, int y)
7260 boolean dragon_found = FALSE;
7261 static int xy[4][2] =
7269 for (i = 0; i < NUM_DIRECTIONS; i++)
7271 for (j = 0; j < 4; j++)
7273 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7275 if (IN_LEV_FIELD(xx, yy) &&
7276 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7278 if (Feld[xx][yy] == EL_DRAGON)
7279 dragon_found = TRUE;
7288 for (i = 0; i < NUM_DIRECTIONS; i++)
7290 for (j = 0; j < 3; j++)
7292 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7294 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7296 Feld[xx][yy] = EL_EMPTY;
7297 DrawLevelField(xx, yy);
7306 static void InitBuggyBase(int x, int y)
7308 int element = Feld[x][y];
7309 int activating_delay = FRAMES_PER_SECOND / 4;
7312 (element == EL_SP_BUGGY_BASE ?
7313 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7314 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7316 element == EL_SP_BUGGY_BASE_ACTIVE ?
7317 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7320 static void WarnBuggyBase(int x, int y)
7323 static int xy[4][2] =
7331 for (i = 0; i < NUM_DIRECTIONS; i++)
7333 int xx = x + xy[i][0], yy = y + xy[i][1];
7335 if (IS_PLAYER(xx, yy))
7337 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7344 static void InitTrap(int x, int y)
7346 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7349 static void ActivateTrap(int x, int y)
7351 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7354 static void ChangeActiveTrap(int x, int y)
7356 int graphic = IMG_TRAP_ACTIVE;
7358 /* if new animation frame was drawn, correct crumbled sand border */
7359 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7360 DrawLevelFieldCrumbledSand(x, y);
7363 static void ChangeElementNowExt(int x, int y, int target_element)
7365 int previous_move_direction = MovDir[x][y];
7367 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7368 IS_WALKABLE(Feld[x][y]));
7370 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7371 IS_WALKABLE(Feld[x][y]) &&
7375 /* check if element under player changes from accessible to unaccessible
7376 (needed for special case of dropping element which then changes) */
7377 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7378 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7389 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7390 RemoveMovingField(x, y);
7394 Feld[x][y] = target_element;
7397 Feld[x][y] = target_element;
7400 ResetGfxAnimation(x, y);
7401 ResetRandomAnimationValue(x, y);
7403 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7404 MovDir[x][y] = previous_move_direction;
7407 InitField_WithBug1(x, y, FALSE);
7409 InitField(x, y, FALSE);
7410 if (CAN_MOVE(Feld[x][y]))
7414 DrawLevelField(x, y);
7416 if (GFX_CRUMBLED(Feld[x][y]))
7417 DrawLevelFieldCrumbledSandNeighbours(x, y);
7420 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7423 TestIfBadThingTouchesHero(x, y);
7424 TestIfPlayerTouchesCustomElement(x, y);
7425 TestIfElementTouchesCustomElement(x, y);
7428 if (ELEM_IS_PLAYER(target_element))
7429 RelocatePlayer(x, y, target_element);
7432 TestIfBadThingTouchesHero(x, y);
7433 TestIfPlayerTouchesCustomElement(x, y);
7434 TestIfElementTouchesCustomElement(x, y);
7438 static boolean ChangeElementNow(int x, int y, int element, int page)
7440 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7442 int old_element = Feld[x][y];
7444 /* always use default change event to prevent running into a loop */
7445 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7446 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7448 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7450 /* reset actual trigger element and player */
7451 change->actual_trigger_element = EL_EMPTY;
7452 change->actual_trigger_player = EL_PLAYER_1;
7455 /* do not change already changed elements with same change event */
7457 if (Changed[x][y] & ChangeEvent[x][y])
7464 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7467 /* !!! indirect change before direct change !!! */
7468 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7471 if (change->explode)
7478 if (change->use_target_content)
7480 boolean complete_replace = TRUE;
7481 boolean can_replace[3][3];
7484 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7487 boolean is_walkable;
7488 boolean is_diggable;
7489 boolean is_collectible;
7490 boolean is_removable;
7491 boolean is_destructible;
7492 int ex = x + xx - 1;
7493 int ey = y + yy - 1;
7494 int content_element = change->target_content[xx][yy];
7497 can_replace[xx][yy] = TRUE;
7499 if (ex == x && ey == y) /* do not check changing element itself */
7502 if (content_element == EL_EMPTY_SPACE)
7504 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7509 if (!IN_LEV_FIELD(ex, ey))
7511 can_replace[xx][yy] = FALSE;
7512 complete_replace = FALSE;
7519 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7520 e = MovingOrBlocked2Element(ex, ey);
7525 is_empty = (IS_FREE(ex, ey) ||
7526 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7527 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7528 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7532 is_empty = (IS_FREE(ex, ey) ||
7533 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7535 is_empty = (IS_FREE(ex, ey) ||
7536 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7540 is_walkable = (is_empty || IS_WALKABLE(e));
7541 is_diggable = (is_empty || IS_DIGGABLE(e));
7542 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7543 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7544 is_removable = (is_diggable || is_collectible);
7546 can_replace[xx][yy] =
7547 ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7548 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7549 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7550 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7551 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7552 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7554 if (!can_replace[xx][yy])
7555 complete_replace = FALSE;
7557 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7558 IS_WALKABLE(content_element)));
7560 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7562 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7565 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7566 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7567 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7569 can_replace[xx][yy] = FALSE;
7570 complete_replace = FALSE;
7575 if (!change->only_if_complete || complete_replace)
7577 boolean something_has_changed = FALSE;
7579 if (change->only_if_complete && change->use_random_replace &&
7580 RND(100) < change->random_percentage)
7583 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7585 int ex = x + xx - 1;
7586 int ey = y + yy - 1;
7587 int content_element;
7589 if (can_replace[xx][yy] && (!change->use_random_replace ||
7590 RND(100) < change->random_percentage))
7592 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7593 RemoveMovingField(ex, ey);
7595 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7597 content_element = change->target_content[xx][yy];
7598 target_element = GET_TARGET_ELEMENT(content_element, change);
7600 ChangeElementNowExt(ex, ey, target_element);
7602 something_has_changed = TRUE;
7604 /* for symmetry reasons, freeze newly created border elements */
7605 if (ex != x || ey != y)
7606 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7610 if (something_has_changed)
7611 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7616 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7618 ChangeElementNowExt(x, y, target_element);
7620 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7624 /* this uses direct change before indirect change */
7625 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7631 static void ChangeElement(int x, int y, int page)
7633 int element = MovingOrBlocked2Element(x, y);
7634 struct ElementInfo *ei = &element_info[element];
7635 struct ElementChangeInfo *change = &ei->change_page[page];
7638 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7641 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7642 x, y, element, element_info[element].token_name);
7643 printf("ChangeElement(): This should never happen!\n");
7648 /* this can happen with classic bombs on walkable, changing elements */
7649 if (!CAN_CHANGE(element))
7652 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7653 ChangeDelay[x][y] = 0;
7659 if (ChangeDelay[x][y] == 0) /* initialize element change */
7661 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7662 RND(change->delay_random * change->delay_frames)) + 1;
7664 ResetGfxAnimation(x, y);
7665 ResetRandomAnimationValue(x, y);
7667 if (change->pre_change_function)
7668 change->pre_change_function(x, y);
7671 ChangeDelay[x][y]--;
7673 if (ChangeDelay[x][y] != 0) /* continue element change */
7675 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7677 if (IS_ANIMATED(graphic))
7678 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7680 if (change->change_function)
7681 change->change_function(x, y);
7683 else /* finish element change */
7685 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7687 page = ChangePage[x][y];
7688 ChangePage[x][y] = -1;
7690 change = &ei->change_page[page];
7694 if (IS_MOVING(x, y) && !change->explode)
7696 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7699 ChangeDelay[x][y] = 1; /* try change after next move step */
7700 ChangePage[x][y] = page; /* remember page to use for change */
7705 if (ChangeElementNow(x, y, element, page))
7707 if (change->post_change_function)
7708 change->post_change_function(x, y);
7713 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7714 int trigger_element,
7721 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7723 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7726 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7728 int element = EL_CUSTOM_START + i;
7730 boolean change_element = FALSE;
7733 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7736 for (j = 0; j < element_info[element].num_change_pages; j++)
7738 struct ElementChangeInfo *change = &element_info[element].change_page[j];
7740 if (change->can_change &&
7741 change->events & CH_EVENT_BIT(trigger_event) &&
7742 change->trigger_side & trigger_side &&
7743 change->trigger_player & trigger_player &&
7744 change->trigger_page & trigger_page_bits &&
7745 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7748 if (!(change->events & CH_EVENT_BIT(trigger_event)))
7749 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7750 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7753 change_element = TRUE;
7756 change->actual_trigger_element = trigger_element;
7757 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7763 if (!change_element)
7766 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7769 if (x == lx && y == ly) /* do not change trigger element itself */
7773 if (Feld[x][y] == element)
7775 ChangeDelay[x][y] = 1;
7776 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7777 ChangeElement(x, y, page);
7785 static boolean CheckElementChangeExt(int x, int y,
7787 int trigger_element,
7793 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7796 if (Feld[x][y] == EL_BLOCKED)
7798 Blocked2Moving(x, y, &x, &y);
7799 element = Feld[x][y];
7803 if (Feld[x][y] != element) /* check if element has already changed */
7806 printf("::: %d ('%s') != %d ('%s') [%d]\n",
7807 Feld[x][y], element_info[Feld[x][y]].token_name,
7808 element, element_info[element].token_name,
7817 if (trigger_page < 0)
7819 boolean change_element = FALSE;
7822 for (i = 0; i < element_info[element].num_change_pages; i++)
7824 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7826 if (change->can_change &&
7827 change->events & CH_EVENT_BIT(trigger_event) &&
7828 change->trigger_side & trigger_side &&
7829 change->trigger_player & trigger_player)
7831 change_element = TRUE;
7834 change->actual_trigger_element = trigger_element;
7835 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7841 if (!change_element)
7846 struct ElementInfo *ei = &element_info[element];
7847 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7849 change->actual_trigger_element = trigger_element;
7850 change->actual_trigger_player = EL_PLAYER_1; /* unused */
7855 /* !!! this check misses pages with same event, but different side !!! */
7857 if (trigger_page < 0)
7858 trigger_page = element_info[element].event_page_nr[trigger_event];
7860 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7864 ChangeDelay[x][y] = 1;
7865 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7866 ChangeElement(x, y, trigger_page);
7871 static void PlayPlayerSound(struct PlayerInfo *player)
7873 int jx = player->jx, jy = player->jy;
7874 int element = player->element_nr;
7875 int last_action = player->last_action_waiting;
7876 int action = player->action_waiting;
7878 if (player->is_waiting)
7880 if (action != last_action)
7881 PlayLevelSoundElementAction(jx, jy, element, action);
7883 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7887 if (action != last_action)
7888 StopSound(element_info[element].sound[last_action]);
7890 if (last_action == ACTION_SLEEPING)
7891 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7895 static void PlayAllPlayersSound()
7899 for (i = 0; i < MAX_PLAYERS; i++)
7900 if (stored_player[i].active)
7901 PlayPlayerSound(&stored_player[i]);
7904 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7906 boolean last_waiting = player->is_waiting;
7907 int move_dir = player->MovDir;
7909 player->last_action_waiting = player->action_waiting;
7913 if (!last_waiting) /* not waiting -> waiting */
7915 player->is_waiting = TRUE;
7917 player->frame_counter_bored =
7919 game.player_boring_delay_fixed +
7920 SimpleRND(game.player_boring_delay_random);
7921 player->frame_counter_sleeping =
7923 game.player_sleeping_delay_fixed +
7924 SimpleRND(game.player_sleeping_delay_random);
7926 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7929 if (game.player_sleeping_delay_fixed +
7930 game.player_sleeping_delay_random > 0 &&
7931 player->anim_delay_counter == 0 &&
7932 player->post_delay_counter == 0 &&
7933 FrameCounter >= player->frame_counter_sleeping)
7934 player->is_sleeping = TRUE;
7935 else if (game.player_boring_delay_fixed +
7936 game.player_boring_delay_random > 0 &&
7937 FrameCounter >= player->frame_counter_bored)
7938 player->is_bored = TRUE;
7940 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7941 player->is_bored ? ACTION_BORING :
7944 if (player->is_sleeping)
7946 if (player->num_special_action_sleeping > 0)
7948 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7950 int last_special_action = player->special_action_sleeping;
7951 int num_special_action = player->num_special_action_sleeping;
7952 int special_action =
7953 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7954 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7955 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7956 last_special_action + 1 : ACTION_SLEEPING);
7957 int special_graphic =
7958 el_act_dir2img(player->element_nr, special_action, move_dir);
7960 player->anim_delay_counter =
7961 graphic_info[special_graphic].anim_delay_fixed +
7962 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7963 player->post_delay_counter =
7964 graphic_info[special_graphic].post_delay_fixed +
7965 SimpleRND(graphic_info[special_graphic].post_delay_random);
7967 player->special_action_sleeping = special_action;
7970 if (player->anim_delay_counter > 0)
7972 player->action_waiting = player->special_action_sleeping;
7973 player->anim_delay_counter--;
7975 else if (player->post_delay_counter > 0)
7977 player->post_delay_counter--;
7981 else if (player->is_bored)
7983 if (player->num_special_action_bored > 0)
7985 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7987 int special_action =
7988 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7989 int special_graphic =
7990 el_act_dir2img(player->element_nr, special_action, move_dir);
7992 player->anim_delay_counter =
7993 graphic_info[special_graphic].anim_delay_fixed +
7994 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7995 player->post_delay_counter =
7996 graphic_info[special_graphic].post_delay_fixed +
7997 SimpleRND(graphic_info[special_graphic].post_delay_random);
7999 player->special_action_bored = special_action;
8002 if (player->anim_delay_counter > 0)
8004 player->action_waiting = player->special_action_bored;
8005 player->anim_delay_counter--;
8007 else if (player->post_delay_counter > 0)
8009 player->post_delay_counter--;
8014 else if (last_waiting) /* waiting -> not waiting */
8016 player->is_waiting = FALSE;
8017 player->is_bored = FALSE;
8018 player->is_sleeping = FALSE;
8020 player->frame_counter_bored = -1;
8021 player->frame_counter_sleeping = -1;
8023 player->anim_delay_counter = 0;
8024 player->post_delay_counter = 0;
8026 player->action_waiting = ACTION_DEFAULT;
8028 player->special_action_bored = ACTION_DEFAULT;
8029 player->special_action_sleeping = ACTION_DEFAULT;
8034 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8037 static byte stored_player_action[MAX_PLAYERS];
8038 static int num_stored_actions = 0;
8040 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8041 int left = player_action & JOY_LEFT;
8042 int right = player_action & JOY_RIGHT;
8043 int up = player_action & JOY_UP;
8044 int down = player_action & JOY_DOWN;
8045 int button1 = player_action & JOY_BUTTON_1;
8046 int button2 = player_action & JOY_BUTTON_2;
8047 int dx = (left ? -1 : right ? 1 : 0);
8048 int dy = (up ? -1 : down ? 1 : 0);
8051 stored_player_action[player->index_nr] = 0;
8052 num_stored_actions++;
8056 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8059 if (!player->active || tape.pausing)
8063 printf("::: [%d %d %d %d] [%d %d]\n",
8064 left, right, up, down, button1, button2);
8070 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8075 if (player->MovPos == 0)
8076 CheckGravityMovement(player);
8079 snapped = SnapField(player, dx, dy);
8083 dropped = DropElement(player);
8085 moved = MovePlayer(player, dx, dy);
8088 if (tape.single_step && tape.recording && !tape.pausing)
8090 if (button1 || (dropped && !moved))
8092 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8093 SnapField(player, 0, 0); /* stop snapping */
8097 SetPlayerWaiting(player, FALSE);
8100 return player_action;
8102 stored_player_action[player->index_nr] = player_action;
8108 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8111 /* no actions for this player (no input at player's configured device) */
8113 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8114 SnapField(player, 0, 0);
8115 CheckGravityMovementWhenNotMoving(player);
8117 if (player->MovPos == 0)
8118 SetPlayerWaiting(player, TRUE);
8120 if (player->MovPos == 0) /* needed for tape.playing */
8121 player->is_moving = FALSE;
8123 player->is_dropping = FALSE;
8129 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8131 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8133 TapeRecordAction(stored_player_action);
8134 num_stored_actions = 0;
8141 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8143 static byte stored_player_action[MAX_PLAYERS];
8144 static int num_stored_actions = 0;
8145 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8146 int left = player_action & JOY_LEFT;
8147 int right = player_action & JOY_RIGHT;
8148 int up = player_action & JOY_UP;
8149 int down = player_action & JOY_DOWN;
8150 int button1 = player_action & JOY_BUTTON_1;
8151 int button2 = player_action & JOY_BUTTON_2;
8152 int dx = (left ? -1 : right ? 1 : 0);
8153 int dy = (up ? -1 : down ? 1 : 0);
8155 stored_player_action[player->index_nr] = 0;
8156 num_stored_actions++;
8158 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8160 if (!player->active || tape.pausing)
8165 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8168 snapped = SnapField(player, dx, dy);
8172 dropped = DropElement(player);
8174 moved = MovePlayer(player, dx, dy);
8177 if (tape.single_step && tape.recording && !tape.pausing)
8179 if (button1 || (dropped && !moved))
8181 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8182 SnapField(player, 0, 0); /* stop snapping */
8186 stored_player_action[player->index_nr] = player_action;
8190 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8192 /* no actions for this player (no input at player's configured device) */
8194 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8195 SnapField(player, 0, 0);
8196 CheckGravityMovementWhenNotMoving(player);
8198 if (player->MovPos == 0)
8199 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8201 if (player->MovPos == 0) /* needed for tape.playing */
8202 player->is_moving = FALSE;
8205 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8207 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8209 TapeRecordAction(stored_player_action);
8210 num_stored_actions = 0;
8217 static unsigned long action_delay = 0;
8218 unsigned long action_delay_value;
8219 int magic_wall_x = 0, magic_wall_y = 0;
8220 int i, x, y, element, graphic;
8221 byte *recorded_player_action;
8222 byte summarized_player_action = 0;
8224 byte tape_action[MAX_PLAYERS];
8227 if (game_status != GAME_MODE_PLAYING)
8230 action_delay_value =
8231 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8233 if (tape.playing && tape.warp_forward && !tape.pausing)
8234 action_delay_value = 0;
8236 /* ---------- main game synchronization point ---------- */
8238 WaitUntilDelayReached(&action_delay, action_delay_value);
8240 if (network_playing && !network_player_action_received)
8244 printf("DEBUG: try to get network player actions in time\n");
8248 #if defined(NETWORK_AVALIABLE)
8249 /* last chance to get network player actions without main loop delay */
8253 if (game_status != GAME_MODE_PLAYING)
8256 if (!network_player_action_received)
8260 printf("DEBUG: failed to get network player actions in time\n");
8271 printf("::: getting new tape action [%d]\n", FrameCounter);
8274 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8277 if (recorded_player_action == NULL && tape.pausing)
8282 printf("::: %d\n", stored_player[0].action);
8286 if (recorded_player_action != NULL)
8287 for (i = 0; i < MAX_PLAYERS; i++)
8288 stored_player[i].action = recorded_player_action[i];
8291 for (i = 0; i < MAX_PLAYERS; i++)
8293 summarized_player_action |= stored_player[i].action;
8295 if (!network_playing)
8296 stored_player[i].effective_action = stored_player[i].action;
8299 #if defined(NETWORK_AVALIABLE)
8300 if (network_playing)
8301 SendToServer_MovePlayer(summarized_player_action);
8304 if (!options.network && !setup.team_mode)
8305 local_player->effective_action = summarized_player_action;
8308 if (recorded_player_action != NULL)
8309 for (i = 0; i < MAX_PLAYERS; i++)
8310 stored_player[i].effective_action = recorded_player_action[i];
8314 for (i = 0; i < MAX_PLAYERS; i++)
8316 tape_action[i] = stored_player[i].effective_action;
8318 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8319 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8322 /* only save actions from input devices, but not programmed actions */
8324 TapeRecordAction(tape_action);
8327 for (i = 0; i < MAX_PLAYERS; i++)
8329 int actual_player_action = stored_player[i].effective_action;
8332 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8333 - rnd_equinox_tetrachloride 048
8334 - rnd_equinox_tetrachloride_ii 096
8335 - rnd_emanuel_schmieg 002
8336 - doctor_sloan_ww 001, 020
8338 if (stored_player[i].MovPos == 0)
8339 CheckGravityMovement(&stored_player[i]);
8343 /* overwrite programmed action with tape action */
8344 if (stored_player[i].programmed_action)
8345 actual_player_action = stored_player[i].programmed_action;
8349 if (stored_player[i].programmed_action)
8350 printf("::: %d\n", stored_player[i].programmed_action);
8353 if (recorded_player_action)
8356 if (stored_player[i].programmed_action &&
8357 stored_player[i].programmed_action != recorded_player_action[i])
8358 printf("::: %d: %d <-> %d\n", i,
8359 stored_player[i].programmed_action, recorded_player_action[i]);
8363 actual_player_action = recorded_player_action[i];
8368 /* overwrite tape action with programmed action */
8369 if (stored_player[i].programmed_action)
8370 actual_player_action = stored_player[i].programmed_action;
8375 printf("::: action: %d: %x [%d]\n",
8376 stored_player[i].MovPos, actual_player_action, FrameCounter);
8380 PlayerActions(&stored_player[i], actual_player_action);
8382 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8384 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8385 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8388 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8393 TapeRecordAction(tape_action);
8396 network_player_action_received = FALSE;
8398 ScrollScreen(NULL, SCROLL_GO_ON);
8404 for (i = 0; i < MAX_PLAYERS; i++)
8405 stored_player[i].Frame++;
8409 /* for downwards compatibility, the following code emulates a fixed bug that
8410 occured when pushing elements (causing elements that just made their last
8411 pushing step to already (if possible) make their first falling step in the
8412 same game frame, which is bad); this code is also needed to use the famous
8413 "spring push bug" which is used in older levels and might be wanted to be
8414 used also in newer levels, but in this case the buggy pushing code is only
8415 affecting the "spring" element and no other elements */
8418 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8420 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8423 for (i = 0; i < MAX_PLAYERS; i++)
8425 struct PlayerInfo *player = &stored_player[i];
8430 if (player->active && player->is_pushing && player->is_moving &&
8432 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8433 Feld[x][y] == EL_SPRING))
8435 if (player->active && player->is_pushing && player->is_moving &&
8439 ContinueMoving(x, y);
8441 /* continue moving after pushing (this is actually a bug) */
8442 if (!IS_MOVING(x, y))
8451 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8453 Changed[x][y] = CE_BITMASK_DEFAULT;
8454 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8457 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8459 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8460 printf("GameActions(): This should never happen!\n");
8462 ChangePage[x][y] = -1;
8467 if (WasJustMoving[x][y] > 0)
8468 WasJustMoving[x][y]--;
8469 if (WasJustFalling[x][y] > 0)
8470 WasJustFalling[x][y]--;
8471 if (CheckCollision[x][y] > 0)
8472 CheckCollision[x][y]--;
8477 /* reset finished pushing action (not done in ContinueMoving() to allow
8478 continous pushing animation for elements with zero push delay) */
8479 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8481 ResetGfxAnimation(x, y);
8482 DrawLevelField(x, y);
8487 if (IS_BLOCKED(x, y))
8491 Blocked2Moving(x, y, &oldx, &oldy);
8492 if (!IS_MOVING(oldx, oldy))
8494 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8495 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8496 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8497 printf("GameActions(): This should never happen!\n");
8503 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8505 element = Feld[x][y];
8507 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8509 graphic = el2img(element);
8515 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8517 element = graphic = 0;
8521 if (graphic_info[graphic].anim_global_sync)
8522 GfxFrame[x][y] = FrameCounter;
8524 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8525 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8526 ResetRandomAnimationValue(x, y);
8528 SetRandomAnimationValue(x, y);
8531 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8534 if (IS_INACTIVE(element))
8536 if (IS_ANIMATED(graphic))
8537 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8543 /* this may take place after moving, so 'element' may have changed */
8545 if (IS_CHANGING(x, y))
8547 if (IS_CHANGING(x, y) &&
8548 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8552 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8553 element_info[element].event_page_nr[CE_DELAY]);
8555 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8558 element = Feld[x][y];
8559 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8563 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8568 element = Feld[x][y];
8569 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8571 if (element == EL_MOLE)
8572 printf("::: %d, %d, %d [%d]\n",
8573 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8577 if (element == EL_YAMYAM)
8578 printf("::: %d, %d, %d\n",
8579 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8583 if (IS_ANIMATED(graphic) &&
8587 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8590 if (element == EL_BUG)
8591 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8595 if (element == EL_MOLE)
8596 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8600 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8601 EdelsteinFunkeln(x, y);
8603 else if ((element == EL_ACID ||
8604 element == EL_EXIT_OPEN ||
8605 element == EL_SP_EXIT_OPEN ||
8606 element == EL_SP_TERMINAL ||
8607 element == EL_SP_TERMINAL_ACTIVE ||
8608 element == EL_EXTRA_TIME ||
8609 element == EL_SHIELD_NORMAL ||
8610 element == EL_SHIELD_DEADLY) &&
8611 IS_ANIMATED(graphic))
8612 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8613 else if (IS_MOVING(x, y))
8614 ContinueMoving(x, y);
8615 else if (IS_ACTIVE_BOMB(element))
8616 CheckDynamite(x, y);
8618 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8619 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8621 else if (element == EL_AMOEBA_GROWING)
8622 AmoebeWaechst(x, y);
8623 else if (element == EL_AMOEBA_SHRINKING)
8624 AmoebaDisappearing(x, y);
8626 #if !USE_NEW_AMOEBA_CODE
8627 else if (IS_AMOEBALIVE(element))
8628 AmoebeAbleger(x, y);
8631 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8633 else if (element == EL_EXIT_CLOSED)
8635 else if (element == EL_SP_EXIT_CLOSED)
8637 else if (element == EL_EXPANDABLE_WALL_GROWING)
8639 else if (element == EL_EXPANDABLE_WALL ||
8640 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8641 element == EL_EXPANDABLE_WALL_VERTICAL ||
8642 element == EL_EXPANDABLE_WALL_ANY)
8644 else if (element == EL_FLAMES)
8645 CheckForDragon(x, y);
8647 else if (IS_AUTO_CHANGING(element))
8648 ChangeElement(x, y);
8650 else if (element == EL_EXPLOSION)
8651 ; /* drawing of correct explosion animation is handled separately */
8652 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8653 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8656 /* this may take place after moving, so 'element' may have changed */
8657 if (IS_AUTO_CHANGING(Feld[x][y]))
8658 ChangeElement(x, y);
8661 if (IS_BELT_ACTIVE(element))
8662 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8664 if (game.magic_wall_active)
8666 int jx = local_player->jx, jy = local_player->jy;
8668 /* play the element sound at the position nearest to the player */
8669 if ((element == EL_MAGIC_WALL_FULL ||
8670 element == EL_MAGIC_WALL_ACTIVE ||
8671 element == EL_MAGIC_WALL_EMPTYING ||
8672 element == EL_BD_MAGIC_WALL_FULL ||
8673 element == EL_BD_MAGIC_WALL_ACTIVE ||
8674 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8675 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8683 #if USE_NEW_AMOEBA_CODE
8684 /* new experimental amoeba growth stuff */
8686 if (!(FrameCounter % 8))
8689 static unsigned long random = 1684108901;
8691 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8694 x = (random >> 10) % lev_fieldx;
8695 y = (random >> 20) % lev_fieldy;
8697 x = RND(lev_fieldx);
8698 y = RND(lev_fieldy);
8700 element = Feld[x][y];
8703 if (!IS_PLAYER(x,y) &&
8704 (element == EL_EMPTY ||
8705 CAN_GROW_INTO(element) ||
8706 element == EL_QUICKSAND_EMPTY ||
8707 element == EL_ACID_SPLASH_LEFT ||
8708 element == EL_ACID_SPLASH_RIGHT))
8710 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8711 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8712 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8713 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8714 Feld[x][y] = EL_AMOEBA_DROP;
8717 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8718 if (!IS_PLAYER(x,y) &&
8719 (element == EL_EMPTY ||
8720 element == EL_SAND ||
8721 element == EL_QUICKSAND_EMPTY ||
8722 element == EL_ACID_SPLASH_LEFT ||
8723 element == EL_ACID_SPLASH_RIGHT))
8725 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8726 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8727 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8728 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8729 Feld[x][y] = EL_AMOEBA_DROP;
8733 random = random * 129 + 1;
8739 if (game.explosions_delayed)
8742 game.explosions_delayed = FALSE;
8744 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8746 element = Feld[x][y];
8748 if (ExplodeField[x][y])
8749 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8750 else if (element == EL_EXPLOSION)
8751 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8753 ExplodeField[x][y] = EX_TYPE_NONE;
8756 game.explosions_delayed = TRUE;
8759 if (game.magic_wall_active)
8761 if (!(game.magic_wall_time_left % 4))
8763 int element = Feld[magic_wall_x][magic_wall_y];
8765 if (element == EL_BD_MAGIC_WALL_FULL ||
8766 element == EL_BD_MAGIC_WALL_ACTIVE ||
8767 element == EL_BD_MAGIC_WALL_EMPTYING)
8768 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8770 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8773 if (game.magic_wall_time_left > 0)
8775 game.magic_wall_time_left--;
8776 if (!game.magic_wall_time_left)
8778 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8780 element = Feld[x][y];
8782 if (element == EL_MAGIC_WALL_ACTIVE ||
8783 element == EL_MAGIC_WALL_FULL)
8785 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8786 DrawLevelField(x, y);
8788 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8789 element == EL_BD_MAGIC_WALL_FULL)
8791 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8792 DrawLevelField(x, y);
8796 game.magic_wall_active = FALSE;
8801 if (game.light_time_left > 0)
8803 game.light_time_left--;
8805 if (game.light_time_left == 0)
8806 RedrawAllLightSwitchesAndInvisibleElements();
8809 if (game.timegate_time_left > 0)
8811 game.timegate_time_left--;
8813 if (game.timegate_time_left == 0)
8814 CloseAllOpenTimegates();
8817 for (i = 0; i < MAX_PLAYERS; i++)
8819 struct PlayerInfo *player = &stored_player[i];
8821 if (SHIELD_ON(player))
8823 if (player->shield_deadly_time_left)
8824 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8825 else if (player->shield_normal_time_left)
8826 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8830 if (TimeFrames >= FRAMES_PER_SECOND)
8835 for (i = 0; i < MAX_PLAYERS; i++)
8837 struct PlayerInfo *player = &stored_player[i];
8839 if (SHIELD_ON(player))
8841 player->shield_normal_time_left--;
8843 if (player->shield_deadly_time_left > 0)
8844 player->shield_deadly_time_left--;
8848 if (!level.use_step_counter)
8856 if (TimeLeft <= 10 && setup.time_limit)
8857 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8859 DrawGameValue_Time(TimeLeft);
8861 if (!TimeLeft && setup.time_limit)
8862 for (i = 0; i < MAX_PLAYERS; i++)
8863 KillHero(&stored_player[i]);
8865 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8866 DrawGameValue_Time(TimePlayed);
8869 if (tape.recording || tape.playing)
8870 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8874 PlayAllPlayersSound();
8876 if (options.debug) /* calculate frames per second */
8878 static unsigned long fps_counter = 0;
8879 static int fps_frames = 0;
8880 unsigned long fps_delay_ms = Counter() - fps_counter;
8884 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8886 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8889 fps_counter = Counter();
8892 redraw_mask |= REDRAW_FPS;
8896 if (stored_player[0].jx != stored_player[0].last_jx ||
8897 stored_player[0].jy != stored_player[0].last_jy)
8898 printf("::: %d, %d, %d, %d, %d\n",
8899 stored_player[0].MovDir,
8900 stored_player[0].MovPos,
8901 stored_player[0].GfxPos,
8902 stored_player[0].Frame,
8903 stored_player[0].StepFrame);
8910 for (i = 0; i < MAX_PLAYERS; i++)
8913 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8915 stored_player[i].Frame += move_frames;
8917 if (stored_player[i].MovPos != 0)
8918 stored_player[i].StepFrame += move_frames;
8920 if (stored_player[i].drop_delay > 0)
8921 stored_player[i].drop_delay--;
8926 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8928 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8930 local_player->show_envelope = 0;
8935 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8937 int min_x = x, min_y = y, max_x = x, max_y = y;
8940 for (i = 0; i < MAX_PLAYERS; i++)
8942 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8944 if (!stored_player[i].active || &stored_player[i] == player)
8947 min_x = MIN(min_x, jx);
8948 min_y = MIN(min_y, jy);
8949 max_x = MAX(max_x, jx);
8950 max_y = MAX(max_y, jy);
8953 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8956 static boolean AllPlayersInVisibleScreen()
8960 for (i = 0; i < MAX_PLAYERS; i++)
8962 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8964 if (!stored_player[i].active)
8967 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8974 void ScrollLevel(int dx, int dy)
8976 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8979 BlitBitmap(drawto_field, drawto_field,
8980 FX + TILEX * (dx == -1) - softscroll_offset,
8981 FY + TILEY * (dy == -1) - softscroll_offset,
8982 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8983 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8984 FX + TILEX * (dx == 1) - softscroll_offset,
8985 FY + TILEY * (dy == 1) - softscroll_offset);
8989 x = (dx == 1 ? BX1 : BX2);
8990 for (y = BY1; y <= BY2; y++)
8991 DrawScreenField(x, y);
8996 y = (dy == 1 ? BY1 : BY2);
8997 for (x = BX1; x <= BX2; x++)
8998 DrawScreenField(x, y);
9001 redraw_mask |= REDRAW_FIELD;
9005 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9007 int nextx = x + dx, nexty = y + dy;
9008 int element = Feld[x][y];
9011 element != EL_SP_PORT_LEFT &&
9012 element != EL_SP_GRAVITY_PORT_LEFT &&
9013 element != EL_SP_PORT_HORIZONTAL &&
9014 element != EL_SP_PORT_ANY) ||
9016 element != EL_SP_PORT_RIGHT &&
9017 element != EL_SP_GRAVITY_PORT_RIGHT &&
9018 element != EL_SP_PORT_HORIZONTAL &&
9019 element != EL_SP_PORT_ANY) ||
9021 element != EL_SP_PORT_UP &&
9022 element != EL_SP_GRAVITY_PORT_UP &&
9023 element != EL_SP_PORT_VERTICAL &&
9024 element != EL_SP_PORT_ANY) ||
9026 element != EL_SP_PORT_DOWN &&
9027 element != EL_SP_GRAVITY_PORT_DOWN &&
9028 element != EL_SP_PORT_VERTICAL &&
9029 element != EL_SP_PORT_ANY) ||
9030 !IN_LEV_FIELD(nextx, nexty) ||
9031 !IS_FREE(nextx, nexty))
9038 static boolean canFallDown(struct PlayerInfo *player)
9040 int jx = player->jx, jy = player->jy;
9042 return (IN_LEV_FIELD(jx, jy + 1) &&
9043 (IS_FREE(jx, jy + 1) ||
9044 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9045 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9046 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9049 static boolean canPassField(int x, int y, int move_dir)
9051 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9052 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9053 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9056 int element = Feld[x][y];
9058 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9059 !CAN_MOVE(element) &&
9060 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9061 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9062 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9065 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9067 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9068 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9069 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9073 int nextx = newx + dx;
9074 int nexty = newy + dy;
9078 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9079 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9080 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9081 (IS_DIGGABLE(Feld[newx][newy]) ||
9082 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9083 canPassField(newx, newy, move_dir)));
9086 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9087 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9088 (IS_DIGGABLE(Feld[newx][newy]) ||
9089 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9090 canPassField(newx, newy, move_dir)));
9093 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9094 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9095 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9096 canPassField(newx, newy, move_dir)));
9098 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9099 (IS_DIGGABLE(Feld[newx][newy]) ||
9100 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9101 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9102 !CAN_MOVE(Feld[newx][newy]) &&
9103 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9104 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9105 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9111 static void CheckGravityMovement(struct PlayerInfo *player)
9113 if (game.gravity && !player->programmed_action)
9116 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9117 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9119 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9120 int move_dir_vertical = player->action & MV_VERTICAL;
9124 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9126 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9129 int jx = player->jx, jy = player->jy;
9131 boolean player_is_moving_to_valid_field =
9132 (!player_is_snapping &&
9133 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9134 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9138 (player->last_move_dir & MV_HORIZONTAL ?
9139 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9140 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9144 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9145 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9146 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9147 int new_jx = jx + dx, new_jy = jy + dy;
9148 int nextx = new_jx + dx, nexty = new_jy + dy;
9154 boolean player_can_fall_down = canFallDown(player);
9156 boolean player_can_fall_down =
9157 (IN_LEV_FIELD(jx, jy + 1) &&
9158 (IS_FREE(jx, jy + 1) ||
9159 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9163 boolean player_can_fall_down =
9164 (IN_LEV_FIELD(jx, jy + 1) &&
9165 (IS_FREE(jx, jy + 1)));
9169 boolean player_is_moving_to_valid_field =
9172 !player_is_snapping &&
9176 IN_LEV_FIELD(new_jx, new_jy) &&
9177 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9178 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9179 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9180 IN_LEV_FIELD(nextx, nexty) &&
9181 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9183 IN_LEV_FIELD(new_jx, new_jy) &&
9184 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9185 Feld[new_jx][new_jy] == EL_SAND ||
9186 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9187 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9188 /* !!! extend EL_SAND to anything diggable !!! */
9194 boolean player_is_standing_on_valid_field =
9195 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9196 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9200 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9201 player_can_fall_down,
9202 player_is_standing_on_valid_field,
9203 player_is_moving_to_valid_field,
9204 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9205 player->effective_action,
9206 player->can_fall_into_acid);
9209 if (player_can_fall_down &&
9211 !player_is_standing_on_valid_field &&
9213 !player_is_moving_to_valid_field)
9216 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9217 jx, jy, FrameCounter);
9220 player->programmed_action = MV_DOWN;
9225 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9228 return CheckGravityMovement(player);
9231 if (game.gravity && !player->programmed_action)
9233 int jx = player->jx, jy = player->jy;
9234 boolean field_under_player_is_free =
9235 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9236 boolean player_is_standing_on_valid_field =
9237 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9238 (IS_WALKABLE(Feld[jx][jy]) &&
9239 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9241 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9242 player->programmed_action = MV_DOWN;
9248 -----------------------------------------------------------------------------
9249 dx, dy: direction (non-diagonal) to try to move the player to
9250 real_dx, real_dy: direction as read from input device (can be diagonal)
9253 boolean MovePlayerOneStep(struct PlayerInfo *player,
9254 int dx, int dy, int real_dx, int real_dy)
9257 static int trigger_sides[4][2] =
9259 /* enter side leave side */
9260 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9261 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9262 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9263 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9265 int move_direction = (dx == -1 ? MV_LEFT :
9266 dx == +1 ? MV_RIGHT :
9268 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9269 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9270 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9272 int jx = player->jx, jy = player->jy;
9273 int new_jx = jx + dx, new_jy = jy + dy;
9277 if (!player->active || (!dx && !dy))
9278 return MF_NO_ACTION;
9280 player->MovDir = (dx < 0 ? MV_LEFT :
9283 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9285 if (!IN_LEV_FIELD(new_jx, new_jy))
9286 return MF_NO_ACTION;
9288 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9289 return MF_NO_ACTION;
9292 element = MovingOrBlocked2Element(new_jx, new_jy);
9294 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9297 if (DONT_RUN_INTO(element))
9299 if (element == EL_ACID && dx == 0 && dy == 1)
9301 SplashAcid(new_jx, new_jy);
9302 Feld[jx][jy] = EL_PLAYER_1;
9303 InitMovingField(jx, jy, MV_DOWN);
9304 Store[jx][jy] = EL_ACID;
9305 ContinueMoving(jx, jy);
9309 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9314 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9315 if (can_move != MF_MOVING)
9318 /* check if DigField() has caused relocation of the player */
9319 if (player->jx != jx || player->jy != jy)
9320 return MF_NO_ACTION;
9322 StorePlayer[jx][jy] = 0;
9323 player->last_jx = jx;
9324 player->last_jy = jy;
9325 player->jx = new_jx;
9326 player->jy = new_jy;
9327 StorePlayer[new_jx][new_jy] = player->element_nr;
9330 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9332 player->step_counter++;
9335 player->drop_delay = 0;
9338 PlayerVisit[jx][jy] = FrameCounter;
9340 ScrollPlayer(player, SCROLL_INIT);
9343 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9345 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9347 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9350 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9352 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9353 CE_OTHER_GETS_ENTERED, enter_side);
9354 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9355 CE_ENTERED_BY_PLAYER, enter_side);
9362 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9364 int jx = player->jx, jy = player->jy;
9365 int old_jx = jx, old_jy = jy;
9366 int moved = MF_NO_ACTION;
9369 if (!player->active)
9374 if (player->MovPos == 0)
9376 player->is_moving = FALSE;
9377 player->is_digging = FALSE;
9378 player->is_collecting = FALSE;
9379 player->is_snapping = FALSE;
9380 player->is_pushing = FALSE;
9386 if (!player->active || (!dx && !dy))
9391 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9397 if (!FrameReached(&player->move_delay, player->move_delay_value))
9400 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9401 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9407 /* store if player is automatically moved to next field */
9408 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9410 /* remove the last programmed player action */
9411 player->programmed_action = 0;
9415 /* should only happen if pre-1.2 tape recordings are played */
9416 /* this is only for backward compatibility */
9418 int original_move_delay_value = player->move_delay_value;
9421 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9425 /* scroll remaining steps with finest movement resolution */
9426 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9428 while (player->MovPos)
9430 ScrollPlayer(player, SCROLL_GO_ON);
9431 ScrollScreen(NULL, SCROLL_GO_ON);
9437 player->move_delay_value = original_move_delay_value;
9440 if (player->last_move_dir & MV_HORIZONTAL)
9442 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9443 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9447 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9448 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9454 if (moved & MF_MOVING && !ScreenMovPos &&
9455 (player == local_player || !options.network))
9457 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9458 int offset = (setup.scroll_delay ? 3 : 0);
9460 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9462 /* actual player has left the screen -- scroll in that direction */
9463 if (jx != old_jx) /* player has moved horizontally */
9464 scroll_x += (jx - old_jx);
9465 else /* player has moved vertically */
9466 scroll_y += (jy - old_jy);
9470 if (jx != old_jx) /* player has moved horizontally */
9472 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9473 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9474 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9476 /* don't scroll over playfield boundaries */
9477 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9478 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9480 /* don't scroll more than one field at a time */
9481 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9483 /* don't scroll against the player's moving direction */
9484 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9485 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9486 scroll_x = old_scroll_x;
9488 else /* player has moved vertically */
9490 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9491 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9492 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9494 /* don't scroll over playfield boundaries */
9495 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9496 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9498 /* don't scroll more than one field at a time */
9499 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9501 /* don't scroll against the player's moving direction */
9502 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9503 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9504 scroll_y = old_scroll_y;
9508 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9510 if (!options.network && !AllPlayersInVisibleScreen())
9512 scroll_x = old_scroll_x;
9513 scroll_y = old_scroll_y;
9517 ScrollScreen(player, SCROLL_INIT);
9518 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9525 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9527 if (!(moved & MF_MOVING) && !player->is_pushing)
9532 player->StepFrame = 0;
9534 if (moved & MF_MOVING)
9536 if (old_jx != jx && old_jy == jy)
9537 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9538 else if (old_jx == jx && old_jy != jy)
9539 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9541 DrawLevelField(jx, jy); /* for "crumbled sand" */
9543 player->last_move_dir = player->MovDir;
9544 player->is_moving = TRUE;
9546 player->is_snapping = FALSE;
9550 player->is_switching = FALSE;
9553 player->is_dropping = FALSE;
9557 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9560 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9563 int move_direction = player->MovDir;
9565 int enter_side = MV_DIR_OPPOSITE(move_direction);
9566 int leave_side = move_direction;
9568 static int trigger_sides[4][2] =
9570 /* enter side leave side */
9571 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9572 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9573 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9574 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9576 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9577 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9579 int old_element = Feld[old_jx][old_jy];
9580 int new_element = Feld[jx][jy];
9583 /* !!! TEST ONLY !!! */
9584 if (IS_CUSTOM_ELEMENT(old_element))
9585 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9587 player->index_bit, leave_side);
9589 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9591 player->index_bit, leave_side);
9593 if (IS_CUSTOM_ELEMENT(new_element))
9594 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9595 player->index_bit, enter_side);
9597 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9598 CE_OTHER_GETS_ENTERED,
9599 player->index_bit, enter_side);
9609 CheckGravityMovementWhenNotMoving(player);
9612 player->last_move_dir = MV_NO_MOVING;
9614 player->is_moving = FALSE;
9617 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9619 TestIfHeroTouchesBadThing(jx, jy);
9620 TestIfPlayerTouchesCustomElement(jx, jy);
9623 if (!player->active)
9629 void ScrollPlayer(struct PlayerInfo *player, int mode)
9631 int jx = player->jx, jy = player->jy;
9632 int last_jx = player->last_jx, last_jy = player->last_jy;
9633 int move_stepsize = TILEX / player->move_delay_value;
9635 if (!player->active || !player->MovPos)
9638 if (mode == SCROLL_INIT)
9640 player->actual_frame_counter = FrameCounter;
9641 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9643 if (Feld[last_jx][last_jy] == EL_EMPTY)
9644 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9652 else if (!FrameReached(&player->actual_frame_counter, 1))
9655 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9656 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9658 if (!player->block_last_field &&
9659 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9660 Feld[last_jx][last_jy] = EL_EMPTY;
9662 /* before DrawPlayer() to draw correct player graphic for this case */
9663 if (player->MovPos == 0)
9664 CheckGravityMovement(player);
9667 DrawPlayer(player); /* needed here only to cleanup last field */
9670 if (player->MovPos == 0) /* player reached destination field */
9673 if (player->move_delay_reset_counter > 0)
9675 player->move_delay_reset_counter--;
9677 if (player->move_delay_reset_counter == 0)
9679 /* continue with normal speed after quickly moving through gate */
9680 HALVE_PLAYER_SPEED(player);
9682 /* be able to make the next move without delay */
9683 player->move_delay = 0;
9687 if (IS_PASSABLE(Feld[last_jx][last_jy]))
9689 /* continue with normal speed after quickly moving through gate */
9690 HALVE_PLAYER_SPEED(player);
9692 /* be able to make the next move without delay */
9693 player->move_delay = 0;
9697 if (player->block_last_field &&
9698 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9699 Feld[last_jx][last_jy] = EL_EMPTY;
9701 player->last_jx = jx;
9702 player->last_jy = jy;
9704 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9705 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9706 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9708 DrawPlayer(player); /* needed here only to cleanup last field */
9711 if (local_player->friends_still_needed == 0 ||
9712 IS_SP_ELEMENT(Feld[jx][jy]))
9713 player->LevelSolved = player->GameOver = TRUE;
9717 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9718 /* this breaks one level: "machine", level 000 */
9720 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9723 int move_direction = player->MovDir;
9725 int enter_side = MV_DIR_OPPOSITE(move_direction);
9726 int leave_side = move_direction;
9728 static int trigger_sides[4][2] =
9730 /* enter side leave side */
9731 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9732 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9733 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9734 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9736 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9737 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9739 int old_jx = last_jx;
9740 int old_jy = last_jy;
9741 int old_element = Feld[old_jx][old_jy];
9742 int new_element = Feld[jx][jy];
9745 /* !!! TEST ONLY !!! */
9746 if (IS_CUSTOM_ELEMENT(old_element))
9747 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9749 player->index_bit, leave_side);
9751 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9753 player->index_bit, leave_side);
9755 if (IS_CUSTOM_ELEMENT(new_element))
9756 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9757 player->index_bit, enter_side);
9759 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9760 CE_OTHER_GETS_ENTERED,
9761 player->index_bit, enter_side);
9767 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9769 TestIfHeroTouchesBadThing(jx, jy);
9770 TestIfPlayerTouchesCustomElement(jx, jy);
9772 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9775 if (!player->active)
9779 if (level.use_step_counter)
9789 if (TimeLeft <= 10 && setup.time_limit)
9790 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9792 DrawGameValue_Time(TimeLeft);
9794 if (!TimeLeft && setup.time_limit)
9795 for (i = 0; i < MAX_PLAYERS; i++)
9796 KillHero(&stored_player[i]);
9798 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9799 DrawGameValue_Time(TimePlayed);
9802 if (tape.single_step && tape.recording && !tape.pausing &&
9803 !player->programmed_action)
9804 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9808 void ScrollScreen(struct PlayerInfo *player, int mode)
9810 static unsigned long screen_frame_counter = 0;
9812 if (mode == SCROLL_INIT)
9814 /* set scrolling step size according to actual player's moving speed */
9815 ScrollStepSize = TILEX / player->move_delay_value;
9817 screen_frame_counter = FrameCounter;
9818 ScreenMovDir = player->MovDir;
9819 ScreenMovPos = player->MovPos;
9820 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9823 else if (!FrameReached(&screen_frame_counter, 1))
9828 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9829 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9830 redraw_mask |= REDRAW_FIELD;
9833 ScreenMovDir = MV_NO_MOVING;
9836 void TestIfPlayerTouchesCustomElement(int x, int y)
9838 static int xy[4][2] =
9845 static int trigger_sides[4][2] =
9847 /* center side border side */
9848 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9849 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9850 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9851 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9853 static int touch_dir[4] =
9860 int center_element = Feld[x][y]; /* should always be non-moving! */
9863 for (i = 0; i < NUM_DIRECTIONS; i++)
9865 int xx = x + xy[i][0];
9866 int yy = y + xy[i][1];
9867 int center_side = trigger_sides[i][0];
9868 int border_side = trigger_sides[i][1];
9871 if (!IN_LEV_FIELD(xx, yy))
9874 if (IS_PLAYER(x, y))
9876 struct PlayerInfo *player = PLAYERINFO(x, y);
9878 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9879 border_element = Feld[xx][yy]; /* may be moving! */
9880 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9881 border_element = Feld[xx][yy];
9882 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9883 border_element = MovingOrBlocked2Element(xx, yy);
9885 continue; /* center and border element do not touch */
9888 /* !!! TEST ONLY !!! */
9889 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9890 player->index_bit, border_side);
9891 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9892 CE_OTHER_GETS_TOUCHED,
9893 player->index_bit, border_side);
9895 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9896 CE_OTHER_GETS_TOUCHED,
9897 player->index_bit, border_side);
9898 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9899 player->index_bit, border_side);
9902 else if (IS_PLAYER(xx, yy))
9904 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9906 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9908 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9909 continue; /* center and border element do not touch */
9913 /* !!! TEST ONLY !!! */
9914 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9915 player->index_bit, center_side);
9916 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9917 CE_OTHER_GETS_TOUCHED,
9918 player->index_bit, center_side);
9920 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9921 CE_OTHER_GETS_TOUCHED,
9922 player->index_bit, center_side);
9923 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9924 player->index_bit, center_side);
9932 void TestIfElementTouchesCustomElement(int x, int y)
9934 static int xy[4][2] =
9941 static int trigger_sides[4][2] =
9943 /* center side border side */
9944 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9945 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9946 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9947 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9949 static int touch_dir[4] =
9956 boolean change_center_element = FALSE;
9957 int center_element_change_page = 0;
9958 int center_element = Feld[x][y]; /* should always be non-moving! */
9959 int border_trigger_element;
9962 for (i = 0; i < NUM_DIRECTIONS; i++)
9964 int xx = x + xy[i][0];
9965 int yy = y + xy[i][1];
9966 int center_side = trigger_sides[i][0];
9967 int border_side = trigger_sides[i][1];
9970 if (!IN_LEV_FIELD(xx, yy))
9973 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9974 border_element = Feld[xx][yy]; /* may be moving! */
9975 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9976 border_element = Feld[xx][yy];
9977 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9978 border_element = MovingOrBlocked2Element(xx, yy);
9980 continue; /* center and border element do not touch */
9982 /* check for change of center element (but change it only once) */
9983 if (IS_CUSTOM_ELEMENT(center_element) &&
9984 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9985 !change_center_element)
9987 for (j = 0; j < element_info[center_element].num_change_pages; j++)
9989 struct ElementChangeInfo *change =
9990 &element_info[center_element].change_page[j];
9992 if (change->can_change &&
9993 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9994 change->trigger_side & border_side &&
9996 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9998 change->trigger_element == border_element
10002 change_center_element = TRUE;
10003 center_element_change_page = j;
10004 border_trigger_element = border_element;
10011 /* check for change of border element */
10012 if (IS_CUSTOM_ELEMENT(border_element) &&
10013 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10015 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10017 struct ElementChangeInfo *change =
10018 &element_info[border_element].change_page[j];
10020 if (change->can_change &&
10021 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10022 change->trigger_side & center_side &&
10024 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10026 change->trigger_element == center_element
10031 printf("::: border_element %d, %d\n", x, y);
10034 CheckElementChangeByPage(xx, yy, border_element, center_element,
10035 CE_OTHER_IS_TOUCHING, j);
10042 if (change_center_element)
10045 printf("::: center_element %d, %d\n", x, y);
10048 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10049 CE_OTHER_IS_TOUCHING, center_element_change_page);
10053 void TestIfElementHitsCustomElement(int x, int y, int direction)
10055 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10056 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10057 int hitx = x + dx, hity = y + dy;
10058 int hitting_element = Feld[x][y];
10059 int touched_element;
10061 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10062 !IS_FREE(hitx, hity) &&
10063 (!IS_MOVING(hitx, hity) ||
10064 MovDir[hitx][hity] != direction ||
10065 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10068 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10072 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10076 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10077 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10079 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10080 CE_HITTING_SOMETHING, direction);
10082 if (IN_LEV_FIELD(hitx, hity))
10084 int opposite_direction = MV_DIR_OPPOSITE(direction);
10085 int hitting_side = direction;
10086 int touched_side = opposite_direction;
10088 int touched_element = MovingOrBlocked2Element(hitx, hity);
10091 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10092 MovDir[hitx][hity] != direction ||
10093 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10102 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10103 CE_HIT_BY_SOMETHING, opposite_direction);
10105 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10106 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10108 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10110 struct ElementChangeInfo *change =
10111 &element_info[hitting_element].change_page[i];
10113 if (change->can_change &&
10114 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10115 change->trigger_side & touched_side &&
10118 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10120 change->trigger_element == touched_element
10124 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10125 CE_OTHER_IS_HITTING, i);
10131 if (IS_CUSTOM_ELEMENT(touched_element) &&
10132 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10134 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10136 struct ElementChangeInfo *change =
10137 &element_info[touched_element].change_page[i];
10139 if (change->can_change &&
10140 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10141 change->trigger_side & hitting_side &&
10143 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10145 change->trigger_element == hitting_element
10149 CheckElementChangeByPage(hitx, hity, touched_element,
10150 hitting_element, CE_OTHER_GETS_HIT, i);
10160 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10162 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10163 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10164 int hitx = x + dx, hity = y + dy;
10165 int hitting_element = Feld[x][y];
10166 int touched_element;
10168 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10169 !IS_FREE(hitx, hity) &&
10170 (!IS_MOVING(hitx, hity) ||
10171 MovDir[hitx][hity] != direction ||
10172 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10175 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10179 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10183 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10184 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10186 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10187 EP_CAN_SMASH_EVERYTHING, direction);
10189 if (IN_LEV_FIELD(hitx, hity))
10191 int opposite_direction = MV_DIR_OPPOSITE(direction);
10192 int hitting_side = direction;
10193 int touched_side = opposite_direction;
10195 int touched_element = MovingOrBlocked2Element(hitx, hity);
10198 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10199 MovDir[hitx][hity] != direction ||
10200 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10209 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10210 CE_SMASHED_BY_SOMETHING, opposite_direction);
10212 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10213 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10215 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10217 struct ElementChangeInfo *change =
10218 &element_info[hitting_element].change_page[i];
10220 if (change->can_change &&
10221 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10222 change->trigger_side & touched_side &&
10225 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10227 change->trigger_element == touched_element
10231 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10232 CE_OTHER_IS_SMASHING, i);
10238 if (IS_CUSTOM_ELEMENT(touched_element) &&
10239 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10241 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10243 struct ElementChangeInfo *change =
10244 &element_info[touched_element].change_page[i];
10246 if (change->can_change &&
10247 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10248 change->trigger_side & hitting_side &&
10250 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10252 change->trigger_element == hitting_element
10256 CheckElementChangeByPage(hitx, hity, touched_element,
10257 hitting_element, CE_OTHER_GETS_SMASHED,i);
10267 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10269 int i, kill_x = -1, kill_y = -1;
10270 int bad_element = -1;
10271 static int test_xy[4][2] =
10278 static int test_dir[4] =
10286 for (i = 0; i < NUM_DIRECTIONS; i++)
10288 int test_x, test_y, test_move_dir, test_element;
10290 test_x = good_x + test_xy[i][0];
10291 test_y = good_y + test_xy[i][1];
10293 if (!IN_LEV_FIELD(test_x, test_y))
10297 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10300 test_element = Feld[test_x][test_y];
10302 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10305 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10306 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10308 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10309 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10313 bad_element = test_element;
10319 if (kill_x != -1 || kill_y != -1)
10321 if (IS_PLAYER(good_x, good_y))
10323 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10326 if (player->shield_deadly_time_left > 0 &&
10327 !IS_INDESTRUCTIBLE(bad_element))
10328 Bang(kill_x, kill_y);
10329 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10332 if (player->shield_deadly_time_left > 0)
10333 Bang(kill_x, kill_y);
10334 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10339 Bang(good_x, good_y);
10343 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10345 int i, kill_x = -1, kill_y = -1;
10346 int bad_element = Feld[bad_x][bad_y];
10347 static int test_xy[4][2] =
10354 static int touch_dir[4] =
10356 MV_LEFT | MV_RIGHT,
10361 static int test_dir[4] =
10369 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10372 for (i = 0; i < NUM_DIRECTIONS; i++)
10374 int test_x, test_y, test_move_dir, test_element;
10376 test_x = bad_x + test_xy[i][0];
10377 test_y = bad_y + test_xy[i][1];
10378 if (!IN_LEV_FIELD(test_x, test_y))
10382 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10384 test_element = Feld[test_x][test_y];
10386 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10387 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10389 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10390 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10392 /* good thing is player or penguin that does not move away */
10393 if (IS_PLAYER(test_x, test_y))
10395 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10397 if (bad_element == EL_ROBOT && player->is_moving)
10398 continue; /* robot does not kill player if he is moving */
10400 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10402 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10403 continue; /* center and border element do not touch */
10410 else if (test_element == EL_PENGUIN)
10419 if (kill_x != -1 || kill_y != -1)
10421 if (IS_PLAYER(kill_x, kill_y))
10423 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10426 if (player->shield_deadly_time_left > 0 &&
10427 !IS_INDESTRUCTIBLE(bad_element))
10428 Bang(bad_x, bad_y);
10429 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10432 if (player->shield_deadly_time_left > 0)
10433 Bang(bad_x, bad_y);
10434 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10439 Bang(kill_x, kill_y);
10443 void TestIfHeroTouchesBadThing(int x, int y)
10445 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10448 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10450 TestIfGoodThingHitsBadThing(x, y, move_dir);
10453 void TestIfBadThingTouchesHero(int x, int y)
10455 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10458 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10460 TestIfBadThingHitsGoodThing(x, y, move_dir);
10463 void TestIfFriendTouchesBadThing(int x, int y)
10465 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10468 void TestIfBadThingTouchesFriend(int x, int y)
10470 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10473 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10475 int i, kill_x = bad_x, kill_y = bad_y;
10476 static int xy[4][2] =
10484 for (i = 0; i < NUM_DIRECTIONS; i++)
10488 x = bad_x + xy[i][0];
10489 y = bad_y + xy[i][1];
10490 if (!IN_LEV_FIELD(x, y))
10493 element = Feld[x][y];
10494 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10495 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10503 if (kill_x != bad_x || kill_y != bad_y)
10504 Bang(bad_x, bad_y);
10507 void KillHero(struct PlayerInfo *player)
10509 int jx = player->jx, jy = player->jy;
10511 if (!player->active)
10514 /* remove accessible field at the player's position */
10515 Feld[jx][jy] = EL_EMPTY;
10517 /* deactivate shield (else Bang()/Explode() would not work right) */
10518 player->shield_normal_time_left = 0;
10519 player->shield_deadly_time_left = 0;
10525 static void KillHeroUnlessEnemyProtected(int x, int y)
10527 if (!PLAYER_ENEMY_PROTECTED(x, y))
10528 KillHero(PLAYERINFO(x, y));
10531 static void KillHeroUnlessExplosionProtected(int x, int y)
10533 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10534 KillHero(PLAYERINFO(x, y));
10537 void BuryHero(struct PlayerInfo *player)
10539 int jx = player->jx, jy = player->jy;
10541 if (!player->active)
10545 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10547 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10549 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10551 player->GameOver = TRUE;
10552 RemoveHero(player);
10555 void RemoveHero(struct PlayerInfo *player)
10557 int jx = player->jx, jy = player->jy;
10558 int i, found = FALSE;
10560 player->present = FALSE;
10561 player->active = FALSE;
10563 if (!ExplodeField[jx][jy])
10564 StorePlayer[jx][jy] = 0;
10566 for (i = 0; i < MAX_PLAYERS; i++)
10567 if (stored_player[i].active)
10571 AllPlayersGone = TRUE;
10578 =============================================================================
10579 checkDiagonalPushing()
10580 -----------------------------------------------------------------------------
10581 check if diagonal input device direction results in pushing of object
10582 (by checking if the alternative direction is walkable, diggable, ...)
10583 =============================================================================
10586 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10587 int x, int y, int real_dx, int real_dy)
10589 int jx, jy, dx, dy, xx, yy;
10591 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10594 /* diagonal direction: check alternative direction */
10599 xx = jx + (dx == 0 ? real_dx : 0);
10600 yy = jy + (dy == 0 ? real_dy : 0);
10602 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10606 =============================================================================
10608 -----------------------------------------------------------------------------
10609 x, y: field next to player (non-diagonal) to try to dig to
10610 real_dx, real_dy: direction as read from input device (can be diagonal)
10611 =============================================================================
10614 int DigField(struct PlayerInfo *player,
10615 int oldx, int oldy, int x, int y,
10616 int real_dx, int real_dy, int mode)
10619 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10621 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10622 boolean player_was_pushing = player->is_pushing;
10623 int jx = oldx, jy = oldy;
10624 int dx = x - jx, dy = y - jy;
10625 int nextx = x + dx, nexty = y + dy;
10626 int move_direction = (dx == -1 ? MV_LEFT :
10627 dx == +1 ? MV_RIGHT :
10629 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10630 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10632 int dig_side = MV_DIR_OPPOSITE(move_direction);
10634 static int trigger_sides[4] =
10636 CH_SIDE_RIGHT, /* moving left */
10637 CH_SIDE_LEFT, /* moving right */
10638 CH_SIDE_BOTTOM, /* moving up */
10639 CH_SIDE_TOP, /* moving down */
10641 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10643 int old_element = Feld[jx][jy];
10646 if (is_player) /* function can also be called by EL_PENGUIN */
10648 if (player->MovPos == 0)
10650 player->is_digging = FALSE;
10651 player->is_collecting = FALSE;
10654 if (player->MovPos == 0) /* last pushing move finished */
10655 player->is_pushing = FALSE;
10657 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10659 player->is_switching = FALSE;
10660 player->push_delay = 0;
10662 return MF_NO_ACTION;
10666 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10667 return MF_NO_ACTION;
10672 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10674 if (IS_TUBE(Feld[jx][jy]) ||
10675 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10679 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10680 int tube_leave_directions[][2] =
10682 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10683 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10684 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10685 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
10686 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
10687 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
10688 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
10689 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
10690 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
10691 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
10692 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
10693 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10696 while (tube_leave_directions[i][0] != tube_element)
10699 if (tube_leave_directions[i][0] == -1) /* should not happen */
10703 if (!(tube_leave_directions[i][1] & move_direction))
10704 return MF_NO_ACTION; /* tube has no opening in this direction */
10709 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10710 old_element = Back[jx][jy];
10714 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10715 return MF_NO_ACTION; /* field has no opening in this direction */
10717 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10718 return MF_NO_ACTION; /* field has no opening in this direction */
10720 element = Feld[x][y];
10722 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
10723 return MF_NO_ACTION;
10725 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10726 game.engine_version >= VERSION_IDENT(2,2,0,0))
10727 return MF_NO_ACTION;
10730 if (game.gravity && is_player && !player->is_auto_moving &&
10731 canFallDown(player) && move_direction != MV_DOWN &&
10732 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10733 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10737 if (element == EL_EMPTY_SPACE &&
10738 game.gravity && !player->is_auto_moving &&
10739 canFallDown(player) && move_direction != MV_DOWN)
10740 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10746 case EL_SP_PORT_LEFT:
10747 case EL_SP_PORT_RIGHT:
10748 case EL_SP_PORT_UP:
10749 case EL_SP_PORT_DOWN:
10750 case EL_SP_PORT_HORIZONTAL:
10751 case EL_SP_PORT_VERTICAL:
10752 case EL_SP_PORT_ANY:
10753 case EL_SP_GRAVITY_PORT_LEFT:
10754 case EL_SP_GRAVITY_PORT_RIGHT:
10755 case EL_SP_GRAVITY_PORT_UP:
10756 case EL_SP_GRAVITY_PORT_DOWN:
10758 if (!canEnterSupaplexPort(x, y, dx, dy))
10759 return MF_NO_ACTION;
10762 element != EL_SP_PORT_LEFT &&
10763 element != EL_SP_GRAVITY_PORT_LEFT &&
10764 element != EL_SP_PORT_HORIZONTAL &&
10765 element != EL_SP_PORT_ANY) ||
10767 element != EL_SP_PORT_RIGHT &&
10768 element != EL_SP_GRAVITY_PORT_RIGHT &&
10769 element != EL_SP_PORT_HORIZONTAL &&
10770 element != EL_SP_PORT_ANY) ||
10772 element != EL_SP_PORT_UP &&
10773 element != EL_SP_GRAVITY_PORT_UP &&
10774 element != EL_SP_PORT_VERTICAL &&
10775 element != EL_SP_PORT_ANY) ||
10777 element != EL_SP_PORT_DOWN &&
10778 element != EL_SP_GRAVITY_PORT_DOWN &&
10779 element != EL_SP_PORT_VERTICAL &&
10780 element != EL_SP_PORT_ANY) ||
10781 !IN_LEV_FIELD(nextx, nexty) ||
10782 !IS_FREE(nextx, nexty))
10783 return MF_NO_ACTION;
10786 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10787 element == EL_SP_GRAVITY_PORT_RIGHT ||
10788 element == EL_SP_GRAVITY_PORT_UP ||
10789 element == EL_SP_GRAVITY_PORT_DOWN)
10790 game.gravity = !game.gravity;
10792 /* automatically move to the next field with double speed */
10793 player->programmed_action = move_direction;
10795 if (player->move_delay_reset_counter == 0)
10797 player->move_delay_reset_counter = 2; /* two double speed steps */
10799 DOUBLE_PLAYER_SPEED(player);
10802 player->move_delay_reset_counter = 2;
10804 DOUBLE_PLAYER_SPEED(player);
10808 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10811 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10817 case EL_TUBE_VERTICAL:
10818 case EL_TUBE_HORIZONTAL:
10819 case EL_TUBE_VERTICAL_LEFT:
10820 case EL_TUBE_VERTICAL_RIGHT:
10821 case EL_TUBE_HORIZONTAL_UP:
10822 case EL_TUBE_HORIZONTAL_DOWN:
10823 case EL_TUBE_LEFT_UP:
10824 case EL_TUBE_LEFT_DOWN:
10825 case EL_TUBE_RIGHT_UP:
10826 case EL_TUBE_RIGHT_DOWN:
10829 int tube_enter_directions[][2] =
10831 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10832 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10833 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10834 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
10835 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
10836 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
10837 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
10838 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
10839 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
10840 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
10841 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
10842 { -1, MV_NO_MOVING }
10845 while (tube_enter_directions[i][0] != element)
10848 if (tube_enter_directions[i][0] == -1) /* should not happen */
10852 if (!(tube_enter_directions[i][1] & move_direction))
10853 return MF_NO_ACTION; /* tube has no opening in this direction */
10855 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10863 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10865 if (IS_WALKABLE(element))
10868 int sound_element = SND_ELEMENT(element);
10869 int sound_action = ACTION_WALKING;
10872 if (!ACCESS_FROM(element, opposite_direction))
10873 return MF_NO_ACTION; /* field not accessible from this direction */
10877 if (element == EL_EMPTY_SPACE &&
10878 game.gravity && !player->is_auto_moving &&
10879 canFallDown(player) && move_direction != MV_DOWN)
10880 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10883 if (IS_GATE(element))
10885 if (!player->key[element - EL_GATE_1])
10886 return MF_NO_ACTION;
10888 else if (IS_GATE_GRAY(element))
10890 if (!player->key[element - EL_GATE_1_GRAY])
10891 return MF_NO_ACTION;
10893 else if (element == EL_EXIT_OPEN ||
10894 element == EL_SP_EXIT_OPEN ||
10895 element == EL_SP_EXIT_OPENING)
10897 sound_action = ACTION_PASSING; /* player is passing exit */
10899 else if (element == EL_EMPTY)
10901 sound_action = ACTION_MOVING; /* nothing to walk on */
10904 /* play sound from background or player, whatever is available */
10905 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
10906 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
10908 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10913 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
10915 else if (IS_PASSABLE(element))
10919 if (!canPassField(x, y, move_direction))
10920 return MF_NO_ACTION;
10925 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
10926 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
10927 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
10928 return MF_NO_ACTION;
10930 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10931 return MF_NO_ACTION;
10936 if (!ACCESS_FROM(element, opposite_direction))
10937 return MF_NO_ACTION; /* field not accessible from this direction */
10939 if (IS_CUSTOM_ELEMENT(element) &&
10940 !ACCESS_FROM(element, opposite_direction))
10941 return MF_NO_ACTION; /* field not accessible from this direction */
10945 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10946 return MF_NO_ACTION;
10951 if (IS_EM_GATE(element))
10953 if (!player->key[element - EL_EM_GATE_1])
10954 return MF_NO_ACTION;
10956 else if (IS_EM_GATE_GRAY(element))
10958 if (!player->key[element - EL_EM_GATE_1_GRAY])
10959 return MF_NO_ACTION;
10961 else if (IS_SP_PORT(element))
10963 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10964 element == EL_SP_GRAVITY_PORT_RIGHT ||
10965 element == EL_SP_GRAVITY_PORT_UP ||
10966 element == EL_SP_GRAVITY_PORT_DOWN)
10967 game.gravity = !game.gravity;
10970 /* automatically move to the next field with double speed */
10971 player->programmed_action = move_direction;
10973 if (player->move_delay_reset_counter == 0)
10975 player->move_delay_reset_counter = 2; /* two double speed steps */
10977 DOUBLE_PLAYER_SPEED(player);
10980 player->move_delay_reset_counter = 2;
10982 DOUBLE_PLAYER_SPEED(player);
10985 PlayLevelSoundAction(x, y, ACTION_PASSING);
10989 else if (IS_DIGGABLE(element))
10993 if (mode != DF_SNAP)
10996 GfxElement[x][y] = GFX_ELEMENT(element);
10999 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11001 player->is_digging = TRUE;
11004 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11006 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11007 player->index_bit, dig_side);
11010 if (mode == DF_SNAP)
11011 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11016 else if (IS_COLLECTIBLE(element))
11020 if (is_player && mode != DF_SNAP)
11022 GfxElement[x][y] = element;
11023 player->is_collecting = TRUE;
11026 if (element == EL_SPEED_PILL)
11027 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11028 else if (element == EL_EXTRA_TIME && level.time > 0)
11031 DrawGameValue_Time(TimeLeft);
11033 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11035 player->shield_normal_time_left += 10;
11036 if (element == EL_SHIELD_DEADLY)
11037 player->shield_deadly_time_left += 10;
11039 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11041 if (player->inventory_size < MAX_INVENTORY_SIZE)
11042 player->inventory_element[player->inventory_size++] = element;
11044 DrawGameValue_Dynamite(local_player->inventory_size);
11046 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11048 player->dynabomb_count++;
11049 player->dynabombs_left++;
11051 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11053 player->dynabomb_size++;
11055 else if (element == EL_DYNABOMB_INCREASE_POWER)
11057 player->dynabomb_xl = TRUE;
11059 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
11060 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
11062 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
11063 element - EL_KEY_1 : element - EL_EM_KEY_1);
11065 player->key[key_nr] = TRUE;
11067 DrawGameValue_Keys(player);
11069 redraw_mask |= REDRAW_DOOR_1;
11071 else if (IS_ENVELOPE(element))
11074 player->show_envelope = element;
11076 ShowEnvelope(element - EL_ENVELOPE_1);
11079 else if (IS_DROPPABLE(element) ||
11080 IS_THROWABLE(element)) /* can be collected and dropped */
11084 if (element_info[element].collect_count == 0)
11085 player->inventory_infinite_element = element;
11087 for (i = 0; i < element_info[element].collect_count; i++)
11088 if (player->inventory_size < MAX_INVENTORY_SIZE)
11089 player->inventory_element[player->inventory_size++] = element;
11091 DrawGameValue_Dynamite(local_player->inventory_size);
11093 else if (element_info[element].collect_count > 0)
11095 local_player->gems_still_needed -=
11096 element_info[element].collect_count;
11097 if (local_player->gems_still_needed < 0)
11098 local_player->gems_still_needed = 0;
11100 DrawGameValue_Emeralds(local_player->gems_still_needed);
11103 RaiseScoreElement(element);
11104 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11107 CheckTriggeredElementChangeByPlayer(x, y, element,
11108 CE_OTHER_GETS_COLLECTED,
11109 player->index_bit, dig_side);
11112 if (mode == DF_SNAP)
11113 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11118 else if (IS_PUSHABLE(element))
11120 if (mode == DF_SNAP && element != EL_BD_ROCK)
11121 return MF_NO_ACTION;
11123 if (CAN_FALL(element) && dy)
11124 return MF_NO_ACTION;
11126 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11127 !(element == EL_SPRING && level.use_spring_bug))
11128 return MF_NO_ACTION;
11131 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11132 ((move_direction & MV_VERTICAL &&
11133 ((element_info[element].move_pattern & MV_LEFT &&
11134 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11135 (element_info[element].move_pattern & MV_RIGHT &&
11136 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11137 (move_direction & MV_HORIZONTAL &&
11138 ((element_info[element].move_pattern & MV_UP &&
11139 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11140 (element_info[element].move_pattern & MV_DOWN &&
11141 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11142 return MF_NO_ACTION;
11146 /* do not push elements already moving away faster than player */
11147 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11148 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11149 return MF_NO_ACTION;
11151 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11152 return MF_NO_ACTION;
11158 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11160 if (player->push_delay_value == -1 || !player_was_pushing)
11161 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11163 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11165 if (player->push_delay_value == -1)
11166 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11169 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11171 if (player->push_delay_value == -1 || !player_was_pushing)
11172 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11175 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11177 if (!player->is_pushing)
11178 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11182 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11183 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11184 !player_is_pushing))
11185 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11188 if (!player->is_pushing &&
11189 game.engine_version >= VERSION_IDENT(2,2,0,7))
11190 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11194 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11195 player->push_delay, player->push_delay_value,
11196 FrameCounter, game.engine_version,
11197 player_was_pushing, player->is_pushing,
11198 element, element_info[element].token_name,
11199 GET_NEW_PUSH_DELAY(element));
11202 player->is_pushing = TRUE;
11204 if (!(IN_LEV_FIELD(nextx, nexty) &&
11205 (IS_FREE(nextx, nexty) ||
11206 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11207 IS_SB_ELEMENT(element)))))
11208 return MF_NO_ACTION;
11210 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11211 return MF_NO_ACTION;
11213 if (player->push_delay == 0) /* new pushing; restart delay */
11214 player->push_delay = FrameCounter;
11216 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11217 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11218 element != EL_SPRING && element != EL_BALLOON)
11220 /* make sure that there is no move delay before next try to push */
11221 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11222 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11224 return MF_NO_ACTION;
11228 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11231 if (IS_SB_ELEMENT(element))
11233 if (element == EL_SOKOBAN_FIELD_FULL)
11235 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11236 local_player->sokobanfields_still_needed++;
11239 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11241 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11242 local_player->sokobanfields_still_needed--;
11245 Feld[x][y] = EL_SOKOBAN_OBJECT;
11247 if (Back[x][y] == Back[nextx][nexty])
11248 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11249 else if (Back[x][y] != 0)
11250 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11253 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11256 if (local_player->sokobanfields_still_needed == 0 &&
11257 game.emulation == EMU_SOKOBAN)
11259 player->LevelSolved = player->GameOver = TRUE;
11260 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11264 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11266 InitMovingField(x, y, move_direction);
11267 GfxAction[x][y] = ACTION_PUSHING;
11269 if (mode == DF_SNAP)
11270 ContinueMoving(x, y);
11272 MovPos[x][y] = (dx != 0 ? dx : dy);
11274 Pushed[x][y] = TRUE;
11275 Pushed[nextx][nexty] = TRUE;
11277 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11278 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11280 player->push_delay_value = -1; /* get new value later */
11283 /* check for element change _after_ element has been pushed! */
11287 /* !!! TEST ONLY !!! */
11288 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11289 player->index_bit, dig_side);
11290 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11291 player->index_bit, dig_side);
11293 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11294 player->index_bit, dig_side);
11295 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11296 player->index_bit, dig_side);
11302 else if (IS_SWITCHABLE(element))
11304 if (PLAYER_SWITCHING(player, x, y))
11306 CheckTriggeredElementChangeByPlayer(x,y, element,
11307 CE_OTHER_GETS_PRESSED,
11308 player->index_bit, dig_side);
11313 player->is_switching = TRUE;
11314 player->switch_x = x;
11315 player->switch_y = y;
11317 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11319 if (element == EL_ROBOT_WHEEL)
11321 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11325 DrawLevelField(x, y);
11327 else if (element == EL_SP_TERMINAL)
11331 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11333 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11335 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11336 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11339 else if (IS_BELT_SWITCH(element))
11341 ToggleBeltSwitch(x, y);
11343 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11344 element == EL_SWITCHGATE_SWITCH_DOWN)
11346 ToggleSwitchgateSwitch(x, y);
11348 else if (element == EL_LIGHT_SWITCH ||
11349 element == EL_LIGHT_SWITCH_ACTIVE)
11351 ToggleLightSwitch(x, y);
11354 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11355 SND_LIGHT_SWITCH_ACTIVATING :
11356 SND_LIGHT_SWITCH_DEACTIVATING);
11359 else if (element == EL_TIMEGATE_SWITCH)
11361 ActivateTimegateSwitch(x, y);
11363 else if (element == EL_BALLOON_SWITCH_LEFT ||
11364 element == EL_BALLOON_SWITCH_RIGHT ||
11365 element == EL_BALLOON_SWITCH_UP ||
11366 element == EL_BALLOON_SWITCH_DOWN ||
11367 element == EL_BALLOON_SWITCH_ANY)
11369 if (element == EL_BALLOON_SWITCH_ANY)
11370 game.balloon_dir = move_direction;
11372 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11373 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11374 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11375 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11378 else if (element == EL_LAMP)
11380 Feld[x][y] = EL_LAMP_ACTIVE;
11381 local_player->lights_still_needed--;
11383 DrawLevelField(x, y);
11385 else if (element == EL_TIME_ORB_FULL)
11387 Feld[x][y] = EL_TIME_ORB_EMPTY;
11389 DrawGameValue_Time(TimeLeft);
11391 DrawLevelField(x, y);
11394 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11398 CheckTriggeredElementChangeByPlayer(x, y, element,
11399 CE_OTHER_IS_SWITCHING,
11400 player->index_bit, dig_side);
11402 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11403 player->index_bit, dig_side);
11409 if (!PLAYER_SWITCHING(player, x, y))
11411 player->is_switching = TRUE;
11412 player->switch_x = x;
11413 player->switch_y = y;
11416 /* !!! TEST ONLY !!! */
11417 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11418 player->index_bit, dig_side);
11419 CheckTriggeredElementChangeByPlayer(x, y, element,
11420 CE_OTHER_IS_SWITCHING,
11421 player->index_bit, dig_side);
11423 CheckTriggeredElementChangeByPlayer(x, y, element,
11424 CE_OTHER_IS_SWITCHING,
11425 player->index_bit, dig_side);
11426 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11427 player->index_bit, dig_side);
11432 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11433 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11434 player->index_bit, dig_side);
11435 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11436 player->index_bit, dig_side);
11438 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11439 player->index_bit, dig_side);
11440 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11441 player->index_bit, dig_side);
11445 return MF_NO_ACTION;
11448 player->push_delay = 0;
11450 if (Feld[x][y] != element) /* really digged/collected something */
11451 player->is_collecting = !player->is_digging;
11456 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11458 int jx = player->jx, jy = player->jy;
11459 int x = jx + dx, y = jy + dy;
11460 int snap_direction = (dx == -1 ? MV_LEFT :
11461 dx == +1 ? MV_RIGHT :
11463 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11466 if (player->MovPos != 0)
11469 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11473 if (!player->active || !IN_LEV_FIELD(x, y))
11481 if (player->MovPos == 0)
11482 player->is_pushing = FALSE;
11484 player->is_snapping = FALSE;
11486 if (player->MovPos == 0)
11488 player->is_moving = FALSE;
11489 player->is_digging = FALSE;
11490 player->is_collecting = FALSE;
11496 if (player->is_snapping)
11499 player->MovDir = snap_direction;
11502 if (player->MovPos == 0)
11505 player->is_moving = FALSE;
11506 player->is_digging = FALSE;
11507 player->is_collecting = FALSE;
11510 player->is_dropping = FALSE;
11512 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11515 player->is_snapping = TRUE;
11518 if (player->MovPos == 0)
11521 player->is_moving = FALSE;
11522 player->is_digging = FALSE;
11523 player->is_collecting = FALSE;
11527 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11528 DrawLevelField(player->last_jx, player->last_jy);
11531 DrawLevelField(x, y);
11540 boolean DropElement(struct PlayerInfo *player)
11542 int old_element, new_element;
11543 int dropx = player->jx, dropy = player->jy;
11544 int drop_direction = player->MovDir;
11546 int drop_side = drop_direction;
11548 static int trigger_sides[4] =
11550 CH_SIDE_LEFT, /* dropping left */
11551 CH_SIDE_RIGHT, /* dropping right */
11552 CH_SIDE_TOP, /* dropping up */
11553 CH_SIDE_BOTTOM, /* dropping down */
11555 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11557 int drop_element = (player->inventory_size > 0 ?
11558 player->inventory_element[player->inventory_size - 1] :
11559 player->inventory_infinite_element != EL_UNDEFINED ?
11560 player->inventory_infinite_element :
11561 player->dynabombs_left > 0 ?
11562 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11565 if (IS_THROWABLE(drop_element))
11567 dropx += GET_DX_FROM_DIR(drop_direction);
11568 dropy += GET_DY_FROM_DIR(drop_direction);
11570 if (!IN_LEV_FIELD(dropx, dropy))
11574 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11575 new_element = drop_element; /* default: no change when dropping */
11577 /* check if player is active, not moving and ready to drop */
11578 if (!player->active || player->MovPos || player->drop_delay > 0)
11581 /* check if player has anything that can be dropped */
11583 if (new_element == EL_UNDEFINED)
11586 if (player->inventory_size == 0 &&
11587 player->inventory_infinite_element == EL_UNDEFINED &&
11588 player->dynabombs_left == 0)
11592 /* check if anything can be dropped at the current position */
11593 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11596 /* collected custom elements can only be dropped on empty fields */
11598 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11601 if (player->inventory_size > 0 &&
11602 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11603 && old_element != EL_EMPTY)
11607 if (old_element != EL_EMPTY)
11608 Back[dropx][dropy] = old_element; /* store old element on this field */
11610 ResetGfxAnimation(dropx, dropy);
11611 ResetRandomAnimationValue(dropx, dropy);
11613 if (player->inventory_size > 0 ||
11614 player->inventory_infinite_element != EL_UNDEFINED)
11616 if (player->inventory_size > 0)
11618 player->inventory_size--;
11621 new_element = player->inventory_element[player->inventory_size];
11624 DrawGameValue_Dynamite(local_player->inventory_size);
11626 if (new_element == EL_DYNAMITE)
11627 new_element = EL_DYNAMITE_ACTIVE;
11628 else if (new_element == EL_SP_DISK_RED)
11629 new_element = EL_SP_DISK_RED_ACTIVE;
11632 Feld[dropx][dropy] = new_element;
11634 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11635 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11636 el2img(Feld[dropx][dropy]), 0);
11638 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11641 /* needed if previous element just changed to "empty" in the last frame */
11642 Changed[dropx][dropy] = 0; /* allow another change */
11646 /* !!! TEST ONLY !!! */
11647 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11648 player->index_bit, drop_side);
11649 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11650 CE_OTHER_GETS_DROPPED,
11651 player->index_bit, drop_side);
11653 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11654 CE_OTHER_GETS_DROPPED,
11655 player->index_bit, drop_side);
11656 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11657 player->index_bit, drop_side);
11660 TestIfElementTouchesCustomElement(dropx, dropy);
11662 else /* player is dropping a dyna bomb */
11664 player->dynabombs_left--;
11667 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
11670 Feld[dropx][dropy] = new_element;
11672 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11673 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11674 el2img(Feld[dropx][dropy]), 0);
11676 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11683 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11686 InitField_WithBug1(dropx, dropy, FALSE);
11688 InitField(dropx, dropy, FALSE);
11689 if (CAN_MOVE(Feld[dropx][dropy]))
11690 InitMovDir(dropx, dropy);
11694 new_element = Feld[dropx][dropy]; /* element might have changed */
11696 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11697 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11700 int move_stepsize = element_info[new_element].move_stepsize;
11702 int move_direction, nextx, nexty;
11704 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11705 MovDir[dropx][dropy] = drop_direction;
11707 move_direction = MovDir[dropx][dropy];
11708 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11709 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11712 Changed[dropx][dropy] = 0; /* allow another change */
11713 CheckCollision[dropx][dropy] = 2;
11716 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
11719 WasJustMoving[dropx][dropy] = 3;
11722 InitMovingField(dropx, dropy, move_direction);
11723 ContinueMoving(dropx, dropy);
11730 Changed[dropx][dropy] = 0; /* allow another change */
11733 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
11735 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
11736 CE_HITTING_SOMETHING, move_direction);
11744 player->drop_delay = 2 * TILEX / move_stepsize + 1;
11749 player->drop_delay = 8 + 8 + 8;
11753 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11758 player->is_dropping = TRUE;
11764 /* ------------------------------------------------------------------------- */
11765 /* game sound playing functions */
11766 /* ------------------------------------------------------------------------- */
11768 static int *loop_sound_frame = NULL;
11769 static int *loop_sound_volume = NULL;
11771 void InitPlayLevelSound()
11773 int num_sounds = getSoundListSize();
11775 checked_free(loop_sound_frame);
11776 checked_free(loop_sound_volume);
11778 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11779 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11782 static void PlayLevelSound(int x, int y, int nr)
11784 int sx = SCREENX(x), sy = SCREENY(y);
11785 int volume, stereo_position;
11786 int max_distance = 8;
11787 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11789 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11790 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11793 if (!IN_LEV_FIELD(x, y) ||
11794 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11795 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11798 volume = SOUND_MAX_VOLUME;
11800 if (!IN_SCR_FIELD(sx, sy))
11802 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11803 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11805 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11808 stereo_position = (SOUND_MAX_LEFT +
11809 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11810 (SCR_FIELDX + 2 * max_distance));
11812 if (IS_LOOP_SOUND(nr))
11814 /* This assures that quieter loop sounds do not overwrite louder ones,
11815 while restarting sound volume comparison with each new game frame. */
11817 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11820 loop_sound_volume[nr] = volume;
11821 loop_sound_frame[nr] = FrameCounter;
11824 PlaySoundExt(nr, volume, stereo_position, type);
11827 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11829 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11830 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11831 y < LEVELY(BY1) ? LEVELY(BY1) :
11832 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11836 static void PlayLevelSoundAction(int x, int y, int action)
11838 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11841 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11843 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11845 if (sound_effect != SND_UNDEFINED)
11846 PlayLevelSound(x, y, sound_effect);
11849 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11852 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11854 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11855 PlayLevelSound(x, y, sound_effect);
11858 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11860 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11862 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11863 PlayLevelSound(x, y, sound_effect);
11866 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11868 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11870 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11871 StopSound(sound_effect);
11874 static void PlayLevelMusic()
11876 if (levelset.music[level_nr] != MUS_UNDEFINED)
11877 PlayMusic(levelset.music[level_nr]); /* from config file */
11879 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11882 void RaiseScore(int value)
11884 local_player->score += value;
11886 DrawGameValue_Score(local_player->score);
11889 void RaiseScoreElement(int element)
11894 case EL_BD_DIAMOND:
11895 case EL_EMERALD_YELLOW:
11896 case EL_EMERALD_RED:
11897 case EL_EMERALD_PURPLE:
11898 case EL_SP_INFOTRON:
11899 RaiseScore(level.score[SC_EMERALD]);
11902 RaiseScore(level.score[SC_DIAMOND]);
11905 RaiseScore(level.score[SC_CRYSTAL]);
11908 RaiseScore(level.score[SC_PEARL]);
11911 case EL_BD_BUTTERFLY:
11912 case EL_SP_ELECTRON:
11913 RaiseScore(level.score[SC_BUG]);
11916 case EL_BD_FIREFLY:
11917 case EL_SP_SNIKSNAK:
11918 RaiseScore(level.score[SC_SPACESHIP]);
11921 case EL_DARK_YAMYAM:
11922 RaiseScore(level.score[SC_YAMYAM]);
11925 RaiseScore(level.score[SC_ROBOT]);
11928 RaiseScore(level.score[SC_PACMAN]);
11931 RaiseScore(level.score[SC_NUT]);
11934 case EL_SP_DISK_RED:
11935 case EL_DYNABOMB_INCREASE_NUMBER:
11936 case EL_DYNABOMB_INCREASE_SIZE:
11937 case EL_DYNABOMB_INCREASE_POWER:
11938 RaiseScore(level.score[SC_DYNAMITE]);
11940 case EL_SHIELD_NORMAL:
11941 case EL_SHIELD_DEADLY:
11942 RaiseScore(level.score[SC_SHIELD]);
11944 case EL_EXTRA_TIME:
11945 RaiseScore(level.score[SC_TIME_BONUS]);
11951 RaiseScore(level.score[SC_KEY]);
11954 RaiseScore(element_info[element].collect_score);
11959 void RequestQuitGame(boolean ask_if_really_quit)
11961 if (AllPlayersGone ||
11962 !ask_if_really_quit ||
11963 level_editor_test_game ||
11964 Request("Do you really want to quit the game ?",
11965 REQ_ASK | REQ_STAY_CLOSED))
11967 #if defined(NETWORK_AVALIABLE)
11968 if (options.network)
11969 SendToServer_StopPlaying();
11973 game_status = GAME_MODE_MAIN;
11981 if (tape.playing && tape.deactivate_display)
11982 TapeDeactivateDisplayOff(TRUE);
11985 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11988 if (tape.playing && tape.deactivate_display)
11989 TapeDeactivateDisplayOn();
11996 /* ---------- new game button stuff ---------------------------------------- */
11998 /* graphic position values for game buttons */
11999 #define GAME_BUTTON_XSIZE 30
12000 #define GAME_BUTTON_YSIZE 30
12001 #define GAME_BUTTON_XPOS 5
12002 #define GAME_BUTTON_YPOS 215
12003 #define SOUND_BUTTON_XPOS 5
12004 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12006 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12007 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12008 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12009 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12010 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12011 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12018 } gamebutton_info[NUM_GAME_BUTTONS] =
12021 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12026 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12027 GAME_CTRL_ID_PAUSE,
12031 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12036 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12037 SOUND_CTRL_ID_MUSIC,
12038 "background music on/off"
12041 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12042 SOUND_CTRL_ID_LOOPS,
12043 "sound loops on/off"
12046 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12047 SOUND_CTRL_ID_SIMPLE,
12048 "normal sounds on/off"
12052 void CreateGameButtons()
12056 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12058 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12059 struct GadgetInfo *gi;
12062 unsigned long event_mask;
12063 int gd_xoffset, gd_yoffset;
12064 int gd_x1, gd_x2, gd_y1, gd_y2;
12067 gd_xoffset = gamebutton_info[i].x;
12068 gd_yoffset = gamebutton_info[i].y;
12069 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12070 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12072 if (id == GAME_CTRL_ID_STOP ||
12073 id == GAME_CTRL_ID_PAUSE ||
12074 id == GAME_CTRL_ID_PLAY)
12076 button_type = GD_TYPE_NORMAL_BUTTON;
12078 event_mask = GD_EVENT_RELEASED;
12079 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12080 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12084 button_type = GD_TYPE_CHECK_BUTTON;
12086 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12087 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12088 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12089 event_mask = GD_EVENT_PRESSED;
12090 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12091 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12094 gi = CreateGadget(GDI_CUSTOM_ID, id,
12095 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12096 GDI_X, DX + gd_xoffset,
12097 GDI_Y, DY + gd_yoffset,
12098 GDI_WIDTH, GAME_BUTTON_XSIZE,
12099 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12100 GDI_TYPE, button_type,
12101 GDI_STATE, GD_BUTTON_UNPRESSED,
12102 GDI_CHECKED, checked,
12103 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12104 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12105 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12106 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12107 GDI_EVENT_MASK, event_mask,
12108 GDI_CALLBACK_ACTION, HandleGameButtons,
12112 Error(ERR_EXIT, "cannot create gadget");
12114 game_gadget[id] = gi;
12118 void FreeGameButtons()
12122 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12123 FreeGadget(game_gadget[i]);
12126 static void MapGameButtons()
12130 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12131 MapGadget(game_gadget[i]);
12134 void UnmapGameButtons()
12138 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12139 UnmapGadget(game_gadget[i]);
12142 static void HandleGameButtons(struct GadgetInfo *gi)
12144 int id = gi->custom_id;
12146 if (game_status != GAME_MODE_PLAYING)
12151 case GAME_CTRL_ID_STOP:
12152 RequestQuitGame(TRUE);
12155 case GAME_CTRL_ID_PAUSE:
12156 if (options.network)
12158 #if defined(NETWORK_AVALIABLE)
12160 SendToServer_ContinuePlaying();
12162 SendToServer_PausePlaying();
12166 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12169 case GAME_CTRL_ID_PLAY:
12172 #if defined(NETWORK_AVALIABLE)
12173 if (options.network)
12174 SendToServer_ContinuePlaying();
12178 tape.pausing = FALSE;
12179 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12184 case SOUND_CTRL_ID_MUSIC:
12185 if (setup.sound_music)
12187 setup.sound_music = FALSE;
12190 else if (audio.music_available)
12192 setup.sound = setup.sound_music = TRUE;
12194 SetAudioMode(setup.sound);
12200 case SOUND_CTRL_ID_LOOPS:
12201 if (setup.sound_loops)
12202 setup.sound_loops = FALSE;
12203 else if (audio.loops_available)
12205 setup.sound = setup.sound_loops = TRUE;
12206 SetAudioMode(setup.sound);
12210 case SOUND_CTRL_ID_SIMPLE:
12211 if (setup.sound_simple)
12212 setup.sound_simple = FALSE;
12213 else if (audio.sound_available)
12215 setup.sound = setup.sound_simple = TRUE;
12216 SetAudioMode(setup.sound);