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(PLATFORM_UNIX)
1586 /* initial null action */
1587 if (network_playing)
1588 SendToServer_MovePlayer(MV_NO_MOVING);
1596 TimeLeft = level.time;
1599 ScreenMovDir = MV_NO_MOVING;
1603 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1605 AllPlayersGone = FALSE;
1607 game.yamyam_content_nr = 0;
1608 game.magic_wall_active = FALSE;
1609 game.magic_wall_time_left = 0;
1610 game.light_time_left = 0;
1611 game.timegate_time_left = 0;
1612 game.switchgate_pos = 0;
1613 game.balloon_dir = MV_NO_MOVING;
1614 game.gravity = level.initial_gravity;
1615 game.explosions_delayed = TRUE;
1617 game.envelope_active = FALSE;
1619 for (i = 0; i < NUM_BELTS; i++)
1621 game.belt_dir[i] = MV_NO_MOVING;
1622 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1625 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1626 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1628 for (x = 0; x < lev_fieldx; x++)
1630 for (y = 0; y < lev_fieldy; y++)
1632 Feld[x][y] = level.field[x][y];
1633 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1634 ChangeDelay[x][y] = 0;
1635 ChangePage[x][y] = -1;
1636 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1638 WasJustMoving[x][y] = 0;
1639 WasJustFalling[x][y] = 0;
1640 CheckCollision[x][y] = 0;
1642 Pushed[x][y] = FALSE;
1644 Changed[x][y] = CE_BITMASK_DEFAULT;
1645 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1647 ExplodePhase[x][y] = 0;
1648 ExplodeDelay[x][y] = 0;
1649 ExplodeField[x][y] = EX_TYPE_NONE;
1651 RunnerVisit[x][y] = 0;
1652 PlayerVisit[x][y] = 0;
1655 GfxRandom[x][y] = INIT_GFX_RANDOM();
1656 GfxElement[x][y] = EL_UNDEFINED;
1657 GfxAction[x][y] = ACTION_DEFAULT;
1658 GfxDir[x][y] = MV_NO_MOVING;
1662 for (y = 0; y < lev_fieldy; y++)
1664 for (x = 0; x < lev_fieldx; x++)
1666 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1668 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1670 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1673 InitField(x, y, TRUE);
1679 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1680 emulate_sb ? EMU_SOKOBAN :
1681 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1683 /* initialize explosion and ignition delay */
1684 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1686 if (!IS_CUSTOM_ELEMENT(i))
1689 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1690 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1691 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1692 int last_phase = (num_phase + 1) * delay;
1693 int half_phase = (num_phase / 2) * delay;
1695 element_info[i].explosion_delay = last_phase - 1;
1696 element_info[i].ignition_delay = half_phase;
1699 if (i == EL_BLACK_ORB)
1700 element_info[i].ignition_delay = 0;
1702 if (i == EL_BLACK_ORB)
1703 element_info[i].ignition_delay = 1;
1708 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1709 element_info[i].explosion_delay = 1;
1711 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1712 element_info[i].ignition_delay = 1;
1716 /* correct non-moving belts to start moving left */
1717 for (i = 0; i < NUM_BELTS; i++)
1718 if (game.belt_dir[i] == MV_NO_MOVING)
1719 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1721 /* check if any connected player was not found in playfield */
1722 for (i = 0; i < MAX_PLAYERS; i++)
1724 struct PlayerInfo *player = &stored_player[i];
1726 if (player->connected && !player->present)
1728 for (j = 0; j < MAX_PLAYERS; j++)
1730 struct PlayerInfo *some_player = &stored_player[j];
1731 int jx = some_player->jx, jy = some_player->jy;
1733 /* assign first free player found that is present in the playfield */
1734 if (some_player->present && !some_player->connected)
1736 player->present = TRUE;
1737 player->active = TRUE;
1739 some_player->present = FALSE;
1740 some_player->active = FALSE;
1743 player->element_nr = some_player->element_nr;
1746 StorePlayer[jx][jy] = player->element_nr;
1747 player->jx = player->last_jx = jx;
1748 player->jy = player->last_jy = jy;
1758 /* when playing a tape, eliminate all players which do not participate */
1760 for (i = 0; i < MAX_PLAYERS; i++)
1762 if (stored_player[i].active && !tape.player_participates[i])
1764 struct PlayerInfo *player = &stored_player[i];
1765 int jx = player->jx, jy = player->jy;
1767 player->active = FALSE;
1768 StorePlayer[jx][jy] = 0;
1769 Feld[jx][jy] = EL_EMPTY;
1773 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1775 /* when in single player mode, eliminate all but the first active player */
1777 for (i = 0; i < MAX_PLAYERS; i++)
1779 if (stored_player[i].active)
1781 for (j = i + 1; j < MAX_PLAYERS; j++)
1783 if (stored_player[j].active)
1785 struct PlayerInfo *player = &stored_player[j];
1786 int jx = player->jx, jy = player->jy;
1788 player->active = FALSE;
1789 player->present = FALSE;
1791 StorePlayer[jx][jy] = 0;
1792 Feld[jx][jy] = EL_EMPTY;
1799 /* when recording the game, store which players take part in the game */
1802 for (i = 0; i < MAX_PLAYERS; i++)
1803 if (stored_player[i].active)
1804 tape.player_participates[i] = TRUE;
1809 for (i = 0; i < MAX_PLAYERS; i++)
1811 struct PlayerInfo *player = &stored_player[i];
1813 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1818 if (local_player == player)
1819 printf("Player %d is local player.\n", i+1);
1823 if (BorderElement == EL_EMPTY)
1826 SBX_Right = lev_fieldx - SCR_FIELDX;
1828 SBY_Lower = lev_fieldy - SCR_FIELDY;
1833 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1835 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1838 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1839 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1841 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1842 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1844 /* if local player not found, look for custom element that might create
1845 the player (make some assumptions about the right custom element) */
1846 if (!local_player->present)
1848 int start_x = 0, start_y = 0;
1849 int found_rating = 0;
1850 int found_element = EL_UNDEFINED;
1852 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1854 int element = Feld[x][y];
1859 if (!IS_CUSTOM_ELEMENT(element))
1862 if (CAN_CHANGE(element))
1864 for (i = 0; i < element_info[element].num_change_pages; i++)
1866 content = element_info[element].change_page[i].target_element;
1867 is_player = ELEM_IS_PLAYER(content);
1869 if (is_player && (found_rating < 3 || element < found_element))
1875 found_element = element;
1880 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1882 content = element_info[element].content[xx][yy];
1883 is_player = ELEM_IS_PLAYER(content);
1885 if (is_player && (found_rating < 2 || element < found_element))
1887 start_x = x + xx - 1;
1888 start_y = y + yy - 1;
1891 found_element = element;
1894 if (!CAN_CHANGE(element))
1897 for (i = 0; i < element_info[element].num_change_pages; i++)
1899 content= element_info[element].change_page[i].target_content[xx][yy];
1900 is_player = ELEM_IS_PLAYER(content);
1902 if (is_player && (found_rating < 1 || element < found_element))
1904 start_x = x + xx - 1;
1905 start_y = y + yy - 1;
1908 found_element = element;
1914 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1915 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1918 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1919 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1925 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1926 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1927 local_player->jx - MIDPOSX);
1929 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1930 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1931 local_player->jy - MIDPOSY);
1933 scroll_x = SBX_Left;
1934 scroll_y = SBY_Upper;
1935 if (local_player->jx >= SBX_Left + MIDPOSX)
1936 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1937 local_player->jx - MIDPOSX :
1939 if (local_player->jy >= SBY_Upper + MIDPOSY)
1940 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1941 local_player->jy - MIDPOSY :
1946 CloseDoor(DOOR_CLOSE_1);
1951 /* after drawing the level, correct some elements */
1952 if (game.timegate_time_left == 0)
1953 CloseAllOpenTimegates();
1955 if (setup.soft_scrolling)
1956 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1958 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1961 /* copy default game door content to main double buffer */
1962 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1963 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1965 DrawGameDoorValues();
1969 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1970 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1971 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1975 /* copy actual game door content to door double buffer for OpenDoor() */
1976 BlitBitmap(drawto, bitmap_db_door,
1977 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1979 OpenDoor(DOOR_OPEN_ALL);
1981 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1983 if (setup.sound_music)
1986 KeyboardAutoRepeatOffUnlessAutoplay();
1990 for (i = 0; i < MAX_PLAYERS; i++)
1991 printf("Player %d %sactive.\n",
1992 i + 1, (stored_player[i].active ? "" : "not "));
1996 printf("::: starting game [%d]\n", FrameCounter);
2000 void InitMovDir(int x, int y)
2002 int i, element = Feld[x][y];
2003 static int xy[4][2] =
2010 static int direction[3][4] =
2012 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2013 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2014 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2023 Feld[x][y] = EL_BUG;
2024 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2027 case EL_SPACESHIP_RIGHT:
2028 case EL_SPACESHIP_UP:
2029 case EL_SPACESHIP_LEFT:
2030 case EL_SPACESHIP_DOWN:
2031 Feld[x][y] = EL_SPACESHIP;
2032 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2035 case EL_BD_BUTTERFLY_RIGHT:
2036 case EL_BD_BUTTERFLY_UP:
2037 case EL_BD_BUTTERFLY_LEFT:
2038 case EL_BD_BUTTERFLY_DOWN:
2039 Feld[x][y] = EL_BD_BUTTERFLY;
2040 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2043 case EL_BD_FIREFLY_RIGHT:
2044 case EL_BD_FIREFLY_UP:
2045 case EL_BD_FIREFLY_LEFT:
2046 case EL_BD_FIREFLY_DOWN:
2047 Feld[x][y] = EL_BD_FIREFLY;
2048 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2051 case EL_PACMAN_RIGHT:
2053 case EL_PACMAN_LEFT:
2054 case EL_PACMAN_DOWN:
2055 Feld[x][y] = EL_PACMAN;
2056 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2059 case EL_SP_SNIKSNAK:
2060 MovDir[x][y] = MV_UP;
2063 case EL_SP_ELECTRON:
2064 MovDir[x][y] = MV_LEFT;
2071 Feld[x][y] = EL_MOLE;
2072 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2076 if (IS_CUSTOM_ELEMENT(element))
2078 struct ElementInfo *ei = &element_info[element];
2079 int move_direction_initial = ei->move_direction_initial;
2080 int move_pattern = ei->move_pattern;
2082 if (move_direction_initial == MV_START_PREVIOUS)
2084 if (MovDir[x][y] != MV_NO_MOVING)
2087 move_direction_initial = MV_START_AUTOMATIC;
2090 if (move_direction_initial == MV_START_RANDOM)
2091 MovDir[x][y] = 1 << RND(4);
2092 else if (move_direction_initial & MV_ANY_DIRECTION)
2093 MovDir[x][y] = move_direction_initial;
2094 else if (move_pattern == MV_ALL_DIRECTIONS ||
2095 move_pattern == MV_TURNING_LEFT ||
2096 move_pattern == MV_TURNING_RIGHT ||
2097 move_pattern == MV_TURNING_LEFT_RIGHT ||
2098 move_pattern == MV_TURNING_RIGHT_LEFT ||
2099 move_pattern == MV_TURNING_RANDOM)
2100 MovDir[x][y] = 1 << RND(4);
2101 else if (move_pattern == MV_HORIZONTAL)
2102 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2103 else if (move_pattern == MV_VERTICAL)
2104 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2105 else if (move_pattern & MV_ANY_DIRECTION)
2106 MovDir[x][y] = element_info[element].move_pattern;
2107 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2108 move_pattern == MV_ALONG_RIGHT_SIDE)
2111 /* use random direction as default start direction */
2112 if (game.engine_version >= VERSION_IDENT(3,1,0,2))
2113 MovDir[x][y] = 1 << RND(4);
2116 for (i = 0; i < NUM_DIRECTIONS; i++)
2118 int x1 = x + xy[i][0];
2119 int y1 = y + xy[i][1];
2121 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2123 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2124 MovDir[x][y] = direction[0][i];
2126 MovDir[x][y] = direction[1][i];
2135 MovDir[x][y] = 1 << RND(4);
2137 if (element != EL_BUG &&
2138 element != EL_SPACESHIP &&
2139 element != EL_BD_BUTTERFLY &&
2140 element != EL_BD_FIREFLY)
2143 for (i = 0; i < NUM_DIRECTIONS; i++)
2145 int x1 = x + xy[i][0];
2146 int y1 = y + xy[i][1];
2148 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2150 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2152 MovDir[x][y] = direction[0][i];
2155 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2156 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2158 MovDir[x][y] = direction[1][i];
2167 GfxDir[x][y] = MovDir[x][y];
2170 void InitAmoebaNr(int x, int y)
2173 int group_nr = AmoebeNachbarNr(x, y);
2177 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2179 if (AmoebaCnt[i] == 0)
2187 AmoebaNr[x][y] = group_nr;
2188 AmoebaCnt[group_nr]++;
2189 AmoebaCnt2[group_nr]++;
2195 boolean raise_level = FALSE;
2197 if (local_player->MovPos)
2201 if (tape.auto_play) /* tape might already be stopped here */
2202 tape.auto_play_level_solved = TRUE;
2204 if (tape.playing && tape.auto_play)
2205 tape.auto_play_level_solved = TRUE;
2208 local_player->LevelSolved = FALSE;
2210 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2214 if (!tape.playing && setup.sound_loops)
2215 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2216 SND_CTRL_PLAY_LOOP);
2218 while (TimeLeft > 0)
2220 if (!tape.playing && !setup.sound_loops)
2221 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2222 if (TimeLeft > 0 && !(TimeLeft % 10))
2223 RaiseScore(level.score[SC_TIME_BONUS]);
2224 if (TimeLeft > 100 && !(TimeLeft % 10))
2229 DrawGameValue_Time(TimeLeft);
2237 if (!tape.playing && setup.sound_loops)
2238 StopSound(SND_GAME_LEVELTIME_BONUS);
2240 else if (level.time == 0) /* level without time limit */
2242 if (!tape.playing && setup.sound_loops)
2243 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2244 SND_CTRL_PLAY_LOOP);
2246 while (TimePlayed < 999)
2248 if (!tape.playing && !setup.sound_loops)
2249 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2250 if (TimePlayed < 999 && !(TimePlayed % 10))
2251 RaiseScore(level.score[SC_TIME_BONUS]);
2252 if (TimePlayed < 900 && !(TimePlayed % 10))
2257 DrawGameValue_Time(TimePlayed);
2265 if (!tape.playing && setup.sound_loops)
2266 StopSound(SND_GAME_LEVELTIME_BONUS);
2269 /* close exit door after last player */
2270 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2271 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2273 int element = Feld[ExitX][ExitY];
2275 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2276 EL_SP_EXIT_CLOSING);
2278 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2281 /* Hero disappears */
2282 DrawLevelField(ExitX, ExitY);
2288 CloseDoor(DOOR_CLOSE_1);
2293 SaveTape(tape.level_nr); /* Ask to save tape */
2296 if (level_nr == leveldir_current->handicap_level)
2298 leveldir_current->handicap_level++;
2299 SaveLevelSetup_SeriesInfo();
2302 if (level_editor_test_game)
2303 local_player->score = -1; /* no highscore when playing from editor */
2304 else if (level_nr < leveldir_current->last_level)
2305 raise_level = TRUE; /* advance to next level */
2307 if ((hi_pos = NewHiScore()) >= 0)
2309 game_status = GAME_MODE_SCORES;
2310 DrawHallOfFame(hi_pos);
2319 game_status = GAME_MODE_MAIN;
2336 LoadScore(level_nr);
2338 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2339 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2342 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2344 if (local_player->score > highscore[k].Score)
2346 /* player has made it to the hall of fame */
2348 if (k < MAX_SCORE_ENTRIES - 1)
2350 int m = MAX_SCORE_ENTRIES - 1;
2353 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2354 if (!strcmp(setup.player_name, highscore[l].Name))
2356 if (m == k) /* player's new highscore overwrites his old one */
2360 for (l = m; l > k; l--)
2362 strcpy(highscore[l].Name, highscore[l - 1].Name);
2363 highscore[l].Score = highscore[l - 1].Score;
2370 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2371 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2372 highscore[k].Score = local_player->score;
2378 else if (!strncmp(setup.player_name, highscore[k].Name,
2379 MAX_PLAYER_NAME_LEN))
2380 break; /* player already there with a higher score */
2386 SaveScore(level_nr);
2391 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2393 if (player->GfxAction != action || player->GfxDir != dir)
2396 printf("Player frame reset! (%d => %d, %d => %d)\n",
2397 player->GfxAction, action, player->GfxDir, dir);
2400 player->GfxAction = action;
2401 player->GfxDir = dir;
2403 player->StepFrame = 0;
2407 static void ResetRandomAnimationValue(int x, int y)
2409 GfxRandom[x][y] = INIT_GFX_RANDOM();
2412 static void ResetGfxAnimation(int x, int y)
2415 GfxAction[x][y] = ACTION_DEFAULT;
2416 GfxDir[x][y] = MovDir[x][y];
2419 void InitMovingField(int x, int y, int direction)
2421 int element = Feld[x][y];
2422 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2423 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2427 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2428 ResetGfxAnimation(x, y);
2430 MovDir[newx][newy] = MovDir[x][y] = direction;
2431 GfxDir[x][y] = direction;
2433 if (Feld[newx][newy] == EL_EMPTY)
2434 Feld[newx][newy] = EL_BLOCKED;
2436 if (direction == MV_DOWN && CAN_FALL(element))
2437 GfxAction[x][y] = ACTION_FALLING;
2439 GfxAction[x][y] = ACTION_MOVING;
2441 GfxFrame[newx][newy] = GfxFrame[x][y];
2442 GfxRandom[newx][newy] = GfxRandom[x][y];
2443 GfxAction[newx][newy] = GfxAction[x][y];
2444 GfxDir[newx][newy] = GfxDir[x][y];
2447 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2449 int direction = MovDir[x][y];
2450 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2451 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2457 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2459 int oldx = x, oldy = y;
2460 int direction = MovDir[x][y];
2462 if (direction == MV_LEFT)
2464 else if (direction == MV_RIGHT)
2466 else if (direction == MV_UP)
2468 else if (direction == MV_DOWN)
2471 *comes_from_x = oldx;
2472 *comes_from_y = oldy;
2475 int MovingOrBlocked2Element(int x, int y)
2477 int element = Feld[x][y];
2479 if (element == EL_BLOCKED)
2483 Blocked2Moving(x, y, &oldx, &oldy);
2484 return Feld[oldx][oldy];
2490 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2492 /* like MovingOrBlocked2Element(), but if element is moving
2493 and (x,y) is the field the moving element is just leaving,
2494 return EL_BLOCKED instead of the element value */
2495 int element = Feld[x][y];
2497 if (IS_MOVING(x, y))
2499 if (element == EL_BLOCKED)
2503 Blocked2Moving(x, y, &oldx, &oldy);
2504 return Feld[oldx][oldy];
2513 static void RemoveField(int x, int y)
2515 Feld[x][y] = EL_EMPTY;
2522 ChangeDelay[x][y] = 0;
2523 ChangePage[x][y] = -1;
2524 Pushed[x][y] = FALSE;
2527 ExplodeField[x][y] = EX_TYPE_NONE;
2530 GfxElement[x][y] = EL_UNDEFINED;
2531 GfxAction[x][y] = ACTION_DEFAULT;
2532 GfxDir[x][y] = MV_NO_MOVING;
2535 void RemoveMovingField(int x, int y)
2537 int oldx = x, oldy = y, newx = x, newy = y;
2538 int element = Feld[x][y];
2539 int next_element = EL_UNDEFINED;
2541 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2544 if (IS_MOVING(x, y))
2546 Moving2Blocked(x, y, &newx, &newy);
2548 if (Feld[newx][newy] != EL_BLOCKED)
2551 if (Feld[newx][newy] != EL_BLOCKED)
2553 /* element is moving, but target field is not free (blocked), but
2554 already occupied by something different (example: acid pool);
2555 in this case, only remove the moving field, but not the target */
2557 RemoveField(oldx, oldy);
2559 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2561 DrawLevelField(oldx, oldy);
2567 else if (element == EL_BLOCKED)
2569 Blocked2Moving(x, y, &oldx, &oldy);
2570 if (!IS_MOVING(oldx, oldy))
2574 if (element == EL_BLOCKED &&
2575 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2576 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2577 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2578 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2579 next_element = get_next_element(Feld[oldx][oldy]);
2581 RemoveField(oldx, oldy);
2582 RemoveField(newx, newy);
2584 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2586 if (next_element != EL_UNDEFINED)
2587 Feld[oldx][oldy] = next_element;
2589 DrawLevelField(oldx, oldy);
2590 DrawLevelField(newx, newy);
2593 void DrawDynamite(int x, int y)
2595 int sx = SCREENX(x), sy = SCREENY(y);
2596 int graphic = el2img(Feld[x][y]);
2599 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2602 if (IS_WALKABLE_INSIDE(Back[x][y]))
2606 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2607 else if (Store[x][y])
2608 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2610 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2613 if (Back[x][y] || Store[x][y])
2614 DrawGraphicThruMask(sx, sy, graphic, frame);
2616 DrawGraphic(sx, sy, graphic, frame);
2618 if (game.emulation == EMU_SUPAPLEX)
2619 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2620 else if (Store[x][y])
2621 DrawGraphicThruMask(sx, sy, graphic, frame);
2623 DrawGraphic(sx, sy, graphic, frame);
2627 void CheckDynamite(int x, int y)
2629 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2633 if (MovDelay[x][y] != 0)
2636 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2643 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2645 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2646 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2647 StopSound(SND_DYNAMITE_ACTIVE);
2649 StopSound(SND_DYNABOMB_ACTIVE);
2655 void DrawRelocatePlayer(struct PlayerInfo *player)
2657 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2658 boolean no_delay = (tape.warp_forward);
2659 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2660 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2661 int jx = player->jx;
2662 int jy = player->jy;
2664 if (level.instant_relocation)
2667 int offset = (setup.scroll_delay ? 3 : 0);
2669 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2671 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2672 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2673 local_player->jx - MIDPOSX);
2675 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2676 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2677 local_player->jy - MIDPOSY);
2681 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2682 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2683 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2685 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2686 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2687 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2689 /* don't scroll over playfield boundaries */
2690 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2691 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2693 /* don't scroll over playfield boundaries */
2694 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2695 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2698 scroll_x += (local_player->jx - old_jx);
2699 scroll_y += (local_player->jy - old_jy);
2701 /* don't scroll over playfield boundaries */
2702 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2703 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2705 /* don't scroll over playfield boundaries */
2706 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2707 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2710 RedrawPlayfield(TRUE, 0,0,0,0);
2716 int offset = (setup.scroll_delay ? 3 : 0);
2718 int scroll_xx = -999, scroll_yy = -999;
2720 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2722 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2725 int fx = FX, fy = FY;
2727 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2728 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2729 local_player->jx - MIDPOSX);
2731 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2732 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2733 local_player->jy - MIDPOSY);
2735 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2736 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2739 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2742 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2749 fx += dx * TILEX / 2;
2750 fy += dy * TILEY / 2;
2752 ScrollLevel(dx, dy);
2755 /* scroll in two steps of half tile size to make things smoother */
2756 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2758 Delay(wait_delay_value);
2760 /* scroll second step to align at full tile size */
2762 Delay(wait_delay_value);
2765 int scroll_xx = -999, scroll_yy = -999;
2767 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2769 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2772 int fx = FX, fy = FY;
2774 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2775 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2776 local_player->jx - MIDPOSX);
2778 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2779 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2780 local_player->jy - MIDPOSY);
2782 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2783 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2786 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2789 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2796 fx += dx * TILEX / 2;
2797 fy += dy * TILEY / 2;
2799 ScrollLevel(dx, dy);
2802 /* scroll in two steps of half tile size to make things smoother */
2803 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2805 Delay(wait_delay_value);
2807 /* scroll second step to align at full tile size */
2809 Delay(wait_delay_value);
2815 Delay(wait_delay_value);
2819 void RelocatePlayer(int jx, int jy, int el_player_raw)
2821 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
2822 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2823 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2824 boolean no_delay = (tape.warp_forward);
2825 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2826 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2827 int old_jx = player->jx;
2828 int old_jy = player->jy;
2829 int old_element = Feld[old_jx][old_jy];
2830 int element = Feld[jx][jy];
2831 boolean player_relocated = (old_jx != jx || old_jy != jy);
2833 static int trigger_sides[4][2] =
2835 /* enter side leave side */
2836 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
2837 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
2838 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
2839 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
2841 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2842 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2843 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
2844 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
2845 int enter_side = enter_side_horiz | enter_side_vert;
2846 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
2847 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
2848 int leave_side = leave_side_horiz | leave_side_vert;
2850 if (player->GameOver) /* do not reanimate dead player */
2853 if (!player_relocated) /* no need to relocate the player */
2856 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2858 RemoveField(jx, jy); /* temporarily remove newly placed player */
2859 DrawLevelField(jx, jy);
2862 if (player->present)
2864 while (player->MovPos)
2866 ScrollPlayer(player, SCROLL_GO_ON);
2867 ScrollScreen(NULL, SCROLL_GO_ON);
2873 Delay(wait_delay_value);
2876 DrawPlayer(player); /* needed here only to cleanup last field */
2877 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2879 player->is_moving = FALSE;
2883 if (IS_CUSTOM_ELEMENT(old_element))
2884 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
2886 player->index_bit, leave_side);
2888 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
2890 player->index_bit, leave_side);
2893 Feld[jx][jy] = el_player;
2894 InitPlayerField(jx, jy, el_player, TRUE);
2896 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
2898 Feld[jx][jy] = element;
2899 InitField(jx, jy, FALSE);
2903 if (player == local_player) /* only visually relocate local player */
2904 DrawRelocatePlayer(player);
2908 TestIfHeroTouchesBadThing(jx, jy);
2909 TestIfPlayerTouchesCustomElement(jx, jy);
2913 /* needed to allow change of walkable custom element by entering player */
2914 Changed[jx][jy] = 0; /* allow another change */
2918 if (IS_CUSTOM_ELEMENT(element))
2919 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
2920 player->index_bit, enter_side);
2922 CheckTriggeredElementChangeByPlayer(jx, jy, element,
2923 CE_OTHER_GETS_ENTERED,
2924 player->index_bit, enter_side);
2928 void Explode(int ex, int ey, int phase, int mode)
2935 /* !!! eliminate this variable !!! */
2936 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2941 int last_phase = num_phase * delay;
2942 int half_phase = (num_phase / 2) * delay;
2943 int first_phase_after_start = EX_PHASE_START + 1;
2947 if (game.explosions_delayed)
2949 ExplodeField[ex][ey] = mode;
2953 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2955 int center_element = Feld[ex][ey];
2958 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
2962 /* --- This is only really needed (and now handled) in "Impact()". --- */
2963 /* do not explode moving elements that left the explode field in time */
2964 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2965 center_element == EL_EMPTY &&
2966 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
2970 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
2971 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2973 /* remove things displayed in background while burning dynamite */
2974 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2977 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2979 /* put moving element to center field (and let it explode there) */
2980 center_element = MovingOrBlocked2Element(ex, ey);
2981 RemoveMovingField(ex, ey);
2982 Feld[ex][ey] = center_element;
2988 last_phase = element_info[center_element].explosion_delay + 1;
2990 last_phase = element_info[center_element].explosion_delay;
2994 printf("::: %d -> %d\n", center_element, last_phase);
2998 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3000 int xx = x - ex + 1;
3001 int yy = y - ey + 1;
3006 if (!IN_LEV_FIELD(x, y) ||
3007 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3008 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3011 if (!IN_LEV_FIELD(x, y) ||
3012 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3016 if (!IN_LEV_FIELD(x, y) ||
3017 ((mode != EX_TYPE_NORMAL ||
3018 center_element == EL_AMOEBA_TO_DIAMOND) &&
3019 (x != ex || y != ey)))
3023 element = Feld[x][y];
3025 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3027 element = MovingOrBlocked2Element(x, y);
3029 if (!IS_EXPLOSION_PROOF(element))
3030 RemoveMovingField(x, y);
3036 if (IS_EXPLOSION_PROOF(element))
3039 /* indestructible elements can only explode in center (but not flames) */
3040 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3041 element == EL_FLAMES)
3046 if ((IS_INDESTRUCTIBLE(element) &&
3047 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3048 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3049 element == EL_FLAMES)
3053 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3055 if (IS_ACTIVE_BOMB(element))
3057 /* re-activate things under the bomb like gate or penguin */
3058 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3065 /* save walkable background elements while explosion on same tile */
3067 if (IS_INDESTRUCTIBLE(element))
3068 Back[x][y] = element;
3070 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3071 Back[x][y] = element;
3074 /* ignite explodable elements reached by other explosion */
3075 if (element == EL_EXPLOSION)
3076 element = Store2[x][y];
3079 if (AmoebaNr[x][y] &&
3080 (element == EL_AMOEBA_FULL ||
3081 element == EL_BD_AMOEBA ||
3082 element == EL_AMOEBA_GROWING))
3084 AmoebaCnt[AmoebaNr[x][y]]--;
3085 AmoebaCnt2[AmoebaNr[x][y]]--;
3091 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3093 switch(StorePlayer[ex][ey])
3096 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3099 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3102 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3106 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3111 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3112 Store[x][y] = EL_EMPTY;
3114 if (game.emulation == EMU_SUPAPLEX)
3115 Store[x][y] = EL_EMPTY;
3118 else if (center_element == EL_MOLE)
3119 Store[x][y] = EL_EMERALD_RED;
3120 else if (center_element == EL_PENGUIN)
3121 Store[x][y] = EL_EMERALD_PURPLE;
3122 else if (center_element == EL_BUG)
3123 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3124 else if (center_element == EL_BD_BUTTERFLY)
3125 Store[x][y] = EL_BD_DIAMOND;
3126 else if (center_element == EL_SP_ELECTRON)
3127 Store[x][y] = EL_SP_INFOTRON;
3128 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3129 Store[x][y] = level.amoeba_content;
3130 else if (center_element == EL_YAMYAM)
3131 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3132 else if (IS_CUSTOM_ELEMENT(center_element) &&
3133 element_info[center_element].content[xx][yy] != EL_EMPTY)
3134 Store[x][y] = element_info[center_element].content[xx][yy];
3135 else if (element == EL_WALL_EMERALD)
3136 Store[x][y] = EL_EMERALD;
3137 else if (element == EL_WALL_DIAMOND)
3138 Store[x][y] = EL_DIAMOND;
3139 else if (element == EL_WALL_BD_DIAMOND)
3140 Store[x][y] = EL_BD_DIAMOND;
3141 else if (element == EL_WALL_EMERALD_YELLOW)
3142 Store[x][y] = EL_EMERALD_YELLOW;
3143 else if (element == EL_WALL_EMERALD_RED)
3144 Store[x][y] = EL_EMERALD_RED;
3145 else if (element == EL_WALL_EMERALD_PURPLE)
3146 Store[x][y] = EL_EMERALD_PURPLE;
3147 else if (element == EL_WALL_PEARL)
3148 Store[x][y] = EL_PEARL;
3149 else if (element == EL_WALL_CRYSTAL)
3150 Store[x][y] = EL_CRYSTAL;
3151 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3152 Store[x][y] = element_info[element].content[1][1];
3154 Store[x][y] = EL_EMPTY;
3156 if (x != ex || y != ey ||
3157 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_TYPE_BORDER)
3158 Store2[x][y] = element;
3161 if (AmoebaNr[x][y] &&
3162 (element == EL_AMOEBA_FULL ||
3163 element == EL_BD_AMOEBA ||
3164 element == EL_AMOEBA_GROWING))
3166 AmoebaCnt[AmoebaNr[x][y]]--;
3167 AmoebaCnt2[AmoebaNr[x][y]]--;
3173 MovDir[x][y] = MovPos[x][y] = 0;
3174 GfxDir[x][y] = MovDir[x][y];
3179 Feld[x][y] = EL_EXPLOSION;
3181 GfxElement[x][y] = center_element;
3183 GfxElement[x][y] = EL_UNDEFINED;
3186 ExplodePhase[x][y] = 1;
3188 ExplodeDelay[x][y] = last_phase;
3193 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3195 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3202 if (center_element == EL_YAMYAM)
3203 game.yamyam_content_nr =
3204 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3217 GfxFrame[x][y] = 0; /* restart explosion animation */
3221 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3225 last_phase = ExplodeDelay[x][y];
3228 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3232 /* activate this even in non-DEBUG version until cause for crash in
3233 getGraphicAnimationFrame() (see below) is found and eliminated */
3237 if (GfxElement[x][y] == EL_UNDEFINED)
3240 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3241 printf("Explode(): This should never happen!\n");
3244 GfxElement[x][y] = EL_EMPTY;
3250 border_element = Store2[x][y];
3251 if (IS_PLAYER(x, y))
3252 border_element = StorePlayer[x][y];
3255 printf("::: phase == %d\n", phase);
3258 if (phase == element_info[border_element].ignition_delay ||
3259 phase == last_phase)
3261 boolean border_explosion = FALSE;
3264 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3266 if (IS_PLAYER(x, y))
3269 KillHeroUnlessExplosionProtected(x, y);
3270 border_explosion = TRUE;
3273 if (phase == last_phase)
3274 printf("::: IS_PLAYER\n");
3277 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3279 Feld[x][y] = Store2[x][y];
3282 border_explosion = TRUE;
3285 if (phase == last_phase)
3286 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3289 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3291 AmoebeUmwandeln(x, y);
3293 border_explosion = TRUE;
3296 if (phase == last_phase)
3297 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3298 element_info[border_element].explosion_delay,
3299 element_info[border_element].ignition_delay,
3305 /* if an element just explodes due to another explosion (chain-reaction),
3306 do not immediately end the new explosion when it was the last frame of
3307 the explosion (as it would be done in the following "if"-statement!) */
3308 if (border_explosion && phase == last_phase)
3315 if (phase == first_phase_after_start)
3317 int element = Store2[x][y];
3319 if (element == EL_BLACK_ORB)
3321 Feld[x][y] = Store2[x][y];
3326 else if (phase == half_phase)
3328 int element = Store2[x][y];
3330 if (IS_PLAYER(x, y))
3331 KillHeroUnlessExplosionProtected(x, y);
3332 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3334 Feld[x][y] = Store2[x][y];
3338 else if (element == EL_AMOEBA_TO_DIAMOND)
3339 AmoebeUmwandeln(x, y);
3343 if (phase == last_phase)
3348 printf("::: done: phase == %d\n", phase);
3352 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3355 element = Feld[x][y] = Store[x][y];
3356 Store[x][y] = Store2[x][y] = 0;
3357 GfxElement[x][y] = EL_UNDEFINED;
3359 /* player can escape from explosions and might therefore be still alive */
3360 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3361 element <= EL_PLAYER_IS_EXPLODING_4)
3362 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3364 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3365 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3366 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3369 /* restore probably existing indestructible background element */
3370 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3371 element = Feld[x][y] = Back[x][y];
3374 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3375 GfxDir[x][y] = MV_NO_MOVING;
3376 ChangeDelay[x][y] = 0;
3377 ChangePage[x][y] = -1;
3380 InitField_WithBug2(x, y, FALSE);
3382 InitField(x, y, FALSE);
3384 /* !!! not needed !!! */
3386 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3387 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3390 if (CAN_MOVE(element))
3395 DrawLevelField(x, y);
3397 TestIfElementTouchesCustomElement(x, y);
3399 if (GFX_CRUMBLED(element))
3400 DrawLevelFieldCrumbledSandNeighbours(x, y);
3402 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3403 StorePlayer[x][y] = 0;
3405 if (ELEM_IS_PLAYER(element))
3406 RelocatePlayer(x, y, element);
3409 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3411 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3415 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3417 int stored = Store[x][y];
3418 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3419 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3423 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3425 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3429 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3433 printf("::: %d / %d [%d - %d]\n",
3434 GfxFrame[x][y], phase - delay, phase, delay);
3438 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3439 element_info[GfxElement[x][y]].token_name,
3444 DrawLevelFieldCrumbledSand(x, y);
3446 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3448 DrawLevelElement(x, y, Back[x][y]);
3449 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3451 else if (IS_WALKABLE_UNDER(Back[x][y]))
3453 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3454 DrawLevelElementThruMask(x, y, Back[x][y]);
3456 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3457 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3461 void DynaExplode(int ex, int ey)
3464 int dynabomb_element = Feld[ex][ey];
3465 int dynabomb_size = 1;
3466 boolean dynabomb_xl = FALSE;
3467 struct PlayerInfo *player;
3468 static int xy[4][2] =
3476 if (IS_ACTIVE_BOMB(dynabomb_element))
3478 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3479 dynabomb_size = player->dynabomb_size;
3480 dynabomb_xl = player->dynabomb_xl;
3481 player->dynabombs_left++;
3484 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3486 for (i = 0; i < NUM_DIRECTIONS; i++)
3488 for (j = 1; j <= dynabomb_size; j++)
3490 int x = ex + j * xy[i][0];
3491 int y = ey + j * xy[i][1];
3494 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3497 element = Feld[x][y];
3499 /* do not restart explosions of fields with active bombs */
3500 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3503 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3507 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3508 !IS_DIGGABLE(element) && !dynabomb_xl)
3511 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3512 !CAN_GROW_INTO(element) && !dynabomb_xl)
3516 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3517 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3518 element != EL_SAND && !dynabomb_xl)
3525 void Bang(int x, int y)
3528 int element = MovingOrBlocked2Element(x, y);
3530 int element = Feld[x][y];
3534 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3536 if (IS_PLAYER(x, y))
3539 struct PlayerInfo *player = PLAYERINFO(x, y);
3541 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3542 player->element_nr);
3547 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3549 if (game.emulation == EMU_SUPAPLEX)
3550 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3552 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3557 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3565 case EL_BD_BUTTERFLY:
3568 case EL_DARK_YAMYAM:
3572 RaiseScoreElement(element);
3573 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3575 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3576 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3577 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3578 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3579 case EL_DYNABOMB_INCREASE_NUMBER:
3580 case EL_DYNABOMB_INCREASE_SIZE:
3581 case EL_DYNABOMB_INCREASE_POWER:
3586 case EL_LAMP_ACTIVE:
3588 case EL_AMOEBA_TO_DIAMOND:
3590 if (IS_PLAYER(x, y))
3591 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3593 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3596 if (CAN_EXPLODE_CROSS(element))
3598 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3602 else if (CAN_EXPLODE_1X1(element))
3603 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3605 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3609 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3612 void SplashAcid(int x, int y)
3615 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3616 (!IN_LEV_FIELD(x - 1, y - 2) ||
3617 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3618 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3620 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3621 (!IN_LEV_FIELD(x + 1, y - 2) ||
3622 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3623 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3625 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3627 /* input: position of element entering acid (obsolete) */
3629 int element = Feld[x][y];
3631 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3634 if (element != EL_ACID_SPLASH_LEFT &&
3635 element != EL_ACID_SPLASH_RIGHT)
3637 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3639 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3640 (!IN_LEV_FIELD(x - 1, y - 1) ||
3641 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3642 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3644 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3645 (!IN_LEV_FIELD(x + 1, y - 1) ||
3646 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3647 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3652 static void InitBeltMovement()
3654 static int belt_base_element[4] =
3656 EL_CONVEYOR_BELT_1_LEFT,
3657 EL_CONVEYOR_BELT_2_LEFT,
3658 EL_CONVEYOR_BELT_3_LEFT,
3659 EL_CONVEYOR_BELT_4_LEFT
3661 static int belt_base_active_element[4] =
3663 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3664 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3665 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3666 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3671 /* set frame order for belt animation graphic according to belt direction */
3672 for (i = 0; i < NUM_BELTS; i++)
3676 for (j = 0; j < NUM_BELT_PARTS; j++)
3678 int element = belt_base_active_element[belt_nr] + j;
3679 int graphic = el2img(element);
3681 if (game.belt_dir[i] == MV_LEFT)
3682 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3684 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3688 for (y = 0; y < lev_fieldy; y++)
3690 for (x = 0; x < lev_fieldx; x++)
3692 int element = Feld[x][y];
3694 for (i = 0; i < NUM_BELTS; i++)
3696 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3698 int e_belt_nr = getBeltNrFromBeltElement(element);
3701 if (e_belt_nr == belt_nr)
3703 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3705 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3713 static void ToggleBeltSwitch(int x, int y)
3715 static int belt_base_element[4] =
3717 EL_CONVEYOR_BELT_1_LEFT,
3718 EL_CONVEYOR_BELT_2_LEFT,
3719 EL_CONVEYOR_BELT_3_LEFT,
3720 EL_CONVEYOR_BELT_4_LEFT
3722 static int belt_base_active_element[4] =
3724 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3725 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3726 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3727 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3729 static int belt_base_switch_element[4] =
3731 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3732 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3733 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3734 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3736 static int belt_move_dir[4] =
3744 int element = Feld[x][y];
3745 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3746 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3747 int belt_dir = belt_move_dir[belt_dir_nr];
3750 if (!IS_BELT_SWITCH(element))
3753 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3754 game.belt_dir[belt_nr] = belt_dir;
3756 if (belt_dir_nr == 3)
3759 /* set frame order for belt animation graphic according to belt direction */
3760 for (i = 0; i < NUM_BELT_PARTS; i++)
3762 int element = belt_base_active_element[belt_nr] + i;
3763 int graphic = el2img(element);
3765 if (belt_dir == MV_LEFT)
3766 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3768 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3771 for (yy = 0; yy < lev_fieldy; yy++)
3773 for (xx = 0; xx < lev_fieldx; xx++)
3775 int element = Feld[xx][yy];
3777 if (IS_BELT_SWITCH(element))
3779 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3781 if (e_belt_nr == belt_nr)
3783 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3784 DrawLevelField(xx, yy);
3787 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3789 int e_belt_nr = getBeltNrFromBeltElement(element);
3791 if (e_belt_nr == belt_nr)
3793 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3795 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3796 DrawLevelField(xx, yy);
3799 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3801 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3803 if (e_belt_nr == belt_nr)
3805 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3807 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3808 DrawLevelField(xx, yy);
3815 static void ToggleSwitchgateSwitch(int x, int y)
3819 game.switchgate_pos = !game.switchgate_pos;
3821 for (yy = 0; yy < lev_fieldy; yy++)
3823 for (xx = 0; xx < lev_fieldx; xx++)
3825 int element = Feld[xx][yy];
3827 if (element == EL_SWITCHGATE_SWITCH_UP ||
3828 element == EL_SWITCHGATE_SWITCH_DOWN)
3830 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3831 DrawLevelField(xx, yy);
3833 else if (element == EL_SWITCHGATE_OPEN ||
3834 element == EL_SWITCHGATE_OPENING)
3836 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3838 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3840 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3843 else if (element == EL_SWITCHGATE_CLOSED ||
3844 element == EL_SWITCHGATE_CLOSING)
3846 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3848 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3850 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3857 static int getInvisibleActiveFromInvisibleElement(int element)
3859 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3860 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3861 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3865 static int getInvisibleFromInvisibleActiveElement(int element)
3867 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3868 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3869 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3873 static void RedrawAllLightSwitchesAndInvisibleElements()
3877 for (y = 0; y < lev_fieldy; y++)
3879 for (x = 0; x < lev_fieldx; x++)
3881 int element = Feld[x][y];
3883 if (element == EL_LIGHT_SWITCH &&
3884 game.light_time_left > 0)
3886 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3887 DrawLevelField(x, y);
3889 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3890 game.light_time_left == 0)
3892 Feld[x][y] = EL_LIGHT_SWITCH;
3893 DrawLevelField(x, y);
3895 else if (element == EL_INVISIBLE_STEELWALL ||
3896 element == EL_INVISIBLE_WALL ||
3897 element == EL_INVISIBLE_SAND)
3899 if (game.light_time_left > 0)
3900 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3902 DrawLevelField(x, y);
3904 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3905 element == EL_INVISIBLE_WALL_ACTIVE ||
3906 element == EL_INVISIBLE_SAND_ACTIVE)
3908 if (game.light_time_left == 0)
3909 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3911 DrawLevelField(x, y);
3917 static void ToggleLightSwitch(int x, int y)
3919 int element = Feld[x][y];
3921 game.light_time_left =
3922 (element == EL_LIGHT_SWITCH ?
3923 level.time_light * FRAMES_PER_SECOND : 0);
3925 RedrawAllLightSwitchesAndInvisibleElements();
3928 static void ActivateTimegateSwitch(int x, int y)
3932 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3934 for (yy = 0; yy < lev_fieldy; yy++)
3936 for (xx = 0; xx < lev_fieldx; xx++)
3938 int element = Feld[xx][yy];
3940 if (element == EL_TIMEGATE_CLOSED ||
3941 element == EL_TIMEGATE_CLOSING)
3943 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3944 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3948 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3950 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3951 DrawLevelField(xx, yy);
3958 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3961 inline static int getElementMoveStepsize(int x, int y)
3963 int element = Feld[x][y];
3964 int direction = MovDir[x][y];
3965 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3966 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3967 int horiz_move = (dx != 0);
3968 int sign = (horiz_move ? dx : dy);
3969 int step = sign * element_info[element].move_stepsize;
3971 /* special values for move stepsize for spring and things on conveyor belt */
3975 if (element == EL_SPRING)
3976 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3977 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3978 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3979 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3981 if (CAN_FALL(element) &&
3982 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3983 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3984 else if (element == EL_SPRING)
3985 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3992 void Impact(int x, int y)
3994 boolean lastline = (y == lev_fieldy-1);
3995 boolean object_hit = FALSE;
3996 boolean impact = (lastline || object_hit);
3997 int element = Feld[x][y];
3998 int smashed = EL_STEELWALL;
4000 if (!lastline) /* check if element below was hit */
4002 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4005 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4006 MovDir[x][y + 1] != MV_DOWN ||
4007 MovPos[x][y + 1] <= TILEY / 2));
4010 object_hit = !IS_FREE(x, y + 1);
4013 /* do not smash moving elements that left the smashed field in time */
4014 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4015 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4019 smashed = MovingOrBlocked2Element(x, y + 1);
4021 impact = (lastline || object_hit);
4024 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4026 SplashAcid(x, y + 1);
4030 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4031 /* only reset graphic animation if graphic really changes after impact */
4033 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4035 ResetGfxAnimation(x, y);
4036 DrawLevelField(x, y);
4039 if (impact && CAN_EXPLODE_IMPACT(element))
4044 else if (impact && element == EL_PEARL)
4046 ResetGfxAnimation(x, y);
4048 Feld[x][y] = EL_PEARL_BREAKING;
4049 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4052 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4054 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4059 if (impact && element == EL_AMOEBA_DROP)
4061 if (object_hit && IS_PLAYER(x, y + 1))
4062 KillHeroUnlessEnemyProtected(x, y + 1);
4063 else if (object_hit && smashed == EL_PENGUIN)
4067 Feld[x][y] = EL_AMOEBA_GROWING;
4068 Store[x][y] = EL_AMOEBA_WET;
4070 ResetRandomAnimationValue(x, y);
4075 if (object_hit) /* check which object was hit */
4077 if (CAN_PASS_MAGIC_WALL(element) &&
4078 (smashed == EL_MAGIC_WALL ||
4079 smashed == EL_BD_MAGIC_WALL))
4082 int activated_magic_wall =
4083 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4084 EL_BD_MAGIC_WALL_ACTIVE);
4086 /* activate magic wall / mill */
4087 for (yy = 0; yy < lev_fieldy; yy++)
4088 for (xx = 0; xx < lev_fieldx; xx++)
4089 if (Feld[xx][yy] == smashed)
4090 Feld[xx][yy] = activated_magic_wall;
4092 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4093 game.magic_wall_active = TRUE;
4095 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4096 SND_MAGIC_WALL_ACTIVATING :
4097 SND_BD_MAGIC_WALL_ACTIVATING));
4100 if (IS_PLAYER(x, y + 1))
4102 if (CAN_SMASH_PLAYER(element))
4104 KillHeroUnlessEnemyProtected(x, y + 1);
4108 else if (smashed == EL_PENGUIN)
4110 if (CAN_SMASH_PLAYER(element))
4116 else if (element == EL_BD_DIAMOND)
4118 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4124 else if (((element == EL_SP_INFOTRON ||
4125 element == EL_SP_ZONK) &&
4126 (smashed == EL_SP_SNIKSNAK ||
4127 smashed == EL_SP_ELECTRON ||
4128 smashed == EL_SP_DISK_ORANGE)) ||
4129 (element == EL_SP_INFOTRON &&
4130 smashed == EL_SP_DISK_YELLOW))
4136 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4142 else if (CAN_SMASH_EVERYTHING(element))
4144 if (IS_CLASSIC_ENEMY(smashed) ||
4145 CAN_EXPLODE_SMASHED(smashed))
4150 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4152 if (smashed == EL_LAMP ||
4153 smashed == EL_LAMP_ACTIVE)
4158 else if (smashed == EL_NUT)
4160 Feld[x][y + 1] = EL_NUT_BREAKING;
4161 PlayLevelSound(x, y, SND_NUT_BREAKING);
4162 RaiseScoreElement(EL_NUT);
4165 else if (smashed == EL_PEARL)
4167 ResetGfxAnimation(x, y);
4169 Feld[x][y + 1] = EL_PEARL_BREAKING;
4170 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4173 else if (smashed == EL_DIAMOND)
4175 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4176 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4179 else if (IS_BELT_SWITCH(smashed))
4181 ToggleBeltSwitch(x, y + 1);
4183 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4184 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4186 ToggleSwitchgateSwitch(x, y + 1);
4188 else if (smashed == EL_LIGHT_SWITCH ||
4189 smashed == EL_LIGHT_SWITCH_ACTIVE)
4191 ToggleLightSwitch(x, y + 1);
4196 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4199 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4202 /* !!! TEST ONLY !!! */
4203 CheckElementChangeBySide(x, y + 1, smashed, element,
4204 CE_SWITCHED, CH_SIDE_TOP);
4205 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4206 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4208 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4209 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4210 CheckElementChangeBySide(x, y + 1, smashed, element,
4211 CE_SWITCHED, CH_SIDE_TOP);
4217 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4222 /* play sound of magic wall / mill */
4224 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4225 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4227 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4228 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4229 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4230 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4235 /* play sound of object that hits the ground */
4236 if (lastline || object_hit)
4237 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4240 inline static void TurnRoundExt(int x, int y)
4252 { 0, 0 }, { 0, 0 }, { 0, 0 },
4257 int left, right, back;
4261 { MV_DOWN, MV_UP, MV_RIGHT },
4262 { MV_UP, MV_DOWN, MV_LEFT },
4264 { MV_LEFT, MV_RIGHT, MV_DOWN },
4268 { MV_RIGHT, MV_LEFT, MV_UP }
4271 int element = Feld[x][y];
4272 int move_pattern = element_info[element].move_pattern;
4274 int old_move_dir = MovDir[x][y];
4275 int left_dir = turn[old_move_dir].left;
4276 int right_dir = turn[old_move_dir].right;
4277 int back_dir = turn[old_move_dir].back;
4279 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4280 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4281 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4282 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4284 int left_x = x + left_dx, left_y = y + left_dy;
4285 int right_x = x + right_dx, right_y = y + right_dy;
4286 int move_x = x + move_dx, move_y = y + move_dy;
4290 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4292 TestIfBadThingTouchesOtherBadThing(x, y);
4294 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4295 MovDir[x][y] = right_dir;
4296 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4297 MovDir[x][y] = left_dir;
4299 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4301 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4305 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4306 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4308 TestIfBadThingTouchesOtherBadThing(x, y);
4310 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4311 MovDir[x][y] = left_dir;
4312 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4313 MovDir[x][y] = right_dir;
4315 if ((element == EL_SPACESHIP ||
4316 element == EL_SP_SNIKSNAK ||
4317 element == EL_SP_ELECTRON)
4318 && MovDir[x][y] != old_move_dir)
4320 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4324 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4326 TestIfBadThingTouchesOtherBadThing(x, y);
4328 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4329 MovDir[x][y] = left_dir;
4330 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4331 MovDir[x][y] = right_dir;
4333 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4335 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4338 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4340 TestIfBadThingTouchesOtherBadThing(x, y);
4342 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4343 MovDir[x][y] = left_dir;
4344 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4345 MovDir[x][y] = right_dir;
4347 if (MovDir[x][y] != old_move_dir)
4351 else if (element == EL_YAMYAM)
4353 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4354 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4356 if (can_turn_left && can_turn_right)
4357 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4358 else if (can_turn_left)
4359 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4360 else if (can_turn_right)
4361 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4363 MovDir[x][y] = back_dir;
4365 MovDelay[x][y] = 16 + 16 * RND(3);
4367 else if (element == EL_DARK_YAMYAM)
4369 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4371 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4374 if (can_turn_left && can_turn_right)
4375 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4376 else if (can_turn_left)
4377 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4378 else if (can_turn_right)
4379 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4381 MovDir[x][y] = back_dir;
4383 MovDelay[x][y] = 16 + 16 * RND(3);
4385 else if (element == EL_PACMAN)
4387 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4388 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4390 if (can_turn_left && can_turn_right)
4391 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4392 else if (can_turn_left)
4393 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4394 else if (can_turn_right)
4395 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4397 MovDir[x][y] = back_dir;
4399 MovDelay[x][y] = 6 + RND(40);
4401 else if (element == EL_PIG)
4403 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4404 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4405 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4406 boolean should_turn_left, should_turn_right, should_move_on;
4408 int rnd = RND(rnd_value);
4410 should_turn_left = (can_turn_left &&
4412 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4413 y + back_dy + left_dy)));
4414 should_turn_right = (can_turn_right &&
4416 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4417 y + back_dy + right_dy)));
4418 should_move_on = (can_move_on &&
4421 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4422 y + move_dy + left_dy) ||
4423 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4424 y + move_dy + right_dy)));
4426 if (should_turn_left || should_turn_right || should_move_on)
4428 if (should_turn_left && should_turn_right && should_move_on)
4429 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4430 rnd < 2 * rnd_value / 3 ? right_dir :
4432 else if (should_turn_left && should_turn_right)
4433 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4434 else if (should_turn_left && should_move_on)
4435 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4436 else if (should_turn_right && should_move_on)
4437 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4438 else if (should_turn_left)
4439 MovDir[x][y] = left_dir;
4440 else if (should_turn_right)
4441 MovDir[x][y] = right_dir;
4442 else if (should_move_on)
4443 MovDir[x][y] = old_move_dir;
4445 else if (can_move_on && rnd > rnd_value / 8)
4446 MovDir[x][y] = old_move_dir;
4447 else if (can_turn_left && can_turn_right)
4448 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4449 else if (can_turn_left && rnd > rnd_value / 8)
4450 MovDir[x][y] = left_dir;
4451 else if (can_turn_right && rnd > rnd_value/8)
4452 MovDir[x][y] = right_dir;
4454 MovDir[x][y] = back_dir;
4456 xx = x + move_xy[MovDir[x][y]].x;
4457 yy = y + move_xy[MovDir[x][y]].y;
4459 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4460 MovDir[x][y] = old_move_dir;
4464 else if (element == EL_DRAGON)
4466 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4467 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4468 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4470 int rnd = RND(rnd_value);
4473 if (FrameCounter < 1 && x == 0 && y == 29)
4474 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4477 if (can_move_on && rnd > rnd_value / 8)
4478 MovDir[x][y] = old_move_dir;
4479 else if (can_turn_left && can_turn_right)
4480 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4481 else if (can_turn_left && rnd > rnd_value / 8)
4482 MovDir[x][y] = left_dir;
4483 else if (can_turn_right && rnd > rnd_value / 8)
4484 MovDir[x][y] = right_dir;
4486 MovDir[x][y] = back_dir;
4488 xx = x + move_xy[MovDir[x][y]].x;
4489 yy = y + move_xy[MovDir[x][y]].y;
4492 if (FrameCounter < 1 && x == 0 && y == 29)
4493 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4494 xx, yy, Feld[xx][yy],
4499 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4500 MovDir[x][y] = old_move_dir;
4502 if (!IS_FREE(xx, yy))
4503 MovDir[x][y] = old_move_dir;
4507 if (FrameCounter < 1 && x == 0 && y == 29)
4508 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4513 else if (element == EL_MOLE)
4515 boolean can_move_on =
4516 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4517 IS_AMOEBOID(Feld[move_x][move_y]) ||
4518 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4521 boolean can_turn_left =
4522 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4523 IS_AMOEBOID(Feld[left_x][left_y])));
4525 boolean can_turn_right =
4526 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4527 IS_AMOEBOID(Feld[right_x][right_y])));
4529 if (can_turn_left && can_turn_right)
4530 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4531 else if (can_turn_left)
4532 MovDir[x][y] = left_dir;
4534 MovDir[x][y] = right_dir;
4537 if (MovDir[x][y] != old_move_dir)
4540 else if (element == EL_BALLOON)
4542 MovDir[x][y] = game.balloon_dir;
4545 else if (element == EL_SPRING)
4548 if (MovDir[x][y] & MV_HORIZONTAL &&
4549 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4550 MovDir[x][y] = MV_NO_MOVING;
4552 if (MovDir[x][y] & MV_HORIZONTAL &&
4553 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4554 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4555 MovDir[x][y] = MV_NO_MOVING;
4560 else if (element == EL_ROBOT ||
4561 element == EL_SATELLITE ||
4562 element == EL_PENGUIN)
4564 int attr_x = -1, attr_y = -1;
4575 for (i = 0; i < MAX_PLAYERS; i++)
4577 struct PlayerInfo *player = &stored_player[i];
4578 int jx = player->jx, jy = player->jy;
4580 if (!player->active)
4584 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4593 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4594 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4595 game.engine_version < VERSION_IDENT(3,1,0,0)))
4597 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4604 if (element == EL_PENGUIN)
4607 static int xy[4][2] =
4615 for (i = 0; i < NUM_DIRECTIONS; i++)
4617 int ex = x + xy[i][0];
4618 int ey = y + xy[i][1];
4620 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4629 MovDir[x][y] = MV_NO_MOVING;
4631 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4632 else if (attr_x > x)
4633 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4635 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4636 else if (attr_y > y)
4637 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4639 if (element == EL_ROBOT)
4643 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4644 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4645 Moving2Blocked(x, y, &newx, &newy);
4647 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4648 MovDelay[x][y] = 8 + 8 * !RND(3);
4650 MovDelay[x][y] = 16;
4652 else if (element == EL_PENGUIN)
4658 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4660 boolean first_horiz = RND(2);
4661 int new_move_dir = MovDir[x][y];
4664 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4665 Moving2Blocked(x, y, &newx, &newy);
4667 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4671 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4672 Moving2Blocked(x, y, &newx, &newy);
4674 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4677 MovDir[x][y] = old_move_dir;
4681 else /* (element == EL_SATELLITE) */
4687 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4689 boolean first_horiz = RND(2);
4690 int new_move_dir = MovDir[x][y];
4693 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4694 Moving2Blocked(x, y, &newx, &newy);
4696 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4700 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4701 Moving2Blocked(x, y, &newx, &newy);
4703 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4706 MovDir[x][y] = old_move_dir;
4711 else if (move_pattern == MV_TURNING_LEFT ||
4712 move_pattern == MV_TURNING_RIGHT ||
4713 move_pattern == MV_TURNING_LEFT_RIGHT ||
4714 move_pattern == MV_TURNING_RIGHT_LEFT ||
4715 move_pattern == MV_TURNING_RANDOM ||
4716 move_pattern == MV_ALL_DIRECTIONS)
4718 boolean can_turn_left =
4719 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4720 boolean can_turn_right =
4721 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4723 if (move_pattern == MV_TURNING_LEFT)
4724 MovDir[x][y] = left_dir;
4725 else if (move_pattern == MV_TURNING_RIGHT)
4726 MovDir[x][y] = right_dir;
4727 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4728 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4729 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4730 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4731 else if (move_pattern == MV_TURNING_RANDOM)
4732 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4733 can_turn_right && !can_turn_left ? right_dir :
4734 RND(2) ? left_dir : right_dir);
4735 else if (can_turn_left && can_turn_right)
4736 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4737 else if (can_turn_left)
4738 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4739 else if (can_turn_right)
4740 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4742 MovDir[x][y] = back_dir;
4744 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4746 else if (move_pattern == MV_HORIZONTAL ||
4747 move_pattern == MV_VERTICAL)
4749 if (move_pattern & old_move_dir)
4750 MovDir[x][y] = back_dir;
4751 else if (move_pattern == MV_HORIZONTAL)
4752 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4753 else if (move_pattern == MV_VERTICAL)
4754 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4756 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4758 else if (move_pattern & MV_ANY_DIRECTION)
4760 MovDir[x][y] = move_pattern;
4761 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4763 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4765 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4766 MovDir[x][y] = left_dir;
4767 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4768 MovDir[x][y] = right_dir;
4770 if (MovDir[x][y] != old_move_dir)
4771 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4773 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4775 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4776 MovDir[x][y] = right_dir;
4777 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4778 MovDir[x][y] = left_dir;
4780 if (MovDir[x][y] != old_move_dir)
4781 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4783 else if (move_pattern == MV_TOWARDS_PLAYER ||
4784 move_pattern == MV_AWAY_FROM_PLAYER)
4786 int attr_x = -1, attr_y = -1;
4788 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4799 for (i = 0; i < MAX_PLAYERS; i++)
4801 struct PlayerInfo *player = &stored_player[i];
4802 int jx = player->jx, jy = player->jy;
4804 if (!player->active)
4808 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4816 MovDir[x][y] = MV_NO_MOVING;
4818 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4819 else if (attr_x > x)
4820 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4822 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4823 else if (attr_y > y)
4824 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4826 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4828 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4830 boolean first_horiz = RND(2);
4831 int new_move_dir = MovDir[x][y];
4834 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4835 Moving2Blocked(x, y, &newx, &newy);
4837 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4841 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4842 Moving2Blocked(x, y, &newx, &newy);
4844 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4847 MovDir[x][y] = old_move_dir;
4850 else if (move_pattern == MV_WHEN_PUSHED ||
4851 move_pattern == MV_WHEN_DROPPED)
4853 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4854 MovDir[x][y] = MV_NO_MOVING;
4858 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4860 static int test_xy[7][2] =
4870 static int test_dir[7] =
4880 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4881 int move_preference = -1000000; /* start with very low preference */
4882 int new_move_dir = MV_NO_MOVING;
4883 int start_test = RND(4);
4886 for (i = 0; i < NUM_DIRECTIONS; i++)
4888 int move_dir = test_dir[start_test + i];
4889 int move_dir_preference;
4891 xx = x + test_xy[start_test + i][0];
4892 yy = y + test_xy[start_test + i][1];
4894 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4895 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4897 new_move_dir = move_dir;
4902 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4905 move_dir_preference = -1 * RunnerVisit[xx][yy];
4906 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4907 move_dir_preference = PlayerVisit[xx][yy];
4909 if (move_dir_preference > move_preference)
4911 /* prefer field that has not been visited for the longest time */
4912 move_preference = move_dir_preference;
4913 new_move_dir = move_dir;
4915 else if (move_dir_preference == move_preference &&
4916 move_dir == old_move_dir)
4918 /* prefer last direction when all directions are preferred equally */
4919 move_preference = move_dir_preference;
4920 new_move_dir = move_dir;
4924 MovDir[x][y] = new_move_dir;
4925 if (old_move_dir != new_move_dir)
4930 static void TurnRound(int x, int y)
4932 int direction = MovDir[x][y];
4935 GfxDir[x][y] = MovDir[x][y];
4941 GfxDir[x][y] = MovDir[x][y];
4944 if (direction != MovDir[x][y])
4949 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4952 GfxAction[x][y] = ACTION_WAITING;
4956 static boolean JustBeingPushed(int x, int y)
4960 for (i = 0; i < MAX_PLAYERS; i++)
4962 struct PlayerInfo *player = &stored_player[i];
4964 if (player->active && player->is_pushing && player->MovPos)
4966 int next_jx = player->jx + (player->jx - player->last_jx);
4967 int next_jy = player->jy + (player->jy - player->last_jy);
4969 if (x == next_jx && y == next_jy)
4977 void StartMoving(int x, int y)
4980 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4982 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4983 int element = Feld[x][y];
4989 if (MovDelay[x][y] == 0)
4990 GfxAction[x][y] = ACTION_DEFAULT;
4992 /* !!! this should be handled more generic (not only for mole) !!! */
4993 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4994 GfxAction[x][y] = ACTION_DEFAULT;
4997 if (CAN_FALL(element) && y < lev_fieldy - 1)
4999 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5000 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5001 if (JustBeingPushed(x, y))
5004 if (element == EL_QUICKSAND_FULL)
5006 if (IS_FREE(x, y + 1))
5008 InitMovingField(x, y, MV_DOWN);
5009 started_moving = TRUE;
5011 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5012 Store[x][y] = EL_ROCK;
5014 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5016 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5019 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5021 if (!MovDelay[x][y])
5022 MovDelay[x][y] = TILEY + 1;
5031 Feld[x][y] = EL_QUICKSAND_EMPTY;
5032 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5033 Store[x][y + 1] = Store[x][y];
5036 PlayLevelSoundAction(x, y, ACTION_FILLING);
5038 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5042 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5043 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5045 InitMovingField(x, y, MV_DOWN);
5046 started_moving = TRUE;
5048 Feld[x][y] = EL_QUICKSAND_FILLING;
5049 Store[x][y] = element;
5051 PlayLevelSoundAction(x, y, ACTION_FILLING);
5053 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5056 else if (element == EL_MAGIC_WALL_FULL)
5058 if (IS_FREE(x, y + 1))
5060 InitMovingField(x, y, MV_DOWN);
5061 started_moving = TRUE;
5063 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5064 Store[x][y] = EL_CHANGED(Store[x][y]);
5066 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5068 if (!MovDelay[x][y])
5069 MovDelay[x][y] = TILEY/4 + 1;
5078 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5079 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5080 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5084 else if (element == EL_BD_MAGIC_WALL_FULL)
5086 if (IS_FREE(x, y + 1))
5088 InitMovingField(x, y, MV_DOWN);
5089 started_moving = TRUE;
5091 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5092 Store[x][y] = EL_CHANGED2(Store[x][y]);
5094 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5096 if (!MovDelay[x][y])
5097 MovDelay[x][y] = TILEY/4 + 1;
5106 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5107 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5108 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5112 else if (CAN_PASS_MAGIC_WALL(element) &&
5113 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5114 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5116 InitMovingField(x, y, MV_DOWN);
5117 started_moving = TRUE;
5120 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5121 EL_BD_MAGIC_WALL_FILLING);
5122 Store[x][y] = element;
5125 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5127 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5130 SplashAcid(x, y + 1);
5132 InitMovingField(x, y, MV_DOWN);
5133 started_moving = TRUE;
5135 Store[x][y] = EL_ACID;
5137 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5138 GfxAction[x][y + 1] = ACTION_ACTIVE;
5142 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5143 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5145 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5146 CAN_SMASH(element) && WasJustFalling[x][y] &&
5147 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5149 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5150 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5151 (Feld[x][y + 1] == EL_BLOCKED)))
5155 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5156 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5157 WasJustMoving[x][y] && !Pushed[x][y + 1])
5159 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5160 WasJustMoving[x][y])
5165 /* this is needed for a special case not covered by calling "Impact()"
5166 from "ContinueMoving()": if an element moves to a tile directly below
5167 another element which was just falling on that tile (which was empty
5168 in the previous frame), the falling element above would just stop
5169 instead of smashing the element below (in previous version, the above
5170 element was just checked for "moving" instead of "falling", resulting
5171 in incorrect smashes caused by horizontal movement of the above
5172 element; also, the case of the player being the element to smash was
5173 simply not covered here... :-/ ) */
5176 WasJustMoving[x][y] = 0;
5177 WasJustFalling[x][y] = 0;
5180 CheckCollision[x][y] = 0;
5184 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5186 if (MovDir[x][y] == MV_NO_MOVING)
5188 InitMovingField(x, y, MV_DOWN);
5189 started_moving = TRUE;
5192 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5194 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5195 MovDir[x][y] = MV_DOWN;
5197 InitMovingField(x, y, MV_DOWN);
5198 started_moving = TRUE;
5200 else if (element == EL_AMOEBA_DROP)
5202 Feld[x][y] = EL_AMOEBA_GROWING;
5203 Store[x][y] = EL_AMOEBA_WET;
5205 /* Store[x][y + 1] must be zero, because:
5206 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5209 #if OLD_GAME_BEHAVIOUR
5210 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5212 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5213 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5214 element != EL_DX_SUPABOMB)
5217 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5218 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5219 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5220 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5223 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5224 (IS_FREE(x - 1, y + 1) ||
5225 Feld[x - 1][y + 1] == EL_ACID));
5226 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5227 (IS_FREE(x + 1, y + 1) ||
5228 Feld[x + 1][y + 1] == EL_ACID));
5229 boolean can_fall_any = (can_fall_left || can_fall_right);
5230 boolean can_fall_both = (can_fall_left && can_fall_right);
5232 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5234 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5236 if (slippery_type == SLIPPERY_ONLY_LEFT)
5237 can_fall_right = FALSE;
5238 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5239 can_fall_left = FALSE;
5240 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5241 can_fall_right = FALSE;
5242 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5243 can_fall_left = FALSE;
5245 can_fall_any = (can_fall_left || can_fall_right);
5246 can_fall_both = (can_fall_left && can_fall_right);
5251 if (can_fall_both &&
5252 (game.emulation != EMU_BOULDERDASH &&
5253 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5254 can_fall_left = !(can_fall_right = RND(2));
5256 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5257 started_moving = TRUE;
5261 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5263 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5266 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5267 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5268 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5269 int belt_dir = game.belt_dir[belt_nr];
5271 if ((belt_dir == MV_LEFT && left_is_free) ||
5272 (belt_dir == MV_RIGHT && right_is_free))
5275 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5278 InitMovingField(x, y, belt_dir);
5279 started_moving = TRUE;
5282 Pushed[x][y] = TRUE;
5283 Pushed[nextx][y] = TRUE;
5286 GfxAction[x][y] = ACTION_DEFAULT;
5290 MovDir[x][y] = 0; /* if element was moving, stop it */
5295 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5297 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5299 if (CAN_MOVE(element) && !started_moving)
5302 int move_pattern = element_info[element].move_pattern;
5307 if (MovDir[x][y] == MV_NO_MOVING)
5309 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5310 x, y, element, element_info[element].token_name);
5311 printf("StartMoving(): This should never happen!\n");
5316 Moving2Blocked(x, y, &newx, &newy);
5319 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5322 if ((element == EL_SATELLITE ||
5323 element == EL_BALLOON ||
5324 element == EL_SPRING)
5325 && JustBeingPushed(x, y))
5332 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5333 CheckCollision[x][y] && IN_LEV_FIELD_AND_NOT_FREE(newx, newy))
5335 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5336 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5337 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5341 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5342 element, element_info[element].token_name,
5343 WasJustMoving[x][y],
5344 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5345 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5346 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5347 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5351 WasJustMoving[x][y] = 0;
5354 CheckCollision[x][y] = 0;
5356 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5359 if (Feld[x][y] != element) /* element has changed */
5361 element = Feld[x][y];
5362 move_pattern = element_info[element].move_pattern;
5364 if (!CAN_MOVE(element))
5368 if (Feld[x][y] != element) /* element has changed */
5376 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5377 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5379 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5381 Moving2Blocked(x, y, &newx, &newy);
5382 if (Feld[newx][newy] == EL_BLOCKED)
5383 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5389 if (FrameCounter < 1 && x == 0 && y == 29)
5390 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5393 if (!MovDelay[x][y]) /* start new movement phase */
5395 /* all objects that can change their move direction after each step
5396 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5398 if (element != EL_YAMYAM &&
5399 element != EL_DARK_YAMYAM &&
5400 element != EL_PACMAN &&
5401 !(move_pattern & MV_ANY_DIRECTION) &&
5402 move_pattern != MV_TURNING_LEFT &&
5403 move_pattern != MV_TURNING_RIGHT &&
5404 move_pattern != MV_TURNING_LEFT_RIGHT &&
5405 move_pattern != MV_TURNING_RIGHT_LEFT &&
5406 move_pattern != MV_TURNING_RANDOM)
5411 if (FrameCounter < 1 && x == 0 && y == 29)
5412 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5415 if (MovDelay[x][y] && (element == EL_BUG ||
5416 element == EL_SPACESHIP ||
5417 element == EL_SP_SNIKSNAK ||
5418 element == EL_SP_ELECTRON ||
5419 element == EL_MOLE))
5420 DrawLevelField(x, y);
5424 if (MovDelay[x][y]) /* wait some time before next movement */
5429 if (element == EL_YAMYAM)
5432 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5433 DrawLevelElementAnimation(x, y, element);
5437 if (MovDelay[x][y]) /* element still has to wait some time */
5440 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5441 ResetGfxAnimation(x, y);
5445 if (GfxAction[x][y] != ACTION_WAITING)
5446 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5448 GfxAction[x][y] = ACTION_WAITING;
5452 if (element == EL_ROBOT ||
5454 element == EL_PACMAN ||
5456 element == EL_YAMYAM ||
5457 element == EL_DARK_YAMYAM)
5460 DrawLevelElementAnimation(x, y, element);
5462 DrawLevelElementAnimationIfNeeded(x, y, element);
5464 PlayLevelSoundAction(x, y, ACTION_WAITING);
5466 else if (element == EL_SP_ELECTRON)
5467 DrawLevelElementAnimationIfNeeded(x, y, element);
5468 else if (element == EL_DRAGON)
5471 int dir = MovDir[x][y];
5472 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5473 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5474 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5475 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5476 dir == MV_UP ? IMG_FLAMES_1_UP :
5477 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5478 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5481 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5484 GfxAction[x][y] = ACTION_ATTACKING;
5486 if (IS_PLAYER(x, y))
5487 DrawPlayerField(x, y);
5489 DrawLevelField(x, y);
5491 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5493 for (i = 1; i <= 3; i++)
5495 int xx = x + i * dx;
5496 int yy = y + i * dy;
5497 int sx = SCREENX(xx);
5498 int sy = SCREENY(yy);
5499 int flame_graphic = graphic + (i - 1);
5501 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5506 int flamed = MovingOrBlocked2Element(xx, yy);
5510 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5512 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5513 RemoveMovingField(xx, yy);
5515 RemoveField(xx, yy);
5517 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5520 RemoveMovingField(xx, yy);
5524 if (ChangeDelay[xx][yy])
5525 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5526 Feld[xx][yy] == EL_BLOCKED));
5530 ChangeDelay[xx][yy] = 0;
5532 Feld[xx][yy] = EL_FLAMES;
5533 if (IN_SCR_FIELD(sx, sy))
5535 DrawLevelFieldCrumbledSand(xx, yy);
5536 DrawGraphic(sx, sy, flame_graphic, frame);
5541 if (Feld[xx][yy] == EL_FLAMES)
5542 Feld[xx][yy] = EL_EMPTY;
5543 DrawLevelField(xx, yy);
5548 if (MovDelay[x][y]) /* element still has to wait some time */
5550 PlayLevelSoundAction(x, y, ACTION_WAITING);
5556 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5557 for all other elements GfxAction will be set by InitMovingField() */
5558 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5559 GfxAction[x][y] = ACTION_MOVING;
5563 /* now make next step */
5565 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5567 if (DONT_COLLIDE_WITH(element) &&
5568 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5569 !PLAYER_ENEMY_PROTECTED(newx, newy))
5572 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5576 /* player killed by element which is deadly when colliding with */
5578 KillHero(PLAYERINFO(newx, newy));
5585 else if (CAN_MOVE_INTO_ACID(element) &&
5586 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5587 (MovDir[x][y] == MV_DOWN ||
5588 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5590 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5591 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5595 else if ((element == EL_PENGUIN ||
5596 element == EL_ROBOT ||
5597 element == EL_SATELLITE ||
5598 element == EL_BALLOON ||
5599 IS_CUSTOM_ELEMENT(element)) &&
5600 IN_LEV_FIELD(newx, newy) &&
5601 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5604 SplashAcid(newx, newy);
5605 Store[x][y] = EL_ACID;
5607 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5609 if (Feld[newx][newy] == EL_EXIT_OPEN)
5613 DrawLevelField(x, y);
5615 Feld[x][y] = EL_EMPTY;
5616 DrawLevelField(x, y);
5619 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5620 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5621 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5623 local_player->friends_still_needed--;
5624 if (!local_player->friends_still_needed &&
5625 !local_player->GameOver && AllPlayersGone)
5626 local_player->LevelSolved = local_player->GameOver = TRUE;
5630 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5632 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5633 DrawLevelField(newx, newy);
5635 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5637 else if (!IS_FREE(newx, newy))
5639 GfxAction[x][y] = ACTION_WAITING;
5641 if (IS_PLAYER(x, y))
5642 DrawPlayerField(x, y);
5644 DrawLevelField(x, y);
5649 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5651 if (IS_FOOD_PIG(Feld[newx][newy]))
5653 if (IS_MOVING(newx, newy))
5654 RemoveMovingField(newx, newy);
5657 Feld[newx][newy] = EL_EMPTY;
5658 DrawLevelField(newx, newy);
5661 PlayLevelSound(x, y, SND_PIG_DIGGING);
5663 else if (!IS_FREE(newx, newy))
5665 if (IS_PLAYER(x, y))
5666 DrawPlayerField(x, y);
5668 DrawLevelField(x, y);
5677 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5680 else if (IS_CUSTOM_ELEMENT(element) &&
5681 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5685 !IS_FREE(newx, newy)
5690 int new_element = Feld[newx][newy];
5693 printf("::: '%s' digs '%s' [%d]\n",
5694 element_info[element].token_name,
5695 element_info[Feld[newx][newy]].token_name,
5696 StorePlayer[newx][newy]);
5699 if (!IS_FREE(newx, newy))
5701 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5702 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5705 /* no element can dig solid indestructible elements */
5706 if (IS_INDESTRUCTIBLE(new_element) &&
5707 !IS_DIGGABLE(new_element) &&
5708 !IS_COLLECTIBLE(new_element))
5711 if (AmoebaNr[newx][newy] &&
5712 (new_element == EL_AMOEBA_FULL ||
5713 new_element == EL_BD_AMOEBA ||
5714 new_element == EL_AMOEBA_GROWING))
5716 AmoebaCnt[AmoebaNr[newx][newy]]--;
5717 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5720 if (IS_MOVING(newx, newy))
5721 RemoveMovingField(newx, newy);
5724 RemoveField(newx, newy);
5725 DrawLevelField(newx, newy);
5728 /* if digged element was about to explode, prevent the explosion */
5729 ExplodeField[newx][newy] = EX_TYPE_NONE;
5731 PlayLevelSoundAction(x, y, action);
5736 Store[newx][newy] = EL_EMPTY;
5737 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5738 Store[newx][newy] = element_info[element].move_leave_element;
5740 Store[newx][newy] = EL_EMPTY;
5741 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5742 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5743 Store[newx][newy] = element_info[element].move_leave_element;
5746 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5747 element_info[element].can_leave_element = TRUE;
5750 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5752 RunnerVisit[x][y] = FrameCounter;
5753 PlayerVisit[x][y] /= 8; /* expire player visit path */
5759 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5761 if (!IS_FREE(newx, newy))
5763 if (IS_PLAYER(x, y))
5764 DrawPlayerField(x, y);
5766 DrawLevelField(x, y);
5772 boolean wanna_flame = !RND(10);
5773 int dx = newx - x, dy = newy - y;
5774 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5775 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5776 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5777 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5778 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5779 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5782 IS_CLASSIC_ENEMY(element1) ||
5783 IS_CLASSIC_ENEMY(element2)) &&
5784 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5785 element1 != EL_FLAMES && element2 != EL_FLAMES)
5788 ResetGfxAnimation(x, y);
5789 GfxAction[x][y] = ACTION_ATTACKING;
5792 if (IS_PLAYER(x, y))
5793 DrawPlayerField(x, y);
5795 DrawLevelField(x, y);
5797 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5799 MovDelay[x][y] = 50;
5803 RemoveField(newx, newy);
5805 Feld[newx][newy] = EL_FLAMES;
5806 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5809 RemoveField(newx1, newy1);
5811 Feld[newx1][newy1] = EL_FLAMES;
5813 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5816 RemoveField(newx2, newy2);
5818 Feld[newx2][newy2] = EL_FLAMES;
5825 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5826 Feld[newx][newy] == EL_DIAMOND)
5828 if (IS_MOVING(newx, newy))
5829 RemoveMovingField(newx, newy);
5832 Feld[newx][newy] = EL_EMPTY;
5833 DrawLevelField(newx, newy);
5836 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5838 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5839 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5841 if (AmoebaNr[newx][newy])
5843 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5844 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5845 Feld[newx][newy] == EL_BD_AMOEBA)
5846 AmoebaCnt[AmoebaNr[newx][newy]]--;
5851 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5853 if (IS_MOVING(newx, newy))
5856 RemoveMovingField(newx, newy);
5860 Feld[newx][newy] = EL_EMPTY;
5861 DrawLevelField(newx, newy);
5864 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5866 else if ((element == EL_PACMAN || element == EL_MOLE)
5867 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5869 if (AmoebaNr[newx][newy])
5871 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5872 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5873 Feld[newx][newy] == EL_BD_AMOEBA)
5874 AmoebaCnt[AmoebaNr[newx][newy]]--;
5877 if (element == EL_MOLE)
5879 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5880 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5882 ResetGfxAnimation(x, y);
5883 GfxAction[x][y] = ACTION_DIGGING;
5884 DrawLevelField(x, y);
5886 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5888 return; /* wait for shrinking amoeba */
5890 else /* element == EL_PACMAN */
5892 Feld[newx][newy] = EL_EMPTY;
5893 DrawLevelField(newx, newy);
5894 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5897 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5898 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5899 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5901 /* wait for shrinking amoeba to completely disappear */
5904 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5906 /* object was running against a wall */
5911 if (move_pattern & MV_ANY_DIRECTION &&
5912 move_pattern == MovDir[x][y])
5914 int blocking_element =
5915 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5918 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
5919 element_info[element].token_name,
5920 element_info[blocking_element].token_name,
5924 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5927 element = Feld[x][y]; /* element might have changed */
5932 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5933 DrawLevelElementAnimation(x, y, element);
5935 if (element == EL_BUG ||
5936 element == EL_SPACESHIP ||
5937 element == EL_SP_SNIKSNAK)
5938 DrawLevelField(x, y);
5939 else if (element == EL_MOLE)
5940 DrawLevelField(x, y);
5941 else if (element == EL_BD_BUTTERFLY ||
5942 element == EL_BD_FIREFLY)
5943 DrawLevelElementAnimationIfNeeded(x, y, element);
5944 else if (element == EL_SATELLITE)
5945 DrawLevelElementAnimationIfNeeded(x, y, element);
5946 else if (element == EL_SP_ELECTRON)
5947 DrawLevelElementAnimationIfNeeded(x, y, element);
5950 if (DONT_TOUCH(element))
5951 TestIfBadThingTouchesHero(x, y);
5954 PlayLevelSoundAction(x, y, ACTION_WAITING);
5960 InitMovingField(x, y, MovDir[x][y]);
5962 PlayLevelSoundAction(x, y, ACTION_MOVING);
5966 ContinueMoving(x, y);
5969 void ContinueMoving(int x, int y)
5971 int element = Feld[x][y];
5972 int stored = Store[x][y];
5973 struct ElementInfo *ei = &element_info[element];
5974 int direction = MovDir[x][y];
5975 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5976 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5977 int newx = x + dx, newy = y + dy;
5979 int nextx = newx + dx, nexty = newy + dy;
5982 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5983 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5985 boolean pushed_by_player = Pushed[x][y];
5988 MovPos[x][y] += getElementMoveStepsize(x, y);
5991 if (pushed_by_player && IS_PLAYER(x, y))
5993 /* special case: moving object pushed by player */
5994 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5997 if (pushed_by_player) /* special case: moving object pushed by player */
5998 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6001 if (ABS(MovPos[x][y]) < TILEX)
6003 DrawLevelField(x, y);
6005 return; /* element is still moving */
6008 /* element reached destination field */
6010 Feld[x][y] = EL_EMPTY;
6011 Feld[newx][newy] = element;
6012 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6015 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6017 element = Feld[newx][newy] = EL_ACID;
6020 else if (element == EL_MOLE)
6022 Feld[x][y] = EL_SAND;
6024 DrawLevelFieldCrumbledSandNeighbours(x, y);
6026 else if (element == EL_QUICKSAND_FILLING)
6028 element = Feld[newx][newy] = get_next_element(element);
6029 Store[newx][newy] = Store[x][y];
6031 else if (element == EL_QUICKSAND_EMPTYING)
6033 Feld[x][y] = get_next_element(element);
6034 element = Feld[newx][newy] = Store[x][y];
6036 else if (element == EL_MAGIC_WALL_FILLING)
6038 element = Feld[newx][newy] = get_next_element(element);
6039 if (!game.magic_wall_active)
6040 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6041 Store[newx][newy] = Store[x][y];
6043 else if (element == EL_MAGIC_WALL_EMPTYING)
6045 Feld[x][y] = get_next_element(element);
6046 if (!game.magic_wall_active)
6047 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6048 element = Feld[newx][newy] = Store[x][y];
6050 else if (element == EL_BD_MAGIC_WALL_FILLING)
6052 element = Feld[newx][newy] = get_next_element(element);
6053 if (!game.magic_wall_active)
6054 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6055 Store[newx][newy] = Store[x][y];
6057 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6059 Feld[x][y] = get_next_element(element);
6060 if (!game.magic_wall_active)
6061 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6062 element = Feld[newx][newy] = Store[x][y];
6064 else if (element == EL_AMOEBA_DROPPING)
6066 Feld[x][y] = get_next_element(element);
6067 element = Feld[newx][newy] = Store[x][y];
6069 else if (element == EL_SOKOBAN_OBJECT)
6072 Feld[x][y] = Back[x][y];
6074 if (Back[newx][newy])
6075 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6077 Back[x][y] = Back[newx][newy] = 0;
6080 else if (Store[x][y] == EL_ACID)
6082 element = Feld[newx][newy] = EL_ACID;
6086 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6087 ei->move_leave_element != EL_EMPTY &&
6088 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6089 Store[x][y] != EL_EMPTY))
6091 /* some elements can leave other elements behind after moving */
6093 Feld[x][y] = ei->move_leave_element;
6094 InitField(x, y, FALSE);
6096 if (GFX_CRUMBLED(Feld[x][y]))
6097 DrawLevelFieldCrumbledSandNeighbours(x, y);
6101 Store[x][y] = EL_EMPTY;
6102 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6103 MovDelay[newx][newy] = 0;
6105 if (CAN_CHANGE(element))
6107 /* copy element change control values to new field */
6108 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6109 ChangePage[newx][newy] = ChangePage[x][y];
6110 Changed[newx][newy] = Changed[x][y];
6111 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6114 ChangeDelay[x][y] = 0;
6115 ChangePage[x][y] = -1;
6116 Changed[x][y] = CE_BITMASK_DEFAULT;
6117 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6119 /* copy animation control values to new field */
6120 GfxFrame[newx][newy] = GfxFrame[x][y];
6121 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6122 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6123 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6125 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6127 ResetGfxAnimation(x, y); /* reset animation values for old field */
6130 /* some elements can leave other elements behind after moving */
6132 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6133 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6134 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6136 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6137 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6141 int move_leave_element = ei->move_leave_element;
6143 Feld[x][y] = move_leave_element;
6144 InitField(x, y, FALSE);
6146 if (GFX_CRUMBLED(Feld[x][y]))
6147 DrawLevelFieldCrumbledSandNeighbours(x, y);
6149 if (ELEM_IS_PLAYER(move_leave_element))
6150 RelocatePlayer(x, y, move_leave_element);
6155 /* some elements can leave other elements behind after moving */
6156 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6157 ei->move_leave_element != EL_EMPTY &&
6158 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6159 ei->can_leave_element_last))
6161 Feld[x][y] = ei->move_leave_element;
6162 InitField(x, y, FALSE);
6164 if (GFX_CRUMBLED(Feld[x][y]))
6165 DrawLevelFieldCrumbledSandNeighbours(x, y);
6168 ei->can_leave_element_last = ei->can_leave_element;
6169 ei->can_leave_element = FALSE;
6173 /* 2.1.1 (does not work correctly for spring) */
6174 if (!CAN_MOVE(element))
6175 MovDir[newx][newy] = 0;
6179 /* (does not work for falling objects that slide horizontally) */
6180 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6181 MovDir[newx][newy] = 0;
6184 if (!CAN_MOVE(element) ||
6185 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6186 MovDir[newx][newy] = 0;
6190 if (!CAN_MOVE(element) ||
6191 (CAN_FALL(element) && direction == MV_DOWN))
6192 GfxDir[x][y] = MovDir[newx][newy] = 0;
6194 if (!CAN_MOVE(element) ||
6195 (CAN_FALL(element) && direction == MV_DOWN &&
6196 (element == EL_SPRING ||
6197 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6198 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6199 GfxDir[x][y] = MovDir[newx][newy] = 0;
6205 DrawLevelField(x, y);
6206 DrawLevelField(newx, newy);
6208 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6210 /* prevent pushed element from moving on in pushed direction */
6211 if (pushed_by_player && CAN_MOVE(element) &&
6212 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6213 !(element_info[element].move_pattern & direction))
6214 TurnRound(newx, newy);
6217 /* prevent elements on conveyor belt from moving on in last direction */
6218 if (pushed_by_conveyor && CAN_FALL(element) &&
6219 direction & MV_HORIZONTAL)
6222 if (CAN_MOVE(element))
6223 InitMovDir(newx, newy);
6225 MovDir[newx][newy] = 0;
6227 MovDir[newx][newy] = 0;
6232 if (!pushed_by_player)
6234 int nextx = newx + dx, nexty = newy + dy;
6235 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6237 WasJustMoving[newx][newy] = 3;
6239 if (CAN_FALL(element) && direction == MV_DOWN)
6240 WasJustFalling[newx][newy] = 3;
6242 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6243 CheckCollision[newx][newy] = 2;
6246 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6248 TestIfBadThingTouchesHero(newx, newy);
6249 TestIfBadThingTouchesFriend(newx, newy);
6251 if (!IS_CUSTOM_ELEMENT(element))
6252 TestIfBadThingTouchesOtherBadThing(newx, newy);
6254 else if (element == EL_PENGUIN)
6255 TestIfFriendTouchesBadThing(newx, newy);
6257 if (CAN_FALL(element) && direction == MV_DOWN &&
6258 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6262 if (pushed_by_player)
6264 static int trigger_sides[4] =
6266 CH_SIDE_RIGHT, /* moving left */
6267 CH_SIDE_LEFT, /* moving right */
6268 CH_SIDE_BOTTOM, /* moving up */
6269 CH_SIDE_TOP, /* moving down */
6271 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6272 struct PlayerInfo *player = PLAYERINFO(x, y);
6274 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6275 player->index_bit, dig_side);
6276 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6277 player->index_bit, dig_side);
6282 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6286 if (ChangePage[newx][newy] != -1) /* delayed change */
6287 ChangeElement(newx, newy, ChangePage[newx][newy]);
6292 TestIfElementHitsCustomElement(newx, newy, direction);
6296 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6298 int hitting_element = Feld[newx][newy];
6300 /* !!! fix side (direction) orientation here and elsewhere !!! */
6301 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6305 if (IN_LEV_FIELD(nextx, nexty))
6307 int opposite_direction = MV_DIR_OPPOSITE(direction);
6308 int hitting_side = direction;
6309 int touched_side = opposite_direction;
6310 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6311 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6312 MovDir[nextx][nexty] != direction ||
6313 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6319 CheckElementChangeBySide(nextx, nexty, touched_element,
6320 CE_HIT_BY_SOMETHING, opposite_direction);
6322 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6323 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6325 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6327 struct ElementChangeInfo *change =
6328 &element_info[hitting_element].change_page[i];
6330 if (change->can_change &&
6331 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6332 change->trigger_side & touched_side &&
6333 change->trigger_element == touched_element)
6335 CheckElementChangeByPage(newx, newy, hitting_element,
6336 touched_element, CE_OTHER_IS_HITTING,i);
6342 if (IS_CUSTOM_ELEMENT(touched_element) &&
6343 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6345 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6347 struct ElementChangeInfo *change =
6348 &element_info[touched_element].change_page[i];
6350 if (change->can_change &&
6351 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6352 change->trigger_side & hitting_side &&
6353 change->trigger_element == hitting_element)
6355 CheckElementChangeByPage(nextx, nexty, touched_element,
6356 hitting_element, CE_OTHER_GETS_HIT, i);
6367 TestIfPlayerTouchesCustomElement(newx, newy);
6368 TestIfElementTouchesCustomElement(newx, newy);
6371 int AmoebeNachbarNr(int ax, int ay)
6374 int element = Feld[ax][ay];
6376 static int xy[4][2] =
6384 for (i = 0; i < NUM_DIRECTIONS; i++)
6386 int x = ax + xy[i][0];
6387 int y = ay + xy[i][1];
6389 if (!IN_LEV_FIELD(x, y))
6392 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6393 group_nr = AmoebaNr[x][y];
6399 void AmoebenVereinigen(int ax, int ay)
6401 int i, x, y, xx, yy;
6402 int new_group_nr = AmoebaNr[ax][ay];
6403 static int xy[4][2] =
6411 if (new_group_nr == 0)
6414 for (i = 0; i < NUM_DIRECTIONS; i++)
6419 if (!IN_LEV_FIELD(x, y))
6422 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6423 Feld[x][y] == EL_BD_AMOEBA ||
6424 Feld[x][y] == EL_AMOEBA_DEAD) &&
6425 AmoebaNr[x][y] != new_group_nr)
6427 int old_group_nr = AmoebaNr[x][y];
6429 if (old_group_nr == 0)
6432 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6433 AmoebaCnt[old_group_nr] = 0;
6434 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6435 AmoebaCnt2[old_group_nr] = 0;
6437 for (yy = 0; yy < lev_fieldy; yy++)
6439 for (xx = 0; xx < lev_fieldx; xx++)
6441 if (AmoebaNr[xx][yy] == old_group_nr)
6442 AmoebaNr[xx][yy] = new_group_nr;
6449 void AmoebeUmwandeln(int ax, int ay)
6453 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6455 int group_nr = AmoebaNr[ax][ay];
6460 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6461 printf("AmoebeUmwandeln(): This should never happen!\n");
6466 for (y = 0; y < lev_fieldy; y++)
6468 for (x = 0; x < lev_fieldx; x++)
6470 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6473 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6477 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6478 SND_AMOEBA_TURNING_TO_GEM :
6479 SND_AMOEBA_TURNING_TO_ROCK));
6484 static int xy[4][2] =
6492 for (i = 0; i < NUM_DIRECTIONS; i++)
6497 if (!IN_LEV_FIELD(x, y))
6500 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6502 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6503 SND_AMOEBA_TURNING_TO_GEM :
6504 SND_AMOEBA_TURNING_TO_ROCK));
6511 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6514 int group_nr = AmoebaNr[ax][ay];
6515 boolean done = FALSE;
6520 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6521 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6526 for (y = 0; y < lev_fieldy; y++)
6528 for (x = 0; x < lev_fieldx; x++)
6530 if (AmoebaNr[x][y] == group_nr &&
6531 (Feld[x][y] == EL_AMOEBA_DEAD ||
6532 Feld[x][y] == EL_BD_AMOEBA ||
6533 Feld[x][y] == EL_AMOEBA_GROWING))
6536 Feld[x][y] = new_element;
6537 InitField(x, y, FALSE);
6538 DrawLevelField(x, y);
6545 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6546 SND_BD_AMOEBA_TURNING_TO_ROCK :
6547 SND_BD_AMOEBA_TURNING_TO_GEM));
6550 void AmoebeWaechst(int x, int y)
6552 static unsigned long sound_delay = 0;
6553 static unsigned long sound_delay_value = 0;
6555 if (!MovDelay[x][y]) /* start new growing cycle */
6559 if (DelayReached(&sound_delay, sound_delay_value))
6562 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6564 if (Store[x][y] == EL_BD_AMOEBA)
6565 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6567 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6569 sound_delay_value = 30;
6573 if (MovDelay[x][y]) /* wait some time before growing bigger */
6576 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6578 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6579 6 - MovDelay[x][y]);
6581 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6584 if (!MovDelay[x][y])
6586 Feld[x][y] = Store[x][y];
6588 DrawLevelField(x, y);
6593 void AmoebaDisappearing(int x, int y)
6595 static unsigned long sound_delay = 0;
6596 static unsigned long sound_delay_value = 0;
6598 if (!MovDelay[x][y]) /* start new shrinking cycle */
6602 if (DelayReached(&sound_delay, sound_delay_value))
6603 sound_delay_value = 30;
6606 if (MovDelay[x][y]) /* wait some time before shrinking */
6609 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6611 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6612 6 - MovDelay[x][y]);
6614 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6617 if (!MovDelay[x][y])
6619 Feld[x][y] = EL_EMPTY;
6620 DrawLevelField(x, y);
6622 /* don't let mole enter this field in this cycle;
6623 (give priority to objects falling to this field from above) */
6629 void AmoebeAbleger(int ax, int ay)
6632 int element = Feld[ax][ay];
6633 int graphic = el2img(element);
6634 int newax = ax, neway = ay;
6635 static int xy[4][2] =
6643 if (!level.amoeba_speed)
6645 Feld[ax][ay] = EL_AMOEBA_DEAD;
6646 DrawLevelField(ax, ay);
6650 if (IS_ANIMATED(graphic))
6651 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6653 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6654 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6656 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6659 if (MovDelay[ax][ay])
6663 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6666 int x = ax + xy[start][0];
6667 int y = ay + xy[start][1];
6669 if (!IN_LEV_FIELD(x, y))
6673 if (IS_FREE(x, y) ||
6674 CAN_GROW_INTO(Feld[x][y]) ||
6675 Feld[x][y] == EL_QUICKSAND_EMPTY)
6681 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6682 if (IS_FREE(x, y) ||
6683 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6690 if (newax == ax && neway == ay)
6693 else /* normal or "filled" (BD style) amoeba */
6696 boolean waiting_for_player = FALSE;
6698 for (i = 0; i < NUM_DIRECTIONS; i++)
6700 int j = (start + i) % 4;
6701 int x = ax + xy[j][0];
6702 int y = ay + xy[j][1];
6704 if (!IN_LEV_FIELD(x, y))
6708 if (IS_FREE(x, y) ||
6709 CAN_GROW_INTO(Feld[x][y]) ||
6710 Feld[x][y] == EL_QUICKSAND_EMPTY)
6717 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6718 if (IS_FREE(x, y) ||
6719 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6726 else if (IS_PLAYER(x, y))
6727 waiting_for_player = TRUE;
6730 if (newax == ax && neway == ay) /* amoeba cannot grow */
6733 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6735 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6738 Feld[ax][ay] = EL_AMOEBA_DEAD;
6739 DrawLevelField(ax, ay);
6740 AmoebaCnt[AmoebaNr[ax][ay]]--;
6742 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6744 if (element == EL_AMOEBA_FULL)
6745 AmoebeUmwandeln(ax, ay);
6746 else if (element == EL_BD_AMOEBA)
6747 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6752 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6754 /* amoeba gets larger by growing in some direction */
6756 int new_group_nr = AmoebaNr[ax][ay];
6759 if (new_group_nr == 0)
6761 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6762 printf("AmoebeAbleger(): This should never happen!\n");
6767 AmoebaNr[newax][neway] = new_group_nr;
6768 AmoebaCnt[new_group_nr]++;
6769 AmoebaCnt2[new_group_nr]++;
6771 /* if amoeba touches other amoeba(s) after growing, unify them */
6772 AmoebenVereinigen(newax, neway);
6774 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6776 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6782 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6783 (neway == lev_fieldy - 1 && newax != ax))
6785 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6786 Store[newax][neway] = element;
6788 else if (neway == ay)
6790 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6792 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6794 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6799 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6800 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6801 Store[ax][ay] = EL_AMOEBA_DROP;
6802 ContinueMoving(ax, ay);
6806 DrawLevelField(newax, neway);
6809 void Life(int ax, int ay)
6812 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6814 int element = Feld[ax][ay];
6815 int graphic = el2img(element);
6816 boolean changed = FALSE;
6818 if (IS_ANIMATED(graphic))
6819 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6824 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6825 MovDelay[ax][ay] = life_time;
6827 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6830 if (MovDelay[ax][ay])
6834 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6836 int xx = ax+x1, yy = ay+y1;
6839 if (!IN_LEV_FIELD(xx, yy))
6842 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6844 int x = xx+x2, y = yy+y2;
6846 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6849 if (((Feld[x][y] == element ||
6850 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6852 (IS_FREE(x, y) && Stop[x][y]))
6856 if (xx == ax && yy == ay) /* field in the middle */
6858 if (nachbarn < life[0] || nachbarn > life[1])
6860 Feld[xx][yy] = EL_EMPTY;
6862 DrawLevelField(xx, yy);
6863 Stop[xx][yy] = TRUE;
6868 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6869 { /* free border field */
6870 if (nachbarn >= life[2] && nachbarn <= life[3])
6872 Feld[xx][yy] = element;
6873 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6875 DrawLevelField(xx, yy);
6876 Stop[xx][yy] = TRUE;
6881 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6882 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6883 { /* free border field */
6884 if (nachbarn >= life[2] && nachbarn <= life[3])
6886 Feld[xx][yy] = element;
6887 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6889 DrawLevelField(xx, yy);
6890 Stop[xx][yy] = TRUE;
6898 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6899 SND_GAME_OF_LIFE_GROWING);
6902 static void InitRobotWheel(int x, int y)
6904 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6907 static void RunRobotWheel(int x, int y)
6909 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6912 static void StopRobotWheel(int x, int y)
6914 if (ZX == x && ZY == y)
6918 static void InitTimegateWheel(int x, int y)
6921 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6923 /* another brainless, "type style" bug ... :-( */
6924 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6928 static void RunTimegateWheel(int x, int y)
6930 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6933 void CheckExit(int x, int y)
6935 if (local_player->gems_still_needed > 0 ||
6936 local_player->sokobanfields_still_needed > 0 ||
6937 local_player->lights_still_needed > 0)
6939 int element = Feld[x][y];
6940 int graphic = el2img(element);
6942 if (IS_ANIMATED(graphic))
6943 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6948 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6951 Feld[x][y] = EL_EXIT_OPENING;
6953 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6956 void CheckExitSP(int x, int y)
6958 if (local_player->gems_still_needed > 0)
6960 int element = Feld[x][y];
6961 int graphic = el2img(element);
6963 if (IS_ANIMATED(graphic))
6964 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6969 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6972 Feld[x][y] = EL_SP_EXIT_OPENING;
6974 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6977 static void CloseAllOpenTimegates()
6981 for (y = 0; y < lev_fieldy; y++)
6983 for (x = 0; x < lev_fieldx; x++)
6985 int element = Feld[x][y];
6987 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6989 Feld[x][y] = EL_TIMEGATE_CLOSING;
6991 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6993 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7000 void EdelsteinFunkeln(int x, int y)
7002 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7005 if (Feld[x][y] == EL_BD_DIAMOND)
7008 if (MovDelay[x][y] == 0) /* next animation frame */
7009 MovDelay[x][y] = 11 * !SimpleRND(500);
7011 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7015 if (setup.direct_draw && MovDelay[x][y])
7016 SetDrawtoField(DRAW_BUFFERED);
7018 DrawLevelElementAnimation(x, y, Feld[x][y]);
7020 if (MovDelay[x][y] != 0)
7022 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7023 10 - MovDelay[x][y]);
7025 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7027 if (setup.direct_draw)
7031 dest_x = FX + SCREENX(x) * TILEX;
7032 dest_y = FY + SCREENY(y) * TILEY;
7034 BlitBitmap(drawto_field, window,
7035 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7036 SetDrawtoField(DRAW_DIRECT);
7042 void MauerWaechst(int x, int y)
7046 if (!MovDelay[x][y]) /* next animation frame */
7047 MovDelay[x][y] = 3 * delay;
7049 if (MovDelay[x][y]) /* wait some time before next frame */
7053 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7055 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7056 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7058 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7061 if (!MovDelay[x][y])
7063 if (MovDir[x][y] == MV_LEFT)
7065 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7066 DrawLevelField(x - 1, y);
7068 else if (MovDir[x][y] == MV_RIGHT)
7070 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7071 DrawLevelField(x + 1, y);
7073 else if (MovDir[x][y] == MV_UP)
7075 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7076 DrawLevelField(x, y - 1);
7080 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7081 DrawLevelField(x, y + 1);
7084 Feld[x][y] = Store[x][y];
7086 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7087 DrawLevelField(x, y);
7092 void MauerAbleger(int ax, int ay)
7094 int element = Feld[ax][ay];
7095 int graphic = el2img(element);
7096 boolean oben_frei = FALSE, unten_frei = FALSE;
7097 boolean links_frei = FALSE, rechts_frei = FALSE;
7098 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7099 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7100 boolean new_wall = FALSE;
7102 if (IS_ANIMATED(graphic))
7103 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7105 if (!MovDelay[ax][ay]) /* start building new wall */
7106 MovDelay[ax][ay] = 6;
7108 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7111 if (MovDelay[ax][ay])
7115 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7117 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7119 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7121 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7124 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7125 element == EL_EXPANDABLE_WALL_ANY)
7129 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7130 Store[ax][ay-1] = element;
7131 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7132 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7133 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7134 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7139 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7140 Store[ax][ay+1] = element;
7141 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7142 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7143 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7144 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7149 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7150 element == EL_EXPANDABLE_WALL_ANY ||
7151 element == EL_EXPANDABLE_WALL)
7155 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7156 Store[ax-1][ay] = element;
7157 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7158 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7159 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7160 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7166 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7167 Store[ax+1][ay] = element;
7168 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7169 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7170 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7171 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7176 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7177 DrawLevelField(ax, ay);
7179 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7181 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7182 unten_massiv = TRUE;
7183 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7184 links_massiv = TRUE;
7185 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7186 rechts_massiv = TRUE;
7188 if (((oben_massiv && unten_massiv) ||
7189 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7190 element == EL_EXPANDABLE_WALL) &&
7191 ((links_massiv && rechts_massiv) ||
7192 element == EL_EXPANDABLE_WALL_VERTICAL))
7193 Feld[ax][ay] = EL_WALL;
7197 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7199 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7203 void CheckForDragon(int x, int y)
7206 boolean dragon_found = FALSE;
7207 static int xy[4][2] =
7215 for (i = 0; i < NUM_DIRECTIONS; i++)
7217 for (j = 0; j < 4; j++)
7219 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7221 if (IN_LEV_FIELD(xx, yy) &&
7222 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7224 if (Feld[xx][yy] == EL_DRAGON)
7225 dragon_found = TRUE;
7234 for (i = 0; i < NUM_DIRECTIONS; i++)
7236 for (j = 0; j < 3; j++)
7238 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7240 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7242 Feld[xx][yy] = EL_EMPTY;
7243 DrawLevelField(xx, yy);
7252 static void InitBuggyBase(int x, int y)
7254 int element = Feld[x][y];
7255 int activating_delay = FRAMES_PER_SECOND / 4;
7258 (element == EL_SP_BUGGY_BASE ?
7259 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7260 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7262 element == EL_SP_BUGGY_BASE_ACTIVE ?
7263 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7266 static void WarnBuggyBase(int x, int y)
7269 static int xy[4][2] =
7277 for (i = 0; i < NUM_DIRECTIONS; i++)
7279 int xx = x + xy[i][0], yy = y + xy[i][1];
7281 if (IS_PLAYER(xx, yy))
7283 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7290 static void InitTrap(int x, int y)
7292 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7295 static void ActivateTrap(int x, int y)
7297 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7300 static void ChangeActiveTrap(int x, int y)
7302 int graphic = IMG_TRAP_ACTIVE;
7304 /* if new animation frame was drawn, correct crumbled sand border */
7305 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7306 DrawLevelFieldCrumbledSand(x, y);
7309 static void ChangeElementNowExt(int x, int y, int target_element)
7311 int previous_move_direction = MovDir[x][y];
7313 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7314 IS_WALKABLE(Feld[x][y]));
7316 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7317 IS_WALKABLE(Feld[x][y]) &&
7321 /* check if element under player changes from accessible to unaccessible
7322 (needed for special case of dropping element which then changes) */
7323 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7324 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7335 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7336 RemoveMovingField(x, y);
7340 Feld[x][y] = target_element;
7343 Feld[x][y] = target_element;
7346 ResetGfxAnimation(x, y);
7347 ResetRandomAnimationValue(x, y);
7349 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7350 MovDir[x][y] = previous_move_direction;
7353 InitField_WithBug1(x, y, FALSE);
7355 InitField(x, y, FALSE);
7356 if (CAN_MOVE(Feld[x][y]))
7360 DrawLevelField(x, y);
7362 if (GFX_CRUMBLED(Feld[x][y]))
7363 DrawLevelFieldCrumbledSandNeighbours(x, y);
7366 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7369 TestIfBadThingTouchesHero(x, y);
7370 TestIfPlayerTouchesCustomElement(x, y);
7371 TestIfElementTouchesCustomElement(x, y);
7374 if (ELEM_IS_PLAYER(target_element))
7375 RelocatePlayer(x, y, target_element);
7378 TestIfBadThingTouchesHero(x, y);
7379 TestIfPlayerTouchesCustomElement(x, y);
7380 TestIfElementTouchesCustomElement(x, y);
7384 static boolean ChangeElementNow(int x, int y, int element, int page)
7386 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7388 int old_element = Feld[x][y];
7390 /* always use default change event to prevent running into a loop */
7391 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7392 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7394 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7396 /* reset actual trigger element and player */
7397 change->actual_trigger_element = EL_EMPTY;
7398 change->actual_trigger_player = EL_PLAYER_1;
7401 /* do not change already changed elements with same change event */
7403 if (Changed[x][y] & ChangeEvent[x][y])
7410 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7413 /* !!! indirect change before direct change !!! */
7414 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7417 if (change->explode)
7424 if (change->use_target_content)
7426 boolean complete_replace = TRUE;
7427 boolean can_replace[3][3];
7430 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7433 boolean is_walkable;
7434 boolean is_diggable;
7435 boolean is_collectible;
7436 boolean is_removable;
7437 boolean is_destructible;
7438 int ex = x + xx - 1;
7439 int ey = y + yy - 1;
7440 int content_element = change->target_content[xx][yy];
7443 can_replace[xx][yy] = TRUE;
7445 if (ex == x && ey == y) /* do not check changing element itself */
7448 if (content_element == EL_EMPTY_SPACE)
7450 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7455 if (!IN_LEV_FIELD(ex, ey))
7457 can_replace[xx][yy] = FALSE;
7458 complete_replace = FALSE;
7465 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7466 e = MovingOrBlocked2Element(ex, ey);
7471 is_empty = (IS_FREE(ex, ey) ||
7472 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7473 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7474 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7476 is_empty = (IS_FREE(ex, ey) ||
7477 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7479 is_walkable = (is_empty || IS_WALKABLE(e));
7480 is_diggable = (is_empty || IS_DIGGABLE(e));
7481 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7482 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7483 is_removable = (is_diggable || is_collectible);
7485 can_replace[xx][yy] =
7486 ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7487 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7488 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7489 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7490 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7491 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7493 if (!can_replace[xx][yy])
7494 complete_replace = FALSE;
7496 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7497 IS_WALKABLE(content_element)));
7499 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7501 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7504 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7505 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7506 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7508 can_replace[xx][yy] = FALSE;
7509 complete_replace = FALSE;
7514 if (!change->only_if_complete || complete_replace)
7516 boolean something_has_changed = FALSE;
7518 if (change->only_if_complete && change->use_random_replace &&
7519 RND(100) < change->random_percentage)
7522 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7524 int ex = x + xx - 1;
7525 int ey = y + yy - 1;
7526 int content_element;
7528 if (can_replace[xx][yy] && (!change->use_random_replace ||
7529 RND(100) < change->random_percentage))
7531 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7532 RemoveMovingField(ex, ey);
7534 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7536 content_element = change->target_content[xx][yy];
7537 target_element = GET_TARGET_ELEMENT(content_element, change);
7539 ChangeElementNowExt(ex, ey, target_element);
7541 something_has_changed = TRUE;
7543 /* for symmetry reasons, freeze newly created border elements */
7544 if (ex != x || ey != y)
7545 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7549 if (something_has_changed)
7550 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7555 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7557 ChangeElementNowExt(x, y, target_element);
7559 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7563 /* this uses direct change before indirect change */
7564 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7570 static void ChangeElement(int x, int y, int page)
7572 int element = MovingOrBlocked2Element(x, y);
7573 struct ElementInfo *ei = &element_info[element];
7574 struct ElementChangeInfo *change = &ei->change_page[page];
7577 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7580 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7581 x, y, element, element_info[element].token_name);
7582 printf("ChangeElement(): This should never happen!\n");
7587 /* this can happen with classic bombs on walkable, changing elements */
7588 if (!CAN_CHANGE(element))
7591 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7592 ChangeDelay[x][y] = 0;
7598 if (ChangeDelay[x][y] == 0) /* initialize element change */
7600 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7601 RND(change->delay_random * change->delay_frames)) + 1;
7603 ResetGfxAnimation(x, y);
7604 ResetRandomAnimationValue(x, y);
7606 if (change->pre_change_function)
7607 change->pre_change_function(x, y);
7610 ChangeDelay[x][y]--;
7612 if (ChangeDelay[x][y] != 0) /* continue element change */
7614 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7616 if (IS_ANIMATED(graphic))
7617 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7619 if (change->change_function)
7620 change->change_function(x, y);
7622 else /* finish element change */
7624 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7626 page = ChangePage[x][y];
7627 ChangePage[x][y] = -1;
7629 change = &ei->change_page[page];
7633 if (IS_MOVING(x, y) && !change->explode)
7635 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7638 ChangeDelay[x][y] = 1; /* try change after next move step */
7639 ChangePage[x][y] = page; /* remember page to use for change */
7644 if (ChangeElementNow(x, y, element, page))
7646 if (change->post_change_function)
7647 change->post_change_function(x, y);
7652 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7653 int trigger_element,
7660 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7662 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7665 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7667 int element = EL_CUSTOM_START + i;
7669 boolean change_element = FALSE;
7672 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7675 for (j = 0; j < element_info[element].num_change_pages; j++)
7677 struct ElementChangeInfo *change = &element_info[element].change_page[j];
7679 if (change->can_change &&
7680 change->events & CH_EVENT_BIT(trigger_event) &&
7681 change->trigger_side & trigger_side &&
7682 change->trigger_player & trigger_player &&
7683 change->trigger_page & trigger_page_bits &&
7684 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7687 if (!(change->events & CH_EVENT_BIT(trigger_event)))
7688 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7689 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7692 change_element = TRUE;
7695 change->actual_trigger_element = trigger_element;
7696 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7702 if (!change_element)
7705 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7708 if (x == lx && y == ly) /* do not change trigger element itself */
7712 if (Feld[x][y] == element)
7714 ChangeDelay[x][y] = 1;
7715 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7716 ChangeElement(x, y, page);
7724 static boolean CheckElementChangeExt(int x, int y,
7726 int trigger_element,
7732 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7735 if (Feld[x][y] == EL_BLOCKED)
7737 Blocked2Moving(x, y, &x, &y);
7738 element = Feld[x][y];
7742 if (Feld[x][y] != element) /* check if element has already changed */
7745 printf("::: %d ('%s') != %d ('%s') [%d]\n",
7746 Feld[x][y], element_info[Feld[x][y]].token_name,
7747 element, element_info[element].token_name,
7756 if (trigger_page < 0)
7758 boolean change_element = FALSE;
7761 for (i = 0; i < element_info[element].num_change_pages; i++)
7763 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7765 if (change->can_change &&
7766 change->events & CH_EVENT_BIT(trigger_event) &&
7767 change->trigger_side & trigger_side &&
7768 change->trigger_player & trigger_player)
7770 change_element = TRUE;
7773 change->actual_trigger_element = trigger_element;
7774 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7780 if (!change_element)
7785 struct ElementInfo *ei = &element_info[element];
7786 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7788 change->actual_trigger_element = trigger_element;
7789 change->actual_trigger_player = EL_PLAYER_1; /* unused */
7794 /* !!! this check misses pages with same event, but different side !!! */
7796 if (trigger_page < 0)
7797 trigger_page = element_info[element].event_page_nr[trigger_event];
7799 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7803 ChangeDelay[x][y] = 1;
7804 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7805 ChangeElement(x, y, trigger_page);
7810 static void PlayPlayerSound(struct PlayerInfo *player)
7812 int jx = player->jx, jy = player->jy;
7813 int element = player->element_nr;
7814 int last_action = player->last_action_waiting;
7815 int action = player->action_waiting;
7817 if (player->is_waiting)
7819 if (action != last_action)
7820 PlayLevelSoundElementAction(jx, jy, element, action);
7822 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7826 if (action != last_action)
7827 StopSound(element_info[element].sound[last_action]);
7829 if (last_action == ACTION_SLEEPING)
7830 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7834 static void PlayAllPlayersSound()
7838 for (i = 0; i < MAX_PLAYERS; i++)
7839 if (stored_player[i].active)
7840 PlayPlayerSound(&stored_player[i]);
7843 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7845 boolean last_waiting = player->is_waiting;
7846 int move_dir = player->MovDir;
7848 player->last_action_waiting = player->action_waiting;
7852 if (!last_waiting) /* not waiting -> waiting */
7854 player->is_waiting = TRUE;
7856 player->frame_counter_bored =
7858 game.player_boring_delay_fixed +
7859 SimpleRND(game.player_boring_delay_random);
7860 player->frame_counter_sleeping =
7862 game.player_sleeping_delay_fixed +
7863 SimpleRND(game.player_sleeping_delay_random);
7865 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7868 if (game.player_sleeping_delay_fixed +
7869 game.player_sleeping_delay_random > 0 &&
7870 player->anim_delay_counter == 0 &&
7871 player->post_delay_counter == 0 &&
7872 FrameCounter >= player->frame_counter_sleeping)
7873 player->is_sleeping = TRUE;
7874 else if (game.player_boring_delay_fixed +
7875 game.player_boring_delay_random > 0 &&
7876 FrameCounter >= player->frame_counter_bored)
7877 player->is_bored = TRUE;
7879 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7880 player->is_bored ? ACTION_BORING :
7883 if (player->is_sleeping)
7885 if (player->num_special_action_sleeping > 0)
7887 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7889 int last_special_action = player->special_action_sleeping;
7890 int num_special_action = player->num_special_action_sleeping;
7891 int special_action =
7892 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7893 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7894 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7895 last_special_action + 1 : ACTION_SLEEPING);
7896 int special_graphic =
7897 el_act_dir2img(player->element_nr, special_action, move_dir);
7899 player->anim_delay_counter =
7900 graphic_info[special_graphic].anim_delay_fixed +
7901 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7902 player->post_delay_counter =
7903 graphic_info[special_graphic].post_delay_fixed +
7904 SimpleRND(graphic_info[special_graphic].post_delay_random);
7906 player->special_action_sleeping = special_action;
7909 if (player->anim_delay_counter > 0)
7911 player->action_waiting = player->special_action_sleeping;
7912 player->anim_delay_counter--;
7914 else if (player->post_delay_counter > 0)
7916 player->post_delay_counter--;
7920 else if (player->is_bored)
7922 if (player->num_special_action_bored > 0)
7924 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7926 int special_action =
7927 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7928 int special_graphic =
7929 el_act_dir2img(player->element_nr, special_action, move_dir);
7931 player->anim_delay_counter =
7932 graphic_info[special_graphic].anim_delay_fixed +
7933 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7934 player->post_delay_counter =
7935 graphic_info[special_graphic].post_delay_fixed +
7936 SimpleRND(graphic_info[special_graphic].post_delay_random);
7938 player->special_action_bored = special_action;
7941 if (player->anim_delay_counter > 0)
7943 player->action_waiting = player->special_action_bored;
7944 player->anim_delay_counter--;
7946 else if (player->post_delay_counter > 0)
7948 player->post_delay_counter--;
7953 else if (last_waiting) /* waiting -> not waiting */
7955 player->is_waiting = FALSE;
7956 player->is_bored = FALSE;
7957 player->is_sleeping = FALSE;
7959 player->frame_counter_bored = -1;
7960 player->frame_counter_sleeping = -1;
7962 player->anim_delay_counter = 0;
7963 player->post_delay_counter = 0;
7965 player->action_waiting = ACTION_DEFAULT;
7967 player->special_action_bored = ACTION_DEFAULT;
7968 player->special_action_sleeping = ACTION_DEFAULT;
7973 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7976 static byte stored_player_action[MAX_PLAYERS];
7977 static int num_stored_actions = 0;
7979 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7980 int left = player_action & JOY_LEFT;
7981 int right = player_action & JOY_RIGHT;
7982 int up = player_action & JOY_UP;
7983 int down = player_action & JOY_DOWN;
7984 int button1 = player_action & JOY_BUTTON_1;
7985 int button2 = player_action & JOY_BUTTON_2;
7986 int dx = (left ? -1 : right ? 1 : 0);
7987 int dy = (up ? -1 : down ? 1 : 0);
7990 stored_player_action[player->index_nr] = 0;
7991 num_stored_actions++;
7995 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7998 if (!player->active || tape.pausing)
8002 printf("::: [%d %d %d %d] [%d %d]\n",
8003 left, right, up, down, button1, button2);
8009 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8014 if (player->MovPos == 0)
8015 CheckGravityMovement(player);
8018 snapped = SnapField(player, dx, dy);
8022 dropped = DropElement(player);
8024 moved = MovePlayer(player, dx, dy);
8027 if (tape.single_step && tape.recording && !tape.pausing)
8029 if (button1 || (dropped && !moved))
8031 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8032 SnapField(player, 0, 0); /* stop snapping */
8036 SetPlayerWaiting(player, FALSE);
8039 return player_action;
8041 stored_player_action[player->index_nr] = player_action;
8047 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8050 /* no actions for this player (no input at player's configured device) */
8052 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8053 SnapField(player, 0, 0);
8054 CheckGravityMovementWhenNotMoving(player);
8056 if (player->MovPos == 0)
8057 SetPlayerWaiting(player, TRUE);
8059 if (player->MovPos == 0) /* needed for tape.playing */
8060 player->is_moving = FALSE;
8062 player->is_dropping = FALSE;
8068 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8070 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8072 TapeRecordAction(stored_player_action);
8073 num_stored_actions = 0;
8080 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8082 static byte stored_player_action[MAX_PLAYERS];
8083 static int num_stored_actions = 0;
8084 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8085 int left = player_action & JOY_LEFT;
8086 int right = player_action & JOY_RIGHT;
8087 int up = player_action & JOY_UP;
8088 int down = player_action & JOY_DOWN;
8089 int button1 = player_action & JOY_BUTTON_1;
8090 int button2 = player_action & JOY_BUTTON_2;
8091 int dx = (left ? -1 : right ? 1 : 0);
8092 int dy = (up ? -1 : down ? 1 : 0);
8094 stored_player_action[player->index_nr] = 0;
8095 num_stored_actions++;
8097 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8099 if (!player->active || tape.pausing)
8104 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8107 snapped = SnapField(player, dx, dy);
8111 dropped = DropElement(player);
8113 moved = MovePlayer(player, dx, dy);
8116 if (tape.single_step && tape.recording && !tape.pausing)
8118 if (button1 || (dropped && !moved))
8120 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8121 SnapField(player, 0, 0); /* stop snapping */
8125 stored_player_action[player->index_nr] = player_action;
8129 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8131 /* no actions for this player (no input at player's configured device) */
8133 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8134 SnapField(player, 0, 0);
8135 CheckGravityMovementWhenNotMoving(player);
8137 if (player->MovPos == 0)
8138 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8140 if (player->MovPos == 0) /* needed for tape.playing */
8141 player->is_moving = FALSE;
8144 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8146 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8148 TapeRecordAction(stored_player_action);
8149 num_stored_actions = 0;
8156 static unsigned long action_delay = 0;
8157 unsigned long action_delay_value;
8158 int magic_wall_x = 0, magic_wall_y = 0;
8159 int i, x, y, element, graphic;
8160 byte *recorded_player_action;
8161 byte summarized_player_action = 0;
8163 byte tape_action[MAX_PLAYERS];
8166 if (game_status != GAME_MODE_PLAYING)
8169 action_delay_value =
8170 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8172 if (tape.playing && tape.warp_forward && !tape.pausing)
8173 action_delay_value = 0;
8175 /* ---------- main game synchronization point ---------- */
8177 WaitUntilDelayReached(&action_delay, action_delay_value);
8179 if (network_playing && !network_player_action_received)
8183 printf("DEBUG: try to get network player actions in time\n");
8187 #if defined(PLATFORM_UNIX)
8188 /* last chance to get network player actions without main loop delay */
8192 if (game_status != GAME_MODE_PLAYING)
8195 if (!network_player_action_received)
8199 printf("DEBUG: failed to get network player actions in time\n");
8210 printf("::: getting new tape action [%d]\n", FrameCounter);
8213 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8216 if (recorded_player_action == NULL && tape.pausing)
8221 printf("::: %d\n", stored_player[0].action);
8225 if (recorded_player_action != NULL)
8226 for (i = 0; i < MAX_PLAYERS; i++)
8227 stored_player[i].action = recorded_player_action[i];
8230 for (i = 0; i < MAX_PLAYERS; i++)
8232 summarized_player_action |= stored_player[i].action;
8234 if (!network_playing)
8235 stored_player[i].effective_action = stored_player[i].action;
8238 #if defined(PLATFORM_UNIX)
8239 if (network_playing)
8240 SendToServer_MovePlayer(summarized_player_action);
8243 if (!options.network && !setup.team_mode)
8244 local_player->effective_action = summarized_player_action;
8247 if (recorded_player_action != NULL)
8248 for (i = 0; i < MAX_PLAYERS; i++)
8249 stored_player[i].effective_action = recorded_player_action[i];
8253 for (i = 0; i < MAX_PLAYERS; i++)
8255 tape_action[i] = stored_player[i].effective_action;
8257 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8258 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8261 /* only save actions from input devices, but not programmed actions */
8263 TapeRecordAction(tape_action);
8266 for (i = 0; i < MAX_PLAYERS; i++)
8268 int actual_player_action = stored_player[i].effective_action;
8271 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8272 - rnd_equinox_tetrachloride 048
8273 - rnd_equinox_tetrachloride_ii 096
8274 - rnd_emanuel_schmieg 002
8275 - doctor_sloan_ww 001, 020
8277 if (stored_player[i].MovPos == 0)
8278 CheckGravityMovement(&stored_player[i]);
8282 /* overwrite programmed action with tape action */
8283 if (stored_player[i].programmed_action)
8284 actual_player_action = stored_player[i].programmed_action;
8288 if (stored_player[i].programmed_action)
8289 printf("::: %d\n", stored_player[i].programmed_action);
8292 if (recorded_player_action)
8295 if (stored_player[i].programmed_action &&
8296 stored_player[i].programmed_action != recorded_player_action[i])
8297 printf("::: %d: %d <-> %d\n", i,
8298 stored_player[i].programmed_action, recorded_player_action[i]);
8302 actual_player_action = recorded_player_action[i];
8307 /* overwrite tape action with programmed action */
8308 if (stored_player[i].programmed_action)
8309 actual_player_action = stored_player[i].programmed_action;
8314 printf("::: action: %d: %x [%d]\n",
8315 stored_player[i].MovPos, actual_player_action, FrameCounter);
8319 PlayerActions(&stored_player[i], actual_player_action);
8321 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8323 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8324 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8327 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8332 TapeRecordAction(tape_action);
8335 network_player_action_received = FALSE;
8337 ScrollScreen(NULL, SCROLL_GO_ON);
8343 for (i = 0; i < MAX_PLAYERS; i++)
8344 stored_player[i].Frame++;
8348 /* for downwards compatibility, the following code emulates a fixed bug that
8349 occured when pushing elements (causing elements that just made their last
8350 pushing step to already (if possible) make their first falling step in the
8351 same game frame, which is bad); this code is also needed to use the famous
8352 "spring push bug" which is used in older levels and might be wanted to be
8353 used also in newer levels, but in this case the buggy pushing code is only
8354 affecting the "spring" element and no other elements */
8357 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8359 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8362 for (i = 0; i < MAX_PLAYERS; i++)
8364 struct PlayerInfo *player = &stored_player[i];
8369 if (player->active && player->is_pushing && player->is_moving &&
8371 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8372 Feld[x][y] == EL_SPRING))
8374 if (player->active && player->is_pushing && player->is_moving &&
8378 ContinueMoving(x, y);
8380 /* continue moving after pushing (this is actually a bug) */
8381 if (!IS_MOVING(x, y))
8390 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8392 Changed[x][y] = CE_BITMASK_DEFAULT;
8393 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8396 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8398 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8399 printf("GameActions(): This should never happen!\n");
8401 ChangePage[x][y] = -1;
8406 if (WasJustMoving[x][y] > 0)
8407 WasJustMoving[x][y]--;
8408 if (WasJustFalling[x][y] > 0)
8409 WasJustFalling[x][y]--;
8410 if (CheckCollision[x][y] > 0)
8411 CheckCollision[x][y]--;
8416 /* reset finished pushing action (not done in ContinueMoving() to allow
8417 continous pushing animation for elements with zero push delay) */
8418 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8420 ResetGfxAnimation(x, y);
8421 DrawLevelField(x, y);
8426 if (IS_BLOCKED(x, y))
8430 Blocked2Moving(x, y, &oldx, &oldy);
8431 if (!IS_MOVING(oldx, oldy))
8433 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8434 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8435 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8436 printf("GameActions(): This should never happen!\n");
8442 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8444 element = Feld[x][y];
8446 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8448 graphic = el2img(element);
8454 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8456 element = graphic = 0;
8460 if (graphic_info[graphic].anim_global_sync)
8461 GfxFrame[x][y] = FrameCounter;
8463 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8464 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8465 ResetRandomAnimationValue(x, y);
8467 SetRandomAnimationValue(x, y);
8470 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8473 if (IS_INACTIVE(element))
8475 if (IS_ANIMATED(graphic))
8476 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8482 /* this may take place after moving, so 'element' may have changed */
8484 if (IS_CHANGING(x, y))
8486 if (IS_CHANGING(x, y) &&
8487 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8491 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8492 element_info[element].event_page_nr[CE_DELAY]);
8494 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8497 element = Feld[x][y];
8498 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8502 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8507 element = Feld[x][y];
8508 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8510 if (element == EL_MOLE)
8511 printf("::: %d, %d, %d [%d]\n",
8512 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8516 if (element == EL_YAMYAM)
8517 printf("::: %d, %d, %d\n",
8518 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8522 if (IS_ANIMATED(graphic) &&
8526 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8529 if (element == EL_BUG)
8530 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8534 if (element == EL_MOLE)
8535 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8539 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8540 EdelsteinFunkeln(x, y);
8542 else if ((element == EL_ACID ||
8543 element == EL_EXIT_OPEN ||
8544 element == EL_SP_EXIT_OPEN ||
8545 element == EL_SP_TERMINAL ||
8546 element == EL_SP_TERMINAL_ACTIVE ||
8547 element == EL_EXTRA_TIME ||
8548 element == EL_SHIELD_NORMAL ||
8549 element == EL_SHIELD_DEADLY) &&
8550 IS_ANIMATED(graphic))
8551 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8552 else if (IS_MOVING(x, y))
8553 ContinueMoving(x, y);
8554 else if (IS_ACTIVE_BOMB(element))
8555 CheckDynamite(x, y);
8557 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8558 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8560 else if (element == EL_AMOEBA_GROWING)
8561 AmoebeWaechst(x, y);
8562 else if (element == EL_AMOEBA_SHRINKING)
8563 AmoebaDisappearing(x, y);
8565 #if !USE_NEW_AMOEBA_CODE
8566 else if (IS_AMOEBALIVE(element))
8567 AmoebeAbleger(x, y);
8570 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8572 else if (element == EL_EXIT_CLOSED)
8574 else if (element == EL_SP_EXIT_CLOSED)
8576 else if (element == EL_EXPANDABLE_WALL_GROWING)
8578 else if (element == EL_EXPANDABLE_WALL ||
8579 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8580 element == EL_EXPANDABLE_WALL_VERTICAL ||
8581 element == EL_EXPANDABLE_WALL_ANY)
8583 else if (element == EL_FLAMES)
8584 CheckForDragon(x, y);
8586 else if (IS_AUTO_CHANGING(element))
8587 ChangeElement(x, y);
8589 else if (element == EL_EXPLOSION)
8590 ; /* drawing of correct explosion animation is handled separately */
8591 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8592 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8595 /* this may take place after moving, so 'element' may have changed */
8596 if (IS_AUTO_CHANGING(Feld[x][y]))
8597 ChangeElement(x, y);
8600 if (IS_BELT_ACTIVE(element))
8601 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8603 if (game.magic_wall_active)
8605 int jx = local_player->jx, jy = local_player->jy;
8607 /* play the element sound at the position nearest to the player */
8608 if ((element == EL_MAGIC_WALL_FULL ||
8609 element == EL_MAGIC_WALL_ACTIVE ||
8610 element == EL_MAGIC_WALL_EMPTYING ||
8611 element == EL_BD_MAGIC_WALL_FULL ||
8612 element == EL_BD_MAGIC_WALL_ACTIVE ||
8613 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8614 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8622 #if USE_NEW_AMOEBA_CODE
8623 /* new experimental amoeba growth stuff */
8625 if (!(FrameCounter % 8))
8628 static unsigned long random = 1684108901;
8630 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8633 x = (random >> 10) % lev_fieldx;
8634 y = (random >> 20) % lev_fieldy;
8636 x = RND(lev_fieldx);
8637 y = RND(lev_fieldy);
8639 element = Feld[x][y];
8642 if (!IS_PLAYER(x,y) &&
8643 (element == EL_EMPTY ||
8644 CAN_GROW_INTO(element) ||
8645 element == EL_QUICKSAND_EMPTY ||
8646 element == EL_ACID_SPLASH_LEFT ||
8647 element == EL_ACID_SPLASH_RIGHT))
8649 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8650 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8651 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8652 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8653 Feld[x][y] = EL_AMOEBA_DROP;
8656 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8657 if (!IS_PLAYER(x,y) &&
8658 (element == EL_EMPTY ||
8659 element == EL_SAND ||
8660 element == EL_QUICKSAND_EMPTY ||
8661 element == EL_ACID_SPLASH_LEFT ||
8662 element == EL_ACID_SPLASH_RIGHT))
8664 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8665 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8666 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8667 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8668 Feld[x][y] = EL_AMOEBA_DROP;
8672 random = random * 129 + 1;
8678 if (game.explosions_delayed)
8681 game.explosions_delayed = FALSE;
8683 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8685 element = Feld[x][y];
8687 if (ExplodeField[x][y])
8688 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8689 else if (element == EL_EXPLOSION)
8690 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8692 ExplodeField[x][y] = EX_TYPE_NONE;
8695 game.explosions_delayed = TRUE;
8698 if (game.magic_wall_active)
8700 if (!(game.magic_wall_time_left % 4))
8702 int element = Feld[magic_wall_x][magic_wall_y];
8704 if (element == EL_BD_MAGIC_WALL_FULL ||
8705 element == EL_BD_MAGIC_WALL_ACTIVE ||
8706 element == EL_BD_MAGIC_WALL_EMPTYING)
8707 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8709 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8712 if (game.magic_wall_time_left > 0)
8714 game.magic_wall_time_left--;
8715 if (!game.magic_wall_time_left)
8717 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8719 element = Feld[x][y];
8721 if (element == EL_MAGIC_WALL_ACTIVE ||
8722 element == EL_MAGIC_WALL_FULL)
8724 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8725 DrawLevelField(x, y);
8727 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8728 element == EL_BD_MAGIC_WALL_FULL)
8730 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8731 DrawLevelField(x, y);
8735 game.magic_wall_active = FALSE;
8740 if (game.light_time_left > 0)
8742 game.light_time_left--;
8744 if (game.light_time_left == 0)
8745 RedrawAllLightSwitchesAndInvisibleElements();
8748 if (game.timegate_time_left > 0)
8750 game.timegate_time_left--;
8752 if (game.timegate_time_left == 0)
8753 CloseAllOpenTimegates();
8756 for (i = 0; i < MAX_PLAYERS; i++)
8758 struct PlayerInfo *player = &stored_player[i];
8760 if (SHIELD_ON(player))
8762 if (player->shield_deadly_time_left)
8763 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8764 else if (player->shield_normal_time_left)
8765 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8769 if (TimeFrames >= FRAMES_PER_SECOND)
8774 if (!level.use_step_counter)
8778 for (i = 0; i < MAX_PLAYERS; i++)
8780 struct PlayerInfo *player = &stored_player[i];
8782 if (SHIELD_ON(player))
8784 player->shield_normal_time_left--;
8786 if (player->shield_deadly_time_left > 0)
8787 player->shield_deadly_time_left--;
8795 if (TimeLeft <= 10 && setup.time_limit)
8796 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8798 DrawGameValue_Time(TimeLeft);
8800 if (!TimeLeft && setup.time_limit)
8801 for (i = 0; i < MAX_PLAYERS; i++)
8802 KillHero(&stored_player[i]);
8804 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8805 DrawGameValue_Time(TimePlayed);
8808 if (tape.recording || tape.playing)
8809 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8813 PlayAllPlayersSound();
8815 if (options.debug) /* calculate frames per second */
8817 static unsigned long fps_counter = 0;
8818 static int fps_frames = 0;
8819 unsigned long fps_delay_ms = Counter() - fps_counter;
8823 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8825 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8828 fps_counter = Counter();
8831 redraw_mask |= REDRAW_FPS;
8835 if (stored_player[0].jx != stored_player[0].last_jx ||
8836 stored_player[0].jy != stored_player[0].last_jy)
8837 printf("::: %d, %d, %d, %d, %d\n",
8838 stored_player[0].MovDir,
8839 stored_player[0].MovPos,
8840 stored_player[0].GfxPos,
8841 stored_player[0].Frame,
8842 stored_player[0].StepFrame);
8849 for (i = 0; i < MAX_PLAYERS; i++)
8852 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8854 stored_player[i].Frame += move_frames;
8856 if (stored_player[i].MovPos != 0)
8857 stored_player[i].StepFrame += move_frames;
8859 if (stored_player[i].drop_delay > 0)
8860 stored_player[i].drop_delay--;
8865 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8867 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8869 local_player->show_envelope = 0;
8874 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8876 int min_x = x, min_y = y, max_x = x, max_y = y;
8879 for (i = 0; i < MAX_PLAYERS; i++)
8881 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8883 if (!stored_player[i].active || &stored_player[i] == player)
8886 min_x = MIN(min_x, jx);
8887 min_y = MIN(min_y, jy);
8888 max_x = MAX(max_x, jx);
8889 max_y = MAX(max_y, jy);
8892 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8895 static boolean AllPlayersInVisibleScreen()
8899 for (i = 0; i < MAX_PLAYERS; i++)
8901 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8903 if (!stored_player[i].active)
8906 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8913 void ScrollLevel(int dx, int dy)
8915 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8918 BlitBitmap(drawto_field, drawto_field,
8919 FX + TILEX * (dx == -1) - softscroll_offset,
8920 FY + TILEY * (dy == -1) - softscroll_offset,
8921 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8922 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8923 FX + TILEX * (dx == 1) - softscroll_offset,
8924 FY + TILEY * (dy == 1) - softscroll_offset);
8928 x = (dx == 1 ? BX1 : BX2);
8929 for (y = BY1; y <= BY2; y++)
8930 DrawScreenField(x, y);
8935 y = (dy == 1 ? BY1 : BY2);
8936 for (x = BX1; x <= BX2; x++)
8937 DrawScreenField(x, y);
8940 redraw_mask |= REDRAW_FIELD;
8944 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8946 int nextx = x + dx, nexty = y + dy;
8947 int element = Feld[x][y];
8950 element != EL_SP_PORT_LEFT &&
8951 element != EL_SP_GRAVITY_PORT_LEFT &&
8952 element != EL_SP_PORT_HORIZONTAL &&
8953 element != EL_SP_PORT_ANY) ||
8955 element != EL_SP_PORT_RIGHT &&
8956 element != EL_SP_GRAVITY_PORT_RIGHT &&
8957 element != EL_SP_PORT_HORIZONTAL &&
8958 element != EL_SP_PORT_ANY) ||
8960 element != EL_SP_PORT_UP &&
8961 element != EL_SP_GRAVITY_PORT_UP &&
8962 element != EL_SP_PORT_VERTICAL &&
8963 element != EL_SP_PORT_ANY) ||
8965 element != EL_SP_PORT_DOWN &&
8966 element != EL_SP_GRAVITY_PORT_DOWN &&
8967 element != EL_SP_PORT_VERTICAL &&
8968 element != EL_SP_PORT_ANY) ||
8969 !IN_LEV_FIELD(nextx, nexty) ||
8970 !IS_FREE(nextx, nexty))
8977 static boolean canFallDown(struct PlayerInfo *player)
8979 int jx = player->jx, jy = player->jy;
8981 return (IN_LEV_FIELD(jx, jy + 1) &&
8982 (IS_FREE(jx, jy + 1) ||
8983 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8984 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8985 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8988 static boolean canPassField(int x, int y, int move_dir)
8990 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8991 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8992 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8995 int element = Feld[x][y];
8997 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8998 !CAN_MOVE(element) &&
8999 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9000 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9001 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9004 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9006 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9007 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9008 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9012 int nextx = newx + dx;
9013 int nexty = newy + dy;
9017 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9018 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9019 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9020 canPassField(newx, newy, move_dir)));
9022 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9023 (IS_DIGGABLE(Feld[newx][newy]) ||
9024 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9025 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9026 !CAN_MOVE(Feld[newx][newy]) &&
9027 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9028 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9029 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9033 static void CheckGravityMovement(struct PlayerInfo *player)
9035 if (game.gravity && !player->programmed_action)
9038 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9039 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9041 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9042 int move_dir_vertical = player->action & MV_VERTICAL;
9046 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9048 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9051 int jx = player->jx, jy = player->jy;
9053 boolean player_is_moving_to_valid_field =
9054 (!player_is_snapping &&
9055 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9056 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9060 (player->last_move_dir & MV_HORIZONTAL ?
9061 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9062 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9066 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9067 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9068 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9069 int new_jx = jx + dx, new_jy = jy + dy;
9070 int nextx = new_jx + dx, nexty = new_jy + dy;
9076 boolean player_can_fall_down = canFallDown(player);
9078 boolean player_can_fall_down =
9079 (IN_LEV_FIELD(jx, jy + 1) &&
9080 (IS_FREE(jx, jy + 1) ||
9081 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9085 boolean player_can_fall_down =
9086 (IN_LEV_FIELD(jx, jy + 1) &&
9087 (IS_FREE(jx, jy + 1)));
9091 boolean player_is_moving_to_valid_field =
9094 !player_is_snapping &&
9098 IN_LEV_FIELD(new_jx, new_jy) &&
9099 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9100 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9101 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9102 IN_LEV_FIELD(nextx, nexty) &&
9103 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9105 IN_LEV_FIELD(new_jx, new_jy) &&
9106 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9107 Feld[new_jx][new_jy] == EL_SAND ||
9108 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9109 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9110 /* !!! extend EL_SAND to anything diggable !!! */
9116 boolean player_is_standing_on_valid_field =
9117 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9118 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9122 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9123 player_can_fall_down,
9124 player_is_standing_on_valid_field,
9125 player_is_moving_to_valid_field,
9126 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9127 player->effective_action,
9128 player->can_fall_into_acid);
9131 if (player_can_fall_down &&
9133 !player_is_standing_on_valid_field &&
9135 !player_is_moving_to_valid_field)
9138 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9139 jx, jy, FrameCounter);
9142 player->programmed_action = MV_DOWN;
9147 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9150 return CheckGravityMovement(player);
9153 if (game.gravity && !player->programmed_action)
9155 int jx = player->jx, jy = player->jy;
9156 boolean field_under_player_is_free =
9157 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9158 boolean player_is_standing_on_valid_field =
9159 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9160 (IS_WALKABLE(Feld[jx][jy]) &&
9161 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9163 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9164 player->programmed_action = MV_DOWN;
9170 -----------------------------------------------------------------------------
9171 dx, dy: direction (non-diagonal) to try to move the player to
9172 real_dx, real_dy: direction as read from input device (can be diagonal)
9175 boolean MovePlayerOneStep(struct PlayerInfo *player,
9176 int dx, int dy, int real_dx, int real_dy)
9179 static int trigger_sides[4][2] =
9181 /* enter side leave side */
9182 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9183 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9184 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9185 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9187 int move_direction = (dx == -1 ? MV_LEFT :
9188 dx == +1 ? MV_RIGHT :
9190 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9191 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9192 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9194 int jx = player->jx, jy = player->jy;
9195 int new_jx = jx + dx, new_jy = jy + dy;
9199 if (!player->active || (!dx && !dy))
9200 return MF_NO_ACTION;
9202 player->MovDir = (dx < 0 ? MV_LEFT :
9205 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9207 if (!IN_LEV_FIELD(new_jx, new_jy))
9208 return MF_NO_ACTION;
9210 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9211 return MF_NO_ACTION;
9214 element = MovingOrBlocked2Element(new_jx, new_jy);
9216 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9219 if (DONT_RUN_INTO(element))
9221 if (element == EL_ACID && dx == 0 && dy == 1)
9223 SplashAcid(new_jx, new_jy);
9224 Feld[jx][jy] = EL_PLAYER_1;
9225 InitMovingField(jx, jy, MV_DOWN);
9226 Store[jx][jy] = EL_ACID;
9227 ContinueMoving(jx, jy);
9231 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9236 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9237 if (can_move != MF_MOVING)
9240 /* check if DigField() has caused relocation of the player */
9241 if (player->jx != jx || player->jy != jy)
9242 return MF_NO_ACTION;
9244 StorePlayer[jx][jy] = 0;
9245 player->last_jx = jx;
9246 player->last_jy = jy;
9247 player->jx = new_jx;
9248 player->jy = new_jy;
9249 StorePlayer[new_jx][new_jy] = player->element_nr;
9252 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9254 player->step_counter++;
9257 player->drop_delay = 0;
9260 PlayerVisit[jx][jy] = FrameCounter;
9262 ScrollPlayer(player, SCROLL_INIT);
9265 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9267 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9269 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9272 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9274 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9275 CE_OTHER_GETS_ENTERED, enter_side);
9276 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9277 CE_ENTERED_BY_PLAYER, enter_side);
9284 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9286 int jx = player->jx, jy = player->jy;
9287 int old_jx = jx, old_jy = jy;
9288 int moved = MF_NO_ACTION;
9291 if (!player->active)
9296 if (player->MovPos == 0)
9298 player->is_moving = FALSE;
9299 player->is_digging = FALSE;
9300 player->is_collecting = FALSE;
9301 player->is_snapping = FALSE;
9302 player->is_pushing = FALSE;
9308 if (!player->active || (!dx && !dy))
9313 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9319 if (!FrameReached(&player->move_delay, player->move_delay_value))
9322 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9323 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9329 /* store if player is automatically moved to next field */
9330 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9332 /* remove the last programmed player action */
9333 player->programmed_action = 0;
9337 /* should only happen if pre-1.2 tape recordings are played */
9338 /* this is only for backward compatibility */
9340 int original_move_delay_value = player->move_delay_value;
9343 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9347 /* scroll remaining steps with finest movement resolution */
9348 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9350 while (player->MovPos)
9352 ScrollPlayer(player, SCROLL_GO_ON);
9353 ScrollScreen(NULL, SCROLL_GO_ON);
9359 player->move_delay_value = original_move_delay_value;
9362 if (player->last_move_dir & MV_HORIZONTAL)
9364 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9365 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9369 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9370 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9376 if (moved & MF_MOVING && !ScreenMovPos &&
9377 (player == local_player || !options.network))
9379 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9380 int offset = (setup.scroll_delay ? 3 : 0);
9382 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9384 /* actual player has left the screen -- scroll in that direction */
9385 if (jx != old_jx) /* player has moved horizontally */
9386 scroll_x += (jx - old_jx);
9387 else /* player has moved vertically */
9388 scroll_y += (jy - old_jy);
9392 if (jx != old_jx) /* player has moved horizontally */
9394 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9395 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9396 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9398 /* don't scroll over playfield boundaries */
9399 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9400 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9402 /* don't scroll more than one field at a time */
9403 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9405 /* don't scroll against the player's moving direction */
9406 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9407 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9408 scroll_x = old_scroll_x;
9410 else /* player has moved vertically */
9412 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9413 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9414 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9416 /* don't scroll over playfield boundaries */
9417 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9418 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9420 /* don't scroll more than one field at a time */
9421 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9423 /* don't scroll against the player's moving direction */
9424 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9425 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9426 scroll_y = old_scroll_y;
9430 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9432 if (!options.network && !AllPlayersInVisibleScreen())
9434 scroll_x = old_scroll_x;
9435 scroll_y = old_scroll_y;
9439 ScrollScreen(player, SCROLL_INIT);
9440 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9447 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9449 if (!(moved & MF_MOVING) && !player->is_pushing)
9454 player->StepFrame = 0;
9456 if (moved & MF_MOVING)
9458 if (old_jx != jx && old_jy == jy)
9459 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9460 else if (old_jx == jx && old_jy != jy)
9461 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9463 DrawLevelField(jx, jy); /* for "crumbled sand" */
9465 player->last_move_dir = player->MovDir;
9466 player->is_moving = TRUE;
9468 player->is_snapping = FALSE;
9472 player->is_switching = FALSE;
9475 player->is_dropping = FALSE;
9479 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9482 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9485 static int trigger_sides[4][2] =
9487 /* enter side leave side */
9488 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9489 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9490 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9491 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9493 int move_direction = player->MovDir;
9494 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9495 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9496 int old_element = Feld[old_jx][old_jy];
9497 int new_element = Feld[jx][jy];
9500 /* !!! TEST ONLY !!! */
9501 if (IS_CUSTOM_ELEMENT(old_element))
9502 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9504 player->index_bit, leave_side);
9506 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9508 player->index_bit, leave_side);
9510 if (IS_CUSTOM_ELEMENT(new_element))
9511 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9512 player->index_bit, enter_side);
9514 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9515 CE_OTHER_GETS_ENTERED,
9516 player->index_bit, enter_side);
9526 CheckGravityMovementWhenNotMoving(player);
9529 player->last_move_dir = MV_NO_MOVING;
9531 player->is_moving = FALSE;
9534 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9536 TestIfHeroTouchesBadThing(jx, jy);
9537 TestIfPlayerTouchesCustomElement(jx, jy);
9540 if (!player->active)
9546 void ScrollPlayer(struct PlayerInfo *player, int mode)
9548 int jx = player->jx, jy = player->jy;
9549 int last_jx = player->last_jx, last_jy = player->last_jy;
9550 int move_stepsize = TILEX / player->move_delay_value;
9552 if (!player->active || !player->MovPos)
9555 if (mode == SCROLL_INIT)
9557 player->actual_frame_counter = FrameCounter;
9558 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9560 if (Feld[last_jx][last_jy] == EL_EMPTY)
9561 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9569 else if (!FrameReached(&player->actual_frame_counter, 1))
9572 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9573 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9575 if (!player->block_last_field &&
9576 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9577 Feld[last_jx][last_jy] = EL_EMPTY;
9579 /* before DrawPlayer() to draw correct player graphic for this case */
9580 if (player->MovPos == 0)
9581 CheckGravityMovement(player);
9584 DrawPlayer(player); /* needed here only to cleanup last field */
9587 if (player->MovPos == 0) /* player reached destination field */
9590 if (player->move_delay_reset_counter > 0)
9592 player->move_delay_reset_counter--;
9594 if (player->move_delay_reset_counter == 0)
9596 /* continue with normal speed after quickly moving through gate */
9597 HALVE_PLAYER_SPEED(player);
9599 /* be able to make the next move without delay */
9600 player->move_delay = 0;
9604 if (IS_PASSABLE(Feld[last_jx][last_jy]))
9606 /* continue with normal speed after quickly moving through gate */
9607 HALVE_PLAYER_SPEED(player);
9609 /* be able to make the next move without delay */
9610 player->move_delay = 0;
9614 if (player->block_last_field &&
9615 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9616 Feld[last_jx][last_jy] = EL_EMPTY;
9618 player->last_jx = jx;
9619 player->last_jy = jy;
9621 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9622 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9623 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9625 DrawPlayer(player); /* needed here only to cleanup last field */
9628 if (local_player->friends_still_needed == 0 ||
9629 IS_SP_ELEMENT(Feld[jx][jy]))
9630 player->LevelSolved = player->GameOver = TRUE;
9634 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9635 /* this breaks one level: "machine", level 000 */
9637 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9640 static int trigger_sides[4][2] =
9642 /* enter side leave side */
9643 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9644 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9645 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9646 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9648 int move_direction = player->MovDir;
9649 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9650 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9651 int old_jx = last_jx;
9652 int old_jy = last_jy;
9653 int old_element = Feld[old_jx][old_jy];
9654 int new_element = Feld[jx][jy];
9657 /* !!! TEST ONLY !!! */
9658 if (IS_CUSTOM_ELEMENT(old_element))
9659 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9661 player->index_bit, leave_side);
9663 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9665 player->index_bit, leave_side);
9667 if (IS_CUSTOM_ELEMENT(new_element))
9668 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9669 player->index_bit, enter_side);
9671 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9672 CE_OTHER_GETS_ENTERED,
9673 player->index_bit, enter_side);
9679 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9681 TestIfHeroTouchesBadThing(jx, jy);
9682 TestIfPlayerTouchesCustomElement(jx, jy);
9684 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9687 if (!player->active)
9691 if (level.use_step_counter)
9697 for (i = 0; i < MAX_PLAYERS; i++)
9699 struct PlayerInfo *player = &stored_player[i];
9701 if (SHIELD_ON(player))
9703 player->shield_normal_time_left--;
9705 if (player->shield_deadly_time_left > 0)
9706 player->shield_deadly_time_left--;
9714 if (TimeLeft <= 10 && setup.time_limit)
9715 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9717 DrawGameValue_Time(TimeLeft);
9719 if (!TimeLeft && setup.time_limit)
9720 for (i = 0; i < MAX_PLAYERS; i++)
9721 KillHero(&stored_player[i]);
9723 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9724 DrawGameValue_Time(TimePlayed);
9727 if (tape.single_step && tape.recording && !tape.pausing &&
9728 !player->programmed_action)
9729 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9733 void ScrollScreen(struct PlayerInfo *player, int mode)
9735 static unsigned long screen_frame_counter = 0;
9737 if (mode == SCROLL_INIT)
9739 /* set scrolling step size according to actual player's moving speed */
9740 ScrollStepSize = TILEX / player->move_delay_value;
9742 screen_frame_counter = FrameCounter;
9743 ScreenMovDir = player->MovDir;
9744 ScreenMovPos = player->MovPos;
9745 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9748 else if (!FrameReached(&screen_frame_counter, 1))
9753 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9754 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9755 redraw_mask |= REDRAW_FIELD;
9758 ScreenMovDir = MV_NO_MOVING;
9761 void TestIfPlayerTouchesCustomElement(int x, int y)
9763 static int xy[4][2] =
9770 static int trigger_sides[4][2] =
9772 /* center side border side */
9773 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9774 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9775 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9776 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9778 static int touch_dir[4] =
9785 int center_element = Feld[x][y]; /* should always be non-moving! */
9788 for (i = 0; i < NUM_DIRECTIONS; i++)
9790 int xx = x + xy[i][0];
9791 int yy = y + xy[i][1];
9792 int center_side = trigger_sides[i][0];
9793 int border_side = trigger_sides[i][1];
9796 if (!IN_LEV_FIELD(xx, yy))
9799 if (IS_PLAYER(x, y))
9801 struct PlayerInfo *player = PLAYERINFO(x, y);
9803 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9804 border_element = Feld[xx][yy]; /* may be moving! */
9805 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9806 border_element = Feld[xx][yy];
9807 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9808 border_element = MovingOrBlocked2Element(xx, yy);
9810 continue; /* center and border element do not touch */
9813 /* !!! TEST ONLY !!! */
9814 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9815 player->index_bit, border_side);
9816 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9817 CE_OTHER_GETS_TOUCHED,
9818 player->index_bit, border_side);
9820 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9821 CE_OTHER_GETS_TOUCHED,
9822 player->index_bit, border_side);
9823 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9824 player->index_bit, border_side);
9827 else if (IS_PLAYER(xx, yy))
9829 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9831 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9833 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9834 continue; /* center and border element do not touch */
9838 /* !!! TEST ONLY !!! */
9839 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9840 player->index_bit, center_side);
9841 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9842 CE_OTHER_GETS_TOUCHED,
9843 player->index_bit, center_side);
9845 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9846 CE_OTHER_GETS_TOUCHED,
9847 player->index_bit, center_side);
9848 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9849 player->index_bit, center_side);
9857 void TestIfElementTouchesCustomElement(int x, int y)
9859 static int xy[4][2] =
9866 static int trigger_sides[4][2] =
9868 /* center side border side */
9869 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9870 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9871 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9872 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9874 static int touch_dir[4] =
9881 boolean change_center_element = FALSE;
9882 int center_element_change_page = 0;
9883 int center_element = Feld[x][y]; /* should always be non-moving! */
9884 int border_trigger_element;
9887 for (i = 0; i < NUM_DIRECTIONS; i++)
9889 int xx = x + xy[i][0];
9890 int yy = y + xy[i][1];
9891 int center_side = trigger_sides[i][0];
9892 int border_side = trigger_sides[i][1];
9895 if (!IN_LEV_FIELD(xx, yy))
9898 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9899 border_element = Feld[xx][yy]; /* may be moving! */
9900 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9901 border_element = Feld[xx][yy];
9902 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9903 border_element = MovingOrBlocked2Element(xx, yy);
9905 continue; /* center and border element do not touch */
9907 /* check for change of center element (but change it only once) */
9908 if (IS_CUSTOM_ELEMENT(center_element) &&
9909 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9910 !change_center_element)
9912 for (j = 0; j < element_info[center_element].num_change_pages; j++)
9914 struct ElementChangeInfo *change =
9915 &element_info[center_element].change_page[j];
9917 if (change->can_change &&
9918 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9919 change->trigger_side & border_side &&
9921 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9923 change->trigger_element == border_element
9927 change_center_element = TRUE;
9928 center_element_change_page = j;
9929 border_trigger_element = border_element;
9936 /* check for change of border element */
9937 if (IS_CUSTOM_ELEMENT(border_element) &&
9938 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9940 for (j = 0; j < element_info[border_element].num_change_pages; j++)
9942 struct ElementChangeInfo *change =
9943 &element_info[border_element].change_page[j];
9945 if (change->can_change &&
9946 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9947 change->trigger_side & center_side &&
9949 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9951 change->trigger_element == center_element
9956 printf("::: border_element %d, %d\n", x, y);
9959 CheckElementChangeByPage(xx, yy, border_element, center_element,
9960 CE_OTHER_IS_TOUCHING, j);
9967 if (change_center_element)
9970 printf("::: center_element %d, %d\n", x, y);
9973 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
9974 CE_OTHER_IS_TOUCHING, center_element_change_page);
9978 void TestIfElementHitsCustomElement(int x, int y, int direction)
9980 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9981 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9982 int hitx = x + dx, hity = y + dy;
9983 int hitting_element = Feld[x][y];
9984 int touched_element;
9986 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9987 !IS_FREE(hitx, hity) &&
9988 (!IS_MOVING(hitx, hity) ||
9989 MovDir[hitx][hity] != direction ||
9990 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9993 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9997 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10001 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10002 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10004 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10005 CE_HITTING_SOMETHING, direction);
10007 if (IN_LEV_FIELD(hitx, hity))
10009 int opposite_direction = MV_DIR_OPPOSITE(direction);
10010 int hitting_side = direction;
10011 int touched_side = opposite_direction;
10013 int touched_element = MovingOrBlocked2Element(hitx, hity);
10016 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10017 MovDir[hitx][hity] != direction ||
10018 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10027 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10028 CE_HIT_BY_SOMETHING, opposite_direction);
10030 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10031 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10033 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10035 struct ElementChangeInfo *change =
10036 &element_info[hitting_element].change_page[i];
10038 if (change->can_change &&
10039 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10040 change->trigger_side & touched_side &&
10043 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10045 change->trigger_element == touched_element
10049 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10050 CE_OTHER_IS_HITTING, i);
10056 if (IS_CUSTOM_ELEMENT(touched_element) &&
10057 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10059 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10061 struct ElementChangeInfo *change =
10062 &element_info[touched_element].change_page[i];
10064 if (change->can_change &&
10065 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10066 change->trigger_side & hitting_side &&
10068 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10070 change->trigger_element == hitting_element
10074 CheckElementChangeByPage(hitx, hity, touched_element,
10075 hitting_element, CE_OTHER_GETS_HIT, i);
10085 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10087 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10088 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10089 int hitx = x + dx, hity = y + dy;
10090 int hitting_element = Feld[x][y];
10091 int touched_element;
10093 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10094 !IS_FREE(hitx, hity) &&
10095 (!IS_MOVING(hitx, hity) ||
10096 MovDir[hitx][hity] != direction ||
10097 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10100 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10104 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10108 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10109 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10111 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10112 EP_CAN_SMASH_EVERYTHING, direction);
10114 if (IN_LEV_FIELD(hitx, hity))
10116 int opposite_direction = MV_DIR_OPPOSITE(direction);
10117 int hitting_side = direction;
10118 int touched_side = opposite_direction;
10120 int touched_element = MovingOrBlocked2Element(hitx, hity);
10123 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10124 MovDir[hitx][hity] != direction ||
10125 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10134 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10135 CE_SMASHED_BY_SOMETHING, opposite_direction);
10137 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10138 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10140 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10142 struct ElementChangeInfo *change =
10143 &element_info[hitting_element].change_page[i];
10145 if (change->can_change &&
10146 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10147 change->trigger_side & touched_side &&
10150 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10152 change->trigger_element == touched_element
10156 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10157 CE_OTHER_IS_SMASHING, i);
10163 if (IS_CUSTOM_ELEMENT(touched_element) &&
10164 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10166 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10168 struct ElementChangeInfo *change =
10169 &element_info[touched_element].change_page[i];
10171 if (change->can_change &&
10172 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10173 change->trigger_side & hitting_side &&
10175 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10177 change->trigger_element == hitting_element
10181 CheckElementChangeByPage(hitx, hity, touched_element,
10182 hitting_element, CE_OTHER_GETS_SMASHED,i);
10192 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10194 int i, kill_x = -1, kill_y = -1;
10195 int bad_element = -1;
10196 static int test_xy[4][2] =
10203 static int test_dir[4] =
10211 for (i = 0; i < NUM_DIRECTIONS; i++)
10213 int test_x, test_y, test_move_dir, test_element;
10215 test_x = good_x + test_xy[i][0];
10216 test_y = good_y + test_xy[i][1];
10218 if (!IN_LEV_FIELD(test_x, test_y))
10222 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10225 test_element = Feld[test_x][test_y];
10227 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10230 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10231 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10233 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10234 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10238 bad_element = test_element;
10244 if (kill_x != -1 || kill_y != -1)
10246 if (IS_PLAYER(good_x, good_y))
10248 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10251 if (player->shield_deadly_time_left > 0 &&
10252 !IS_INDESTRUCTIBLE(bad_element))
10253 Bang(kill_x, kill_y);
10254 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10257 if (player->shield_deadly_time_left > 0)
10258 Bang(kill_x, kill_y);
10259 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10264 Bang(good_x, good_y);
10268 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10270 int i, kill_x = -1, kill_y = -1;
10271 int bad_element = Feld[bad_x][bad_y];
10272 static int test_xy[4][2] =
10279 static int touch_dir[4] =
10281 MV_LEFT | MV_RIGHT,
10286 static int test_dir[4] =
10294 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10297 for (i = 0; i < NUM_DIRECTIONS; i++)
10299 int test_x, test_y, test_move_dir, test_element;
10301 test_x = bad_x + test_xy[i][0];
10302 test_y = bad_y + test_xy[i][1];
10303 if (!IN_LEV_FIELD(test_x, test_y))
10307 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10309 test_element = Feld[test_x][test_y];
10311 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10312 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10314 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10315 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10317 /* good thing is player or penguin that does not move away */
10318 if (IS_PLAYER(test_x, test_y))
10320 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10322 if (bad_element == EL_ROBOT && player->is_moving)
10323 continue; /* robot does not kill player if he is moving */
10325 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10327 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10328 continue; /* center and border element do not touch */
10335 else if (test_element == EL_PENGUIN)
10344 if (kill_x != -1 || kill_y != -1)
10346 if (IS_PLAYER(kill_x, kill_y))
10348 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10351 if (player->shield_deadly_time_left > 0 &&
10352 !IS_INDESTRUCTIBLE(bad_element))
10353 Bang(bad_x, bad_y);
10354 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10357 if (player->shield_deadly_time_left > 0)
10358 Bang(bad_x, bad_y);
10359 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10364 Bang(kill_x, kill_y);
10368 void TestIfHeroTouchesBadThing(int x, int y)
10370 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10373 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10375 TestIfGoodThingHitsBadThing(x, y, move_dir);
10378 void TestIfBadThingTouchesHero(int x, int y)
10380 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10383 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10385 TestIfBadThingHitsGoodThing(x, y, move_dir);
10388 void TestIfFriendTouchesBadThing(int x, int y)
10390 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10393 void TestIfBadThingTouchesFriend(int x, int y)
10395 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10398 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10400 int i, kill_x = bad_x, kill_y = bad_y;
10401 static int xy[4][2] =
10409 for (i = 0; i < NUM_DIRECTIONS; i++)
10413 x = bad_x + xy[i][0];
10414 y = bad_y + xy[i][1];
10415 if (!IN_LEV_FIELD(x, y))
10418 element = Feld[x][y];
10419 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10420 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10428 if (kill_x != bad_x || kill_y != bad_y)
10429 Bang(bad_x, bad_y);
10432 void KillHero(struct PlayerInfo *player)
10434 int jx = player->jx, jy = player->jy;
10436 if (!player->active)
10439 /* remove accessible field at the player's position */
10440 Feld[jx][jy] = EL_EMPTY;
10442 /* deactivate shield (else Bang()/Explode() would not work right) */
10443 player->shield_normal_time_left = 0;
10444 player->shield_deadly_time_left = 0;
10450 static void KillHeroUnlessEnemyProtected(int x, int y)
10452 if (!PLAYER_ENEMY_PROTECTED(x, y))
10453 KillHero(PLAYERINFO(x, y));
10456 static void KillHeroUnlessExplosionProtected(int x, int y)
10458 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10459 KillHero(PLAYERINFO(x, y));
10462 void BuryHero(struct PlayerInfo *player)
10464 int jx = player->jx, jy = player->jy;
10466 if (!player->active)
10470 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10472 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10474 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10476 player->GameOver = TRUE;
10477 RemoveHero(player);
10480 void RemoveHero(struct PlayerInfo *player)
10482 int jx = player->jx, jy = player->jy;
10483 int i, found = FALSE;
10485 player->present = FALSE;
10486 player->active = FALSE;
10488 if (!ExplodeField[jx][jy])
10489 StorePlayer[jx][jy] = 0;
10491 for (i = 0; i < MAX_PLAYERS; i++)
10492 if (stored_player[i].active)
10496 AllPlayersGone = TRUE;
10503 =============================================================================
10504 checkDiagonalPushing()
10505 -----------------------------------------------------------------------------
10506 check if diagonal input device direction results in pushing of object
10507 (by checking if the alternative direction is walkable, diggable, ...)
10508 =============================================================================
10511 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10512 int x, int y, int real_dx, int real_dy)
10514 int jx, jy, dx, dy, xx, yy;
10516 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10519 /* diagonal direction: check alternative direction */
10524 xx = jx + (dx == 0 ? real_dx : 0);
10525 yy = jy + (dy == 0 ? real_dy : 0);
10527 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10531 =============================================================================
10533 -----------------------------------------------------------------------------
10534 x, y: field next to player (non-diagonal) to try to dig to
10535 real_dx, real_dy: direction as read from input device (can be diagonal)
10536 =============================================================================
10539 int DigField(struct PlayerInfo *player,
10540 int oldx, int oldy, int x, int y,
10541 int real_dx, int real_dy, int mode)
10543 static int trigger_sides[4] =
10545 CH_SIDE_RIGHT, /* moving left */
10546 CH_SIDE_LEFT, /* moving right */
10547 CH_SIDE_BOTTOM, /* moving up */
10548 CH_SIDE_TOP, /* moving down */
10551 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10553 int jx = oldx, jy = oldy;
10554 int dx = x - jx, dy = y - jy;
10555 int nextx = x + dx, nexty = y + dy;
10556 int move_direction = (dx == -1 ? MV_LEFT :
10557 dx == +1 ? MV_RIGHT :
10559 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10560 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10561 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10562 int old_element = Feld[jx][jy];
10565 if (player->MovPos == 0)
10567 player->is_digging = FALSE;
10568 player->is_collecting = FALSE;
10571 if (player->MovPos == 0) /* last pushing move finished */
10572 player->is_pushing = FALSE;
10574 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10576 player->is_switching = FALSE;
10577 player->push_delay = 0;
10579 return MF_NO_ACTION;
10582 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10583 return MF_NO_ACTION;
10588 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10590 if (IS_TUBE(Feld[jx][jy]) ||
10591 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10595 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10596 int tube_leave_directions[][2] =
10598 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10599 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10600 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10601 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
10602 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
10603 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
10604 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
10605 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
10606 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
10607 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
10608 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
10609 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10612 while (tube_leave_directions[i][0] != tube_element)
10615 if (tube_leave_directions[i][0] == -1) /* should not happen */
10619 if (!(tube_leave_directions[i][1] & move_direction))
10620 return MF_NO_ACTION; /* tube has no opening in this direction */
10625 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10626 old_element = Back[jx][jy];
10630 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10631 return MF_NO_ACTION; /* field has no opening in this direction */
10633 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10634 return MF_NO_ACTION; /* field has no opening in this direction */
10636 element = Feld[x][y];
10638 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10639 game.engine_version >= VERSION_IDENT(2,2,0,0))
10640 return MF_NO_ACTION;
10643 if (game.gravity && !player->is_auto_moving &&
10644 canFallDown(player) && move_direction != MV_DOWN &&
10645 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10646 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10650 if (element == EL_EMPTY_SPACE &&
10651 game.gravity && !player->is_auto_moving &&
10652 canFallDown(player) && move_direction != MV_DOWN)
10653 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10659 case EL_SP_PORT_LEFT:
10660 case EL_SP_PORT_RIGHT:
10661 case EL_SP_PORT_UP:
10662 case EL_SP_PORT_DOWN:
10663 case EL_SP_PORT_HORIZONTAL:
10664 case EL_SP_PORT_VERTICAL:
10665 case EL_SP_PORT_ANY:
10666 case EL_SP_GRAVITY_PORT_LEFT:
10667 case EL_SP_GRAVITY_PORT_RIGHT:
10668 case EL_SP_GRAVITY_PORT_UP:
10669 case EL_SP_GRAVITY_PORT_DOWN:
10671 if (!canEnterSupaplexPort(x, y, dx, dy))
10672 return MF_NO_ACTION;
10675 element != EL_SP_PORT_LEFT &&
10676 element != EL_SP_GRAVITY_PORT_LEFT &&
10677 element != EL_SP_PORT_HORIZONTAL &&
10678 element != EL_SP_PORT_ANY) ||
10680 element != EL_SP_PORT_RIGHT &&
10681 element != EL_SP_GRAVITY_PORT_RIGHT &&
10682 element != EL_SP_PORT_HORIZONTAL &&
10683 element != EL_SP_PORT_ANY) ||
10685 element != EL_SP_PORT_UP &&
10686 element != EL_SP_GRAVITY_PORT_UP &&
10687 element != EL_SP_PORT_VERTICAL &&
10688 element != EL_SP_PORT_ANY) ||
10690 element != EL_SP_PORT_DOWN &&
10691 element != EL_SP_GRAVITY_PORT_DOWN &&
10692 element != EL_SP_PORT_VERTICAL &&
10693 element != EL_SP_PORT_ANY) ||
10694 !IN_LEV_FIELD(nextx, nexty) ||
10695 !IS_FREE(nextx, nexty))
10696 return MF_NO_ACTION;
10699 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10700 element == EL_SP_GRAVITY_PORT_RIGHT ||
10701 element == EL_SP_GRAVITY_PORT_UP ||
10702 element == EL_SP_GRAVITY_PORT_DOWN)
10703 game.gravity = !game.gravity;
10705 /* automatically move to the next field with double speed */
10706 player->programmed_action = move_direction;
10708 if (player->move_delay_reset_counter == 0)
10710 player->move_delay_reset_counter = 2; /* two double speed steps */
10712 DOUBLE_PLAYER_SPEED(player);
10715 player->move_delay_reset_counter = 2;
10717 DOUBLE_PLAYER_SPEED(player);
10721 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10724 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10730 case EL_TUBE_VERTICAL:
10731 case EL_TUBE_HORIZONTAL:
10732 case EL_TUBE_VERTICAL_LEFT:
10733 case EL_TUBE_VERTICAL_RIGHT:
10734 case EL_TUBE_HORIZONTAL_UP:
10735 case EL_TUBE_HORIZONTAL_DOWN:
10736 case EL_TUBE_LEFT_UP:
10737 case EL_TUBE_LEFT_DOWN:
10738 case EL_TUBE_RIGHT_UP:
10739 case EL_TUBE_RIGHT_DOWN:
10742 int tube_enter_directions[][2] =
10744 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10745 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10746 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10747 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
10748 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
10749 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
10750 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
10751 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
10752 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
10753 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
10754 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
10755 { -1, MV_NO_MOVING }
10758 while (tube_enter_directions[i][0] != element)
10761 if (tube_enter_directions[i][0] == -1) /* should not happen */
10765 if (!(tube_enter_directions[i][1] & move_direction))
10766 return MF_NO_ACTION; /* tube has no opening in this direction */
10768 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10776 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10778 if (IS_WALKABLE(element))
10781 int sound_action = ACTION_WALKING;
10784 if (!ACCESS_FROM(element, opposite_direction))
10785 return MF_NO_ACTION; /* field not accessible from this direction */
10789 if (element == EL_EMPTY_SPACE &&
10790 game.gravity && !player->is_auto_moving &&
10791 canFallDown(player) && move_direction != MV_DOWN)
10792 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10795 if (IS_GATE(element))
10797 if (!player->key[element - EL_GATE_1])
10798 return MF_NO_ACTION;
10800 else if (IS_GATE_GRAY(element))
10802 if (!player->key[element - EL_GATE_1_GRAY])
10803 return MF_NO_ACTION;
10805 else if (element == EL_EXIT_OPEN ||
10806 element == EL_SP_EXIT_OPEN ||
10807 element == EL_SP_EXIT_OPENING)
10809 sound_action = ACTION_PASSING; /* player is passing exit */
10811 else if (element == EL_EMPTY)
10813 sound_action = ACTION_MOVING; /* nothing to walk on */
10816 /* play sound from background or player, whatever is available */
10817 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
10818 PlayLevelSoundElementAction(x, y, element, sound_action);
10820 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10825 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
10827 else if (IS_PASSABLE(element))
10831 if (!canPassField(x, y, move_direction))
10832 return MF_NO_ACTION;
10837 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
10838 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
10839 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
10840 return MF_NO_ACTION;
10842 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10843 return MF_NO_ACTION;
10848 if (!ACCESS_FROM(element, opposite_direction))
10849 return MF_NO_ACTION; /* field not accessible from this direction */
10851 if (IS_CUSTOM_ELEMENT(element) &&
10852 !ACCESS_FROM(element, opposite_direction))
10853 return MF_NO_ACTION; /* field not accessible from this direction */
10857 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10858 return MF_NO_ACTION;
10863 if (IS_EM_GATE(element))
10865 if (!player->key[element - EL_EM_GATE_1])
10866 return MF_NO_ACTION;
10868 else if (IS_EM_GATE_GRAY(element))
10870 if (!player->key[element - EL_EM_GATE_1_GRAY])
10871 return MF_NO_ACTION;
10873 else if (IS_SP_PORT(element))
10875 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10876 element == EL_SP_GRAVITY_PORT_RIGHT ||
10877 element == EL_SP_GRAVITY_PORT_UP ||
10878 element == EL_SP_GRAVITY_PORT_DOWN)
10879 game.gravity = !game.gravity;
10882 /* automatically move to the next field with double speed */
10883 player->programmed_action = move_direction;
10885 if (player->move_delay_reset_counter == 0)
10887 player->move_delay_reset_counter = 2; /* two double speed steps */
10889 DOUBLE_PLAYER_SPEED(player);
10892 player->move_delay_reset_counter = 2;
10894 DOUBLE_PLAYER_SPEED(player);
10897 PlayLevelSoundAction(x, y, ACTION_PASSING);
10901 else if (IS_DIGGABLE(element))
10905 if (mode != DF_SNAP)
10908 GfxElement[x][y] = GFX_ELEMENT(element);
10911 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
10913 player->is_digging = TRUE;
10916 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10918 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
10919 player->index_bit, dig_side);
10922 if (mode == DF_SNAP)
10923 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10928 else if (IS_COLLECTIBLE(element))
10932 if (mode != DF_SNAP)
10934 GfxElement[x][y] = element;
10935 player->is_collecting = TRUE;
10938 if (element == EL_SPEED_PILL)
10939 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10940 else if (element == EL_EXTRA_TIME && level.time > 0)
10943 DrawGameValue_Time(TimeLeft);
10945 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10947 player->shield_normal_time_left += 10;
10948 if (element == EL_SHIELD_DEADLY)
10949 player->shield_deadly_time_left += 10;
10951 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10953 if (player->inventory_size < MAX_INVENTORY_SIZE)
10954 player->inventory_element[player->inventory_size++] = element;
10956 DrawGameValue_Dynamite(local_player->inventory_size);
10958 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10960 player->dynabomb_count++;
10961 player->dynabombs_left++;
10963 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10965 player->dynabomb_size++;
10967 else if (element == EL_DYNABOMB_INCREASE_POWER)
10969 player->dynabomb_xl = TRUE;
10971 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
10972 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
10974 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
10975 element - EL_KEY_1 : element - EL_EM_KEY_1);
10977 player->key[key_nr] = TRUE;
10979 DrawGameValue_Keys(player);
10981 redraw_mask |= REDRAW_DOOR_1;
10983 else if (IS_ENVELOPE(element))
10986 player->show_envelope = element;
10988 ShowEnvelope(element - EL_ENVELOPE_1);
10991 else if (IS_DROPPABLE(element) ||
10992 IS_THROWABLE(element)) /* can be collected and dropped */
10996 if (element_info[element].collect_count == 0)
10997 player->inventory_infinite_element = element;
10999 for (i = 0; i < element_info[element].collect_count; i++)
11000 if (player->inventory_size < MAX_INVENTORY_SIZE)
11001 player->inventory_element[player->inventory_size++] = element;
11003 DrawGameValue_Dynamite(local_player->inventory_size);
11005 else if (element_info[element].collect_count > 0)
11007 local_player->gems_still_needed -=
11008 element_info[element].collect_count;
11009 if (local_player->gems_still_needed < 0)
11010 local_player->gems_still_needed = 0;
11012 DrawGameValue_Emeralds(local_player->gems_still_needed);
11015 RaiseScoreElement(element);
11016 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11018 CheckTriggeredElementChangeByPlayer(x, y, element,
11019 CE_OTHER_GETS_COLLECTED,
11020 player->index_bit, dig_side);
11023 if (mode == DF_SNAP)
11024 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11029 else if (IS_PUSHABLE(element))
11031 if (mode == DF_SNAP && element != EL_BD_ROCK)
11032 return MF_NO_ACTION;
11034 if (CAN_FALL(element) && dy)
11035 return MF_NO_ACTION;
11037 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11038 !(element == EL_SPRING && level.use_spring_bug))
11039 return MF_NO_ACTION;
11042 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11043 ((move_direction & MV_VERTICAL &&
11044 ((element_info[element].move_pattern & MV_LEFT &&
11045 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11046 (element_info[element].move_pattern & MV_RIGHT &&
11047 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11048 (move_direction & MV_HORIZONTAL &&
11049 ((element_info[element].move_pattern & MV_UP &&
11050 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11051 (element_info[element].move_pattern & MV_DOWN &&
11052 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11053 return MF_NO_ACTION;
11057 /* do not push elements already moving away faster than player */
11058 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11059 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11060 return MF_NO_ACTION;
11062 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11063 return MF_NO_ACTION;
11067 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11069 if (player->push_delay_value == -1)
11070 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11072 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11074 if (!player->is_pushing)
11075 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11079 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11080 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11081 !player_is_pushing))
11082 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11085 if (!player->is_pushing &&
11086 game.engine_version >= VERSION_IDENT(2,2,0,7))
11087 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11091 printf("::: push delay: %ld [%d, %d] [%d]\n",
11092 player->push_delay_value, FrameCounter, game.engine_version,
11093 player->is_pushing);
11096 player->is_pushing = TRUE;
11098 if (!(IN_LEV_FIELD(nextx, nexty) &&
11099 (IS_FREE(nextx, nexty) ||
11100 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11101 IS_SB_ELEMENT(element)))))
11102 return MF_NO_ACTION;
11104 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11105 return MF_NO_ACTION;
11107 if (player->push_delay == 0) /* new pushing; restart delay */
11108 player->push_delay = FrameCounter;
11110 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11111 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11112 element != EL_SPRING && element != EL_BALLOON)
11114 /* make sure that there is no move delay before next try to push */
11115 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11116 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11118 return MF_NO_ACTION;
11122 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11125 if (IS_SB_ELEMENT(element))
11127 if (element == EL_SOKOBAN_FIELD_FULL)
11129 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11130 local_player->sokobanfields_still_needed++;
11133 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11135 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11136 local_player->sokobanfields_still_needed--;
11139 Feld[x][y] = EL_SOKOBAN_OBJECT;
11141 if (Back[x][y] == Back[nextx][nexty])
11142 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11143 else if (Back[x][y] != 0)
11144 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11147 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11150 if (local_player->sokobanfields_still_needed == 0 &&
11151 game.emulation == EMU_SOKOBAN)
11153 player->LevelSolved = player->GameOver = TRUE;
11154 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11158 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11160 InitMovingField(x, y, move_direction);
11161 GfxAction[x][y] = ACTION_PUSHING;
11163 if (mode == DF_SNAP)
11164 ContinueMoving(x, y);
11166 MovPos[x][y] = (dx != 0 ? dx : dy);
11168 Pushed[x][y] = TRUE;
11169 Pushed[nextx][nexty] = TRUE;
11171 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11172 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11174 player->push_delay_value = -1; /* get new value later */
11177 /* check for element change _after_ element has been pushed! */
11181 /* !!! TEST ONLY !!! */
11182 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11183 player->index_bit, dig_side);
11184 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11185 player->index_bit, dig_side);
11187 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11188 player->index_bit, dig_side);
11189 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11190 player->index_bit, dig_side);
11196 else if (IS_SWITCHABLE(element))
11198 if (PLAYER_SWITCHING(player, x, y))
11201 player->is_switching = TRUE;
11202 player->switch_x = x;
11203 player->switch_y = y;
11205 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11207 if (element == EL_ROBOT_WHEEL)
11209 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11213 DrawLevelField(x, y);
11215 else if (element == EL_SP_TERMINAL)
11219 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11221 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11223 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11224 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11227 else if (IS_BELT_SWITCH(element))
11229 ToggleBeltSwitch(x, y);
11231 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11232 element == EL_SWITCHGATE_SWITCH_DOWN)
11234 ToggleSwitchgateSwitch(x, y);
11236 else if (element == EL_LIGHT_SWITCH ||
11237 element == EL_LIGHT_SWITCH_ACTIVE)
11239 ToggleLightSwitch(x, y);
11242 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11243 SND_LIGHT_SWITCH_ACTIVATING :
11244 SND_LIGHT_SWITCH_DEACTIVATING);
11247 else if (element == EL_TIMEGATE_SWITCH)
11249 ActivateTimegateSwitch(x, y);
11251 else if (element == EL_BALLOON_SWITCH_LEFT ||
11252 element == EL_BALLOON_SWITCH_RIGHT ||
11253 element == EL_BALLOON_SWITCH_UP ||
11254 element == EL_BALLOON_SWITCH_DOWN ||
11255 element == EL_BALLOON_SWITCH_ANY)
11257 if (element == EL_BALLOON_SWITCH_ANY)
11258 game.balloon_dir = move_direction;
11260 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11261 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11262 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11263 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11266 else if (element == EL_LAMP)
11268 Feld[x][y] = EL_LAMP_ACTIVE;
11269 local_player->lights_still_needed--;
11271 DrawLevelField(x, y);
11273 else if (element == EL_TIME_ORB_FULL)
11275 Feld[x][y] = EL_TIME_ORB_EMPTY;
11277 DrawGameValue_Time(TimeLeft);
11279 DrawLevelField(x, y);
11282 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11290 if (!PLAYER_SWITCHING(player, x, y))
11292 player->is_switching = TRUE;
11293 player->switch_x = x;
11294 player->switch_y = y;
11297 /* !!! TEST ONLY !!! */
11298 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11299 player->index_bit, dig_side);
11300 CheckTriggeredElementChangeByPlayer(x, y, element,
11301 CE_OTHER_IS_SWITCHING,
11302 player->index_bit, dig_side);
11304 CheckTriggeredElementChangeByPlayer(x, y, element,
11305 CE_OTHER_IS_SWITCHING,
11306 player->index_bit, dig_side);
11307 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11308 player->index_bit, dig_side);
11313 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11314 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11315 player->index_bit, dig_side);
11316 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11317 player->index_bit, dig_side);
11319 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11320 player->index_bit, dig_side);
11321 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11322 player->index_bit, dig_side);
11326 return MF_NO_ACTION;
11329 player->push_delay = 0;
11331 if (Feld[x][y] != element) /* really digged/collected something */
11332 player->is_collecting = !player->is_digging;
11337 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11339 int jx = player->jx, jy = player->jy;
11340 int x = jx + dx, y = jy + dy;
11341 int snap_direction = (dx == -1 ? MV_LEFT :
11342 dx == +1 ? MV_RIGHT :
11344 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11347 if (player->MovPos != 0)
11350 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11354 if (!player->active || !IN_LEV_FIELD(x, y))
11362 if (player->MovPos == 0)
11363 player->is_pushing = FALSE;
11365 player->is_snapping = FALSE;
11367 if (player->MovPos == 0)
11369 player->is_moving = FALSE;
11370 player->is_digging = FALSE;
11371 player->is_collecting = FALSE;
11377 if (player->is_snapping)
11380 player->MovDir = snap_direction;
11383 if (player->MovPos == 0)
11386 player->is_moving = FALSE;
11387 player->is_digging = FALSE;
11388 player->is_collecting = FALSE;
11391 player->is_dropping = FALSE;
11393 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11396 player->is_snapping = TRUE;
11399 if (player->MovPos == 0)
11402 player->is_moving = FALSE;
11403 player->is_digging = FALSE;
11404 player->is_collecting = FALSE;
11408 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11409 DrawLevelField(player->last_jx, player->last_jy);
11412 DrawLevelField(x, y);
11421 boolean DropElement(struct PlayerInfo *player)
11423 static int trigger_sides[4] =
11425 CH_SIDE_LEFT, /* dropping left */
11426 CH_SIDE_RIGHT, /* dropping right */
11427 CH_SIDE_TOP, /* dropping up */
11428 CH_SIDE_BOTTOM, /* dropping down */
11430 int old_element, new_element;
11431 int dropx = player->jx, dropy = player->jy;
11432 int drop_direction = player->MovDir;
11433 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11434 int drop_element = (player->inventory_size > 0 ?
11435 player->inventory_element[player->inventory_size - 1] :
11436 player->inventory_infinite_element != EL_UNDEFINED ?
11437 player->inventory_infinite_element :
11438 player->dynabombs_left > 0 ?
11439 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11442 if (IS_THROWABLE(drop_element))
11444 dropx += GET_DX_FROM_DIR(drop_direction);
11445 dropy += GET_DY_FROM_DIR(drop_direction);
11447 if (!IN_LEV_FIELD(dropx, dropy))
11451 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11452 new_element = drop_element; /* default: no change when dropping */
11454 /* check if player is active, not moving and ready to drop */
11455 if (!player->active || player->MovPos || player->drop_delay > 0)
11458 /* check if player has anything that can be dropped */
11460 if (new_element == EL_UNDEFINED)
11463 if (player->inventory_size == 0 &&
11464 player->inventory_infinite_element == EL_UNDEFINED &&
11465 player->dynabombs_left == 0)
11469 /* check if anything can be dropped at the current position */
11470 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11473 /* collected custom elements can only be dropped on empty fields */
11475 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11478 if (player->inventory_size > 0 &&
11479 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11480 && old_element != EL_EMPTY)
11484 if (old_element != EL_EMPTY)
11485 Back[dropx][dropy] = old_element; /* store old element on this field */
11487 ResetGfxAnimation(dropx, dropy);
11488 ResetRandomAnimationValue(dropx, dropy);
11490 if (player->inventory_size > 0 ||
11491 player->inventory_infinite_element != EL_UNDEFINED)
11493 if (player->inventory_size > 0)
11495 player->inventory_size--;
11498 new_element = player->inventory_element[player->inventory_size];
11501 DrawGameValue_Dynamite(local_player->inventory_size);
11503 if (new_element == EL_DYNAMITE)
11504 new_element = EL_DYNAMITE_ACTIVE;
11505 else if (new_element == EL_SP_DISK_RED)
11506 new_element = EL_SP_DISK_RED_ACTIVE;
11509 Feld[dropx][dropy] = new_element;
11511 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11512 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11513 el2img(Feld[dropx][dropy]), 0);
11515 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11518 /* needed if previous element just changed to "empty" in the last frame */
11519 Changed[dropx][dropy] = 0; /* allow another change */
11523 /* !!! TEST ONLY !!! */
11524 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11525 player->index_bit, drop_side);
11526 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11527 CE_OTHER_GETS_DROPPED,
11528 player->index_bit, drop_side);
11530 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11531 CE_OTHER_GETS_DROPPED,
11532 player->index_bit, drop_side);
11533 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11534 player->index_bit, drop_side);
11537 TestIfElementTouchesCustomElement(dropx, dropy);
11539 else /* player is dropping a dyna bomb */
11541 player->dynabombs_left--;
11544 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
11547 Feld[dropx][dropy] = new_element;
11549 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11550 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11551 el2img(Feld[dropx][dropy]), 0);
11553 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11560 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11563 InitField_WithBug1(dropx, dropy, FALSE);
11565 InitField(dropx, dropy, FALSE);
11566 if (CAN_MOVE(Feld[dropx][dropy]))
11567 InitMovDir(dropx, dropy);
11571 new_element = Feld[dropx][dropy]; /* element might have changed */
11573 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11574 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11577 int move_stepsize = element_info[new_element].move_stepsize;
11579 int move_direction, nextx, nexty;
11581 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11582 MovDir[dropx][dropy] = drop_direction;
11584 move_direction = MovDir[dropx][dropy];
11585 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11586 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11589 Changed[dropx][dropy] = 0; /* allow another change */
11590 CheckCollision[dropx][dropy] = 2;
11593 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
11596 WasJustMoving[dropx][dropy] = 3;
11599 InitMovingField(dropx, dropy, move_direction);
11600 ContinueMoving(dropx, dropy);
11607 Changed[dropx][dropy] = 0; /* allow another change */
11610 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
11612 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
11613 CE_HITTING_SOMETHING, move_direction);
11621 player->drop_delay = 2 * TILEX / move_stepsize + 1;
11626 player->drop_delay = 8 + 8 + 8;
11630 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11635 player->is_dropping = TRUE;
11641 /* ------------------------------------------------------------------------- */
11642 /* game sound playing functions */
11643 /* ------------------------------------------------------------------------- */
11645 static int *loop_sound_frame = NULL;
11646 static int *loop_sound_volume = NULL;
11648 void InitPlayLevelSound()
11650 int num_sounds = getSoundListSize();
11652 checked_free(loop_sound_frame);
11653 checked_free(loop_sound_volume);
11655 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11656 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11659 static void PlayLevelSound(int x, int y, int nr)
11661 int sx = SCREENX(x), sy = SCREENY(y);
11662 int volume, stereo_position;
11663 int max_distance = 8;
11664 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11666 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11667 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11670 if (!IN_LEV_FIELD(x, y) ||
11671 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11672 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11675 volume = SOUND_MAX_VOLUME;
11677 if (!IN_SCR_FIELD(sx, sy))
11679 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11680 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11682 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11685 stereo_position = (SOUND_MAX_LEFT +
11686 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11687 (SCR_FIELDX + 2 * max_distance));
11689 if (IS_LOOP_SOUND(nr))
11691 /* This assures that quieter loop sounds do not overwrite louder ones,
11692 while restarting sound volume comparison with each new game frame. */
11694 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11697 loop_sound_volume[nr] = volume;
11698 loop_sound_frame[nr] = FrameCounter;
11701 PlaySoundExt(nr, volume, stereo_position, type);
11704 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11706 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11707 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11708 y < LEVELY(BY1) ? LEVELY(BY1) :
11709 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11713 static void PlayLevelSoundAction(int x, int y, int action)
11715 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11718 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11720 int sound_effect = element_info[element].sound[action];
11722 if (sound_effect != SND_UNDEFINED)
11723 PlayLevelSound(x, y, sound_effect);
11726 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11729 int sound_effect = element_info[element].sound[action];
11731 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11732 PlayLevelSound(x, y, sound_effect);
11735 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11737 int sound_effect = element_info[Feld[x][y]].sound[action];
11739 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11740 PlayLevelSound(x, y, sound_effect);
11743 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11745 int sound_effect = element_info[Feld[x][y]].sound[action];
11747 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11748 StopSound(sound_effect);
11751 static void PlayLevelMusic()
11753 if (levelset.music[level_nr] != MUS_UNDEFINED)
11754 PlayMusic(levelset.music[level_nr]); /* from config file */
11756 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11759 void RaiseScore(int value)
11761 local_player->score += value;
11763 DrawGameValue_Score(local_player->score);
11766 void RaiseScoreElement(int element)
11771 case EL_BD_DIAMOND:
11772 case EL_EMERALD_YELLOW:
11773 case EL_EMERALD_RED:
11774 case EL_EMERALD_PURPLE:
11775 case EL_SP_INFOTRON:
11776 RaiseScore(level.score[SC_EMERALD]);
11779 RaiseScore(level.score[SC_DIAMOND]);
11782 RaiseScore(level.score[SC_CRYSTAL]);
11785 RaiseScore(level.score[SC_PEARL]);
11788 case EL_BD_BUTTERFLY:
11789 case EL_SP_ELECTRON:
11790 RaiseScore(level.score[SC_BUG]);
11793 case EL_BD_FIREFLY:
11794 case EL_SP_SNIKSNAK:
11795 RaiseScore(level.score[SC_SPACESHIP]);
11798 case EL_DARK_YAMYAM:
11799 RaiseScore(level.score[SC_YAMYAM]);
11802 RaiseScore(level.score[SC_ROBOT]);
11805 RaiseScore(level.score[SC_PACMAN]);
11808 RaiseScore(level.score[SC_NUT]);
11811 case EL_SP_DISK_RED:
11812 case EL_DYNABOMB_INCREASE_NUMBER:
11813 case EL_DYNABOMB_INCREASE_SIZE:
11814 case EL_DYNABOMB_INCREASE_POWER:
11815 RaiseScore(level.score[SC_DYNAMITE]);
11817 case EL_SHIELD_NORMAL:
11818 case EL_SHIELD_DEADLY:
11819 RaiseScore(level.score[SC_SHIELD]);
11821 case EL_EXTRA_TIME:
11822 RaiseScore(level.score[SC_TIME_BONUS]);
11828 RaiseScore(level.score[SC_KEY]);
11831 RaiseScore(element_info[element].collect_score);
11836 void RequestQuitGame(boolean ask_if_really_quit)
11838 if (AllPlayersGone ||
11839 !ask_if_really_quit ||
11840 level_editor_test_game ||
11841 Request("Do you really want to quit the game ?",
11842 REQ_ASK | REQ_STAY_CLOSED))
11844 #if defined(PLATFORM_UNIX)
11845 if (options.network)
11846 SendToServer_StopPlaying();
11850 game_status = GAME_MODE_MAIN;
11858 if (tape.playing && tape.deactivate_display)
11859 TapeDeactivateDisplayOff(TRUE);
11862 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11865 if (tape.playing && tape.deactivate_display)
11866 TapeDeactivateDisplayOn();
11873 /* ---------- new game button stuff ---------------------------------------- */
11875 /* graphic position values for game buttons */
11876 #define GAME_BUTTON_XSIZE 30
11877 #define GAME_BUTTON_YSIZE 30
11878 #define GAME_BUTTON_XPOS 5
11879 #define GAME_BUTTON_YPOS 215
11880 #define SOUND_BUTTON_XPOS 5
11881 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11883 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11884 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11885 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11886 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11887 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11888 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11895 } gamebutton_info[NUM_GAME_BUTTONS] =
11898 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11903 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11904 GAME_CTRL_ID_PAUSE,
11908 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11913 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11914 SOUND_CTRL_ID_MUSIC,
11915 "background music on/off"
11918 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11919 SOUND_CTRL_ID_LOOPS,
11920 "sound loops on/off"
11923 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11924 SOUND_CTRL_ID_SIMPLE,
11925 "normal sounds on/off"
11929 void CreateGameButtons()
11933 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11935 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11936 struct GadgetInfo *gi;
11939 unsigned long event_mask;
11940 int gd_xoffset, gd_yoffset;
11941 int gd_x1, gd_x2, gd_y1, gd_y2;
11944 gd_xoffset = gamebutton_info[i].x;
11945 gd_yoffset = gamebutton_info[i].y;
11946 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11947 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11949 if (id == GAME_CTRL_ID_STOP ||
11950 id == GAME_CTRL_ID_PAUSE ||
11951 id == GAME_CTRL_ID_PLAY)
11953 button_type = GD_TYPE_NORMAL_BUTTON;
11955 event_mask = GD_EVENT_RELEASED;
11956 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11957 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11961 button_type = GD_TYPE_CHECK_BUTTON;
11963 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11964 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11965 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11966 event_mask = GD_EVENT_PRESSED;
11967 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11968 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11971 gi = CreateGadget(GDI_CUSTOM_ID, id,
11972 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11973 GDI_X, DX + gd_xoffset,
11974 GDI_Y, DY + gd_yoffset,
11975 GDI_WIDTH, GAME_BUTTON_XSIZE,
11976 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11977 GDI_TYPE, button_type,
11978 GDI_STATE, GD_BUTTON_UNPRESSED,
11979 GDI_CHECKED, checked,
11980 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11981 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11982 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11983 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11984 GDI_EVENT_MASK, event_mask,
11985 GDI_CALLBACK_ACTION, HandleGameButtons,
11989 Error(ERR_EXIT, "cannot create gadget");
11991 game_gadget[id] = gi;
11995 void FreeGameButtons()
11999 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12000 FreeGadget(game_gadget[i]);
12003 static void MapGameButtons()
12007 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12008 MapGadget(game_gadget[i]);
12011 void UnmapGameButtons()
12015 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12016 UnmapGadget(game_gadget[i]);
12019 static void HandleGameButtons(struct GadgetInfo *gi)
12021 int id = gi->custom_id;
12023 if (game_status != GAME_MODE_PLAYING)
12028 case GAME_CTRL_ID_STOP:
12029 RequestQuitGame(TRUE);
12032 case GAME_CTRL_ID_PAUSE:
12033 if (options.network)
12035 #if defined(PLATFORM_UNIX)
12037 SendToServer_ContinuePlaying();
12039 SendToServer_PausePlaying();
12043 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12046 case GAME_CTRL_ID_PLAY:
12049 #if defined(PLATFORM_UNIX)
12050 if (options.network)
12051 SendToServer_ContinuePlaying();
12055 tape.pausing = FALSE;
12056 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12061 case SOUND_CTRL_ID_MUSIC:
12062 if (setup.sound_music)
12064 setup.sound_music = FALSE;
12067 else if (audio.music_available)
12069 setup.sound = setup.sound_music = TRUE;
12071 SetAudioMode(setup.sound);
12077 case SOUND_CTRL_ID_LOOPS:
12078 if (setup.sound_loops)
12079 setup.sound_loops = FALSE;
12080 else if (audio.loops_available)
12082 setup.sound = setup.sound_loops = TRUE;
12083 SetAudioMode(setup.sound);
12087 case SOUND_CTRL_ID_SIMPLE:
12088 if (setup.sound_simple)
12089 setup.sound_simple = FALSE;
12090 else if (audio.sound_available)
12092 setup.sound = setup.sound_simple = TRUE;
12093 SetAudioMode(setup.sound);