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;
773 player->jx = player->last_jx = x;
774 player->jy = player->last_jy = y;
778 static void InitField(int x, int y, boolean init_game)
780 int element = Feld[x][y];
789 InitPlayerField(x, y, element, init_game);
792 case EL_SOKOBAN_FIELD_PLAYER:
793 element = Feld[x][y] = EL_PLAYER_1;
794 InitField(x, y, init_game);
796 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
797 InitField(x, y, init_game);
800 case EL_SOKOBAN_FIELD_EMPTY:
801 local_player->sokobanfields_still_needed++;
805 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
806 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
807 else if (x > 0 && Feld[x-1][y] == EL_ACID)
808 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
809 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
810 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
811 else if (y > 0 && Feld[x][y-1] == EL_ACID)
812 Feld[x][y] = EL_ACID_POOL_BOTTOM;
813 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
814 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
822 case EL_SPACESHIP_RIGHT:
823 case EL_SPACESHIP_UP:
824 case EL_SPACESHIP_LEFT:
825 case EL_SPACESHIP_DOWN:
827 case EL_BD_BUTTERFLY_RIGHT:
828 case EL_BD_BUTTERFLY_UP:
829 case EL_BD_BUTTERFLY_LEFT:
830 case EL_BD_BUTTERFLY_DOWN:
831 case EL_BD_BUTTERFLY:
832 case EL_BD_FIREFLY_RIGHT:
833 case EL_BD_FIREFLY_UP:
834 case EL_BD_FIREFLY_LEFT:
835 case EL_BD_FIREFLY_DOWN:
837 case EL_PACMAN_RIGHT:
861 if (y == lev_fieldy - 1)
863 Feld[x][y] = EL_AMOEBA_GROWING;
864 Store[x][y] = EL_AMOEBA_WET;
868 case EL_DYNAMITE_ACTIVE:
869 case EL_SP_DISK_RED_ACTIVE:
870 case EL_DYNABOMB_PLAYER_1_ACTIVE:
871 case EL_DYNABOMB_PLAYER_2_ACTIVE:
872 case EL_DYNABOMB_PLAYER_3_ACTIVE:
873 case EL_DYNABOMB_PLAYER_4_ACTIVE:
878 local_player->lights_still_needed++;
882 local_player->friends_still_needed++;
887 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
892 Feld[x][y] = EL_EMPTY;
897 case EL_EM_KEY_1_FILE:
898 Feld[x][y] = EL_EM_KEY_1;
900 case EL_EM_KEY_2_FILE:
901 Feld[x][y] = EL_EM_KEY_2;
903 case EL_EM_KEY_3_FILE:
904 Feld[x][y] = EL_EM_KEY_3;
906 case EL_EM_KEY_4_FILE:
907 Feld[x][y] = EL_EM_KEY_4;
911 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
912 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
913 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
914 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
915 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
916 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
917 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
918 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
919 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
920 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
921 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
922 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
925 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
926 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
927 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
929 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
931 game.belt_dir[belt_nr] = belt_dir;
932 game.belt_dir_nr[belt_nr] = belt_dir_nr;
934 else /* more than one switch -- set it like the first switch */
936 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
941 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
943 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
946 case EL_LIGHT_SWITCH_ACTIVE:
948 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
952 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
954 else if (IS_GROUP_ELEMENT(element))
956 struct ElementGroupInfo *group = element_info[element].group;
957 int last_anim_random_frame = gfx.anim_random_frame;
960 if (group->choice_mode == ANIM_RANDOM)
961 gfx.anim_random_frame = RND(group->num_elements_resolved);
963 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
964 group->choice_mode, 0,
967 if (group->choice_mode == ANIM_RANDOM)
968 gfx.anim_random_frame = last_anim_random_frame;
972 Feld[x][y] = group->element_resolved[element_pos];
974 InitField(x, y, init_game);
980 static inline void InitField_WithBug1(int x, int y, boolean init_game)
982 InitField(x, y, init_game);
984 /* not needed to call InitMovDir() -- already done by InitField()! */
985 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
986 CAN_MOVE(Feld[x][y]))
990 static inline void InitField_WithBug2(int x, int y, boolean init_game)
992 int old_element = Feld[x][y];
994 InitField(x, y, init_game);
996 /* not needed to call InitMovDir() -- already done by InitField()! */
997 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
998 CAN_MOVE(old_element) &&
999 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1002 /* this case is in fact a combination of not less than three bugs:
1003 first, it calls InitMovDir() for elements that can move, although this is
1004 already done by InitField(); then, it checks the element that was at this
1005 field _before_ the call to InitField() (which can change it); lastly, it
1006 was not called for "mole with direction" elements, which were treated as
1007 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1011 inline void DrawGameValue_Emeralds(int value)
1013 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1016 inline void DrawGameValue_Dynamite(int value)
1018 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1021 inline void DrawGameValue_Keys(struct PlayerInfo *player)
1025 for (i = 0; i < MAX_KEYS; i++)
1027 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1028 el2edimg(EL_KEY_1 + i));
1031 inline void DrawGameValue_Score(int value)
1033 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1036 inline void DrawGameValue_Time(int value)
1039 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1041 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1044 inline void DrawGameValue_Level(int value)
1047 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1050 /* misuse area for displaying emeralds to draw bigger level number */
1051 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1052 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1054 /* now copy it to the area for displaying level number */
1055 BlitBitmap(drawto, drawto,
1056 DX_EMERALDS, DY_EMERALDS + 1,
1057 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1058 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1059 DX_LEVEL - 1, DY_LEVEL + 1);
1061 /* restore the area for displaying emeralds */
1062 DrawGameValue_Emeralds(local_player->gems_still_needed);
1064 /* yes, this is all really ugly :-) */
1068 void DrawGameDoorValues()
1072 DrawGameValue_Level(level_nr);
1074 for (i = 0; i < MAX_PLAYERS; i++)
1075 DrawGameValue_Keys(&stored_player[i]);
1077 DrawGameValue_Emeralds(local_player->gems_still_needed);
1078 DrawGameValue_Dynamite(local_player->inventory_size);
1079 DrawGameValue_Score(local_player->score);
1080 DrawGameValue_Time(TimeLeft);
1083 static void resolve_group_element(int group_element, int recursion_depth)
1085 static int group_nr;
1086 static struct ElementGroupInfo *group;
1087 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1090 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1092 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1093 group_element - EL_GROUP_START + 1);
1095 /* replace element which caused too deep recursion by question mark */
1096 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1101 if (recursion_depth == 0) /* initialization */
1103 group = element_info[group_element].group;
1104 group_nr = group_element - EL_GROUP_START;
1106 group->num_elements_resolved = 0;
1107 group->choice_pos = 0;
1110 for (i = 0; i < actual_group->num_elements; i++)
1112 int element = actual_group->element[i];
1114 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1117 if (IS_GROUP_ELEMENT(element))
1118 resolve_group_element(element, recursion_depth + 1);
1121 group->element_resolved[group->num_elements_resolved++] = element;
1122 element_info[element].in_group[group_nr] = TRUE;
1127 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1129 printf("::: group %d: %d resolved elements\n",
1130 group_element - EL_GROUP_START, group->num_elements_resolved);
1131 for (i = 0; i < group->num_elements_resolved; i++)
1132 printf("::: - %d ['%s']\n", group->element_resolved[i],
1133 element_info[group->element_resolved[i]].token_name);
1140 =============================================================================
1142 -----------------------------------------------------------------------------
1143 initialize game engine due to level / tape version number
1144 =============================================================================
1147 static void InitGameEngine()
1151 /* set game engine from tape file when re-playing, else from level file */
1152 game.engine_version = (tape.playing ? tape.engine_version :
1153 level.game_version);
1155 /* dynamically adjust element properties according to game engine version */
1156 InitElementPropertiesEngine(game.engine_version);
1159 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1160 printf(" tape version == %06d [%s] [file: %06d]\n",
1161 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1163 printf(" => game.engine_version == %06d\n", game.engine_version);
1166 /* ---------- recursively resolve group elements ------------------------- */
1168 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1169 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1170 element_info[i].in_group[j] = FALSE;
1172 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1173 resolve_group_element(EL_GROUP_START + i, 0);
1175 /* ---------- initialize player's initial move delay --------------------- */
1177 /* dynamically adjust player properties according to game engine version */
1178 game.initial_move_delay =
1179 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1180 INITIAL_MOVE_DELAY_OFF);
1182 /* dynamically adjust player properties according to level information */
1183 game.initial_move_delay_value =
1184 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1186 /* ---------- initialize player's initial push delay --------------------- */
1188 /* dynamically adjust player properties according to game engine version */
1189 game.initial_push_delay_value =
1190 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1192 /* ---------- initialize changing elements ------------------------------- */
1194 /* initialize changing elements information */
1195 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1197 struct ElementInfo *ei = &element_info[i];
1199 /* this pointer might have been changed in the level editor */
1200 ei->change = &ei->change_page[0];
1202 if (!IS_CUSTOM_ELEMENT(i))
1204 ei->change->target_element = EL_EMPTY_SPACE;
1205 ei->change->delay_fixed = 0;
1206 ei->change->delay_random = 0;
1207 ei->change->delay_frames = 1;
1210 ei->change_events = CE_BITMASK_DEFAULT;
1211 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1213 ei->event_page_nr[j] = 0;
1214 ei->event_page[j] = &ei->change_page[0];
1218 /* add changing elements from pre-defined list */
1219 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1221 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1222 struct ElementInfo *ei = &element_info[ch_delay->element];
1224 ei->change->target_element = ch_delay->target_element;
1225 ei->change->delay_fixed = ch_delay->change_delay;
1227 ei->change->pre_change_function = ch_delay->pre_change_function;
1228 ei->change->change_function = ch_delay->change_function;
1229 ei->change->post_change_function = ch_delay->post_change_function;
1231 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1234 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1239 /* add change events from custom element configuration */
1240 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1242 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1244 for (j = 0; j < ei->num_change_pages; j++)
1246 if (!ei->change_page[j].can_change)
1249 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1251 /* only add event page for the first page found with this event */
1252 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1253 !(ei->change_events & CH_EVENT_BIT(k)))
1255 ei->change_events |= CH_EVENT_BIT(k);
1256 ei->event_page_nr[k] = j;
1257 ei->event_page[k] = &ei->change_page[j];
1265 /* add change events from custom element configuration */
1266 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1268 int element = EL_CUSTOM_START + i;
1270 /* only add custom elements that change after fixed/random frame delay */
1271 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1272 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1276 /* ---------- initialize run-time trigger player and element ------------- */
1278 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1280 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1282 for (j = 0; j < ei->num_change_pages; j++)
1284 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1285 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1289 /* ---------- initialize trigger events ---------------------------------- */
1291 /* initialize trigger events information */
1292 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1293 trigger_events[i] = EP_BITMASK_DEFAULT;
1296 /* add trigger events from element change event properties */
1297 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1299 struct ElementInfo *ei = &element_info[i];
1301 for (j = 0; j < ei->num_change_pages; j++)
1303 if (!ei->change_page[j].can_change)
1306 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1308 int trigger_element = ei->change_page[j].trigger_element;
1310 if (IS_GROUP_ELEMENT(trigger_element))
1312 struct ElementGroupInfo *group = element_info[trigger_element].group;
1314 for (k = 0; k < group->num_elements_resolved; k++)
1315 trigger_events[group->element_resolved[k]]
1316 |= ei->change_page[j].events;
1319 trigger_events[trigger_element] |= ei->change_page[j].events;
1324 /* add trigger events from element change event properties */
1325 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1326 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1327 trigger_events[element_info[i].change->trigger_element] |=
1328 element_info[i].change->events;
1331 /* ---------- initialize push delay -------------------------------------- */
1333 /* initialize push delay values to default */
1334 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1336 if (!IS_CUSTOM_ELEMENT(i))
1338 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1339 element_info[i].push_delay_random = game.default_push_delay_random;
1343 /* set push delay value for certain elements from pre-defined list */
1344 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1346 int e = push_delay_list[i].element;
1348 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1349 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1352 /* set push delay value for Supaplex elements for newer engine versions */
1353 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1355 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1357 if (IS_SP_ELEMENT(i))
1359 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1360 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1365 /* ---------- initialize move stepsize ----------------------------------- */
1367 /* initialize move stepsize values to default */
1368 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1369 if (!IS_CUSTOM_ELEMENT(i))
1370 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1372 /* set move stepsize value for certain elements from pre-defined list */
1373 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1375 int e = move_stepsize_list[i].element;
1377 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1381 /* ---------- initialize move dig/leave ---------------------------------- */
1383 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1385 element_info[i].can_leave_element = FALSE;
1386 element_info[i].can_leave_element_last = FALSE;
1390 /* ---------- initialize gem count --------------------------------------- */
1392 /* initialize gem count values for each element */
1393 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1394 if (!IS_CUSTOM_ELEMENT(i))
1395 element_info[i].collect_count = 0;
1397 /* add gem count values for all elements from pre-defined list */
1398 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1399 element_info[collect_count_list[i].element].collect_count =
1400 collect_count_list[i].count;
1402 /* ---------- initialize access direction -------------------------------- */
1404 /* initialize access direction values to default (access from every side) */
1405 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1406 if (!IS_CUSTOM_ELEMENT(i))
1407 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1409 /* set access direction value for certain elements from pre-defined list */
1410 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1411 element_info[access_direction_list[i].element].access_direction =
1412 access_direction_list[i].direction;
1417 =============================================================================
1419 -----------------------------------------------------------------------------
1420 initialize and start new game
1421 =============================================================================
1426 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1427 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1428 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1435 #if USE_NEW_AMOEBA_CODE
1436 printf("Using new amoeba code.\n");
1438 printf("Using old amoeba code.\n");
1443 /* don't play tapes over network */
1444 network_playing = (options.network && !tape.playing);
1446 for (i = 0; i < MAX_PLAYERS; i++)
1448 struct PlayerInfo *player = &stored_player[i];
1450 player->index_nr = i;
1451 player->index_bit = (1 << i);
1452 player->element_nr = EL_PLAYER_1 + i;
1454 player->present = FALSE;
1455 player->active = FALSE;
1458 player->effective_action = 0;
1459 player->programmed_action = 0;
1462 player->gems_still_needed = level.gems_needed;
1463 player->sokobanfields_still_needed = 0;
1464 player->lights_still_needed = 0;
1465 player->friends_still_needed = 0;
1467 for (j = 0; j < MAX_KEYS; j++)
1468 player->key[j] = FALSE;
1470 player->dynabomb_count = 0;
1471 player->dynabomb_size = 1;
1472 player->dynabombs_left = 0;
1473 player->dynabomb_xl = FALSE;
1475 player->MovDir = MV_NO_MOVING;
1478 player->GfxDir = MV_NO_MOVING;
1479 player->GfxAction = ACTION_DEFAULT;
1481 player->StepFrame = 0;
1483 player->use_murphy_graphic = FALSE;
1485 player->block_last_field = FALSE;
1486 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1488 player->actual_frame_counter = 0;
1490 player->step_counter = 0;
1492 player->last_move_dir = MV_NO_MOVING;
1494 player->is_waiting = FALSE;
1495 player->is_moving = FALSE;
1496 player->is_auto_moving = FALSE;
1497 player->is_digging = FALSE;
1498 player->is_snapping = FALSE;
1499 player->is_collecting = FALSE;
1500 player->is_pushing = FALSE;
1501 player->is_switching = FALSE;
1502 player->is_dropping = FALSE;
1504 player->is_bored = FALSE;
1505 player->is_sleeping = FALSE;
1507 player->frame_counter_bored = -1;
1508 player->frame_counter_sleeping = -1;
1510 player->anim_delay_counter = 0;
1511 player->post_delay_counter = 0;
1513 player->action_waiting = ACTION_DEFAULT;
1514 player->last_action_waiting = ACTION_DEFAULT;
1515 player->special_action_bored = ACTION_DEFAULT;
1516 player->special_action_sleeping = ACTION_DEFAULT;
1518 player->num_special_action_bored = 0;
1519 player->num_special_action_sleeping = 0;
1521 /* determine number of special actions for bored and sleeping animation */
1522 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1524 boolean found = FALSE;
1526 for (k = 0; k < NUM_DIRECTIONS; k++)
1527 if (el_act_dir2img(player->element_nr, j, k) !=
1528 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1532 player->num_special_action_bored++;
1536 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1538 boolean found = FALSE;
1540 for (k = 0; k < NUM_DIRECTIONS; k++)
1541 if (el_act_dir2img(player->element_nr, j, k) !=
1542 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1546 player->num_special_action_sleeping++;
1551 player->switch_x = -1;
1552 player->switch_y = -1;
1554 player->show_envelope = 0;
1556 player->move_delay = game.initial_move_delay;
1557 player->move_delay_value = game.initial_move_delay_value;
1559 player->move_delay_reset_counter = 0;
1561 player->push_delay = 0;
1562 player->push_delay_value = game.initial_push_delay_value;
1564 player->drop_delay = 0;
1566 player->last_jx = player->last_jy = 0;
1567 player->jx = player->jy = 0;
1569 player->shield_normal_time_left = 0;
1570 player->shield_deadly_time_left = 0;
1572 player->inventory_infinite_element = EL_UNDEFINED;
1573 player->inventory_size = 0;
1575 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1576 SnapField(player, 0, 0);
1578 player->LevelSolved = FALSE;
1579 player->GameOver = FALSE;
1582 network_player_action_received = FALSE;
1584 #if defined(PLATFORM_UNIX)
1585 /* initial null action */
1586 if (network_playing)
1587 SendToServer_MovePlayer(MV_NO_MOVING);
1595 TimeLeft = level.time;
1598 ScreenMovDir = MV_NO_MOVING;
1602 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1604 AllPlayersGone = FALSE;
1606 game.yamyam_content_nr = 0;
1607 game.magic_wall_active = FALSE;
1608 game.magic_wall_time_left = 0;
1609 game.light_time_left = 0;
1610 game.timegate_time_left = 0;
1611 game.switchgate_pos = 0;
1612 game.balloon_dir = MV_NO_MOVING;
1613 game.gravity = level.initial_gravity;
1614 game.explosions_delayed = TRUE;
1616 game.envelope_active = FALSE;
1618 for (i = 0; i < NUM_BELTS; i++)
1620 game.belt_dir[i] = MV_NO_MOVING;
1621 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1624 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1625 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1627 for (x = 0; x < lev_fieldx; x++)
1629 for (y = 0; y < lev_fieldy; y++)
1631 Feld[x][y] = level.field[x][y];
1632 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1633 ChangeDelay[x][y] = 0;
1634 ChangePage[x][y] = -1;
1635 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1637 WasJustMoving[x][y] = 0;
1638 WasJustFalling[x][y] = 0;
1639 CheckCollision[x][y] = 0;
1641 Pushed[x][y] = FALSE;
1643 Changed[x][y] = CE_BITMASK_DEFAULT;
1644 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1646 ExplodePhase[x][y] = 0;
1647 ExplodeDelay[x][y] = 0;
1648 ExplodeField[x][y] = EX_TYPE_NONE;
1650 RunnerVisit[x][y] = 0;
1651 PlayerVisit[x][y] = 0;
1654 GfxRandom[x][y] = INIT_GFX_RANDOM();
1655 GfxElement[x][y] = EL_UNDEFINED;
1656 GfxAction[x][y] = ACTION_DEFAULT;
1657 GfxDir[x][y] = MV_NO_MOVING;
1661 for (y = 0; y < lev_fieldy; y++)
1663 for (x = 0; x < lev_fieldx; x++)
1665 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1667 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1669 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1672 InitField(x, y, TRUE);
1678 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1679 emulate_sb ? EMU_SOKOBAN :
1680 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1682 /* initialize explosion and ignition delay */
1683 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1685 if (!IS_CUSTOM_ELEMENT(i))
1688 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1689 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1690 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1691 int last_phase = (num_phase + 1) * delay;
1692 int half_phase = (num_phase / 2) * delay;
1694 element_info[i].explosion_delay = last_phase - 1;
1695 element_info[i].ignition_delay = half_phase;
1698 if (i == EL_BLACK_ORB)
1699 element_info[i].ignition_delay = 0;
1701 if (i == EL_BLACK_ORB)
1702 element_info[i].ignition_delay = 1;
1707 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1708 element_info[i].explosion_delay = 1;
1710 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1711 element_info[i].ignition_delay = 1;
1715 /* correct non-moving belts to start moving left */
1716 for (i = 0; i < NUM_BELTS; i++)
1717 if (game.belt_dir[i] == MV_NO_MOVING)
1718 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1720 /* check if any connected player was not found in playfield */
1721 for (i = 0; i < MAX_PLAYERS; i++)
1723 struct PlayerInfo *player = &stored_player[i];
1725 if (player->connected && !player->present)
1727 for (j = 0; j < MAX_PLAYERS; j++)
1729 struct PlayerInfo *some_player = &stored_player[j];
1730 int jx = some_player->jx, jy = some_player->jy;
1732 /* assign first free player found that is present in the playfield */
1733 if (some_player->present && !some_player->connected)
1735 player->present = TRUE;
1736 player->active = TRUE;
1738 some_player->present = FALSE;
1739 some_player->active = FALSE;
1742 player->element_nr = some_player->element_nr;
1745 StorePlayer[jx][jy] = player->element_nr;
1746 player->jx = player->last_jx = jx;
1747 player->jy = player->last_jy = jy;
1757 /* when playing a tape, eliminate all players which do not participate */
1759 for (i = 0; i < MAX_PLAYERS; i++)
1761 if (stored_player[i].active && !tape.player_participates[i])
1763 struct PlayerInfo *player = &stored_player[i];
1764 int jx = player->jx, jy = player->jy;
1766 player->active = FALSE;
1767 StorePlayer[jx][jy] = 0;
1768 Feld[jx][jy] = EL_EMPTY;
1772 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1774 /* when in single player mode, eliminate all but the first active player */
1776 for (i = 0; i < MAX_PLAYERS; i++)
1778 if (stored_player[i].active)
1780 for (j = i + 1; j < MAX_PLAYERS; j++)
1782 if (stored_player[j].active)
1784 struct PlayerInfo *player = &stored_player[j];
1785 int jx = player->jx, jy = player->jy;
1787 player->active = FALSE;
1788 player->present = FALSE;
1790 StorePlayer[jx][jy] = 0;
1791 Feld[jx][jy] = EL_EMPTY;
1798 /* when recording the game, store which players take part in the game */
1801 for (i = 0; i < MAX_PLAYERS; i++)
1802 if (stored_player[i].active)
1803 tape.player_participates[i] = TRUE;
1808 for (i = 0; i < MAX_PLAYERS; i++)
1810 struct PlayerInfo *player = &stored_player[i];
1812 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1817 if (local_player == player)
1818 printf("Player %d is local player.\n", i+1);
1822 if (BorderElement == EL_EMPTY)
1825 SBX_Right = lev_fieldx - SCR_FIELDX;
1827 SBY_Lower = lev_fieldy - SCR_FIELDY;
1832 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1834 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1837 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1838 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1840 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1841 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1843 /* if local player not found, look for custom element that might create
1844 the player (make some assumptions about the right custom element) */
1845 if (!local_player->present)
1847 int start_x = 0, start_y = 0;
1848 int found_rating = 0;
1849 int found_element = EL_UNDEFINED;
1851 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1853 int element = Feld[x][y];
1858 if (!IS_CUSTOM_ELEMENT(element))
1861 if (CAN_CHANGE(element))
1863 for (i = 0; i < element_info[element].num_change_pages; i++)
1865 content = element_info[element].change_page[i].target_element;
1866 is_player = ELEM_IS_PLAYER(content);
1868 if (is_player && (found_rating < 3 || element < found_element))
1874 found_element = element;
1879 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1881 content = element_info[element].content[xx][yy];
1882 is_player = ELEM_IS_PLAYER(content);
1884 if (is_player && (found_rating < 2 || element < found_element))
1886 start_x = x + xx - 1;
1887 start_y = y + yy - 1;
1890 found_element = element;
1893 if (!CAN_CHANGE(element))
1896 for (i = 0; i < element_info[element].num_change_pages; i++)
1898 content= element_info[element].change_page[i].target_content[xx][yy];
1899 is_player = ELEM_IS_PLAYER(content);
1901 if (is_player && (found_rating < 1 || element < found_element))
1903 start_x = x + xx - 1;
1904 start_y = y + yy - 1;
1907 found_element = element;
1913 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1914 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1917 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1918 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1924 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1925 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1926 local_player->jx - MIDPOSX);
1928 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1929 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1930 local_player->jy - MIDPOSY);
1932 scroll_x = SBX_Left;
1933 scroll_y = SBY_Upper;
1934 if (local_player->jx >= SBX_Left + MIDPOSX)
1935 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1936 local_player->jx - MIDPOSX :
1938 if (local_player->jy >= SBY_Upper + MIDPOSY)
1939 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1940 local_player->jy - MIDPOSY :
1945 CloseDoor(DOOR_CLOSE_1);
1950 /* after drawing the level, correct some elements */
1951 if (game.timegate_time_left == 0)
1952 CloseAllOpenTimegates();
1954 if (setup.soft_scrolling)
1955 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1957 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1960 /* copy default game door content to main double buffer */
1961 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1962 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1964 DrawGameDoorValues();
1968 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1969 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1970 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1974 /* copy actual game door content to door double buffer for OpenDoor() */
1975 BlitBitmap(drawto, bitmap_db_door,
1976 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1978 OpenDoor(DOOR_OPEN_ALL);
1980 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1982 if (setup.sound_music)
1985 KeyboardAutoRepeatOffUnlessAutoplay();
1989 for (i = 0; i < MAX_PLAYERS; i++)
1990 printf("Player %d %sactive.\n",
1991 i + 1, (stored_player[i].active ? "" : "not "));
1995 printf("::: starting game [%d]\n", FrameCounter);
1999 void InitMovDir(int x, int y)
2001 int i, element = Feld[x][y];
2002 static int xy[4][2] =
2009 static int direction[3][4] =
2011 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2012 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2013 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2022 Feld[x][y] = EL_BUG;
2023 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2026 case EL_SPACESHIP_RIGHT:
2027 case EL_SPACESHIP_UP:
2028 case EL_SPACESHIP_LEFT:
2029 case EL_SPACESHIP_DOWN:
2030 Feld[x][y] = EL_SPACESHIP;
2031 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2034 case EL_BD_BUTTERFLY_RIGHT:
2035 case EL_BD_BUTTERFLY_UP:
2036 case EL_BD_BUTTERFLY_LEFT:
2037 case EL_BD_BUTTERFLY_DOWN:
2038 Feld[x][y] = EL_BD_BUTTERFLY;
2039 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2042 case EL_BD_FIREFLY_RIGHT:
2043 case EL_BD_FIREFLY_UP:
2044 case EL_BD_FIREFLY_LEFT:
2045 case EL_BD_FIREFLY_DOWN:
2046 Feld[x][y] = EL_BD_FIREFLY;
2047 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2050 case EL_PACMAN_RIGHT:
2052 case EL_PACMAN_LEFT:
2053 case EL_PACMAN_DOWN:
2054 Feld[x][y] = EL_PACMAN;
2055 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2058 case EL_SP_SNIKSNAK:
2059 MovDir[x][y] = MV_UP;
2062 case EL_SP_ELECTRON:
2063 MovDir[x][y] = MV_LEFT;
2070 Feld[x][y] = EL_MOLE;
2071 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2075 if (IS_CUSTOM_ELEMENT(element))
2077 struct ElementInfo *ei = &element_info[element];
2078 int move_direction_initial = ei->move_direction_initial;
2079 int move_pattern = ei->move_pattern;
2081 if (move_direction_initial == MV_START_PREVIOUS)
2083 if (MovDir[x][y] != MV_NO_MOVING)
2086 move_direction_initial = MV_START_AUTOMATIC;
2089 if (move_direction_initial == MV_START_RANDOM)
2090 MovDir[x][y] = 1 << RND(4);
2091 else if (move_direction_initial & MV_ANY_DIRECTION)
2092 MovDir[x][y] = move_direction_initial;
2093 else if (move_pattern == MV_ALL_DIRECTIONS ||
2094 move_pattern == MV_TURNING_LEFT ||
2095 move_pattern == MV_TURNING_RIGHT ||
2096 move_pattern == MV_TURNING_LEFT_RIGHT ||
2097 move_pattern == MV_TURNING_RIGHT_LEFT ||
2098 move_pattern == MV_TURNING_RANDOM)
2099 MovDir[x][y] = 1 << RND(4);
2100 else if (move_pattern == MV_HORIZONTAL)
2101 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2102 else if (move_pattern == MV_VERTICAL)
2103 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2104 else if (move_pattern & MV_ANY_DIRECTION)
2105 MovDir[x][y] = element_info[element].move_pattern;
2106 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2107 move_pattern == MV_ALONG_RIGHT_SIDE)
2110 /* use random direction as default start direction */
2111 if (game.engine_version >= VERSION_IDENT(3,1,0,2))
2112 MovDir[x][y] = 1 << RND(4);
2115 for (i = 0; i < NUM_DIRECTIONS; i++)
2117 int x1 = x + xy[i][0];
2118 int y1 = y + xy[i][1];
2120 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2122 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2123 MovDir[x][y] = direction[0][i];
2125 MovDir[x][y] = direction[1][i];
2134 MovDir[x][y] = 1 << RND(4);
2136 if (element != EL_BUG &&
2137 element != EL_SPACESHIP &&
2138 element != EL_BD_BUTTERFLY &&
2139 element != EL_BD_FIREFLY)
2142 for (i = 0; i < NUM_DIRECTIONS; i++)
2144 int x1 = x + xy[i][0];
2145 int y1 = y + xy[i][1];
2147 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2149 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2151 MovDir[x][y] = direction[0][i];
2154 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2155 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2157 MovDir[x][y] = direction[1][i];
2166 GfxDir[x][y] = MovDir[x][y];
2169 void InitAmoebaNr(int x, int y)
2172 int group_nr = AmoebeNachbarNr(x, y);
2176 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2178 if (AmoebaCnt[i] == 0)
2186 AmoebaNr[x][y] = group_nr;
2187 AmoebaCnt[group_nr]++;
2188 AmoebaCnt2[group_nr]++;
2194 boolean raise_level = FALSE;
2196 if (local_player->MovPos)
2200 if (tape.auto_play) /* tape might already be stopped here */
2201 tape.auto_play_level_solved = TRUE;
2203 if (tape.playing && tape.auto_play)
2204 tape.auto_play_level_solved = TRUE;
2207 local_player->LevelSolved = FALSE;
2209 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2213 if (!tape.playing && setup.sound_loops)
2214 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2215 SND_CTRL_PLAY_LOOP);
2217 while (TimeLeft > 0)
2219 if (!tape.playing && !setup.sound_loops)
2220 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2221 if (TimeLeft > 0 && !(TimeLeft % 10))
2222 RaiseScore(level.score[SC_TIME_BONUS]);
2223 if (TimeLeft > 100 && !(TimeLeft % 10))
2228 DrawGameValue_Time(TimeLeft);
2236 if (!tape.playing && setup.sound_loops)
2237 StopSound(SND_GAME_LEVELTIME_BONUS);
2239 else if (level.time == 0) /* level without time limit */
2241 if (!tape.playing && setup.sound_loops)
2242 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2243 SND_CTRL_PLAY_LOOP);
2245 while (TimePlayed < 999)
2247 if (!tape.playing && !setup.sound_loops)
2248 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2249 if (TimePlayed < 999 && !(TimePlayed % 10))
2250 RaiseScore(level.score[SC_TIME_BONUS]);
2251 if (TimePlayed < 900 && !(TimePlayed % 10))
2256 DrawGameValue_Time(TimePlayed);
2264 if (!tape.playing && setup.sound_loops)
2265 StopSound(SND_GAME_LEVELTIME_BONUS);
2268 /* close exit door after last player */
2269 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2270 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2272 int element = Feld[ExitX][ExitY];
2274 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2275 EL_SP_EXIT_CLOSING);
2277 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2280 /* Hero disappears */
2281 DrawLevelField(ExitX, ExitY);
2287 CloseDoor(DOOR_CLOSE_1);
2292 SaveTape(tape.level_nr); /* Ask to save tape */
2295 if (level_nr == leveldir_current->handicap_level)
2297 leveldir_current->handicap_level++;
2298 SaveLevelSetup_SeriesInfo();
2301 if (level_editor_test_game)
2302 local_player->score = -1; /* no highscore when playing from editor */
2303 else if (level_nr < leveldir_current->last_level)
2304 raise_level = TRUE; /* advance to next level */
2306 if ((hi_pos = NewHiScore()) >= 0)
2308 game_status = GAME_MODE_SCORES;
2309 DrawHallOfFame(hi_pos);
2318 game_status = GAME_MODE_MAIN;
2335 LoadScore(level_nr);
2337 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2338 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2341 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2343 if (local_player->score > highscore[k].Score)
2345 /* player has made it to the hall of fame */
2347 if (k < MAX_SCORE_ENTRIES - 1)
2349 int m = MAX_SCORE_ENTRIES - 1;
2352 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2353 if (!strcmp(setup.player_name, highscore[l].Name))
2355 if (m == k) /* player's new highscore overwrites his old one */
2359 for (l = m; l > k; l--)
2361 strcpy(highscore[l].Name, highscore[l - 1].Name);
2362 highscore[l].Score = highscore[l - 1].Score;
2369 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2370 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2371 highscore[k].Score = local_player->score;
2377 else if (!strncmp(setup.player_name, highscore[k].Name,
2378 MAX_PLAYER_NAME_LEN))
2379 break; /* player already there with a higher score */
2385 SaveScore(level_nr);
2390 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2392 if (player->GfxAction != action || player->GfxDir != dir)
2395 printf("Player frame reset! (%d => %d, %d => %d)\n",
2396 player->GfxAction, action, player->GfxDir, dir);
2399 player->GfxAction = action;
2400 player->GfxDir = dir;
2402 player->StepFrame = 0;
2406 static void ResetRandomAnimationValue(int x, int y)
2408 GfxRandom[x][y] = INIT_GFX_RANDOM();
2411 static void ResetGfxAnimation(int x, int y)
2414 GfxAction[x][y] = ACTION_DEFAULT;
2415 GfxDir[x][y] = MovDir[x][y];
2418 void InitMovingField(int x, int y, int direction)
2420 int element = Feld[x][y];
2421 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2422 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2426 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2427 ResetGfxAnimation(x, y);
2429 MovDir[newx][newy] = MovDir[x][y] = direction;
2430 GfxDir[x][y] = direction;
2432 if (Feld[newx][newy] == EL_EMPTY)
2433 Feld[newx][newy] = EL_BLOCKED;
2435 if (direction == MV_DOWN && CAN_FALL(element))
2436 GfxAction[x][y] = ACTION_FALLING;
2438 GfxAction[x][y] = ACTION_MOVING;
2440 GfxFrame[newx][newy] = GfxFrame[x][y];
2441 GfxRandom[newx][newy] = GfxRandom[x][y];
2442 GfxAction[newx][newy] = GfxAction[x][y];
2443 GfxDir[newx][newy] = GfxDir[x][y];
2446 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2448 int direction = MovDir[x][y];
2449 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2450 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2456 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2458 int oldx = x, oldy = y;
2459 int direction = MovDir[x][y];
2461 if (direction == MV_LEFT)
2463 else if (direction == MV_RIGHT)
2465 else if (direction == MV_UP)
2467 else if (direction == MV_DOWN)
2470 *comes_from_x = oldx;
2471 *comes_from_y = oldy;
2474 int MovingOrBlocked2Element(int x, int y)
2476 int element = Feld[x][y];
2478 if (element == EL_BLOCKED)
2482 Blocked2Moving(x, y, &oldx, &oldy);
2483 return Feld[oldx][oldy];
2489 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2491 /* like MovingOrBlocked2Element(), but if element is moving
2492 and (x,y) is the field the moving element is just leaving,
2493 return EL_BLOCKED instead of the element value */
2494 int element = Feld[x][y];
2496 if (IS_MOVING(x, y))
2498 if (element == EL_BLOCKED)
2502 Blocked2Moving(x, y, &oldx, &oldy);
2503 return Feld[oldx][oldy];
2512 static void RemoveField(int x, int y)
2514 Feld[x][y] = EL_EMPTY;
2521 ChangeDelay[x][y] = 0;
2522 ChangePage[x][y] = -1;
2523 Pushed[x][y] = FALSE;
2526 ExplodeField[x][y] = EX_TYPE_NONE;
2529 GfxElement[x][y] = EL_UNDEFINED;
2530 GfxAction[x][y] = ACTION_DEFAULT;
2531 GfxDir[x][y] = MV_NO_MOVING;
2534 void RemoveMovingField(int x, int y)
2536 int oldx = x, oldy = y, newx = x, newy = y;
2537 int element = Feld[x][y];
2538 int next_element = EL_UNDEFINED;
2540 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2543 if (IS_MOVING(x, y))
2545 Moving2Blocked(x, y, &newx, &newy);
2547 if (Feld[newx][newy] != EL_BLOCKED)
2550 if (Feld[newx][newy] != EL_BLOCKED)
2552 /* element is moving, but target field is not free (blocked), but
2553 already occupied by something different (example: acid pool);
2554 in this case, only remove the moving field, but not the target */
2556 RemoveField(oldx, oldy);
2558 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2560 DrawLevelField(oldx, oldy);
2566 else if (element == EL_BLOCKED)
2568 Blocked2Moving(x, y, &oldx, &oldy);
2569 if (!IS_MOVING(oldx, oldy))
2573 if (element == EL_BLOCKED &&
2574 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2575 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2576 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2577 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2578 next_element = get_next_element(Feld[oldx][oldy]);
2580 RemoveField(oldx, oldy);
2581 RemoveField(newx, newy);
2583 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2585 if (next_element != EL_UNDEFINED)
2586 Feld[oldx][oldy] = next_element;
2588 DrawLevelField(oldx, oldy);
2589 DrawLevelField(newx, newy);
2592 void DrawDynamite(int x, int y)
2594 int sx = SCREENX(x), sy = SCREENY(y);
2595 int graphic = el2img(Feld[x][y]);
2598 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2601 if (IS_WALKABLE_INSIDE(Back[x][y]))
2605 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2606 else if (Store[x][y])
2607 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2609 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2612 if (Back[x][y] || Store[x][y])
2613 DrawGraphicThruMask(sx, sy, graphic, frame);
2615 DrawGraphic(sx, sy, graphic, frame);
2617 if (game.emulation == EMU_SUPAPLEX)
2618 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2619 else if (Store[x][y])
2620 DrawGraphicThruMask(sx, sy, graphic, frame);
2622 DrawGraphic(sx, sy, graphic, frame);
2626 void CheckDynamite(int x, int y)
2628 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2632 if (MovDelay[x][y] != 0)
2635 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2642 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2644 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2645 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2646 StopSound(SND_DYNAMITE_ACTIVE);
2648 StopSound(SND_DYNABOMB_ACTIVE);
2654 void RelocatePlayer(int x, int y, int element_raw)
2656 int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
2657 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2658 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2659 boolean no_delay = (tape.warp_forward);
2660 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2661 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2664 if (player->GameOver) /* do not reanimate dead player */
2667 RemoveField(x, y); /* temporarily remove newly placed player */
2668 DrawLevelField(x, y);
2670 if (player->present)
2672 while (player->MovPos)
2674 ScrollPlayer(player, SCROLL_GO_ON);
2675 ScrollScreen(NULL, SCROLL_GO_ON);
2681 Delay(wait_delay_value);
2684 DrawPlayer(player); /* needed here only to cleanup last field */
2685 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2687 player->is_moving = FALSE;
2690 old_jx = player->jx;
2691 old_jy = player->jy;
2693 Feld[x][y] = element;
2694 InitPlayerField(x, y, element, TRUE);
2696 if (player != local_player) /* do not visually relocate other players */
2699 if (level.instant_relocation)
2702 int offset = (setup.scroll_delay ? 3 : 0);
2703 int jx = local_player->jx;
2704 int jy = local_player->jy;
2706 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2708 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2709 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2710 local_player->jx - MIDPOSX);
2712 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2713 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2714 local_player->jy - MIDPOSY);
2718 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2719 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2720 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2722 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2723 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2724 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2726 /* don't scroll over playfield boundaries */
2727 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2728 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2730 /* don't scroll over playfield boundaries */
2731 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2732 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2735 scroll_x += (local_player->jx - old_jx);
2736 scroll_y += (local_player->jy - old_jy);
2738 /* don't scroll over playfield boundaries */
2739 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2740 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2742 /* don't scroll over playfield boundaries */
2743 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2744 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2747 RedrawPlayfield(TRUE, 0,0,0,0);
2753 int offset = (setup.scroll_delay ? 3 : 0);
2754 int jx = local_player->jx;
2755 int jy = local_player->jy;
2757 int scroll_xx = -999, scroll_yy = -999;
2759 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2761 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2764 int fx = FX, fy = FY;
2766 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2767 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2768 local_player->jx - MIDPOSX);
2770 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2771 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2772 local_player->jy - MIDPOSY);
2774 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2775 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2778 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2781 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2788 fx += dx * TILEX / 2;
2789 fy += dy * TILEY / 2;
2791 ScrollLevel(dx, dy);
2794 /* scroll in two steps of half tile size to make things smoother */
2795 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2797 Delay(wait_delay_value);
2799 /* scroll second step to align at full tile size */
2801 Delay(wait_delay_value);
2804 int scroll_xx = -999, scroll_yy = -999;
2806 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2808 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2811 int fx = FX, fy = FY;
2813 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2814 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2815 local_player->jx - MIDPOSX);
2817 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2818 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2819 local_player->jy - MIDPOSY);
2821 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2822 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2825 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2828 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2835 fx += dx * TILEX / 2;
2836 fy += dy * TILEY / 2;
2838 ScrollLevel(dx, dy);
2841 /* scroll in two steps of half tile size to make things smoother */
2842 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2844 Delay(wait_delay_value);
2846 /* scroll second step to align at full tile size */
2848 Delay(wait_delay_value);
2854 void Explode(int ex, int ey, int phase, int mode)
2861 /* !!! eliminate this variable !!! */
2862 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2867 int last_phase = num_phase * delay;
2868 int half_phase = (num_phase / 2) * delay;
2869 int first_phase_after_start = EX_PHASE_START + 1;
2873 if (game.explosions_delayed)
2875 ExplodeField[ex][ey] = mode;
2879 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2881 int center_element = Feld[ex][ey];
2884 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
2888 /* --- This is only really needed (and now handled) in "Impact()". --- */
2889 /* do not explode moving elements that left the explode field in time */
2890 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2891 center_element == EL_EMPTY &&
2892 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
2896 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
2897 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2899 /* remove things displayed in background while burning dynamite */
2900 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2903 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2905 /* put moving element to center field (and let it explode there) */
2906 center_element = MovingOrBlocked2Element(ex, ey);
2907 RemoveMovingField(ex, ey);
2908 Feld[ex][ey] = center_element;
2914 last_phase = element_info[center_element].explosion_delay + 1;
2916 last_phase = element_info[center_element].explosion_delay;
2920 printf("::: %d -> %d\n", center_element, last_phase);
2924 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2926 int xx = x - ex + 1;
2927 int yy = y - ey + 1;
2932 if (!IN_LEV_FIELD(x, y) ||
2933 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
2934 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
2937 if (!IN_LEV_FIELD(x, y) ||
2938 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
2942 if (!IN_LEV_FIELD(x, y) ||
2943 ((mode != EX_TYPE_NORMAL ||
2944 center_element == EL_AMOEBA_TO_DIAMOND) &&
2945 (x != ex || y != ey)))
2949 element = Feld[x][y];
2951 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2953 element = MovingOrBlocked2Element(x, y);
2955 if (!IS_EXPLOSION_PROOF(element))
2956 RemoveMovingField(x, y);
2962 if (IS_EXPLOSION_PROOF(element))
2965 /* indestructible elements can only explode in center (but not flames) */
2966 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2967 element == EL_FLAMES)
2972 if ((IS_INDESTRUCTIBLE(element) &&
2973 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2974 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2975 element == EL_FLAMES)
2979 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2981 if (IS_ACTIVE_BOMB(element))
2983 /* re-activate things under the bomb like gate or penguin */
2984 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2991 /* save walkable background elements while explosion on same tile */
2993 if (IS_INDESTRUCTIBLE(element))
2994 Back[x][y] = element;
2996 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2997 Back[x][y] = element;
3000 /* ignite explodable elements reached by other explosion */
3001 if (element == EL_EXPLOSION)
3002 element = Store2[x][y];
3005 if (AmoebaNr[x][y] &&
3006 (element == EL_AMOEBA_FULL ||
3007 element == EL_BD_AMOEBA ||
3008 element == EL_AMOEBA_GROWING))
3010 AmoebaCnt[AmoebaNr[x][y]]--;
3011 AmoebaCnt2[AmoebaNr[x][y]]--;
3017 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3019 switch(StorePlayer[ex][ey])
3022 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3025 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3028 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3032 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3037 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3038 Store[x][y] = EL_EMPTY;
3040 if (game.emulation == EMU_SUPAPLEX)
3041 Store[x][y] = EL_EMPTY;
3044 else if (center_element == EL_MOLE)
3045 Store[x][y] = EL_EMERALD_RED;
3046 else if (center_element == EL_PENGUIN)
3047 Store[x][y] = EL_EMERALD_PURPLE;
3048 else if (center_element == EL_BUG)
3049 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3050 else if (center_element == EL_BD_BUTTERFLY)
3051 Store[x][y] = EL_BD_DIAMOND;
3052 else if (center_element == EL_SP_ELECTRON)
3053 Store[x][y] = EL_SP_INFOTRON;
3054 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3055 Store[x][y] = level.amoeba_content;
3056 else if (center_element == EL_YAMYAM)
3057 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3058 else if (IS_CUSTOM_ELEMENT(center_element) &&
3059 element_info[center_element].content[xx][yy] != EL_EMPTY)
3060 Store[x][y] = element_info[center_element].content[xx][yy];
3061 else if (element == EL_WALL_EMERALD)
3062 Store[x][y] = EL_EMERALD;
3063 else if (element == EL_WALL_DIAMOND)
3064 Store[x][y] = EL_DIAMOND;
3065 else if (element == EL_WALL_BD_DIAMOND)
3066 Store[x][y] = EL_BD_DIAMOND;
3067 else if (element == EL_WALL_EMERALD_YELLOW)
3068 Store[x][y] = EL_EMERALD_YELLOW;
3069 else if (element == EL_WALL_EMERALD_RED)
3070 Store[x][y] = EL_EMERALD_RED;
3071 else if (element == EL_WALL_EMERALD_PURPLE)
3072 Store[x][y] = EL_EMERALD_PURPLE;
3073 else if (element == EL_WALL_PEARL)
3074 Store[x][y] = EL_PEARL;
3075 else if (element == EL_WALL_CRYSTAL)
3076 Store[x][y] = EL_CRYSTAL;
3077 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3078 Store[x][y] = element_info[element].content[1][1];
3080 Store[x][y] = EL_EMPTY;
3082 if (x != ex || y != ey ||
3083 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_TYPE_BORDER)
3084 Store2[x][y] = element;
3087 if (AmoebaNr[x][y] &&
3088 (element == EL_AMOEBA_FULL ||
3089 element == EL_BD_AMOEBA ||
3090 element == EL_AMOEBA_GROWING))
3092 AmoebaCnt[AmoebaNr[x][y]]--;
3093 AmoebaCnt2[AmoebaNr[x][y]]--;
3099 MovDir[x][y] = MovPos[x][y] = 0;
3100 GfxDir[x][y] = MovDir[x][y];
3105 Feld[x][y] = EL_EXPLOSION;
3107 GfxElement[x][y] = center_element;
3109 GfxElement[x][y] = EL_UNDEFINED;
3112 ExplodePhase[x][y] = 1;
3114 ExplodeDelay[x][y] = last_phase;
3119 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3121 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3128 if (center_element == EL_YAMYAM)
3129 game.yamyam_content_nr =
3130 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3143 GfxFrame[x][y] = 0; /* restart explosion animation */
3147 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3151 last_phase = ExplodeDelay[x][y];
3154 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3158 /* activate this even in non-DEBUG version until cause for crash in
3159 getGraphicAnimationFrame() (see below) is found and eliminated */
3163 if (GfxElement[x][y] == EL_UNDEFINED)
3166 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3167 printf("Explode(): This should never happen!\n");
3170 GfxElement[x][y] = EL_EMPTY;
3176 border_element = Store2[x][y];
3177 if (IS_PLAYER(x, y))
3178 border_element = StorePlayer[x][y];
3181 printf("::: phase == %d\n", phase);
3184 if (phase == element_info[border_element].ignition_delay ||
3185 phase == last_phase)
3187 boolean border_explosion = FALSE;
3190 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3192 if (IS_PLAYER(x, y))
3195 KillHeroUnlessExplosionProtected(x, y);
3196 border_explosion = TRUE;
3199 if (phase == last_phase)
3200 printf("::: IS_PLAYER\n");
3203 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3205 Feld[x][y] = Store2[x][y];
3208 border_explosion = TRUE;
3211 if (phase == last_phase)
3212 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3215 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3217 AmoebeUmwandeln(x, y);
3219 border_explosion = TRUE;
3222 if (phase == last_phase)
3223 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3224 element_info[border_element].explosion_delay,
3225 element_info[border_element].ignition_delay,
3231 /* if an element just explodes due to another explosion (chain-reaction),
3232 do not immediately end the new explosion when it was the last frame of
3233 the explosion (as it would be done in the following "if"-statement!) */
3234 if (border_explosion && phase == last_phase)
3241 if (phase == first_phase_after_start)
3243 int element = Store2[x][y];
3245 if (element == EL_BLACK_ORB)
3247 Feld[x][y] = Store2[x][y];
3252 else if (phase == half_phase)
3254 int element = Store2[x][y];
3256 if (IS_PLAYER(x, y))
3257 KillHeroUnlessExplosionProtected(x, y);
3258 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3260 Feld[x][y] = Store2[x][y];
3264 else if (element == EL_AMOEBA_TO_DIAMOND)
3265 AmoebeUmwandeln(x, y);
3269 if (phase == last_phase)
3274 printf("::: done: phase == %d\n", phase);
3278 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3281 element = Feld[x][y] = Store[x][y];
3282 Store[x][y] = Store2[x][y] = 0;
3283 GfxElement[x][y] = EL_UNDEFINED;
3285 /* player can escape from explosions and might therefore be still alive */
3286 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3287 element <= EL_PLAYER_IS_EXPLODING_4)
3288 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3290 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3291 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3292 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3295 /* restore probably existing indestructible background element */
3296 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3297 element = Feld[x][y] = Back[x][y];
3300 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3301 GfxDir[x][y] = MV_NO_MOVING;
3302 ChangeDelay[x][y] = 0;
3303 ChangePage[x][y] = -1;
3306 InitField_WithBug2(x, y, FALSE);
3308 InitField(x, y, FALSE);
3310 /* !!! not needed !!! */
3312 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3313 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3316 if (CAN_MOVE(element))
3321 DrawLevelField(x, y);
3323 TestIfElementTouchesCustomElement(x, y);
3325 if (GFX_CRUMBLED(element))
3326 DrawLevelFieldCrumbledSandNeighbours(x, y);
3328 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3329 StorePlayer[x][y] = 0;
3331 if (ELEM_IS_PLAYER(element))
3332 RelocatePlayer(x, y, element);
3335 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3337 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3341 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3343 int stored = Store[x][y];
3344 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3345 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3349 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3351 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3355 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3359 printf("::: %d / %d [%d - %d]\n",
3360 GfxFrame[x][y], phase - delay, phase, delay);
3364 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3365 element_info[GfxElement[x][y]].token_name,
3370 DrawLevelFieldCrumbledSand(x, y);
3372 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3374 DrawLevelElement(x, y, Back[x][y]);
3375 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3377 else if (IS_WALKABLE_UNDER(Back[x][y]))
3379 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3380 DrawLevelElementThruMask(x, y, Back[x][y]);
3382 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3383 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3387 void DynaExplode(int ex, int ey)
3390 int dynabomb_element = Feld[ex][ey];
3391 int dynabomb_size = 1;
3392 boolean dynabomb_xl = FALSE;
3393 struct PlayerInfo *player;
3394 static int xy[4][2] =
3402 if (IS_ACTIVE_BOMB(dynabomb_element))
3404 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3405 dynabomb_size = player->dynabomb_size;
3406 dynabomb_xl = player->dynabomb_xl;
3407 player->dynabombs_left++;
3410 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3412 for (i = 0; i < NUM_DIRECTIONS; i++)
3414 for (j = 1; j <= dynabomb_size; j++)
3416 int x = ex + j * xy[i][0];
3417 int y = ey + j * xy[i][1];
3420 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3423 element = Feld[x][y];
3425 /* do not restart explosions of fields with active bombs */
3426 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3429 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3432 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3433 !CAN_GROW_INTO(element) && !dynabomb_xl)
3436 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3437 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3438 element != EL_SAND && !dynabomb_xl)
3445 void Bang(int x, int y)
3448 int element = MovingOrBlocked2Element(x, y);
3450 int element = Feld[x][y];
3454 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3456 if (IS_PLAYER(x, y))
3459 struct PlayerInfo *player = PLAYERINFO(x, y);
3461 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3462 player->element_nr);
3467 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3469 if (game.emulation == EMU_SUPAPLEX)
3470 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3472 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3477 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3485 case EL_BD_BUTTERFLY:
3488 case EL_DARK_YAMYAM:
3492 RaiseScoreElement(element);
3493 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3495 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3496 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3497 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3498 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3499 case EL_DYNABOMB_INCREASE_NUMBER:
3500 case EL_DYNABOMB_INCREASE_SIZE:
3501 case EL_DYNABOMB_INCREASE_POWER:
3506 case EL_LAMP_ACTIVE:
3508 case EL_AMOEBA_TO_DIAMOND:
3510 if (IS_PLAYER(x, y))
3511 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3513 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3516 if (CAN_EXPLODE_CROSS(element))
3518 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3522 else if (CAN_EXPLODE_1X1(element))
3523 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3525 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3529 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3532 void SplashAcid(int x, int y)
3535 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3536 (!IN_LEV_FIELD(x - 1, y - 2) ||
3537 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3538 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3540 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3541 (!IN_LEV_FIELD(x + 1, y - 2) ||
3542 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3543 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3545 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3547 /* input: position of element entering acid (obsolete) */
3549 int element = Feld[x][y];
3551 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3554 if (element != EL_ACID_SPLASH_LEFT &&
3555 element != EL_ACID_SPLASH_RIGHT)
3557 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3559 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3560 (!IN_LEV_FIELD(x - 1, y - 1) ||
3561 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3562 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3564 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3565 (!IN_LEV_FIELD(x + 1, y - 1) ||
3566 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3567 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3572 static void InitBeltMovement()
3574 static int belt_base_element[4] =
3576 EL_CONVEYOR_BELT_1_LEFT,
3577 EL_CONVEYOR_BELT_2_LEFT,
3578 EL_CONVEYOR_BELT_3_LEFT,
3579 EL_CONVEYOR_BELT_4_LEFT
3581 static int belt_base_active_element[4] =
3583 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3584 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3585 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3586 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3591 /* set frame order for belt animation graphic according to belt direction */
3592 for (i = 0; i < NUM_BELTS; i++)
3596 for (j = 0; j < NUM_BELT_PARTS; j++)
3598 int element = belt_base_active_element[belt_nr] + j;
3599 int graphic = el2img(element);
3601 if (game.belt_dir[i] == MV_LEFT)
3602 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3604 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3608 for (y = 0; y < lev_fieldy; y++)
3610 for (x = 0; x < lev_fieldx; x++)
3612 int element = Feld[x][y];
3614 for (i = 0; i < NUM_BELTS; i++)
3616 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3618 int e_belt_nr = getBeltNrFromBeltElement(element);
3621 if (e_belt_nr == belt_nr)
3623 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3625 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3633 static void ToggleBeltSwitch(int x, int y)
3635 static int belt_base_element[4] =
3637 EL_CONVEYOR_BELT_1_LEFT,
3638 EL_CONVEYOR_BELT_2_LEFT,
3639 EL_CONVEYOR_BELT_3_LEFT,
3640 EL_CONVEYOR_BELT_4_LEFT
3642 static int belt_base_active_element[4] =
3644 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3645 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3646 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3647 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3649 static int belt_base_switch_element[4] =
3651 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3652 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3653 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3654 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3656 static int belt_move_dir[4] =
3664 int element = Feld[x][y];
3665 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3666 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3667 int belt_dir = belt_move_dir[belt_dir_nr];
3670 if (!IS_BELT_SWITCH(element))
3673 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3674 game.belt_dir[belt_nr] = belt_dir;
3676 if (belt_dir_nr == 3)
3679 /* set frame order for belt animation graphic according to belt direction */
3680 for (i = 0; i < NUM_BELT_PARTS; i++)
3682 int element = belt_base_active_element[belt_nr] + i;
3683 int graphic = el2img(element);
3685 if (belt_dir == MV_LEFT)
3686 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3688 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3691 for (yy = 0; yy < lev_fieldy; yy++)
3693 for (xx = 0; xx < lev_fieldx; xx++)
3695 int element = Feld[xx][yy];
3697 if (IS_BELT_SWITCH(element))
3699 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3701 if (e_belt_nr == belt_nr)
3703 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3704 DrawLevelField(xx, yy);
3707 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3709 int e_belt_nr = getBeltNrFromBeltElement(element);
3711 if (e_belt_nr == belt_nr)
3713 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3715 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3716 DrawLevelField(xx, yy);
3719 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3721 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3723 if (e_belt_nr == belt_nr)
3725 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3727 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3728 DrawLevelField(xx, yy);
3735 static void ToggleSwitchgateSwitch(int x, int y)
3739 game.switchgate_pos = !game.switchgate_pos;
3741 for (yy = 0; yy < lev_fieldy; yy++)
3743 for (xx = 0; xx < lev_fieldx; xx++)
3745 int element = Feld[xx][yy];
3747 if (element == EL_SWITCHGATE_SWITCH_UP ||
3748 element == EL_SWITCHGATE_SWITCH_DOWN)
3750 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3751 DrawLevelField(xx, yy);
3753 else if (element == EL_SWITCHGATE_OPEN ||
3754 element == EL_SWITCHGATE_OPENING)
3756 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3758 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3760 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3763 else if (element == EL_SWITCHGATE_CLOSED ||
3764 element == EL_SWITCHGATE_CLOSING)
3766 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3768 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3770 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3777 static int getInvisibleActiveFromInvisibleElement(int element)
3779 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3780 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3781 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3785 static int getInvisibleFromInvisibleActiveElement(int element)
3787 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3788 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3789 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3793 static void RedrawAllLightSwitchesAndInvisibleElements()
3797 for (y = 0; y < lev_fieldy; y++)
3799 for (x = 0; x < lev_fieldx; x++)
3801 int element = Feld[x][y];
3803 if (element == EL_LIGHT_SWITCH &&
3804 game.light_time_left > 0)
3806 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3807 DrawLevelField(x, y);
3809 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3810 game.light_time_left == 0)
3812 Feld[x][y] = EL_LIGHT_SWITCH;
3813 DrawLevelField(x, y);
3815 else if (element == EL_INVISIBLE_STEELWALL ||
3816 element == EL_INVISIBLE_WALL ||
3817 element == EL_INVISIBLE_SAND)
3819 if (game.light_time_left > 0)
3820 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3822 DrawLevelField(x, y);
3824 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3825 element == EL_INVISIBLE_WALL_ACTIVE ||
3826 element == EL_INVISIBLE_SAND_ACTIVE)
3828 if (game.light_time_left == 0)
3829 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3831 DrawLevelField(x, y);
3837 static void ToggleLightSwitch(int x, int y)
3839 int element = Feld[x][y];
3841 game.light_time_left =
3842 (element == EL_LIGHT_SWITCH ?
3843 level.time_light * FRAMES_PER_SECOND : 0);
3845 RedrawAllLightSwitchesAndInvisibleElements();
3848 static void ActivateTimegateSwitch(int x, int y)
3852 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3854 for (yy = 0; yy < lev_fieldy; yy++)
3856 for (xx = 0; xx < lev_fieldx; xx++)
3858 int element = Feld[xx][yy];
3860 if (element == EL_TIMEGATE_CLOSED ||
3861 element == EL_TIMEGATE_CLOSING)
3863 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3864 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3868 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3870 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3871 DrawLevelField(xx, yy);
3878 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3881 inline static int getElementMoveStepsize(int x, int y)
3883 int element = Feld[x][y];
3884 int direction = MovDir[x][y];
3885 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3886 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3887 int horiz_move = (dx != 0);
3888 int sign = (horiz_move ? dx : dy);
3889 int step = sign * element_info[element].move_stepsize;
3891 /* special values for move stepsize for spring and things on conveyor belt */
3895 if (element == EL_SPRING)
3896 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3897 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3898 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3899 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3901 if (CAN_FALL(element) &&
3902 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3903 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3904 else if (element == EL_SPRING)
3905 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3912 void Impact(int x, int y)
3914 boolean lastline = (y == lev_fieldy-1);
3915 boolean object_hit = FALSE;
3916 boolean impact = (lastline || object_hit);
3917 int element = Feld[x][y];
3918 int smashed = EL_STEELWALL;
3920 if (!lastline) /* check if element below was hit */
3922 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3925 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3926 MovDir[x][y + 1] != MV_DOWN ||
3927 MovPos[x][y + 1] <= TILEY / 2));
3930 object_hit = !IS_FREE(x, y + 1);
3933 /* do not smash moving elements that left the smashed field in time */
3934 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3935 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3939 smashed = MovingOrBlocked2Element(x, y + 1);
3941 impact = (lastline || object_hit);
3944 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3946 SplashAcid(x, y + 1);
3950 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
3951 /* only reset graphic animation if graphic really changes after impact */
3953 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3955 ResetGfxAnimation(x, y);
3956 DrawLevelField(x, y);
3959 if (impact && CAN_EXPLODE_IMPACT(element))
3964 else if (impact && element == EL_PEARL)
3966 ResetGfxAnimation(x, y);
3968 Feld[x][y] = EL_PEARL_BREAKING;
3969 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3972 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
3974 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3979 if (impact && element == EL_AMOEBA_DROP)
3981 if (object_hit && IS_PLAYER(x, y + 1))
3982 KillHeroUnlessEnemyProtected(x, y + 1);
3983 else if (object_hit && smashed == EL_PENGUIN)
3987 Feld[x][y] = EL_AMOEBA_GROWING;
3988 Store[x][y] = EL_AMOEBA_WET;
3990 ResetRandomAnimationValue(x, y);
3995 if (object_hit) /* check which object was hit */
3997 if (CAN_PASS_MAGIC_WALL(element) &&
3998 (smashed == EL_MAGIC_WALL ||
3999 smashed == EL_BD_MAGIC_WALL))
4002 int activated_magic_wall =
4003 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4004 EL_BD_MAGIC_WALL_ACTIVE);
4006 /* activate magic wall / mill */
4007 for (yy = 0; yy < lev_fieldy; yy++)
4008 for (xx = 0; xx < lev_fieldx; xx++)
4009 if (Feld[xx][yy] == smashed)
4010 Feld[xx][yy] = activated_magic_wall;
4012 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4013 game.magic_wall_active = TRUE;
4015 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4016 SND_MAGIC_WALL_ACTIVATING :
4017 SND_BD_MAGIC_WALL_ACTIVATING));
4020 if (IS_PLAYER(x, y + 1))
4022 if (CAN_SMASH_PLAYER(element))
4024 KillHeroUnlessEnemyProtected(x, y + 1);
4028 else if (smashed == EL_PENGUIN)
4030 if (CAN_SMASH_PLAYER(element))
4036 else if (element == EL_BD_DIAMOND)
4038 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4044 else if (((element == EL_SP_INFOTRON ||
4045 element == EL_SP_ZONK) &&
4046 (smashed == EL_SP_SNIKSNAK ||
4047 smashed == EL_SP_ELECTRON ||
4048 smashed == EL_SP_DISK_ORANGE)) ||
4049 (element == EL_SP_INFOTRON &&
4050 smashed == EL_SP_DISK_YELLOW))
4056 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4062 else if (CAN_SMASH_EVERYTHING(element))
4064 if (IS_CLASSIC_ENEMY(smashed) ||
4065 CAN_EXPLODE_SMASHED(smashed))
4070 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4072 if (smashed == EL_LAMP ||
4073 smashed == EL_LAMP_ACTIVE)
4078 else if (smashed == EL_NUT)
4080 Feld[x][y + 1] = EL_NUT_BREAKING;
4081 PlayLevelSound(x, y, SND_NUT_BREAKING);
4082 RaiseScoreElement(EL_NUT);
4085 else if (smashed == EL_PEARL)
4087 ResetGfxAnimation(x, y);
4089 Feld[x][y + 1] = EL_PEARL_BREAKING;
4090 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4093 else if (smashed == EL_DIAMOND)
4095 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4096 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4099 else if (IS_BELT_SWITCH(smashed))
4101 ToggleBeltSwitch(x, y + 1);
4103 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4104 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4106 ToggleSwitchgateSwitch(x, y + 1);
4108 else if (smashed == EL_LIGHT_SWITCH ||
4109 smashed == EL_LIGHT_SWITCH_ACTIVE)
4111 ToggleLightSwitch(x, y + 1);
4116 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4119 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4122 /* !!! TEST ONLY !!! */
4123 CheckElementChangeBySide(x, y + 1, smashed, element,
4124 CE_SWITCHED, CH_SIDE_TOP);
4125 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4126 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4128 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4129 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4130 CheckElementChangeBySide(x, y + 1, smashed, element,
4131 CE_SWITCHED, CH_SIDE_TOP);
4137 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4142 /* play sound of magic wall / mill */
4144 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4145 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4147 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4148 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4149 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4150 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4155 /* play sound of object that hits the ground */
4156 if (lastline || object_hit)
4157 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4160 inline static void TurnRoundExt(int x, int y)
4172 { 0, 0 }, { 0, 0 }, { 0, 0 },
4177 int left, right, back;
4181 { MV_DOWN, MV_UP, MV_RIGHT },
4182 { MV_UP, MV_DOWN, MV_LEFT },
4184 { MV_LEFT, MV_RIGHT, MV_DOWN },
4188 { MV_RIGHT, MV_LEFT, MV_UP }
4191 int element = Feld[x][y];
4192 int move_pattern = element_info[element].move_pattern;
4194 int old_move_dir = MovDir[x][y];
4195 int left_dir = turn[old_move_dir].left;
4196 int right_dir = turn[old_move_dir].right;
4197 int back_dir = turn[old_move_dir].back;
4199 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4200 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4201 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4202 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4204 int left_x = x + left_dx, left_y = y + left_dy;
4205 int right_x = x + right_dx, right_y = y + right_dy;
4206 int move_x = x + move_dx, move_y = y + move_dy;
4210 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4212 TestIfBadThingTouchesOtherBadThing(x, y);
4214 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4215 MovDir[x][y] = right_dir;
4216 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4217 MovDir[x][y] = left_dir;
4219 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4221 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4225 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4226 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4228 TestIfBadThingTouchesOtherBadThing(x, y);
4230 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4231 MovDir[x][y] = left_dir;
4232 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4233 MovDir[x][y] = right_dir;
4235 if ((element == EL_SPACESHIP ||
4236 element == EL_SP_SNIKSNAK ||
4237 element == EL_SP_ELECTRON)
4238 && MovDir[x][y] != old_move_dir)
4240 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4244 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4246 TestIfBadThingTouchesOtherBadThing(x, y);
4248 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4249 MovDir[x][y] = left_dir;
4250 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4251 MovDir[x][y] = right_dir;
4253 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4255 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4258 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4260 TestIfBadThingTouchesOtherBadThing(x, y);
4262 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4263 MovDir[x][y] = left_dir;
4264 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4265 MovDir[x][y] = right_dir;
4267 if (MovDir[x][y] != old_move_dir)
4271 else if (element == EL_YAMYAM)
4273 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4274 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4276 if (can_turn_left && can_turn_right)
4277 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4278 else if (can_turn_left)
4279 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4280 else if (can_turn_right)
4281 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4283 MovDir[x][y] = back_dir;
4285 MovDelay[x][y] = 16 + 16 * RND(3);
4287 else if (element == EL_DARK_YAMYAM)
4289 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4291 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4294 if (can_turn_left && can_turn_right)
4295 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4296 else if (can_turn_left)
4297 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4298 else if (can_turn_right)
4299 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4301 MovDir[x][y] = back_dir;
4303 MovDelay[x][y] = 16 + 16 * RND(3);
4305 else if (element == EL_PACMAN)
4307 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4308 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4310 if (can_turn_left && can_turn_right)
4311 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4312 else if (can_turn_left)
4313 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4314 else if (can_turn_right)
4315 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4317 MovDir[x][y] = back_dir;
4319 MovDelay[x][y] = 6 + RND(40);
4321 else if (element == EL_PIG)
4323 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4324 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4325 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4326 boolean should_turn_left, should_turn_right, should_move_on;
4328 int rnd = RND(rnd_value);
4330 should_turn_left = (can_turn_left &&
4332 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4333 y + back_dy + left_dy)));
4334 should_turn_right = (can_turn_right &&
4336 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4337 y + back_dy + right_dy)));
4338 should_move_on = (can_move_on &&
4341 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4342 y + move_dy + left_dy) ||
4343 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4344 y + move_dy + right_dy)));
4346 if (should_turn_left || should_turn_right || should_move_on)
4348 if (should_turn_left && should_turn_right && should_move_on)
4349 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4350 rnd < 2 * rnd_value / 3 ? right_dir :
4352 else if (should_turn_left && should_turn_right)
4353 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4354 else if (should_turn_left && should_move_on)
4355 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4356 else if (should_turn_right && should_move_on)
4357 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4358 else if (should_turn_left)
4359 MovDir[x][y] = left_dir;
4360 else if (should_turn_right)
4361 MovDir[x][y] = right_dir;
4362 else if (should_move_on)
4363 MovDir[x][y] = old_move_dir;
4365 else if (can_move_on && rnd > rnd_value / 8)
4366 MovDir[x][y] = old_move_dir;
4367 else if (can_turn_left && can_turn_right)
4368 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4369 else if (can_turn_left && rnd > rnd_value / 8)
4370 MovDir[x][y] = left_dir;
4371 else if (can_turn_right && rnd > rnd_value/8)
4372 MovDir[x][y] = right_dir;
4374 MovDir[x][y] = back_dir;
4376 xx = x + move_xy[MovDir[x][y]].x;
4377 yy = y + move_xy[MovDir[x][y]].y;
4379 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4380 MovDir[x][y] = old_move_dir;
4384 else if (element == EL_DRAGON)
4386 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4387 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4388 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4390 int rnd = RND(rnd_value);
4393 if (FrameCounter < 1 && x == 0 && y == 29)
4394 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4397 if (can_move_on && rnd > rnd_value / 8)
4398 MovDir[x][y] = old_move_dir;
4399 else if (can_turn_left && can_turn_right)
4400 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4401 else if (can_turn_left && rnd > rnd_value / 8)
4402 MovDir[x][y] = left_dir;
4403 else if (can_turn_right && rnd > rnd_value / 8)
4404 MovDir[x][y] = right_dir;
4406 MovDir[x][y] = back_dir;
4408 xx = x + move_xy[MovDir[x][y]].x;
4409 yy = y + move_xy[MovDir[x][y]].y;
4412 if (FrameCounter < 1 && x == 0 && y == 29)
4413 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4414 xx, yy, Feld[xx][yy],
4419 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4420 MovDir[x][y] = old_move_dir;
4422 if (!IS_FREE(xx, yy))
4423 MovDir[x][y] = old_move_dir;
4427 if (FrameCounter < 1 && x == 0 && y == 29)
4428 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4433 else if (element == EL_MOLE)
4435 boolean can_move_on =
4436 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4437 IS_AMOEBOID(Feld[move_x][move_y]) ||
4438 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4441 boolean can_turn_left =
4442 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4443 IS_AMOEBOID(Feld[left_x][left_y])));
4445 boolean can_turn_right =
4446 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4447 IS_AMOEBOID(Feld[right_x][right_y])));
4449 if (can_turn_left && can_turn_right)
4450 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4451 else if (can_turn_left)
4452 MovDir[x][y] = left_dir;
4454 MovDir[x][y] = right_dir;
4457 if (MovDir[x][y] != old_move_dir)
4460 else if (element == EL_BALLOON)
4462 MovDir[x][y] = game.balloon_dir;
4465 else if (element == EL_SPRING)
4468 if (MovDir[x][y] & MV_HORIZONTAL &&
4469 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4470 MovDir[x][y] = MV_NO_MOVING;
4472 if (MovDir[x][y] & MV_HORIZONTAL &&
4473 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4474 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4475 MovDir[x][y] = MV_NO_MOVING;
4480 else if (element == EL_ROBOT ||
4481 element == EL_SATELLITE ||
4482 element == EL_PENGUIN)
4484 int attr_x = -1, attr_y = -1;
4495 for (i = 0; i < MAX_PLAYERS; i++)
4497 struct PlayerInfo *player = &stored_player[i];
4498 int jx = player->jx, jy = player->jy;
4500 if (!player->active)
4504 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4513 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4514 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4515 game.engine_version < VERSION_IDENT(3,1,0,0)))
4517 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4524 if (element == EL_PENGUIN)
4527 static int xy[4][2] =
4535 for (i = 0; i < NUM_DIRECTIONS; i++)
4537 int ex = x + xy[i][0];
4538 int ey = y + xy[i][1];
4540 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4549 MovDir[x][y] = MV_NO_MOVING;
4551 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4552 else if (attr_x > x)
4553 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4555 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4556 else if (attr_y > y)
4557 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4559 if (element == EL_ROBOT)
4563 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4564 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4565 Moving2Blocked(x, y, &newx, &newy);
4567 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4568 MovDelay[x][y] = 8 + 8 * !RND(3);
4570 MovDelay[x][y] = 16;
4572 else if (element == EL_PENGUIN)
4578 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4580 boolean first_horiz = RND(2);
4581 int new_move_dir = MovDir[x][y];
4584 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4585 Moving2Blocked(x, y, &newx, &newy);
4587 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4591 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4592 Moving2Blocked(x, y, &newx, &newy);
4594 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4597 MovDir[x][y] = old_move_dir;
4601 else /* (element == EL_SATELLITE) */
4607 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4609 boolean first_horiz = RND(2);
4610 int new_move_dir = MovDir[x][y];
4613 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4614 Moving2Blocked(x, y, &newx, &newy);
4616 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4620 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4621 Moving2Blocked(x, y, &newx, &newy);
4623 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4626 MovDir[x][y] = old_move_dir;
4631 else if (move_pattern == MV_TURNING_LEFT ||
4632 move_pattern == MV_TURNING_RIGHT ||
4633 move_pattern == MV_TURNING_LEFT_RIGHT ||
4634 move_pattern == MV_TURNING_RIGHT_LEFT ||
4635 move_pattern == MV_TURNING_RANDOM ||
4636 move_pattern == MV_ALL_DIRECTIONS)
4638 boolean can_turn_left =
4639 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4640 boolean can_turn_right =
4641 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4643 if (move_pattern == MV_TURNING_LEFT)
4644 MovDir[x][y] = left_dir;
4645 else if (move_pattern == MV_TURNING_RIGHT)
4646 MovDir[x][y] = right_dir;
4647 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4648 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4649 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4650 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4651 else if (move_pattern == MV_TURNING_RANDOM)
4652 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4653 can_turn_right && !can_turn_left ? right_dir :
4654 RND(2) ? left_dir : right_dir);
4655 else if (can_turn_left && can_turn_right)
4656 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4657 else if (can_turn_left)
4658 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4659 else if (can_turn_right)
4660 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4662 MovDir[x][y] = back_dir;
4664 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4666 else if (move_pattern == MV_HORIZONTAL ||
4667 move_pattern == MV_VERTICAL)
4669 if (move_pattern & old_move_dir)
4670 MovDir[x][y] = back_dir;
4671 else if (move_pattern == MV_HORIZONTAL)
4672 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4673 else if (move_pattern == MV_VERTICAL)
4674 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4676 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4678 else if (move_pattern & MV_ANY_DIRECTION)
4680 MovDir[x][y] = move_pattern;
4681 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4683 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4685 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4686 MovDir[x][y] = left_dir;
4687 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4688 MovDir[x][y] = right_dir;
4690 if (MovDir[x][y] != old_move_dir)
4691 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4693 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4695 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4696 MovDir[x][y] = right_dir;
4697 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4698 MovDir[x][y] = left_dir;
4700 if (MovDir[x][y] != old_move_dir)
4701 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4703 else if (move_pattern == MV_TOWARDS_PLAYER ||
4704 move_pattern == MV_AWAY_FROM_PLAYER)
4706 int attr_x = -1, attr_y = -1;
4708 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4719 for (i = 0; i < MAX_PLAYERS; i++)
4721 struct PlayerInfo *player = &stored_player[i];
4722 int jx = player->jx, jy = player->jy;
4724 if (!player->active)
4728 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4736 MovDir[x][y] = MV_NO_MOVING;
4738 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4739 else if (attr_x > x)
4740 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4742 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4743 else if (attr_y > y)
4744 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4746 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4748 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4750 boolean first_horiz = RND(2);
4751 int new_move_dir = MovDir[x][y];
4754 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4755 Moving2Blocked(x, y, &newx, &newy);
4757 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4761 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4762 Moving2Blocked(x, y, &newx, &newy);
4764 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4767 MovDir[x][y] = old_move_dir;
4770 else if (move_pattern == MV_WHEN_PUSHED ||
4771 move_pattern == MV_WHEN_DROPPED)
4773 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4774 MovDir[x][y] = MV_NO_MOVING;
4778 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4780 static int test_xy[7][2] =
4790 static int test_dir[7] =
4800 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4801 int move_preference = -1000000; /* start with very low preference */
4802 int new_move_dir = MV_NO_MOVING;
4803 int start_test = RND(4);
4806 for (i = 0; i < NUM_DIRECTIONS; i++)
4808 int move_dir = test_dir[start_test + i];
4809 int move_dir_preference;
4811 xx = x + test_xy[start_test + i][0];
4812 yy = y + test_xy[start_test + i][1];
4814 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4815 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4817 new_move_dir = move_dir;
4822 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4825 move_dir_preference = -1 * RunnerVisit[xx][yy];
4826 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4827 move_dir_preference = PlayerVisit[xx][yy];
4829 if (move_dir_preference > move_preference)
4831 /* prefer field that has not been visited for the longest time */
4832 move_preference = move_dir_preference;
4833 new_move_dir = move_dir;
4835 else if (move_dir_preference == move_preference &&
4836 move_dir == old_move_dir)
4838 /* prefer last direction when all directions are preferred equally */
4839 move_preference = move_dir_preference;
4840 new_move_dir = move_dir;
4844 MovDir[x][y] = new_move_dir;
4845 if (old_move_dir != new_move_dir)
4850 static void TurnRound(int x, int y)
4852 int direction = MovDir[x][y];
4855 GfxDir[x][y] = MovDir[x][y];
4861 GfxDir[x][y] = MovDir[x][y];
4864 if (direction != MovDir[x][y])
4869 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4872 GfxAction[x][y] = ACTION_WAITING;
4876 static boolean JustBeingPushed(int x, int y)
4880 for (i = 0; i < MAX_PLAYERS; i++)
4882 struct PlayerInfo *player = &stored_player[i];
4884 if (player->active && player->is_pushing && player->MovPos)
4886 int next_jx = player->jx + (player->jx - player->last_jx);
4887 int next_jy = player->jy + (player->jy - player->last_jy);
4889 if (x == next_jx && y == next_jy)
4897 void StartMoving(int x, int y)
4900 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4902 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4903 int element = Feld[x][y];
4909 if (MovDelay[x][y] == 0)
4910 GfxAction[x][y] = ACTION_DEFAULT;
4912 /* !!! this should be handled more generic (not only for mole) !!! */
4913 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4914 GfxAction[x][y] = ACTION_DEFAULT;
4917 if (CAN_FALL(element) && y < lev_fieldy - 1)
4919 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4920 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4921 if (JustBeingPushed(x, y))
4924 if (element == EL_QUICKSAND_FULL)
4926 if (IS_FREE(x, y + 1))
4928 InitMovingField(x, y, MV_DOWN);
4929 started_moving = TRUE;
4931 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4932 Store[x][y] = EL_ROCK;
4934 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4936 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4939 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4941 if (!MovDelay[x][y])
4942 MovDelay[x][y] = TILEY + 1;
4951 Feld[x][y] = EL_QUICKSAND_EMPTY;
4952 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4953 Store[x][y + 1] = Store[x][y];
4956 PlayLevelSoundAction(x, y, ACTION_FILLING);
4958 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4962 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4963 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4965 InitMovingField(x, y, MV_DOWN);
4966 started_moving = TRUE;
4968 Feld[x][y] = EL_QUICKSAND_FILLING;
4969 Store[x][y] = element;
4971 PlayLevelSoundAction(x, y, ACTION_FILLING);
4973 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4976 else if (element == EL_MAGIC_WALL_FULL)
4978 if (IS_FREE(x, y + 1))
4980 InitMovingField(x, y, MV_DOWN);
4981 started_moving = TRUE;
4983 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4984 Store[x][y] = EL_CHANGED(Store[x][y]);
4986 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4988 if (!MovDelay[x][y])
4989 MovDelay[x][y] = TILEY/4 + 1;
4998 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4999 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5000 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5004 else if (element == EL_BD_MAGIC_WALL_FULL)
5006 if (IS_FREE(x, y + 1))
5008 InitMovingField(x, y, MV_DOWN);
5009 started_moving = TRUE;
5011 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5012 Store[x][y] = EL_CHANGED2(Store[x][y]);
5014 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5016 if (!MovDelay[x][y])
5017 MovDelay[x][y] = TILEY/4 + 1;
5026 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5027 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5028 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5032 else if (CAN_PASS_MAGIC_WALL(element) &&
5033 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5034 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5036 InitMovingField(x, y, MV_DOWN);
5037 started_moving = TRUE;
5040 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5041 EL_BD_MAGIC_WALL_FILLING);
5042 Store[x][y] = element;
5045 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5047 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5050 SplashAcid(x, y + 1);
5052 InitMovingField(x, y, MV_DOWN);
5053 started_moving = TRUE;
5055 Store[x][y] = EL_ACID;
5057 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5058 GfxAction[x][y + 1] = ACTION_ACTIVE;
5062 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5063 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5065 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5066 CAN_SMASH(element) && WasJustFalling[x][y] &&
5067 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5069 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5070 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5071 (Feld[x][y + 1] == EL_BLOCKED)))
5075 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5076 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5077 WasJustMoving[x][y] && !Pushed[x][y + 1])
5079 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5080 WasJustMoving[x][y])
5085 /* this is needed for a special case not covered by calling "Impact()"
5086 from "ContinueMoving()": if an element moves to a tile directly below
5087 another element which was just falling on that tile (which was empty
5088 in the previous frame), the falling element above would just stop
5089 instead of smashing the element below (in previous version, the above
5090 element was just checked for "moving" instead of "falling", resulting
5091 in incorrect smashes caused by horizontal movement of the above
5092 element; also, the case of the player being the element to smash was
5093 simply not covered here... :-/ ) */
5096 WasJustMoving[x][y] = 0;
5097 WasJustFalling[x][y] = 0;
5102 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5104 if (MovDir[x][y] == MV_NO_MOVING)
5106 InitMovingField(x, y, MV_DOWN);
5107 started_moving = TRUE;
5110 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5112 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5113 MovDir[x][y] = MV_DOWN;
5115 InitMovingField(x, y, MV_DOWN);
5116 started_moving = TRUE;
5118 else if (element == EL_AMOEBA_DROP)
5120 Feld[x][y] = EL_AMOEBA_GROWING;
5121 Store[x][y] = EL_AMOEBA_WET;
5123 /* Store[x][y + 1] must be zero, because:
5124 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5127 #if OLD_GAME_BEHAVIOUR
5128 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5130 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5131 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5132 element != EL_DX_SUPABOMB)
5135 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5136 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5137 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5138 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5141 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5142 (IS_FREE(x - 1, y + 1) ||
5143 Feld[x - 1][y + 1] == EL_ACID));
5144 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5145 (IS_FREE(x + 1, y + 1) ||
5146 Feld[x + 1][y + 1] == EL_ACID));
5147 boolean can_fall_any = (can_fall_left || can_fall_right);
5148 boolean can_fall_both = (can_fall_left && can_fall_right);
5150 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5152 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5154 if (slippery_type == SLIPPERY_ONLY_LEFT)
5155 can_fall_right = FALSE;
5156 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5157 can_fall_left = FALSE;
5158 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5159 can_fall_right = FALSE;
5160 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5161 can_fall_left = FALSE;
5163 can_fall_any = (can_fall_left || can_fall_right);
5164 can_fall_both = (can_fall_left && can_fall_right);
5169 if (can_fall_both &&
5170 (game.emulation != EMU_BOULDERDASH &&
5171 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5172 can_fall_left = !(can_fall_right = RND(2));
5174 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5175 started_moving = TRUE;
5179 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5181 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5184 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5185 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5186 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5187 int belt_dir = game.belt_dir[belt_nr];
5189 if ((belt_dir == MV_LEFT && left_is_free) ||
5190 (belt_dir == MV_RIGHT && right_is_free))
5193 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5196 InitMovingField(x, y, belt_dir);
5197 started_moving = TRUE;
5200 Pushed[x][y] = TRUE;
5201 Pushed[nextx][y] = TRUE;
5204 GfxAction[x][y] = ACTION_DEFAULT;
5208 MovDir[x][y] = 0; /* if element was moving, stop it */
5213 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5214 if (CAN_MOVE(element) && !started_moving)
5216 int move_pattern = element_info[element].move_pattern;
5219 Moving2Blocked(x, y, &newx, &newy);
5222 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5225 if ((element == EL_SATELLITE ||
5226 element == EL_BALLOON ||
5227 element == EL_SPRING)
5228 && JustBeingPushed(x, y))
5235 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5236 CheckCollision[x][y] && IN_LEV_FIELD_AND_NOT_FREE(newx, newy))
5238 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5239 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5240 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5244 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5245 element, element_info[element].token_name,
5246 WasJustMoving[x][y],
5247 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5248 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5249 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5250 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5254 WasJustMoving[x][y] = 0;
5257 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5260 if (Feld[x][y] != element) /* element has changed */
5262 element = Feld[x][y];
5263 move_pattern = element_info[element].move_pattern;
5265 if (!CAN_MOVE(element))
5269 if (Feld[x][y] != element) /* element has changed */
5277 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5278 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5280 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5282 Moving2Blocked(x, y, &newx, &newy);
5283 if (Feld[newx][newy] == EL_BLOCKED)
5284 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5290 if (FrameCounter < 1 && x == 0 && y == 29)
5291 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5294 if (!MovDelay[x][y]) /* start new movement phase */
5296 /* all objects that can change their move direction after each step
5297 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5299 if (element != EL_YAMYAM &&
5300 element != EL_DARK_YAMYAM &&
5301 element != EL_PACMAN &&
5302 !(move_pattern & MV_ANY_DIRECTION) &&
5303 move_pattern != MV_TURNING_LEFT &&
5304 move_pattern != MV_TURNING_RIGHT &&
5305 move_pattern != MV_TURNING_LEFT_RIGHT &&
5306 move_pattern != MV_TURNING_RIGHT_LEFT &&
5307 move_pattern != MV_TURNING_RANDOM)
5312 if (FrameCounter < 1 && x == 0 && y == 29)
5313 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5316 if (MovDelay[x][y] && (element == EL_BUG ||
5317 element == EL_SPACESHIP ||
5318 element == EL_SP_SNIKSNAK ||
5319 element == EL_SP_ELECTRON ||
5320 element == EL_MOLE))
5321 DrawLevelField(x, y);
5325 if (MovDelay[x][y]) /* wait some time before next movement */
5330 if (element == EL_YAMYAM)
5333 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5334 DrawLevelElementAnimation(x, y, element);
5338 if (MovDelay[x][y]) /* element still has to wait some time */
5341 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5342 ResetGfxAnimation(x, y);
5346 if (GfxAction[x][y] != ACTION_WAITING)
5347 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5349 GfxAction[x][y] = ACTION_WAITING;
5353 if (element == EL_ROBOT ||
5355 element == EL_PACMAN ||
5357 element == EL_YAMYAM ||
5358 element == EL_DARK_YAMYAM)
5361 DrawLevelElementAnimation(x, y, element);
5363 DrawLevelElementAnimationIfNeeded(x, y, element);
5365 PlayLevelSoundAction(x, y, ACTION_WAITING);
5367 else if (element == EL_SP_ELECTRON)
5368 DrawLevelElementAnimationIfNeeded(x, y, element);
5369 else if (element == EL_DRAGON)
5372 int dir = MovDir[x][y];
5373 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5374 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5375 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5376 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5377 dir == MV_UP ? IMG_FLAMES_1_UP :
5378 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5379 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5382 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5385 GfxAction[x][y] = ACTION_ATTACKING;
5387 if (IS_PLAYER(x, y))
5388 DrawPlayerField(x, y);
5390 DrawLevelField(x, y);
5392 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5394 for (i = 1; i <= 3; i++)
5396 int xx = x + i * dx;
5397 int yy = y + i * dy;
5398 int sx = SCREENX(xx);
5399 int sy = SCREENY(yy);
5400 int flame_graphic = graphic + (i - 1);
5402 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5407 int flamed = MovingOrBlocked2Element(xx, yy);
5411 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5413 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5414 RemoveMovingField(xx, yy);
5416 RemoveField(xx, yy);
5418 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5421 RemoveMovingField(xx, yy);
5425 if (ChangeDelay[xx][yy])
5426 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5427 Feld[xx][yy] == EL_BLOCKED));
5431 ChangeDelay[xx][yy] = 0;
5433 Feld[xx][yy] = EL_FLAMES;
5434 if (IN_SCR_FIELD(sx, sy))
5436 DrawLevelFieldCrumbledSand(xx, yy);
5437 DrawGraphic(sx, sy, flame_graphic, frame);
5442 if (Feld[xx][yy] == EL_FLAMES)
5443 Feld[xx][yy] = EL_EMPTY;
5444 DrawLevelField(xx, yy);
5449 if (MovDelay[x][y]) /* element still has to wait some time */
5451 PlayLevelSoundAction(x, y, ACTION_WAITING);
5457 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5458 for all other elements GfxAction will be set by InitMovingField() */
5459 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5460 GfxAction[x][y] = ACTION_MOVING;
5464 /* now make next step */
5466 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5468 if (DONT_COLLIDE_WITH(element) &&
5469 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5470 !PLAYER_ENEMY_PROTECTED(newx, newy))
5473 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5477 /* player killed by element which is deadly when colliding with */
5479 KillHero(PLAYERINFO(newx, newy));
5486 else if (CAN_MOVE_INTO_ACID(element) &&
5487 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5488 (MovDir[x][y] == MV_DOWN ||
5489 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5491 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5492 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5496 else if ((element == EL_PENGUIN ||
5497 element == EL_ROBOT ||
5498 element == EL_SATELLITE ||
5499 element == EL_BALLOON ||
5500 IS_CUSTOM_ELEMENT(element)) &&
5501 IN_LEV_FIELD(newx, newy) &&
5502 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5505 SplashAcid(newx, newy);
5506 Store[x][y] = EL_ACID;
5508 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5510 if (Feld[newx][newy] == EL_EXIT_OPEN)
5514 DrawLevelField(x, y);
5516 Feld[x][y] = EL_EMPTY;
5517 DrawLevelField(x, y);
5520 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5521 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5522 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5524 local_player->friends_still_needed--;
5525 if (!local_player->friends_still_needed &&
5526 !local_player->GameOver && AllPlayersGone)
5527 local_player->LevelSolved = local_player->GameOver = TRUE;
5531 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5533 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5534 DrawLevelField(newx, newy);
5536 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5538 else if (!IS_FREE(newx, newy))
5540 GfxAction[x][y] = ACTION_WAITING;
5542 if (IS_PLAYER(x, y))
5543 DrawPlayerField(x, y);
5545 DrawLevelField(x, y);
5550 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5552 if (IS_FOOD_PIG(Feld[newx][newy]))
5554 if (IS_MOVING(newx, newy))
5555 RemoveMovingField(newx, newy);
5558 Feld[newx][newy] = EL_EMPTY;
5559 DrawLevelField(newx, newy);
5562 PlayLevelSound(x, y, SND_PIG_DIGGING);
5564 else if (!IS_FREE(newx, newy))
5566 if (IS_PLAYER(x, y))
5567 DrawPlayerField(x, y);
5569 DrawLevelField(x, y);
5578 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5581 else if (IS_CUSTOM_ELEMENT(element) &&
5582 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5586 !IS_FREE(newx, newy)
5591 int new_element = Feld[newx][newy];
5594 printf("::: '%s' digs '%s' [%d]\n",
5595 element_info[element].token_name,
5596 element_info[Feld[newx][newy]].token_name,
5597 StorePlayer[newx][newy]);
5600 if (!IS_FREE(newx, newy))
5602 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5603 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5606 /* no element can dig solid indestructible elements */
5607 if (IS_INDESTRUCTIBLE(new_element) &&
5608 !IS_DIGGABLE(new_element) &&
5609 !IS_COLLECTIBLE(new_element))
5612 if (AmoebaNr[newx][newy] &&
5613 (new_element == EL_AMOEBA_FULL ||
5614 new_element == EL_BD_AMOEBA ||
5615 new_element == EL_AMOEBA_GROWING))
5617 AmoebaCnt[AmoebaNr[newx][newy]]--;
5618 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5621 if (IS_MOVING(newx, newy))
5622 RemoveMovingField(newx, newy);
5625 RemoveField(newx, newy);
5626 DrawLevelField(newx, newy);
5629 /* if digged element was about to explode, prevent the explosion */
5630 ExplodeField[newx][newy] = EX_TYPE_NONE;
5632 PlayLevelSoundAction(x, y, action);
5637 Store[newx][newy] = EL_EMPTY;
5638 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5639 Store[newx][newy] = element_info[element].move_leave_element;
5641 Store[newx][newy] = EL_EMPTY;
5642 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5643 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5644 Store[newx][newy] = element_info[element].move_leave_element;
5647 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5648 element_info[element].can_leave_element = TRUE;
5651 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5653 RunnerVisit[x][y] = FrameCounter;
5654 PlayerVisit[x][y] /= 8; /* expire player visit path */
5660 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5662 if (!IS_FREE(newx, newy))
5664 if (IS_PLAYER(x, y))
5665 DrawPlayerField(x, y);
5667 DrawLevelField(x, y);
5673 boolean wanna_flame = !RND(10);
5674 int dx = newx - x, dy = newy - y;
5675 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5676 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5677 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5678 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5679 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5680 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5683 IS_CLASSIC_ENEMY(element1) ||
5684 IS_CLASSIC_ENEMY(element2)) &&
5685 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5686 element1 != EL_FLAMES && element2 != EL_FLAMES)
5689 ResetGfxAnimation(x, y);
5690 GfxAction[x][y] = ACTION_ATTACKING;
5693 if (IS_PLAYER(x, y))
5694 DrawPlayerField(x, y);
5696 DrawLevelField(x, y);
5698 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5700 MovDelay[x][y] = 50;
5704 RemoveField(newx, newy);
5706 Feld[newx][newy] = EL_FLAMES;
5707 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5710 RemoveField(newx1, newy1);
5712 Feld[newx1][newy1] = EL_FLAMES;
5714 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5717 RemoveField(newx2, newy2);
5719 Feld[newx2][newy2] = EL_FLAMES;
5726 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5727 Feld[newx][newy] == EL_DIAMOND)
5729 if (IS_MOVING(newx, newy))
5730 RemoveMovingField(newx, newy);
5733 Feld[newx][newy] = EL_EMPTY;
5734 DrawLevelField(newx, newy);
5737 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5739 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5740 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5742 if (AmoebaNr[newx][newy])
5744 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5745 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5746 Feld[newx][newy] == EL_BD_AMOEBA)
5747 AmoebaCnt[AmoebaNr[newx][newy]]--;
5752 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5754 if (IS_MOVING(newx, newy))
5757 RemoveMovingField(newx, newy);
5761 Feld[newx][newy] = EL_EMPTY;
5762 DrawLevelField(newx, newy);
5765 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5767 else if ((element == EL_PACMAN || element == EL_MOLE)
5768 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5770 if (AmoebaNr[newx][newy])
5772 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5773 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5774 Feld[newx][newy] == EL_BD_AMOEBA)
5775 AmoebaCnt[AmoebaNr[newx][newy]]--;
5778 if (element == EL_MOLE)
5780 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5781 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5783 ResetGfxAnimation(x, y);
5784 GfxAction[x][y] = ACTION_DIGGING;
5785 DrawLevelField(x, y);
5787 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5789 return; /* wait for shrinking amoeba */
5791 else /* element == EL_PACMAN */
5793 Feld[newx][newy] = EL_EMPTY;
5794 DrawLevelField(newx, newy);
5795 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5798 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5799 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5800 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5802 /* wait for shrinking amoeba to completely disappear */
5805 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5807 /* object was running against a wall */
5812 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5813 DrawLevelElementAnimation(x, y, element);
5815 if (element == EL_BUG ||
5816 element == EL_SPACESHIP ||
5817 element == EL_SP_SNIKSNAK)
5818 DrawLevelField(x, y);
5819 else if (element == EL_MOLE)
5820 DrawLevelField(x, y);
5821 else if (element == EL_BD_BUTTERFLY ||
5822 element == EL_BD_FIREFLY)
5823 DrawLevelElementAnimationIfNeeded(x, y, element);
5824 else if (element == EL_SATELLITE)
5825 DrawLevelElementAnimationIfNeeded(x, y, element);
5826 else if (element == EL_SP_ELECTRON)
5827 DrawLevelElementAnimationIfNeeded(x, y, element);
5830 if (DONT_TOUCH(element))
5831 TestIfBadThingTouchesHero(x, y);
5834 PlayLevelSoundAction(x, y, ACTION_WAITING);
5840 InitMovingField(x, y, MovDir[x][y]);
5842 PlayLevelSoundAction(x, y, ACTION_MOVING);
5846 ContinueMoving(x, y);
5849 void ContinueMoving(int x, int y)
5851 int element = Feld[x][y];
5852 int stored = Store[x][y];
5853 struct ElementInfo *ei = &element_info[element];
5854 int direction = MovDir[x][y];
5855 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5856 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5857 int newx = x + dx, newy = y + dy;
5859 int nextx = newx + dx, nexty = newy + dy;
5862 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5863 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5865 boolean pushed_by_player = Pushed[x][y];
5868 MovPos[x][y] += getElementMoveStepsize(x, y);
5871 if (pushed_by_player && IS_PLAYER(x, y))
5873 /* special case: moving object pushed by player */
5874 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5877 if (pushed_by_player) /* special case: moving object pushed by player */
5878 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5881 if (ABS(MovPos[x][y]) < TILEX)
5883 DrawLevelField(x, y);
5885 return; /* element is still moving */
5888 /* element reached destination field */
5890 Feld[x][y] = EL_EMPTY;
5891 Feld[newx][newy] = element;
5892 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5894 if (element == EL_MOLE)
5896 Feld[x][y] = EL_SAND;
5898 DrawLevelFieldCrumbledSandNeighbours(x, y);
5900 else if (element == EL_QUICKSAND_FILLING)
5902 element = Feld[newx][newy] = get_next_element(element);
5903 Store[newx][newy] = Store[x][y];
5905 else if (element == EL_QUICKSAND_EMPTYING)
5907 Feld[x][y] = get_next_element(element);
5908 element = Feld[newx][newy] = Store[x][y];
5910 else if (element == EL_MAGIC_WALL_FILLING)
5912 element = Feld[newx][newy] = get_next_element(element);
5913 if (!game.magic_wall_active)
5914 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5915 Store[newx][newy] = Store[x][y];
5917 else if (element == EL_MAGIC_WALL_EMPTYING)
5919 Feld[x][y] = get_next_element(element);
5920 if (!game.magic_wall_active)
5921 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5922 element = Feld[newx][newy] = Store[x][y];
5924 else if (element == EL_BD_MAGIC_WALL_FILLING)
5926 element = Feld[newx][newy] = get_next_element(element);
5927 if (!game.magic_wall_active)
5928 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5929 Store[newx][newy] = Store[x][y];
5931 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5933 Feld[x][y] = get_next_element(element);
5934 if (!game.magic_wall_active)
5935 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5936 element = Feld[newx][newy] = Store[x][y];
5938 else if (element == EL_AMOEBA_DROPPING)
5940 Feld[x][y] = get_next_element(element);
5941 element = Feld[newx][newy] = Store[x][y];
5943 else if (element == EL_SOKOBAN_OBJECT)
5946 Feld[x][y] = Back[x][y];
5948 if (Back[newx][newy])
5949 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5951 Back[x][y] = Back[newx][newy] = 0;
5953 else if (Store[x][y] == EL_ACID)
5955 element = Feld[newx][newy] = EL_ACID;
5958 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5959 ei->move_leave_element != EL_EMPTY &&
5960 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5961 Store[x][y] != EL_EMPTY))
5963 /* some elements can leave other elements behind after moving */
5965 Feld[x][y] = ei->move_leave_element;
5966 InitField(x, y, FALSE);
5968 if (GFX_CRUMBLED(Feld[x][y]))
5969 DrawLevelFieldCrumbledSandNeighbours(x, y);
5973 Store[x][y] = EL_EMPTY;
5974 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5975 MovDelay[newx][newy] = 0;
5977 if (CAN_CHANGE(element))
5979 /* copy element change control values to new field */
5980 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5981 ChangePage[newx][newy] = ChangePage[x][y];
5982 Changed[newx][newy] = Changed[x][y];
5983 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5986 ChangeDelay[x][y] = 0;
5987 ChangePage[x][y] = -1;
5988 Changed[x][y] = CE_BITMASK_DEFAULT;
5989 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5991 /* copy animation control values to new field */
5992 GfxFrame[newx][newy] = GfxFrame[x][y];
5993 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5994 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5995 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5997 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5999 ResetGfxAnimation(x, y); /* reset animation values for old field */
6002 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6003 ei->move_leave_element != EL_EMPTY &&
6004 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6005 stored != EL_EMPTY))
6007 /* some elements can leave other elements behind after moving */
6009 Feld[x][y] = ei->move_leave_element;
6010 InitField(x, y, FALSE);
6012 if (GFX_CRUMBLED(Feld[x][y]))
6013 DrawLevelFieldCrumbledSandNeighbours(x, y);
6018 /* some elements can leave other elements behind after moving */
6019 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6020 ei->move_leave_element != EL_EMPTY &&
6021 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6022 ei->can_leave_element_last))
6024 Feld[x][y] = ei->move_leave_element;
6025 InitField(x, y, FALSE);
6027 if (GFX_CRUMBLED(Feld[x][y]))
6028 DrawLevelFieldCrumbledSandNeighbours(x, y);
6031 ei->can_leave_element_last = ei->can_leave_element;
6032 ei->can_leave_element = FALSE;
6036 /* 2.1.1 (does not work correctly for spring) */
6037 if (!CAN_MOVE(element))
6038 MovDir[newx][newy] = 0;
6042 /* (does not work for falling objects that slide horizontally) */
6043 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6044 MovDir[newx][newy] = 0;
6047 if (!CAN_MOVE(element) ||
6048 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6049 MovDir[newx][newy] = 0;
6053 if (!CAN_MOVE(element) ||
6054 (CAN_FALL(element) && direction == MV_DOWN))
6055 GfxDir[x][y] = MovDir[newx][newy] = 0;
6057 if (!CAN_MOVE(element) ||
6058 (CAN_FALL(element) && direction == MV_DOWN &&
6059 (element == EL_SPRING ||
6060 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6061 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6062 GfxDir[x][y] = MovDir[newx][newy] = 0;
6068 DrawLevelField(x, y);
6069 DrawLevelField(newx, newy);
6071 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6073 /* prevent pushed element from moving on in pushed direction */
6074 if (pushed_by_player && CAN_MOVE(element) &&
6075 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6076 !(element_info[element].move_pattern & direction))
6077 TurnRound(newx, newy);
6080 /* prevent elements on conveyor belt from moving on in last direction */
6081 if (pushed_by_conveyor && CAN_FALL(element) &&
6082 direction & MV_HORIZONTAL)
6083 MovDir[newx][newy] = 0;
6086 if (!pushed_by_player)
6088 int nextx = newx + dx, nexty = newy + dy;
6089 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6091 WasJustMoving[newx][newy] = 3;
6093 if (CAN_FALL(element) && direction == MV_DOWN)
6094 WasJustFalling[newx][newy] = 3;
6096 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6097 CheckCollision[newx][newy] = 2;
6100 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6102 TestIfBadThingTouchesHero(newx, newy);
6103 TestIfBadThingTouchesFriend(newx, newy);
6105 if (!IS_CUSTOM_ELEMENT(element))
6106 TestIfBadThingTouchesOtherBadThing(newx, newy);
6108 else if (element == EL_PENGUIN)
6109 TestIfFriendTouchesBadThing(newx, newy);
6111 if (CAN_FALL(element) && direction == MV_DOWN &&
6112 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6116 if (pushed_by_player)
6118 static int trigger_sides[4] =
6120 CH_SIDE_RIGHT, /* moving left */
6121 CH_SIDE_LEFT, /* moving right */
6122 CH_SIDE_BOTTOM, /* moving up */
6123 CH_SIDE_TOP, /* moving down */
6125 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6126 struct PlayerInfo *player = PLAYERINFO(x, y);
6128 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6129 player->index_bit, dig_side);
6130 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6131 player->index_bit, dig_side);
6136 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6140 if (ChangePage[newx][newy] != -1) /* delayed change */
6141 ChangeElement(newx, newy, ChangePage[newx][newy]);
6146 TestIfElementHitsCustomElement(newx, newy, direction);
6150 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6152 int hitting_element = Feld[newx][newy];
6154 /* !!! fix side (direction) orientation here and elsewhere !!! */
6155 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6159 if (IN_LEV_FIELD(nextx, nexty))
6161 int opposite_direction = MV_DIR_OPPOSITE(direction);
6162 int hitting_side = direction;
6163 int touched_side = opposite_direction;
6164 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6165 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6166 MovDir[nextx][nexty] != direction ||
6167 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6173 CheckElementChangeBySide(nextx, nexty, touched_element,
6174 CE_HIT_BY_SOMETHING, opposite_direction);
6176 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6177 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6179 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6181 struct ElementChangeInfo *change =
6182 &element_info[hitting_element].change_page[i];
6184 if (change->can_change &&
6185 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6186 change->trigger_side & touched_side &&
6187 change->trigger_element == touched_element)
6189 CheckElementChangeByPage(newx, newy, hitting_element,
6190 touched_element, CE_OTHER_IS_HITTING,i);
6196 if (IS_CUSTOM_ELEMENT(touched_element) &&
6197 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6199 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6201 struct ElementChangeInfo *change =
6202 &element_info[touched_element].change_page[i];
6204 if (change->can_change &&
6205 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6206 change->trigger_side & hitting_side &&
6207 change->trigger_element == hitting_element)
6209 CheckElementChangeByPage(nextx, nexty, touched_element,
6210 hitting_element, CE_OTHER_GETS_HIT, i);
6221 TestIfPlayerTouchesCustomElement(newx, newy);
6222 TestIfElementTouchesCustomElement(newx, newy);
6225 int AmoebeNachbarNr(int ax, int ay)
6228 int element = Feld[ax][ay];
6230 static int xy[4][2] =
6238 for (i = 0; i < NUM_DIRECTIONS; i++)
6240 int x = ax + xy[i][0];
6241 int y = ay + xy[i][1];
6243 if (!IN_LEV_FIELD(x, y))
6246 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6247 group_nr = AmoebaNr[x][y];
6253 void AmoebenVereinigen(int ax, int ay)
6255 int i, x, y, xx, yy;
6256 int new_group_nr = AmoebaNr[ax][ay];
6257 static int xy[4][2] =
6265 if (new_group_nr == 0)
6268 for (i = 0; i < NUM_DIRECTIONS; i++)
6273 if (!IN_LEV_FIELD(x, y))
6276 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6277 Feld[x][y] == EL_BD_AMOEBA ||
6278 Feld[x][y] == EL_AMOEBA_DEAD) &&
6279 AmoebaNr[x][y] != new_group_nr)
6281 int old_group_nr = AmoebaNr[x][y];
6283 if (old_group_nr == 0)
6286 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6287 AmoebaCnt[old_group_nr] = 0;
6288 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6289 AmoebaCnt2[old_group_nr] = 0;
6291 for (yy = 0; yy < lev_fieldy; yy++)
6293 for (xx = 0; xx < lev_fieldx; xx++)
6295 if (AmoebaNr[xx][yy] == old_group_nr)
6296 AmoebaNr[xx][yy] = new_group_nr;
6303 void AmoebeUmwandeln(int ax, int ay)
6307 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6309 int group_nr = AmoebaNr[ax][ay];
6314 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6315 printf("AmoebeUmwandeln(): This should never happen!\n");
6320 for (y = 0; y < lev_fieldy; y++)
6322 for (x = 0; x < lev_fieldx; x++)
6324 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6327 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6331 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6332 SND_AMOEBA_TURNING_TO_GEM :
6333 SND_AMOEBA_TURNING_TO_ROCK));
6338 static int xy[4][2] =
6346 for (i = 0; i < NUM_DIRECTIONS; i++)
6351 if (!IN_LEV_FIELD(x, y))
6354 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6356 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6357 SND_AMOEBA_TURNING_TO_GEM :
6358 SND_AMOEBA_TURNING_TO_ROCK));
6365 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6368 int group_nr = AmoebaNr[ax][ay];
6369 boolean done = FALSE;
6374 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6375 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6380 for (y = 0; y < lev_fieldy; y++)
6382 for (x = 0; x < lev_fieldx; x++)
6384 if (AmoebaNr[x][y] == group_nr &&
6385 (Feld[x][y] == EL_AMOEBA_DEAD ||
6386 Feld[x][y] == EL_BD_AMOEBA ||
6387 Feld[x][y] == EL_AMOEBA_GROWING))
6390 Feld[x][y] = new_element;
6391 InitField(x, y, FALSE);
6392 DrawLevelField(x, y);
6399 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6400 SND_BD_AMOEBA_TURNING_TO_ROCK :
6401 SND_BD_AMOEBA_TURNING_TO_GEM));
6404 void AmoebeWaechst(int x, int y)
6406 static unsigned long sound_delay = 0;
6407 static unsigned long sound_delay_value = 0;
6409 if (!MovDelay[x][y]) /* start new growing cycle */
6413 if (DelayReached(&sound_delay, sound_delay_value))
6416 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6418 if (Store[x][y] == EL_BD_AMOEBA)
6419 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6421 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6423 sound_delay_value = 30;
6427 if (MovDelay[x][y]) /* wait some time before growing bigger */
6430 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6432 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6433 6 - MovDelay[x][y]);
6435 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6438 if (!MovDelay[x][y])
6440 Feld[x][y] = Store[x][y];
6442 DrawLevelField(x, y);
6447 void AmoebaDisappearing(int x, int y)
6449 static unsigned long sound_delay = 0;
6450 static unsigned long sound_delay_value = 0;
6452 if (!MovDelay[x][y]) /* start new shrinking cycle */
6456 if (DelayReached(&sound_delay, sound_delay_value))
6457 sound_delay_value = 30;
6460 if (MovDelay[x][y]) /* wait some time before shrinking */
6463 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6465 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6466 6 - MovDelay[x][y]);
6468 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6471 if (!MovDelay[x][y])
6473 Feld[x][y] = EL_EMPTY;
6474 DrawLevelField(x, y);
6476 /* don't let mole enter this field in this cycle;
6477 (give priority to objects falling to this field from above) */
6483 void AmoebeAbleger(int ax, int ay)
6486 int element = Feld[ax][ay];
6487 int graphic = el2img(element);
6488 int newax = ax, neway = ay;
6489 static int xy[4][2] =
6497 if (!level.amoeba_speed)
6499 Feld[ax][ay] = EL_AMOEBA_DEAD;
6500 DrawLevelField(ax, ay);
6504 if (IS_ANIMATED(graphic))
6505 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6507 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6508 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6510 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6513 if (MovDelay[ax][ay])
6517 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6520 int x = ax + xy[start][0];
6521 int y = ay + xy[start][1];
6523 if (!IN_LEV_FIELD(x, y))
6527 if (IS_FREE(x, y) ||
6528 CAN_GROW_INTO(Feld[x][y]) ||
6529 Feld[x][y] == EL_QUICKSAND_EMPTY)
6535 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6536 if (IS_FREE(x, y) ||
6537 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6544 if (newax == ax && neway == ay)
6547 else /* normal or "filled" (BD style) amoeba */
6550 boolean waiting_for_player = FALSE;
6552 for (i = 0; i < NUM_DIRECTIONS; i++)
6554 int j = (start + i) % 4;
6555 int x = ax + xy[j][0];
6556 int y = ay + xy[j][1];
6558 if (!IN_LEV_FIELD(x, y))
6562 if (IS_FREE(x, y) ||
6563 CAN_GROW_INTO(Feld[x][y]) ||
6564 Feld[x][y] == EL_QUICKSAND_EMPTY)
6571 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6572 if (IS_FREE(x, y) ||
6573 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6580 else if (IS_PLAYER(x, y))
6581 waiting_for_player = TRUE;
6584 if (newax == ax && neway == ay) /* amoeba cannot grow */
6587 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6589 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6592 Feld[ax][ay] = EL_AMOEBA_DEAD;
6593 DrawLevelField(ax, ay);
6594 AmoebaCnt[AmoebaNr[ax][ay]]--;
6596 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6598 if (element == EL_AMOEBA_FULL)
6599 AmoebeUmwandeln(ax, ay);
6600 else if (element == EL_BD_AMOEBA)
6601 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6606 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6608 /* amoeba gets larger by growing in some direction */
6610 int new_group_nr = AmoebaNr[ax][ay];
6613 if (new_group_nr == 0)
6615 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6616 printf("AmoebeAbleger(): This should never happen!\n");
6621 AmoebaNr[newax][neway] = new_group_nr;
6622 AmoebaCnt[new_group_nr]++;
6623 AmoebaCnt2[new_group_nr]++;
6625 /* if amoeba touches other amoeba(s) after growing, unify them */
6626 AmoebenVereinigen(newax, neway);
6628 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6630 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6636 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6637 (neway == lev_fieldy - 1 && newax != ax))
6639 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6640 Store[newax][neway] = element;
6642 else if (neway == ay)
6644 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6646 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6648 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6653 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6654 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6655 Store[ax][ay] = EL_AMOEBA_DROP;
6656 ContinueMoving(ax, ay);
6660 DrawLevelField(newax, neway);
6663 void Life(int ax, int ay)
6666 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6668 int element = Feld[ax][ay];
6669 int graphic = el2img(element);
6670 boolean changed = FALSE;
6672 if (IS_ANIMATED(graphic))
6673 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6678 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6679 MovDelay[ax][ay] = life_time;
6681 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6684 if (MovDelay[ax][ay])
6688 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6690 int xx = ax+x1, yy = ay+y1;
6693 if (!IN_LEV_FIELD(xx, yy))
6696 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6698 int x = xx+x2, y = yy+y2;
6700 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6703 if (((Feld[x][y] == element ||
6704 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6706 (IS_FREE(x, y) && Stop[x][y]))
6710 if (xx == ax && yy == ay) /* field in the middle */
6712 if (nachbarn < life[0] || nachbarn > life[1])
6714 Feld[xx][yy] = EL_EMPTY;
6716 DrawLevelField(xx, yy);
6717 Stop[xx][yy] = TRUE;
6722 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6723 { /* free border field */
6724 if (nachbarn >= life[2] && nachbarn <= life[3])
6726 Feld[xx][yy] = element;
6727 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6729 DrawLevelField(xx, yy);
6730 Stop[xx][yy] = TRUE;
6735 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6736 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6737 { /* free border field */
6738 if (nachbarn >= life[2] && nachbarn <= life[3])
6740 Feld[xx][yy] = element;
6741 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6743 DrawLevelField(xx, yy);
6744 Stop[xx][yy] = TRUE;
6752 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6753 SND_GAME_OF_LIFE_GROWING);
6756 static void InitRobotWheel(int x, int y)
6758 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6761 static void RunRobotWheel(int x, int y)
6763 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6766 static void StopRobotWheel(int x, int y)
6768 if (ZX == x && ZY == y)
6772 static void InitTimegateWheel(int x, int y)
6775 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6777 /* another brainless, "type style" bug ... :-( */
6778 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6782 static void RunTimegateWheel(int x, int y)
6784 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6787 void CheckExit(int x, int y)
6789 if (local_player->gems_still_needed > 0 ||
6790 local_player->sokobanfields_still_needed > 0 ||
6791 local_player->lights_still_needed > 0)
6793 int element = Feld[x][y];
6794 int graphic = el2img(element);
6796 if (IS_ANIMATED(graphic))
6797 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6802 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6805 Feld[x][y] = EL_EXIT_OPENING;
6807 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6810 void CheckExitSP(int x, int y)
6812 if (local_player->gems_still_needed > 0)
6814 int element = Feld[x][y];
6815 int graphic = el2img(element);
6817 if (IS_ANIMATED(graphic))
6818 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6823 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6826 Feld[x][y] = EL_SP_EXIT_OPENING;
6828 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6831 static void CloseAllOpenTimegates()
6835 for (y = 0; y < lev_fieldy; y++)
6837 for (x = 0; x < lev_fieldx; x++)
6839 int element = Feld[x][y];
6841 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6843 Feld[x][y] = EL_TIMEGATE_CLOSING;
6845 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6847 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6854 void EdelsteinFunkeln(int x, int y)
6856 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6859 if (Feld[x][y] == EL_BD_DIAMOND)
6862 if (MovDelay[x][y] == 0) /* next animation frame */
6863 MovDelay[x][y] = 11 * !SimpleRND(500);
6865 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6869 if (setup.direct_draw && MovDelay[x][y])
6870 SetDrawtoField(DRAW_BUFFERED);
6872 DrawLevelElementAnimation(x, y, Feld[x][y]);
6874 if (MovDelay[x][y] != 0)
6876 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6877 10 - MovDelay[x][y]);
6879 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6881 if (setup.direct_draw)
6885 dest_x = FX + SCREENX(x) * TILEX;
6886 dest_y = FY + SCREENY(y) * TILEY;
6888 BlitBitmap(drawto_field, window,
6889 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6890 SetDrawtoField(DRAW_DIRECT);
6896 void MauerWaechst(int x, int y)
6900 if (!MovDelay[x][y]) /* next animation frame */
6901 MovDelay[x][y] = 3 * delay;
6903 if (MovDelay[x][y]) /* wait some time before next frame */
6907 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6909 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6910 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6912 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6915 if (!MovDelay[x][y])
6917 if (MovDir[x][y] == MV_LEFT)
6919 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6920 DrawLevelField(x - 1, y);
6922 else if (MovDir[x][y] == MV_RIGHT)
6924 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6925 DrawLevelField(x + 1, y);
6927 else if (MovDir[x][y] == MV_UP)
6929 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6930 DrawLevelField(x, y - 1);
6934 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6935 DrawLevelField(x, y + 1);
6938 Feld[x][y] = Store[x][y];
6940 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6941 DrawLevelField(x, y);
6946 void MauerAbleger(int ax, int ay)
6948 int element = Feld[ax][ay];
6949 int graphic = el2img(element);
6950 boolean oben_frei = FALSE, unten_frei = FALSE;
6951 boolean links_frei = FALSE, rechts_frei = FALSE;
6952 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6953 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6954 boolean new_wall = FALSE;
6956 if (IS_ANIMATED(graphic))
6957 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6959 if (!MovDelay[ax][ay]) /* start building new wall */
6960 MovDelay[ax][ay] = 6;
6962 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6965 if (MovDelay[ax][ay])
6969 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6971 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6973 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6975 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6978 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6979 element == EL_EXPANDABLE_WALL_ANY)
6983 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6984 Store[ax][ay-1] = element;
6985 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6986 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6987 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6988 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6993 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6994 Store[ax][ay+1] = element;
6995 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6996 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6997 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6998 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7003 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7004 element == EL_EXPANDABLE_WALL_ANY ||
7005 element == EL_EXPANDABLE_WALL)
7009 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7010 Store[ax-1][ay] = element;
7011 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7012 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7013 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7014 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7020 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7021 Store[ax+1][ay] = element;
7022 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7023 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7024 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7025 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7030 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7031 DrawLevelField(ax, ay);
7033 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7035 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7036 unten_massiv = TRUE;
7037 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7038 links_massiv = TRUE;
7039 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7040 rechts_massiv = TRUE;
7042 if (((oben_massiv && unten_massiv) ||
7043 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7044 element == EL_EXPANDABLE_WALL) &&
7045 ((links_massiv && rechts_massiv) ||
7046 element == EL_EXPANDABLE_WALL_VERTICAL))
7047 Feld[ax][ay] = EL_WALL;
7051 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7053 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7057 void CheckForDragon(int x, int y)
7060 boolean dragon_found = FALSE;
7061 static int xy[4][2] =
7069 for (i = 0; i < NUM_DIRECTIONS; i++)
7071 for (j = 0; j < 4; j++)
7073 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7075 if (IN_LEV_FIELD(xx, yy) &&
7076 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7078 if (Feld[xx][yy] == EL_DRAGON)
7079 dragon_found = TRUE;
7088 for (i = 0; i < NUM_DIRECTIONS; i++)
7090 for (j = 0; j < 3; j++)
7092 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7094 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7096 Feld[xx][yy] = EL_EMPTY;
7097 DrawLevelField(xx, yy);
7106 static void InitBuggyBase(int x, int y)
7108 int element = Feld[x][y];
7109 int activating_delay = FRAMES_PER_SECOND / 4;
7112 (element == EL_SP_BUGGY_BASE ?
7113 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7114 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7116 element == EL_SP_BUGGY_BASE_ACTIVE ?
7117 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7120 static void WarnBuggyBase(int x, int y)
7123 static int xy[4][2] =
7131 for (i = 0; i < NUM_DIRECTIONS; i++)
7133 int xx = x + xy[i][0], yy = y + xy[i][1];
7135 if (IS_PLAYER(xx, yy))
7137 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7144 static void InitTrap(int x, int y)
7146 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7149 static void ActivateTrap(int x, int y)
7151 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7154 static void ChangeActiveTrap(int x, int y)
7156 int graphic = IMG_TRAP_ACTIVE;
7158 /* if new animation frame was drawn, correct crumbled sand border */
7159 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7160 DrawLevelFieldCrumbledSand(x, y);
7163 static void ChangeElementNowExt(int x, int y, int target_element)
7165 int previous_move_direction = MovDir[x][y];
7167 /* check if element under player changes from accessible to unaccessible
7168 (needed for special case of dropping element which then changes) */
7169 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7170 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7177 Feld[x][y] = target_element;
7179 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7181 ResetGfxAnimation(x, y);
7182 ResetRandomAnimationValue(x, y);
7184 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7185 MovDir[x][y] = previous_move_direction;
7188 InitField_WithBug1(x, y, FALSE);
7190 InitField(x, y, FALSE);
7191 if (CAN_MOVE(Feld[x][y]))
7195 DrawLevelField(x, y);
7197 if (GFX_CRUMBLED(Feld[x][y]))
7198 DrawLevelFieldCrumbledSandNeighbours(x, y);
7200 TestIfBadThingTouchesHero(x, y);
7201 TestIfPlayerTouchesCustomElement(x, y);
7202 TestIfElementTouchesCustomElement(x, y);
7204 if (ELEM_IS_PLAYER(target_element))
7205 RelocatePlayer(x, y, target_element);
7208 static boolean ChangeElementNow(int x, int y, int element, int page)
7210 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7213 /* always use default change event to prevent running into a loop */
7214 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7215 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7217 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7219 /* reset actual trigger element and player */
7220 change->actual_trigger_element = EL_EMPTY;
7221 change->actual_trigger_player = EL_PLAYER_1;
7224 /* do not change already changed elements with same change event */
7226 if (Changed[x][y] & ChangeEvent[x][y])
7233 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7235 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7237 if (change->explode)
7244 if (change->use_target_content)
7246 boolean complete_replace = TRUE;
7247 boolean can_replace[3][3];
7250 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7253 boolean is_diggable;
7254 boolean is_collectible;
7255 boolean is_removable;
7256 boolean is_destructible;
7257 int ex = x + xx - 1;
7258 int ey = y + yy - 1;
7259 int content_element = change->target_content[xx][yy];
7262 can_replace[xx][yy] = TRUE;
7264 if (ex == x && ey == y) /* do not check changing element itself */
7267 if (content_element == EL_EMPTY_SPACE)
7269 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7274 if (!IN_LEV_FIELD(ex, ey))
7276 can_replace[xx][yy] = FALSE;
7277 complete_replace = FALSE;
7284 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7285 e = MovingOrBlocked2Element(ex, ey);
7290 is_empty = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7291 IS_WALKABLE(content_element)));
7293 is_empty = (IS_FREE(ex, ey) || (IS_PLAYER(ex, ey) &&
7294 IS_WALKABLE(content_element)));
7296 is_diggable = (is_empty || IS_DIGGABLE(e));
7297 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7298 is_removable = (is_diggable || is_collectible);
7299 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7301 can_replace[xx][yy] =
7302 ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7303 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7304 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7305 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7306 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7308 if (!can_replace[xx][yy])
7309 complete_replace = FALSE;
7311 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7312 IS_WALKABLE(content_element)));
7314 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7316 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7319 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7320 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7321 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7323 can_replace[xx][yy] = FALSE;
7324 complete_replace = FALSE;
7329 if (!change->only_if_complete || complete_replace)
7331 boolean something_has_changed = FALSE;
7333 if (change->only_if_complete && change->use_random_replace &&
7334 RND(100) < change->random_percentage)
7337 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7339 int ex = x + xx - 1;
7340 int ey = y + yy - 1;
7341 int content_element;
7343 if (can_replace[xx][yy] && (!change->use_random_replace ||
7344 RND(100) < change->random_percentage))
7346 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7347 RemoveMovingField(ex, ey);
7349 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7351 content_element = change->target_content[xx][yy];
7352 target_element = GET_TARGET_ELEMENT(content_element, change);
7354 ChangeElementNowExt(ex, ey, target_element);
7356 something_has_changed = TRUE;
7358 /* for symmetry reasons, freeze newly created border elements */
7359 if (ex != x || ey != y)
7360 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7364 if (something_has_changed)
7365 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7370 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7372 ChangeElementNowExt(x, y, target_element);
7374 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7380 static void ChangeElement(int x, int y, int page)
7382 int element = MovingOrBlocked2Element(x, y);
7383 struct ElementInfo *ei = &element_info[element];
7384 struct ElementChangeInfo *change = &ei->change_page[page];
7387 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7390 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7391 x, y, element, element_info[element].token_name);
7392 printf("ChangeElement(): This should never happen!\n");
7397 /* this can happen with classic bombs on walkable, changing elements */
7398 if (!CAN_CHANGE(element))
7401 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7402 ChangeDelay[x][y] = 0;
7408 if (ChangeDelay[x][y] == 0) /* initialize element change */
7410 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7411 RND(change->delay_random * change->delay_frames)) + 1;
7413 ResetGfxAnimation(x, y);
7414 ResetRandomAnimationValue(x, y);
7416 if (change->pre_change_function)
7417 change->pre_change_function(x, y);
7420 ChangeDelay[x][y]--;
7422 if (ChangeDelay[x][y] != 0) /* continue element change */
7424 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7426 if (IS_ANIMATED(graphic))
7427 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7429 if (change->change_function)
7430 change->change_function(x, y);
7432 else /* finish element change */
7434 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7436 page = ChangePage[x][y];
7437 ChangePage[x][y] = -1;
7439 change = &ei->change_page[page];
7443 if (IS_MOVING(x, y) && !change->explode)
7445 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7448 ChangeDelay[x][y] = 1; /* try change after next move step */
7449 ChangePage[x][y] = page; /* remember page to use for change */
7454 if (ChangeElementNow(x, y, element, page))
7456 if (change->post_change_function)
7457 change->post_change_function(x, y);
7462 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7463 int trigger_element,
7470 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7472 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7475 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7477 int element = EL_CUSTOM_START + i;
7479 boolean change_element = FALSE;
7482 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7485 for (j = 0; j < element_info[element].num_change_pages; j++)
7487 struct ElementChangeInfo *change = &element_info[element].change_page[j];
7489 if (change->can_change &&
7490 change->events & CH_EVENT_BIT(trigger_event) &&
7491 change->trigger_side & trigger_side &&
7492 change->trigger_player & trigger_player &&
7493 change->trigger_page & trigger_page_bits &&
7494 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7497 if (!(change->events & CH_EVENT_BIT(trigger_event)))
7498 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7499 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7502 change_element = TRUE;
7505 change->actual_trigger_element = trigger_element;
7506 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7512 if (!change_element)
7515 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7518 if (x == lx && y == ly) /* do not change trigger element itself */
7522 if (Feld[x][y] == element)
7524 ChangeDelay[x][y] = 1;
7525 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7526 ChangeElement(x, y, page);
7534 static boolean CheckElementChangeExt(int x, int y,
7536 int trigger_element,
7542 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7545 if (Feld[x][y] == EL_BLOCKED)
7547 Blocked2Moving(x, y, &x, &y);
7548 element = Feld[x][y];
7552 if (Feld[x][y] != element) /* check if element has already changed */
7555 printf("::: %d ('%s') != %d ('%s') [%d]\n",
7556 Feld[x][y], element_info[Feld[x][y]].token_name,
7557 element, element_info[element].token_name,
7566 if (trigger_page < 0)
7568 boolean change_element = FALSE;
7571 for (i = 0; i < element_info[element].num_change_pages; i++)
7573 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7575 if (change->can_change &&
7576 change->events & CH_EVENT_BIT(trigger_event) &&
7577 change->trigger_side & trigger_side &&
7578 change->trigger_player & trigger_player)
7580 change_element = TRUE;
7583 change->actual_trigger_element = trigger_element;
7584 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7590 if (!change_element)
7595 struct ElementInfo *ei = &element_info[element];
7596 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7598 change->actual_trigger_element = trigger_element;
7599 change->actual_trigger_player = EL_PLAYER_1; /* unused */
7604 /* !!! this check misses pages with same event, but different side !!! */
7606 if (trigger_page < 0)
7607 trigger_page = element_info[element].event_page_nr[trigger_event];
7609 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7613 ChangeDelay[x][y] = 1;
7614 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7615 ChangeElement(x, y, trigger_page);
7620 static void PlayPlayerSound(struct PlayerInfo *player)
7622 int jx = player->jx, jy = player->jy;
7623 int element = player->element_nr;
7624 int last_action = player->last_action_waiting;
7625 int action = player->action_waiting;
7627 if (player->is_waiting)
7629 if (action != last_action)
7630 PlayLevelSoundElementAction(jx, jy, element, action);
7632 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7636 if (action != last_action)
7637 StopSound(element_info[element].sound[last_action]);
7639 if (last_action == ACTION_SLEEPING)
7640 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7644 static void PlayAllPlayersSound()
7648 for (i = 0; i < MAX_PLAYERS; i++)
7649 if (stored_player[i].active)
7650 PlayPlayerSound(&stored_player[i]);
7653 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7655 boolean last_waiting = player->is_waiting;
7656 int move_dir = player->MovDir;
7658 player->last_action_waiting = player->action_waiting;
7662 if (!last_waiting) /* not waiting -> waiting */
7664 player->is_waiting = TRUE;
7666 player->frame_counter_bored =
7668 game.player_boring_delay_fixed +
7669 SimpleRND(game.player_boring_delay_random);
7670 player->frame_counter_sleeping =
7672 game.player_sleeping_delay_fixed +
7673 SimpleRND(game.player_sleeping_delay_random);
7675 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7678 if (game.player_sleeping_delay_fixed +
7679 game.player_sleeping_delay_random > 0 &&
7680 player->anim_delay_counter == 0 &&
7681 player->post_delay_counter == 0 &&
7682 FrameCounter >= player->frame_counter_sleeping)
7683 player->is_sleeping = TRUE;
7684 else if (game.player_boring_delay_fixed +
7685 game.player_boring_delay_random > 0 &&
7686 FrameCounter >= player->frame_counter_bored)
7687 player->is_bored = TRUE;
7689 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7690 player->is_bored ? ACTION_BORING :
7693 if (player->is_sleeping)
7695 if (player->num_special_action_sleeping > 0)
7697 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7699 int last_special_action = player->special_action_sleeping;
7700 int num_special_action = player->num_special_action_sleeping;
7701 int special_action =
7702 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7703 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7704 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7705 last_special_action + 1 : ACTION_SLEEPING);
7706 int special_graphic =
7707 el_act_dir2img(player->element_nr, special_action, move_dir);
7709 player->anim_delay_counter =
7710 graphic_info[special_graphic].anim_delay_fixed +
7711 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7712 player->post_delay_counter =
7713 graphic_info[special_graphic].post_delay_fixed +
7714 SimpleRND(graphic_info[special_graphic].post_delay_random);
7716 player->special_action_sleeping = special_action;
7719 if (player->anim_delay_counter > 0)
7721 player->action_waiting = player->special_action_sleeping;
7722 player->anim_delay_counter--;
7724 else if (player->post_delay_counter > 0)
7726 player->post_delay_counter--;
7730 else if (player->is_bored)
7732 if (player->num_special_action_bored > 0)
7734 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7736 int special_action =
7737 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7738 int special_graphic =
7739 el_act_dir2img(player->element_nr, special_action, move_dir);
7741 player->anim_delay_counter =
7742 graphic_info[special_graphic].anim_delay_fixed +
7743 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7744 player->post_delay_counter =
7745 graphic_info[special_graphic].post_delay_fixed +
7746 SimpleRND(graphic_info[special_graphic].post_delay_random);
7748 player->special_action_bored = special_action;
7751 if (player->anim_delay_counter > 0)
7753 player->action_waiting = player->special_action_bored;
7754 player->anim_delay_counter--;
7756 else if (player->post_delay_counter > 0)
7758 player->post_delay_counter--;
7763 else if (last_waiting) /* waiting -> not waiting */
7765 player->is_waiting = FALSE;
7766 player->is_bored = FALSE;
7767 player->is_sleeping = FALSE;
7769 player->frame_counter_bored = -1;
7770 player->frame_counter_sleeping = -1;
7772 player->anim_delay_counter = 0;
7773 player->post_delay_counter = 0;
7775 player->action_waiting = ACTION_DEFAULT;
7777 player->special_action_bored = ACTION_DEFAULT;
7778 player->special_action_sleeping = ACTION_DEFAULT;
7783 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7786 static byte stored_player_action[MAX_PLAYERS];
7787 static int num_stored_actions = 0;
7789 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7790 int left = player_action & JOY_LEFT;
7791 int right = player_action & JOY_RIGHT;
7792 int up = player_action & JOY_UP;
7793 int down = player_action & JOY_DOWN;
7794 int button1 = player_action & JOY_BUTTON_1;
7795 int button2 = player_action & JOY_BUTTON_2;
7796 int dx = (left ? -1 : right ? 1 : 0);
7797 int dy = (up ? -1 : down ? 1 : 0);
7800 stored_player_action[player->index_nr] = 0;
7801 num_stored_actions++;
7805 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7808 if (!player->active || tape.pausing)
7812 printf("::: [%d %d %d %d] [%d %d]\n",
7813 left, right, up, down, button1, button2);
7819 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7824 if (player->MovPos == 0)
7825 CheckGravityMovement(player);
7828 snapped = SnapField(player, dx, dy);
7832 dropped = DropElement(player);
7834 moved = MovePlayer(player, dx, dy);
7837 if (tape.single_step && tape.recording && !tape.pausing)
7839 if (button1 || (dropped && !moved))
7841 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7842 SnapField(player, 0, 0); /* stop snapping */
7846 SetPlayerWaiting(player, FALSE);
7849 return player_action;
7851 stored_player_action[player->index_nr] = player_action;
7857 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7860 /* no actions for this player (no input at player's configured device) */
7862 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7863 SnapField(player, 0, 0);
7864 CheckGravityMovementWhenNotMoving(player);
7866 if (player->MovPos == 0)
7867 SetPlayerWaiting(player, TRUE);
7869 if (player->MovPos == 0) /* needed for tape.playing */
7870 player->is_moving = FALSE;
7872 player->is_dropping = FALSE;
7878 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7880 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7882 TapeRecordAction(stored_player_action);
7883 num_stored_actions = 0;
7890 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7892 static byte stored_player_action[MAX_PLAYERS];
7893 static int num_stored_actions = 0;
7894 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7895 int left = player_action & JOY_LEFT;
7896 int right = player_action & JOY_RIGHT;
7897 int up = player_action & JOY_UP;
7898 int down = player_action & JOY_DOWN;
7899 int button1 = player_action & JOY_BUTTON_1;
7900 int button2 = player_action & JOY_BUTTON_2;
7901 int dx = (left ? -1 : right ? 1 : 0);
7902 int dy = (up ? -1 : down ? 1 : 0);
7904 stored_player_action[player->index_nr] = 0;
7905 num_stored_actions++;
7907 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7909 if (!player->active || tape.pausing)
7914 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7917 snapped = SnapField(player, dx, dy);
7921 dropped = DropElement(player);
7923 moved = MovePlayer(player, dx, dy);
7926 if (tape.single_step && tape.recording && !tape.pausing)
7928 if (button1 || (dropped && !moved))
7930 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7931 SnapField(player, 0, 0); /* stop snapping */
7935 stored_player_action[player->index_nr] = player_action;
7939 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7941 /* no actions for this player (no input at player's configured device) */
7943 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7944 SnapField(player, 0, 0);
7945 CheckGravityMovementWhenNotMoving(player);
7947 if (player->MovPos == 0)
7948 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7950 if (player->MovPos == 0) /* needed for tape.playing */
7951 player->is_moving = FALSE;
7954 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7956 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7958 TapeRecordAction(stored_player_action);
7959 num_stored_actions = 0;
7966 static unsigned long action_delay = 0;
7967 unsigned long action_delay_value;
7968 int magic_wall_x = 0, magic_wall_y = 0;
7969 int i, x, y, element, graphic;
7970 byte *recorded_player_action;
7971 byte summarized_player_action = 0;
7973 byte tape_action[MAX_PLAYERS];
7976 if (game_status != GAME_MODE_PLAYING)
7979 action_delay_value =
7980 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7982 if (tape.playing && tape.warp_forward && !tape.pausing)
7983 action_delay_value = 0;
7985 /* ---------- main game synchronization point ---------- */
7987 WaitUntilDelayReached(&action_delay, action_delay_value);
7989 if (network_playing && !network_player_action_received)
7993 printf("DEBUG: try to get network player actions in time\n");
7997 #if defined(PLATFORM_UNIX)
7998 /* last chance to get network player actions without main loop delay */
8002 if (game_status != GAME_MODE_PLAYING)
8005 if (!network_player_action_received)
8009 printf("DEBUG: failed to get network player actions in time\n");
8020 printf("::: getting new tape action [%d]\n", FrameCounter);
8023 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8026 if (recorded_player_action == NULL && tape.pausing)
8031 printf("::: %d\n", stored_player[0].action);
8035 if (recorded_player_action != NULL)
8036 for (i = 0; i < MAX_PLAYERS; i++)
8037 stored_player[i].action = recorded_player_action[i];
8040 for (i = 0; i < MAX_PLAYERS; i++)
8042 summarized_player_action |= stored_player[i].action;
8044 if (!network_playing)
8045 stored_player[i].effective_action = stored_player[i].action;
8048 #if defined(PLATFORM_UNIX)
8049 if (network_playing)
8050 SendToServer_MovePlayer(summarized_player_action);
8053 if (!options.network && !setup.team_mode)
8054 local_player->effective_action = summarized_player_action;
8057 if (recorded_player_action != NULL)
8058 for (i = 0; i < MAX_PLAYERS; i++)
8059 stored_player[i].effective_action = recorded_player_action[i];
8063 for (i = 0; i < MAX_PLAYERS; i++)
8065 tape_action[i] = stored_player[i].effective_action;
8067 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8068 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8071 /* only save actions from input devices, but not programmed actions */
8073 TapeRecordAction(tape_action);
8076 for (i = 0; i < MAX_PLAYERS; i++)
8078 int actual_player_action = stored_player[i].effective_action;
8081 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8082 - rnd_equinox_tetrachloride 048
8083 - rnd_equinox_tetrachloride_ii 096
8084 - rnd_emanuel_schmieg 002
8085 - doctor_sloan_ww 001, 020
8087 if (stored_player[i].MovPos == 0)
8088 CheckGravityMovement(&stored_player[i]);
8092 /* overwrite programmed action with tape action */
8093 if (stored_player[i].programmed_action)
8094 actual_player_action = stored_player[i].programmed_action;
8098 if (stored_player[i].programmed_action)
8099 printf("::: %d\n", stored_player[i].programmed_action);
8102 if (recorded_player_action)
8105 if (stored_player[i].programmed_action &&
8106 stored_player[i].programmed_action != recorded_player_action[i])
8107 printf("::: %d: %d <-> %d\n", i,
8108 stored_player[i].programmed_action, recorded_player_action[i]);
8112 actual_player_action = recorded_player_action[i];
8117 /* overwrite tape action with programmed action */
8118 if (stored_player[i].programmed_action)
8119 actual_player_action = stored_player[i].programmed_action;
8124 printf("::: action: %d: %x [%d]\n",
8125 stored_player[i].MovPos, actual_player_action, FrameCounter);
8129 PlayerActions(&stored_player[i], actual_player_action);
8131 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8133 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8134 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8137 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8142 TapeRecordAction(tape_action);
8145 network_player_action_received = FALSE;
8147 ScrollScreen(NULL, SCROLL_GO_ON);
8153 for (i = 0; i < MAX_PLAYERS; i++)
8154 stored_player[i].Frame++;
8158 /* for downwards compatibility, the following code emulates a fixed bug that
8159 occured when pushing elements (causing elements that just made their last
8160 pushing step to already (if possible) make their first falling step in the
8161 same game frame, which is bad); this code is also needed to use the famous
8162 "spring push bug" which is used in older levels and might be wanted to be
8163 used also in newer levels, but in this case the buggy pushing code is only
8164 affecting the "spring" element and no other elements */
8167 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8169 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8172 for (i = 0; i < MAX_PLAYERS; i++)
8174 struct PlayerInfo *player = &stored_player[i];
8179 if (player->active && player->is_pushing && player->is_moving &&
8181 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8182 Feld[x][y] == EL_SPRING))
8184 if (player->active && player->is_pushing && player->is_moving &&
8188 ContinueMoving(x, y);
8190 /* continue moving after pushing (this is actually a bug) */
8191 if (!IS_MOVING(x, y))
8200 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8202 Changed[x][y] = CE_BITMASK_DEFAULT;
8203 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8206 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8208 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8209 printf("GameActions(): This should never happen!\n");
8211 ChangePage[x][y] = -1;
8216 if (WasJustMoving[x][y] > 0)
8217 WasJustMoving[x][y]--;
8218 if (WasJustFalling[x][y] > 0)
8219 WasJustFalling[x][y]--;
8220 if (CheckCollision[x][y] > 0)
8221 CheckCollision[x][y]--;
8226 /* reset finished pushing action (not done in ContinueMoving() to allow
8227 continous pushing animation for elements with zero push delay) */
8228 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8230 ResetGfxAnimation(x, y);
8231 DrawLevelField(x, y);
8236 if (IS_BLOCKED(x, y))
8240 Blocked2Moving(x, y, &oldx, &oldy);
8241 if (!IS_MOVING(oldx, oldy))
8243 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8244 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8245 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8246 printf("GameActions(): This should never happen!\n");
8252 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8254 element = Feld[x][y];
8256 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8258 graphic = el2img(element);
8264 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8266 element = graphic = 0;
8270 if (graphic_info[graphic].anim_global_sync)
8271 GfxFrame[x][y] = FrameCounter;
8273 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8274 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8275 ResetRandomAnimationValue(x, y);
8277 SetRandomAnimationValue(x, y);
8280 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8283 if (IS_INACTIVE(element))
8285 if (IS_ANIMATED(graphic))
8286 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8292 /* this may take place after moving, so 'element' may have changed */
8294 if (IS_CHANGING(x, y))
8296 if (IS_CHANGING(x, y) &&
8297 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8301 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8302 element_info[element].event_page_nr[CE_DELAY]);
8304 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8307 element = Feld[x][y];
8308 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8312 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8317 element = Feld[x][y];
8318 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8320 if (element == EL_MOLE)
8321 printf("::: %d, %d, %d [%d]\n",
8322 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8326 if (element == EL_YAMYAM)
8327 printf("::: %d, %d, %d\n",
8328 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8332 if (IS_ANIMATED(graphic) &&
8336 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8339 if (element == EL_BUG)
8340 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8344 if (element == EL_MOLE)
8345 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8349 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8350 EdelsteinFunkeln(x, y);
8352 else if ((element == EL_ACID ||
8353 element == EL_EXIT_OPEN ||
8354 element == EL_SP_EXIT_OPEN ||
8355 element == EL_SP_TERMINAL ||
8356 element == EL_SP_TERMINAL_ACTIVE ||
8357 element == EL_EXTRA_TIME ||
8358 element == EL_SHIELD_NORMAL ||
8359 element == EL_SHIELD_DEADLY) &&
8360 IS_ANIMATED(graphic))
8361 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8362 else if (IS_MOVING(x, y))
8363 ContinueMoving(x, y);
8364 else if (IS_ACTIVE_BOMB(element))
8365 CheckDynamite(x, y);
8367 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8368 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8370 else if (element == EL_AMOEBA_GROWING)
8371 AmoebeWaechst(x, y);
8372 else if (element == EL_AMOEBA_SHRINKING)
8373 AmoebaDisappearing(x, y);
8375 #if !USE_NEW_AMOEBA_CODE
8376 else if (IS_AMOEBALIVE(element))
8377 AmoebeAbleger(x, y);
8380 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8382 else if (element == EL_EXIT_CLOSED)
8384 else if (element == EL_SP_EXIT_CLOSED)
8386 else if (element == EL_EXPANDABLE_WALL_GROWING)
8388 else if (element == EL_EXPANDABLE_WALL ||
8389 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8390 element == EL_EXPANDABLE_WALL_VERTICAL ||
8391 element == EL_EXPANDABLE_WALL_ANY)
8393 else if (element == EL_FLAMES)
8394 CheckForDragon(x, y);
8396 else if (IS_AUTO_CHANGING(element))
8397 ChangeElement(x, y);
8399 else if (element == EL_EXPLOSION)
8400 ; /* drawing of correct explosion animation is handled separately */
8401 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8402 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8405 /* this may take place after moving, so 'element' may have changed */
8406 if (IS_AUTO_CHANGING(Feld[x][y]))
8407 ChangeElement(x, y);
8410 if (IS_BELT_ACTIVE(element))
8411 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8413 if (game.magic_wall_active)
8415 int jx = local_player->jx, jy = local_player->jy;
8417 /* play the element sound at the position nearest to the player */
8418 if ((element == EL_MAGIC_WALL_FULL ||
8419 element == EL_MAGIC_WALL_ACTIVE ||
8420 element == EL_MAGIC_WALL_EMPTYING ||
8421 element == EL_BD_MAGIC_WALL_FULL ||
8422 element == EL_BD_MAGIC_WALL_ACTIVE ||
8423 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8424 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8432 #if USE_NEW_AMOEBA_CODE
8433 /* new experimental amoeba growth stuff */
8435 if (!(FrameCounter % 8))
8438 static unsigned long random = 1684108901;
8440 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8443 x = (random >> 10) % lev_fieldx;
8444 y = (random >> 20) % lev_fieldy;
8446 x = RND(lev_fieldx);
8447 y = RND(lev_fieldy);
8449 element = Feld[x][y];
8452 if (!IS_PLAYER(x,y) &&
8453 (element == EL_EMPTY ||
8454 CAN_GROW_INTO(element) ||
8455 element == EL_QUICKSAND_EMPTY ||
8456 element == EL_ACID_SPLASH_LEFT ||
8457 element == EL_ACID_SPLASH_RIGHT))
8459 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8460 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8461 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8462 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8463 Feld[x][y] = EL_AMOEBA_DROP;
8466 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8467 if (!IS_PLAYER(x,y) &&
8468 (element == EL_EMPTY ||
8469 element == EL_SAND ||
8470 element == EL_QUICKSAND_EMPTY ||
8471 element == EL_ACID_SPLASH_LEFT ||
8472 element == EL_ACID_SPLASH_RIGHT))
8474 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8475 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8476 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8477 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8478 Feld[x][y] = EL_AMOEBA_DROP;
8482 random = random * 129 + 1;
8488 if (game.explosions_delayed)
8491 game.explosions_delayed = FALSE;
8493 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8495 element = Feld[x][y];
8497 if (ExplodeField[x][y])
8498 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8499 else if (element == EL_EXPLOSION)
8500 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8502 ExplodeField[x][y] = EX_TYPE_NONE;
8505 game.explosions_delayed = TRUE;
8508 if (game.magic_wall_active)
8510 if (!(game.magic_wall_time_left % 4))
8512 int element = Feld[magic_wall_x][magic_wall_y];
8514 if (element == EL_BD_MAGIC_WALL_FULL ||
8515 element == EL_BD_MAGIC_WALL_ACTIVE ||
8516 element == EL_BD_MAGIC_WALL_EMPTYING)
8517 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8519 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8522 if (game.magic_wall_time_left > 0)
8524 game.magic_wall_time_left--;
8525 if (!game.magic_wall_time_left)
8527 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8529 element = Feld[x][y];
8531 if (element == EL_MAGIC_WALL_ACTIVE ||
8532 element == EL_MAGIC_WALL_FULL)
8534 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8535 DrawLevelField(x, y);
8537 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8538 element == EL_BD_MAGIC_WALL_FULL)
8540 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8541 DrawLevelField(x, y);
8545 game.magic_wall_active = FALSE;
8550 if (game.light_time_left > 0)
8552 game.light_time_left--;
8554 if (game.light_time_left == 0)
8555 RedrawAllLightSwitchesAndInvisibleElements();
8558 if (game.timegate_time_left > 0)
8560 game.timegate_time_left--;
8562 if (game.timegate_time_left == 0)
8563 CloseAllOpenTimegates();
8566 for (i = 0; i < MAX_PLAYERS; i++)
8568 struct PlayerInfo *player = &stored_player[i];
8570 if (SHIELD_ON(player))
8572 if (player->shield_deadly_time_left)
8573 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8574 else if (player->shield_normal_time_left)
8575 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8579 if (TimeFrames >= FRAMES_PER_SECOND)
8584 if (!level.use_step_counter)
8588 for (i = 0; i < MAX_PLAYERS; i++)
8590 struct PlayerInfo *player = &stored_player[i];
8592 if (SHIELD_ON(player))
8594 player->shield_normal_time_left--;
8596 if (player->shield_deadly_time_left > 0)
8597 player->shield_deadly_time_left--;
8605 if (TimeLeft <= 10 && setup.time_limit)
8606 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8608 DrawGameValue_Time(TimeLeft);
8610 if (!TimeLeft && setup.time_limit)
8611 for (i = 0; i < MAX_PLAYERS; i++)
8612 KillHero(&stored_player[i]);
8614 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8615 DrawGameValue_Time(TimePlayed);
8618 if (tape.recording || tape.playing)
8619 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8623 PlayAllPlayersSound();
8625 if (options.debug) /* calculate frames per second */
8627 static unsigned long fps_counter = 0;
8628 static int fps_frames = 0;
8629 unsigned long fps_delay_ms = Counter() - fps_counter;
8633 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8635 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8638 fps_counter = Counter();
8641 redraw_mask |= REDRAW_FPS;
8645 if (stored_player[0].jx != stored_player[0].last_jx ||
8646 stored_player[0].jy != stored_player[0].last_jy)
8647 printf("::: %d, %d, %d, %d, %d\n",
8648 stored_player[0].MovDir,
8649 stored_player[0].MovPos,
8650 stored_player[0].GfxPos,
8651 stored_player[0].Frame,
8652 stored_player[0].StepFrame);
8659 for (i = 0; i < MAX_PLAYERS; i++)
8662 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8664 stored_player[i].Frame += move_frames;
8666 if (stored_player[i].MovPos != 0)
8667 stored_player[i].StepFrame += move_frames;
8669 if (stored_player[i].drop_delay > 0)
8670 stored_player[i].drop_delay--;
8675 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8677 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8679 local_player->show_envelope = 0;
8684 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8686 int min_x = x, min_y = y, max_x = x, max_y = y;
8689 for (i = 0; i < MAX_PLAYERS; i++)
8691 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8693 if (!stored_player[i].active || &stored_player[i] == player)
8696 min_x = MIN(min_x, jx);
8697 min_y = MIN(min_y, jy);
8698 max_x = MAX(max_x, jx);
8699 max_y = MAX(max_y, jy);
8702 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8705 static boolean AllPlayersInVisibleScreen()
8709 for (i = 0; i < MAX_PLAYERS; i++)
8711 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8713 if (!stored_player[i].active)
8716 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8723 void ScrollLevel(int dx, int dy)
8725 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8728 BlitBitmap(drawto_field, drawto_field,
8729 FX + TILEX * (dx == -1) - softscroll_offset,
8730 FY + TILEY * (dy == -1) - softscroll_offset,
8731 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8732 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8733 FX + TILEX * (dx == 1) - softscroll_offset,
8734 FY + TILEY * (dy == 1) - softscroll_offset);
8738 x = (dx == 1 ? BX1 : BX2);
8739 for (y = BY1; y <= BY2; y++)
8740 DrawScreenField(x, y);
8745 y = (dy == 1 ? BY1 : BY2);
8746 for (x = BX1; x <= BX2; x++)
8747 DrawScreenField(x, y);
8750 redraw_mask |= REDRAW_FIELD;
8754 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8756 int nextx = x + dx, nexty = y + dy;
8757 int element = Feld[x][y];
8760 element != EL_SP_PORT_LEFT &&
8761 element != EL_SP_GRAVITY_PORT_LEFT &&
8762 element != EL_SP_PORT_HORIZONTAL &&
8763 element != EL_SP_PORT_ANY) ||
8765 element != EL_SP_PORT_RIGHT &&
8766 element != EL_SP_GRAVITY_PORT_RIGHT &&
8767 element != EL_SP_PORT_HORIZONTAL &&
8768 element != EL_SP_PORT_ANY) ||
8770 element != EL_SP_PORT_UP &&
8771 element != EL_SP_GRAVITY_PORT_UP &&
8772 element != EL_SP_PORT_VERTICAL &&
8773 element != EL_SP_PORT_ANY) ||
8775 element != EL_SP_PORT_DOWN &&
8776 element != EL_SP_GRAVITY_PORT_DOWN &&
8777 element != EL_SP_PORT_VERTICAL &&
8778 element != EL_SP_PORT_ANY) ||
8779 !IN_LEV_FIELD(nextx, nexty) ||
8780 !IS_FREE(nextx, nexty))
8787 static boolean canFallDown(struct PlayerInfo *player)
8789 int jx = player->jx, jy = player->jy;
8791 return (IN_LEV_FIELD(jx, jy + 1) &&
8792 (IS_FREE(jx, jy + 1) ||
8793 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8794 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8795 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8798 static boolean canPassField(int x, int y, int move_dir)
8800 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8801 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8802 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8805 int element = Feld[x][y];
8807 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8808 !CAN_MOVE(element) &&
8809 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8810 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8811 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8814 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8816 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8817 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8818 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8822 int nextx = newx + dx;
8823 int nexty = newy + dy;
8827 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8828 (IS_DIGGABLE(Feld[newx][newy]) ||
8829 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8830 canPassField(newx, newy, move_dir)));
8832 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8833 (IS_DIGGABLE(Feld[newx][newy]) ||
8834 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8835 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
8836 !CAN_MOVE(Feld[newx][newy]) &&
8837 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8838 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8839 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
8843 static void CheckGravityMovement(struct PlayerInfo *player)
8845 if (game.gravity && !player->programmed_action)
8848 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8849 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8851 int move_dir_horizontal = player->action & MV_HORIZONTAL;
8852 int move_dir_vertical = player->action & MV_VERTICAL;
8856 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8858 boolean player_is_snapping = player->action & JOY_BUTTON_1;
8861 int jx = player->jx, jy = player->jy;
8863 boolean player_is_moving_to_valid_field =
8864 (!player_is_snapping &&
8865 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8866 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8870 (player->last_move_dir & MV_HORIZONTAL ?
8871 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8872 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8876 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8877 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8878 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8879 int new_jx = jx + dx, new_jy = jy + dy;
8880 int nextx = new_jx + dx, nexty = new_jy + dy;
8886 boolean player_can_fall_down = canFallDown(player);
8888 boolean player_can_fall_down =
8889 (IN_LEV_FIELD(jx, jy + 1) &&
8890 (IS_FREE(jx, jy + 1) ||
8891 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
8895 boolean player_can_fall_down =
8896 (IN_LEV_FIELD(jx, jy + 1) &&
8897 (IS_FREE(jx, jy + 1)));
8901 boolean player_is_moving_to_valid_field =
8904 !player_is_snapping &&
8908 IN_LEV_FIELD(new_jx, new_jy) &&
8909 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
8910 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8911 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
8912 IN_LEV_FIELD(nextx, nexty) &&
8913 element_info[Feld[nextx][nexty]].access_direction & move_dir))
8915 IN_LEV_FIELD(new_jx, new_jy) &&
8916 (Feld[new_jx][new_jy] == EL_SP_BASE ||
8917 Feld[new_jx][new_jy] == EL_SAND ||
8918 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8919 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
8920 /* !!! extend EL_SAND to anything diggable !!! */
8926 boolean player_is_standing_on_valid_field =
8927 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8928 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
8932 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
8933 player_can_fall_down,
8934 player_is_standing_on_valid_field,
8935 player_is_moving_to_valid_field,
8936 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
8937 player->effective_action,
8938 player->can_fall_into_acid);
8941 if (player_can_fall_down &&
8943 !player_is_standing_on_valid_field &&
8945 !player_is_moving_to_valid_field)
8948 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
8949 jx, jy, FrameCounter);
8952 player->programmed_action = MV_DOWN;
8957 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8960 return CheckGravityMovement(player);
8963 if (game.gravity && !player->programmed_action)
8965 int jx = player->jx, jy = player->jy;
8966 boolean field_under_player_is_free =
8967 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8968 boolean player_is_standing_on_valid_field =
8969 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8970 (IS_WALKABLE(Feld[jx][jy]) &&
8971 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8973 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8974 player->programmed_action = MV_DOWN;
8980 -----------------------------------------------------------------------------
8981 dx, dy: direction (non-diagonal) to try to move the player to
8982 real_dx, real_dy: direction as read from input device (can be diagonal)
8985 boolean MovePlayerOneStep(struct PlayerInfo *player,
8986 int dx, int dy, int real_dx, int real_dy)
8989 static int trigger_sides[4][2] =
8991 /* enter side leave side */
8992 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8993 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8994 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8995 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8997 int move_direction = (dx == -1 ? MV_LEFT :
8998 dx == +1 ? MV_RIGHT :
9000 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9001 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9002 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9004 int jx = player->jx, jy = player->jy;
9005 int new_jx = jx + dx, new_jy = jy + dy;
9009 if (!player->active || (!dx && !dy))
9010 return MF_NO_ACTION;
9012 player->MovDir = (dx < 0 ? MV_LEFT :
9015 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9017 if (!IN_LEV_FIELD(new_jx, new_jy))
9018 return MF_NO_ACTION;
9020 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9021 return MF_NO_ACTION;
9024 element = MovingOrBlocked2Element(new_jx, new_jy);
9026 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9029 if (DONT_RUN_INTO(element))
9031 if (element == EL_ACID && dx == 0 && dy == 1)
9033 SplashAcid(new_jx, new_jy);
9034 Feld[jx][jy] = EL_PLAYER_1;
9035 InitMovingField(jx, jy, MV_DOWN);
9036 Store[jx][jy] = EL_ACID;
9037 ContinueMoving(jx, jy);
9041 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9046 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9047 if (can_move != MF_MOVING)
9050 /* check if DigField() has caused relocation of the player */
9051 if (player->jx != jx || player->jy != jy)
9052 return MF_NO_ACTION;
9054 StorePlayer[jx][jy] = 0;
9055 player->last_jx = jx;
9056 player->last_jy = jy;
9057 player->jx = new_jx;
9058 player->jy = new_jy;
9059 StorePlayer[new_jx][new_jy] = player->element_nr;
9062 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9064 player->step_counter++;
9067 player->drop_delay = 0;
9070 PlayerVisit[jx][jy] = FrameCounter;
9072 ScrollPlayer(player, SCROLL_INIT);
9075 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9077 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9079 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9082 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9084 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9085 CE_OTHER_GETS_ENTERED, enter_side);
9086 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9087 CE_ENTERED_BY_PLAYER, enter_side);
9094 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9096 int jx = player->jx, jy = player->jy;
9097 int old_jx = jx, old_jy = jy;
9098 int moved = MF_NO_ACTION;
9101 if (!player->active)
9106 if (player->MovPos == 0)
9108 player->is_moving = FALSE;
9109 player->is_digging = FALSE;
9110 player->is_collecting = FALSE;
9111 player->is_snapping = FALSE;
9112 player->is_pushing = FALSE;
9118 if (!player->active || (!dx && !dy))
9123 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9129 if (!FrameReached(&player->move_delay, player->move_delay_value))
9132 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9133 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9139 /* store if player is automatically moved to next field */
9140 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9142 /* remove the last programmed player action */
9143 player->programmed_action = 0;
9147 /* should only happen if pre-1.2 tape recordings are played */
9148 /* this is only for backward compatibility */
9150 int original_move_delay_value = player->move_delay_value;
9153 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9157 /* scroll remaining steps with finest movement resolution */
9158 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9160 while (player->MovPos)
9162 ScrollPlayer(player, SCROLL_GO_ON);
9163 ScrollScreen(NULL, SCROLL_GO_ON);
9169 player->move_delay_value = original_move_delay_value;
9172 if (player->last_move_dir & MV_HORIZONTAL)
9174 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9175 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9179 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9180 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9186 if (moved & MF_MOVING && !ScreenMovPos &&
9187 (player == local_player || !options.network))
9189 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9190 int offset = (setup.scroll_delay ? 3 : 0);
9192 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9194 /* actual player has left the screen -- scroll in that direction */
9195 if (jx != old_jx) /* player has moved horizontally */
9196 scroll_x += (jx - old_jx);
9197 else /* player has moved vertically */
9198 scroll_y += (jy - old_jy);
9202 if (jx != old_jx) /* player has moved horizontally */
9204 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9205 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9206 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9208 /* don't scroll over playfield boundaries */
9209 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9210 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9212 /* don't scroll more than one field at a time */
9213 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9215 /* don't scroll against the player's moving direction */
9216 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9217 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9218 scroll_x = old_scroll_x;
9220 else /* player has moved vertically */
9222 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9223 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9224 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9226 /* don't scroll over playfield boundaries */
9227 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9228 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9230 /* don't scroll more than one field at a time */
9231 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9233 /* don't scroll against the player's moving direction */
9234 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9235 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9236 scroll_y = old_scroll_y;
9240 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9242 if (!options.network && !AllPlayersInVisibleScreen())
9244 scroll_x = old_scroll_x;
9245 scroll_y = old_scroll_y;
9249 ScrollScreen(player, SCROLL_INIT);
9250 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9257 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9259 if (!(moved & MF_MOVING) && !player->is_pushing)
9264 player->StepFrame = 0;
9266 if (moved & MF_MOVING)
9268 if (old_jx != jx && old_jy == jy)
9269 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9270 else if (old_jx == jx && old_jy != jy)
9271 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9273 DrawLevelField(jx, jy); /* for "crumbled sand" */
9275 player->last_move_dir = player->MovDir;
9276 player->is_moving = TRUE;
9278 player->is_snapping = FALSE;
9282 player->is_switching = FALSE;
9285 player->is_dropping = FALSE;
9289 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9292 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9295 static int trigger_sides[4][2] =
9297 /* enter side leave side */
9298 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9299 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9300 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9301 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9303 int move_direction = player->MovDir;
9304 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9305 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9308 /* !!! TEST ONLY !!! */
9309 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9310 CheckElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9312 player->index_bit, leave_side);
9314 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9316 player->index_bit, leave_side);
9318 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9319 CheckElementChangeByPlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9320 player->index_bit, enter_side);
9322 CheckTriggeredElementChangeByPlayer(jx, jy, Feld[jx][jy],
9323 CE_OTHER_GETS_ENTERED,
9324 player->index_bit, enter_side);
9334 CheckGravityMovementWhenNotMoving(player);
9337 player->last_move_dir = MV_NO_MOVING;
9339 player->is_moving = FALSE;
9342 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9344 TestIfHeroTouchesBadThing(jx, jy);
9345 TestIfPlayerTouchesCustomElement(jx, jy);
9348 if (!player->active)
9354 void ScrollPlayer(struct PlayerInfo *player, int mode)
9356 int jx = player->jx, jy = player->jy;
9357 int last_jx = player->last_jx, last_jy = player->last_jy;
9358 int move_stepsize = TILEX / player->move_delay_value;
9360 if (!player->active || !player->MovPos)
9363 if (mode == SCROLL_INIT)
9365 player->actual_frame_counter = FrameCounter;
9366 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9368 if (Feld[last_jx][last_jy] == EL_EMPTY)
9369 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9377 else if (!FrameReached(&player->actual_frame_counter, 1))
9380 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9381 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9383 if (!player->block_last_field &&
9384 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9385 Feld[last_jx][last_jy] = EL_EMPTY;
9387 /* before DrawPlayer() to draw correct player graphic for this case */
9388 if (player->MovPos == 0)
9389 CheckGravityMovement(player);
9392 DrawPlayer(player); /* needed here only to cleanup last field */
9395 if (player->MovPos == 0) /* player reached destination field */
9398 if (player->move_delay_reset_counter > 0)
9400 player->move_delay_reset_counter--;
9402 if (player->move_delay_reset_counter == 0)
9404 /* continue with normal speed after quickly moving through gate */
9405 HALVE_PLAYER_SPEED(player);
9407 /* be able to make the next move without delay */
9408 player->move_delay = 0;
9412 if (IS_PASSABLE(Feld[last_jx][last_jy]))
9414 /* continue with normal speed after quickly moving through gate */
9415 HALVE_PLAYER_SPEED(player);
9417 /* be able to make the next move without delay */
9418 player->move_delay = 0;
9422 if (player->block_last_field &&
9423 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9424 Feld[last_jx][last_jy] = EL_EMPTY;
9426 player->last_jx = jx;
9427 player->last_jy = jy;
9429 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9430 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9431 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9433 DrawPlayer(player); /* needed here only to cleanup last field */
9436 if (local_player->friends_still_needed == 0 ||
9437 IS_SP_ELEMENT(Feld[jx][jy]))
9438 player->LevelSolved = player->GameOver = TRUE;
9442 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9443 /* this breaks one level: "machine", level 000 */
9445 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9448 static int trigger_sides[4][2] =
9450 /* enter side leave side */
9451 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9452 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9453 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9454 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9456 int move_direction = player->MovDir;
9457 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9458 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9459 int old_jx = last_jx;
9460 int old_jy = last_jy;
9463 /* !!! TEST ONLY !!! */
9464 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9465 CheckElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9467 player->index_bit, leave_side);
9469 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9471 player->index_bit, leave_side);
9473 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9474 CheckElementChangeByPlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9475 player->index_bit, enter_side);
9477 CheckTriggeredElementChangeByPlayer(jx, jy, Feld[jx][jy],
9478 CE_OTHER_GETS_ENTERED,
9479 player->index_bit, enter_side);
9485 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9487 TestIfHeroTouchesBadThing(jx, jy);
9488 TestIfPlayerTouchesCustomElement(jx, jy);
9490 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9493 if (!player->active)
9497 if (level.use_step_counter)
9503 for (i = 0; i < MAX_PLAYERS; i++)
9505 struct PlayerInfo *player = &stored_player[i];
9507 if (SHIELD_ON(player))
9509 player->shield_normal_time_left--;
9511 if (player->shield_deadly_time_left > 0)
9512 player->shield_deadly_time_left--;
9520 if (TimeLeft <= 10 && setup.time_limit)
9521 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9523 DrawGameValue_Time(TimeLeft);
9525 if (!TimeLeft && setup.time_limit)
9526 for (i = 0; i < MAX_PLAYERS; i++)
9527 KillHero(&stored_player[i]);
9529 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9530 DrawGameValue_Time(TimePlayed);
9533 if (tape.single_step && tape.recording && !tape.pausing &&
9534 !player->programmed_action)
9535 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9539 void ScrollScreen(struct PlayerInfo *player, int mode)
9541 static unsigned long screen_frame_counter = 0;
9543 if (mode == SCROLL_INIT)
9545 /* set scrolling step size according to actual player's moving speed */
9546 ScrollStepSize = TILEX / player->move_delay_value;
9548 screen_frame_counter = FrameCounter;
9549 ScreenMovDir = player->MovDir;
9550 ScreenMovPos = player->MovPos;
9551 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9554 else if (!FrameReached(&screen_frame_counter, 1))
9559 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9560 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9561 redraw_mask |= REDRAW_FIELD;
9564 ScreenMovDir = MV_NO_MOVING;
9567 void TestIfPlayerTouchesCustomElement(int x, int y)
9569 static int xy[4][2] =
9576 static int trigger_sides[4][2] =
9578 /* center side border side */
9579 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9580 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9581 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9582 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9584 static int touch_dir[4] =
9591 int center_element = Feld[x][y]; /* should always be non-moving! */
9594 for (i = 0; i < NUM_DIRECTIONS; i++)
9596 int xx = x + xy[i][0];
9597 int yy = y + xy[i][1];
9598 int center_side = trigger_sides[i][0];
9599 int border_side = trigger_sides[i][1];
9602 if (!IN_LEV_FIELD(xx, yy))
9605 if (IS_PLAYER(x, y))
9607 struct PlayerInfo *player = PLAYERINFO(x, y);
9609 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9610 border_element = Feld[xx][yy]; /* may be moving! */
9611 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9612 border_element = Feld[xx][yy];
9613 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9614 border_element = MovingOrBlocked2Element(xx, yy);
9616 continue; /* center and border element do not touch */
9619 /* !!! TEST ONLY !!! */
9620 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9621 player->index_bit, border_side);
9622 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9623 CE_OTHER_GETS_TOUCHED,
9624 player->index_bit, border_side);
9626 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9627 CE_OTHER_GETS_TOUCHED,
9628 player->index_bit, border_side);
9629 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9630 player->index_bit, border_side);
9633 else if (IS_PLAYER(xx, yy))
9635 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9637 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9639 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9640 continue; /* center and border element do not touch */
9644 /* !!! TEST ONLY !!! */
9645 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9646 player->index_bit, center_side);
9647 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9648 CE_OTHER_GETS_TOUCHED,
9649 player->index_bit, center_side);
9651 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9652 CE_OTHER_GETS_TOUCHED,
9653 player->index_bit, center_side);
9654 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9655 player->index_bit, center_side);
9663 void TestIfElementTouchesCustomElement(int x, int y)
9665 static int xy[4][2] =
9672 static int trigger_sides[4][2] =
9674 /* center side border side */
9675 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9676 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9677 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9678 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9680 static int touch_dir[4] =
9687 boolean change_center_element = FALSE;
9688 int center_element_change_page = 0;
9689 int center_element = Feld[x][y]; /* should always be non-moving! */
9690 int border_trigger_element;
9693 for (i = 0; i < NUM_DIRECTIONS; i++)
9695 int xx = x + xy[i][0];
9696 int yy = y + xy[i][1];
9697 int center_side = trigger_sides[i][0];
9698 int border_side = trigger_sides[i][1];
9701 if (!IN_LEV_FIELD(xx, yy))
9704 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9705 border_element = Feld[xx][yy]; /* may be moving! */
9706 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9707 border_element = Feld[xx][yy];
9708 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9709 border_element = MovingOrBlocked2Element(xx, yy);
9711 continue; /* center and border element do not touch */
9713 /* check for change of center element (but change it only once) */
9714 if (IS_CUSTOM_ELEMENT(center_element) &&
9715 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9716 !change_center_element)
9718 for (j = 0; j < element_info[center_element].num_change_pages; j++)
9720 struct ElementChangeInfo *change =
9721 &element_info[center_element].change_page[j];
9723 if (change->can_change &&
9724 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9725 change->trigger_side & border_side &&
9727 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9729 change->trigger_element == border_element
9733 change_center_element = TRUE;
9734 center_element_change_page = j;
9735 border_trigger_element = border_element;
9742 /* check for change of border element */
9743 if (IS_CUSTOM_ELEMENT(border_element) &&
9744 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9746 for (j = 0; j < element_info[border_element].num_change_pages; j++)
9748 struct ElementChangeInfo *change =
9749 &element_info[border_element].change_page[j];
9751 if (change->can_change &&
9752 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9753 change->trigger_side & center_side &&
9755 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9757 change->trigger_element == center_element
9762 printf("::: border_element %d, %d\n", x, y);
9765 CheckElementChangeByPage(xx, yy, border_element, center_element,
9766 CE_OTHER_IS_TOUCHING, j);
9773 if (change_center_element)
9776 printf("::: center_element %d, %d\n", x, y);
9779 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
9780 CE_OTHER_IS_TOUCHING, center_element_change_page);
9784 void TestIfElementHitsCustomElement(int x, int y, int direction)
9786 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9787 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9788 int hitx = x + dx, hity = y + dy;
9789 int hitting_element = Feld[x][y];
9790 int touched_element;
9792 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9793 !IS_FREE(hitx, hity) &&
9794 (!IS_MOVING(hitx, hity) ||
9795 MovDir[hitx][hity] != direction ||
9796 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9799 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9803 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9807 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9808 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9810 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9811 CE_HITTING_SOMETHING, direction);
9813 if (IN_LEV_FIELD(hitx, hity))
9815 int opposite_direction = MV_DIR_OPPOSITE(direction);
9816 int hitting_side = direction;
9817 int touched_side = opposite_direction;
9819 int touched_element = MovingOrBlocked2Element(hitx, hity);
9822 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9823 MovDir[hitx][hity] != direction ||
9824 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9833 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9834 CE_HIT_BY_SOMETHING, opposite_direction);
9836 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9837 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
9839 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9841 struct ElementChangeInfo *change =
9842 &element_info[hitting_element].change_page[i];
9844 if (change->can_change &&
9845 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
9846 change->trigger_side & touched_side &&
9849 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9851 change->trigger_element == touched_element
9855 CheckElementChangeByPage(x, y, hitting_element, touched_element,
9856 CE_OTHER_IS_HITTING, i);
9862 if (IS_CUSTOM_ELEMENT(touched_element) &&
9863 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
9865 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9867 struct ElementChangeInfo *change =
9868 &element_info[touched_element].change_page[i];
9870 if (change->can_change &&
9871 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
9872 change->trigger_side & hitting_side &&
9874 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9876 change->trigger_element == hitting_element
9880 CheckElementChangeByPage(hitx, hity, touched_element,
9881 hitting_element, CE_OTHER_GETS_HIT, i);
9891 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9893 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9894 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9895 int hitx = x + dx, hity = y + dy;
9896 int hitting_element = Feld[x][y];
9897 int touched_element;
9899 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9900 !IS_FREE(hitx, hity) &&
9901 (!IS_MOVING(hitx, hity) ||
9902 MovDir[hitx][hity] != direction ||
9903 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9906 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9910 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9914 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9915 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9917 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9918 EP_CAN_SMASH_EVERYTHING, direction);
9920 if (IN_LEV_FIELD(hitx, hity))
9922 int opposite_direction = MV_DIR_OPPOSITE(direction);
9923 int hitting_side = direction;
9924 int touched_side = opposite_direction;
9926 int touched_element = MovingOrBlocked2Element(hitx, hity);
9929 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9930 MovDir[hitx][hity] != direction ||
9931 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9940 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9941 CE_SMASHED_BY_SOMETHING, opposite_direction);
9943 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9944 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
9946 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9948 struct ElementChangeInfo *change =
9949 &element_info[hitting_element].change_page[i];
9951 if (change->can_change &&
9952 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
9953 change->trigger_side & touched_side &&
9956 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9958 change->trigger_element == touched_element
9962 CheckElementChangeByPage(x, y, hitting_element, touched_element,
9963 CE_OTHER_IS_SMASHING, i);
9969 if (IS_CUSTOM_ELEMENT(touched_element) &&
9970 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
9972 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9974 struct ElementChangeInfo *change =
9975 &element_info[touched_element].change_page[i];
9977 if (change->can_change &&
9978 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
9979 change->trigger_side & hitting_side &&
9981 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9983 change->trigger_element == hitting_element
9987 CheckElementChangeByPage(hitx, hity, touched_element,
9988 hitting_element, CE_OTHER_GETS_SMASHED,i);
9998 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10000 int i, kill_x = -1, kill_y = -1;
10001 int bad_element = -1;
10002 static int test_xy[4][2] =
10009 static int test_dir[4] =
10017 for (i = 0; i < NUM_DIRECTIONS; i++)
10019 int test_x, test_y, test_move_dir, test_element;
10021 test_x = good_x + test_xy[i][0];
10022 test_y = good_y + test_xy[i][1];
10024 if (!IN_LEV_FIELD(test_x, test_y))
10028 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10031 test_element = Feld[test_x][test_y];
10033 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10036 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10037 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10039 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10040 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10044 bad_element = test_element;
10050 if (kill_x != -1 || kill_y != -1)
10052 if (IS_PLAYER(good_x, good_y))
10054 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10057 if (player->shield_deadly_time_left > 0 &&
10058 !IS_INDESTRUCTIBLE(bad_element))
10059 Bang(kill_x, kill_y);
10060 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10063 if (player->shield_deadly_time_left > 0)
10064 Bang(kill_x, kill_y);
10065 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10070 Bang(good_x, good_y);
10074 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10076 int i, kill_x = -1, kill_y = -1;
10077 int bad_element = Feld[bad_x][bad_y];
10078 static int test_xy[4][2] =
10085 static int touch_dir[4] =
10087 MV_LEFT | MV_RIGHT,
10092 static int test_dir[4] =
10100 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10103 for (i = 0; i < NUM_DIRECTIONS; i++)
10105 int test_x, test_y, test_move_dir, test_element;
10107 test_x = bad_x + test_xy[i][0];
10108 test_y = bad_y + test_xy[i][1];
10109 if (!IN_LEV_FIELD(test_x, test_y))
10113 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10115 test_element = Feld[test_x][test_y];
10117 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10118 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10120 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10121 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10123 /* good thing is player or penguin that does not move away */
10124 if (IS_PLAYER(test_x, test_y))
10126 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10128 if (bad_element == EL_ROBOT && player->is_moving)
10129 continue; /* robot does not kill player if he is moving */
10131 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10133 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10134 continue; /* center and border element do not touch */
10141 else if (test_element == EL_PENGUIN)
10150 if (kill_x != -1 || kill_y != -1)
10152 if (IS_PLAYER(kill_x, kill_y))
10154 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10157 if (player->shield_deadly_time_left > 0 &&
10158 !IS_INDESTRUCTIBLE(bad_element))
10159 Bang(bad_x, bad_y);
10160 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10163 if (player->shield_deadly_time_left > 0)
10164 Bang(bad_x, bad_y);
10165 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10170 Bang(kill_x, kill_y);
10174 void TestIfHeroTouchesBadThing(int x, int y)
10176 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10179 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10181 TestIfGoodThingHitsBadThing(x, y, move_dir);
10184 void TestIfBadThingTouchesHero(int x, int y)
10186 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10189 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10191 TestIfBadThingHitsGoodThing(x, y, move_dir);
10194 void TestIfFriendTouchesBadThing(int x, int y)
10196 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10199 void TestIfBadThingTouchesFriend(int x, int y)
10201 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10204 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10206 int i, kill_x = bad_x, kill_y = bad_y;
10207 static int xy[4][2] =
10215 for (i = 0; i < NUM_DIRECTIONS; i++)
10219 x = bad_x + xy[i][0];
10220 y = bad_y + xy[i][1];
10221 if (!IN_LEV_FIELD(x, y))
10224 element = Feld[x][y];
10225 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10226 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10234 if (kill_x != bad_x || kill_y != bad_y)
10235 Bang(bad_x, bad_y);
10238 void KillHero(struct PlayerInfo *player)
10240 int jx = player->jx, jy = player->jy;
10242 if (!player->active)
10245 /* remove accessible field at the player's position */
10246 Feld[jx][jy] = EL_EMPTY;
10248 /* deactivate shield (else Bang()/Explode() would not work right) */
10249 player->shield_normal_time_left = 0;
10250 player->shield_deadly_time_left = 0;
10256 static void KillHeroUnlessEnemyProtected(int x, int y)
10258 if (!PLAYER_ENEMY_PROTECTED(x, y))
10259 KillHero(PLAYERINFO(x, y));
10262 static void KillHeroUnlessExplosionProtected(int x, int y)
10264 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10265 KillHero(PLAYERINFO(x, y));
10268 void BuryHero(struct PlayerInfo *player)
10270 int jx = player->jx, jy = player->jy;
10272 if (!player->active)
10276 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10278 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10280 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10282 player->GameOver = TRUE;
10283 RemoveHero(player);
10286 void RemoveHero(struct PlayerInfo *player)
10288 int jx = player->jx, jy = player->jy;
10289 int i, found = FALSE;
10291 player->present = FALSE;
10292 player->active = FALSE;
10294 if (!ExplodeField[jx][jy])
10295 StorePlayer[jx][jy] = 0;
10297 for (i = 0; i < MAX_PLAYERS; i++)
10298 if (stored_player[i].active)
10302 AllPlayersGone = TRUE;
10309 =============================================================================
10310 checkDiagonalPushing()
10311 -----------------------------------------------------------------------------
10312 check if diagonal input device direction results in pushing of object
10313 (by checking if the alternative direction is walkable, diggable, ...)
10314 =============================================================================
10317 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10318 int x, int y, int real_dx, int real_dy)
10320 int jx, jy, dx, dy, xx, yy;
10322 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10325 /* diagonal direction: check alternative direction */
10330 xx = jx + (dx == 0 ? real_dx : 0);
10331 yy = jy + (dy == 0 ? real_dy : 0);
10333 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10337 =============================================================================
10339 -----------------------------------------------------------------------------
10340 x, y: field next to player (non-diagonal) to try to dig to
10341 real_dx, real_dy: direction as read from input device (can be diagonal)
10342 =============================================================================
10345 int DigField(struct PlayerInfo *player,
10346 int oldx, int oldy, int x, int y,
10347 int real_dx, int real_dy, int mode)
10349 static int trigger_sides[4] =
10351 CH_SIDE_RIGHT, /* moving left */
10352 CH_SIDE_LEFT, /* moving right */
10353 CH_SIDE_BOTTOM, /* moving up */
10354 CH_SIDE_TOP, /* moving down */
10357 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10359 int jx = oldx, jy = oldy;
10360 int dx = x - jx, dy = y - jy;
10361 int nextx = x + dx, nexty = y + dy;
10362 int move_direction = (dx == -1 ? MV_LEFT :
10363 dx == +1 ? MV_RIGHT :
10365 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10366 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10367 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10368 int old_element = Feld[jx][jy];
10371 if (player->MovPos == 0)
10373 player->is_digging = FALSE;
10374 player->is_collecting = FALSE;
10377 if (player->MovPos == 0) /* last pushing move finished */
10378 player->is_pushing = FALSE;
10380 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10382 player->is_switching = FALSE;
10383 player->push_delay = 0;
10385 return MF_NO_ACTION;
10388 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10389 return MF_NO_ACTION;
10394 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10396 if (IS_TUBE(Feld[jx][jy]) ||
10397 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10401 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10402 int tube_leave_directions[][2] =
10404 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10405 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10406 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10407 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
10408 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
10409 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
10410 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
10411 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
10412 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
10413 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
10414 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
10415 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10418 while (tube_leave_directions[i][0] != tube_element)
10421 if (tube_leave_directions[i][0] == -1) /* should not happen */
10425 if (!(tube_leave_directions[i][1] & move_direction))
10426 return MF_NO_ACTION; /* tube has no opening in this direction */
10431 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10432 old_element = Back[jx][jy];
10436 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10437 return MF_NO_ACTION; /* field has no opening in this direction */
10439 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10440 return MF_NO_ACTION; /* field has no opening in this direction */
10442 element = Feld[x][y];
10444 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10445 game.engine_version >= VERSION_IDENT(2,2,0,0))
10446 return MF_NO_ACTION;
10451 case EL_SP_PORT_LEFT:
10452 case EL_SP_PORT_RIGHT:
10453 case EL_SP_PORT_UP:
10454 case EL_SP_PORT_DOWN:
10455 case EL_SP_PORT_HORIZONTAL:
10456 case EL_SP_PORT_VERTICAL:
10457 case EL_SP_PORT_ANY:
10458 case EL_SP_GRAVITY_PORT_LEFT:
10459 case EL_SP_GRAVITY_PORT_RIGHT:
10460 case EL_SP_GRAVITY_PORT_UP:
10461 case EL_SP_GRAVITY_PORT_DOWN:
10463 if (!canEnterSupaplexPort(x, y, dx, dy))
10464 return MF_NO_ACTION;
10467 element != EL_SP_PORT_LEFT &&
10468 element != EL_SP_GRAVITY_PORT_LEFT &&
10469 element != EL_SP_PORT_HORIZONTAL &&
10470 element != EL_SP_PORT_ANY) ||
10472 element != EL_SP_PORT_RIGHT &&
10473 element != EL_SP_GRAVITY_PORT_RIGHT &&
10474 element != EL_SP_PORT_HORIZONTAL &&
10475 element != EL_SP_PORT_ANY) ||
10477 element != EL_SP_PORT_UP &&
10478 element != EL_SP_GRAVITY_PORT_UP &&
10479 element != EL_SP_PORT_VERTICAL &&
10480 element != EL_SP_PORT_ANY) ||
10482 element != EL_SP_PORT_DOWN &&
10483 element != EL_SP_GRAVITY_PORT_DOWN &&
10484 element != EL_SP_PORT_VERTICAL &&
10485 element != EL_SP_PORT_ANY) ||
10486 !IN_LEV_FIELD(nextx, nexty) ||
10487 !IS_FREE(nextx, nexty))
10488 return MF_NO_ACTION;
10491 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10492 element == EL_SP_GRAVITY_PORT_RIGHT ||
10493 element == EL_SP_GRAVITY_PORT_UP ||
10494 element == EL_SP_GRAVITY_PORT_DOWN)
10495 game.gravity = !game.gravity;
10497 /* automatically move to the next field with double speed */
10498 player->programmed_action = move_direction;
10500 if (player->move_delay_reset_counter == 0)
10502 player->move_delay_reset_counter = 2; /* two double speed steps */
10504 DOUBLE_PLAYER_SPEED(player);
10507 player->move_delay_reset_counter = 2;
10509 DOUBLE_PLAYER_SPEED(player);
10513 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10516 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10522 case EL_TUBE_VERTICAL:
10523 case EL_TUBE_HORIZONTAL:
10524 case EL_TUBE_VERTICAL_LEFT:
10525 case EL_TUBE_VERTICAL_RIGHT:
10526 case EL_TUBE_HORIZONTAL_UP:
10527 case EL_TUBE_HORIZONTAL_DOWN:
10528 case EL_TUBE_LEFT_UP:
10529 case EL_TUBE_LEFT_DOWN:
10530 case EL_TUBE_RIGHT_UP:
10531 case EL_TUBE_RIGHT_DOWN:
10534 int tube_enter_directions[][2] =
10536 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10537 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10538 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10539 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
10540 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
10541 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
10542 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
10543 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
10544 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
10545 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
10546 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
10547 { -1, MV_NO_MOVING }
10550 while (tube_enter_directions[i][0] != element)
10553 if (tube_enter_directions[i][0] == -1) /* should not happen */
10557 if (!(tube_enter_directions[i][1] & move_direction))
10558 return MF_NO_ACTION; /* tube has no opening in this direction */
10560 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10568 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10570 if (IS_WALKABLE(element))
10573 int sound_action = ACTION_WALKING;
10576 if (!ACCESS_FROM(element, opposite_direction))
10577 return MF_NO_ACTION; /* field not accessible from this direction */
10581 if (element == EL_EMPTY_SPACE &&
10582 game.gravity && !player->is_auto_moving &&
10583 canFallDown(player) && move_direction != MV_DOWN)
10584 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10587 if (IS_GATE(element))
10589 if (!player->key[element - EL_GATE_1])
10590 return MF_NO_ACTION;
10592 else if (IS_GATE_GRAY(element))
10594 if (!player->key[element - EL_GATE_1_GRAY])
10595 return MF_NO_ACTION;
10597 else if (element == EL_EXIT_OPEN ||
10598 element == EL_SP_EXIT_OPEN ||
10599 element == EL_SP_EXIT_OPENING)
10601 sound_action = ACTION_PASSING; /* player is passing exit */
10603 else if (element == EL_EMPTY)
10605 sound_action = ACTION_MOVING; /* nothing to walk on */
10608 /* play sound from background or player, whatever is available */
10609 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
10610 PlayLevelSoundElementAction(x, y, element, sound_action);
10612 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10617 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
10619 else if (IS_PASSABLE(element))
10623 if (!canPassField(x, y, move_direction))
10624 return MF_NO_ACTION;
10629 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
10630 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
10631 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
10632 return MF_NO_ACTION;
10634 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10635 return MF_NO_ACTION;
10640 if (!ACCESS_FROM(element, opposite_direction))
10641 return MF_NO_ACTION; /* field not accessible from this direction */
10643 if (IS_CUSTOM_ELEMENT(element) &&
10644 !ACCESS_FROM(element, opposite_direction))
10645 return MF_NO_ACTION; /* field not accessible from this direction */
10649 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10650 return MF_NO_ACTION;
10655 if (IS_EM_GATE(element))
10657 if (!player->key[element - EL_EM_GATE_1])
10658 return MF_NO_ACTION;
10660 else if (IS_EM_GATE_GRAY(element))
10662 if (!player->key[element - EL_EM_GATE_1_GRAY])
10663 return MF_NO_ACTION;
10665 else if (IS_SP_PORT(element))
10667 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10668 element == EL_SP_GRAVITY_PORT_RIGHT ||
10669 element == EL_SP_GRAVITY_PORT_UP ||
10670 element == EL_SP_GRAVITY_PORT_DOWN)
10671 game.gravity = !game.gravity;
10674 /* automatically move to the next field with double speed */
10675 player->programmed_action = move_direction;
10677 if (player->move_delay_reset_counter == 0)
10679 player->move_delay_reset_counter = 2; /* two double speed steps */
10681 DOUBLE_PLAYER_SPEED(player);
10684 player->move_delay_reset_counter = 2;
10686 DOUBLE_PLAYER_SPEED(player);
10689 PlayLevelSoundAction(x, y, ACTION_PASSING);
10693 else if (IS_DIGGABLE(element))
10697 if (mode != DF_SNAP)
10700 GfxElement[x][y] = GFX_ELEMENT(element);
10703 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
10705 player->is_digging = TRUE;
10708 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10710 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
10711 player->index_bit, dig_side);
10714 if (mode == DF_SNAP)
10715 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10720 else if (IS_COLLECTIBLE(element))
10724 if (mode != DF_SNAP)
10726 GfxElement[x][y] = element;
10727 player->is_collecting = TRUE;
10730 if (element == EL_SPEED_PILL)
10731 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10732 else if (element == EL_EXTRA_TIME && level.time > 0)
10735 DrawGameValue_Time(TimeLeft);
10737 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10739 player->shield_normal_time_left += 10;
10740 if (element == EL_SHIELD_DEADLY)
10741 player->shield_deadly_time_left += 10;
10743 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10745 if (player->inventory_size < MAX_INVENTORY_SIZE)
10746 player->inventory_element[player->inventory_size++] = element;
10748 DrawGameValue_Dynamite(local_player->inventory_size);
10750 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10752 player->dynabomb_count++;
10753 player->dynabombs_left++;
10755 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10757 player->dynabomb_size++;
10759 else if (element == EL_DYNABOMB_INCREASE_POWER)
10761 player->dynabomb_xl = TRUE;
10763 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
10764 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
10766 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
10767 element - EL_KEY_1 : element - EL_EM_KEY_1);
10769 player->key[key_nr] = TRUE;
10771 DrawGameValue_Keys(player);
10773 redraw_mask |= REDRAW_DOOR_1;
10775 else if (IS_ENVELOPE(element))
10778 player->show_envelope = element;
10780 ShowEnvelope(element - EL_ENVELOPE_1);
10783 else if (IS_DROPPABLE(element) ||
10784 IS_THROWABLE(element)) /* can be collected and dropped */
10788 if (element_info[element].collect_count == 0)
10789 player->inventory_infinite_element = element;
10791 for (i = 0; i < element_info[element].collect_count; i++)
10792 if (player->inventory_size < MAX_INVENTORY_SIZE)
10793 player->inventory_element[player->inventory_size++] = element;
10795 DrawGameValue_Dynamite(local_player->inventory_size);
10797 else if (element_info[element].collect_count > 0)
10799 local_player->gems_still_needed -=
10800 element_info[element].collect_count;
10801 if (local_player->gems_still_needed < 0)
10802 local_player->gems_still_needed = 0;
10804 DrawGameValue_Emeralds(local_player->gems_still_needed);
10807 RaiseScoreElement(element);
10808 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10810 CheckTriggeredElementChangeByPlayer(x, y, element,
10811 CE_OTHER_GETS_COLLECTED,
10812 player->index_bit, dig_side);
10815 if (mode == DF_SNAP)
10816 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10821 else if (IS_PUSHABLE(element))
10823 if (mode == DF_SNAP && element != EL_BD_ROCK)
10824 return MF_NO_ACTION;
10826 if (CAN_FALL(element) && dy)
10827 return MF_NO_ACTION;
10829 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10830 !(element == EL_SPRING && level.use_spring_bug))
10831 return MF_NO_ACTION;
10834 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10835 ((move_direction & MV_VERTICAL &&
10836 ((element_info[element].move_pattern & MV_LEFT &&
10837 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10838 (element_info[element].move_pattern & MV_RIGHT &&
10839 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10840 (move_direction & MV_HORIZONTAL &&
10841 ((element_info[element].move_pattern & MV_UP &&
10842 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10843 (element_info[element].move_pattern & MV_DOWN &&
10844 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10845 return MF_NO_ACTION;
10849 /* do not push elements already moving away faster than player */
10850 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10851 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10852 return MF_NO_ACTION;
10854 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
10855 return MF_NO_ACTION;
10859 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10861 if (player->push_delay_value == -1)
10862 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10864 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10866 if (!player->is_pushing)
10867 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10871 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
10872 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
10873 !player_is_pushing))
10874 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10877 if (!player->is_pushing &&
10878 game.engine_version >= VERSION_IDENT(2,2,0,7))
10879 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10883 printf("::: push delay: %ld [%d, %d] [%d]\n",
10884 player->push_delay_value, FrameCounter, game.engine_version,
10885 player->is_pushing);
10888 player->is_pushing = TRUE;
10890 if (!(IN_LEV_FIELD(nextx, nexty) &&
10891 (IS_FREE(nextx, nexty) ||
10892 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10893 IS_SB_ELEMENT(element)))))
10894 return MF_NO_ACTION;
10896 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10897 return MF_NO_ACTION;
10899 if (player->push_delay == 0) /* new pushing; restart delay */
10900 player->push_delay = FrameCounter;
10902 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
10903 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10904 element != EL_SPRING && element != EL_BALLOON)
10906 /* make sure that there is no move delay before next try to push */
10907 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10908 player->move_delay = INITIAL_MOVE_DELAY_OFF;
10910 return MF_NO_ACTION;
10914 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
10917 if (IS_SB_ELEMENT(element))
10919 if (element == EL_SOKOBAN_FIELD_FULL)
10921 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10922 local_player->sokobanfields_still_needed++;
10925 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10927 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10928 local_player->sokobanfields_still_needed--;
10931 Feld[x][y] = EL_SOKOBAN_OBJECT;
10933 if (Back[x][y] == Back[nextx][nexty])
10934 PlayLevelSoundAction(x, y, ACTION_PUSHING);
10935 else if (Back[x][y] != 0)
10936 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10939 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10942 if (local_player->sokobanfields_still_needed == 0 &&
10943 game.emulation == EMU_SOKOBAN)
10945 player->LevelSolved = player->GameOver = TRUE;
10946 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10950 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10952 InitMovingField(x, y, move_direction);
10953 GfxAction[x][y] = ACTION_PUSHING;
10955 if (mode == DF_SNAP)
10956 ContinueMoving(x, y);
10958 MovPos[x][y] = (dx != 0 ? dx : dy);
10960 Pushed[x][y] = TRUE;
10961 Pushed[nextx][nexty] = TRUE;
10963 if (game.engine_version < VERSION_IDENT(2,2,0,7))
10964 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10966 player->push_delay_value = -1; /* get new value later */
10969 /* check for element change _after_ element has been pushed! */
10973 /* !!! TEST ONLY !!! */
10974 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10975 player->index_bit, dig_side);
10976 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
10977 player->index_bit, dig_side);
10979 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
10980 player->index_bit, dig_side);
10981 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10982 player->index_bit, dig_side);
10988 else if (IS_SWITCHABLE(element))
10990 if (PLAYER_SWITCHING(player, x, y))
10993 player->is_switching = TRUE;
10994 player->switch_x = x;
10995 player->switch_y = y;
10997 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10999 if (element == EL_ROBOT_WHEEL)
11001 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11005 DrawLevelField(x, y);
11007 else if (element == EL_SP_TERMINAL)
11011 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11013 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11015 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11016 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11019 else if (IS_BELT_SWITCH(element))
11021 ToggleBeltSwitch(x, y);
11023 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11024 element == EL_SWITCHGATE_SWITCH_DOWN)
11026 ToggleSwitchgateSwitch(x, y);
11028 else if (element == EL_LIGHT_SWITCH ||
11029 element == EL_LIGHT_SWITCH_ACTIVE)
11031 ToggleLightSwitch(x, y);
11034 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11035 SND_LIGHT_SWITCH_ACTIVATING :
11036 SND_LIGHT_SWITCH_DEACTIVATING);
11039 else if (element == EL_TIMEGATE_SWITCH)
11041 ActivateTimegateSwitch(x, y);
11043 else if (element == EL_BALLOON_SWITCH_LEFT ||
11044 element == EL_BALLOON_SWITCH_RIGHT ||
11045 element == EL_BALLOON_SWITCH_UP ||
11046 element == EL_BALLOON_SWITCH_DOWN ||
11047 element == EL_BALLOON_SWITCH_ANY)
11049 if (element == EL_BALLOON_SWITCH_ANY)
11050 game.balloon_dir = move_direction;
11052 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11053 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11054 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11055 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11058 else if (element == EL_LAMP)
11060 Feld[x][y] = EL_LAMP_ACTIVE;
11061 local_player->lights_still_needed--;
11063 DrawLevelField(x, y);
11065 else if (element == EL_TIME_ORB_FULL)
11067 Feld[x][y] = EL_TIME_ORB_EMPTY;
11069 DrawGameValue_Time(TimeLeft);
11071 DrawLevelField(x, y);
11074 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11082 if (!PLAYER_SWITCHING(player, x, y))
11084 player->is_switching = TRUE;
11085 player->switch_x = x;
11086 player->switch_y = y;
11089 /* !!! TEST ONLY !!! */
11090 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11091 player->index_bit, dig_side);
11092 CheckTriggeredElementChangeByPlayer(x, y, element,
11093 CE_OTHER_IS_SWITCHING,
11094 player->index_bit, dig_side);
11096 CheckTriggeredElementChangeByPlayer(x, y, element,
11097 CE_OTHER_IS_SWITCHING,
11098 player->index_bit, dig_side);
11099 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11100 player->index_bit, dig_side);
11105 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11106 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11107 player->index_bit, dig_side);
11108 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11109 player->index_bit, dig_side);
11111 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11112 player->index_bit, dig_side);
11113 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11114 player->index_bit, dig_side);
11118 return MF_NO_ACTION;
11121 player->push_delay = 0;
11123 if (Feld[x][y] != element) /* really digged/collected something */
11124 player->is_collecting = !player->is_digging;
11129 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11131 int jx = player->jx, jy = player->jy;
11132 int x = jx + dx, y = jy + dy;
11133 int snap_direction = (dx == -1 ? MV_LEFT :
11134 dx == +1 ? MV_RIGHT :
11136 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11139 if (player->MovPos)
11142 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
11146 if (!player->active || !IN_LEV_FIELD(x, y))
11154 if (player->MovPos == 0)
11155 player->is_pushing = FALSE;
11157 player->is_snapping = FALSE;
11159 if (player->MovPos == 0)
11161 player->is_moving = FALSE;
11162 player->is_digging = FALSE;
11163 player->is_collecting = FALSE;
11169 if (player->is_snapping)
11172 player->MovDir = snap_direction;
11175 if (player->MovPos == 0)
11178 player->is_moving = FALSE;
11179 player->is_digging = FALSE;
11180 player->is_collecting = FALSE;
11183 player->is_dropping = FALSE;
11185 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11188 player->is_snapping = TRUE;
11191 if (player->MovPos == 0)
11194 player->is_moving = FALSE;
11195 player->is_digging = FALSE;
11196 player->is_collecting = FALSE;
11199 DrawLevelField(x, y);
11205 boolean DropElement(struct PlayerInfo *player)
11207 static int trigger_sides[4] =
11209 CH_SIDE_LEFT, /* dropping left */
11210 CH_SIDE_RIGHT, /* dropping right */
11211 CH_SIDE_TOP, /* dropping up */
11212 CH_SIDE_BOTTOM, /* dropping down */
11214 int old_element, new_element;
11215 int dropx = player->jx, dropy = player->jy;
11216 int drop_direction = player->MovDir;
11217 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11218 int drop_element = (player->inventory_size > 0 ?
11219 player->inventory_element[player->inventory_size - 1] :
11220 player->inventory_infinite_element != EL_UNDEFINED ?
11221 player->inventory_infinite_element :
11222 player->dynabombs_left > 0 ?
11223 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11226 if (IS_THROWABLE(drop_element))
11228 dropx += GET_DX_FROM_DIR(drop_direction);
11229 dropy += GET_DY_FROM_DIR(drop_direction);
11231 if (!IN_LEV_FIELD(dropx, dropy))
11235 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11236 new_element = drop_element; /* default: no change when dropping */
11238 /* check if player is active, not moving and ready to drop */
11239 if (!player->active || player->MovPos || player->drop_delay > 0)
11242 /* check if player has anything that can be dropped */
11244 if (new_element == EL_UNDEFINED)
11247 if (player->inventory_size == 0 &&
11248 player->inventory_infinite_element == EL_UNDEFINED &&
11249 player->dynabombs_left == 0)
11253 /* check if anything can be dropped at the current position */
11254 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11257 /* collected custom elements can only be dropped on empty fields */
11259 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11262 if (player->inventory_size > 0 &&
11263 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11264 && old_element != EL_EMPTY)
11268 if (old_element != EL_EMPTY)
11269 Back[dropx][dropy] = old_element; /* store old element on this field */
11271 ResetGfxAnimation(dropx, dropy);
11272 ResetRandomAnimationValue(dropx, dropy);
11274 if (player->inventory_size > 0 ||
11275 player->inventory_infinite_element != EL_UNDEFINED)
11277 if (player->inventory_size > 0)
11279 player->inventory_size--;
11282 new_element = player->inventory_element[player->inventory_size];
11285 DrawGameValue_Dynamite(local_player->inventory_size);
11287 if (new_element == EL_DYNAMITE)
11288 new_element = EL_DYNAMITE_ACTIVE;
11289 else if (new_element == EL_SP_DISK_RED)
11290 new_element = EL_SP_DISK_RED_ACTIVE;
11293 Feld[dropx][dropy] = new_element;
11295 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11296 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11297 el2img(Feld[dropx][dropy]), 0);
11299 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11302 /* needed if previous element just changed to "empty" in the last frame */
11303 Changed[dropx][dropy] = 0; /* allow another change */
11307 /* !!! TEST ONLY !!! */
11308 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11309 player->index_bit, drop_side);
11310 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11311 CE_OTHER_GETS_DROPPED,
11312 player->index_bit, drop_side);
11314 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11315 CE_OTHER_GETS_DROPPED,
11316 player->index_bit, drop_side);
11317 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11318 player->index_bit, drop_side);
11321 TestIfElementTouchesCustomElement(dropx, dropy);
11323 else /* player is dropping a dyna bomb */
11325 player->dynabombs_left--;
11328 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
11331 Feld[dropx][dropy] = new_element;
11333 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11334 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11335 el2img(Feld[dropx][dropy]), 0);
11337 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11344 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11347 InitField_WithBug1(dropx, dropy, FALSE);
11349 InitField(dropx, dropy, FALSE);
11350 if (CAN_MOVE(Feld[dropx][dropy]))
11351 InitMovDir(dropx, dropy);
11355 new_element = Feld[dropx][dropy]; /* element might have changed */
11357 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11358 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11361 int move_stepsize = element_info[new_element].move_stepsize;
11363 int move_direction, nextx, nexty;
11365 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11366 MovDir[dropx][dropy] = drop_direction;
11368 move_direction = MovDir[dropx][dropy];
11369 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11370 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11373 Changed[dropx][dropy] = 0; /* allow another change */
11374 CheckCollision[dropx][dropy] = 2;
11377 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
11380 WasJustMoving[dropx][dropy] = 3;
11383 InitMovingField(dropx, dropy, move_direction);
11384 ContinueMoving(dropx, dropy);
11391 Changed[dropx][dropy] = 0; /* allow another change */
11394 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
11396 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
11397 CE_HITTING_SOMETHING, move_direction);
11405 player->drop_delay = 2 * TILEX / move_stepsize + 1;
11410 player->drop_delay = 8 + 8 + 8;
11414 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11419 player->is_dropping = TRUE;
11425 /* ------------------------------------------------------------------------- */
11426 /* game sound playing functions */
11427 /* ------------------------------------------------------------------------- */
11429 static int *loop_sound_frame = NULL;
11430 static int *loop_sound_volume = NULL;
11432 void InitPlayLevelSound()
11434 int num_sounds = getSoundListSize();
11436 checked_free(loop_sound_frame);
11437 checked_free(loop_sound_volume);
11439 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11440 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11443 static void PlayLevelSound(int x, int y, int nr)
11445 int sx = SCREENX(x), sy = SCREENY(y);
11446 int volume, stereo_position;
11447 int max_distance = 8;
11448 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11450 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11451 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11454 if (!IN_LEV_FIELD(x, y) ||
11455 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11456 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11459 volume = SOUND_MAX_VOLUME;
11461 if (!IN_SCR_FIELD(sx, sy))
11463 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11464 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11466 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11469 stereo_position = (SOUND_MAX_LEFT +
11470 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11471 (SCR_FIELDX + 2 * max_distance));
11473 if (IS_LOOP_SOUND(nr))
11475 /* This assures that quieter loop sounds do not overwrite louder ones,
11476 while restarting sound volume comparison with each new game frame. */
11478 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11481 loop_sound_volume[nr] = volume;
11482 loop_sound_frame[nr] = FrameCounter;
11485 PlaySoundExt(nr, volume, stereo_position, type);
11488 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11490 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11491 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11492 y < LEVELY(BY1) ? LEVELY(BY1) :
11493 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11497 static void PlayLevelSoundAction(int x, int y, int action)
11499 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11502 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11504 int sound_effect = element_info[element].sound[action];
11506 if (sound_effect != SND_UNDEFINED)
11507 PlayLevelSound(x, y, sound_effect);
11510 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11513 int sound_effect = element_info[element].sound[action];
11515 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11516 PlayLevelSound(x, y, sound_effect);
11519 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11521 int sound_effect = element_info[Feld[x][y]].sound[action];
11523 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11524 PlayLevelSound(x, y, sound_effect);
11527 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11529 int sound_effect = element_info[Feld[x][y]].sound[action];
11531 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11532 StopSound(sound_effect);
11535 static void PlayLevelMusic()
11537 if (levelset.music[level_nr] != MUS_UNDEFINED)
11538 PlayMusic(levelset.music[level_nr]); /* from config file */
11540 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11543 void RaiseScore(int value)
11545 local_player->score += value;
11547 DrawGameValue_Score(local_player->score);
11550 void RaiseScoreElement(int element)
11555 case EL_BD_DIAMOND:
11556 case EL_EMERALD_YELLOW:
11557 case EL_EMERALD_RED:
11558 case EL_EMERALD_PURPLE:
11559 case EL_SP_INFOTRON:
11560 RaiseScore(level.score[SC_EMERALD]);
11563 RaiseScore(level.score[SC_DIAMOND]);
11566 RaiseScore(level.score[SC_CRYSTAL]);
11569 RaiseScore(level.score[SC_PEARL]);
11572 case EL_BD_BUTTERFLY:
11573 case EL_SP_ELECTRON:
11574 RaiseScore(level.score[SC_BUG]);
11577 case EL_BD_FIREFLY:
11578 case EL_SP_SNIKSNAK:
11579 RaiseScore(level.score[SC_SPACESHIP]);
11582 case EL_DARK_YAMYAM:
11583 RaiseScore(level.score[SC_YAMYAM]);
11586 RaiseScore(level.score[SC_ROBOT]);
11589 RaiseScore(level.score[SC_PACMAN]);
11592 RaiseScore(level.score[SC_NUT]);
11595 case EL_SP_DISK_RED:
11596 case EL_DYNABOMB_INCREASE_NUMBER:
11597 case EL_DYNABOMB_INCREASE_SIZE:
11598 case EL_DYNABOMB_INCREASE_POWER:
11599 RaiseScore(level.score[SC_DYNAMITE]);
11601 case EL_SHIELD_NORMAL:
11602 case EL_SHIELD_DEADLY:
11603 RaiseScore(level.score[SC_SHIELD]);
11605 case EL_EXTRA_TIME:
11606 RaiseScore(level.score[SC_TIME_BONUS]);
11612 RaiseScore(level.score[SC_KEY]);
11615 RaiseScore(element_info[element].collect_score);
11620 void RequestQuitGame(boolean ask_if_really_quit)
11622 if (AllPlayersGone ||
11623 !ask_if_really_quit ||
11624 level_editor_test_game ||
11625 Request("Do you really want to quit the game ?",
11626 REQ_ASK | REQ_STAY_CLOSED))
11628 #if defined(PLATFORM_UNIX)
11629 if (options.network)
11630 SendToServer_StopPlaying();
11634 game_status = GAME_MODE_MAIN;
11642 if (tape.playing && tape.deactivate_display)
11643 TapeDeactivateDisplayOff(TRUE);
11646 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11649 if (tape.playing && tape.deactivate_display)
11650 TapeDeactivateDisplayOn();
11657 /* ---------- new game button stuff ---------------------------------------- */
11659 /* graphic position values for game buttons */
11660 #define GAME_BUTTON_XSIZE 30
11661 #define GAME_BUTTON_YSIZE 30
11662 #define GAME_BUTTON_XPOS 5
11663 #define GAME_BUTTON_YPOS 215
11664 #define SOUND_BUTTON_XPOS 5
11665 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11667 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11668 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11669 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11670 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11671 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11672 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11679 } gamebutton_info[NUM_GAME_BUTTONS] =
11682 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11687 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11688 GAME_CTRL_ID_PAUSE,
11692 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11697 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11698 SOUND_CTRL_ID_MUSIC,
11699 "background music on/off"
11702 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11703 SOUND_CTRL_ID_LOOPS,
11704 "sound loops on/off"
11707 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11708 SOUND_CTRL_ID_SIMPLE,
11709 "normal sounds on/off"
11713 void CreateGameButtons()
11717 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11719 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11720 struct GadgetInfo *gi;
11723 unsigned long event_mask;
11724 int gd_xoffset, gd_yoffset;
11725 int gd_x1, gd_x2, gd_y1, gd_y2;
11728 gd_xoffset = gamebutton_info[i].x;
11729 gd_yoffset = gamebutton_info[i].y;
11730 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11731 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11733 if (id == GAME_CTRL_ID_STOP ||
11734 id == GAME_CTRL_ID_PAUSE ||
11735 id == GAME_CTRL_ID_PLAY)
11737 button_type = GD_TYPE_NORMAL_BUTTON;
11739 event_mask = GD_EVENT_RELEASED;
11740 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11741 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11745 button_type = GD_TYPE_CHECK_BUTTON;
11747 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11748 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11749 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11750 event_mask = GD_EVENT_PRESSED;
11751 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11752 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11755 gi = CreateGadget(GDI_CUSTOM_ID, id,
11756 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11757 GDI_X, DX + gd_xoffset,
11758 GDI_Y, DY + gd_yoffset,
11759 GDI_WIDTH, GAME_BUTTON_XSIZE,
11760 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11761 GDI_TYPE, button_type,
11762 GDI_STATE, GD_BUTTON_UNPRESSED,
11763 GDI_CHECKED, checked,
11764 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11765 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11766 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11767 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11768 GDI_EVENT_MASK, event_mask,
11769 GDI_CALLBACK_ACTION, HandleGameButtons,
11773 Error(ERR_EXIT, "cannot create gadget");
11775 game_gadget[id] = gi;
11779 void FreeGameButtons()
11783 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11784 FreeGadget(game_gadget[i]);
11787 static void MapGameButtons()
11791 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11792 MapGadget(game_gadget[i]);
11795 void UnmapGameButtons()
11799 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11800 UnmapGadget(game_gadget[i]);
11803 static void HandleGameButtons(struct GadgetInfo *gi)
11805 int id = gi->custom_id;
11807 if (game_status != GAME_MODE_PLAYING)
11812 case GAME_CTRL_ID_STOP:
11813 RequestQuitGame(TRUE);
11816 case GAME_CTRL_ID_PAUSE:
11817 if (options.network)
11819 #if defined(PLATFORM_UNIX)
11821 SendToServer_ContinuePlaying();
11823 SendToServer_PausePlaying();
11827 TapeTogglePause(TAPE_TOGGLE_MANUAL);
11830 case GAME_CTRL_ID_PLAY:
11833 #if defined(PLATFORM_UNIX)
11834 if (options.network)
11835 SendToServer_ContinuePlaying();
11839 tape.pausing = FALSE;
11840 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11845 case SOUND_CTRL_ID_MUSIC:
11846 if (setup.sound_music)
11848 setup.sound_music = FALSE;
11851 else if (audio.music_available)
11853 setup.sound = setup.sound_music = TRUE;
11855 SetAudioMode(setup.sound);
11861 case SOUND_CTRL_ID_LOOPS:
11862 if (setup.sound_loops)
11863 setup.sound_loops = FALSE;
11864 else if (audio.loops_available)
11866 setup.sound = setup.sound_loops = TRUE;
11867 SetAudioMode(setup.sound);
11871 case SOUND_CTRL_ID_SIMPLE:
11872 if (setup.sound_simple)
11873 setup.sound_simple = FALSE;
11874 else if (audio.sound_available)
11876 setup.sound = setup.sound_simple = TRUE;
11877 SetAudioMode(setup.sound);