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;
5100 CheckCollision[x][y] = 0;
5104 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5106 if (MovDir[x][y] == MV_NO_MOVING)
5108 InitMovingField(x, y, MV_DOWN);
5109 started_moving = TRUE;
5112 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5114 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5115 MovDir[x][y] = MV_DOWN;
5117 InitMovingField(x, y, MV_DOWN);
5118 started_moving = TRUE;
5120 else if (element == EL_AMOEBA_DROP)
5122 Feld[x][y] = EL_AMOEBA_GROWING;
5123 Store[x][y] = EL_AMOEBA_WET;
5125 /* Store[x][y + 1] must be zero, because:
5126 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5129 #if OLD_GAME_BEHAVIOUR
5130 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5132 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5133 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5134 element != EL_DX_SUPABOMB)
5137 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5138 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5139 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5140 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5143 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5144 (IS_FREE(x - 1, y + 1) ||
5145 Feld[x - 1][y + 1] == EL_ACID));
5146 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5147 (IS_FREE(x + 1, y + 1) ||
5148 Feld[x + 1][y + 1] == EL_ACID));
5149 boolean can_fall_any = (can_fall_left || can_fall_right);
5150 boolean can_fall_both = (can_fall_left && can_fall_right);
5152 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5154 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5156 if (slippery_type == SLIPPERY_ONLY_LEFT)
5157 can_fall_right = FALSE;
5158 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5159 can_fall_left = FALSE;
5160 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5161 can_fall_right = FALSE;
5162 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5163 can_fall_left = FALSE;
5165 can_fall_any = (can_fall_left || can_fall_right);
5166 can_fall_both = (can_fall_left && can_fall_right);
5171 if (can_fall_both &&
5172 (game.emulation != EMU_BOULDERDASH &&
5173 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5174 can_fall_left = !(can_fall_right = RND(2));
5176 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5177 started_moving = TRUE;
5181 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5183 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5186 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5187 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5188 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5189 int belt_dir = game.belt_dir[belt_nr];
5191 if ((belt_dir == MV_LEFT && left_is_free) ||
5192 (belt_dir == MV_RIGHT && right_is_free))
5195 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5198 InitMovingField(x, y, belt_dir);
5199 started_moving = TRUE;
5202 Pushed[x][y] = TRUE;
5203 Pushed[nextx][y] = TRUE;
5206 GfxAction[x][y] = ACTION_DEFAULT;
5210 MovDir[x][y] = 0; /* if element was moving, stop it */
5215 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5217 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5219 if (CAN_MOVE(element) && !started_moving)
5222 int move_pattern = element_info[element].move_pattern;
5227 if (MovDir[x][y] == MV_NO_MOVING)
5229 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5230 x, y, element, element_info[element].token_name);
5231 printf("StartMoving(): This should never happen!\n");
5236 Moving2Blocked(x, y, &newx, &newy);
5239 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5242 if ((element == EL_SATELLITE ||
5243 element == EL_BALLOON ||
5244 element == EL_SPRING)
5245 && JustBeingPushed(x, y))
5252 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5253 CheckCollision[x][y] && IN_LEV_FIELD_AND_NOT_FREE(newx, newy))
5255 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5256 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5257 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5261 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5262 element, element_info[element].token_name,
5263 WasJustMoving[x][y],
5264 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5265 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5266 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5267 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5271 WasJustMoving[x][y] = 0;
5274 CheckCollision[x][y] = 0;
5276 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5279 if (Feld[x][y] != element) /* element has changed */
5281 element = Feld[x][y];
5282 move_pattern = element_info[element].move_pattern;
5284 if (!CAN_MOVE(element))
5288 if (Feld[x][y] != element) /* element has changed */
5296 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5297 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5299 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5301 Moving2Blocked(x, y, &newx, &newy);
5302 if (Feld[newx][newy] == EL_BLOCKED)
5303 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5309 if (FrameCounter < 1 && x == 0 && y == 29)
5310 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5313 if (!MovDelay[x][y]) /* start new movement phase */
5315 /* all objects that can change their move direction after each step
5316 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5318 if (element != EL_YAMYAM &&
5319 element != EL_DARK_YAMYAM &&
5320 element != EL_PACMAN &&
5321 !(move_pattern & MV_ANY_DIRECTION) &&
5322 move_pattern != MV_TURNING_LEFT &&
5323 move_pattern != MV_TURNING_RIGHT &&
5324 move_pattern != MV_TURNING_LEFT_RIGHT &&
5325 move_pattern != MV_TURNING_RIGHT_LEFT &&
5326 move_pattern != MV_TURNING_RANDOM)
5331 if (FrameCounter < 1 && x == 0 && y == 29)
5332 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5335 if (MovDelay[x][y] && (element == EL_BUG ||
5336 element == EL_SPACESHIP ||
5337 element == EL_SP_SNIKSNAK ||
5338 element == EL_SP_ELECTRON ||
5339 element == EL_MOLE))
5340 DrawLevelField(x, y);
5344 if (MovDelay[x][y]) /* wait some time before next movement */
5349 if (element == EL_YAMYAM)
5352 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5353 DrawLevelElementAnimation(x, y, element);
5357 if (MovDelay[x][y]) /* element still has to wait some time */
5360 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5361 ResetGfxAnimation(x, y);
5365 if (GfxAction[x][y] != ACTION_WAITING)
5366 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5368 GfxAction[x][y] = ACTION_WAITING;
5372 if (element == EL_ROBOT ||
5374 element == EL_PACMAN ||
5376 element == EL_YAMYAM ||
5377 element == EL_DARK_YAMYAM)
5380 DrawLevelElementAnimation(x, y, element);
5382 DrawLevelElementAnimationIfNeeded(x, y, element);
5384 PlayLevelSoundAction(x, y, ACTION_WAITING);
5386 else if (element == EL_SP_ELECTRON)
5387 DrawLevelElementAnimationIfNeeded(x, y, element);
5388 else if (element == EL_DRAGON)
5391 int dir = MovDir[x][y];
5392 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5393 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5394 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5395 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5396 dir == MV_UP ? IMG_FLAMES_1_UP :
5397 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5398 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5401 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5404 GfxAction[x][y] = ACTION_ATTACKING;
5406 if (IS_PLAYER(x, y))
5407 DrawPlayerField(x, y);
5409 DrawLevelField(x, y);
5411 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5413 for (i = 1; i <= 3; i++)
5415 int xx = x + i * dx;
5416 int yy = y + i * dy;
5417 int sx = SCREENX(xx);
5418 int sy = SCREENY(yy);
5419 int flame_graphic = graphic + (i - 1);
5421 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5426 int flamed = MovingOrBlocked2Element(xx, yy);
5430 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5432 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5433 RemoveMovingField(xx, yy);
5435 RemoveField(xx, yy);
5437 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5440 RemoveMovingField(xx, yy);
5444 if (ChangeDelay[xx][yy])
5445 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5446 Feld[xx][yy] == EL_BLOCKED));
5450 ChangeDelay[xx][yy] = 0;
5452 Feld[xx][yy] = EL_FLAMES;
5453 if (IN_SCR_FIELD(sx, sy))
5455 DrawLevelFieldCrumbledSand(xx, yy);
5456 DrawGraphic(sx, sy, flame_graphic, frame);
5461 if (Feld[xx][yy] == EL_FLAMES)
5462 Feld[xx][yy] = EL_EMPTY;
5463 DrawLevelField(xx, yy);
5468 if (MovDelay[x][y]) /* element still has to wait some time */
5470 PlayLevelSoundAction(x, y, ACTION_WAITING);
5476 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5477 for all other elements GfxAction will be set by InitMovingField() */
5478 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5479 GfxAction[x][y] = ACTION_MOVING;
5483 /* now make next step */
5485 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5487 if (DONT_COLLIDE_WITH(element) &&
5488 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5489 !PLAYER_ENEMY_PROTECTED(newx, newy))
5492 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5496 /* player killed by element which is deadly when colliding with */
5498 KillHero(PLAYERINFO(newx, newy));
5505 else if (CAN_MOVE_INTO_ACID(element) &&
5506 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5507 (MovDir[x][y] == MV_DOWN ||
5508 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5510 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5511 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5515 else if ((element == EL_PENGUIN ||
5516 element == EL_ROBOT ||
5517 element == EL_SATELLITE ||
5518 element == EL_BALLOON ||
5519 IS_CUSTOM_ELEMENT(element)) &&
5520 IN_LEV_FIELD(newx, newy) &&
5521 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5524 SplashAcid(newx, newy);
5525 Store[x][y] = EL_ACID;
5527 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5529 if (Feld[newx][newy] == EL_EXIT_OPEN)
5533 DrawLevelField(x, y);
5535 Feld[x][y] = EL_EMPTY;
5536 DrawLevelField(x, y);
5539 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5540 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5541 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5543 local_player->friends_still_needed--;
5544 if (!local_player->friends_still_needed &&
5545 !local_player->GameOver && AllPlayersGone)
5546 local_player->LevelSolved = local_player->GameOver = TRUE;
5550 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5552 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5553 DrawLevelField(newx, newy);
5555 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5557 else if (!IS_FREE(newx, newy))
5559 GfxAction[x][y] = ACTION_WAITING;
5561 if (IS_PLAYER(x, y))
5562 DrawPlayerField(x, y);
5564 DrawLevelField(x, y);
5569 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5571 if (IS_FOOD_PIG(Feld[newx][newy]))
5573 if (IS_MOVING(newx, newy))
5574 RemoveMovingField(newx, newy);
5577 Feld[newx][newy] = EL_EMPTY;
5578 DrawLevelField(newx, newy);
5581 PlayLevelSound(x, y, SND_PIG_DIGGING);
5583 else if (!IS_FREE(newx, newy))
5585 if (IS_PLAYER(x, y))
5586 DrawPlayerField(x, y);
5588 DrawLevelField(x, y);
5597 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5600 else if (IS_CUSTOM_ELEMENT(element) &&
5601 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5605 !IS_FREE(newx, newy)
5610 int new_element = Feld[newx][newy];
5613 printf("::: '%s' digs '%s' [%d]\n",
5614 element_info[element].token_name,
5615 element_info[Feld[newx][newy]].token_name,
5616 StorePlayer[newx][newy]);
5619 if (!IS_FREE(newx, newy))
5621 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5622 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5625 /* no element can dig solid indestructible elements */
5626 if (IS_INDESTRUCTIBLE(new_element) &&
5627 !IS_DIGGABLE(new_element) &&
5628 !IS_COLLECTIBLE(new_element))
5631 if (AmoebaNr[newx][newy] &&
5632 (new_element == EL_AMOEBA_FULL ||
5633 new_element == EL_BD_AMOEBA ||
5634 new_element == EL_AMOEBA_GROWING))
5636 AmoebaCnt[AmoebaNr[newx][newy]]--;
5637 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5640 if (IS_MOVING(newx, newy))
5641 RemoveMovingField(newx, newy);
5644 RemoveField(newx, newy);
5645 DrawLevelField(newx, newy);
5648 /* if digged element was about to explode, prevent the explosion */
5649 ExplodeField[newx][newy] = EX_TYPE_NONE;
5651 PlayLevelSoundAction(x, y, action);
5656 Store[newx][newy] = EL_EMPTY;
5657 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5658 Store[newx][newy] = element_info[element].move_leave_element;
5660 Store[newx][newy] = EL_EMPTY;
5661 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5662 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5663 Store[newx][newy] = element_info[element].move_leave_element;
5666 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5667 element_info[element].can_leave_element = TRUE;
5670 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5672 RunnerVisit[x][y] = FrameCounter;
5673 PlayerVisit[x][y] /= 8; /* expire player visit path */
5679 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5681 if (!IS_FREE(newx, newy))
5683 if (IS_PLAYER(x, y))
5684 DrawPlayerField(x, y);
5686 DrawLevelField(x, y);
5692 boolean wanna_flame = !RND(10);
5693 int dx = newx - x, dy = newy - y;
5694 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5695 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5696 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5697 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5698 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5699 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5702 IS_CLASSIC_ENEMY(element1) ||
5703 IS_CLASSIC_ENEMY(element2)) &&
5704 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5705 element1 != EL_FLAMES && element2 != EL_FLAMES)
5708 ResetGfxAnimation(x, y);
5709 GfxAction[x][y] = ACTION_ATTACKING;
5712 if (IS_PLAYER(x, y))
5713 DrawPlayerField(x, y);
5715 DrawLevelField(x, y);
5717 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5719 MovDelay[x][y] = 50;
5723 RemoveField(newx, newy);
5725 Feld[newx][newy] = EL_FLAMES;
5726 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5729 RemoveField(newx1, newy1);
5731 Feld[newx1][newy1] = EL_FLAMES;
5733 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5736 RemoveField(newx2, newy2);
5738 Feld[newx2][newy2] = EL_FLAMES;
5745 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5746 Feld[newx][newy] == EL_DIAMOND)
5748 if (IS_MOVING(newx, newy))
5749 RemoveMovingField(newx, newy);
5752 Feld[newx][newy] = EL_EMPTY;
5753 DrawLevelField(newx, newy);
5756 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5758 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5759 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5761 if (AmoebaNr[newx][newy])
5763 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5764 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5765 Feld[newx][newy] == EL_BD_AMOEBA)
5766 AmoebaCnt[AmoebaNr[newx][newy]]--;
5771 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5773 if (IS_MOVING(newx, newy))
5776 RemoveMovingField(newx, newy);
5780 Feld[newx][newy] = EL_EMPTY;
5781 DrawLevelField(newx, newy);
5784 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5786 else if ((element == EL_PACMAN || element == EL_MOLE)
5787 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5789 if (AmoebaNr[newx][newy])
5791 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5792 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5793 Feld[newx][newy] == EL_BD_AMOEBA)
5794 AmoebaCnt[AmoebaNr[newx][newy]]--;
5797 if (element == EL_MOLE)
5799 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5800 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5802 ResetGfxAnimation(x, y);
5803 GfxAction[x][y] = ACTION_DIGGING;
5804 DrawLevelField(x, y);
5806 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5808 return; /* wait for shrinking amoeba */
5810 else /* element == EL_PACMAN */
5812 Feld[newx][newy] = EL_EMPTY;
5813 DrawLevelField(newx, newy);
5814 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5817 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5818 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5819 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5821 /* wait for shrinking amoeba to completely disappear */
5824 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5826 /* object was running against a wall */
5831 if (move_pattern & MV_ANY_DIRECTION &&
5832 move_pattern == MovDir[x][y])
5834 int blocking_element =
5835 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5838 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
5839 element_info[element].token_name,
5840 element_info[blocking_element].token_name,
5844 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5847 element = Feld[x][y]; /* element might have changed */
5852 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5853 DrawLevelElementAnimation(x, y, element);
5855 if (element == EL_BUG ||
5856 element == EL_SPACESHIP ||
5857 element == EL_SP_SNIKSNAK)
5858 DrawLevelField(x, y);
5859 else if (element == EL_MOLE)
5860 DrawLevelField(x, y);
5861 else if (element == EL_BD_BUTTERFLY ||
5862 element == EL_BD_FIREFLY)
5863 DrawLevelElementAnimationIfNeeded(x, y, element);
5864 else if (element == EL_SATELLITE)
5865 DrawLevelElementAnimationIfNeeded(x, y, element);
5866 else if (element == EL_SP_ELECTRON)
5867 DrawLevelElementAnimationIfNeeded(x, y, element);
5870 if (DONT_TOUCH(element))
5871 TestIfBadThingTouchesHero(x, y);
5874 PlayLevelSoundAction(x, y, ACTION_WAITING);
5880 InitMovingField(x, y, MovDir[x][y]);
5882 PlayLevelSoundAction(x, y, ACTION_MOVING);
5886 ContinueMoving(x, y);
5889 void ContinueMoving(int x, int y)
5891 int element = Feld[x][y];
5892 int stored = Store[x][y];
5893 struct ElementInfo *ei = &element_info[element];
5894 int direction = MovDir[x][y];
5895 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5896 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5897 int newx = x + dx, newy = y + dy;
5899 int nextx = newx + dx, nexty = newy + dy;
5902 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5903 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5905 boolean pushed_by_player = Pushed[x][y];
5908 MovPos[x][y] += getElementMoveStepsize(x, y);
5911 if (pushed_by_player && IS_PLAYER(x, y))
5913 /* special case: moving object pushed by player */
5914 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5917 if (pushed_by_player) /* special case: moving object pushed by player */
5918 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5921 if (ABS(MovPos[x][y]) < TILEX)
5923 DrawLevelField(x, y);
5925 return; /* element is still moving */
5928 /* element reached destination field */
5930 Feld[x][y] = EL_EMPTY;
5931 Feld[newx][newy] = element;
5932 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5934 if (element == EL_MOLE)
5936 Feld[x][y] = EL_SAND;
5938 DrawLevelFieldCrumbledSandNeighbours(x, y);
5940 else if (element == EL_QUICKSAND_FILLING)
5942 element = Feld[newx][newy] = get_next_element(element);
5943 Store[newx][newy] = Store[x][y];
5945 else if (element == EL_QUICKSAND_EMPTYING)
5947 Feld[x][y] = get_next_element(element);
5948 element = Feld[newx][newy] = Store[x][y];
5950 else if (element == EL_MAGIC_WALL_FILLING)
5952 element = Feld[newx][newy] = get_next_element(element);
5953 if (!game.magic_wall_active)
5954 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5955 Store[newx][newy] = Store[x][y];
5957 else if (element == EL_MAGIC_WALL_EMPTYING)
5959 Feld[x][y] = get_next_element(element);
5960 if (!game.magic_wall_active)
5961 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5962 element = Feld[newx][newy] = Store[x][y];
5964 else if (element == EL_BD_MAGIC_WALL_FILLING)
5966 element = Feld[newx][newy] = get_next_element(element);
5967 if (!game.magic_wall_active)
5968 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5969 Store[newx][newy] = Store[x][y];
5971 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5973 Feld[x][y] = get_next_element(element);
5974 if (!game.magic_wall_active)
5975 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5976 element = Feld[newx][newy] = Store[x][y];
5978 else if (element == EL_AMOEBA_DROPPING)
5980 Feld[x][y] = get_next_element(element);
5981 element = Feld[newx][newy] = Store[x][y];
5983 else if (element == EL_SOKOBAN_OBJECT)
5986 Feld[x][y] = Back[x][y];
5988 if (Back[newx][newy])
5989 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5991 Back[x][y] = Back[newx][newy] = 0;
5993 else if (Store[x][y] == EL_ACID)
5995 element = Feld[newx][newy] = EL_ACID;
5998 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5999 ei->move_leave_element != EL_EMPTY &&
6000 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6001 Store[x][y] != EL_EMPTY))
6003 /* some elements can leave other elements behind after moving */
6005 Feld[x][y] = ei->move_leave_element;
6006 InitField(x, y, FALSE);
6008 if (GFX_CRUMBLED(Feld[x][y]))
6009 DrawLevelFieldCrumbledSandNeighbours(x, y);
6013 Store[x][y] = EL_EMPTY;
6014 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6015 MovDelay[newx][newy] = 0;
6017 if (CAN_CHANGE(element))
6019 /* copy element change control values to new field */
6020 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6021 ChangePage[newx][newy] = ChangePage[x][y];
6022 Changed[newx][newy] = Changed[x][y];
6023 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6026 ChangeDelay[x][y] = 0;
6027 ChangePage[x][y] = -1;
6028 Changed[x][y] = CE_BITMASK_DEFAULT;
6029 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6031 /* copy animation control values to new field */
6032 GfxFrame[newx][newy] = GfxFrame[x][y];
6033 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6034 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6035 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6037 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6039 ResetGfxAnimation(x, y); /* reset animation values for old field */
6042 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6043 ei->move_leave_element != EL_EMPTY &&
6044 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6045 stored != EL_EMPTY))
6047 /* some elements can leave other elements behind after moving */
6049 Feld[x][y] = ei->move_leave_element;
6050 InitField(x, y, FALSE);
6052 if (GFX_CRUMBLED(Feld[x][y]))
6053 DrawLevelFieldCrumbledSandNeighbours(x, y);
6058 /* some elements can leave other elements behind after moving */
6059 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6060 ei->move_leave_element != EL_EMPTY &&
6061 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6062 ei->can_leave_element_last))
6064 Feld[x][y] = ei->move_leave_element;
6065 InitField(x, y, FALSE);
6067 if (GFX_CRUMBLED(Feld[x][y]))
6068 DrawLevelFieldCrumbledSandNeighbours(x, y);
6071 ei->can_leave_element_last = ei->can_leave_element;
6072 ei->can_leave_element = FALSE;
6076 /* 2.1.1 (does not work correctly for spring) */
6077 if (!CAN_MOVE(element))
6078 MovDir[newx][newy] = 0;
6082 /* (does not work for falling objects that slide horizontally) */
6083 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6084 MovDir[newx][newy] = 0;
6087 if (!CAN_MOVE(element) ||
6088 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6089 MovDir[newx][newy] = 0;
6093 if (!CAN_MOVE(element) ||
6094 (CAN_FALL(element) && direction == MV_DOWN))
6095 GfxDir[x][y] = MovDir[newx][newy] = 0;
6097 if (!CAN_MOVE(element) ||
6098 (CAN_FALL(element) && direction == MV_DOWN &&
6099 (element == EL_SPRING ||
6100 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6101 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6102 GfxDir[x][y] = MovDir[newx][newy] = 0;
6108 DrawLevelField(x, y);
6109 DrawLevelField(newx, newy);
6111 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6113 /* prevent pushed element from moving on in pushed direction */
6114 if (pushed_by_player && CAN_MOVE(element) &&
6115 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6116 !(element_info[element].move_pattern & direction))
6117 TurnRound(newx, newy);
6120 /* prevent elements on conveyor belt from moving on in last direction */
6121 if (pushed_by_conveyor && CAN_FALL(element) &&
6122 direction & MV_HORIZONTAL)
6125 if (CAN_MOVE(element))
6126 InitMovDir(newx, newy);
6128 MovDir[newx][newy] = 0;
6130 MovDir[newx][newy] = 0;
6135 if (!pushed_by_player)
6137 int nextx = newx + dx, nexty = newy + dy;
6138 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6140 WasJustMoving[newx][newy] = 3;
6142 if (CAN_FALL(element) && direction == MV_DOWN)
6143 WasJustFalling[newx][newy] = 3;
6145 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6146 CheckCollision[newx][newy] = 2;
6149 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6151 TestIfBadThingTouchesHero(newx, newy);
6152 TestIfBadThingTouchesFriend(newx, newy);
6154 if (!IS_CUSTOM_ELEMENT(element))
6155 TestIfBadThingTouchesOtherBadThing(newx, newy);
6157 else if (element == EL_PENGUIN)
6158 TestIfFriendTouchesBadThing(newx, newy);
6160 if (CAN_FALL(element) && direction == MV_DOWN &&
6161 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6165 if (pushed_by_player)
6167 static int trigger_sides[4] =
6169 CH_SIDE_RIGHT, /* moving left */
6170 CH_SIDE_LEFT, /* moving right */
6171 CH_SIDE_BOTTOM, /* moving up */
6172 CH_SIDE_TOP, /* moving down */
6174 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6175 struct PlayerInfo *player = PLAYERINFO(x, y);
6177 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6178 player->index_bit, dig_side);
6179 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6180 player->index_bit, dig_side);
6185 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6189 if (ChangePage[newx][newy] != -1) /* delayed change */
6190 ChangeElement(newx, newy, ChangePage[newx][newy]);
6195 TestIfElementHitsCustomElement(newx, newy, direction);
6199 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6201 int hitting_element = Feld[newx][newy];
6203 /* !!! fix side (direction) orientation here and elsewhere !!! */
6204 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6208 if (IN_LEV_FIELD(nextx, nexty))
6210 int opposite_direction = MV_DIR_OPPOSITE(direction);
6211 int hitting_side = direction;
6212 int touched_side = opposite_direction;
6213 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6214 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6215 MovDir[nextx][nexty] != direction ||
6216 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6222 CheckElementChangeBySide(nextx, nexty, touched_element,
6223 CE_HIT_BY_SOMETHING, opposite_direction);
6225 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6226 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6228 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6230 struct ElementChangeInfo *change =
6231 &element_info[hitting_element].change_page[i];
6233 if (change->can_change &&
6234 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6235 change->trigger_side & touched_side &&
6236 change->trigger_element == touched_element)
6238 CheckElementChangeByPage(newx, newy, hitting_element,
6239 touched_element, CE_OTHER_IS_HITTING,i);
6245 if (IS_CUSTOM_ELEMENT(touched_element) &&
6246 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6248 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6250 struct ElementChangeInfo *change =
6251 &element_info[touched_element].change_page[i];
6253 if (change->can_change &&
6254 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6255 change->trigger_side & hitting_side &&
6256 change->trigger_element == hitting_element)
6258 CheckElementChangeByPage(nextx, nexty, touched_element,
6259 hitting_element, CE_OTHER_GETS_HIT, i);
6270 TestIfPlayerTouchesCustomElement(newx, newy);
6271 TestIfElementTouchesCustomElement(newx, newy);
6274 int AmoebeNachbarNr(int ax, int ay)
6277 int element = Feld[ax][ay];
6279 static int xy[4][2] =
6287 for (i = 0; i < NUM_DIRECTIONS; i++)
6289 int x = ax + xy[i][0];
6290 int y = ay + xy[i][1];
6292 if (!IN_LEV_FIELD(x, y))
6295 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6296 group_nr = AmoebaNr[x][y];
6302 void AmoebenVereinigen(int ax, int ay)
6304 int i, x, y, xx, yy;
6305 int new_group_nr = AmoebaNr[ax][ay];
6306 static int xy[4][2] =
6314 if (new_group_nr == 0)
6317 for (i = 0; i < NUM_DIRECTIONS; i++)
6322 if (!IN_LEV_FIELD(x, y))
6325 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6326 Feld[x][y] == EL_BD_AMOEBA ||
6327 Feld[x][y] == EL_AMOEBA_DEAD) &&
6328 AmoebaNr[x][y] != new_group_nr)
6330 int old_group_nr = AmoebaNr[x][y];
6332 if (old_group_nr == 0)
6335 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6336 AmoebaCnt[old_group_nr] = 0;
6337 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6338 AmoebaCnt2[old_group_nr] = 0;
6340 for (yy = 0; yy < lev_fieldy; yy++)
6342 for (xx = 0; xx < lev_fieldx; xx++)
6344 if (AmoebaNr[xx][yy] == old_group_nr)
6345 AmoebaNr[xx][yy] = new_group_nr;
6352 void AmoebeUmwandeln(int ax, int ay)
6356 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6358 int group_nr = AmoebaNr[ax][ay];
6363 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6364 printf("AmoebeUmwandeln(): This should never happen!\n");
6369 for (y = 0; y < lev_fieldy; y++)
6371 for (x = 0; x < lev_fieldx; x++)
6373 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6376 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6380 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6381 SND_AMOEBA_TURNING_TO_GEM :
6382 SND_AMOEBA_TURNING_TO_ROCK));
6387 static int xy[4][2] =
6395 for (i = 0; i < NUM_DIRECTIONS; i++)
6400 if (!IN_LEV_FIELD(x, y))
6403 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6405 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6406 SND_AMOEBA_TURNING_TO_GEM :
6407 SND_AMOEBA_TURNING_TO_ROCK));
6414 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6417 int group_nr = AmoebaNr[ax][ay];
6418 boolean done = FALSE;
6423 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6424 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6429 for (y = 0; y < lev_fieldy; y++)
6431 for (x = 0; x < lev_fieldx; x++)
6433 if (AmoebaNr[x][y] == group_nr &&
6434 (Feld[x][y] == EL_AMOEBA_DEAD ||
6435 Feld[x][y] == EL_BD_AMOEBA ||
6436 Feld[x][y] == EL_AMOEBA_GROWING))
6439 Feld[x][y] = new_element;
6440 InitField(x, y, FALSE);
6441 DrawLevelField(x, y);
6448 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6449 SND_BD_AMOEBA_TURNING_TO_ROCK :
6450 SND_BD_AMOEBA_TURNING_TO_GEM));
6453 void AmoebeWaechst(int x, int y)
6455 static unsigned long sound_delay = 0;
6456 static unsigned long sound_delay_value = 0;
6458 if (!MovDelay[x][y]) /* start new growing cycle */
6462 if (DelayReached(&sound_delay, sound_delay_value))
6465 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6467 if (Store[x][y] == EL_BD_AMOEBA)
6468 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6470 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6472 sound_delay_value = 30;
6476 if (MovDelay[x][y]) /* wait some time before growing bigger */
6479 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6481 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6482 6 - MovDelay[x][y]);
6484 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6487 if (!MovDelay[x][y])
6489 Feld[x][y] = Store[x][y];
6491 DrawLevelField(x, y);
6496 void AmoebaDisappearing(int x, int y)
6498 static unsigned long sound_delay = 0;
6499 static unsigned long sound_delay_value = 0;
6501 if (!MovDelay[x][y]) /* start new shrinking cycle */
6505 if (DelayReached(&sound_delay, sound_delay_value))
6506 sound_delay_value = 30;
6509 if (MovDelay[x][y]) /* wait some time before shrinking */
6512 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6514 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6515 6 - MovDelay[x][y]);
6517 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6520 if (!MovDelay[x][y])
6522 Feld[x][y] = EL_EMPTY;
6523 DrawLevelField(x, y);
6525 /* don't let mole enter this field in this cycle;
6526 (give priority to objects falling to this field from above) */
6532 void AmoebeAbleger(int ax, int ay)
6535 int element = Feld[ax][ay];
6536 int graphic = el2img(element);
6537 int newax = ax, neway = ay;
6538 static int xy[4][2] =
6546 if (!level.amoeba_speed)
6548 Feld[ax][ay] = EL_AMOEBA_DEAD;
6549 DrawLevelField(ax, ay);
6553 if (IS_ANIMATED(graphic))
6554 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6556 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6557 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6559 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6562 if (MovDelay[ax][ay])
6566 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6569 int x = ax + xy[start][0];
6570 int y = ay + xy[start][1];
6572 if (!IN_LEV_FIELD(x, y))
6576 if (IS_FREE(x, y) ||
6577 CAN_GROW_INTO(Feld[x][y]) ||
6578 Feld[x][y] == EL_QUICKSAND_EMPTY)
6584 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6585 if (IS_FREE(x, y) ||
6586 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6593 if (newax == ax && neway == ay)
6596 else /* normal or "filled" (BD style) amoeba */
6599 boolean waiting_for_player = FALSE;
6601 for (i = 0; i < NUM_DIRECTIONS; i++)
6603 int j = (start + i) % 4;
6604 int x = ax + xy[j][0];
6605 int y = ay + xy[j][1];
6607 if (!IN_LEV_FIELD(x, y))
6611 if (IS_FREE(x, y) ||
6612 CAN_GROW_INTO(Feld[x][y]) ||
6613 Feld[x][y] == EL_QUICKSAND_EMPTY)
6620 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6621 if (IS_FREE(x, y) ||
6622 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6629 else if (IS_PLAYER(x, y))
6630 waiting_for_player = TRUE;
6633 if (newax == ax && neway == ay) /* amoeba cannot grow */
6636 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6638 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6641 Feld[ax][ay] = EL_AMOEBA_DEAD;
6642 DrawLevelField(ax, ay);
6643 AmoebaCnt[AmoebaNr[ax][ay]]--;
6645 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6647 if (element == EL_AMOEBA_FULL)
6648 AmoebeUmwandeln(ax, ay);
6649 else if (element == EL_BD_AMOEBA)
6650 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6655 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6657 /* amoeba gets larger by growing in some direction */
6659 int new_group_nr = AmoebaNr[ax][ay];
6662 if (new_group_nr == 0)
6664 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6665 printf("AmoebeAbleger(): This should never happen!\n");
6670 AmoebaNr[newax][neway] = new_group_nr;
6671 AmoebaCnt[new_group_nr]++;
6672 AmoebaCnt2[new_group_nr]++;
6674 /* if amoeba touches other amoeba(s) after growing, unify them */
6675 AmoebenVereinigen(newax, neway);
6677 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6679 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6685 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6686 (neway == lev_fieldy - 1 && newax != ax))
6688 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6689 Store[newax][neway] = element;
6691 else if (neway == ay)
6693 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6695 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6697 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6702 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6703 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6704 Store[ax][ay] = EL_AMOEBA_DROP;
6705 ContinueMoving(ax, ay);
6709 DrawLevelField(newax, neway);
6712 void Life(int ax, int ay)
6715 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6717 int element = Feld[ax][ay];
6718 int graphic = el2img(element);
6719 boolean changed = FALSE;
6721 if (IS_ANIMATED(graphic))
6722 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6727 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6728 MovDelay[ax][ay] = life_time;
6730 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6733 if (MovDelay[ax][ay])
6737 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6739 int xx = ax+x1, yy = ay+y1;
6742 if (!IN_LEV_FIELD(xx, yy))
6745 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6747 int x = xx+x2, y = yy+y2;
6749 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6752 if (((Feld[x][y] == element ||
6753 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6755 (IS_FREE(x, y) && Stop[x][y]))
6759 if (xx == ax && yy == ay) /* field in the middle */
6761 if (nachbarn < life[0] || nachbarn > life[1])
6763 Feld[xx][yy] = EL_EMPTY;
6765 DrawLevelField(xx, yy);
6766 Stop[xx][yy] = TRUE;
6771 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6772 { /* free border field */
6773 if (nachbarn >= life[2] && nachbarn <= life[3])
6775 Feld[xx][yy] = element;
6776 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6778 DrawLevelField(xx, yy);
6779 Stop[xx][yy] = TRUE;
6784 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6785 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6786 { /* free border field */
6787 if (nachbarn >= life[2] && nachbarn <= life[3])
6789 Feld[xx][yy] = element;
6790 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6792 DrawLevelField(xx, yy);
6793 Stop[xx][yy] = TRUE;
6801 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6802 SND_GAME_OF_LIFE_GROWING);
6805 static void InitRobotWheel(int x, int y)
6807 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6810 static void RunRobotWheel(int x, int y)
6812 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6815 static void StopRobotWheel(int x, int y)
6817 if (ZX == x && ZY == y)
6821 static void InitTimegateWheel(int x, int y)
6824 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6826 /* another brainless, "type style" bug ... :-( */
6827 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6831 static void RunTimegateWheel(int x, int y)
6833 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6836 void CheckExit(int x, int y)
6838 if (local_player->gems_still_needed > 0 ||
6839 local_player->sokobanfields_still_needed > 0 ||
6840 local_player->lights_still_needed > 0)
6842 int element = Feld[x][y];
6843 int graphic = el2img(element);
6845 if (IS_ANIMATED(graphic))
6846 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6851 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6854 Feld[x][y] = EL_EXIT_OPENING;
6856 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6859 void CheckExitSP(int x, int y)
6861 if (local_player->gems_still_needed > 0)
6863 int element = Feld[x][y];
6864 int graphic = el2img(element);
6866 if (IS_ANIMATED(graphic))
6867 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6872 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6875 Feld[x][y] = EL_SP_EXIT_OPENING;
6877 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6880 static void CloseAllOpenTimegates()
6884 for (y = 0; y < lev_fieldy; y++)
6886 for (x = 0; x < lev_fieldx; x++)
6888 int element = Feld[x][y];
6890 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6892 Feld[x][y] = EL_TIMEGATE_CLOSING;
6894 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6896 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6903 void EdelsteinFunkeln(int x, int y)
6905 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6908 if (Feld[x][y] == EL_BD_DIAMOND)
6911 if (MovDelay[x][y] == 0) /* next animation frame */
6912 MovDelay[x][y] = 11 * !SimpleRND(500);
6914 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6918 if (setup.direct_draw && MovDelay[x][y])
6919 SetDrawtoField(DRAW_BUFFERED);
6921 DrawLevelElementAnimation(x, y, Feld[x][y]);
6923 if (MovDelay[x][y] != 0)
6925 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6926 10 - MovDelay[x][y]);
6928 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6930 if (setup.direct_draw)
6934 dest_x = FX + SCREENX(x) * TILEX;
6935 dest_y = FY + SCREENY(y) * TILEY;
6937 BlitBitmap(drawto_field, window,
6938 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6939 SetDrawtoField(DRAW_DIRECT);
6945 void MauerWaechst(int x, int y)
6949 if (!MovDelay[x][y]) /* next animation frame */
6950 MovDelay[x][y] = 3 * delay;
6952 if (MovDelay[x][y]) /* wait some time before next frame */
6956 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6958 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6959 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6961 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6964 if (!MovDelay[x][y])
6966 if (MovDir[x][y] == MV_LEFT)
6968 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6969 DrawLevelField(x - 1, y);
6971 else if (MovDir[x][y] == MV_RIGHT)
6973 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6974 DrawLevelField(x + 1, y);
6976 else if (MovDir[x][y] == MV_UP)
6978 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6979 DrawLevelField(x, y - 1);
6983 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6984 DrawLevelField(x, y + 1);
6987 Feld[x][y] = Store[x][y];
6989 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6990 DrawLevelField(x, y);
6995 void MauerAbleger(int ax, int ay)
6997 int element = Feld[ax][ay];
6998 int graphic = el2img(element);
6999 boolean oben_frei = FALSE, unten_frei = FALSE;
7000 boolean links_frei = FALSE, rechts_frei = FALSE;
7001 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7002 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7003 boolean new_wall = FALSE;
7005 if (IS_ANIMATED(graphic))
7006 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7008 if (!MovDelay[ax][ay]) /* start building new wall */
7009 MovDelay[ax][ay] = 6;
7011 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7014 if (MovDelay[ax][ay])
7018 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7020 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7022 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7024 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7027 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7028 element == EL_EXPANDABLE_WALL_ANY)
7032 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7033 Store[ax][ay-1] = element;
7034 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7035 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7036 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7037 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7042 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7043 Store[ax][ay+1] = element;
7044 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7045 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7046 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7047 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7052 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7053 element == EL_EXPANDABLE_WALL_ANY ||
7054 element == EL_EXPANDABLE_WALL)
7058 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7059 Store[ax-1][ay] = element;
7060 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7061 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7062 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7063 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7069 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7070 Store[ax+1][ay] = element;
7071 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7072 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7073 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7074 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7079 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7080 DrawLevelField(ax, ay);
7082 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7084 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7085 unten_massiv = TRUE;
7086 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7087 links_massiv = TRUE;
7088 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7089 rechts_massiv = TRUE;
7091 if (((oben_massiv && unten_massiv) ||
7092 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7093 element == EL_EXPANDABLE_WALL) &&
7094 ((links_massiv && rechts_massiv) ||
7095 element == EL_EXPANDABLE_WALL_VERTICAL))
7096 Feld[ax][ay] = EL_WALL;
7100 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7102 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7106 void CheckForDragon(int x, int y)
7109 boolean dragon_found = FALSE;
7110 static int xy[4][2] =
7118 for (i = 0; i < NUM_DIRECTIONS; i++)
7120 for (j = 0; j < 4; j++)
7122 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7124 if (IN_LEV_FIELD(xx, yy) &&
7125 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7127 if (Feld[xx][yy] == EL_DRAGON)
7128 dragon_found = TRUE;
7137 for (i = 0; i < NUM_DIRECTIONS; i++)
7139 for (j = 0; j < 3; j++)
7141 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7143 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7145 Feld[xx][yy] = EL_EMPTY;
7146 DrawLevelField(xx, yy);
7155 static void InitBuggyBase(int x, int y)
7157 int element = Feld[x][y];
7158 int activating_delay = FRAMES_PER_SECOND / 4;
7161 (element == EL_SP_BUGGY_BASE ?
7162 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7163 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7165 element == EL_SP_BUGGY_BASE_ACTIVE ?
7166 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7169 static void WarnBuggyBase(int x, int y)
7172 static int xy[4][2] =
7180 for (i = 0; i < NUM_DIRECTIONS; i++)
7182 int xx = x + xy[i][0], yy = y + xy[i][1];
7184 if (IS_PLAYER(xx, yy))
7186 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7193 static void InitTrap(int x, int y)
7195 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7198 static void ActivateTrap(int x, int y)
7200 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7203 static void ChangeActiveTrap(int x, int y)
7205 int graphic = IMG_TRAP_ACTIVE;
7207 /* if new animation frame was drawn, correct crumbled sand border */
7208 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7209 DrawLevelFieldCrumbledSand(x, y);
7212 static void ChangeElementNowExt(int x, int y, int target_element)
7214 int previous_move_direction = MovDir[x][y];
7216 /* check if element under player changes from accessible to unaccessible
7217 (needed for special case of dropping element which then changes) */
7218 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7219 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7226 Feld[x][y] = target_element;
7228 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7230 ResetGfxAnimation(x, y);
7231 ResetRandomAnimationValue(x, y);
7233 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7234 MovDir[x][y] = previous_move_direction;
7237 InitField_WithBug1(x, y, FALSE);
7239 InitField(x, y, FALSE);
7240 if (CAN_MOVE(Feld[x][y]))
7244 DrawLevelField(x, y);
7246 if (GFX_CRUMBLED(Feld[x][y]))
7247 DrawLevelFieldCrumbledSandNeighbours(x, y);
7249 TestIfBadThingTouchesHero(x, y);
7250 TestIfPlayerTouchesCustomElement(x, y);
7251 TestIfElementTouchesCustomElement(x, y);
7253 if (ELEM_IS_PLAYER(target_element))
7254 RelocatePlayer(x, y, target_element);
7257 static boolean ChangeElementNow(int x, int y, int element, int page)
7259 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7262 /* always use default change event to prevent running into a loop */
7263 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7264 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7266 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7268 /* reset actual trigger element and player */
7269 change->actual_trigger_element = EL_EMPTY;
7270 change->actual_trigger_player = EL_PLAYER_1;
7273 /* do not change already changed elements with same change event */
7275 if (Changed[x][y] & ChangeEvent[x][y])
7282 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7284 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7286 if (change->explode)
7293 if (change->use_target_content)
7295 boolean complete_replace = TRUE;
7296 boolean can_replace[3][3];
7299 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7302 boolean is_diggable;
7303 boolean is_collectible;
7304 boolean is_removable;
7305 boolean is_destructible;
7306 int ex = x + xx - 1;
7307 int ey = y + yy - 1;
7308 int content_element = change->target_content[xx][yy];
7311 can_replace[xx][yy] = TRUE;
7313 if (ex == x && ey == y) /* do not check changing element itself */
7316 if (content_element == EL_EMPTY_SPACE)
7318 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7323 if (!IN_LEV_FIELD(ex, ey))
7325 can_replace[xx][yy] = FALSE;
7326 complete_replace = FALSE;
7333 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7334 e = MovingOrBlocked2Element(ex, ey);
7339 is_empty = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7340 IS_WALKABLE(content_element)));
7342 is_empty = (IS_FREE(ex, ey) || (IS_PLAYER(ex, ey) &&
7343 IS_WALKABLE(content_element)));
7345 is_diggable = (is_empty || IS_DIGGABLE(e));
7346 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7347 is_removable = (is_diggable || is_collectible);
7348 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7350 can_replace[xx][yy] =
7351 ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7352 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7353 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7354 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7355 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7357 if (!can_replace[xx][yy])
7358 complete_replace = FALSE;
7360 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7361 IS_WALKABLE(content_element)));
7363 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7365 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7368 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7369 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7370 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7372 can_replace[xx][yy] = FALSE;
7373 complete_replace = FALSE;
7378 if (!change->only_if_complete || complete_replace)
7380 boolean something_has_changed = FALSE;
7382 if (change->only_if_complete && change->use_random_replace &&
7383 RND(100) < change->random_percentage)
7386 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7388 int ex = x + xx - 1;
7389 int ey = y + yy - 1;
7390 int content_element;
7392 if (can_replace[xx][yy] && (!change->use_random_replace ||
7393 RND(100) < change->random_percentage))
7395 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7396 RemoveMovingField(ex, ey);
7398 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7400 content_element = change->target_content[xx][yy];
7401 target_element = GET_TARGET_ELEMENT(content_element, change);
7403 ChangeElementNowExt(ex, ey, target_element);
7405 something_has_changed = TRUE;
7407 /* for symmetry reasons, freeze newly created border elements */
7408 if (ex != x || ey != y)
7409 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7413 if (something_has_changed)
7414 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7419 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7421 ChangeElementNowExt(x, y, target_element);
7423 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7429 static void ChangeElement(int x, int y, int page)
7431 int element = MovingOrBlocked2Element(x, y);
7432 struct ElementInfo *ei = &element_info[element];
7433 struct ElementChangeInfo *change = &ei->change_page[page];
7436 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7439 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7440 x, y, element, element_info[element].token_name);
7441 printf("ChangeElement(): This should never happen!\n");
7446 /* this can happen with classic bombs on walkable, changing elements */
7447 if (!CAN_CHANGE(element))
7450 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7451 ChangeDelay[x][y] = 0;
7457 if (ChangeDelay[x][y] == 0) /* initialize element change */
7459 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7460 RND(change->delay_random * change->delay_frames)) + 1;
7462 ResetGfxAnimation(x, y);
7463 ResetRandomAnimationValue(x, y);
7465 if (change->pre_change_function)
7466 change->pre_change_function(x, y);
7469 ChangeDelay[x][y]--;
7471 if (ChangeDelay[x][y] != 0) /* continue element change */
7473 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7475 if (IS_ANIMATED(graphic))
7476 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7478 if (change->change_function)
7479 change->change_function(x, y);
7481 else /* finish element change */
7483 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7485 page = ChangePage[x][y];
7486 ChangePage[x][y] = -1;
7488 change = &ei->change_page[page];
7492 if (IS_MOVING(x, y) && !change->explode)
7494 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7497 ChangeDelay[x][y] = 1; /* try change after next move step */
7498 ChangePage[x][y] = page; /* remember page to use for change */
7503 if (ChangeElementNow(x, y, element, page))
7505 if (change->post_change_function)
7506 change->post_change_function(x, y);
7511 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7512 int trigger_element,
7519 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7521 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7524 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7526 int element = EL_CUSTOM_START + i;
7528 boolean change_element = FALSE;
7531 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7534 for (j = 0; j < element_info[element].num_change_pages; j++)
7536 struct ElementChangeInfo *change = &element_info[element].change_page[j];
7538 if (change->can_change &&
7539 change->events & CH_EVENT_BIT(trigger_event) &&
7540 change->trigger_side & trigger_side &&
7541 change->trigger_player & trigger_player &&
7542 change->trigger_page & trigger_page_bits &&
7543 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7546 if (!(change->events & CH_EVENT_BIT(trigger_event)))
7547 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7548 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7551 change_element = TRUE;
7554 change->actual_trigger_element = trigger_element;
7555 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7561 if (!change_element)
7564 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7567 if (x == lx && y == ly) /* do not change trigger element itself */
7571 if (Feld[x][y] == element)
7573 ChangeDelay[x][y] = 1;
7574 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7575 ChangeElement(x, y, page);
7583 static boolean CheckElementChangeExt(int x, int y,
7585 int trigger_element,
7591 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7594 if (Feld[x][y] == EL_BLOCKED)
7596 Blocked2Moving(x, y, &x, &y);
7597 element = Feld[x][y];
7601 if (Feld[x][y] != element) /* check if element has already changed */
7604 printf("::: %d ('%s') != %d ('%s') [%d]\n",
7605 Feld[x][y], element_info[Feld[x][y]].token_name,
7606 element, element_info[element].token_name,
7615 if (trigger_page < 0)
7617 boolean change_element = FALSE;
7620 for (i = 0; i < element_info[element].num_change_pages; i++)
7622 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7624 if (change->can_change &&
7625 change->events & CH_EVENT_BIT(trigger_event) &&
7626 change->trigger_side & trigger_side &&
7627 change->trigger_player & trigger_player)
7629 change_element = TRUE;
7632 change->actual_trigger_element = trigger_element;
7633 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7639 if (!change_element)
7644 struct ElementInfo *ei = &element_info[element];
7645 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7647 change->actual_trigger_element = trigger_element;
7648 change->actual_trigger_player = EL_PLAYER_1; /* unused */
7653 /* !!! this check misses pages with same event, but different side !!! */
7655 if (trigger_page < 0)
7656 trigger_page = element_info[element].event_page_nr[trigger_event];
7658 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7662 ChangeDelay[x][y] = 1;
7663 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7664 ChangeElement(x, y, trigger_page);
7669 static void PlayPlayerSound(struct PlayerInfo *player)
7671 int jx = player->jx, jy = player->jy;
7672 int element = player->element_nr;
7673 int last_action = player->last_action_waiting;
7674 int action = player->action_waiting;
7676 if (player->is_waiting)
7678 if (action != last_action)
7679 PlayLevelSoundElementAction(jx, jy, element, action);
7681 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7685 if (action != last_action)
7686 StopSound(element_info[element].sound[last_action]);
7688 if (last_action == ACTION_SLEEPING)
7689 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7693 static void PlayAllPlayersSound()
7697 for (i = 0; i < MAX_PLAYERS; i++)
7698 if (stored_player[i].active)
7699 PlayPlayerSound(&stored_player[i]);
7702 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7704 boolean last_waiting = player->is_waiting;
7705 int move_dir = player->MovDir;
7707 player->last_action_waiting = player->action_waiting;
7711 if (!last_waiting) /* not waiting -> waiting */
7713 player->is_waiting = TRUE;
7715 player->frame_counter_bored =
7717 game.player_boring_delay_fixed +
7718 SimpleRND(game.player_boring_delay_random);
7719 player->frame_counter_sleeping =
7721 game.player_sleeping_delay_fixed +
7722 SimpleRND(game.player_sleeping_delay_random);
7724 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7727 if (game.player_sleeping_delay_fixed +
7728 game.player_sleeping_delay_random > 0 &&
7729 player->anim_delay_counter == 0 &&
7730 player->post_delay_counter == 0 &&
7731 FrameCounter >= player->frame_counter_sleeping)
7732 player->is_sleeping = TRUE;
7733 else if (game.player_boring_delay_fixed +
7734 game.player_boring_delay_random > 0 &&
7735 FrameCounter >= player->frame_counter_bored)
7736 player->is_bored = TRUE;
7738 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7739 player->is_bored ? ACTION_BORING :
7742 if (player->is_sleeping)
7744 if (player->num_special_action_sleeping > 0)
7746 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7748 int last_special_action = player->special_action_sleeping;
7749 int num_special_action = player->num_special_action_sleeping;
7750 int special_action =
7751 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7752 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7753 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7754 last_special_action + 1 : ACTION_SLEEPING);
7755 int special_graphic =
7756 el_act_dir2img(player->element_nr, special_action, move_dir);
7758 player->anim_delay_counter =
7759 graphic_info[special_graphic].anim_delay_fixed +
7760 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7761 player->post_delay_counter =
7762 graphic_info[special_graphic].post_delay_fixed +
7763 SimpleRND(graphic_info[special_graphic].post_delay_random);
7765 player->special_action_sleeping = special_action;
7768 if (player->anim_delay_counter > 0)
7770 player->action_waiting = player->special_action_sleeping;
7771 player->anim_delay_counter--;
7773 else if (player->post_delay_counter > 0)
7775 player->post_delay_counter--;
7779 else if (player->is_bored)
7781 if (player->num_special_action_bored > 0)
7783 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7785 int special_action =
7786 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7787 int special_graphic =
7788 el_act_dir2img(player->element_nr, special_action, move_dir);
7790 player->anim_delay_counter =
7791 graphic_info[special_graphic].anim_delay_fixed +
7792 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7793 player->post_delay_counter =
7794 graphic_info[special_graphic].post_delay_fixed +
7795 SimpleRND(graphic_info[special_graphic].post_delay_random);
7797 player->special_action_bored = special_action;
7800 if (player->anim_delay_counter > 0)
7802 player->action_waiting = player->special_action_bored;
7803 player->anim_delay_counter--;
7805 else if (player->post_delay_counter > 0)
7807 player->post_delay_counter--;
7812 else if (last_waiting) /* waiting -> not waiting */
7814 player->is_waiting = FALSE;
7815 player->is_bored = FALSE;
7816 player->is_sleeping = FALSE;
7818 player->frame_counter_bored = -1;
7819 player->frame_counter_sleeping = -1;
7821 player->anim_delay_counter = 0;
7822 player->post_delay_counter = 0;
7824 player->action_waiting = ACTION_DEFAULT;
7826 player->special_action_bored = ACTION_DEFAULT;
7827 player->special_action_sleeping = ACTION_DEFAULT;
7832 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7835 static byte stored_player_action[MAX_PLAYERS];
7836 static int num_stored_actions = 0;
7838 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7839 int left = player_action & JOY_LEFT;
7840 int right = player_action & JOY_RIGHT;
7841 int up = player_action & JOY_UP;
7842 int down = player_action & JOY_DOWN;
7843 int button1 = player_action & JOY_BUTTON_1;
7844 int button2 = player_action & JOY_BUTTON_2;
7845 int dx = (left ? -1 : right ? 1 : 0);
7846 int dy = (up ? -1 : down ? 1 : 0);
7849 stored_player_action[player->index_nr] = 0;
7850 num_stored_actions++;
7854 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7857 if (!player->active || tape.pausing)
7861 printf("::: [%d %d %d %d] [%d %d]\n",
7862 left, right, up, down, button1, button2);
7868 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7873 if (player->MovPos == 0)
7874 CheckGravityMovement(player);
7877 snapped = SnapField(player, dx, dy);
7881 dropped = DropElement(player);
7883 moved = MovePlayer(player, dx, dy);
7886 if (tape.single_step && tape.recording && !tape.pausing)
7888 if (button1 || (dropped && !moved))
7890 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7891 SnapField(player, 0, 0); /* stop snapping */
7895 SetPlayerWaiting(player, FALSE);
7898 return player_action;
7900 stored_player_action[player->index_nr] = player_action;
7906 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7909 /* no actions for this player (no input at player's configured device) */
7911 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7912 SnapField(player, 0, 0);
7913 CheckGravityMovementWhenNotMoving(player);
7915 if (player->MovPos == 0)
7916 SetPlayerWaiting(player, TRUE);
7918 if (player->MovPos == 0) /* needed for tape.playing */
7919 player->is_moving = FALSE;
7921 player->is_dropping = FALSE;
7927 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7929 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7931 TapeRecordAction(stored_player_action);
7932 num_stored_actions = 0;
7939 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7941 static byte stored_player_action[MAX_PLAYERS];
7942 static int num_stored_actions = 0;
7943 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7944 int left = player_action & JOY_LEFT;
7945 int right = player_action & JOY_RIGHT;
7946 int up = player_action & JOY_UP;
7947 int down = player_action & JOY_DOWN;
7948 int button1 = player_action & JOY_BUTTON_1;
7949 int button2 = player_action & JOY_BUTTON_2;
7950 int dx = (left ? -1 : right ? 1 : 0);
7951 int dy = (up ? -1 : down ? 1 : 0);
7953 stored_player_action[player->index_nr] = 0;
7954 num_stored_actions++;
7956 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7958 if (!player->active || tape.pausing)
7963 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7966 snapped = SnapField(player, dx, dy);
7970 dropped = DropElement(player);
7972 moved = MovePlayer(player, dx, dy);
7975 if (tape.single_step && tape.recording && !tape.pausing)
7977 if (button1 || (dropped && !moved))
7979 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7980 SnapField(player, 0, 0); /* stop snapping */
7984 stored_player_action[player->index_nr] = player_action;
7988 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7990 /* no actions for this player (no input at player's configured device) */
7992 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7993 SnapField(player, 0, 0);
7994 CheckGravityMovementWhenNotMoving(player);
7996 if (player->MovPos == 0)
7997 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7999 if (player->MovPos == 0) /* needed for tape.playing */
8000 player->is_moving = FALSE;
8003 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8005 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8007 TapeRecordAction(stored_player_action);
8008 num_stored_actions = 0;
8015 static unsigned long action_delay = 0;
8016 unsigned long action_delay_value;
8017 int magic_wall_x = 0, magic_wall_y = 0;
8018 int i, x, y, element, graphic;
8019 byte *recorded_player_action;
8020 byte summarized_player_action = 0;
8022 byte tape_action[MAX_PLAYERS];
8025 if (game_status != GAME_MODE_PLAYING)
8028 action_delay_value =
8029 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8031 if (tape.playing && tape.warp_forward && !tape.pausing)
8032 action_delay_value = 0;
8034 /* ---------- main game synchronization point ---------- */
8036 WaitUntilDelayReached(&action_delay, action_delay_value);
8038 if (network_playing && !network_player_action_received)
8042 printf("DEBUG: try to get network player actions in time\n");
8046 #if defined(PLATFORM_UNIX)
8047 /* last chance to get network player actions without main loop delay */
8051 if (game_status != GAME_MODE_PLAYING)
8054 if (!network_player_action_received)
8058 printf("DEBUG: failed to get network player actions in time\n");
8069 printf("::: getting new tape action [%d]\n", FrameCounter);
8072 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8075 if (recorded_player_action == NULL && tape.pausing)
8080 printf("::: %d\n", stored_player[0].action);
8084 if (recorded_player_action != NULL)
8085 for (i = 0; i < MAX_PLAYERS; i++)
8086 stored_player[i].action = recorded_player_action[i];
8089 for (i = 0; i < MAX_PLAYERS; i++)
8091 summarized_player_action |= stored_player[i].action;
8093 if (!network_playing)
8094 stored_player[i].effective_action = stored_player[i].action;
8097 #if defined(PLATFORM_UNIX)
8098 if (network_playing)
8099 SendToServer_MovePlayer(summarized_player_action);
8102 if (!options.network && !setup.team_mode)
8103 local_player->effective_action = summarized_player_action;
8106 if (recorded_player_action != NULL)
8107 for (i = 0; i < MAX_PLAYERS; i++)
8108 stored_player[i].effective_action = recorded_player_action[i];
8112 for (i = 0; i < MAX_PLAYERS; i++)
8114 tape_action[i] = stored_player[i].effective_action;
8116 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8117 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8120 /* only save actions from input devices, but not programmed actions */
8122 TapeRecordAction(tape_action);
8125 for (i = 0; i < MAX_PLAYERS; i++)
8127 int actual_player_action = stored_player[i].effective_action;
8130 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8131 - rnd_equinox_tetrachloride 048
8132 - rnd_equinox_tetrachloride_ii 096
8133 - rnd_emanuel_schmieg 002
8134 - doctor_sloan_ww 001, 020
8136 if (stored_player[i].MovPos == 0)
8137 CheckGravityMovement(&stored_player[i]);
8141 /* overwrite programmed action with tape action */
8142 if (stored_player[i].programmed_action)
8143 actual_player_action = stored_player[i].programmed_action;
8147 if (stored_player[i].programmed_action)
8148 printf("::: %d\n", stored_player[i].programmed_action);
8151 if (recorded_player_action)
8154 if (stored_player[i].programmed_action &&
8155 stored_player[i].programmed_action != recorded_player_action[i])
8156 printf("::: %d: %d <-> %d\n", i,
8157 stored_player[i].programmed_action, recorded_player_action[i]);
8161 actual_player_action = recorded_player_action[i];
8166 /* overwrite tape action with programmed action */
8167 if (stored_player[i].programmed_action)
8168 actual_player_action = stored_player[i].programmed_action;
8173 printf("::: action: %d: %x [%d]\n",
8174 stored_player[i].MovPos, actual_player_action, FrameCounter);
8178 PlayerActions(&stored_player[i], actual_player_action);
8180 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8182 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8183 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8186 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8191 TapeRecordAction(tape_action);
8194 network_player_action_received = FALSE;
8196 ScrollScreen(NULL, SCROLL_GO_ON);
8202 for (i = 0; i < MAX_PLAYERS; i++)
8203 stored_player[i].Frame++;
8207 /* for downwards compatibility, the following code emulates a fixed bug that
8208 occured when pushing elements (causing elements that just made their last
8209 pushing step to already (if possible) make their first falling step in the
8210 same game frame, which is bad); this code is also needed to use the famous
8211 "spring push bug" which is used in older levels and might be wanted to be
8212 used also in newer levels, but in this case the buggy pushing code is only
8213 affecting the "spring" element and no other elements */
8216 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8218 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8221 for (i = 0; i < MAX_PLAYERS; i++)
8223 struct PlayerInfo *player = &stored_player[i];
8228 if (player->active && player->is_pushing && player->is_moving &&
8230 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8231 Feld[x][y] == EL_SPRING))
8233 if (player->active && player->is_pushing && player->is_moving &&
8237 ContinueMoving(x, y);
8239 /* continue moving after pushing (this is actually a bug) */
8240 if (!IS_MOVING(x, y))
8249 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8251 Changed[x][y] = CE_BITMASK_DEFAULT;
8252 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8255 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8257 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8258 printf("GameActions(): This should never happen!\n");
8260 ChangePage[x][y] = -1;
8265 if (WasJustMoving[x][y] > 0)
8266 WasJustMoving[x][y]--;
8267 if (WasJustFalling[x][y] > 0)
8268 WasJustFalling[x][y]--;
8269 if (CheckCollision[x][y] > 0)
8270 CheckCollision[x][y]--;
8275 /* reset finished pushing action (not done in ContinueMoving() to allow
8276 continous pushing animation for elements with zero push delay) */
8277 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8279 ResetGfxAnimation(x, y);
8280 DrawLevelField(x, y);
8285 if (IS_BLOCKED(x, y))
8289 Blocked2Moving(x, y, &oldx, &oldy);
8290 if (!IS_MOVING(oldx, oldy))
8292 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8293 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8294 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8295 printf("GameActions(): This should never happen!\n");
8301 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8303 element = Feld[x][y];
8305 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8307 graphic = el2img(element);
8313 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8315 element = graphic = 0;
8319 if (graphic_info[graphic].anim_global_sync)
8320 GfxFrame[x][y] = FrameCounter;
8322 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8323 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8324 ResetRandomAnimationValue(x, y);
8326 SetRandomAnimationValue(x, y);
8329 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8332 if (IS_INACTIVE(element))
8334 if (IS_ANIMATED(graphic))
8335 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8341 /* this may take place after moving, so 'element' may have changed */
8343 if (IS_CHANGING(x, y))
8345 if (IS_CHANGING(x, y) &&
8346 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8350 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8351 element_info[element].event_page_nr[CE_DELAY]);
8353 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8356 element = Feld[x][y];
8357 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8361 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8366 element = Feld[x][y];
8367 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8369 if (element == EL_MOLE)
8370 printf("::: %d, %d, %d [%d]\n",
8371 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8375 if (element == EL_YAMYAM)
8376 printf("::: %d, %d, %d\n",
8377 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8381 if (IS_ANIMATED(graphic) &&
8385 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8388 if (element == EL_BUG)
8389 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8393 if (element == EL_MOLE)
8394 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8398 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8399 EdelsteinFunkeln(x, y);
8401 else if ((element == EL_ACID ||
8402 element == EL_EXIT_OPEN ||
8403 element == EL_SP_EXIT_OPEN ||
8404 element == EL_SP_TERMINAL ||
8405 element == EL_SP_TERMINAL_ACTIVE ||
8406 element == EL_EXTRA_TIME ||
8407 element == EL_SHIELD_NORMAL ||
8408 element == EL_SHIELD_DEADLY) &&
8409 IS_ANIMATED(graphic))
8410 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8411 else if (IS_MOVING(x, y))
8412 ContinueMoving(x, y);
8413 else if (IS_ACTIVE_BOMB(element))
8414 CheckDynamite(x, y);
8416 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8417 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8419 else if (element == EL_AMOEBA_GROWING)
8420 AmoebeWaechst(x, y);
8421 else if (element == EL_AMOEBA_SHRINKING)
8422 AmoebaDisappearing(x, y);
8424 #if !USE_NEW_AMOEBA_CODE
8425 else if (IS_AMOEBALIVE(element))
8426 AmoebeAbleger(x, y);
8429 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8431 else if (element == EL_EXIT_CLOSED)
8433 else if (element == EL_SP_EXIT_CLOSED)
8435 else if (element == EL_EXPANDABLE_WALL_GROWING)
8437 else if (element == EL_EXPANDABLE_WALL ||
8438 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8439 element == EL_EXPANDABLE_WALL_VERTICAL ||
8440 element == EL_EXPANDABLE_WALL_ANY)
8442 else if (element == EL_FLAMES)
8443 CheckForDragon(x, y);
8445 else if (IS_AUTO_CHANGING(element))
8446 ChangeElement(x, y);
8448 else if (element == EL_EXPLOSION)
8449 ; /* drawing of correct explosion animation is handled separately */
8450 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8451 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8454 /* this may take place after moving, so 'element' may have changed */
8455 if (IS_AUTO_CHANGING(Feld[x][y]))
8456 ChangeElement(x, y);
8459 if (IS_BELT_ACTIVE(element))
8460 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8462 if (game.magic_wall_active)
8464 int jx = local_player->jx, jy = local_player->jy;
8466 /* play the element sound at the position nearest to the player */
8467 if ((element == EL_MAGIC_WALL_FULL ||
8468 element == EL_MAGIC_WALL_ACTIVE ||
8469 element == EL_MAGIC_WALL_EMPTYING ||
8470 element == EL_BD_MAGIC_WALL_FULL ||
8471 element == EL_BD_MAGIC_WALL_ACTIVE ||
8472 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8473 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8481 #if USE_NEW_AMOEBA_CODE
8482 /* new experimental amoeba growth stuff */
8484 if (!(FrameCounter % 8))
8487 static unsigned long random = 1684108901;
8489 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8492 x = (random >> 10) % lev_fieldx;
8493 y = (random >> 20) % lev_fieldy;
8495 x = RND(lev_fieldx);
8496 y = RND(lev_fieldy);
8498 element = Feld[x][y];
8501 if (!IS_PLAYER(x,y) &&
8502 (element == EL_EMPTY ||
8503 CAN_GROW_INTO(element) ||
8504 element == EL_QUICKSAND_EMPTY ||
8505 element == EL_ACID_SPLASH_LEFT ||
8506 element == EL_ACID_SPLASH_RIGHT))
8508 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8509 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8510 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8511 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8512 Feld[x][y] = EL_AMOEBA_DROP;
8515 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8516 if (!IS_PLAYER(x,y) &&
8517 (element == EL_EMPTY ||
8518 element == EL_SAND ||
8519 element == EL_QUICKSAND_EMPTY ||
8520 element == EL_ACID_SPLASH_LEFT ||
8521 element == EL_ACID_SPLASH_RIGHT))
8523 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8524 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8525 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8526 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8527 Feld[x][y] = EL_AMOEBA_DROP;
8531 random = random * 129 + 1;
8537 if (game.explosions_delayed)
8540 game.explosions_delayed = FALSE;
8542 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8544 element = Feld[x][y];
8546 if (ExplodeField[x][y])
8547 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8548 else if (element == EL_EXPLOSION)
8549 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8551 ExplodeField[x][y] = EX_TYPE_NONE;
8554 game.explosions_delayed = TRUE;
8557 if (game.magic_wall_active)
8559 if (!(game.magic_wall_time_left % 4))
8561 int element = Feld[magic_wall_x][magic_wall_y];
8563 if (element == EL_BD_MAGIC_WALL_FULL ||
8564 element == EL_BD_MAGIC_WALL_ACTIVE ||
8565 element == EL_BD_MAGIC_WALL_EMPTYING)
8566 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8568 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8571 if (game.magic_wall_time_left > 0)
8573 game.magic_wall_time_left--;
8574 if (!game.magic_wall_time_left)
8576 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8578 element = Feld[x][y];
8580 if (element == EL_MAGIC_WALL_ACTIVE ||
8581 element == EL_MAGIC_WALL_FULL)
8583 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8584 DrawLevelField(x, y);
8586 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8587 element == EL_BD_MAGIC_WALL_FULL)
8589 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8590 DrawLevelField(x, y);
8594 game.magic_wall_active = FALSE;
8599 if (game.light_time_left > 0)
8601 game.light_time_left--;
8603 if (game.light_time_left == 0)
8604 RedrawAllLightSwitchesAndInvisibleElements();
8607 if (game.timegate_time_left > 0)
8609 game.timegate_time_left--;
8611 if (game.timegate_time_left == 0)
8612 CloseAllOpenTimegates();
8615 for (i = 0; i < MAX_PLAYERS; i++)
8617 struct PlayerInfo *player = &stored_player[i];
8619 if (SHIELD_ON(player))
8621 if (player->shield_deadly_time_left)
8622 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8623 else if (player->shield_normal_time_left)
8624 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8628 if (TimeFrames >= FRAMES_PER_SECOND)
8633 if (!level.use_step_counter)
8637 for (i = 0; i < MAX_PLAYERS; i++)
8639 struct PlayerInfo *player = &stored_player[i];
8641 if (SHIELD_ON(player))
8643 player->shield_normal_time_left--;
8645 if (player->shield_deadly_time_left > 0)
8646 player->shield_deadly_time_left--;
8654 if (TimeLeft <= 10 && setup.time_limit)
8655 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8657 DrawGameValue_Time(TimeLeft);
8659 if (!TimeLeft && setup.time_limit)
8660 for (i = 0; i < MAX_PLAYERS; i++)
8661 KillHero(&stored_player[i]);
8663 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8664 DrawGameValue_Time(TimePlayed);
8667 if (tape.recording || tape.playing)
8668 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8672 PlayAllPlayersSound();
8674 if (options.debug) /* calculate frames per second */
8676 static unsigned long fps_counter = 0;
8677 static int fps_frames = 0;
8678 unsigned long fps_delay_ms = Counter() - fps_counter;
8682 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8684 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8687 fps_counter = Counter();
8690 redraw_mask |= REDRAW_FPS;
8694 if (stored_player[0].jx != stored_player[0].last_jx ||
8695 stored_player[0].jy != stored_player[0].last_jy)
8696 printf("::: %d, %d, %d, %d, %d\n",
8697 stored_player[0].MovDir,
8698 stored_player[0].MovPos,
8699 stored_player[0].GfxPos,
8700 stored_player[0].Frame,
8701 stored_player[0].StepFrame);
8708 for (i = 0; i < MAX_PLAYERS; i++)
8711 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8713 stored_player[i].Frame += move_frames;
8715 if (stored_player[i].MovPos != 0)
8716 stored_player[i].StepFrame += move_frames;
8718 if (stored_player[i].drop_delay > 0)
8719 stored_player[i].drop_delay--;
8724 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8726 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8728 local_player->show_envelope = 0;
8733 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8735 int min_x = x, min_y = y, max_x = x, max_y = y;
8738 for (i = 0; i < MAX_PLAYERS; i++)
8740 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8742 if (!stored_player[i].active || &stored_player[i] == player)
8745 min_x = MIN(min_x, jx);
8746 min_y = MIN(min_y, jy);
8747 max_x = MAX(max_x, jx);
8748 max_y = MAX(max_y, jy);
8751 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8754 static boolean AllPlayersInVisibleScreen()
8758 for (i = 0; i < MAX_PLAYERS; i++)
8760 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8762 if (!stored_player[i].active)
8765 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8772 void ScrollLevel(int dx, int dy)
8774 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8777 BlitBitmap(drawto_field, drawto_field,
8778 FX + TILEX * (dx == -1) - softscroll_offset,
8779 FY + TILEY * (dy == -1) - softscroll_offset,
8780 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8781 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8782 FX + TILEX * (dx == 1) - softscroll_offset,
8783 FY + TILEY * (dy == 1) - softscroll_offset);
8787 x = (dx == 1 ? BX1 : BX2);
8788 for (y = BY1; y <= BY2; y++)
8789 DrawScreenField(x, y);
8794 y = (dy == 1 ? BY1 : BY2);
8795 for (x = BX1; x <= BX2; x++)
8796 DrawScreenField(x, y);
8799 redraw_mask |= REDRAW_FIELD;
8803 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8805 int nextx = x + dx, nexty = y + dy;
8806 int element = Feld[x][y];
8809 element != EL_SP_PORT_LEFT &&
8810 element != EL_SP_GRAVITY_PORT_LEFT &&
8811 element != EL_SP_PORT_HORIZONTAL &&
8812 element != EL_SP_PORT_ANY) ||
8814 element != EL_SP_PORT_RIGHT &&
8815 element != EL_SP_GRAVITY_PORT_RIGHT &&
8816 element != EL_SP_PORT_HORIZONTAL &&
8817 element != EL_SP_PORT_ANY) ||
8819 element != EL_SP_PORT_UP &&
8820 element != EL_SP_GRAVITY_PORT_UP &&
8821 element != EL_SP_PORT_VERTICAL &&
8822 element != EL_SP_PORT_ANY) ||
8824 element != EL_SP_PORT_DOWN &&
8825 element != EL_SP_GRAVITY_PORT_DOWN &&
8826 element != EL_SP_PORT_VERTICAL &&
8827 element != EL_SP_PORT_ANY) ||
8828 !IN_LEV_FIELD(nextx, nexty) ||
8829 !IS_FREE(nextx, nexty))
8836 static boolean canFallDown(struct PlayerInfo *player)
8838 int jx = player->jx, jy = player->jy;
8840 return (IN_LEV_FIELD(jx, jy + 1) &&
8841 (IS_FREE(jx, jy + 1) ||
8842 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8843 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8844 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8847 static boolean canPassField(int x, int y, int move_dir)
8849 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8850 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8851 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8854 int element = Feld[x][y];
8856 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8857 !CAN_MOVE(element) &&
8858 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8859 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8860 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8863 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8865 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8866 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8867 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8871 int nextx = newx + dx;
8872 int nexty = newy + dy;
8876 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8877 (IS_DIGGABLE(Feld[newx][newy]) ||
8878 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8879 canPassField(newx, newy, move_dir)));
8881 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8882 (IS_DIGGABLE(Feld[newx][newy]) ||
8883 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8884 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
8885 !CAN_MOVE(Feld[newx][newy]) &&
8886 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8887 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8888 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
8892 static void CheckGravityMovement(struct PlayerInfo *player)
8894 if (game.gravity && !player->programmed_action)
8897 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8898 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8900 int move_dir_horizontal = player->action & MV_HORIZONTAL;
8901 int move_dir_vertical = player->action & MV_VERTICAL;
8905 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8907 boolean player_is_snapping = player->action & JOY_BUTTON_1;
8910 int jx = player->jx, jy = player->jy;
8912 boolean player_is_moving_to_valid_field =
8913 (!player_is_snapping &&
8914 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8915 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8919 (player->last_move_dir & MV_HORIZONTAL ?
8920 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8921 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8925 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8926 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8927 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8928 int new_jx = jx + dx, new_jy = jy + dy;
8929 int nextx = new_jx + dx, nexty = new_jy + dy;
8935 boolean player_can_fall_down = canFallDown(player);
8937 boolean player_can_fall_down =
8938 (IN_LEV_FIELD(jx, jy + 1) &&
8939 (IS_FREE(jx, jy + 1) ||
8940 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
8944 boolean player_can_fall_down =
8945 (IN_LEV_FIELD(jx, jy + 1) &&
8946 (IS_FREE(jx, jy + 1)));
8950 boolean player_is_moving_to_valid_field =
8953 !player_is_snapping &&
8957 IN_LEV_FIELD(new_jx, new_jy) &&
8958 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
8959 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8960 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
8961 IN_LEV_FIELD(nextx, nexty) &&
8962 element_info[Feld[nextx][nexty]].access_direction & move_dir))
8964 IN_LEV_FIELD(new_jx, new_jy) &&
8965 (Feld[new_jx][new_jy] == EL_SP_BASE ||
8966 Feld[new_jx][new_jy] == EL_SAND ||
8967 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8968 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
8969 /* !!! extend EL_SAND to anything diggable !!! */
8975 boolean player_is_standing_on_valid_field =
8976 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8977 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
8981 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
8982 player_can_fall_down,
8983 player_is_standing_on_valid_field,
8984 player_is_moving_to_valid_field,
8985 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
8986 player->effective_action,
8987 player->can_fall_into_acid);
8990 if (player_can_fall_down &&
8992 !player_is_standing_on_valid_field &&
8994 !player_is_moving_to_valid_field)
8997 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
8998 jx, jy, FrameCounter);
9001 player->programmed_action = MV_DOWN;
9006 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9009 return CheckGravityMovement(player);
9012 if (game.gravity && !player->programmed_action)
9014 int jx = player->jx, jy = player->jy;
9015 boolean field_under_player_is_free =
9016 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9017 boolean player_is_standing_on_valid_field =
9018 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9019 (IS_WALKABLE(Feld[jx][jy]) &&
9020 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9022 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9023 player->programmed_action = MV_DOWN;
9029 -----------------------------------------------------------------------------
9030 dx, dy: direction (non-diagonal) to try to move the player to
9031 real_dx, real_dy: direction as read from input device (can be diagonal)
9034 boolean MovePlayerOneStep(struct PlayerInfo *player,
9035 int dx, int dy, int real_dx, int real_dy)
9038 static int trigger_sides[4][2] =
9040 /* enter side leave side */
9041 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9042 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9043 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9044 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9046 int move_direction = (dx == -1 ? MV_LEFT :
9047 dx == +1 ? MV_RIGHT :
9049 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9050 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9051 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9053 int jx = player->jx, jy = player->jy;
9054 int new_jx = jx + dx, new_jy = jy + dy;
9058 if (!player->active || (!dx && !dy))
9059 return MF_NO_ACTION;
9061 player->MovDir = (dx < 0 ? MV_LEFT :
9064 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9066 if (!IN_LEV_FIELD(new_jx, new_jy))
9067 return MF_NO_ACTION;
9069 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9070 return MF_NO_ACTION;
9073 element = MovingOrBlocked2Element(new_jx, new_jy);
9075 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9078 if (DONT_RUN_INTO(element))
9080 if (element == EL_ACID && dx == 0 && dy == 1)
9082 SplashAcid(new_jx, new_jy);
9083 Feld[jx][jy] = EL_PLAYER_1;
9084 InitMovingField(jx, jy, MV_DOWN);
9085 Store[jx][jy] = EL_ACID;
9086 ContinueMoving(jx, jy);
9090 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9095 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9096 if (can_move != MF_MOVING)
9099 /* check if DigField() has caused relocation of the player */
9100 if (player->jx != jx || player->jy != jy)
9101 return MF_NO_ACTION;
9103 StorePlayer[jx][jy] = 0;
9104 player->last_jx = jx;
9105 player->last_jy = jy;
9106 player->jx = new_jx;
9107 player->jy = new_jy;
9108 StorePlayer[new_jx][new_jy] = player->element_nr;
9111 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9113 player->step_counter++;
9116 player->drop_delay = 0;
9119 PlayerVisit[jx][jy] = FrameCounter;
9121 ScrollPlayer(player, SCROLL_INIT);
9124 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9126 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9128 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9131 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9133 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9134 CE_OTHER_GETS_ENTERED, enter_side);
9135 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9136 CE_ENTERED_BY_PLAYER, enter_side);
9143 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9145 int jx = player->jx, jy = player->jy;
9146 int old_jx = jx, old_jy = jy;
9147 int moved = MF_NO_ACTION;
9150 if (!player->active)
9155 if (player->MovPos == 0)
9157 player->is_moving = FALSE;
9158 player->is_digging = FALSE;
9159 player->is_collecting = FALSE;
9160 player->is_snapping = FALSE;
9161 player->is_pushing = FALSE;
9167 if (!player->active || (!dx && !dy))
9172 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9178 if (!FrameReached(&player->move_delay, player->move_delay_value))
9181 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9182 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9188 /* store if player is automatically moved to next field */
9189 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9191 /* remove the last programmed player action */
9192 player->programmed_action = 0;
9196 /* should only happen if pre-1.2 tape recordings are played */
9197 /* this is only for backward compatibility */
9199 int original_move_delay_value = player->move_delay_value;
9202 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9206 /* scroll remaining steps with finest movement resolution */
9207 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9209 while (player->MovPos)
9211 ScrollPlayer(player, SCROLL_GO_ON);
9212 ScrollScreen(NULL, SCROLL_GO_ON);
9218 player->move_delay_value = original_move_delay_value;
9221 if (player->last_move_dir & MV_HORIZONTAL)
9223 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9224 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9228 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9229 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9235 if (moved & MF_MOVING && !ScreenMovPos &&
9236 (player == local_player || !options.network))
9238 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9239 int offset = (setup.scroll_delay ? 3 : 0);
9241 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9243 /* actual player has left the screen -- scroll in that direction */
9244 if (jx != old_jx) /* player has moved horizontally */
9245 scroll_x += (jx - old_jx);
9246 else /* player has moved vertically */
9247 scroll_y += (jy - old_jy);
9251 if (jx != old_jx) /* player has moved horizontally */
9253 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9254 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9255 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9257 /* don't scroll over playfield boundaries */
9258 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9259 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9261 /* don't scroll more than one field at a time */
9262 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9264 /* don't scroll against the player's moving direction */
9265 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9266 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9267 scroll_x = old_scroll_x;
9269 else /* player has moved vertically */
9271 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9272 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9273 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9275 /* don't scroll over playfield boundaries */
9276 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9277 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9279 /* don't scroll more than one field at a time */
9280 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9282 /* don't scroll against the player's moving direction */
9283 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9284 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9285 scroll_y = old_scroll_y;
9289 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9291 if (!options.network && !AllPlayersInVisibleScreen())
9293 scroll_x = old_scroll_x;
9294 scroll_y = old_scroll_y;
9298 ScrollScreen(player, SCROLL_INIT);
9299 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9306 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9308 if (!(moved & MF_MOVING) && !player->is_pushing)
9313 player->StepFrame = 0;
9315 if (moved & MF_MOVING)
9317 if (old_jx != jx && old_jy == jy)
9318 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9319 else if (old_jx == jx && old_jy != jy)
9320 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9322 DrawLevelField(jx, jy); /* for "crumbled sand" */
9324 player->last_move_dir = player->MovDir;
9325 player->is_moving = TRUE;
9327 player->is_snapping = FALSE;
9331 player->is_switching = FALSE;
9334 player->is_dropping = FALSE;
9338 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9341 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9344 static int trigger_sides[4][2] =
9346 /* enter side leave side */
9347 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9348 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9349 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9350 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9352 int move_direction = player->MovDir;
9353 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9354 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9357 /* !!! TEST ONLY !!! */
9358 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9359 CheckElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9361 player->index_bit, leave_side);
9363 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9365 player->index_bit, leave_side);
9367 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9368 CheckElementChangeByPlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9369 player->index_bit, enter_side);
9371 CheckTriggeredElementChangeByPlayer(jx, jy, Feld[jx][jy],
9372 CE_OTHER_GETS_ENTERED,
9373 player->index_bit, enter_side);
9383 CheckGravityMovementWhenNotMoving(player);
9386 player->last_move_dir = MV_NO_MOVING;
9388 player->is_moving = FALSE;
9391 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9393 TestIfHeroTouchesBadThing(jx, jy);
9394 TestIfPlayerTouchesCustomElement(jx, jy);
9397 if (!player->active)
9403 void ScrollPlayer(struct PlayerInfo *player, int mode)
9405 int jx = player->jx, jy = player->jy;
9406 int last_jx = player->last_jx, last_jy = player->last_jy;
9407 int move_stepsize = TILEX / player->move_delay_value;
9409 if (!player->active || !player->MovPos)
9412 if (mode == SCROLL_INIT)
9414 player->actual_frame_counter = FrameCounter;
9415 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9417 if (Feld[last_jx][last_jy] == EL_EMPTY)
9418 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9426 else if (!FrameReached(&player->actual_frame_counter, 1))
9429 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9430 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9432 if (!player->block_last_field &&
9433 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9434 Feld[last_jx][last_jy] = EL_EMPTY;
9436 /* before DrawPlayer() to draw correct player graphic for this case */
9437 if (player->MovPos == 0)
9438 CheckGravityMovement(player);
9441 DrawPlayer(player); /* needed here only to cleanup last field */
9444 if (player->MovPos == 0) /* player reached destination field */
9447 if (player->move_delay_reset_counter > 0)
9449 player->move_delay_reset_counter--;
9451 if (player->move_delay_reset_counter == 0)
9453 /* continue with normal speed after quickly moving through gate */
9454 HALVE_PLAYER_SPEED(player);
9456 /* be able to make the next move without delay */
9457 player->move_delay = 0;
9461 if (IS_PASSABLE(Feld[last_jx][last_jy]))
9463 /* continue with normal speed after quickly moving through gate */
9464 HALVE_PLAYER_SPEED(player);
9466 /* be able to make the next move without delay */
9467 player->move_delay = 0;
9471 if (player->block_last_field &&
9472 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9473 Feld[last_jx][last_jy] = EL_EMPTY;
9475 player->last_jx = jx;
9476 player->last_jy = jy;
9478 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9479 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9480 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9482 DrawPlayer(player); /* needed here only to cleanup last field */
9485 if (local_player->friends_still_needed == 0 ||
9486 IS_SP_ELEMENT(Feld[jx][jy]))
9487 player->LevelSolved = player->GameOver = TRUE;
9491 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9492 /* this breaks one level: "machine", level 000 */
9494 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9497 static int trigger_sides[4][2] =
9499 /* enter side leave side */
9500 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9501 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9502 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9503 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9505 int move_direction = player->MovDir;
9506 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9507 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9508 int old_jx = last_jx;
9509 int old_jy = last_jy;
9512 /* !!! TEST ONLY !!! */
9513 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9514 CheckElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9516 player->index_bit, leave_side);
9518 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9520 player->index_bit, leave_side);
9522 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9523 CheckElementChangeByPlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9524 player->index_bit, enter_side);
9526 CheckTriggeredElementChangeByPlayer(jx, jy, Feld[jx][jy],
9527 CE_OTHER_GETS_ENTERED,
9528 player->index_bit, enter_side);
9534 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9536 TestIfHeroTouchesBadThing(jx, jy);
9537 TestIfPlayerTouchesCustomElement(jx, jy);
9539 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9542 if (!player->active)
9546 if (level.use_step_counter)
9552 for (i = 0; i < MAX_PLAYERS; i++)
9554 struct PlayerInfo *player = &stored_player[i];
9556 if (SHIELD_ON(player))
9558 player->shield_normal_time_left--;
9560 if (player->shield_deadly_time_left > 0)
9561 player->shield_deadly_time_left--;
9569 if (TimeLeft <= 10 && setup.time_limit)
9570 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9572 DrawGameValue_Time(TimeLeft);
9574 if (!TimeLeft && setup.time_limit)
9575 for (i = 0; i < MAX_PLAYERS; i++)
9576 KillHero(&stored_player[i]);
9578 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9579 DrawGameValue_Time(TimePlayed);
9582 if (tape.single_step && tape.recording && !tape.pausing &&
9583 !player->programmed_action)
9584 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9588 void ScrollScreen(struct PlayerInfo *player, int mode)
9590 static unsigned long screen_frame_counter = 0;
9592 if (mode == SCROLL_INIT)
9594 /* set scrolling step size according to actual player's moving speed */
9595 ScrollStepSize = TILEX / player->move_delay_value;
9597 screen_frame_counter = FrameCounter;
9598 ScreenMovDir = player->MovDir;
9599 ScreenMovPos = player->MovPos;
9600 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9603 else if (!FrameReached(&screen_frame_counter, 1))
9608 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9609 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9610 redraw_mask |= REDRAW_FIELD;
9613 ScreenMovDir = MV_NO_MOVING;
9616 void TestIfPlayerTouchesCustomElement(int x, int y)
9618 static int xy[4][2] =
9625 static int trigger_sides[4][2] =
9627 /* center side border side */
9628 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9629 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9630 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9631 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9633 static int touch_dir[4] =
9640 int center_element = Feld[x][y]; /* should always be non-moving! */
9643 for (i = 0; i < NUM_DIRECTIONS; i++)
9645 int xx = x + xy[i][0];
9646 int yy = y + xy[i][1];
9647 int center_side = trigger_sides[i][0];
9648 int border_side = trigger_sides[i][1];
9651 if (!IN_LEV_FIELD(xx, yy))
9654 if (IS_PLAYER(x, y))
9656 struct PlayerInfo *player = PLAYERINFO(x, y);
9658 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9659 border_element = Feld[xx][yy]; /* may be moving! */
9660 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9661 border_element = Feld[xx][yy];
9662 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9663 border_element = MovingOrBlocked2Element(xx, yy);
9665 continue; /* center and border element do not touch */
9668 /* !!! TEST ONLY !!! */
9669 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9670 player->index_bit, border_side);
9671 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9672 CE_OTHER_GETS_TOUCHED,
9673 player->index_bit, border_side);
9675 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9676 CE_OTHER_GETS_TOUCHED,
9677 player->index_bit, border_side);
9678 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9679 player->index_bit, border_side);
9682 else if (IS_PLAYER(xx, yy))
9684 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9686 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9688 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9689 continue; /* center and border element do not touch */
9693 /* !!! TEST ONLY !!! */
9694 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9695 player->index_bit, center_side);
9696 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9697 CE_OTHER_GETS_TOUCHED,
9698 player->index_bit, center_side);
9700 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9701 CE_OTHER_GETS_TOUCHED,
9702 player->index_bit, center_side);
9703 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9704 player->index_bit, center_side);
9712 void TestIfElementTouchesCustomElement(int x, int y)
9714 static int xy[4][2] =
9721 static int trigger_sides[4][2] =
9723 /* center side border side */
9724 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9725 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9726 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9727 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9729 static int touch_dir[4] =
9736 boolean change_center_element = FALSE;
9737 int center_element_change_page = 0;
9738 int center_element = Feld[x][y]; /* should always be non-moving! */
9739 int border_trigger_element;
9742 for (i = 0; i < NUM_DIRECTIONS; i++)
9744 int xx = x + xy[i][0];
9745 int yy = y + xy[i][1];
9746 int center_side = trigger_sides[i][0];
9747 int border_side = trigger_sides[i][1];
9750 if (!IN_LEV_FIELD(xx, yy))
9753 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9754 border_element = Feld[xx][yy]; /* may be moving! */
9755 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9756 border_element = Feld[xx][yy];
9757 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9758 border_element = MovingOrBlocked2Element(xx, yy);
9760 continue; /* center and border element do not touch */
9762 /* check for change of center element (but change it only once) */
9763 if (IS_CUSTOM_ELEMENT(center_element) &&
9764 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9765 !change_center_element)
9767 for (j = 0; j < element_info[center_element].num_change_pages; j++)
9769 struct ElementChangeInfo *change =
9770 &element_info[center_element].change_page[j];
9772 if (change->can_change &&
9773 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9774 change->trigger_side & border_side &&
9776 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9778 change->trigger_element == border_element
9782 change_center_element = TRUE;
9783 center_element_change_page = j;
9784 border_trigger_element = border_element;
9791 /* check for change of border element */
9792 if (IS_CUSTOM_ELEMENT(border_element) &&
9793 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9795 for (j = 0; j < element_info[border_element].num_change_pages; j++)
9797 struct ElementChangeInfo *change =
9798 &element_info[border_element].change_page[j];
9800 if (change->can_change &&
9801 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9802 change->trigger_side & center_side &&
9804 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9806 change->trigger_element == center_element
9811 printf("::: border_element %d, %d\n", x, y);
9814 CheckElementChangeByPage(xx, yy, border_element, center_element,
9815 CE_OTHER_IS_TOUCHING, j);
9822 if (change_center_element)
9825 printf("::: center_element %d, %d\n", x, y);
9828 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
9829 CE_OTHER_IS_TOUCHING, center_element_change_page);
9833 void TestIfElementHitsCustomElement(int x, int y, int direction)
9835 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9836 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9837 int hitx = x + dx, hity = y + dy;
9838 int hitting_element = Feld[x][y];
9839 int touched_element;
9841 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9842 !IS_FREE(hitx, hity) &&
9843 (!IS_MOVING(hitx, hity) ||
9844 MovDir[hitx][hity] != direction ||
9845 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9848 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9852 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9856 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9857 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9859 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9860 CE_HITTING_SOMETHING, direction);
9862 if (IN_LEV_FIELD(hitx, hity))
9864 int opposite_direction = MV_DIR_OPPOSITE(direction);
9865 int hitting_side = direction;
9866 int touched_side = opposite_direction;
9868 int touched_element = MovingOrBlocked2Element(hitx, hity);
9871 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9872 MovDir[hitx][hity] != direction ||
9873 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9882 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9883 CE_HIT_BY_SOMETHING, opposite_direction);
9885 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9886 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
9888 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9890 struct ElementChangeInfo *change =
9891 &element_info[hitting_element].change_page[i];
9893 if (change->can_change &&
9894 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
9895 change->trigger_side & touched_side &&
9898 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9900 change->trigger_element == touched_element
9904 CheckElementChangeByPage(x, y, hitting_element, touched_element,
9905 CE_OTHER_IS_HITTING, i);
9911 if (IS_CUSTOM_ELEMENT(touched_element) &&
9912 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
9914 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9916 struct ElementChangeInfo *change =
9917 &element_info[touched_element].change_page[i];
9919 if (change->can_change &&
9920 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
9921 change->trigger_side & hitting_side &&
9923 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9925 change->trigger_element == hitting_element
9929 CheckElementChangeByPage(hitx, hity, touched_element,
9930 hitting_element, CE_OTHER_GETS_HIT, i);
9940 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9942 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9943 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9944 int hitx = x + dx, hity = y + dy;
9945 int hitting_element = Feld[x][y];
9946 int touched_element;
9948 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9949 !IS_FREE(hitx, hity) &&
9950 (!IS_MOVING(hitx, hity) ||
9951 MovDir[hitx][hity] != direction ||
9952 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9955 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9959 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9963 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9964 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9966 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9967 EP_CAN_SMASH_EVERYTHING, direction);
9969 if (IN_LEV_FIELD(hitx, hity))
9971 int opposite_direction = MV_DIR_OPPOSITE(direction);
9972 int hitting_side = direction;
9973 int touched_side = opposite_direction;
9975 int touched_element = MovingOrBlocked2Element(hitx, hity);
9978 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9979 MovDir[hitx][hity] != direction ||
9980 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9989 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9990 CE_SMASHED_BY_SOMETHING, opposite_direction);
9992 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9993 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
9995 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9997 struct ElementChangeInfo *change =
9998 &element_info[hitting_element].change_page[i];
10000 if (change->can_change &&
10001 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10002 change->trigger_side & touched_side &&
10005 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10007 change->trigger_element == touched_element
10011 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10012 CE_OTHER_IS_SMASHING, i);
10018 if (IS_CUSTOM_ELEMENT(touched_element) &&
10019 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10021 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10023 struct ElementChangeInfo *change =
10024 &element_info[touched_element].change_page[i];
10026 if (change->can_change &&
10027 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10028 change->trigger_side & hitting_side &&
10030 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10032 change->trigger_element == hitting_element
10036 CheckElementChangeByPage(hitx, hity, touched_element,
10037 hitting_element, CE_OTHER_GETS_SMASHED,i);
10047 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10049 int i, kill_x = -1, kill_y = -1;
10050 int bad_element = -1;
10051 static int test_xy[4][2] =
10058 static int test_dir[4] =
10066 for (i = 0; i < NUM_DIRECTIONS; i++)
10068 int test_x, test_y, test_move_dir, test_element;
10070 test_x = good_x + test_xy[i][0];
10071 test_y = good_y + test_xy[i][1];
10073 if (!IN_LEV_FIELD(test_x, test_y))
10077 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10080 test_element = Feld[test_x][test_y];
10082 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10085 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10086 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10088 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10089 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10093 bad_element = test_element;
10099 if (kill_x != -1 || kill_y != -1)
10101 if (IS_PLAYER(good_x, good_y))
10103 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10106 if (player->shield_deadly_time_left > 0 &&
10107 !IS_INDESTRUCTIBLE(bad_element))
10108 Bang(kill_x, kill_y);
10109 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10112 if (player->shield_deadly_time_left > 0)
10113 Bang(kill_x, kill_y);
10114 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10119 Bang(good_x, good_y);
10123 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10125 int i, kill_x = -1, kill_y = -1;
10126 int bad_element = Feld[bad_x][bad_y];
10127 static int test_xy[4][2] =
10134 static int touch_dir[4] =
10136 MV_LEFT | MV_RIGHT,
10141 static int test_dir[4] =
10149 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10152 for (i = 0; i < NUM_DIRECTIONS; i++)
10154 int test_x, test_y, test_move_dir, test_element;
10156 test_x = bad_x + test_xy[i][0];
10157 test_y = bad_y + test_xy[i][1];
10158 if (!IN_LEV_FIELD(test_x, test_y))
10162 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10164 test_element = Feld[test_x][test_y];
10166 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10167 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10169 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10170 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10172 /* good thing is player or penguin that does not move away */
10173 if (IS_PLAYER(test_x, test_y))
10175 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10177 if (bad_element == EL_ROBOT && player->is_moving)
10178 continue; /* robot does not kill player if he is moving */
10180 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10182 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10183 continue; /* center and border element do not touch */
10190 else if (test_element == EL_PENGUIN)
10199 if (kill_x != -1 || kill_y != -1)
10201 if (IS_PLAYER(kill_x, kill_y))
10203 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10206 if (player->shield_deadly_time_left > 0 &&
10207 !IS_INDESTRUCTIBLE(bad_element))
10208 Bang(bad_x, bad_y);
10209 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10212 if (player->shield_deadly_time_left > 0)
10213 Bang(bad_x, bad_y);
10214 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10219 Bang(kill_x, kill_y);
10223 void TestIfHeroTouchesBadThing(int x, int y)
10225 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10228 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10230 TestIfGoodThingHitsBadThing(x, y, move_dir);
10233 void TestIfBadThingTouchesHero(int x, int y)
10235 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10238 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10240 TestIfBadThingHitsGoodThing(x, y, move_dir);
10243 void TestIfFriendTouchesBadThing(int x, int y)
10245 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10248 void TestIfBadThingTouchesFriend(int x, int y)
10250 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10253 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10255 int i, kill_x = bad_x, kill_y = bad_y;
10256 static int xy[4][2] =
10264 for (i = 0; i < NUM_DIRECTIONS; i++)
10268 x = bad_x + xy[i][0];
10269 y = bad_y + xy[i][1];
10270 if (!IN_LEV_FIELD(x, y))
10273 element = Feld[x][y];
10274 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10275 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10283 if (kill_x != bad_x || kill_y != bad_y)
10284 Bang(bad_x, bad_y);
10287 void KillHero(struct PlayerInfo *player)
10289 int jx = player->jx, jy = player->jy;
10291 if (!player->active)
10294 /* remove accessible field at the player's position */
10295 Feld[jx][jy] = EL_EMPTY;
10297 /* deactivate shield (else Bang()/Explode() would not work right) */
10298 player->shield_normal_time_left = 0;
10299 player->shield_deadly_time_left = 0;
10305 static void KillHeroUnlessEnemyProtected(int x, int y)
10307 if (!PLAYER_ENEMY_PROTECTED(x, y))
10308 KillHero(PLAYERINFO(x, y));
10311 static void KillHeroUnlessExplosionProtected(int x, int y)
10313 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10314 KillHero(PLAYERINFO(x, y));
10317 void BuryHero(struct PlayerInfo *player)
10319 int jx = player->jx, jy = player->jy;
10321 if (!player->active)
10325 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10327 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10329 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10331 player->GameOver = TRUE;
10332 RemoveHero(player);
10335 void RemoveHero(struct PlayerInfo *player)
10337 int jx = player->jx, jy = player->jy;
10338 int i, found = FALSE;
10340 player->present = FALSE;
10341 player->active = FALSE;
10343 if (!ExplodeField[jx][jy])
10344 StorePlayer[jx][jy] = 0;
10346 for (i = 0; i < MAX_PLAYERS; i++)
10347 if (stored_player[i].active)
10351 AllPlayersGone = TRUE;
10358 =============================================================================
10359 checkDiagonalPushing()
10360 -----------------------------------------------------------------------------
10361 check if diagonal input device direction results in pushing of object
10362 (by checking if the alternative direction is walkable, diggable, ...)
10363 =============================================================================
10366 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10367 int x, int y, int real_dx, int real_dy)
10369 int jx, jy, dx, dy, xx, yy;
10371 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10374 /* diagonal direction: check alternative direction */
10379 xx = jx + (dx == 0 ? real_dx : 0);
10380 yy = jy + (dy == 0 ? real_dy : 0);
10382 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10386 =============================================================================
10388 -----------------------------------------------------------------------------
10389 x, y: field next to player (non-diagonal) to try to dig to
10390 real_dx, real_dy: direction as read from input device (can be diagonal)
10391 =============================================================================
10394 int DigField(struct PlayerInfo *player,
10395 int oldx, int oldy, int x, int y,
10396 int real_dx, int real_dy, int mode)
10398 static int trigger_sides[4] =
10400 CH_SIDE_RIGHT, /* moving left */
10401 CH_SIDE_LEFT, /* moving right */
10402 CH_SIDE_BOTTOM, /* moving up */
10403 CH_SIDE_TOP, /* moving down */
10406 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10408 int jx = oldx, jy = oldy;
10409 int dx = x - jx, dy = y - jy;
10410 int nextx = x + dx, nexty = y + dy;
10411 int move_direction = (dx == -1 ? MV_LEFT :
10412 dx == +1 ? MV_RIGHT :
10414 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10415 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10416 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10417 int old_element = Feld[jx][jy];
10420 if (player->MovPos == 0)
10422 player->is_digging = FALSE;
10423 player->is_collecting = FALSE;
10426 if (player->MovPos == 0) /* last pushing move finished */
10427 player->is_pushing = FALSE;
10429 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10431 player->is_switching = FALSE;
10432 player->push_delay = 0;
10434 return MF_NO_ACTION;
10437 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10438 return MF_NO_ACTION;
10443 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10445 if (IS_TUBE(Feld[jx][jy]) ||
10446 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10450 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10451 int tube_leave_directions[][2] =
10453 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10454 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10455 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10456 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
10457 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
10458 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
10459 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
10460 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
10461 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
10462 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
10463 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
10464 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10467 while (tube_leave_directions[i][0] != tube_element)
10470 if (tube_leave_directions[i][0] == -1) /* should not happen */
10474 if (!(tube_leave_directions[i][1] & move_direction))
10475 return MF_NO_ACTION; /* tube has no opening in this direction */
10480 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10481 old_element = Back[jx][jy];
10485 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10486 return MF_NO_ACTION; /* field has no opening in this direction */
10488 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10489 return MF_NO_ACTION; /* field has no opening in this direction */
10491 element = Feld[x][y];
10493 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10494 game.engine_version >= VERSION_IDENT(2,2,0,0))
10495 return MF_NO_ACTION;
10500 case EL_SP_PORT_LEFT:
10501 case EL_SP_PORT_RIGHT:
10502 case EL_SP_PORT_UP:
10503 case EL_SP_PORT_DOWN:
10504 case EL_SP_PORT_HORIZONTAL:
10505 case EL_SP_PORT_VERTICAL:
10506 case EL_SP_PORT_ANY:
10507 case EL_SP_GRAVITY_PORT_LEFT:
10508 case EL_SP_GRAVITY_PORT_RIGHT:
10509 case EL_SP_GRAVITY_PORT_UP:
10510 case EL_SP_GRAVITY_PORT_DOWN:
10512 if (!canEnterSupaplexPort(x, y, dx, dy))
10513 return MF_NO_ACTION;
10516 element != EL_SP_PORT_LEFT &&
10517 element != EL_SP_GRAVITY_PORT_LEFT &&
10518 element != EL_SP_PORT_HORIZONTAL &&
10519 element != EL_SP_PORT_ANY) ||
10521 element != EL_SP_PORT_RIGHT &&
10522 element != EL_SP_GRAVITY_PORT_RIGHT &&
10523 element != EL_SP_PORT_HORIZONTAL &&
10524 element != EL_SP_PORT_ANY) ||
10526 element != EL_SP_PORT_UP &&
10527 element != EL_SP_GRAVITY_PORT_UP &&
10528 element != EL_SP_PORT_VERTICAL &&
10529 element != EL_SP_PORT_ANY) ||
10531 element != EL_SP_PORT_DOWN &&
10532 element != EL_SP_GRAVITY_PORT_DOWN &&
10533 element != EL_SP_PORT_VERTICAL &&
10534 element != EL_SP_PORT_ANY) ||
10535 !IN_LEV_FIELD(nextx, nexty) ||
10536 !IS_FREE(nextx, nexty))
10537 return MF_NO_ACTION;
10540 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10541 element == EL_SP_GRAVITY_PORT_RIGHT ||
10542 element == EL_SP_GRAVITY_PORT_UP ||
10543 element == EL_SP_GRAVITY_PORT_DOWN)
10544 game.gravity = !game.gravity;
10546 /* automatically move to the next field with double speed */
10547 player->programmed_action = move_direction;
10549 if (player->move_delay_reset_counter == 0)
10551 player->move_delay_reset_counter = 2; /* two double speed steps */
10553 DOUBLE_PLAYER_SPEED(player);
10556 player->move_delay_reset_counter = 2;
10558 DOUBLE_PLAYER_SPEED(player);
10562 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10565 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10571 case EL_TUBE_VERTICAL:
10572 case EL_TUBE_HORIZONTAL:
10573 case EL_TUBE_VERTICAL_LEFT:
10574 case EL_TUBE_VERTICAL_RIGHT:
10575 case EL_TUBE_HORIZONTAL_UP:
10576 case EL_TUBE_HORIZONTAL_DOWN:
10577 case EL_TUBE_LEFT_UP:
10578 case EL_TUBE_LEFT_DOWN:
10579 case EL_TUBE_RIGHT_UP:
10580 case EL_TUBE_RIGHT_DOWN:
10583 int tube_enter_directions[][2] =
10585 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10586 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10587 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10588 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
10589 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
10590 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
10591 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
10592 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
10593 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
10594 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
10595 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
10596 { -1, MV_NO_MOVING }
10599 while (tube_enter_directions[i][0] != element)
10602 if (tube_enter_directions[i][0] == -1) /* should not happen */
10606 if (!(tube_enter_directions[i][1] & move_direction))
10607 return MF_NO_ACTION; /* tube has no opening in this direction */
10609 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10617 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10619 if (IS_WALKABLE(element))
10622 int sound_action = ACTION_WALKING;
10625 if (!ACCESS_FROM(element, opposite_direction))
10626 return MF_NO_ACTION; /* field not accessible from this direction */
10630 if (element == EL_EMPTY_SPACE &&
10631 game.gravity && !player->is_auto_moving &&
10632 canFallDown(player) && move_direction != MV_DOWN)
10633 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10636 if (IS_GATE(element))
10638 if (!player->key[element - EL_GATE_1])
10639 return MF_NO_ACTION;
10641 else if (IS_GATE_GRAY(element))
10643 if (!player->key[element - EL_GATE_1_GRAY])
10644 return MF_NO_ACTION;
10646 else if (element == EL_EXIT_OPEN ||
10647 element == EL_SP_EXIT_OPEN ||
10648 element == EL_SP_EXIT_OPENING)
10650 sound_action = ACTION_PASSING; /* player is passing exit */
10652 else if (element == EL_EMPTY)
10654 sound_action = ACTION_MOVING; /* nothing to walk on */
10657 /* play sound from background or player, whatever is available */
10658 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
10659 PlayLevelSoundElementAction(x, y, element, sound_action);
10661 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10666 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
10668 else if (IS_PASSABLE(element))
10672 if (!canPassField(x, y, move_direction))
10673 return MF_NO_ACTION;
10678 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
10679 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
10680 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
10681 return MF_NO_ACTION;
10683 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10684 return MF_NO_ACTION;
10689 if (!ACCESS_FROM(element, opposite_direction))
10690 return MF_NO_ACTION; /* field not accessible from this direction */
10692 if (IS_CUSTOM_ELEMENT(element) &&
10693 !ACCESS_FROM(element, opposite_direction))
10694 return MF_NO_ACTION; /* field not accessible from this direction */
10698 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10699 return MF_NO_ACTION;
10704 if (IS_EM_GATE(element))
10706 if (!player->key[element - EL_EM_GATE_1])
10707 return MF_NO_ACTION;
10709 else if (IS_EM_GATE_GRAY(element))
10711 if (!player->key[element - EL_EM_GATE_1_GRAY])
10712 return MF_NO_ACTION;
10714 else if (IS_SP_PORT(element))
10716 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10717 element == EL_SP_GRAVITY_PORT_RIGHT ||
10718 element == EL_SP_GRAVITY_PORT_UP ||
10719 element == EL_SP_GRAVITY_PORT_DOWN)
10720 game.gravity = !game.gravity;
10723 /* automatically move to the next field with double speed */
10724 player->programmed_action = move_direction;
10726 if (player->move_delay_reset_counter == 0)
10728 player->move_delay_reset_counter = 2; /* two double speed steps */
10730 DOUBLE_PLAYER_SPEED(player);
10733 player->move_delay_reset_counter = 2;
10735 DOUBLE_PLAYER_SPEED(player);
10738 PlayLevelSoundAction(x, y, ACTION_PASSING);
10742 else if (IS_DIGGABLE(element))
10746 if (mode != DF_SNAP)
10749 GfxElement[x][y] = GFX_ELEMENT(element);
10752 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
10754 player->is_digging = TRUE;
10757 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10759 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
10760 player->index_bit, dig_side);
10763 if (mode == DF_SNAP)
10764 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10769 else if (IS_COLLECTIBLE(element))
10773 if (mode != DF_SNAP)
10775 GfxElement[x][y] = element;
10776 player->is_collecting = TRUE;
10779 if (element == EL_SPEED_PILL)
10780 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10781 else if (element == EL_EXTRA_TIME && level.time > 0)
10784 DrawGameValue_Time(TimeLeft);
10786 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10788 player->shield_normal_time_left += 10;
10789 if (element == EL_SHIELD_DEADLY)
10790 player->shield_deadly_time_left += 10;
10792 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10794 if (player->inventory_size < MAX_INVENTORY_SIZE)
10795 player->inventory_element[player->inventory_size++] = element;
10797 DrawGameValue_Dynamite(local_player->inventory_size);
10799 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10801 player->dynabomb_count++;
10802 player->dynabombs_left++;
10804 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10806 player->dynabomb_size++;
10808 else if (element == EL_DYNABOMB_INCREASE_POWER)
10810 player->dynabomb_xl = TRUE;
10812 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
10813 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
10815 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
10816 element - EL_KEY_1 : element - EL_EM_KEY_1);
10818 player->key[key_nr] = TRUE;
10820 DrawGameValue_Keys(player);
10822 redraw_mask |= REDRAW_DOOR_1;
10824 else if (IS_ENVELOPE(element))
10827 player->show_envelope = element;
10829 ShowEnvelope(element - EL_ENVELOPE_1);
10832 else if (IS_DROPPABLE(element) ||
10833 IS_THROWABLE(element)) /* can be collected and dropped */
10837 if (element_info[element].collect_count == 0)
10838 player->inventory_infinite_element = element;
10840 for (i = 0; i < element_info[element].collect_count; i++)
10841 if (player->inventory_size < MAX_INVENTORY_SIZE)
10842 player->inventory_element[player->inventory_size++] = element;
10844 DrawGameValue_Dynamite(local_player->inventory_size);
10846 else if (element_info[element].collect_count > 0)
10848 local_player->gems_still_needed -=
10849 element_info[element].collect_count;
10850 if (local_player->gems_still_needed < 0)
10851 local_player->gems_still_needed = 0;
10853 DrawGameValue_Emeralds(local_player->gems_still_needed);
10856 RaiseScoreElement(element);
10857 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10859 CheckTriggeredElementChangeByPlayer(x, y, element,
10860 CE_OTHER_GETS_COLLECTED,
10861 player->index_bit, dig_side);
10864 if (mode == DF_SNAP)
10865 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10870 else if (IS_PUSHABLE(element))
10872 if (mode == DF_SNAP && element != EL_BD_ROCK)
10873 return MF_NO_ACTION;
10875 if (CAN_FALL(element) && dy)
10876 return MF_NO_ACTION;
10878 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10879 !(element == EL_SPRING && level.use_spring_bug))
10880 return MF_NO_ACTION;
10883 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10884 ((move_direction & MV_VERTICAL &&
10885 ((element_info[element].move_pattern & MV_LEFT &&
10886 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10887 (element_info[element].move_pattern & MV_RIGHT &&
10888 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10889 (move_direction & MV_HORIZONTAL &&
10890 ((element_info[element].move_pattern & MV_UP &&
10891 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10892 (element_info[element].move_pattern & MV_DOWN &&
10893 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10894 return MF_NO_ACTION;
10898 /* do not push elements already moving away faster than player */
10899 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10900 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10901 return MF_NO_ACTION;
10903 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
10904 return MF_NO_ACTION;
10908 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10910 if (player->push_delay_value == -1)
10911 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10913 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10915 if (!player->is_pushing)
10916 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10920 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
10921 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
10922 !player_is_pushing))
10923 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10926 if (!player->is_pushing &&
10927 game.engine_version >= VERSION_IDENT(2,2,0,7))
10928 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10932 printf("::: push delay: %ld [%d, %d] [%d]\n",
10933 player->push_delay_value, FrameCounter, game.engine_version,
10934 player->is_pushing);
10937 player->is_pushing = TRUE;
10939 if (!(IN_LEV_FIELD(nextx, nexty) &&
10940 (IS_FREE(nextx, nexty) ||
10941 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10942 IS_SB_ELEMENT(element)))))
10943 return MF_NO_ACTION;
10945 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10946 return MF_NO_ACTION;
10948 if (player->push_delay == 0) /* new pushing; restart delay */
10949 player->push_delay = FrameCounter;
10951 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
10952 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10953 element != EL_SPRING && element != EL_BALLOON)
10955 /* make sure that there is no move delay before next try to push */
10956 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10957 player->move_delay = INITIAL_MOVE_DELAY_OFF;
10959 return MF_NO_ACTION;
10963 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
10966 if (IS_SB_ELEMENT(element))
10968 if (element == EL_SOKOBAN_FIELD_FULL)
10970 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10971 local_player->sokobanfields_still_needed++;
10974 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10976 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10977 local_player->sokobanfields_still_needed--;
10980 Feld[x][y] = EL_SOKOBAN_OBJECT;
10982 if (Back[x][y] == Back[nextx][nexty])
10983 PlayLevelSoundAction(x, y, ACTION_PUSHING);
10984 else if (Back[x][y] != 0)
10985 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10988 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10991 if (local_player->sokobanfields_still_needed == 0 &&
10992 game.emulation == EMU_SOKOBAN)
10994 player->LevelSolved = player->GameOver = TRUE;
10995 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10999 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11001 InitMovingField(x, y, move_direction);
11002 GfxAction[x][y] = ACTION_PUSHING;
11004 if (mode == DF_SNAP)
11005 ContinueMoving(x, y);
11007 MovPos[x][y] = (dx != 0 ? dx : dy);
11009 Pushed[x][y] = TRUE;
11010 Pushed[nextx][nexty] = TRUE;
11012 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11013 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11015 player->push_delay_value = -1; /* get new value later */
11018 /* check for element change _after_ element has been pushed! */
11022 /* !!! TEST ONLY !!! */
11023 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11024 player->index_bit, dig_side);
11025 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11026 player->index_bit, dig_side);
11028 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11029 player->index_bit, dig_side);
11030 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11031 player->index_bit, dig_side);
11037 else if (IS_SWITCHABLE(element))
11039 if (PLAYER_SWITCHING(player, x, y))
11042 player->is_switching = TRUE;
11043 player->switch_x = x;
11044 player->switch_y = y;
11046 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11048 if (element == EL_ROBOT_WHEEL)
11050 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11054 DrawLevelField(x, y);
11056 else if (element == EL_SP_TERMINAL)
11060 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11062 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11064 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11065 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11068 else if (IS_BELT_SWITCH(element))
11070 ToggleBeltSwitch(x, y);
11072 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11073 element == EL_SWITCHGATE_SWITCH_DOWN)
11075 ToggleSwitchgateSwitch(x, y);
11077 else if (element == EL_LIGHT_SWITCH ||
11078 element == EL_LIGHT_SWITCH_ACTIVE)
11080 ToggleLightSwitch(x, y);
11083 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11084 SND_LIGHT_SWITCH_ACTIVATING :
11085 SND_LIGHT_SWITCH_DEACTIVATING);
11088 else if (element == EL_TIMEGATE_SWITCH)
11090 ActivateTimegateSwitch(x, y);
11092 else if (element == EL_BALLOON_SWITCH_LEFT ||
11093 element == EL_BALLOON_SWITCH_RIGHT ||
11094 element == EL_BALLOON_SWITCH_UP ||
11095 element == EL_BALLOON_SWITCH_DOWN ||
11096 element == EL_BALLOON_SWITCH_ANY)
11098 if (element == EL_BALLOON_SWITCH_ANY)
11099 game.balloon_dir = move_direction;
11101 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11102 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11103 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11104 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11107 else if (element == EL_LAMP)
11109 Feld[x][y] = EL_LAMP_ACTIVE;
11110 local_player->lights_still_needed--;
11112 DrawLevelField(x, y);
11114 else if (element == EL_TIME_ORB_FULL)
11116 Feld[x][y] = EL_TIME_ORB_EMPTY;
11118 DrawGameValue_Time(TimeLeft);
11120 DrawLevelField(x, y);
11123 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11131 if (!PLAYER_SWITCHING(player, x, y))
11133 player->is_switching = TRUE;
11134 player->switch_x = x;
11135 player->switch_y = y;
11138 /* !!! TEST ONLY !!! */
11139 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11140 player->index_bit, dig_side);
11141 CheckTriggeredElementChangeByPlayer(x, y, element,
11142 CE_OTHER_IS_SWITCHING,
11143 player->index_bit, dig_side);
11145 CheckTriggeredElementChangeByPlayer(x, y, element,
11146 CE_OTHER_IS_SWITCHING,
11147 player->index_bit, dig_side);
11148 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11149 player->index_bit, dig_side);
11154 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11155 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11156 player->index_bit, dig_side);
11157 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11158 player->index_bit, dig_side);
11160 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11161 player->index_bit, dig_side);
11162 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11163 player->index_bit, dig_side);
11167 return MF_NO_ACTION;
11170 player->push_delay = 0;
11172 if (Feld[x][y] != element) /* really digged/collected something */
11173 player->is_collecting = !player->is_digging;
11178 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11180 int jx = player->jx, jy = player->jy;
11181 int x = jx + dx, y = jy + dy;
11182 int snap_direction = (dx == -1 ? MV_LEFT :
11183 dx == +1 ? MV_RIGHT :
11185 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11188 if (player->MovPos)
11191 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
11195 if (!player->active || !IN_LEV_FIELD(x, y))
11203 if (player->MovPos == 0)
11204 player->is_pushing = FALSE;
11206 player->is_snapping = FALSE;
11208 if (player->MovPos == 0)
11210 player->is_moving = FALSE;
11211 player->is_digging = FALSE;
11212 player->is_collecting = FALSE;
11218 if (player->is_snapping)
11221 player->MovDir = snap_direction;
11224 if (player->MovPos == 0)
11227 player->is_moving = FALSE;
11228 player->is_digging = FALSE;
11229 player->is_collecting = FALSE;
11232 player->is_dropping = FALSE;
11234 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11237 player->is_snapping = TRUE;
11240 if (player->MovPos == 0)
11243 player->is_moving = FALSE;
11244 player->is_digging = FALSE;
11245 player->is_collecting = FALSE;
11248 DrawLevelField(x, y);
11254 boolean DropElement(struct PlayerInfo *player)
11256 static int trigger_sides[4] =
11258 CH_SIDE_LEFT, /* dropping left */
11259 CH_SIDE_RIGHT, /* dropping right */
11260 CH_SIDE_TOP, /* dropping up */
11261 CH_SIDE_BOTTOM, /* dropping down */
11263 int old_element, new_element;
11264 int dropx = player->jx, dropy = player->jy;
11265 int drop_direction = player->MovDir;
11266 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11267 int drop_element = (player->inventory_size > 0 ?
11268 player->inventory_element[player->inventory_size - 1] :
11269 player->inventory_infinite_element != EL_UNDEFINED ?
11270 player->inventory_infinite_element :
11271 player->dynabombs_left > 0 ?
11272 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11275 if (IS_THROWABLE(drop_element))
11277 dropx += GET_DX_FROM_DIR(drop_direction);
11278 dropy += GET_DY_FROM_DIR(drop_direction);
11280 if (!IN_LEV_FIELD(dropx, dropy))
11284 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11285 new_element = drop_element; /* default: no change when dropping */
11287 /* check if player is active, not moving and ready to drop */
11288 if (!player->active || player->MovPos || player->drop_delay > 0)
11291 /* check if player has anything that can be dropped */
11293 if (new_element == EL_UNDEFINED)
11296 if (player->inventory_size == 0 &&
11297 player->inventory_infinite_element == EL_UNDEFINED &&
11298 player->dynabombs_left == 0)
11302 /* check if anything can be dropped at the current position */
11303 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11306 /* collected custom elements can only be dropped on empty fields */
11308 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11311 if (player->inventory_size > 0 &&
11312 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11313 && old_element != EL_EMPTY)
11317 if (old_element != EL_EMPTY)
11318 Back[dropx][dropy] = old_element; /* store old element on this field */
11320 ResetGfxAnimation(dropx, dropy);
11321 ResetRandomAnimationValue(dropx, dropy);
11323 if (player->inventory_size > 0 ||
11324 player->inventory_infinite_element != EL_UNDEFINED)
11326 if (player->inventory_size > 0)
11328 player->inventory_size--;
11331 new_element = player->inventory_element[player->inventory_size];
11334 DrawGameValue_Dynamite(local_player->inventory_size);
11336 if (new_element == EL_DYNAMITE)
11337 new_element = EL_DYNAMITE_ACTIVE;
11338 else if (new_element == EL_SP_DISK_RED)
11339 new_element = EL_SP_DISK_RED_ACTIVE;
11342 Feld[dropx][dropy] = new_element;
11344 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11345 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11346 el2img(Feld[dropx][dropy]), 0);
11348 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11351 /* needed if previous element just changed to "empty" in the last frame */
11352 Changed[dropx][dropy] = 0; /* allow another change */
11356 /* !!! TEST ONLY !!! */
11357 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11358 player->index_bit, drop_side);
11359 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11360 CE_OTHER_GETS_DROPPED,
11361 player->index_bit, drop_side);
11363 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11364 CE_OTHER_GETS_DROPPED,
11365 player->index_bit, drop_side);
11366 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11367 player->index_bit, drop_side);
11370 TestIfElementTouchesCustomElement(dropx, dropy);
11372 else /* player is dropping a dyna bomb */
11374 player->dynabombs_left--;
11377 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
11380 Feld[dropx][dropy] = new_element;
11382 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11383 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11384 el2img(Feld[dropx][dropy]), 0);
11386 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11393 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11396 InitField_WithBug1(dropx, dropy, FALSE);
11398 InitField(dropx, dropy, FALSE);
11399 if (CAN_MOVE(Feld[dropx][dropy]))
11400 InitMovDir(dropx, dropy);
11404 new_element = Feld[dropx][dropy]; /* element might have changed */
11406 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11407 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11410 int move_stepsize = element_info[new_element].move_stepsize;
11412 int move_direction, nextx, nexty;
11414 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11415 MovDir[dropx][dropy] = drop_direction;
11417 move_direction = MovDir[dropx][dropy];
11418 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11419 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11422 Changed[dropx][dropy] = 0; /* allow another change */
11423 CheckCollision[dropx][dropy] = 2;
11426 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
11429 WasJustMoving[dropx][dropy] = 3;
11432 InitMovingField(dropx, dropy, move_direction);
11433 ContinueMoving(dropx, dropy);
11440 Changed[dropx][dropy] = 0; /* allow another change */
11443 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
11445 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
11446 CE_HITTING_SOMETHING, move_direction);
11454 player->drop_delay = 2 * TILEX / move_stepsize + 1;
11459 player->drop_delay = 8 + 8 + 8;
11463 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11468 player->is_dropping = TRUE;
11474 /* ------------------------------------------------------------------------- */
11475 /* game sound playing functions */
11476 /* ------------------------------------------------------------------------- */
11478 static int *loop_sound_frame = NULL;
11479 static int *loop_sound_volume = NULL;
11481 void InitPlayLevelSound()
11483 int num_sounds = getSoundListSize();
11485 checked_free(loop_sound_frame);
11486 checked_free(loop_sound_volume);
11488 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11489 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11492 static void PlayLevelSound(int x, int y, int nr)
11494 int sx = SCREENX(x), sy = SCREENY(y);
11495 int volume, stereo_position;
11496 int max_distance = 8;
11497 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11499 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11500 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11503 if (!IN_LEV_FIELD(x, y) ||
11504 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11505 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11508 volume = SOUND_MAX_VOLUME;
11510 if (!IN_SCR_FIELD(sx, sy))
11512 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11513 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11515 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11518 stereo_position = (SOUND_MAX_LEFT +
11519 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11520 (SCR_FIELDX + 2 * max_distance));
11522 if (IS_LOOP_SOUND(nr))
11524 /* This assures that quieter loop sounds do not overwrite louder ones,
11525 while restarting sound volume comparison with each new game frame. */
11527 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11530 loop_sound_volume[nr] = volume;
11531 loop_sound_frame[nr] = FrameCounter;
11534 PlaySoundExt(nr, volume, stereo_position, type);
11537 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11539 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11540 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11541 y < LEVELY(BY1) ? LEVELY(BY1) :
11542 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11546 static void PlayLevelSoundAction(int x, int y, int action)
11548 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11551 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11553 int sound_effect = element_info[element].sound[action];
11555 if (sound_effect != SND_UNDEFINED)
11556 PlayLevelSound(x, y, sound_effect);
11559 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11562 int sound_effect = element_info[element].sound[action];
11564 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11565 PlayLevelSound(x, y, sound_effect);
11568 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11570 int sound_effect = element_info[Feld[x][y]].sound[action];
11572 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11573 PlayLevelSound(x, y, sound_effect);
11576 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11578 int sound_effect = element_info[Feld[x][y]].sound[action];
11580 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11581 StopSound(sound_effect);
11584 static void PlayLevelMusic()
11586 if (levelset.music[level_nr] != MUS_UNDEFINED)
11587 PlayMusic(levelset.music[level_nr]); /* from config file */
11589 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11592 void RaiseScore(int value)
11594 local_player->score += value;
11596 DrawGameValue_Score(local_player->score);
11599 void RaiseScoreElement(int element)
11604 case EL_BD_DIAMOND:
11605 case EL_EMERALD_YELLOW:
11606 case EL_EMERALD_RED:
11607 case EL_EMERALD_PURPLE:
11608 case EL_SP_INFOTRON:
11609 RaiseScore(level.score[SC_EMERALD]);
11612 RaiseScore(level.score[SC_DIAMOND]);
11615 RaiseScore(level.score[SC_CRYSTAL]);
11618 RaiseScore(level.score[SC_PEARL]);
11621 case EL_BD_BUTTERFLY:
11622 case EL_SP_ELECTRON:
11623 RaiseScore(level.score[SC_BUG]);
11626 case EL_BD_FIREFLY:
11627 case EL_SP_SNIKSNAK:
11628 RaiseScore(level.score[SC_SPACESHIP]);
11631 case EL_DARK_YAMYAM:
11632 RaiseScore(level.score[SC_YAMYAM]);
11635 RaiseScore(level.score[SC_ROBOT]);
11638 RaiseScore(level.score[SC_PACMAN]);
11641 RaiseScore(level.score[SC_NUT]);
11644 case EL_SP_DISK_RED:
11645 case EL_DYNABOMB_INCREASE_NUMBER:
11646 case EL_DYNABOMB_INCREASE_SIZE:
11647 case EL_DYNABOMB_INCREASE_POWER:
11648 RaiseScore(level.score[SC_DYNAMITE]);
11650 case EL_SHIELD_NORMAL:
11651 case EL_SHIELD_DEADLY:
11652 RaiseScore(level.score[SC_SHIELD]);
11654 case EL_EXTRA_TIME:
11655 RaiseScore(level.score[SC_TIME_BONUS]);
11661 RaiseScore(level.score[SC_KEY]);
11664 RaiseScore(element_info[element].collect_score);
11669 void RequestQuitGame(boolean ask_if_really_quit)
11671 if (AllPlayersGone ||
11672 !ask_if_really_quit ||
11673 level_editor_test_game ||
11674 Request("Do you really want to quit the game ?",
11675 REQ_ASK | REQ_STAY_CLOSED))
11677 #if defined(PLATFORM_UNIX)
11678 if (options.network)
11679 SendToServer_StopPlaying();
11683 game_status = GAME_MODE_MAIN;
11691 if (tape.playing && tape.deactivate_display)
11692 TapeDeactivateDisplayOff(TRUE);
11695 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11698 if (tape.playing && tape.deactivate_display)
11699 TapeDeactivateDisplayOn();
11706 /* ---------- new game button stuff ---------------------------------------- */
11708 /* graphic position values for game buttons */
11709 #define GAME_BUTTON_XSIZE 30
11710 #define GAME_BUTTON_YSIZE 30
11711 #define GAME_BUTTON_XPOS 5
11712 #define GAME_BUTTON_YPOS 215
11713 #define SOUND_BUTTON_XPOS 5
11714 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11716 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11717 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11718 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11719 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11720 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11721 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11728 } gamebutton_info[NUM_GAME_BUTTONS] =
11731 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11736 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11737 GAME_CTRL_ID_PAUSE,
11741 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11746 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11747 SOUND_CTRL_ID_MUSIC,
11748 "background music on/off"
11751 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11752 SOUND_CTRL_ID_LOOPS,
11753 "sound loops on/off"
11756 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11757 SOUND_CTRL_ID_SIMPLE,
11758 "normal sounds on/off"
11762 void CreateGameButtons()
11766 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11768 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11769 struct GadgetInfo *gi;
11772 unsigned long event_mask;
11773 int gd_xoffset, gd_yoffset;
11774 int gd_x1, gd_x2, gd_y1, gd_y2;
11777 gd_xoffset = gamebutton_info[i].x;
11778 gd_yoffset = gamebutton_info[i].y;
11779 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11780 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11782 if (id == GAME_CTRL_ID_STOP ||
11783 id == GAME_CTRL_ID_PAUSE ||
11784 id == GAME_CTRL_ID_PLAY)
11786 button_type = GD_TYPE_NORMAL_BUTTON;
11788 event_mask = GD_EVENT_RELEASED;
11789 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11790 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11794 button_type = GD_TYPE_CHECK_BUTTON;
11796 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11797 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11798 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11799 event_mask = GD_EVENT_PRESSED;
11800 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11801 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11804 gi = CreateGadget(GDI_CUSTOM_ID, id,
11805 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11806 GDI_X, DX + gd_xoffset,
11807 GDI_Y, DY + gd_yoffset,
11808 GDI_WIDTH, GAME_BUTTON_XSIZE,
11809 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11810 GDI_TYPE, button_type,
11811 GDI_STATE, GD_BUTTON_UNPRESSED,
11812 GDI_CHECKED, checked,
11813 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11814 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11815 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11816 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11817 GDI_EVENT_MASK, event_mask,
11818 GDI_CALLBACK_ACTION, HandleGameButtons,
11822 Error(ERR_EXIT, "cannot create gadget");
11824 game_gadget[id] = gi;
11828 void FreeGameButtons()
11832 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11833 FreeGadget(game_gadget[i]);
11836 static void MapGameButtons()
11840 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11841 MapGadget(game_gadget[i]);
11844 void UnmapGameButtons()
11848 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11849 UnmapGadget(game_gadget[i]);
11852 static void HandleGameButtons(struct GadgetInfo *gi)
11854 int id = gi->custom_id;
11856 if (game_status != GAME_MODE_PLAYING)
11861 case GAME_CTRL_ID_STOP:
11862 RequestQuitGame(TRUE);
11865 case GAME_CTRL_ID_PAUSE:
11866 if (options.network)
11868 #if defined(PLATFORM_UNIX)
11870 SendToServer_ContinuePlaying();
11872 SendToServer_PausePlaying();
11876 TapeTogglePause(TAPE_TOGGLE_MANUAL);
11879 case GAME_CTRL_ID_PLAY:
11882 #if defined(PLATFORM_UNIX)
11883 if (options.network)
11884 SendToServer_ContinuePlaying();
11888 tape.pausing = FALSE;
11889 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11894 case SOUND_CTRL_ID_MUSIC:
11895 if (setup.sound_music)
11897 setup.sound_music = FALSE;
11900 else if (audio.music_available)
11902 setup.sound = setup.sound_music = TRUE;
11904 SetAudioMode(setup.sound);
11910 case SOUND_CTRL_ID_LOOPS:
11911 if (setup.sound_loops)
11912 setup.sound_loops = FALSE;
11913 else if (audio.loops_available)
11915 setup.sound = setup.sound_loops = TRUE;
11916 SetAudioMode(setup.sound);
11920 case SOUND_CTRL_ID_SIMPLE:
11921 if (setup.sound_simple)
11922 setup.sound_simple = FALSE;
11923 else if (audio.sound_available)
11925 setup.sound = setup.sound_simple = TRUE;
11926 SetAudioMode(setup.sound);