1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
35 /* for MovePlayer() */
36 #define MF_NO_ACTION 0
40 /* for ScrollPlayer() */
42 #define SCROLL_GO_ON 1
45 #define EX_PHASE_START 0
46 #define EX_TYPE_NONE 0
47 #define EX_TYPE_NORMAL (1 << 0)
48 #define EX_TYPE_CENTER (1 << 1)
49 #define EX_TYPE_BORDER (1 << 2)
50 #define EX_TYPE_CROSS (1 << 3)
51 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
53 /* special positions in the game control window (relative to control window) */
56 #define XX_EMERALDS 29
57 #define YY_EMERALDS 54
58 #define XX_DYNAMITE 29
59 #define YY_DYNAMITE 89
68 /* special positions in the game control window (relative to main window) */
69 #define DX_LEVEL (DX + XX_LEVEL)
70 #define DY_LEVEL (DY + YY_LEVEL)
71 #define DX_EMERALDS (DX + XX_EMERALDS)
72 #define DY_EMERALDS (DY + YY_EMERALDS)
73 #define DX_DYNAMITE (DX + XX_DYNAMITE)
74 #define DY_DYNAMITE (DY + YY_DYNAMITE)
75 #define DX_KEYS (DX + XX_KEYS)
76 #define DY_KEYS (DY + YY_KEYS)
77 #define DX_SCORE (DX + XX_SCORE)
78 #define DY_SCORE (DY + YY_SCORE)
79 #define DX_TIME1 (DX + XX_TIME1)
80 #define DX_TIME2 (DX + XX_TIME2)
81 #define DY_TIME (DY + YY_TIME)
83 /* values for initial player move delay (initial delay counter value) */
84 #define INITIAL_MOVE_DELAY_OFF -1
85 #define INITIAL_MOVE_DELAY_ON 0
87 /* values for player movement speed (which is in fact a delay value) */
88 #define MOVE_DELAY_NORMAL_SPEED 8
89 #define MOVE_DELAY_HIGH_SPEED 4
91 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
92 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
93 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
94 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
96 /* values for other actions */
97 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
99 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
100 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
102 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
104 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
105 RND(element_info[e].push_delay_random))
106 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
107 RND(element_info[e].drop_delay_random))
108 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
109 RND(element_info[e].move_delay_random))
110 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
111 (element_info[e].move_delay_random))
113 #define GET_TARGET_ELEMENT(e, ch) \
114 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
115 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
117 #define CAN_GROW_INTO(e) \
118 (e == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
120 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
121 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
124 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
125 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
126 (CAN_MOVE_INTO_ACID(e) && \
127 Feld[x][y] == EL_ACID) || \
130 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
131 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
132 (CAN_MOVE_INTO_ACID(e) && \
133 Feld[x][y] == EL_ACID) || \
136 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
137 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
139 (CAN_MOVE_INTO_ACID(e) && \
140 Feld[x][y] == EL_ACID) || \
141 (DONT_COLLIDE_WITH(e) && \
143 !PLAYER_ENEMY_PROTECTED(x, y))))
146 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
147 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
149 (DONT_COLLIDE_WITH(e) && \
151 !PLAYER_ENEMY_PROTECTED(x, y))))
154 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
155 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
158 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
159 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
161 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
162 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
166 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
169 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
170 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
174 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
175 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
177 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
178 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
180 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
181 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
183 #define PIG_CAN_ENTER_FIELD(e, x, y) \
184 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
186 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
187 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
188 IS_FOOD_PENGUIN(Feld[x][y])))
189 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
192 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
193 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
195 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
196 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
200 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
201 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
202 (CAN_MOVE_INTO_ACID(e) && \
203 Feld[x][y] == EL_ACID) || \
204 Feld[x][y] == EL_DIAMOND))
206 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
207 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
208 (CAN_MOVE_INTO_ACID(e) && \
209 Feld[x][y] == EL_ACID) || \
210 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
212 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
213 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
214 (CAN_MOVE_INTO_ACID(e) && \
215 Feld[x][y] == EL_ACID) || \
216 IS_AMOEBOID(Feld[x][y])))
218 #define PIG_CAN_ENTER_FIELD(e, x, y) \
219 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
220 (CAN_MOVE_INTO_ACID(e) && \
221 Feld[x][y] == EL_ACID) || \
222 IS_FOOD_PIG(Feld[x][y])))
224 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
225 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
226 (CAN_MOVE_INTO_ACID(e) && \
227 Feld[x][y] == EL_ACID) || \
228 IS_FOOD_PENGUIN(Feld[x][y]) || \
229 Feld[x][y] == EL_EXIT_OPEN))
231 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
232 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
233 (CAN_MOVE_INTO_ACID(e) && \
234 Feld[x][y] == EL_ACID)))
236 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
237 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
238 (CAN_MOVE_INTO_ACID(e) && \
239 Feld[x][y] == EL_ACID) || \
242 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
243 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
244 (CAN_MOVE_INTO_ACID(e) && \
245 Feld[x][y] == EL_ACID)))
249 #define GROUP_NR(e) ((e) - EL_GROUP_START)
250 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
251 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
252 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
254 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
255 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
258 #define CE_ENTER_FIELD_COND(e, x, y) \
259 (!IS_PLAYER(x, y) && \
260 (Feld[x][y] == EL_ACID || \
261 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
263 #define CE_ENTER_FIELD_COND(e, x, y) \
264 (!IS_PLAYER(x, y) && \
265 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
268 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
269 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
271 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
272 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
274 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
275 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
276 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
277 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
279 /* game button identifiers */
280 #define GAME_CTRL_ID_STOP 0
281 #define GAME_CTRL_ID_PAUSE 1
282 #define GAME_CTRL_ID_PLAY 2
283 #define SOUND_CTRL_ID_MUSIC 3
284 #define SOUND_CTRL_ID_LOOPS 4
285 #define SOUND_CTRL_ID_SIMPLE 5
287 #define NUM_GAME_BUTTONS 6
290 /* forward declaration for internal use */
292 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
293 static boolean MovePlayer(struct PlayerInfo *, int, int);
294 static void ScrollPlayer(struct PlayerInfo *, int);
295 static void ScrollScreen(struct PlayerInfo *, int);
297 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
299 static void InitBeltMovement(void);
300 static void CloseAllOpenTimegates(void);
301 static void CheckGravityMovement(struct PlayerInfo *);
302 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
303 static void KillHeroUnlessEnemyProtected(int, int);
304 static void KillHeroUnlessExplosionProtected(int, int);
306 static void TestIfPlayerTouchesCustomElement(int, int);
307 static void TestIfElementTouchesCustomElement(int, int);
308 static void TestIfElementHitsCustomElement(int, int, int);
310 static void TestIfElementSmashesCustomElement(int, int, int);
313 static void ChangeElement(int, int, int);
315 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
316 #define CheckTriggeredElementChange(x, y, e, ev) \
317 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
319 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
320 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
321 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
322 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
323 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
324 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
327 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
328 #define CheckElementChange(x, y, e, te, ev) \
329 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
330 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
331 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
332 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
333 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
334 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
335 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
337 static void PlayLevelSound(int, int, int);
338 static void PlayLevelSoundNearest(int, int, int);
339 static void PlayLevelSoundAction(int, int, int);
340 static void PlayLevelSoundElementAction(int, int, int, int);
341 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
342 static void PlayLevelSoundActionIfLoop(int, int, int);
343 static void StopLevelSoundActionIfLoop(int, int, int);
344 static void PlayLevelMusic();
346 static void MapGameButtons();
347 static void HandleGameButtons(struct GadgetInfo *);
349 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
352 /* ------------------------------------------------------------------------- */
353 /* definition of elements that automatically change to other elements after */
354 /* a specified time, eventually calling a function when changing */
355 /* ------------------------------------------------------------------------- */
357 /* forward declaration for changer functions */
358 static void InitBuggyBase(int x, int y);
359 static void WarnBuggyBase(int x, int y);
361 static void InitTrap(int x, int y);
362 static void ActivateTrap(int x, int y);
363 static void ChangeActiveTrap(int x, int y);
365 static void InitRobotWheel(int x, int y);
366 static void RunRobotWheel(int x, int y);
367 static void StopRobotWheel(int x, int y);
369 static void InitTimegateWheel(int x, int y);
370 static void RunTimegateWheel(int x, int y);
372 struct ChangingElementInfo
377 void (*pre_change_function)(int x, int y);
378 void (*change_function)(int x, int y);
379 void (*post_change_function)(int x, int y);
382 static struct ChangingElementInfo change_delay_list[] =
433 EL_SWITCHGATE_OPENING,
441 EL_SWITCHGATE_CLOSING,
442 EL_SWITCHGATE_CLOSED,
474 EL_ACID_SPLASH_RIGHT,
483 EL_SP_BUGGY_BASE_ACTIVATING,
490 EL_SP_BUGGY_BASE_ACTIVATING,
491 EL_SP_BUGGY_BASE_ACTIVE,
498 EL_SP_BUGGY_BASE_ACTIVE,
522 EL_ROBOT_WHEEL_ACTIVE,
530 EL_TIMEGATE_SWITCH_ACTIVE,
551 int push_delay_fixed, push_delay_random;
556 { EL_BALLOON, 0, 0 },
558 { EL_SOKOBAN_OBJECT, 2, 0 },
559 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
560 { EL_SATELLITE, 2, 0 },
561 { EL_SP_DISK_YELLOW, 2, 0 },
563 { EL_UNDEFINED, 0, 0 },
571 move_stepsize_list[] =
573 { EL_AMOEBA_DROP, 2 },
574 { EL_AMOEBA_DROPPING, 2 },
575 { EL_QUICKSAND_FILLING, 1 },
576 { EL_QUICKSAND_EMPTYING, 1 },
577 { EL_MAGIC_WALL_FILLING, 2 },
578 { EL_BD_MAGIC_WALL_FILLING, 2 },
579 { EL_MAGIC_WALL_EMPTYING, 2 },
580 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
590 collect_count_list[] =
593 { EL_BD_DIAMOND, 1 },
594 { EL_EMERALD_YELLOW, 1 },
595 { EL_EMERALD_RED, 1 },
596 { EL_EMERALD_PURPLE, 1 },
598 { EL_SP_INFOTRON, 1 },
610 access_direction_list[] =
612 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
613 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
614 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
615 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
616 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
617 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
618 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
619 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
620 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
621 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
622 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
624 { EL_SP_PORT_LEFT, MV_RIGHT },
625 { EL_SP_PORT_RIGHT, MV_LEFT },
626 { EL_SP_PORT_UP, MV_DOWN },
627 { EL_SP_PORT_DOWN, MV_UP },
628 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
629 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
630 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
631 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
632 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
633 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
634 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
636 { EL_UNDEFINED, MV_NO_MOVING }
639 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
641 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
642 CH_EVENT_BIT(CE_DELAY))
643 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
644 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
645 IS_JUST_CHANGING(x, y))
647 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
650 void GetPlayerConfig()
652 if (!audio.sound_available)
653 setup.sound_simple = FALSE;
655 if (!audio.loops_available)
656 setup.sound_loops = FALSE;
658 if (!audio.music_available)
659 setup.sound_music = FALSE;
661 if (!video.fullscreen_available)
662 setup.fullscreen = FALSE;
664 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
666 SetAudioMode(setup.sound);
670 static int getBeltNrFromBeltElement(int element)
672 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
673 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
674 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
677 static int getBeltNrFromBeltActiveElement(int element)
679 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
680 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
681 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
684 static int getBeltNrFromBeltSwitchElement(int element)
686 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
687 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
688 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
691 static int getBeltDirNrFromBeltSwitchElement(int element)
693 static int belt_base_element[4] =
695 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
696 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
697 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
698 EL_CONVEYOR_BELT_4_SWITCH_LEFT
701 int belt_nr = getBeltNrFromBeltSwitchElement(element);
702 int belt_dir_nr = element - belt_base_element[belt_nr];
704 return (belt_dir_nr % 3);
707 static int getBeltDirFromBeltSwitchElement(int element)
709 static int belt_move_dir[3] =
716 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
718 return belt_move_dir[belt_dir_nr];
721 static void InitPlayerField(int x, int y, int element, boolean init_game)
723 if (element == EL_SP_MURPHY)
727 if (stored_player[0].present)
729 Feld[x][y] = EL_SP_MURPHY_CLONE;
735 stored_player[0].use_murphy_graphic = TRUE;
738 Feld[x][y] = EL_PLAYER_1;
744 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
745 int jx = player->jx, jy = player->jy;
747 player->present = TRUE;
749 player->block_last_field = (element == EL_SP_MURPHY ?
750 level.sp_block_last_field :
751 level.block_last_field);
753 if (!options.network || player->connected)
755 player->active = TRUE;
757 /* remove potentially duplicate players */
758 if (StorePlayer[jx][jy] == Feld[x][y])
759 StorePlayer[jx][jy] = 0;
761 StorePlayer[x][y] = Feld[x][y];
765 printf("Player %d activated.\n", player->element_nr);
766 printf("[Local player is %d and currently %s.]\n",
767 local_player->element_nr,
768 local_player->active ? "active" : "not active");
772 Feld[x][y] = EL_EMPTY;
774 player->jx = player->last_jx = x;
775 player->jy = player->last_jy = y;
779 static void InitField(int x, int y, boolean init_game)
781 int element = Feld[x][y];
790 InitPlayerField(x, y, element, init_game);
793 case EL_SOKOBAN_FIELD_PLAYER:
794 element = Feld[x][y] = EL_PLAYER_1;
795 InitField(x, y, init_game);
797 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
798 InitField(x, y, init_game);
801 case EL_SOKOBAN_FIELD_EMPTY:
802 local_player->sokobanfields_still_needed++;
806 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
807 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
808 else if (x > 0 && Feld[x-1][y] == EL_ACID)
809 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
810 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
811 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
812 else if (y > 0 && Feld[x][y-1] == EL_ACID)
813 Feld[x][y] = EL_ACID_POOL_BOTTOM;
814 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
815 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
823 case EL_SPACESHIP_RIGHT:
824 case EL_SPACESHIP_UP:
825 case EL_SPACESHIP_LEFT:
826 case EL_SPACESHIP_DOWN:
828 case EL_BD_BUTTERFLY_RIGHT:
829 case EL_BD_BUTTERFLY_UP:
830 case EL_BD_BUTTERFLY_LEFT:
831 case EL_BD_BUTTERFLY_DOWN:
832 case EL_BD_BUTTERFLY:
833 case EL_BD_FIREFLY_RIGHT:
834 case EL_BD_FIREFLY_UP:
835 case EL_BD_FIREFLY_LEFT:
836 case EL_BD_FIREFLY_DOWN:
838 case EL_PACMAN_RIGHT:
862 if (y == lev_fieldy - 1)
864 Feld[x][y] = EL_AMOEBA_GROWING;
865 Store[x][y] = EL_AMOEBA_WET;
869 case EL_DYNAMITE_ACTIVE:
870 case EL_SP_DISK_RED_ACTIVE:
871 case EL_DYNABOMB_PLAYER_1_ACTIVE:
872 case EL_DYNABOMB_PLAYER_2_ACTIVE:
873 case EL_DYNABOMB_PLAYER_3_ACTIVE:
874 case EL_DYNABOMB_PLAYER_4_ACTIVE:
879 local_player->lights_still_needed++;
883 local_player->friends_still_needed++;
888 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
893 Feld[x][y] = EL_EMPTY;
898 case EL_EM_KEY_1_FILE:
899 Feld[x][y] = EL_EM_KEY_1;
901 case EL_EM_KEY_2_FILE:
902 Feld[x][y] = EL_EM_KEY_2;
904 case EL_EM_KEY_3_FILE:
905 Feld[x][y] = EL_EM_KEY_3;
907 case EL_EM_KEY_4_FILE:
908 Feld[x][y] = EL_EM_KEY_4;
912 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
913 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
914 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
915 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
916 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
917 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
918 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
919 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
920 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
921 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
922 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
923 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
926 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
927 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
928 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
930 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
932 game.belt_dir[belt_nr] = belt_dir;
933 game.belt_dir_nr[belt_nr] = belt_dir_nr;
935 else /* more than one switch -- set it like the first switch */
937 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
942 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
944 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
947 case EL_LIGHT_SWITCH_ACTIVE:
949 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
953 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
955 else if (IS_GROUP_ELEMENT(element))
957 struct ElementGroupInfo *group = element_info[element].group;
958 int last_anim_random_frame = gfx.anim_random_frame;
961 if (group->choice_mode == ANIM_RANDOM)
962 gfx.anim_random_frame = RND(group->num_elements_resolved);
964 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
965 group->choice_mode, 0,
968 if (group->choice_mode == ANIM_RANDOM)
969 gfx.anim_random_frame = last_anim_random_frame;
973 Feld[x][y] = group->element_resolved[element_pos];
975 InitField(x, y, init_game);
981 static inline void InitField_WithBug1(int x, int y, boolean init_game)
983 InitField(x, y, init_game);
985 /* not needed to call InitMovDir() -- already done by InitField()! */
986 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
987 CAN_MOVE(Feld[x][y]))
991 static inline void InitField_WithBug2(int x, int y, boolean init_game)
993 int old_element = Feld[x][y];
995 InitField(x, y, init_game);
997 /* not needed to call InitMovDir() -- already done by InitField()! */
998 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
999 CAN_MOVE(old_element) &&
1000 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1003 /* this case is in fact a combination of not less than three bugs:
1004 first, it calls InitMovDir() for elements that can move, although this is
1005 already done by InitField(); then, it checks the element that was at this
1006 field _before_ the call to InitField() (which can change it); lastly, it
1007 was not called for "mole with direction" elements, which were treated as
1008 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1012 inline void DrawGameValue_Emeralds(int value)
1014 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1017 inline void DrawGameValue_Dynamite(int value)
1019 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1022 inline void DrawGameValue_Keys(struct PlayerInfo *player)
1026 for (i = 0; i < MAX_KEYS; i++)
1028 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1029 el2edimg(EL_KEY_1 + i));
1032 inline void DrawGameValue_Score(int value)
1034 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1037 inline void DrawGameValue_Time(int value)
1040 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1042 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1045 inline void DrawGameValue_Level(int value)
1048 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1051 /* misuse area for displaying emeralds to draw bigger level number */
1052 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1053 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1055 /* now copy it to the area for displaying level number */
1056 BlitBitmap(drawto, drawto,
1057 DX_EMERALDS, DY_EMERALDS + 1,
1058 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1059 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1060 DX_LEVEL - 1, DY_LEVEL + 1);
1062 /* restore the area for displaying emeralds */
1063 DrawGameValue_Emeralds(local_player->gems_still_needed);
1065 /* yes, this is all really ugly :-) */
1069 void DrawGameDoorValues()
1073 DrawGameValue_Level(level_nr);
1075 for (i = 0; i < MAX_PLAYERS; i++)
1076 DrawGameValue_Keys(&stored_player[i]);
1078 DrawGameValue_Emeralds(local_player->gems_still_needed);
1079 DrawGameValue_Dynamite(local_player->inventory_size);
1080 DrawGameValue_Score(local_player->score);
1081 DrawGameValue_Time(TimeLeft);
1084 static void resolve_group_element(int group_element, int recursion_depth)
1086 static int group_nr;
1087 static struct ElementGroupInfo *group;
1088 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1091 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1093 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1094 group_element - EL_GROUP_START + 1);
1096 /* replace element which caused too deep recursion by question mark */
1097 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1102 if (recursion_depth == 0) /* initialization */
1104 group = element_info[group_element].group;
1105 group_nr = group_element - EL_GROUP_START;
1107 group->num_elements_resolved = 0;
1108 group->choice_pos = 0;
1111 for (i = 0; i < actual_group->num_elements; i++)
1113 int element = actual_group->element[i];
1115 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1118 if (IS_GROUP_ELEMENT(element))
1119 resolve_group_element(element, recursion_depth + 1);
1122 group->element_resolved[group->num_elements_resolved++] = element;
1123 element_info[element].in_group[group_nr] = TRUE;
1128 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1130 printf("::: group %d: %d resolved elements\n",
1131 group_element - EL_GROUP_START, group->num_elements_resolved);
1132 for (i = 0; i < group->num_elements_resolved; i++)
1133 printf("::: - %d ['%s']\n", group->element_resolved[i],
1134 element_info[group->element_resolved[i]].token_name);
1141 =============================================================================
1143 -----------------------------------------------------------------------------
1144 initialize game engine due to level / tape version number
1145 =============================================================================
1148 static void InitGameEngine()
1152 /* set game engine from tape file when re-playing, else from level file */
1153 game.engine_version = (tape.playing ? tape.engine_version :
1154 level.game_version);
1156 /* dynamically adjust element properties according to game engine version */
1157 InitElementPropertiesEngine(game.engine_version);
1160 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1161 printf(" tape version == %06d [%s] [file: %06d]\n",
1162 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1164 printf(" => game.engine_version == %06d\n", game.engine_version);
1167 /* ---------- recursively resolve group elements ------------------------- */
1169 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1170 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1171 element_info[i].in_group[j] = FALSE;
1173 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1174 resolve_group_element(EL_GROUP_START + i, 0);
1176 /* ---------- initialize player's initial move delay --------------------- */
1178 /* dynamically adjust player properties according to game engine version */
1179 game.initial_move_delay =
1180 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1181 INITIAL_MOVE_DELAY_OFF);
1183 /* dynamically adjust player properties according to level information */
1184 game.initial_move_delay_value =
1185 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1187 /* ---------- initialize player's initial push delay --------------------- */
1189 /* dynamically adjust player properties according to game engine version */
1190 game.initial_push_delay_value =
1191 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1193 /* ---------- initialize changing elements ------------------------------- */
1195 /* initialize changing elements information */
1196 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1198 struct ElementInfo *ei = &element_info[i];
1200 /* this pointer might have been changed in the level editor */
1201 ei->change = &ei->change_page[0];
1203 if (!IS_CUSTOM_ELEMENT(i))
1205 ei->change->target_element = EL_EMPTY_SPACE;
1206 ei->change->delay_fixed = 0;
1207 ei->change->delay_random = 0;
1208 ei->change->delay_frames = 1;
1211 ei->change_events = CE_BITMASK_DEFAULT;
1212 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1214 ei->event_page_nr[j] = 0;
1215 ei->event_page[j] = &ei->change_page[0];
1219 /* add changing elements from pre-defined list */
1220 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1222 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1223 struct ElementInfo *ei = &element_info[ch_delay->element];
1225 ei->change->target_element = ch_delay->target_element;
1226 ei->change->delay_fixed = ch_delay->change_delay;
1228 ei->change->pre_change_function = ch_delay->pre_change_function;
1229 ei->change->change_function = ch_delay->change_function;
1230 ei->change->post_change_function = ch_delay->post_change_function;
1232 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1235 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1240 /* add change events from custom element configuration */
1241 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1243 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1245 for (j = 0; j < ei->num_change_pages; j++)
1247 if (!ei->change_page[j].can_change)
1250 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1252 /* only add event page for the first page found with this event */
1253 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1254 !(ei->change_events & CH_EVENT_BIT(k)))
1256 ei->change_events |= CH_EVENT_BIT(k);
1257 ei->event_page_nr[k] = j;
1258 ei->event_page[k] = &ei->change_page[j];
1266 /* add change events from custom element configuration */
1267 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1269 int element = EL_CUSTOM_START + i;
1271 /* only add custom elements that change after fixed/random frame delay */
1272 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1273 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1277 /* ---------- initialize run-time trigger player and element ------------- */
1279 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1281 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1283 for (j = 0; j < ei->num_change_pages; j++)
1285 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1286 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1290 /* ---------- initialize trigger events ---------------------------------- */
1292 /* initialize trigger events information */
1293 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1294 trigger_events[i] = EP_BITMASK_DEFAULT;
1297 /* add trigger events from element change event properties */
1298 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1300 struct ElementInfo *ei = &element_info[i];
1302 for (j = 0; j < ei->num_change_pages; j++)
1304 if (!ei->change_page[j].can_change)
1307 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1309 int trigger_element = ei->change_page[j].trigger_element;
1311 if (IS_GROUP_ELEMENT(trigger_element))
1313 struct ElementGroupInfo *group = element_info[trigger_element].group;
1315 for (k = 0; k < group->num_elements_resolved; k++)
1316 trigger_events[group->element_resolved[k]]
1317 |= ei->change_page[j].events;
1320 trigger_events[trigger_element] |= ei->change_page[j].events;
1325 /* add trigger events from element change event properties */
1326 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1327 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1328 trigger_events[element_info[i].change->trigger_element] |=
1329 element_info[i].change->events;
1332 /* ---------- initialize push delay -------------------------------------- */
1334 /* initialize push delay values to default */
1335 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1337 if (!IS_CUSTOM_ELEMENT(i))
1339 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1340 element_info[i].push_delay_random = game.default_push_delay_random;
1344 /* set push delay value for certain elements from pre-defined list */
1345 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1347 int e = push_delay_list[i].element;
1349 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1350 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1353 /* set push delay value for Supaplex elements for newer engine versions */
1354 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1356 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1358 if (IS_SP_ELEMENT(i))
1360 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1361 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1366 /* ---------- initialize move stepsize ----------------------------------- */
1368 /* initialize move stepsize values to default */
1369 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1370 if (!IS_CUSTOM_ELEMENT(i))
1371 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1373 /* set move stepsize value for certain elements from pre-defined list */
1374 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1376 int e = move_stepsize_list[i].element;
1378 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1382 /* ---------- initialize move dig/leave ---------------------------------- */
1384 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1386 element_info[i].can_leave_element = FALSE;
1387 element_info[i].can_leave_element_last = FALSE;
1391 /* ---------- initialize gem count --------------------------------------- */
1393 /* initialize gem count values for each element */
1394 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1395 if (!IS_CUSTOM_ELEMENT(i))
1396 element_info[i].collect_count = 0;
1398 /* add gem count values for all elements from pre-defined list */
1399 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1400 element_info[collect_count_list[i].element].collect_count =
1401 collect_count_list[i].count;
1403 /* ---------- initialize access direction -------------------------------- */
1405 /* initialize access direction values to default (access from every side) */
1406 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1407 if (!IS_CUSTOM_ELEMENT(i))
1408 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1410 /* set access direction value for certain elements from pre-defined list */
1411 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1412 element_info[access_direction_list[i].element].access_direction =
1413 access_direction_list[i].direction;
1418 =============================================================================
1420 -----------------------------------------------------------------------------
1421 initialize and start new game
1422 =============================================================================
1427 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1428 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1429 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1436 #if USE_NEW_AMOEBA_CODE
1437 printf("Using new amoeba code.\n");
1439 printf("Using old amoeba code.\n");
1444 /* don't play tapes over network */
1445 network_playing = (options.network && !tape.playing);
1447 for (i = 0; i < MAX_PLAYERS; i++)
1449 struct PlayerInfo *player = &stored_player[i];
1451 player->index_nr = i;
1452 player->index_bit = (1 << i);
1453 player->element_nr = EL_PLAYER_1 + i;
1455 player->present = FALSE;
1456 player->active = FALSE;
1459 player->effective_action = 0;
1460 player->programmed_action = 0;
1463 player->gems_still_needed = level.gems_needed;
1464 player->sokobanfields_still_needed = 0;
1465 player->lights_still_needed = 0;
1466 player->friends_still_needed = 0;
1468 for (j = 0; j < MAX_KEYS; j++)
1469 player->key[j] = FALSE;
1471 player->dynabomb_count = 0;
1472 player->dynabomb_size = 1;
1473 player->dynabombs_left = 0;
1474 player->dynabomb_xl = FALSE;
1476 player->MovDir = MV_NO_MOVING;
1479 player->GfxDir = MV_NO_MOVING;
1480 player->GfxAction = ACTION_DEFAULT;
1482 player->StepFrame = 0;
1484 player->use_murphy_graphic = FALSE;
1486 player->block_last_field = FALSE;
1487 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1489 player->actual_frame_counter = 0;
1491 player->step_counter = 0;
1493 player->last_move_dir = MV_NO_MOVING;
1495 player->is_waiting = FALSE;
1496 player->is_moving = FALSE;
1497 player->is_auto_moving = FALSE;
1498 player->is_digging = FALSE;
1499 player->is_snapping = FALSE;
1500 player->is_collecting = FALSE;
1501 player->is_pushing = FALSE;
1502 player->is_switching = FALSE;
1503 player->is_dropping = FALSE;
1505 player->is_bored = FALSE;
1506 player->is_sleeping = FALSE;
1508 player->frame_counter_bored = -1;
1509 player->frame_counter_sleeping = -1;
1511 player->anim_delay_counter = 0;
1512 player->post_delay_counter = 0;
1514 player->action_waiting = ACTION_DEFAULT;
1515 player->last_action_waiting = ACTION_DEFAULT;
1516 player->special_action_bored = ACTION_DEFAULT;
1517 player->special_action_sleeping = ACTION_DEFAULT;
1519 player->num_special_action_bored = 0;
1520 player->num_special_action_sleeping = 0;
1522 /* determine number of special actions for bored and sleeping animation */
1523 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1525 boolean found = FALSE;
1527 for (k = 0; k < NUM_DIRECTIONS; k++)
1528 if (el_act_dir2img(player->element_nr, j, k) !=
1529 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1533 player->num_special_action_bored++;
1537 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1539 boolean found = FALSE;
1541 for (k = 0; k < NUM_DIRECTIONS; k++)
1542 if (el_act_dir2img(player->element_nr, j, k) !=
1543 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1547 player->num_special_action_sleeping++;
1552 player->switch_x = -1;
1553 player->switch_y = -1;
1555 player->show_envelope = 0;
1557 player->move_delay = game.initial_move_delay;
1558 player->move_delay_value = game.initial_move_delay_value;
1560 player->move_delay_reset_counter = 0;
1562 player->push_delay = 0;
1563 player->push_delay_value = game.initial_push_delay_value;
1565 player->drop_delay = 0;
1567 player->last_jx = player->last_jy = 0;
1568 player->jx = player->jy = 0;
1570 player->shield_normal_time_left = 0;
1571 player->shield_deadly_time_left = 0;
1573 player->inventory_infinite_element = EL_UNDEFINED;
1574 player->inventory_size = 0;
1576 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1577 SnapField(player, 0, 0);
1579 player->LevelSolved = FALSE;
1580 player->GameOver = FALSE;
1583 network_player_action_received = FALSE;
1585 #if defined(PLATFORM_UNIX)
1586 /* initial null action */
1587 if (network_playing)
1588 SendToServer_MovePlayer(MV_NO_MOVING);
1596 TimeLeft = level.time;
1599 ScreenMovDir = MV_NO_MOVING;
1603 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1605 AllPlayersGone = FALSE;
1607 game.yamyam_content_nr = 0;
1608 game.magic_wall_active = FALSE;
1609 game.magic_wall_time_left = 0;
1610 game.light_time_left = 0;
1611 game.timegate_time_left = 0;
1612 game.switchgate_pos = 0;
1613 game.balloon_dir = MV_NO_MOVING;
1614 game.gravity = level.initial_gravity;
1615 game.explosions_delayed = TRUE;
1617 game.envelope_active = FALSE;
1619 for (i = 0; i < NUM_BELTS; i++)
1621 game.belt_dir[i] = MV_NO_MOVING;
1622 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1625 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1626 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1628 for (x = 0; x < lev_fieldx; x++)
1630 for (y = 0; y < lev_fieldy; y++)
1632 Feld[x][y] = level.field[x][y];
1633 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1634 ChangeDelay[x][y] = 0;
1635 ChangePage[x][y] = -1;
1636 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1638 WasJustMoving[x][y] = 0;
1639 WasJustFalling[x][y] = 0;
1640 CheckCollision[x][y] = 0;
1642 Pushed[x][y] = FALSE;
1644 Changed[x][y] = CE_BITMASK_DEFAULT;
1645 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1647 ExplodePhase[x][y] = 0;
1648 ExplodeDelay[x][y] = 0;
1649 ExplodeField[x][y] = EX_TYPE_NONE;
1651 RunnerVisit[x][y] = 0;
1652 PlayerVisit[x][y] = 0;
1655 GfxRandom[x][y] = INIT_GFX_RANDOM();
1656 GfxElement[x][y] = EL_UNDEFINED;
1657 GfxAction[x][y] = ACTION_DEFAULT;
1658 GfxDir[x][y] = MV_NO_MOVING;
1662 for (y = 0; y < lev_fieldy; y++)
1664 for (x = 0; x < lev_fieldx; x++)
1666 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1668 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1670 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1673 InitField(x, y, TRUE);
1679 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1680 emulate_sb ? EMU_SOKOBAN :
1681 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1683 /* initialize explosion and ignition delay */
1684 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1686 if (!IS_CUSTOM_ELEMENT(i))
1689 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1690 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1691 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1692 int last_phase = (num_phase + 1) * delay;
1693 int half_phase = (num_phase / 2) * delay;
1695 element_info[i].explosion_delay = last_phase - 1;
1696 element_info[i].ignition_delay = half_phase;
1699 if (i == EL_BLACK_ORB)
1700 element_info[i].ignition_delay = 0;
1702 if (i == EL_BLACK_ORB)
1703 element_info[i].ignition_delay = 1;
1708 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1709 element_info[i].explosion_delay = 1;
1711 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1712 element_info[i].ignition_delay = 1;
1716 /* correct non-moving belts to start moving left */
1717 for (i = 0; i < NUM_BELTS; i++)
1718 if (game.belt_dir[i] == MV_NO_MOVING)
1719 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1721 /* check if any connected player was not found in playfield */
1722 for (i = 0; i < MAX_PLAYERS; i++)
1724 struct PlayerInfo *player = &stored_player[i];
1726 if (player->connected && !player->present)
1728 for (j = 0; j < MAX_PLAYERS; j++)
1730 struct PlayerInfo *some_player = &stored_player[j];
1731 int jx = some_player->jx, jy = some_player->jy;
1733 /* assign first free player found that is present in the playfield */
1734 if (some_player->present && !some_player->connected)
1736 player->present = TRUE;
1737 player->active = TRUE;
1739 some_player->present = FALSE;
1740 some_player->active = FALSE;
1743 player->element_nr = some_player->element_nr;
1746 StorePlayer[jx][jy] = player->element_nr;
1747 player->jx = player->last_jx = jx;
1748 player->jy = player->last_jy = jy;
1758 /* when playing a tape, eliminate all players which do not participate */
1760 for (i = 0; i < MAX_PLAYERS; i++)
1762 if (stored_player[i].active && !tape.player_participates[i])
1764 struct PlayerInfo *player = &stored_player[i];
1765 int jx = player->jx, jy = player->jy;
1767 player->active = FALSE;
1768 StorePlayer[jx][jy] = 0;
1769 Feld[jx][jy] = EL_EMPTY;
1773 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1775 /* when in single player mode, eliminate all but the first active player */
1777 for (i = 0; i < MAX_PLAYERS; i++)
1779 if (stored_player[i].active)
1781 for (j = i + 1; j < MAX_PLAYERS; j++)
1783 if (stored_player[j].active)
1785 struct PlayerInfo *player = &stored_player[j];
1786 int jx = player->jx, jy = player->jy;
1788 player->active = FALSE;
1789 player->present = FALSE;
1791 StorePlayer[jx][jy] = 0;
1792 Feld[jx][jy] = EL_EMPTY;
1799 /* when recording the game, store which players take part in the game */
1802 for (i = 0; i < MAX_PLAYERS; i++)
1803 if (stored_player[i].active)
1804 tape.player_participates[i] = TRUE;
1809 for (i = 0; i < MAX_PLAYERS; i++)
1811 struct PlayerInfo *player = &stored_player[i];
1813 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1818 if (local_player == player)
1819 printf("Player %d is local player.\n", i+1);
1823 if (BorderElement == EL_EMPTY)
1826 SBX_Right = lev_fieldx - SCR_FIELDX;
1828 SBY_Lower = lev_fieldy - SCR_FIELDY;
1833 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1835 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1838 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1839 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1841 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1842 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1844 /* if local player not found, look for custom element that might create
1845 the player (make some assumptions about the right custom element) */
1846 if (!local_player->present)
1848 int start_x = 0, start_y = 0;
1849 int found_rating = 0;
1850 int found_element = EL_UNDEFINED;
1852 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1854 int element = Feld[x][y];
1859 if (!IS_CUSTOM_ELEMENT(element))
1862 if (CAN_CHANGE(element))
1864 for (i = 0; i < element_info[element].num_change_pages; i++)
1866 content = element_info[element].change_page[i].target_element;
1867 is_player = ELEM_IS_PLAYER(content);
1869 if (is_player && (found_rating < 3 || element < found_element))
1875 found_element = element;
1880 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1882 content = element_info[element].content[xx][yy];
1883 is_player = ELEM_IS_PLAYER(content);
1885 if (is_player && (found_rating < 2 || element < found_element))
1887 start_x = x + xx - 1;
1888 start_y = y + yy - 1;
1891 found_element = element;
1894 if (!CAN_CHANGE(element))
1897 for (i = 0; i < element_info[element].num_change_pages; i++)
1899 content= element_info[element].change_page[i].target_content[xx][yy];
1900 is_player = ELEM_IS_PLAYER(content);
1902 if (is_player && (found_rating < 1 || element < found_element))
1904 start_x = x + xx - 1;
1905 start_y = y + yy - 1;
1908 found_element = element;
1914 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1915 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1918 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1919 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1925 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1926 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1927 local_player->jx - MIDPOSX);
1929 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1930 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1931 local_player->jy - MIDPOSY);
1933 scroll_x = SBX_Left;
1934 scroll_y = SBY_Upper;
1935 if (local_player->jx >= SBX_Left + MIDPOSX)
1936 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1937 local_player->jx - MIDPOSX :
1939 if (local_player->jy >= SBY_Upper + MIDPOSY)
1940 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1941 local_player->jy - MIDPOSY :
1946 CloseDoor(DOOR_CLOSE_1);
1951 /* after drawing the level, correct some elements */
1952 if (game.timegate_time_left == 0)
1953 CloseAllOpenTimegates();
1955 if (setup.soft_scrolling)
1956 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1958 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1961 /* copy default game door content to main double buffer */
1962 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1963 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1965 DrawGameDoorValues();
1969 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1970 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1971 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1975 /* copy actual game door content to door double buffer for OpenDoor() */
1976 BlitBitmap(drawto, bitmap_db_door,
1977 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1979 OpenDoor(DOOR_OPEN_ALL);
1981 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1983 if (setup.sound_music)
1986 KeyboardAutoRepeatOffUnlessAutoplay();
1990 for (i = 0; i < MAX_PLAYERS; i++)
1991 printf("Player %d %sactive.\n",
1992 i + 1, (stored_player[i].active ? "" : "not "));
1996 printf("::: starting game [%d]\n", FrameCounter);
2000 void InitMovDir(int x, int y)
2002 int i, element = Feld[x][y];
2003 static int xy[4][2] =
2010 static int direction[3][4] =
2012 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2013 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2014 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2023 Feld[x][y] = EL_BUG;
2024 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2027 case EL_SPACESHIP_RIGHT:
2028 case EL_SPACESHIP_UP:
2029 case EL_SPACESHIP_LEFT:
2030 case EL_SPACESHIP_DOWN:
2031 Feld[x][y] = EL_SPACESHIP;
2032 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2035 case EL_BD_BUTTERFLY_RIGHT:
2036 case EL_BD_BUTTERFLY_UP:
2037 case EL_BD_BUTTERFLY_LEFT:
2038 case EL_BD_BUTTERFLY_DOWN:
2039 Feld[x][y] = EL_BD_BUTTERFLY;
2040 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2043 case EL_BD_FIREFLY_RIGHT:
2044 case EL_BD_FIREFLY_UP:
2045 case EL_BD_FIREFLY_LEFT:
2046 case EL_BD_FIREFLY_DOWN:
2047 Feld[x][y] = EL_BD_FIREFLY;
2048 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2051 case EL_PACMAN_RIGHT:
2053 case EL_PACMAN_LEFT:
2054 case EL_PACMAN_DOWN:
2055 Feld[x][y] = EL_PACMAN;
2056 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2059 case EL_SP_SNIKSNAK:
2060 MovDir[x][y] = MV_UP;
2063 case EL_SP_ELECTRON:
2064 MovDir[x][y] = MV_LEFT;
2071 Feld[x][y] = EL_MOLE;
2072 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2076 if (IS_CUSTOM_ELEMENT(element))
2078 struct ElementInfo *ei = &element_info[element];
2079 int move_direction_initial = ei->move_direction_initial;
2080 int move_pattern = ei->move_pattern;
2082 if (move_direction_initial == MV_START_PREVIOUS)
2084 if (MovDir[x][y] != MV_NO_MOVING)
2087 move_direction_initial = MV_START_AUTOMATIC;
2090 if (move_direction_initial == MV_START_RANDOM)
2091 MovDir[x][y] = 1 << RND(4);
2092 else if (move_direction_initial & MV_ANY_DIRECTION)
2093 MovDir[x][y] = move_direction_initial;
2094 else if (move_pattern == MV_ALL_DIRECTIONS ||
2095 move_pattern == MV_TURNING_LEFT ||
2096 move_pattern == MV_TURNING_RIGHT ||
2097 move_pattern == MV_TURNING_LEFT_RIGHT ||
2098 move_pattern == MV_TURNING_RIGHT_LEFT ||
2099 move_pattern == MV_TURNING_RANDOM)
2100 MovDir[x][y] = 1 << RND(4);
2101 else if (move_pattern == MV_HORIZONTAL)
2102 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2103 else if (move_pattern == MV_VERTICAL)
2104 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2105 else if (move_pattern & MV_ANY_DIRECTION)
2106 MovDir[x][y] = element_info[element].move_pattern;
2107 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2108 move_pattern == MV_ALONG_RIGHT_SIDE)
2111 /* use random direction as default start direction */
2112 if (game.engine_version >= VERSION_IDENT(3,1,0,2))
2113 MovDir[x][y] = 1 << RND(4);
2116 for (i = 0; i < NUM_DIRECTIONS; i++)
2118 int x1 = x + xy[i][0];
2119 int y1 = y + xy[i][1];
2121 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2123 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2124 MovDir[x][y] = direction[0][i];
2126 MovDir[x][y] = direction[1][i];
2135 MovDir[x][y] = 1 << RND(4);
2137 if (element != EL_BUG &&
2138 element != EL_SPACESHIP &&
2139 element != EL_BD_BUTTERFLY &&
2140 element != EL_BD_FIREFLY)
2143 for (i = 0; i < NUM_DIRECTIONS; i++)
2145 int x1 = x + xy[i][0];
2146 int y1 = y + xy[i][1];
2148 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2150 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2152 MovDir[x][y] = direction[0][i];
2155 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2156 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2158 MovDir[x][y] = direction[1][i];
2167 GfxDir[x][y] = MovDir[x][y];
2170 void InitAmoebaNr(int x, int y)
2173 int group_nr = AmoebeNachbarNr(x, y);
2177 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2179 if (AmoebaCnt[i] == 0)
2187 AmoebaNr[x][y] = group_nr;
2188 AmoebaCnt[group_nr]++;
2189 AmoebaCnt2[group_nr]++;
2195 boolean raise_level = FALSE;
2197 if (local_player->MovPos)
2201 if (tape.auto_play) /* tape might already be stopped here */
2202 tape.auto_play_level_solved = TRUE;
2204 if (tape.playing && tape.auto_play)
2205 tape.auto_play_level_solved = TRUE;
2208 local_player->LevelSolved = FALSE;
2210 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2214 if (!tape.playing && setup.sound_loops)
2215 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2216 SND_CTRL_PLAY_LOOP);
2218 while (TimeLeft > 0)
2220 if (!tape.playing && !setup.sound_loops)
2221 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2222 if (TimeLeft > 0 && !(TimeLeft % 10))
2223 RaiseScore(level.score[SC_TIME_BONUS]);
2224 if (TimeLeft > 100 && !(TimeLeft % 10))
2229 DrawGameValue_Time(TimeLeft);
2237 if (!tape.playing && setup.sound_loops)
2238 StopSound(SND_GAME_LEVELTIME_BONUS);
2240 else if (level.time == 0) /* level without time limit */
2242 if (!tape.playing && setup.sound_loops)
2243 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2244 SND_CTRL_PLAY_LOOP);
2246 while (TimePlayed < 999)
2248 if (!tape.playing && !setup.sound_loops)
2249 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2250 if (TimePlayed < 999 && !(TimePlayed % 10))
2251 RaiseScore(level.score[SC_TIME_BONUS]);
2252 if (TimePlayed < 900 && !(TimePlayed % 10))
2257 DrawGameValue_Time(TimePlayed);
2265 if (!tape.playing && setup.sound_loops)
2266 StopSound(SND_GAME_LEVELTIME_BONUS);
2269 /* close exit door after last player */
2270 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2271 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2273 int element = Feld[ExitX][ExitY];
2275 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2276 EL_SP_EXIT_CLOSING);
2278 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2281 /* Hero disappears */
2282 DrawLevelField(ExitX, ExitY);
2288 CloseDoor(DOOR_CLOSE_1);
2293 SaveTape(tape.level_nr); /* Ask to save tape */
2296 if (level_nr == leveldir_current->handicap_level)
2298 leveldir_current->handicap_level++;
2299 SaveLevelSetup_SeriesInfo();
2302 if (level_editor_test_game)
2303 local_player->score = -1; /* no highscore when playing from editor */
2304 else if (level_nr < leveldir_current->last_level)
2305 raise_level = TRUE; /* advance to next level */
2307 if ((hi_pos = NewHiScore()) >= 0)
2309 game_status = GAME_MODE_SCORES;
2310 DrawHallOfFame(hi_pos);
2319 game_status = GAME_MODE_MAIN;
2336 LoadScore(level_nr);
2338 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2339 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2342 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2344 if (local_player->score > highscore[k].Score)
2346 /* player has made it to the hall of fame */
2348 if (k < MAX_SCORE_ENTRIES - 1)
2350 int m = MAX_SCORE_ENTRIES - 1;
2353 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2354 if (!strcmp(setup.player_name, highscore[l].Name))
2356 if (m == k) /* player's new highscore overwrites his old one */
2360 for (l = m; l > k; l--)
2362 strcpy(highscore[l].Name, highscore[l - 1].Name);
2363 highscore[l].Score = highscore[l - 1].Score;
2370 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2371 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2372 highscore[k].Score = local_player->score;
2378 else if (!strncmp(setup.player_name, highscore[k].Name,
2379 MAX_PLAYER_NAME_LEN))
2380 break; /* player already there with a higher score */
2386 SaveScore(level_nr);
2391 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2393 if (player->GfxAction != action || player->GfxDir != dir)
2396 printf("Player frame reset! (%d => %d, %d => %d)\n",
2397 player->GfxAction, action, player->GfxDir, dir);
2400 player->GfxAction = action;
2401 player->GfxDir = dir;
2403 player->StepFrame = 0;
2407 static void ResetRandomAnimationValue(int x, int y)
2409 GfxRandom[x][y] = INIT_GFX_RANDOM();
2412 static void ResetGfxAnimation(int x, int y)
2415 GfxAction[x][y] = ACTION_DEFAULT;
2416 GfxDir[x][y] = MovDir[x][y];
2419 void InitMovingField(int x, int y, int direction)
2421 int element = Feld[x][y];
2422 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2423 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2427 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2428 ResetGfxAnimation(x, y);
2430 MovDir[newx][newy] = MovDir[x][y] = direction;
2431 GfxDir[x][y] = direction;
2433 if (Feld[newx][newy] == EL_EMPTY)
2434 Feld[newx][newy] = EL_BLOCKED;
2436 if (direction == MV_DOWN && CAN_FALL(element))
2437 GfxAction[x][y] = ACTION_FALLING;
2439 GfxAction[x][y] = ACTION_MOVING;
2441 GfxFrame[newx][newy] = GfxFrame[x][y];
2442 GfxRandom[newx][newy] = GfxRandom[x][y];
2443 GfxAction[newx][newy] = GfxAction[x][y];
2444 GfxDir[newx][newy] = GfxDir[x][y];
2447 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2449 int direction = MovDir[x][y];
2450 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2451 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2457 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2459 int oldx = x, oldy = y;
2460 int direction = MovDir[x][y];
2462 if (direction == MV_LEFT)
2464 else if (direction == MV_RIGHT)
2466 else if (direction == MV_UP)
2468 else if (direction == MV_DOWN)
2471 *comes_from_x = oldx;
2472 *comes_from_y = oldy;
2475 int MovingOrBlocked2Element(int x, int y)
2477 int element = Feld[x][y];
2479 if (element == EL_BLOCKED)
2483 Blocked2Moving(x, y, &oldx, &oldy);
2484 return Feld[oldx][oldy];
2490 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2492 /* like MovingOrBlocked2Element(), but if element is moving
2493 and (x,y) is the field the moving element is just leaving,
2494 return EL_BLOCKED instead of the element value */
2495 int element = Feld[x][y];
2497 if (IS_MOVING(x, y))
2499 if (element == EL_BLOCKED)
2503 Blocked2Moving(x, y, &oldx, &oldy);
2504 return Feld[oldx][oldy];
2513 static void RemoveField(int x, int y)
2515 Feld[x][y] = EL_EMPTY;
2522 ChangeDelay[x][y] = 0;
2523 ChangePage[x][y] = -1;
2524 Pushed[x][y] = FALSE;
2527 ExplodeField[x][y] = EX_TYPE_NONE;
2530 GfxElement[x][y] = EL_UNDEFINED;
2531 GfxAction[x][y] = ACTION_DEFAULT;
2532 GfxDir[x][y] = MV_NO_MOVING;
2535 void RemoveMovingField(int x, int y)
2537 int oldx = x, oldy = y, newx = x, newy = y;
2538 int element = Feld[x][y];
2539 int next_element = EL_UNDEFINED;
2541 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2544 if (IS_MOVING(x, y))
2546 Moving2Blocked(x, y, &newx, &newy);
2548 if (Feld[newx][newy] != EL_BLOCKED)
2551 if (Feld[newx][newy] != EL_BLOCKED)
2553 /* element is moving, but target field is not free (blocked), but
2554 already occupied by something different (example: acid pool);
2555 in this case, only remove the moving field, but not the target */
2557 RemoveField(oldx, oldy);
2559 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2561 DrawLevelField(oldx, oldy);
2567 else if (element == EL_BLOCKED)
2569 Blocked2Moving(x, y, &oldx, &oldy);
2570 if (!IS_MOVING(oldx, oldy))
2574 if (element == EL_BLOCKED &&
2575 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2576 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2577 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2578 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2579 next_element = get_next_element(Feld[oldx][oldy]);
2581 RemoveField(oldx, oldy);
2582 RemoveField(newx, newy);
2584 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2586 if (next_element != EL_UNDEFINED)
2587 Feld[oldx][oldy] = next_element;
2589 DrawLevelField(oldx, oldy);
2590 DrawLevelField(newx, newy);
2593 void DrawDynamite(int x, int y)
2595 int sx = SCREENX(x), sy = SCREENY(y);
2596 int graphic = el2img(Feld[x][y]);
2599 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2602 if (IS_WALKABLE_INSIDE(Back[x][y]))
2606 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2607 else if (Store[x][y])
2608 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2610 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2613 if (Back[x][y] || Store[x][y])
2614 DrawGraphicThruMask(sx, sy, graphic, frame);
2616 DrawGraphic(sx, sy, graphic, frame);
2618 if (game.emulation == EMU_SUPAPLEX)
2619 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2620 else if (Store[x][y])
2621 DrawGraphicThruMask(sx, sy, graphic, frame);
2623 DrawGraphic(sx, sy, graphic, frame);
2627 void CheckDynamite(int x, int y)
2629 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2633 if (MovDelay[x][y] != 0)
2636 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2643 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2645 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2646 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2647 StopSound(SND_DYNAMITE_ACTIVE);
2649 StopSound(SND_DYNABOMB_ACTIVE);
2655 void DrawRelocatePlayer(struct PlayerInfo *player)
2657 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2658 boolean no_delay = (tape.warp_forward);
2659 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2660 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2661 int jx = player->jx;
2662 int jy = player->jy;
2664 if (level.instant_relocation)
2667 int offset = (setup.scroll_delay ? 3 : 0);
2669 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2671 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2672 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2673 local_player->jx - MIDPOSX);
2675 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2676 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2677 local_player->jy - MIDPOSY);
2681 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2682 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2683 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2685 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2686 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2687 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2689 /* don't scroll over playfield boundaries */
2690 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2691 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2693 /* don't scroll over playfield boundaries */
2694 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2695 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2698 scroll_x += (local_player->jx - old_jx);
2699 scroll_y += (local_player->jy - old_jy);
2701 /* don't scroll over playfield boundaries */
2702 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2703 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2705 /* don't scroll over playfield boundaries */
2706 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2707 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2710 RedrawPlayfield(TRUE, 0,0,0,0);
2716 int offset = (setup.scroll_delay ? 3 : 0);
2718 int scroll_xx = -999, scroll_yy = -999;
2720 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2722 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2725 int fx = FX, fy = FY;
2727 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2728 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2729 local_player->jx - MIDPOSX);
2731 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2732 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2733 local_player->jy - MIDPOSY);
2735 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2736 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2739 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2742 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2749 fx += dx * TILEX / 2;
2750 fy += dy * TILEY / 2;
2752 ScrollLevel(dx, dy);
2755 /* scroll in two steps of half tile size to make things smoother */
2756 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2758 Delay(wait_delay_value);
2760 /* scroll second step to align at full tile size */
2762 Delay(wait_delay_value);
2765 int scroll_xx = -999, scroll_yy = -999;
2767 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2769 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2772 int fx = FX, fy = FY;
2774 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2775 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2776 local_player->jx - MIDPOSX);
2778 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2779 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2780 local_player->jy - MIDPOSY);
2782 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2783 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2786 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2789 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2796 fx += dx * TILEX / 2;
2797 fy += dy * TILEY / 2;
2799 ScrollLevel(dx, dy);
2802 /* scroll in two steps of half tile size to make things smoother */
2803 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2805 Delay(wait_delay_value);
2807 /* scroll second step to align at full tile size */
2809 Delay(wait_delay_value);
2815 Delay(wait_delay_value);
2819 void RelocatePlayer(int jx, int jy, int el_player_raw)
2821 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
2822 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2823 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2824 boolean no_delay = (tape.warp_forward);
2825 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2826 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2827 int old_jx = player->jx;
2828 int old_jy = player->jy;
2829 int old_element = Feld[old_jx][old_jy];
2830 int element = Feld[jx][jy];
2831 boolean player_relocated = (old_jx != jx || old_jy != jy);
2833 static int trigger_sides[4][2] =
2835 /* enter side leave side */
2836 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
2837 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
2838 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
2839 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
2841 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2842 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2843 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
2844 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
2845 int enter_side = enter_side_horiz | enter_side_vert;
2846 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
2847 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
2848 int leave_side = leave_side_horiz | leave_side_vert;
2850 if (player->GameOver) /* do not reanimate dead player */
2853 if (!player_relocated) /* no need to relocate the player */
2856 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2858 RemoveField(jx, jy); /* temporarily remove newly placed player */
2859 DrawLevelField(jx, jy);
2862 if (player->present)
2864 while (player->MovPos)
2866 ScrollPlayer(player, SCROLL_GO_ON);
2867 ScrollScreen(NULL, SCROLL_GO_ON);
2873 Delay(wait_delay_value);
2876 DrawPlayer(player); /* needed here only to cleanup last field */
2877 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2879 player->is_moving = FALSE;
2883 if (IS_CUSTOM_ELEMENT(old_element))
2884 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
2886 player->index_bit, leave_side);
2888 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
2890 player->index_bit, leave_side);
2893 Feld[jx][jy] = el_player;
2894 InitPlayerField(jx, jy, el_player, TRUE);
2896 Feld[jx][jy] = element; /* player may be set on walkable element */
2897 InitField(jx, jy, FALSE);
2900 if (player == local_player) /* only visually relocate local player */
2901 DrawRelocatePlayer(player);
2905 TestIfHeroTouchesBadThing(jx, jy);
2906 TestIfPlayerTouchesCustomElement(jx, jy);
2910 /* needed to allow change of walkable custom element by entering player */
2911 Changed[jx][jy] = 0; /* allow another change */
2915 if (IS_CUSTOM_ELEMENT(element))
2916 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
2917 player->index_bit, enter_side);
2919 CheckTriggeredElementChangeByPlayer(jx, jy, element,
2920 CE_OTHER_GETS_ENTERED,
2921 player->index_bit, enter_side);
2925 void Explode(int ex, int ey, int phase, int mode)
2932 /* !!! eliminate this variable !!! */
2933 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2938 int last_phase = num_phase * delay;
2939 int half_phase = (num_phase / 2) * delay;
2940 int first_phase_after_start = EX_PHASE_START + 1;
2944 if (game.explosions_delayed)
2946 ExplodeField[ex][ey] = mode;
2950 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2952 int center_element = Feld[ex][ey];
2955 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
2959 /* --- This is only really needed (and now handled) in "Impact()". --- */
2960 /* do not explode moving elements that left the explode field in time */
2961 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2962 center_element == EL_EMPTY &&
2963 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
2967 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
2968 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2970 /* remove things displayed in background while burning dynamite */
2971 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2974 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2976 /* put moving element to center field (and let it explode there) */
2977 center_element = MovingOrBlocked2Element(ex, ey);
2978 RemoveMovingField(ex, ey);
2979 Feld[ex][ey] = center_element;
2985 last_phase = element_info[center_element].explosion_delay + 1;
2987 last_phase = element_info[center_element].explosion_delay;
2991 printf("::: %d -> %d\n", center_element, last_phase);
2995 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2997 int xx = x - ex + 1;
2998 int yy = y - ey + 1;
3003 if (!IN_LEV_FIELD(x, y) ||
3004 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3005 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3008 if (!IN_LEV_FIELD(x, y) ||
3009 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3013 if (!IN_LEV_FIELD(x, y) ||
3014 ((mode != EX_TYPE_NORMAL ||
3015 center_element == EL_AMOEBA_TO_DIAMOND) &&
3016 (x != ex || y != ey)))
3020 element = Feld[x][y];
3022 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3024 element = MovingOrBlocked2Element(x, y);
3026 if (!IS_EXPLOSION_PROOF(element))
3027 RemoveMovingField(x, y);
3033 if (IS_EXPLOSION_PROOF(element))
3036 /* indestructible elements can only explode in center (but not flames) */
3037 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3038 element == EL_FLAMES)
3043 if ((IS_INDESTRUCTIBLE(element) &&
3044 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3045 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3046 element == EL_FLAMES)
3050 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3052 if (IS_ACTIVE_BOMB(element))
3054 /* re-activate things under the bomb like gate or penguin */
3055 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3062 /* save walkable background elements while explosion on same tile */
3064 if (IS_INDESTRUCTIBLE(element))
3065 Back[x][y] = element;
3067 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3068 Back[x][y] = element;
3071 /* ignite explodable elements reached by other explosion */
3072 if (element == EL_EXPLOSION)
3073 element = Store2[x][y];
3076 if (AmoebaNr[x][y] &&
3077 (element == EL_AMOEBA_FULL ||
3078 element == EL_BD_AMOEBA ||
3079 element == EL_AMOEBA_GROWING))
3081 AmoebaCnt[AmoebaNr[x][y]]--;
3082 AmoebaCnt2[AmoebaNr[x][y]]--;
3088 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3090 switch(StorePlayer[ex][ey])
3093 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3096 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3099 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3103 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3108 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3109 Store[x][y] = EL_EMPTY;
3111 if (game.emulation == EMU_SUPAPLEX)
3112 Store[x][y] = EL_EMPTY;
3115 else if (center_element == EL_MOLE)
3116 Store[x][y] = EL_EMERALD_RED;
3117 else if (center_element == EL_PENGUIN)
3118 Store[x][y] = EL_EMERALD_PURPLE;
3119 else if (center_element == EL_BUG)
3120 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3121 else if (center_element == EL_BD_BUTTERFLY)
3122 Store[x][y] = EL_BD_DIAMOND;
3123 else if (center_element == EL_SP_ELECTRON)
3124 Store[x][y] = EL_SP_INFOTRON;
3125 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3126 Store[x][y] = level.amoeba_content;
3127 else if (center_element == EL_YAMYAM)
3128 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3129 else if (IS_CUSTOM_ELEMENT(center_element) &&
3130 element_info[center_element].content[xx][yy] != EL_EMPTY)
3131 Store[x][y] = element_info[center_element].content[xx][yy];
3132 else if (element == EL_WALL_EMERALD)
3133 Store[x][y] = EL_EMERALD;
3134 else if (element == EL_WALL_DIAMOND)
3135 Store[x][y] = EL_DIAMOND;
3136 else if (element == EL_WALL_BD_DIAMOND)
3137 Store[x][y] = EL_BD_DIAMOND;
3138 else if (element == EL_WALL_EMERALD_YELLOW)
3139 Store[x][y] = EL_EMERALD_YELLOW;
3140 else if (element == EL_WALL_EMERALD_RED)
3141 Store[x][y] = EL_EMERALD_RED;
3142 else if (element == EL_WALL_EMERALD_PURPLE)
3143 Store[x][y] = EL_EMERALD_PURPLE;
3144 else if (element == EL_WALL_PEARL)
3145 Store[x][y] = EL_PEARL;
3146 else if (element == EL_WALL_CRYSTAL)
3147 Store[x][y] = EL_CRYSTAL;
3148 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3149 Store[x][y] = element_info[element].content[1][1];
3151 Store[x][y] = EL_EMPTY;
3153 if (x != ex || y != ey ||
3154 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_TYPE_BORDER)
3155 Store2[x][y] = element;
3158 if (AmoebaNr[x][y] &&
3159 (element == EL_AMOEBA_FULL ||
3160 element == EL_BD_AMOEBA ||
3161 element == EL_AMOEBA_GROWING))
3163 AmoebaCnt[AmoebaNr[x][y]]--;
3164 AmoebaCnt2[AmoebaNr[x][y]]--;
3170 MovDir[x][y] = MovPos[x][y] = 0;
3171 GfxDir[x][y] = MovDir[x][y];
3176 Feld[x][y] = EL_EXPLOSION;
3178 GfxElement[x][y] = center_element;
3180 GfxElement[x][y] = EL_UNDEFINED;
3183 ExplodePhase[x][y] = 1;
3185 ExplodeDelay[x][y] = last_phase;
3190 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3192 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3199 if (center_element == EL_YAMYAM)
3200 game.yamyam_content_nr =
3201 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3214 GfxFrame[x][y] = 0; /* restart explosion animation */
3218 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3222 last_phase = ExplodeDelay[x][y];
3225 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3229 /* activate this even in non-DEBUG version until cause for crash in
3230 getGraphicAnimationFrame() (see below) is found and eliminated */
3234 if (GfxElement[x][y] == EL_UNDEFINED)
3237 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3238 printf("Explode(): This should never happen!\n");
3241 GfxElement[x][y] = EL_EMPTY;
3247 border_element = Store2[x][y];
3248 if (IS_PLAYER(x, y))
3249 border_element = StorePlayer[x][y];
3252 printf("::: phase == %d\n", phase);
3255 if (phase == element_info[border_element].ignition_delay ||
3256 phase == last_phase)
3258 boolean border_explosion = FALSE;
3261 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3263 if (IS_PLAYER(x, y))
3266 KillHeroUnlessExplosionProtected(x, y);
3267 border_explosion = TRUE;
3270 if (phase == last_phase)
3271 printf("::: IS_PLAYER\n");
3274 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3276 Feld[x][y] = Store2[x][y];
3279 border_explosion = TRUE;
3282 if (phase == last_phase)
3283 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3286 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3288 AmoebeUmwandeln(x, y);
3290 border_explosion = TRUE;
3293 if (phase == last_phase)
3294 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3295 element_info[border_element].explosion_delay,
3296 element_info[border_element].ignition_delay,
3302 /* if an element just explodes due to another explosion (chain-reaction),
3303 do not immediately end the new explosion when it was the last frame of
3304 the explosion (as it would be done in the following "if"-statement!) */
3305 if (border_explosion && phase == last_phase)
3312 if (phase == first_phase_after_start)
3314 int element = Store2[x][y];
3316 if (element == EL_BLACK_ORB)
3318 Feld[x][y] = Store2[x][y];
3323 else if (phase == half_phase)
3325 int element = Store2[x][y];
3327 if (IS_PLAYER(x, y))
3328 KillHeroUnlessExplosionProtected(x, y);
3329 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3331 Feld[x][y] = Store2[x][y];
3335 else if (element == EL_AMOEBA_TO_DIAMOND)
3336 AmoebeUmwandeln(x, y);
3340 if (phase == last_phase)
3345 printf("::: done: phase == %d\n", phase);
3349 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3352 element = Feld[x][y] = Store[x][y];
3353 Store[x][y] = Store2[x][y] = 0;
3354 GfxElement[x][y] = EL_UNDEFINED;
3356 /* player can escape from explosions and might therefore be still alive */
3357 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3358 element <= EL_PLAYER_IS_EXPLODING_4)
3359 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3361 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3362 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3363 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3366 /* restore probably existing indestructible background element */
3367 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3368 element = Feld[x][y] = Back[x][y];
3371 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3372 GfxDir[x][y] = MV_NO_MOVING;
3373 ChangeDelay[x][y] = 0;
3374 ChangePage[x][y] = -1;
3377 InitField_WithBug2(x, y, FALSE);
3379 InitField(x, y, FALSE);
3381 /* !!! not needed !!! */
3383 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3384 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3387 if (CAN_MOVE(element))
3392 DrawLevelField(x, y);
3394 TestIfElementTouchesCustomElement(x, y);
3396 if (GFX_CRUMBLED(element))
3397 DrawLevelFieldCrumbledSandNeighbours(x, y);
3399 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3400 StorePlayer[x][y] = 0;
3402 if (ELEM_IS_PLAYER(element))
3403 RelocatePlayer(x, y, element);
3406 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3408 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3412 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3414 int stored = Store[x][y];
3415 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3416 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3420 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3422 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3426 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3430 printf("::: %d / %d [%d - %d]\n",
3431 GfxFrame[x][y], phase - delay, phase, delay);
3435 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3436 element_info[GfxElement[x][y]].token_name,
3441 DrawLevelFieldCrumbledSand(x, y);
3443 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3445 DrawLevelElement(x, y, Back[x][y]);
3446 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3448 else if (IS_WALKABLE_UNDER(Back[x][y]))
3450 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3451 DrawLevelElementThruMask(x, y, Back[x][y]);
3453 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3454 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3458 void DynaExplode(int ex, int ey)
3461 int dynabomb_element = Feld[ex][ey];
3462 int dynabomb_size = 1;
3463 boolean dynabomb_xl = FALSE;
3464 struct PlayerInfo *player;
3465 static int xy[4][2] =
3473 if (IS_ACTIVE_BOMB(dynabomb_element))
3475 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3476 dynabomb_size = player->dynabomb_size;
3477 dynabomb_xl = player->dynabomb_xl;
3478 player->dynabombs_left++;
3481 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3483 for (i = 0; i < NUM_DIRECTIONS; i++)
3485 for (j = 1; j <= dynabomb_size; j++)
3487 int x = ex + j * xy[i][0];
3488 int y = ey + j * xy[i][1];
3491 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3494 element = Feld[x][y];
3496 /* do not restart explosions of fields with active bombs */
3497 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3500 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3503 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3504 !CAN_GROW_INTO(element) && !dynabomb_xl)
3507 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3508 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3509 element != EL_SAND && !dynabomb_xl)
3516 void Bang(int x, int y)
3519 int element = MovingOrBlocked2Element(x, y);
3521 int element = Feld[x][y];
3525 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3527 if (IS_PLAYER(x, y))
3530 struct PlayerInfo *player = PLAYERINFO(x, y);
3532 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3533 player->element_nr);
3538 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3540 if (game.emulation == EMU_SUPAPLEX)
3541 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3543 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3548 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3556 case EL_BD_BUTTERFLY:
3559 case EL_DARK_YAMYAM:
3563 RaiseScoreElement(element);
3564 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3566 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3567 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3568 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3569 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3570 case EL_DYNABOMB_INCREASE_NUMBER:
3571 case EL_DYNABOMB_INCREASE_SIZE:
3572 case EL_DYNABOMB_INCREASE_POWER:
3577 case EL_LAMP_ACTIVE:
3579 case EL_AMOEBA_TO_DIAMOND:
3581 if (IS_PLAYER(x, y))
3582 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3584 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3587 if (CAN_EXPLODE_CROSS(element))
3589 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3593 else if (CAN_EXPLODE_1X1(element))
3594 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3596 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3600 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3603 void SplashAcid(int x, int y)
3606 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3607 (!IN_LEV_FIELD(x - 1, y - 2) ||
3608 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3609 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3611 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3612 (!IN_LEV_FIELD(x + 1, y - 2) ||
3613 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3614 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3616 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3618 /* input: position of element entering acid (obsolete) */
3620 int element = Feld[x][y];
3622 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3625 if (element != EL_ACID_SPLASH_LEFT &&
3626 element != EL_ACID_SPLASH_RIGHT)
3628 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3630 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3631 (!IN_LEV_FIELD(x - 1, y - 1) ||
3632 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3633 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3635 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3636 (!IN_LEV_FIELD(x + 1, y - 1) ||
3637 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3638 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3643 static void InitBeltMovement()
3645 static int belt_base_element[4] =
3647 EL_CONVEYOR_BELT_1_LEFT,
3648 EL_CONVEYOR_BELT_2_LEFT,
3649 EL_CONVEYOR_BELT_3_LEFT,
3650 EL_CONVEYOR_BELT_4_LEFT
3652 static int belt_base_active_element[4] =
3654 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3655 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3656 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3657 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3662 /* set frame order for belt animation graphic according to belt direction */
3663 for (i = 0; i < NUM_BELTS; i++)
3667 for (j = 0; j < NUM_BELT_PARTS; j++)
3669 int element = belt_base_active_element[belt_nr] + j;
3670 int graphic = el2img(element);
3672 if (game.belt_dir[i] == MV_LEFT)
3673 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3675 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3679 for (y = 0; y < lev_fieldy; y++)
3681 for (x = 0; x < lev_fieldx; x++)
3683 int element = Feld[x][y];
3685 for (i = 0; i < NUM_BELTS; i++)
3687 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3689 int e_belt_nr = getBeltNrFromBeltElement(element);
3692 if (e_belt_nr == belt_nr)
3694 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3696 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3704 static void ToggleBeltSwitch(int x, int y)
3706 static int belt_base_element[4] =
3708 EL_CONVEYOR_BELT_1_LEFT,
3709 EL_CONVEYOR_BELT_2_LEFT,
3710 EL_CONVEYOR_BELT_3_LEFT,
3711 EL_CONVEYOR_BELT_4_LEFT
3713 static int belt_base_active_element[4] =
3715 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3716 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3717 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3718 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3720 static int belt_base_switch_element[4] =
3722 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3723 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3724 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3725 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3727 static int belt_move_dir[4] =
3735 int element = Feld[x][y];
3736 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3737 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3738 int belt_dir = belt_move_dir[belt_dir_nr];
3741 if (!IS_BELT_SWITCH(element))
3744 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3745 game.belt_dir[belt_nr] = belt_dir;
3747 if (belt_dir_nr == 3)
3750 /* set frame order for belt animation graphic according to belt direction */
3751 for (i = 0; i < NUM_BELT_PARTS; i++)
3753 int element = belt_base_active_element[belt_nr] + i;
3754 int graphic = el2img(element);
3756 if (belt_dir == MV_LEFT)
3757 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3759 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3762 for (yy = 0; yy < lev_fieldy; yy++)
3764 for (xx = 0; xx < lev_fieldx; xx++)
3766 int element = Feld[xx][yy];
3768 if (IS_BELT_SWITCH(element))
3770 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3772 if (e_belt_nr == belt_nr)
3774 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3775 DrawLevelField(xx, yy);
3778 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3780 int e_belt_nr = getBeltNrFromBeltElement(element);
3782 if (e_belt_nr == belt_nr)
3784 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3786 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3787 DrawLevelField(xx, yy);
3790 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3792 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3794 if (e_belt_nr == belt_nr)
3796 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3798 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3799 DrawLevelField(xx, yy);
3806 static void ToggleSwitchgateSwitch(int x, int y)
3810 game.switchgate_pos = !game.switchgate_pos;
3812 for (yy = 0; yy < lev_fieldy; yy++)
3814 for (xx = 0; xx < lev_fieldx; xx++)
3816 int element = Feld[xx][yy];
3818 if (element == EL_SWITCHGATE_SWITCH_UP ||
3819 element == EL_SWITCHGATE_SWITCH_DOWN)
3821 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3822 DrawLevelField(xx, yy);
3824 else if (element == EL_SWITCHGATE_OPEN ||
3825 element == EL_SWITCHGATE_OPENING)
3827 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3829 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3831 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3834 else if (element == EL_SWITCHGATE_CLOSED ||
3835 element == EL_SWITCHGATE_CLOSING)
3837 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3839 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3841 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3848 static int getInvisibleActiveFromInvisibleElement(int element)
3850 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3851 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3852 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3856 static int getInvisibleFromInvisibleActiveElement(int element)
3858 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3859 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3860 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3864 static void RedrawAllLightSwitchesAndInvisibleElements()
3868 for (y = 0; y < lev_fieldy; y++)
3870 for (x = 0; x < lev_fieldx; x++)
3872 int element = Feld[x][y];
3874 if (element == EL_LIGHT_SWITCH &&
3875 game.light_time_left > 0)
3877 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3878 DrawLevelField(x, y);
3880 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3881 game.light_time_left == 0)
3883 Feld[x][y] = EL_LIGHT_SWITCH;
3884 DrawLevelField(x, y);
3886 else if (element == EL_INVISIBLE_STEELWALL ||
3887 element == EL_INVISIBLE_WALL ||
3888 element == EL_INVISIBLE_SAND)
3890 if (game.light_time_left > 0)
3891 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3893 DrawLevelField(x, y);
3895 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3896 element == EL_INVISIBLE_WALL_ACTIVE ||
3897 element == EL_INVISIBLE_SAND_ACTIVE)
3899 if (game.light_time_left == 0)
3900 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3902 DrawLevelField(x, y);
3908 static void ToggleLightSwitch(int x, int y)
3910 int element = Feld[x][y];
3912 game.light_time_left =
3913 (element == EL_LIGHT_SWITCH ?
3914 level.time_light * FRAMES_PER_SECOND : 0);
3916 RedrawAllLightSwitchesAndInvisibleElements();
3919 static void ActivateTimegateSwitch(int x, int y)
3923 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3925 for (yy = 0; yy < lev_fieldy; yy++)
3927 for (xx = 0; xx < lev_fieldx; xx++)
3929 int element = Feld[xx][yy];
3931 if (element == EL_TIMEGATE_CLOSED ||
3932 element == EL_TIMEGATE_CLOSING)
3934 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3935 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3939 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3941 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3942 DrawLevelField(xx, yy);
3949 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3952 inline static int getElementMoveStepsize(int x, int y)
3954 int element = Feld[x][y];
3955 int direction = MovDir[x][y];
3956 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3957 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3958 int horiz_move = (dx != 0);
3959 int sign = (horiz_move ? dx : dy);
3960 int step = sign * element_info[element].move_stepsize;
3962 /* special values for move stepsize for spring and things on conveyor belt */
3966 if (element == EL_SPRING)
3967 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3968 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3969 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3970 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3972 if (CAN_FALL(element) &&
3973 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3974 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3975 else if (element == EL_SPRING)
3976 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3983 void Impact(int x, int y)
3985 boolean lastline = (y == lev_fieldy-1);
3986 boolean object_hit = FALSE;
3987 boolean impact = (lastline || object_hit);
3988 int element = Feld[x][y];
3989 int smashed = EL_STEELWALL;
3991 if (!lastline) /* check if element below was hit */
3993 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3996 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3997 MovDir[x][y + 1] != MV_DOWN ||
3998 MovPos[x][y + 1] <= TILEY / 2));
4001 object_hit = !IS_FREE(x, y + 1);
4004 /* do not smash moving elements that left the smashed field in time */
4005 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4006 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4010 smashed = MovingOrBlocked2Element(x, y + 1);
4012 impact = (lastline || object_hit);
4015 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4017 SplashAcid(x, y + 1);
4021 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4022 /* only reset graphic animation if graphic really changes after impact */
4024 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4026 ResetGfxAnimation(x, y);
4027 DrawLevelField(x, y);
4030 if (impact && CAN_EXPLODE_IMPACT(element))
4035 else if (impact && element == EL_PEARL)
4037 ResetGfxAnimation(x, y);
4039 Feld[x][y] = EL_PEARL_BREAKING;
4040 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4043 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4045 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4050 if (impact && element == EL_AMOEBA_DROP)
4052 if (object_hit && IS_PLAYER(x, y + 1))
4053 KillHeroUnlessEnemyProtected(x, y + 1);
4054 else if (object_hit && smashed == EL_PENGUIN)
4058 Feld[x][y] = EL_AMOEBA_GROWING;
4059 Store[x][y] = EL_AMOEBA_WET;
4061 ResetRandomAnimationValue(x, y);
4066 if (object_hit) /* check which object was hit */
4068 if (CAN_PASS_MAGIC_WALL(element) &&
4069 (smashed == EL_MAGIC_WALL ||
4070 smashed == EL_BD_MAGIC_WALL))
4073 int activated_magic_wall =
4074 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4075 EL_BD_MAGIC_WALL_ACTIVE);
4077 /* activate magic wall / mill */
4078 for (yy = 0; yy < lev_fieldy; yy++)
4079 for (xx = 0; xx < lev_fieldx; xx++)
4080 if (Feld[xx][yy] == smashed)
4081 Feld[xx][yy] = activated_magic_wall;
4083 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4084 game.magic_wall_active = TRUE;
4086 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4087 SND_MAGIC_WALL_ACTIVATING :
4088 SND_BD_MAGIC_WALL_ACTIVATING));
4091 if (IS_PLAYER(x, y + 1))
4093 if (CAN_SMASH_PLAYER(element))
4095 KillHeroUnlessEnemyProtected(x, y + 1);
4099 else if (smashed == EL_PENGUIN)
4101 if (CAN_SMASH_PLAYER(element))
4107 else if (element == EL_BD_DIAMOND)
4109 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4115 else if (((element == EL_SP_INFOTRON ||
4116 element == EL_SP_ZONK) &&
4117 (smashed == EL_SP_SNIKSNAK ||
4118 smashed == EL_SP_ELECTRON ||
4119 smashed == EL_SP_DISK_ORANGE)) ||
4120 (element == EL_SP_INFOTRON &&
4121 smashed == EL_SP_DISK_YELLOW))
4127 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4133 else if (CAN_SMASH_EVERYTHING(element))
4135 if (IS_CLASSIC_ENEMY(smashed) ||
4136 CAN_EXPLODE_SMASHED(smashed))
4141 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4143 if (smashed == EL_LAMP ||
4144 smashed == EL_LAMP_ACTIVE)
4149 else if (smashed == EL_NUT)
4151 Feld[x][y + 1] = EL_NUT_BREAKING;
4152 PlayLevelSound(x, y, SND_NUT_BREAKING);
4153 RaiseScoreElement(EL_NUT);
4156 else if (smashed == EL_PEARL)
4158 ResetGfxAnimation(x, y);
4160 Feld[x][y + 1] = EL_PEARL_BREAKING;
4161 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4164 else if (smashed == EL_DIAMOND)
4166 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4167 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4170 else if (IS_BELT_SWITCH(smashed))
4172 ToggleBeltSwitch(x, y + 1);
4174 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4175 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4177 ToggleSwitchgateSwitch(x, y + 1);
4179 else if (smashed == EL_LIGHT_SWITCH ||
4180 smashed == EL_LIGHT_SWITCH_ACTIVE)
4182 ToggleLightSwitch(x, y + 1);
4187 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4190 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4193 /* !!! TEST ONLY !!! */
4194 CheckElementChangeBySide(x, y + 1, smashed, element,
4195 CE_SWITCHED, CH_SIDE_TOP);
4196 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4197 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4199 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4200 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4201 CheckElementChangeBySide(x, y + 1, smashed, element,
4202 CE_SWITCHED, CH_SIDE_TOP);
4208 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4213 /* play sound of magic wall / mill */
4215 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4216 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4218 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4219 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4220 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4221 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4226 /* play sound of object that hits the ground */
4227 if (lastline || object_hit)
4228 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4231 inline static void TurnRoundExt(int x, int y)
4243 { 0, 0 }, { 0, 0 }, { 0, 0 },
4248 int left, right, back;
4252 { MV_DOWN, MV_UP, MV_RIGHT },
4253 { MV_UP, MV_DOWN, MV_LEFT },
4255 { MV_LEFT, MV_RIGHT, MV_DOWN },
4259 { MV_RIGHT, MV_LEFT, MV_UP }
4262 int element = Feld[x][y];
4263 int move_pattern = element_info[element].move_pattern;
4265 int old_move_dir = MovDir[x][y];
4266 int left_dir = turn[old_move_dir].left;
4267 int right_dir = turn[old_move_dir].right;
4268 int back_dir = turn[old_move_dir].back;
4270 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4271 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4272 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4273 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4275 int left_x = x + left_dx, left_y = y + left_dy;
4276 int right_x = x + right_dx, right_y = y + right_dy;
4277 int move_x = x + move_dx, move_y = y + move_dy;
4281 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4283 TestIfBadThingTouchesOtherBadThing(x, y);
4285 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4286 MovDir[x][y] = right_dir;
4287 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4288 MovDir[x][y] = left_dir;
4290 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4292 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4296 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4297 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4299 TestIfBadThingTouchesOtherBadThing(x, y);
4301 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4302 MovDir[x][y] = left_dir;
4303 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4304 MovDir[x][y] = right_dir;
4306 if ((element == EL_SPACESHIP ||
4307 element == EL_SP_SNIKSNAK ||
4308 element == EL_SP_ELECTRON)
4309 && MovDir[x][y] != old_move_dir)
4311 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4315 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4317 TestIfBadThingTouchesOtherBadThing(x, y);
4319 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4320 MovDir[x][y] = left_dir;
4321 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4322 MovDir[x][y] = right_dir;
4324 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4326 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4329 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4331 TestIfBadThingTouchesOtherBadThing(x, y);
4333 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4334 MovDir[x][y] = left_dir;
4335 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4336 MovDir[x][y] = right_dir;
4338 if (MovDir[x][y] != old_move_dir)
4342 else if (element == EL_YAMYAM)
4344 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4345 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4347 if (can_turn_left && can_turn_right)
4348 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4349 else if (can_turn_left)
4350 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4351 else if (can_turn_right)
4352 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4354 MovDir[x][y] = back_dir;
4356 MovDelay[x][y] = 16 + 16 * RND(3);
4358 else if (element == EL_DARK_YAMYAM)
4360 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4362 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4365 if (can_turn_left && can_turn_right)
4366 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4367 else if (can_turn_left)
4368 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4369 else if (can_turn_right)
4370 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4372 MovDir[x][y] = back_dir;
4374 MovDelay[x][y] = 16 + 16 * RND(3);
4376 else if (element == EL_PACMAN)
4378 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4379 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4381 if (can_turn_left && can_turn_right)
4382 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4383 else if (can_turn_left)
4384 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4385 else if (can_turn_right)
4386 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4388 MovDir[x][y] = back_dir;
4390 MovDelay[x][y] = 6 + RND(40);
4392 else if (element == EL_PIG)
4394 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4395 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4396 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4397 boolean should_turn_left, should_turn_right, should_move_on;
4399 int rnd = RND(rnd_value);
4401 should_turn_left = (can_turn_left &&
4403 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4404 y + back_dy + left_dy)));
4405 should_turn_right = (can_turn_right &&
4407 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4408 y + back_dy + right_dy)));
4409 should_move_on = (can_move_on &&
4412 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4413 y + move_dy + left_dy) ||
4414 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4415 y + move_dy + right_dy)));
4417 if (should_turn_left || should_turn_right || should_move_on)
4419 if (should_turn_left && should_turn_right && should_move_on)
4420 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4421 rnd < 2 * rnd_value / 3 ? right_dir :
4423 else if (should_turn_left && should_turn_right)
4424 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4425 else if (should_turn_left && should_move_on)
4426 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4427 else if (should_turn_right && should_move_on)
4428 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4429 else if (should_turn_left)
4430 MovDir[x][y] = left_dir;
4431 else if (should_turn_right)
4432 MovDir[x][y] = right_dir;
4433 else if (should_move_on)
4434 MovDir[x][y] = old_move_dir;
4436 else if (can_move_on && rnd > rnd_value / 8)
4437 MovDir[x][y] = old_move_dir;
4438 else if (can_turn_left && can_turn_right)
4439 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4440 else if (can_turn_left && rnd > rnd_value / 8)
4441 MovDir[x][y] = left_dir;
4442 else if (can_turn_right && rnd > rnd_value/8)
4443 MovDir[x][y] = right_dir;
4445 MovDir[x][y] = back_dir;
4447 xx = x + move_xy[MovDir[x][y]].x;
4448 yy = y + move_xy[MovDir[x][y]].y;
4450 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4451 MovDir[x][y] = old_move_dir;
4455 else if (element == EL_DRAGON)
4457 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4458 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4459 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4461 int rnd = RND(rnd_value);
4464 if (FrameCounter < 1 && x == 0 && y == 29)
4465 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4468 if (can_move_on && rnd > rnd_value / 8)
4469 MovDir[x][y] = old_move_dir;
4470 else if (can_turn_left && can_turn_right)
4471 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4472 else if (can_turn_left && rnd > rnd_value / 8)
4473 MovDir[x][y] = left_dir;
4474 else if (can_turn_right && rnd > rnd_value / 8)
4475 MovDir[x][y] = right_dir;
4477 MovDir[x][y] = back_dir;
4479 xx = x + move_xy[MovDir[x][y]].x;
4480 yy = y + move_xy[MovDir[x][y]].y;
4483 if (FrameCounter < 1 && x == 0 && y == 29)
4484 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4485 xx, yy, Feld[xx][yy],
4490 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4491 MovDir[x][y] = old_move_dir;
4493 if (!IS_FREE(xx, yy))
4494 MovDir[x][y] = old_move_dir;
4498 if (FrameCounter < 1 && x == 0 && y == 29)
4499 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4504 else if (element == EL_MOLE)
4506 boolean can_move_on =
4507 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4508 IS_AMOEBOID(Feld[move_x][move_y]) ||
4509 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4512 boolean can_turn_left =
4513 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4514 IS_AMOEBOID(Feld[left_x][left_y])));
4516 boolean can_turn_right =
4517 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4518 IS_AMOEBOID(Feld[right_x][right_y])));
4520 if (can_turn_left && can_turn_right)
4521 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4522 else if (can_turn_left)
4523 MovDir[x][y] = left_dir;
4525 MovDir[x][y] = right_dir;
4528 if (MovDir[x][y] != old_move_dir)
4531 else if (element == EL_BALLOON)
4533 MovDir[x][y] = game.balloon_dir;
4536 else if (element == EL_SPRING)
4539 if (MovDir[x][y] & MV_HORIZONTAL &&
4540 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4541 MovDir[x][y] = MV_NO_MOVING;
4543 if (MovDir[x][y] & MV_HORIZONTAL &&
4544 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4545 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4546 MovDir[x][y] = MV_NO_MOVING;
4551 else if (element == EL_ROBOT ||
4552 element == EL_SATELLITE ||
4553 element == EL_PENGUIN)
4555 int attr_x = -1, attr_y = -1;
4566 for (i = 0; i < MAX_PLAYERS; i++)
4568 struct PlayerInfo *player = &stored_player[i];
4569 int jx = player->jx, jy = player->jy;
4571 if (!player->active)
4575 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4584 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4585 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4586 game.engine_version < VERSION_IDENT(3,1,0,0)))
4588 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4595 if (element == EL_PENGUIN)
4598 static int xy[4][2] =
4606 for (i = 0; i < NUM_DIRECTIONS; i++)
4608 int ex = x + xy[i][0];
4609 int ey = y + xy[i][1];
4611 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4620 MovDir[x][y] = MV_NO_MOVING;
4622 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4623 else if (attr_x > x)
4624 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4626 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4627 else if (attr_y > y)
4628 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4630 if (element == EL_ROBOT)
4634 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4635 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4636 Moving2Blocked(x, y, &newx, &newy);
4638 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4639 MovDelay[x][y] = 8 + 8 * !RND(3);
4641 MovDelay[x][y] = 16;
4643 else if (element == EL_PENGUIN)
4649 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4651 boolean first_horiz = RND(2);
4652 int new_move_dir = MovDir[x][y];
4655 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4656 Moving2Blocked(x, y, &newx, &newy);
4658 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4662 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4663 Moving2Blocked(x, y, &newx, &newy);
4665 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4668 MovDir[x][y] = old_move_dir;
4672 else /* (element == EL_SATELLITE) */
4678 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4680 boolean first_horiz = RND(2);
4681 int new_move_dir = MovDir[x][y];
4684 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4685 Moving2Blocked(x, y, &newx, &newy);
4687 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4691 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4692 Moving2Blocked(x, y, &newx, &newy);
4694 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4697 MovDir[x][y] = old_move_dir;
4702 else if (move_pattern == MV_TURNING_LEFT ||
4703 move_pattern == MV_TURNING_RIGHT ||
4704 move_pattern == MV_TURNING_LEFT_RIGHT ||
4705 move_pattern == MV_TURNING_RIGHT_LEFT ||
4706 move_pattern == MV_TURNING_RANDOM ||
4707 move_pattern == MV_ALL_DIRECTIONS)
4709 boolean can_turn_left =
4710 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4711 boolean can_turn_right =
4712 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4714 if (move_pattern == MV_TURNING_LEFT)
4715 MovDir[x][y] = left_dir;
4716 else if (move_pattern == MV_TURNING_RIGHT)
4717 MovDir[x][y] = right_dir;
4718 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4719 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4720 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4721 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4722 else if (move_pattern == MV_TURNING_RANDOM)
4723 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4724 can_turn_right && !can_turn_left ? right_dir :
4725 RND(2) ? left_dir : right_dir);
4726 else if (can_turn_left && can_turn_right)
4727 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4728 else if (can_turn_left)
4729 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4730 else if (can_turn_right)
4731 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4733 MovDir[x][y] = back_dir;
4735 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4737 else if (move_pattern == MV_HORIZONTAL ||
4738 move_pattern == MV_VERTICAL)
4740 if (move_pattern & old_move_dir)
4741 MovDir[x][y] = back_dir;
4742 else if (move_pattern == MV_HORIZONTAL)
4743 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4744 else if (move_pattern == MV_VERTICAL)
4745 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4747 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4749 else if (move_pattern & MV_ANY_DIRECTION)
4751 MovDir[x][y] = move_pattern;
4752 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4754 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4756 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4757 MovDir[x][y] = left_dir;
4758 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4759 MovDir[x][y] = right_dir;
4761 if (MovDir[x][y] != old_move_dir)
4762 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4764 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4766 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4767 MovDir[x][y] = right_dir;
4768 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4769 MovDir[x][y] = left_dir;
4771 if (MovDir[x][y] != old_move_dir)
4772 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4774 else if (move_pattern == MV_TOWARDS_PLAYER ||
4775 move_pattern == MV_AWAY_FROM_PLAYER)
4777 int attr_x = -1, attr_y = -1;
4779 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4790 for (i = 0; i < MAX_PLAYERS; i++)
4792 struct PlayerInfo *player = &stored_player[i];
4793 int jx = player->jx, jy = player->jy;
4795 if (!player->active)
4799 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4807 MovDir[x][y] = MV_NO_MOVING;
4809 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4810 else if (attr_x > x)
4811 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4813 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4814 else if (attr_y > y)
4815 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4817 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4819 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4821 boolean first_horiz = RND(2);
4822 int new_move_dir = MovDir[x][y];
4825 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4826 Moving2Blocked(x, y, &newx, &newy);
4828 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4832 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4833 Moving2Blocked(x, y, &newx, &newy);
4835 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4838 MovDir[x][y] = old_move_dir;
4841 else if (move_pattern == MV_WHEN_PUSHED ||
4842 move_pattern == MV_WHEN_DROPPED)
4844 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4845 MovDir[x][y] = MV_NO_MOVING;
4849 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4851 static int test_xy[7][2] =
4861 static int test_dir[7] =
4871 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4872 int move_preference = -1000000; /* start with very low preference */
4873 int new_move_dir = MV_NO_MOVING;
4874 int start_test = RND(4);
4877 for (i = 0; i < NUM_DIRECTIONS; i++)
4879 int move_dir = test_dir[start_test + i];
4880 int move_dir_preference;
4882 xx = x + test_xy[start_test + i][0];
4883 yy = y + test_xy[start_test + i][1];
4885 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4886 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4888 new_move_dir = move_dir;
4893 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4896 move_dir_preference = -1 * RunnerVisit[xx][yy];
4897 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4898 move_dir_preference = PlayerVisit[xx][yy];
4900 if (move_dir_preference > move_preference)
4902 /* prefer field that has not been visited for the longest time */
4903 move_preference = move_dir_preference;
4904 new_move_dir = move_dir;
4906 else if (move_dir_preference == move_preference &&
4907 move_dir == old_move_dir)
4909 /* prefer last direction when all directions are preferred equally */
4910 move_preference = move_dir_preference;
4911 new_move_dir = move_dir;
4915 MovDir[x][y] = new_move_dir;
4916 if (old_move_dir != new_move_dir)
4921 static void TurnRound(int x, int y)
4923 int direction = MovDir[x][y];
4926 GfxDir[x][y] = MovDir[x][y];
4932 GfxDir[x][y] = MovDir[x][y];
4935 if (direction != MovDir[x][y])
4940 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4943 GfxAction[x][y] = ACTION_WAITING;
4947 static boolean JustBeingPushed(int x, int y)
4951 for (i = 0; i < MAX_PLAYERS; i++)
4953 struct PlayerInfo *player = &stored_player[i];
4955 if (player->active && player->is_pushing && player->MovPos)
4957 int next_jx = player->jx + (player->jx - player->last_jx);
4958 int next_jy = player->jy + (player->jy - player->last_jy);
4960 if (x == next_jx && y == next_jy)
4968 void StartMoving(int x, int y)
4971 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4973 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4974 int element = Feld[x][y];
4980 if (MovDelay[x][y] == 0)
4981 GfxAction[x][y] = ACTION_DEFAULT;
4983 /* !!! this should be handled more generic (not only for mole) !!! */
4984 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4985 GfxAction[x][y] = ACTION_DEFAULT;
4988 if (CAN_FALL(element) && y < lev_fieldy - 1)
4990 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4991 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4992 if (JustBeingPushed(x, y))
4995 if (element == EL_QUICKSAND_FULL)
4997 if (IS_FREE(x, y + 1))
4999 InitMovingField(x, y, MV_DOWN);
5000 started_moving = TRUE;
5002 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5003 Store[x][y] = EL_ROCK;
5005 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5007 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5010 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5012 if (!MovDelay[x][y])
5013 MovDelay[x][y] = TILEY + 1;
5022 Feld[x][y] = EL_QUICKSAND_EMPTY;
5023 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5024 Store[x][y + 1] = Store[x][y];
5027 PlayLevelSoundAction(x, y, ACTION_FILLING);
5029 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5033 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5034 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5036 InitMovingField(x, y, MV_DOWN);
5037 started_moving = TRUE;
5039 Feld[x][y] = EL_QUICKSAND_FILLING;
5040 Store[x][y] = element;
5042 PlayLevelSoundAction(x, y, ACTION_FILLING);
5044 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5047 else if (element == EL_MAGIC_WALL_FULL)
5049 if (IS_FREE(x, y + 1))
5051 InitMovingField(x, y, MV_DOWN);
5052 started_moving = TRUE;
5054 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5055 Store[x][y] = EL_CHANGED(Store[x][y]);
5057 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5059 if (!MovDelay[x][y])
5060 MovDelay[x][y] = TILEY/4 + 1;
5069 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5070 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5071 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5075 else if (element == EL_BD_MAGIC_WALL_FULL)
5077 if (IS_FREE(x, y + 1))
5079 InitMovingField(x, y, MV_DOWN);
5080 started_moving = TRUE;
5082 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5083 Store[x][y] = EL_CHANGED2(Store[x][y]);
5085 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5087 if (!MovDelay[x][y])
5088 MovDelay[x][y] = TILEY/4 + 1;
5097 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5098 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5099 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5103 else if (CAN_PASS_MAGIC_WALL(element) &&
5104 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5105 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5107 InitMovingField(x, y, MV_DOWN);
5108 started_moving = TRUE;
5111 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5112 EL_BD_MAGIC_WALL_FILLING);
5113 Store[x][y] = element;
5116 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5118 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5121 SplashAcid(x, y + 1);
5123 InitMovingField(x, y, MV_DOWN);
5124 started_moving = TRUE;
5126 Store[x][y] = EL_ACID;
5128 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5129 GfxAction[x][y + 1] = ACTION_ACTIVE;
5133 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5134 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5136 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5137 CAN_SMASH(element) && WasJustFalling[x][y] &&
5138 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5140 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5141 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5142 (Feld[x][y + 1] == EL_BLOCKED)))
5146 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5147 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5148 WasJustMoving[x][y] && !Pushed[x][y + 1])
5150 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5151 WasJustMoving[x][y])
5156 /* this is needed for a special case not covered by calling "Impact()"
5157 from "ContinueMoving()": if an element moves to a tile directly below
5158 another element which was just falling on that tile (which was empty
5159 in the previous frame), the falling element above would just stop
5160 instead of smashing the element below (in previous version, the above
5161 element was just checked for "moving" instead of "falling", resulting
5162 in incorrect smashes caused by horizontal movement of the above
5163 element; also, the case of the player being the element to smash was
5164 simply not covered here... :-/ ) */
5167 WasJustMoving[x][y] = 0;
5168 WasJustFalling[x][y] = 0;
5171 CheckCollision[x][y] = 0;
5175 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5177 if (MovDir[x][y] == MV_NO_MOVING)
5179 InitMovingField(x, y, MV_DOWN);
5180 started_moving = TRUE;
5183 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5185 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5186 MovDir[x][y] = MV_DOWN;
5188 InitMovingField(x, y, MV_DOWN);
5189 started_moving = TRUE;
5191 else if (element == EL_AMOEBA_DROP)
5193 Feld[x][y] = EL_AMOEBA_GROWING;
5194 Store[x][y] = EL_AMOEBA_WET;
5196 /* Store[x][y + 1] must be zero, because:
5197 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5200 #if OLD_GAME_BEHAVIOUR
5201 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5203 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5204 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5205 element != EL_DX_SUPABOMB)
5208 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5209 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5210 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5211 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5214 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5215 (IS_FREE(x - 1, y + 1) ||
5216 Feld[x - 1][y + 1] == EL_ACID));
5217 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5218 (IS_FREE(x + 1, y + 1) ||
5219 Feld[x + 1][y + 1] == EL_ACID));
5220 boolean can_fall_any = (can_fall_left || can_fall_right);
5221 boolean can_fall_both = (can_fall_left && can_fall_right);
5223 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5225 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5227 if (slippery_type == SLIPPERY_ONLY_LEFT)
5228 can_fall_right = FALSE;
5229 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5230 can_fall_left = FALSE;
5231 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5232 can_fall_right = FALSE;
5233 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5234 can_fall_left = FALSE;
5236 can_fall_any = (can_fall_left || can_fall_right);
5237 can_fall_both = (can_fall_left && can_fall_right);
5242 if (can_fall_both &&
5243 (game.emulation != EMU_BOULDERDASH &&
5244 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5245 can_fall_left = !(can_fall_right = RND(2));
5247 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5248 started_moving = TRUE;
5252 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5254 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5257 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5258 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5259 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5260 int belt_dir = game.belt_dir[belt_nr];
5262 if ((belt_dir == MV_LEFT && left_is_free) ||
5263 (belt_dir == MV_RIGHT && right_is_free))
5266 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5269 InitMovingField(x, y, belt_dir);
5270 started_moving = TRUE;
5273 Pushed[x][y] = TRUE;
5274 Pushed[nextx][y] = TRUE;
5277 GfxAction[x][y] = ACTION_DEFAULT;
5281 MovDir[x][y] = 0; /* if element was moving, stop it */
5286 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5288 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5290 if (CAN_MOVE(element) && !started_moving)
5293 int move_pattern = element_info[element].move_pattern;
5298 if (MovDir[x][y] == MV_NO_MOVING)
5300 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5301 x, y, element, element_info[element].token_name);
5302 printf("StartMoving(): This should never happen!\n");
5307 Moving2Blocked(x, y, &newx, &newy);
5310 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5313 if ((element == EL_SATELLITE ||
5314 element == EL_BALLOON ||
5315 element == EL_SPRING)
5316 && JustBeingPushed(x, y))
5323 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5324 CheckCollision[x][y] && IN_LEV_FIELD_AND_NOT_FREE(newx, newy))
5326 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5327 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5328 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5332 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5333 element, element_info[element].token_name,
5334 WasJustMoving[x][y],
5335 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5336 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5337 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5338 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5342 WasJustMoving[x][y] = 0;
5345 CheckCollision[x][y] = 0;
5347 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5350 if (Feld[x][y] != element) /* element has changed */
5352 element = Feld[x][y];
5353 move_pattern = element_info[element].move_pattern;
5355 if (!CAN_MOVE(element))
5359 if (Feld[x][y] != element) /* element has changed */
5367 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5368 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5370 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5372 Moving2Blocked(x, y, &newx, &newy);
5373 if (Feld[newx][newy] == EL_BLOCKED)
5374 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5380 if (FrameCounter < 1 && x == 0 && y == 29)
5381 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5384 if (!MovDelay[x][y]) /* start new movement phase */
5386 /* all objects that can change their move direction after each step
5387 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5389 if (element != EL_YAMYAM &&
5390 element != EL_DARK_YAMYAM &&
5391 element != EL_PACMAN &&
5392 !(move_pattern & MV_ANY_DIRECTION) &&
5393 move_pattern != MV_TURNING_LEFT &&
5394 move_pattern != MV_TURNING_RIGHT &&
5395 move_pattern != MV_TURNING_LEFT_RIGHT &&
5396 move_pattern != MV_TURNING_RIGHT_LEFT &&
5397 move_pattern != MV_TURNING_RANDOM)
5402 if (FrameCounter < 1 && x == 0 && y == 29)
5403 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5406 if (MovDelay[x][y] && (element == EL_BUG ||
5407 element == EL_SPACESHIP ||
5408 element == EL_SP_SNIKSNAK ||
5409 element == EL_SP_ELECTRON ||
5410 element == EL_MOLE))
5411 DrawLevelField(x, y);
5415 if (MovDelay[x][y]) /* wait some time before next movement */
5420 if (element == EL_YAMYAM)
5423 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5424 DrawLevelElementAnimation(x, y, element);
5428 if (MovDelay[x][y]) /* element still has to wait some time */
5431 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5432 ResetGfxAnimation(x, y);
5436 if (GfxAction[x][y] != ACTION_WAITING)
5437 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5439 GfxAction[x][y] = ACTION_WAITING;
5443 if (element == EL_ROBOT ||
5445 element == EL_PACMAN ||
5447 element == EL_YAMYAM ||
5448 element == EL_DARK_YAMYAM)
5451 DrawLevelElementAnimation(x, y, element);
5453 DrawLevelElementAnimationIfNeeded(x, y, element);
5455 PlayLevelSoundAction(x, y, ACTION_WAITING);
5457 else if (element == EL_SP_ELECTRON)
5458 DrawLevelElementAnimationIfNeeded(x, y, element);
5459 else if (element == EL_DRAGON)
5462 int dir = MovDir[x][y];
5463 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5464 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5465 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5466 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5467 dir == MV_UP ? IMG_FLAMES_1_UP :
5468 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5469 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5472 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5475 GfxAction[x][y] = ACTION_ATTACKING;
5477 if (IS_PLAYER(x, y))
5478 DrawPlayerField(x, y);
5480 DrawLevelField(x, y);
5482 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5484 for (i = 1; i <= 3; i++)
5486 int xx = x + i * dx;
5487 int yy = y + i * dy;
5488 int sx = SCREENX(xx);
5489 int sy = SCREENY(yy);
5490 int flame_graphic = graphic + (i - 1);
5492 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5497 int flamed = MovingOrBlocked2Element(xx, yy);
5501 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5503 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5504 RemoveMovingField(xx, yy);
5506 RemoveField(xx, yy);
5508 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5511 RemoveMovingField(xx, yy);
5515 if (ChangeDelay[xx][yy])
5516 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5517 Feld[xx][yy] == EL_BLOCKED));
5521 ChangeDelay[xx][yy] = 0;
5523 Feld[xx][yy] = EL_FLAMES;
5524 if (IN_SCR_FIELD(sx, sy))
5526 DrawLevelFieldCrumbledSand(xx, yy);
5527 DrawGraphic(sx, sy, flame_graphic, frame);
5532 if (Feld[xx][yy] == EL_FLAMES)
5533 Feld[xx][yy] = EL_EMPTY;
5534 DrawLevelField(xx, yy);
5539 if (MovDelay[x][y]) /* element still has to wait some time */
5541 PlayLevelSoundAction(x, y, ACTION_WAITING);
5547 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5548 for all other elements GfxAction will be set by InitMovingField() */
5549 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5550 GfxAction[x][y] = ACTION_MOVING;
5554 /* now make next step */
5556 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5558 if (DONT_COLLIDE_WITH(element) &&
5559 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5560 !PLAYER_ENEMY_PROTECTED(newx, newy))
5563 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5567 /* player killed by element which is deadly when colliding with */
5569 KillHero(PLAYERINFO(newx, newy));
5576 else if (CAN_MOVE_INTO_ACID(element) &&
5577 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5578 (MovDir[x][y] == MV_DOWN ||
5579 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5581 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5582 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5586 else if ((element == EL_PENGUIN ||
5587 element == EL_ROBOT ||
5588 element == EL_SATELLITE ||
5589 element == EL_BALLOON ||
5590 IS_CUSTOM_ELEMENT(element)) &&
5591 IN_LEV_FIELD(newx, newy) &&
5592 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5595 SplashAcid(newx, newy);
5596 Store[x][y] = EL_ACID;
5598 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5600 if (Feld[newx][newy] == EL_EXIT_OPEN)
5604 DrawLevelField(x, y);
5606 Feld[x][y] = EL_EMPTY;
5607 DrawLevelField(x, y);
5610 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5611 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5612 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5614 local_player->friends_still_needed--;
5615 if (!local_player->friends_still_needed &&
5616 !local_player->GameOver && AllPlayersGone)
5617 local_player->LevelSolved = local_player->GameOver = TRUE;
5621 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5623 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5624 DrawLevelField(newx, newy);
5626 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5628 else if (!IS_FREE(newx, newy))
5630 GfxAction[x][y] = ACTION_WAITING;
5632 if (IS_PLAYER(x, y))
5633 DrawPlayerField(x, y);
5635 DrawLevelField(x, y);
5640 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5642 if (IS_FOOD_PIG(Feld[newx][newy]))
5644 if (IS_MOVING(newx, newy))
5645 RemoveMovingField(newx, newy);
5648 Feld[newx][newy] = EL_EMPTY;
5649 DrawLevelField(newx, newy);
5652 PlayLevelSound(x, y, SND_PIG_DIGGING);
5654 else if (!IS_FREE(newx, newy))
5656 if (IS_PLAYER(x, y))
5657 DrawPlayerField(x, y);
5659 DrawLevelField(x, y);
5668 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5671 else if (IS_CUSTOM_ELEMENT(element) &&
5672 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5676 !IS_FREE(newx, newy)
5681 int new_element = Feld[newx][newy];
5684 printf("::: '%s' digs '%s' [%d]\n",
5685 element_info[element].token_name,
5686 element_info[Feld[newx][newy]].token_name,
5687 StorePlayer[newx][newy]);
5690 if (!IS_FREE(newx, newy))
5692 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5693 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5696 /* no element can dig solid indestructible elements */
5697 if (IS_INDESTRUCTIBLE(new_element) &&
5698 !IS_DIGGABLE(new_element) &&
5699 !IS_COLLECTIBLE(new_element))
5702 if (AmoebaNr[newx][newy] &&
5703 (new_element == EL_AMOEBA_FULL ||
5704 new_element == EL_BD_AMOEBA ||
5705 new_element == EL_AMOEBA_GROWING))
5707 AmoebaCnt[AmoebaNr[newx][newy]]--;
5708 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5711 if (IS_MOVING(newx, newy))
5712 RemoveMovingField(newx, newy);
5715 RemoveField(newx, newy);
5716 DrawLevelField(newx, newy);
5719 /* if digged element was about to explode, prevent the explosion */
5720 ExplodeField[newx][newy] = EX_TYPE_NONE;
5722 PlayLevelSoundAction(x, y, action);
5727 Store[newx][newy] = EL_EMPTY;
5728 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5729 Store[newx][newy] = element_info[element].move_leave_element;
5731 Store[newx][newy] = EL_EMPTY;
5732 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5733 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5734 Store[newx][newy] = element_info[element].move_leave_element;
5737 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5738 element_info[element].can_leave_element = TRUE;
5741 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5743 RunnerVisit[x][y] = FrameCounter;
5744 PlayerVisit[x][y] /= 8; /* expire player visit path */
5750 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5752 if (!IS_FREE(newx, newy))
5754 if (IS_PLAYER(x, y))
5755 DrawPlayerField(x, y);
5757 DrawLevelField(x, y);
5763 boolean wanna_flame = !RND(10);
5764 int dx = newx - x, dy = newy - y;
5765 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5766 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5767 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5768 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5769 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5770 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5773 IS_CLASSIC_ENEMY(element1) ||
5774 IS_CLASSIC_ENEMY(element2)) &&
5775 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5776 element1 != EL_FLAMES && element2 != EL_FLAMES)
5779 ResetGfxAnimation(x, y);
5780 GfxAction[x][y] = ACTION_ATTACKING;
5783 if (IS_PLAYER(x, y))
5784 DrawPlayerField(x, y);
5786 DrawLevelField(x, y);
5788 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5790 MovDelay[x][y] = 50;
5794 RemoveField(newx, newy);
5796 Feld[newx][newy] = EL_FLAMES;
5797 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5800 RemoveField(newx1, newy1);
5802 Feld[newx1][newy1] = EL_FLAMES;
5804 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5807 RemoveField(newx2, newy2);
5809 Feld[newx2][newy2] = EL_FLAMES;
5816 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5817 Feld[newx][newy] == EL_DIAMOND)
5819 if (IS_MOVING(newx, newy))
5820 RemoveMovingField(newx, newy);
5823 Feld[newx][newy] = EL_EMPTY;
5824 DrawLevelField(newx, newy);
5827 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5829 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5830 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5832 if (AmoebaNr[newx][newy])
5834 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5835 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5836 Feld[newx][newy] == EL_BD_AMOEBA)
5837 AmoebaCnt[AmoebaNr[newx][newy]]--;
5842 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5844 if (IS_MOVING(newx, newy))
5847 RemoveMovingField(newx, newy);
5851 Feld[newx][newy] = EL_EMPTY;
5852 DrawLevelField(newx, newy);
5855 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5857 else if ((element == EL_PACMAN || element == EL_MOLE)
5858 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5860 if (AmoebaNr[newx][newy])
5862 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5863 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5864 Feld[newx][newy] == EL_BD_AMOEBA)
5865 AmoebaCnt[AmoebaNr[newx][newy]]--;
5868 if (element == EL_MOLE)
5870 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5871 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5873 ResetGfxAnimation(x, y);
5874 GfxAction[x][y] = ACTION_DIGGING;
5875 DrawLevelField(x, y);
5877 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5879 return; /* wait for shrinking amoeba */
5881 else /* element == EL_PACMAN */
5883 Feld[newx][newy] = EL_EMPTY;
5884 DrawLevelField(newx, newy);
5885 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5888 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5889 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5890 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5892 /* wait for shrinking amoeba to completely disappear */
5895 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5897 /* object was running against a wall */
5902 if (move_pattern & MV_ANY_DIRECTION &&
5903 move_pattern == MovDir[x][y])
5905 int blocking_element =
5906 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5909 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
5910 element_info[element].token_name,
5911 element_info[blocking_element].token_name,
5915 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5918 element = Feld[x][y]; /* element might have changed */
5923 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5924 DrawLevelElementAnimation(x, y, element);
5926 if (element == EL_BUG ||
5927 element == EL_SPACESHIP ||
5928 element == EL_SP_SNIKSNAK)
5929 DrawLevelField(x, y);
5930 else if (element == EL_MOLE)
5931 DrawLevelField(x, y);
5932 else if (element == EL_BD_BUTTERFLY ||
5933 element == EL_BD_FIREFLY)
5934 DrawLevelElementAnimationIfNeeded(x, y, element);
5935 else if (element == EL_SATELLITE)
5936 DrawLevelElementAnimationIfNeeded(x, y, element);
5937 else if (element == EL_SP_ELECTRON)
5938 DrawLevelElementAnimationIfNeeded(x, y, element);
5941 if (DONT_TOUCH(element))
5942 TestIfBadThingTouchesHero(x, y);
5945 PlayLevelSoundAction(x, y, ACTION_WAITING);
5951 InitMovingField(x, y, MovDir[x][y]);
5953 PlayLevelSoundAction(x, y, ACTION_MOVING);
5957 ContinueMoving(x, y);
5960 void ContinueMoving(int x, int y)
5962 int element = Feld[x][y];
5963 int stored = Store[x][y];
5964 struct ElementInfo *ei = &element_info[element];
5965 int direction = MovDir[x][y];
5966 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5967 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5968 int newx = x + dx, newy = y + dy;
5970 int nextx = newx + dx, nexty = newy + dy;
5973 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5974 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5976 boolean pushed_by_player = Pushed[x][y];
5979 MovPos[x][y] += getElementMoveStepsize(x, y);
5982 if (pushed_by_player && IS_PLAYER(x, y))
5984 /* special case: moving object pushed by player */
5985 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5988 if (pushed_by_player) /* special case: moving object pushed by player */
5989 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5992 if (ABS(MovPos[x][y]) < TILEX)
5994 DrawLevelField(x, y);
5996 return; /* element is still moving */
5999 /* element reached destination field */
6001 Feld[x][y] = EL_EMPTY;
6002 Feld[newx][newy] = element;
6003 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6006 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6008 element = Feld[newx][newy] = EL_ACID;
6011 else if (element == EL_MOLE)
6013 Feld[x][y] = EL_SAND;
6015 DrawLevelFieldCrumbledSandNeighbours(x, y);
6017 else if (element == EL_QUICKSAND_FILLING)
6019 element = Feld[newx][newy] = get_next_element(element);
6020 Store[newx][newy] = Store[x][y];
6022 else if (element == EL_QUICKSAND_EMPTYING)
6024 Feld[x][y] = get_next_element(element);
6025 element = Feld[newx][newy] = Store[x][y];
6027 else if (element == EL_MAGIC_WALL_FILLING)
6029 element = Feld[newx][newy] = get_next_element(element);
6030 if (!game.magic_wall_active)
6031 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6032 Store[newx][newy] = Store[x][y];
6034 else if (element == EL_MAGIC_WALL_EMPTYING)
6036 Feld[x][y] = get_next_element(element);
6037 if (!game.magic_wall_active)
6038 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6039 element = Feld[newx][newy] = Store[x][y];
6041 else if (element == EL_BD_MAGIC_WALL_FILLING)
6043 element = Feld[newx][newy] = get_next_element(element);
6044 if (!game.magic_wall_active)
6045 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6046 Store[newx][newy] = Store[x][y];
6048 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6050 Feld[x][y] = get_next_element(element);
6051 if (!game.magic_wall_active)
6052 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6053 element = Feld[newx][newy] = Store[x][y];
6055 else if (element == EL_AMOEBA_DROPPING)
6057 Feld[x][y] = get_next_element(element);
6058 element = Feld[newx][newy] = Store[x][y];
6060 else if (element == EL_SOKOBAN_OBJECT)
6063 Feld[x][y] = Back[x][y];
6065 if (Back[newx][newy])
6066 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6068 Back[x][y] = Back[newx][newy] = 0;
6071 else if (Store[x][y] == EL_ACID)
6073 element = Feld[newx][newy] = EL_ACID;
6077 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6078 ei->move_leave_element != EL_EMPTY &&
6079 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6080 Store[x][y] != EL_EMPTY))
6082 /* some elements can leave other elements behind after moving */
6084 Feld[x][y] = ei->move_leave_element;
6085 InitField(x, y, FALSE);
6087 if (GFX_CRUMBLED(Feld[x][y]))
6088 DrawLevelFieldCrumbledSandNeighbours(x, y);
6092 Store[x][y] = EL_EMPTY;
6093 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6094 MovDelay[newx][newy] = 0;
6096 if (CAN_CHANGE(element))
6098 /* copy element change control values to new field */
6099 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6100 ChangePage[newx][newy] = ChangePage[x][y];
6101 Changed[newx][newy] = Changed[x][y];
6102 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6105 ChangeDelay[x][y] = 0;
6106 ChangePage[x][y] = -1;
6107 Changed[x][y] = CE_BITMASK_DEFAULT;
6108 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6110 /* copy animation control values to new field */
6111 GfxFrame[newx][newy] = GfxFrame[x][y];
6112 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6113 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6114 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6116 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6118 ResetGfxAnimation(x, y); /* reset animation values for old field */
6121 /* some elements can leave other elements behind after moving */
6123 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6124 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6125 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6127 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6128 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6132 int move_leave_element = ei->move_leave_element;
6134 Feld[x][y] = move_leave_element;
6135 InitField(x, y, FALSE);
6137 if (GFX_CRUMBLED(Feld[x][y]))
6138 DrawLevelFieldCrumbledSandNeighbours(x, y);
6140 if (ELEM_IS_PLAYER(move_leave_element))
6141 RelocatePlayer(x, y, move_leave_element);
6146 /* some elements can leave other elements behind after moving */
6147 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6148 ei->move_leave_element != EL_EMPTY &&
6149 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6150 ei->can_leave_element_last))
6152 Feld[x][y] = ei->move_leave_element;
6153 InitField(x, y, FALSE);
6155 if (GFX_CRUMBLED(Feld[x][y]))
6156 DrawLevelFieldCrumbledSandNeighbours(x, y);
6159 ei->can_leave_element_last = ei->can_leave_element;
6160 ei->can_leave_element = FALSE;
6164 /* 2.1.1 (does not work correctly for spring) */
6165 if (!CAN_MOVE(element))
6166 MovDir[newx][newy] = 0;
6170 /* (does not work for falling objects that slide horizontally) */
6171 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6172 MovDir[newx][newy] = 0;
6175 if (!CAN_MOVE(element) ||
6176 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6177 MovDir[newx][newy] = 0;
6181 if (!CAN_MOVE(element) ||
6182 (CAN_FALL(element) && direction == MV_DOWN))
6183 GfxDir[x][y] = MovDir[newx][newy] = 0;
6185 if (!CAN_MOVE(element) ||
6186 (CAN_FALL(element) && direction == MV_DOWN &&
6187 (element == EL_SPRING ||
6188 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6189 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6190 GfxDir[x][y] = MovDir[newx][newy] = 0;
6196 DrawLevelField(x, y);
6197 DrawLevelField(newx, newy);
6199 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6201 /* prevent pushed element from moving on in pushed direction */
6202 if (pushed_by_player && CAN_MOVE(element) &&
6203 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6204 !(element_info[element].move_pattern & direction))
6205 TurnRound(newx, newy);
6208 /* prevent elements on conveyor belt from moving on in last direction */
6209 if (pushed_by_conveyor && CAN_FALL(element) &&
6210 direction & MV_HORIZONTAL)
6213 if (CAN_MOVE(element))
6214 InitMovDir(newx, newy);
6216 MovDir[newx][newy] = 0;
6218 MovDir[newx][newy] = 0;
6223 if (!pushed_by_player)
6225 int nextx = newx + dx, nexty = newy + dy;
6226 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6228 WasJustMoving[newx][newy] = 3;
6230 if (CAN_FALL(element) && direction == MV_DOWN)
6231 WasJustFalling[newx][newy] = 3;
6233 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6234 CheckCollision[newx][newy] = 2;
6237 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6239 TestIfBadThingTouchesHero(newx, newy);
6240 TestIfBadThingTouchesFriend(newx, newy);
6242 if (!IS_CUSTOM_ELEMENT(element))
6243 TestIfBadThingTouchesOtherBadThing(newx, newy);
6245 else if (element == EL_PENGUIN)
6246 TestIfFriendTouchesBadThing(newx, newy);
6248 if (CAN_FALL(element) && direction == MV_DOWN &&
6249 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6253 if (pushed_by_player)
6255 static int trigger_sides[4] =
6257 CH_SIDE_RIGHT, /* moving left */
6258 CH_SIDE_LEFT, /* moving right */
6259 CH_SIDE_BOTTOM, /* moving up */
6260 CH_SIDE_TOP, /* moving down */
6262 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6263 struct PlayerInfo *player = PLAYERINFO(x, y);
6265 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6266 player->index_bit, dig_side);
6267 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6268 player->index_bit, dig_side);
6273 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6277 if (ChangePage[newx][newy] != -1) /* delayed change */
6278 ChangeElement(newx, newy, ChangePage[newx][newy]);
6283 TestIfElementHitsCustomElement(newx, newy, direction);
6287 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6289 int hitting_element = Feld[newx][newy];
6291 /* !!! fix side (direction) orientation here and elsewhere !!! */
6292 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6296 if (IN_LEV_FIELD(nextx, nexty))
6298 int opposite_direction = MV_DIR_OPPOSITE(direction);
6299 int hitting_side = direction;
6300 int touched_side = opposite_direction;
6301 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6302 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6303 MovDir[nextx][nexty] != direction ||
6304 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6310 CheckElementChangeBySide(nextx, nexty, touched_element,
6311 CE_HIT_BY_SOMETHING, opposite_direction);
6313 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6314 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6316 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6318 struct ElementChangeInfo *change =
6319 &element_info[hitting_element].change_page[i];
6321 if (change->can_change &&
6322 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6323 change->trigger_side & touched_side &&
6324 change->trigger_element == touched_element)
6326 CheckElementChangeByPage(newx, newy, hitting_element,
6327 touched_element, CE_OTHER_IS_HITTING,i);
6333 if (IS_CUSTOM_ELEMENT(touched_element) &&
6334 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6336 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6338 struct ElementChangeInfo *change =
6339 &element_info[touched_element].change_page[i];
6341 if (change->can_change &&
6342 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6343 change->trigger_side & hitting_side &&
6344 change->trigger_element == hitting_element)
6346 CheckElementChangeByPage(nextx, nexty, touched_element,
6347 hitting_element, CE_OTHER_GETS_HIT, i);
6358 TestIfPlayerTouchesCustomElement(newx, newy);
6359 TestIfElementTouchesCustomElement(newx, newy);
6362 int AmoebeNachbarNr(int ax, int ay)
6365 int element = Feld[ax][ay];
6367 static int xy[4][2] =
6375 for (i = 0; i < NUM_DIRECTIONS; i++)
6377 int x = ax + xy[i][0];
6378 int y = ay + xy[i][1];
6380 if (!IN_LEV_FIELD(x, y))
6383 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6384 group_nr = AmoebaNr[x][y];
6390 void AmoebenVereinigen(int ax, int ay)
6392 int i, x, y, xx, yy;
6393 int new_group_nr = AmoebaNr[ax][ay];
6394 static int xy[4][2] =
6402 if (new_group_nr == 0)
6405 for (i = 0; i < NUM_DIRECTIONS; i++)
6410 if (!IN_LEV_FIELD(x, y))
6413 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6414 Feld[x][y] == EL_BD_AMOEBA ||
6415 Feld[x][y] == EL_AMOEBA_DEAD) &&
6416 AmoebaNr[x][y] != new_group_nr)
6418 int old_group_nr = AmoebaNr[x][y];
6420 if (old_group_nr == 0)
6423 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6424 AmoebaCnt[old_group_nr] = 0;
6425 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6426 AmoebaCnt2[old_group_nr] = 0;
6428 for (yy = 0; yy < lev_fieldy; yy++)
6430 for (xx = 0; xx < lev_fieldx; xx++)
6432 if (AmoebaNr[xx][yy] == old_group_nr)
6433 AmoebaNr[xx][yy] = new_group_nr;
6440 void AmoebeUmwandeln(int ax, int ay)
6444 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6446 int group_nr = AmoebaNr[ax][ay];
6451 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6452 printf("AmoebeUmwandeln(): This should never happen!\n");
6457 for (y = 0; y < lev_fieldy; y++)
6459 for (x = 0; x < lev_fieldx; x++)
6461 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6464 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6468 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6469 SND_AMOEBA_TURNING_TO_GEM :
6470 SND_AMOEBA_TURNING_TO_ROCK));
6475 static int xy[4][2] =
6483 for (i = 0; i < NUM_DIRECTIONS; i++)
6488 if (!IN_LEV_FIELD(x, y))
6491 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6493 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6494 SND_AMOEBA_TURNING_TO_GEM :
6495 SND_AMOEBA_TURNING_TO_ROCK));
6502 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6505 int group_nr = AmoebaNr[ax][ay];
6506 boolean done = FALSE;
6511 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6512 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6517 for (y = 0; y < lev_fieldy; y++)
6519 for (x = 0; x < lev_fieldx; x++)
6521 if (AmoebaNr[x][y] == group_nr &&
6522 (Feld[x][y] == EL_AMOEBA_DEAD ||
6523 Feld[x][y] == EL_BD_AMOEBA ||
6524 Feld[x][y] == EL_AMOEBA_GROWING))
6527 Feld[x][y] = new_element;
6528 InitField(x, y, FALSE);
6529 DrawLevelField(x, y);
6536 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6537 SND_BD_AMOEBA_TURNING_TO_ROCK :
6538 SND_BD_AMOEBA_TURNING_TO_GEM));
6541 void AmoebeWaechst(int x, int y)
6543 static unsigned long sound_delay = 0;
6544 static unsigned long sound_delay_value = 0;
6546 if (!MovDelay[x][y]) /* start new growing cycle */
6550 if (DelayReached(&sound_delay, sound_delay_value))
6553 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6555 if (Store[x][y] == EL_BD_AMOEBA)
6556 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6558 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6560 sound_delay_value = 30;
6564 if (MovDelay[x][y]) /* wait some time before growing bigger */
6567 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6569 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6570 6 - MovDelay[x][y]);
6572 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6575 if (!MovDelay[x][y])
6577 Feld[x][y] = Store[x][y];
6579 DrawLevelField(x, y);
6584 void AmoebaDisappearing(int x, int y)
6586 static unsigned long sound_delay = 0;
6587 static unsigned long sound_delay_value = 0;
6589 if (!MovDelay[x][y]) /* start new shrinking cycle */
6593 if (DelayReached(&sound_delay, sound_delay_value))
6594 sound_delay_value = 30;
6597 if (MovDelay[x][y]) /* wait some time before shrinking */
6600 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6602 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6603 6 - MovDelay[x][y]);
6605 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6608 if (!MovDelay[x][y])
6610 Feld[x][y] = EL_EMPTY;
6611 DrawLevelField(x, y);
6613 /* don't let mole enter this field in this cycle;
6614 (give priority to objects falling to this field from above) */
6620 void AmoebeAbleger(int ax, int ay)
6623 int element = Feld[ax][ay];
6624 int graphic = el2img(element);
6625 int newax = ax, neway = ay;
6626 static int xy[4][2] =
6634 if (!level.amoeba_speed)
6636 Feld[ax][ay] = EL_AMOEBA_DEAD;
6637 DrawLevelField(ax, ay);
6641 if (IS_ANIMATED(graphic))
6642 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6644 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6645 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6647 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6650 if (MovDelay[ax][ay])
6654 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6657 int x = ax + xy[start][0];
6658 int y = ay + xy[start][1];
6660 if (!IN_LEV_FIELD(x, y))
6664 if (IS_FREE(x, y) ||
6665 CAN_GROW_INTO(Feld[x][y]) ||
6666 Feld[x][y] == EL_QUICKSAND_EMPTY)
6672 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6673 if (IS_FREE(x, y) ||
6674 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6681 if (newax == ax && neway == ay)
6684 else /* normal or "filled" (BD style) amoeba */
6687 boolean waiting_for_player = FALSE;
6689 for (i = 0; i < NUM_DIRECTIONS; i++)
6691 int j = (start + i) % 4;
6692 int x = ax + xy[j][0];
6693 int y = ay + xy[j][1];
6695 if (!IN_LEV_FIELD(x, y))
6699 if (IS_FREE(x, y) ||
6700 CAN_GROW_INTO(Feld[x][y]) ||
6701 Feld[x][y] == EL_QUICKSAND_EMPTY)
6708 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6709 if (IS_FREE(x, y) ||
6710 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6717 else if (IS_PLAYER(x, y))
6718 waiting_for_player = TRUE;
6721 if (newax == ax && neway == ay) /* amoeba cannot grow */
6724 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6726 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6729 Feld[ax][ay] = EL_AMOEBA_DEAD;
6730 DrawLevelField(ax, ay);
6731 AmoebaCnt[AmoebaNr[ax][ay]]--;
6733 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6735 if (element == EL_AMOEBA_FULL)
6736 AmoebeUmwandeln(ax, ay);
6737 else if (element == EL_BD_AMOEBA)
6738 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6743 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6745 /* amoeba gets larger by growing in some direction */
6747 int new_group_nr = AmoebaNr[ax][ay];
6750 if (new_group_nr == 0)
6752 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6753 printf("AmoebeAbleger(): This should never happen!\n");
6758 AmoebaNr[newax][neway] = new_group_nr;
6759 AmoebaCnt[new_group_nr]++;
6760 AmoebaCnt2[new_group_nr]++;
6762 /* if amoeba touches other amoeba(s) after growing, unify them */
6763 AmoebenVereinigen(newax, neway);
6765 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6767 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6773 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6774 (neway == lev_fieldy - 1 && newax != ax))
6776 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6777 Store[newax][neway] = element;
6779 else if (neway == ay)
6781 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6783 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6785 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6790 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6791 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6792 Store[ax][ay] = EL_AMOEBA_DROP;
6793 ContinueMoving(ax, ay);
6797 DrawLevelField(newax, neway);
6800 void Life(int ax, int ay)
6803 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6805 int element = Feld[ax][ay];
6806 int graphic = el2img(element);
6807 boolean changed = FALSE;
6809 if (IS_ANIMATED(graphic))
6810 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6815 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6816 MovDelay[ax][ay] = life_time;
6818 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6821 if (MovDelay[ax][ay])
6825 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6827 int xx = ax+x1, yy = ay+y1;
6830 if (!IN_LEV_FIELD(xx, yy))
6833 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6835 int x = xx+x2, y = yy+y2;
6837 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6840 if (((Feld[x][y] == element ||
6841 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6843 (IS_FREE(x, y) && Stop[x][y]))
6847 if (xx == ax && yy == ay) /* field in the middle */
6849 if (nachbarn < life[0] || nachbarn > life[1])
6851 Feld[xx][yy] = EL_EMPTY;
6853 DrawLevelField(xx, yy);
6854 Stop[xx][yy] = TRUE;
6859 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6860 { /* free border field */
6861 if (nachbarn >= life[2] && nachbarn <= life[3])
6863 Feld[xx][yy] = element;
6864 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6866 DrawLevelField(xx, yy);
6867 Stop[xx][yy] = TRUE;
6872 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6873 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6874 { /* free border field */
6875 if (nachbarn >= life[2] && nachbarn <= life[3])
6877 Feld[xx][yy] = element;
6878 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6880 DrawLevelField(xx, yy);
6881 Stop[xx][yy] = TRUE;
6889 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6890 SND_GAME_OF_LIFE_GROWING);
6893 static void InitRobotWheel(int x, int y)
6895 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6898 static void RunRobotWheel(int x, int y)
6900 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6903 static void StopRobotWheel(int x, int y)
6905 if (ZX == x && ZY == y)
6909 static void InitTimegateWheel(int x, int y)
6912 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6914 /* another brainless, "type style" bug ... :-( */
6915 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6919 static void RunTimegateWheel(int x, int y)
6921 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6924 void CheckExit(int x, int y)
6926 if (local_player->gems_still_needed > 0 ||
6927 local_player->sokobanfields_still_needed > 0 ||
6928 local_player->lights_still_needed > 0)
6930 int element = Feld[x][y];
6931 int graphic = el2img(element);
6933 if (IS_ANIMATED(graphic))
6934 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6939 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6942 Feld[x][y] = EL_EXIT_OPENING;
6944 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6947 void CheckExitSP(int x, int y)
6949 if (local_player->gems_still_needed > 0)
6951 int element = Feld[x][y];
6952 int graphic = el2img(element);
6954 if (IS_ANIMATED(graphic))
6955 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6960 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6963 Feld[x][y] = EL_SP_EXIT_OPENING;
6965 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6968 static void CloseAllOpenTimegates()
6972 for (y = 0; y < lev_fieldy; y++)
6974 for (x = 0; x < lev_fieldx; x++)
6976 int element = Feld[x][y];
6978 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6980 Feld[x][y] = EL_TIMEGATE_CLOSING;
6982 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6984 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6991 void EdelsteinFunkeln(int x, int y)
6993 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6996 if (Feld[x][y] == EL_BD_DIAMOND)
6999 if (MovDelay[x][y] == 0) /* next animation frame */
7000 MovDelay[x][y] = 11 * !SimpleRND(500);
7002 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7006 if (setup.direct_draw && MovDelay[x][y])
7007 SetDrawtoField(DRAW_BUFFERED);
7009 DrawLevelElementAnimation(x, y, Feld[x][y]);
7011 if (MovDelay[x][y] != 0)
7013 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7014 10 - MovDelay[x][y]);
7016 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7018 if (setup.direct_draw)
7022 dest_x = FX + SCREENX(x) * TILEX;
7023 dest_y = FY + SCREENY(y) * TILEY;
7025 BlitBitmap(drawto_field, window,
7026 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7027 SetDrawtoField(DRAW_DIRECT);
7033 void MauerWaechst(int x, int y)
7037 if (!MovDelay[x][y]) /* next animation frame */
7038 MovDelay[x][y] = 3 * delay;
7040 if (MovDelay[x][y]) /* wait some time before next frame */
7044 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7046 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7047 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7049 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7052 if (!MovDelay[x][y])
7054 if (MovDir[x][y] == MV_LEFT)
7056 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7057 DrawLevelField(x - 1, y);
7059 else if (MovDir[x][y] == MV_RIGHT)
7061 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7062 DrawLevelField(x + 1, y);
7064 else if (MovDir[x][y] == MV_UP)
7066 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7067 DrawLevelField(x, y - 1);
7071 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7072 DrawLevelField(x, y + 1);
7075 Feld[x][y] = Store[x][y];
7077 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7078 DrawLevelField(x, y);
7083 void MauerAbleger(int ax, int ay)
7085 int element = Feld[ax][ay];
7086 int graphic = el2img(element);
7087 boolean oben_frei = FALSE, unten_frei = FALSE;
7088 boolean links_frei = FALSE, rechts_frei = FALSE;
7089 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7090 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7091 boolean new_wall = FALSE;
7093 if (IS_ANIMATED(graphic))
7094 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7096 if (!MovDelay[ax][ay]) /* start building new wall */
7097 MovDelay[ax][ay] = 6;
7099 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7102 if (MovDelay[ax][ay])
7106 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7108 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7110 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7112 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7115 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7116 element == EL_EXPANDABLE_WALL_ANY)
7120 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7121 Store[ax][ay-1] = element;
7122 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7123 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7124 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7125 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7130 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7131 Store[ax][ay+1] = element;
7132 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7133 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7134 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7135 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7140 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7141 element == EL_EXPANDABLE_WALL_ANY ||
7142 element == EL_EXPANDABLE_WALL)
7146 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7147 Store[ax-1][ay] = element;
7148 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7149 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7150 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7151 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7157 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7158 Store[ax+1][ay] = element;
7159 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7160 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7161 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7162 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7167 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7168 DrawLevelField(ax, ay);
7170 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7172 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7173 unten_massiv = TRUE;
7174 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7175 links_massiv = TRUE;
7176 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7177 rechts_massiv = TRUE;
7179 if (((oben_massiv && unten_massiv) ||
7180 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7181 element == EL_EXPANDABLE_WALL) &&
7182 ((links_massiv && rechts_massiv) ||
7183 element == EL_EXPANDABLE_WALL_VERTICAL))
7184 Feld[ax][ay] = EL_WALL;
7188 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7190 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7194 void CheckForDragon(int x, int y)
7197 boolean dragon_found = FALSE;
7198 static int xy[4][2] =
7206 for (i = 0; i < NUM_DIRECTIONS; i++)
7208 for (j = 0; j < 4; j++)
7210 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7212 if (IN_LEV_FIELD(xx, yy) &&
7213 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7215 if (Feld[xx][yy] == EL_DRAGON)
7216 dragon_found = TRUE;
7225 for (i = 0; i < NUM_DIRECTIONS; i++)
7227 for (j = 0; j < 3; j++)
7229 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7231 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7233 Feld[xx][yy] = EL_EMPTY;
7234 DrawLevelField(xx, yy);
7243 static void InitBuggyBase(int x, int y)
7245 int element = Feld[x][y];
7246 int activating_delay = FRAMES_PER_SECOND / 4;
7249 (element == EL_SP_BUGGY_BASE ?
7250 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7251 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7253 element == EL_SP_BUGGY_BASE_ACTIVE ?
7254 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7257 static void WarnBuggyBase(int x, int y)
7260 static int xy[4][2] =
7268 for (i = 0; i < NUM_DIRECTIONS; i++)
7270 int xx = x + xy[i][0], yy = y + xy[i][1];
7272 if (IS_PLAYER(xx, yy))
7274 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7281 static void InitTrap(int x, int y)
7283 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7286 static void ActivateTrap(int x, int y)
7288 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7291 static void ChangeActiveTrap(int x, int y)
7293 int graphic = IMG_TRAP_ACTIVE;
7295 /* if new animation frame was drawn, correct crumbled sand border */
7296 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7297 DrawLevelFieldCrumbledSand(x, y);
7300 static void ChangeElementNowExt(int x, int y, int target_element)
7302 int previous_move_direction = MovDir[x][y];
7304 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7305 IS_WALKABLE(Feld[x][y]));
7307 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7308 IS_WALKABLE(Feld[x][y]) &&
7312 /* check if element under player changes from accessible to unaccessible
7313 (needed for special case of dropping element which then changes) */
7314 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7315 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7326 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7327 RemoveMovingField(x, y);
7331 Feld[x][y] = target_element;
7334 Feld[x][y] = target_element;
7337 ResetGfxAnimation(x, y);
7338 ResetRandomAnimationValue(x, y);
7340 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7341 MovDir[x][y] = previous_move_direction;
7344 InitField_WithBug1(x, y, FALSE);
7346 InitField(x, y, FALSE);
7347 if (CAN_MOVE(Feld[x][y]))
7351 DrawLevelField(x, y);
7353 if (GFX_CRUMBLED(Feld[x][y]))
7354 DrawLevelFieldCrumbledSandNeighbours(x, y);
7357 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7360 TestIfBadThingTouchesHero(x, y);
7361 TestIfPlayerTouchesCustomElement(x, y);
7362 TestIfElementTouchesCustomElement(x, y);
7365 if (ELEM_IS_PLAYER(target_element))
7366 RelocatePlayer(x, y, target_element);
7369 TestIfBadThingTouchesHero(x, y);
7370 TestIfPlayerTouchesCustomElement(x, y);
7371 TestIfElementTouchesCustomElement(x, y);
7375 static boolean ChangeElementNow(int x, int y, int element, int page)
7377 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7380 /* always use default change event to prevent running into a loop */
7381 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7382 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7384 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7386 /* reset actual trigger element and player */
7387 change->actual_trigger_element = EL_EMPTY;
7388 change->actual_trigger_player = EL_PLAYER_1;
7391 /* do not change already changed elements with same change event */
7393 if (Changed[x][y] & ChangeEvent[x][y])
7400 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7403 /* !!! indirect change before direct change !!! */
7404 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7407 if (change->explode)
7414 if (change->use_target_content)
7416 boolean complete_replace = TRUE;
7417 boolean can_replace[3][3];
7420 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7423 boolean is_walkable;
7424 boolean is_diggable;
7425 boolean is_collectible;
7426 boolean is_removable;
7427 boolean is_destructible;
7428 int ex = x + xx - 1;
7429 int ey = y + yy - 1;
7430 int content_element = change->target_content[xx][yy];
7433 can_replace[xx][yy] = TRUE;
7435 if (ex == x && ey == y) /* do not check changing element itself */
7438 if (content_element == EL_EMPTY_SPACE)
7440 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7445 if (!IN_LEV_FIELD(ex, ey))
7447 can_replace[xx][yy] = FALSE;
7448 complete_replace = FALSE;
7455 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7456 e = MovingOrBlocked2Element(ex, ey);
7461 is_empty = (IS_FREE(ex, ey) ||
7462 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7463 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7464 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7466 is_empty = (IS_FREE(ex, ey) ||
7467 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7469 is_walkable = (is_empty || IS_WALKABLE(e));
7470 is_diggable = (is_empty || IS_DIGGABLE(e));
7471 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7472 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7473 is_removable = (is_diggable || is_collectible);
7475 can_replace[xx][yy] =
7476 ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7477 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7478 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7479 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7480 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7481 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7483 if (!can_replace[xx][yy])
7484 complete_replace = FALSE;
7486 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7487 IS_WALKABLE(content_element)));
7489 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7491 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7494 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7495 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7496 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7498 can_replace[xx][yy] = FALSE;
7499 complete_replace = FALSE;
7504 if (!change->only_if_complete || complete_replace)
7506 boolean something_has_changed = FALSE;
7508 if (change->only_if_complete && change->use_random_replace &&
7509 RND(100) < change->random_percentage)
7512 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7514 int ex = x + xx - 1;
7515 int ey = y + yy - 1;
7516 int content_element;
7518 if (can_replace[xx][yy] && (!change->use_random_replace ||
7519 RND(100) < change->random_percentage))
7521 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7522 RemoveMovingField(ex, ey);
7524 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7526 content_element = change->target_content[xx][yy];
7527 target_element = GET_TARGET_ELEMENT(content_element, change);
7529 ChangeElementNowExt(ex, ey, target_element);
7531 something_has_changed = TRUE;
7533 /* for symmetry reasons, freeze newly created border elements */
7534 if (ex != x || ey != y)
7535 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7539 if (something_has_changed)
7540 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7545 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7547 ChangeElementNowExt(x, y, target_element);
7549 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7555 static void ChangeElement(int x, int y, int page)
7557 int element = MovingOrBlocked2Element(x, y);
7558 struct ElementInfo *ei = &element_info[element];
7559 struct ElementChangeInfo *change = &ei->change_page[page];
7562 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7565 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7566 x, y, element, element_info[element].token_name);
7567 printf("ChangeElement(): This should never happen!\n");
7572 /* this can happen with classic bombs on walkable, changing elements */
7573 if (!CAN_CHANGE(element))
7576 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7577 ChangeDelay[x][y] = 0;
7583 if (ChangeDelay[x][y] == 0) /* initialize element change */
7585 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7586 RND(change->delay_random * change->delay_frames)) + 1;
7588 ResetGfxAnimation(x, y);
7589 ResetRandomAnimationValue(x, y);
7591 if (change->pre_change_function)
7592 change->pre_change_function(x, y);
7595 ChangeDelay[x][y]--;
7597 if (ChangeDelay[x][y] != 0) /* continue element change */
7599 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7601 if (IS_ANIMATED(graphic))
7602 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7604 if (change->change_function)
7605 change->change_function(x, y);
7607 else /* finish element change */
7609 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7611 page = ChangePage[x][y];
7612 ChangePage[x][y] = -1;
7614 change = &ei->change_page[page];
7618 if (IS_MOVING(x, y) && !change->explode)
7620 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7623 ChangeDelay[x][y] = 1; /* try change after next move step */
7624 ChangePage[x][y] = page; /* remember page to use for change */
7629 if (ChangeElementNow(x, y, element, page))
7631 if (change->post_change_function)
7632 change->post_change_function(x, y);
7637 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7638 int trigger_element,
7645 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7647 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7650 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7652 int element = EL_CUSTOM_START + i;
7654 boolean change_element = FALSE;
7657 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7660 for (j = 0; j < element_info[element].num_change_pages; j++)
7662 struct ElementChangeInfo *change = &element_info[element].change_page[j];
7664 if (change->can_change &&
7665 change->events & CH_EVENT_BIT(trigger_event) &&
7666 change->trigger_side & trigger_side &&
7667 change->trigger_player & trigger_player &&
7668 change->trigger_page & trigger_page_bits &&
7669 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7672 if (!(change->events & CH_EVENT_BIT(trigger_event)))
7673 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7674 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7677 change_element = TRUE;
7680 change->actual_trigger_element = trigger_element;
7681 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7687 if (!change_element)
7690 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7693 if (x == lx && y == ly) /* do not change trigger element itself */
7697 if (Feld[x][y] == element)
7699 ChangeDelay[x][y] = 1;
7700 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7701 ChangeElement(x, y, page);
7709 static boolean CheckElementChangeExt(int x, int y,
7711 int trigger_element,
7717 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7720 if (Feld[x][y] == EL_BLOCKED)
7722 Blocked2Moving(x, y, &x, &y);
7723 element = Feld[x][y];
7727 if (Feld[x][y] != element) /* check if element has already changed */
7730 printf("::: %d ('%s') != %d ('%s') [%d]\n",
7731 Feld[x][y], element_info[Feld[x][y]].token_name,
7732 element, element_info[element].token_name,
7741 if (trigger_page < 0)
7743 boolean change_element = FALSE;
7746 for (i = 0; i < element_info[element].num_change_pages; i++)
7748 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7750 if (change->can_change &&
7751 change->events & CH_EVENT_BIT(trigger_event) &&
7752 change->trigger_side & trigger_side &&
7753 change->trigger_player & trigger_player)
7755 change_element = TRUE;
7758 change->actual_trigger_element = trigger_element;
7759 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7765 if (!change_element)
7770 struct ElementInfo *ei = &element_info[element];
7771 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7773 change->actual_trigger_element = trigger_element;
7774 change->actual_trigger_player = EL_PLAYER_1; /* unused */
7779 /* !!! this check misses pages with same event, but different side !!! */
7781 if (trigger_page < 0)
7782 trigger_page = element_info[element].event_page_nr[trigger_event];
7784 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7788 ChangeDelay[x][y] = 1;
7789 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7790 ChangeElement(x, y, trigger_page);
7795 static void PlayPlayerSound(struct PlayerInfo *player)
7797 int jx = player->jx, jy = player->jy;
7798 int element = player->element_nr;
7799 int last_action = player->last_action_waiting;
7800 int action = player->action_waiting;
7802 if (player->is_waiting)
7804 if (action != last_action)
7805 PlayLevelSoundElementAction(jx, jy, element, action);
7807 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7811 if (action != last_action)
7812 StopSound(element_info[element].sound[last_action]);
7814 if (last_action == ACTION_SLEEPING)
7815 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7819 static void PlayAllPlayersSound()
7823 for (i = 0; i < MAX_PLAYERS; i++)
7824 if (stored_player[i].active)
7825 PlayPlayerSound(&stored_player[i]);
7828 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7830 boolean last_waiting = player->is_waiting;
7831 int move_dir = player->MovDir;
7833 player->last_action_waiting = player->action_waiting;
7837 if (!last_waiting) /* not waiting -> waiting */
7839 player->is_waiting = TRUE;
7841 player->frame_counter_bored =
7843 game.player_boring_delay_fixed +
7844 SimpleRND(game.player_boring_delay_random);
7845 player->frame_counter_sleeping =
7847 game.player_sleeping_delay_fixed +
7848 SimpleRND(game.player_sleeping_delay_random);
7850 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7853 if (game.player_sleeping_delay_fixed +
7854 game.player_sleeping_delay_random > 0 &&
7855 player->anim_delay_counter == 0 &&
7856 player->post_delay_counter == 0 &&
7857 FrameCounter >= player->frame_counter_sleeping)
7858 player->is_sleeping = TRUE;
7859 else if (game.player_boring_delay_fixed +
7860 game.player_boring_delay_random > 0 &&
7861 FrameCounter >= player->frame_counter_bored)
7862 player->is_bored = TRUE;
7864 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7865 player->is_bored ? ACTION_BORING :
7868 if (player->is_sleeping)
7870 if (player->num_special_action_sleeping > 0)
7872 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7874 int last_special_action = player->special_action_sleeping;
7875 int num_special_action = player->num_special_action_sleeping;
7876 int special_action =
7877 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7878 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7879 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7880 last_special_action + 1 : ACTION_SLEEPING);
7881 int special_graphic =
7882 el_act_dir2img(player->element_nr, special_action, move_dir);
7884 player->anim_delay_counter =
7885 graphic_info[special_graphic].anim_delay_fixed +
7886 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7887 player->post_delay_counter =
7888 graphic_info[special_graphic].post_delay_fixed +
7889 SimpleRND(graphic_info[special_graphic].post_delay_random);
7891 player->special_action_sleeping = special_action;
7894 if (player->anim_delay_counter > 0)
7896 player->action_waiting = player->special_action_sleeping;
7897 player->anim_delay_counter--;
7899 else if (player->post_delay_counter > 0)
7901 player->post_delay_counter--;
7905 else if (player->is_bored)
7907 if (player->num_special_action_bored > 0)
7909 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7911 int special_action =
7912 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7913 int special_graphic =
7914 el_act_dir2img(player->element_nr, special_action, move_dir);
7916 player->anim_delay_counter =
7917 graphic_info[special_graphic].anim_delay_fixed +
7918 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7919 player->post_delay_counter =
7920 graphic_info[special_graphic].post_delay_fixed +
7921 SimpleRND(graphic_info[special_graphic].post_delay_random);
7923 player->special_action_bored = special_action;
7926 if (player->anim_delay_counter > 0)
7928 player->action_waiting = player->special_action_bored;
7929 player->anim_delay_counter--;
7931 else if (player->post_delay_counter > 0)
7933 player->post_delay_counter--;
7938 else if (last_waiting) /* waiting -> not waiting */
7940 player->is_waiting = FALSE;
7941 player->is_bored = FALSE;
7942 player->is_sleeping = FALSE;
7944 player->frame_counter_bored = -1;
7945 player->frame_counter_sleeping = -1;
7947 player->anim_delay_counter = 0;
7948 player->post_delay_counter = 0;
7950 player->action_waiting = ACTION_DEFAULT;
7952 player->special_action_bored = ACTION_DEFAULT;
7953 player->special_action_sleeping = ACTION_DEFAULT;
7958 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7961 static byte stored_player_action[MAX_PLAYERS];
7962 static int num_stored_actions = 0;
7964 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7965 int left = player_action & JOY_LEFT;
7966 int right = player_action & JOY_RIGHT;
7967 int up = player_action & JOY_UP;
7968 int down = player_action & JOY_DOWN;
7969 int button1 = player_action & JOY_BUTTON_1;
7970 int button2 = player_action & JOY_BUTTON_2;
7971 int dx = (left ? -1 : right ? 1 : 0);
7972 int dy = (up ? -1 : down ? 1 : 0);
7975 stored_player_action[player->index_nr] = 0;
7976 num_stored_actions++;
7980 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7983 if (!player->active || tape.pausing)
7987 printf("::: [%d %d %d %d] [%d %d]\n",
7988 left, right, up, down, button1, button2);
7994 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7999 if (player->MovPos == 0)
8000 CheckGravityMovement(player);
8003 snapped = SnapField(player, dx, dy);
8007 dropped = DropElement(player);
8009 moved = MovePlayer(player, dx, dy);
8012 if (tape.single_step && tape.recording && !tape.pausing)
8014 if (button1 || (dropped && !moved))
8016 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8017 SnapField(player, 0, 0); /* stop snapping */
8021 SetPlayerWaiting(player, FALSE);
8024 return player_action;
8026 stored_player_action[player->index_nr] = player_action;
8032 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8035 /* no actions for this player (no input at player's configured device) */
8037 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8038 SnapField(player, 0, 0);
8039 CheckGravityMovementWhenNotMoving(player);
8041 if (player->MovPos == 0)
8042 SetPlayerWaiting(player, TRUE);
8044 if (player->MovPos == 0) /* needed for tape.playing */
8045 player->is_moving = FALSE;
8047 player->is_dropping = FALSE;
8053 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8055 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8057 TapeRecordAction(stored_player_action);
8058 num_stored_actions = 0;
8065 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8067 static byte stored_player_action[MAX_PLAYERS];
8068 static int num_stored_actions = 0;
8069 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8070 int left = player_action & JOY_LEFT;
8071 int right = player_action & JOY_RIGHT;
8072 int up = player_action & JOY_UP;
8073 int down = player_action & JOY_DOWN;
8074 int button1 = player_action & JOY_BUTTON_1;
8075 int button2 = player_action & JOY_BUTTON_2;
8076 int dx = (left ? -1 : right ? 1 : 0);
8077 int dy = (up ? -1 : down ? 1 : 0);
8079 stored_player_action[player->index_nr] = 0;
8080 num_stored_actions++;
8082 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8084 if (!player->active || tape.pausing)
8089 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8092 snapped = SnapField(player, dx, dy);
8096 dropped = DropElement(player);
8098 moved = MovePlayer(player, dx, dy);
8101 if (tape.single_step && tape.recording && !tape.pausing)
8103 if (button1 || (dropped && !moved))
8105 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8106 SnapField(player, 0, 0); /* stop snapping */
8110 stored_player_action[player->index_nr] = player_action;
8114 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8116 /* no actions for this player (no input at player's configured device) */
8118 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8119 SnapField(player, 0, 0);
8120 CheckGravityMovementWhenNotMoving(player);
8122 if (player->MovPos == 0)
8123 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8125 if (player->MovPos == 0) /* needed for tape.playing */
8126 player->is_moving = FALSE;
8129 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8131 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8133 TapeRecordAction(stored_player_action);
8134 num_stored_actions = 0;
8141 static unsigned long action_delay = 0;
8142 unsigned long action_delay_value;
8143 int magic_wall_x = 0, magic_wall_y = 0;
8144 int i, x, y, element, graphic;
8145 byte *recorded_player_action;
8146 byte summarized_player_action = 0;
8148 byte tape_action[MAX_PLAYERS];
8151 if (game_status != GAME_MODE_PLAYING)
8154 action_delay_value =
8155 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8157 if (tape.playing && tape.warp_forward && !tape.pausing)
8158 action_delay_value = 0;
8160 /* ---------- main game synchronization point ---------- */
8162 WaitUntilDelayReached(&action_delay, action_delay_value);
8164 if (network_playing && !network_player_action_received)
8168 printf("DEBUG: try to get network player actions in time\n");
8172 #if defined(PLATFORM_UNIX)
8173 /* last chance to get network player actions without main loop delay */
8177 if (game_status != GAME_MODE_PLAYING)
8180 if (!network_player_action_received)
8184 printf("DEBUG: failed to get network player actions in time\n");
8195 printf("::: getting new tape action [%d]\n", FrameCounter);
8198 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8201 if (recorded_player_action == NULL && tape.pausing)
8206 printf("::: %d\n", stored_player[0].action);
8210 if (recorded_player_action != NULL)
8211 for (i = 0; i < MAX_PLAYERS; i++)
8212 stored_player[i].action = recorded_player_action[i];
8215 for (i = 0; i < MAX_PLAYERS; i++)
8217 summarized_player_action |= stored_player[i].action;
8219 if (!network_playing)
8220 stored_player[i].effective_action = stored_player[i].action;
8223 #if defined(PLATFORM_UNIX)
8224 if (network_playing)
8225 SendToServer_MovePlayer(summarized_player_action);
8228 if (!options.network && !setup.team_mode)
8229 local_player->effective_action = summarized_player_action;
8232 if (recorded_player_action != NULL)
8233 for (i = 0; i < MAX_PLAYERS; i++)
8234 stored_player[i].effective_action = recorded_player_action[i];
8238 for (i = 0; i < MAX_PLAYERS; i++)
8240 tape_action[i] = stored_player[i].effective_action;
8242 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8243 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8246 /* only save actions from input devices, but not programmed actions */
8248 TapeRecordAction(tape_action);
8251 for (i = 0; i < MAX_PLAYERS; i++)
8253 int actual_player_action = stored_player[i].effective_action;
8256 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8257 - rnd_equinox_tetrachloride 048
8258 - rnd_equinox_tetrachloride_ii 096
8259 - rnd_emanuel_schmieg 002
8260 - doctor_sloan_ww 001, 020
8262 if (stored_player[i].MovPos == 0)
8263 CheckGravityMovement(&stored_player[i]);
8267 /* overwrite programmed action with tape action */
8268 if (stored_player[i].programmed_action)
8269 actual_player_action = stored_player[i].programmed_action;
8273 if (stored_player[i].programmed_action)
8274 printf("::: %d\n", stored_player[i].programmed_action);
8277 if (recorded_player_action)
8280 if (stored_player[i].programmed_action &&
8281 stored_player[i].programmed_action != recorded_player_action[i])
8282 printf("::: %d: %d <-> %d\n", i,
8283 stored_player[i].programmed_action, recorded_player_action[i]);
8287 actual_player_action = recorded_player_action[i];
8292 /* overwrite tape action with programmed action */
8293 if (stored_player[i].programmed_action)
8294 actual_player_action = stored_player[i].programmed_action;
8299 printf("::: action: %d: %x [%d]\n",
8300 stored_player[i].MovPos, actual_player_action, FrameCounter);
8304 PlayerActions(&stored_player[i], actual_player_action);
8306 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8308 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8309 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8312 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8317 TapeRecordAction(tape_action);
8320 network_player_action_received = FALSE;
8322 ScrollScreen(NULL, SCROLL_GO_ON);
8328 for (i = 0; i < MAX_PLAYERS; i++)
8329 stored_player[i].Frame++;
8333 /* for downwards compatibility, the following code emulates a fixed bug that
8334 occured when pushing elements (causing elements that just made their last
8335 pushing step to already (if possible) make their first falling step in the
8336 same game frame, which is bad); this code is also needed to use the famous
8337 "spring push bug" which is used in older levels and might be wanted to be
8338 used also in newer levels, but in this case the buggy pushing code is only
8339 affecting the "spring" element and no other elements */
8342 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8344 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8347 for (i = 0; i < MAX_PLAYERS; i++)
8349 struct PlayerInfo *player = &stored_player[i];
8354 if (player->active && player->is_pushing && player->is_moving &&
8356 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8357 Feld[x][y] == EL_SPRING))
8359 if (player->active && player->is_pushing && player->is_moving &&
8363 ContinueMoving(x, y);
8365 /* continue moving after pushing (this is actually a bug) */
8366 if (!IS_MOVING(x, y))
8375 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8377 Changed[x][y] = CE_BITMASK_DEFAULT;
8378 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8381 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8383 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8384 printf("GameActions(): This should never happen!\n");
8386 ChangePage[x][y] = -1;
8391 if (WasJustMoving[x][y] > 0)
8392 WasJustMoving[x][y]--;
8393 if (WasJustFalling[x][y] > 0)
8394 WasJustFalling[x][y]--;
8395 if (CheckCollision[x][y] > 0)
8396 CheckCollision[x][y]--;
8401 /* reset finished pushing action (not done in ContinueMoving() to allow
8402 continous pushing animation for elements with zero push delay) */
8403 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8405 ResetGfxAnimation(x, y);
8406 DrawLevelField(x, y);
8411 if (IS_BLOCKED(x, y))
8415 Blocked2Moving(x, y, &oldx, &oldy);
8416 if (!IS_MOVING(oldx, oldy))
8418 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8419 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8420 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8421 printf("GameActions(): This should never happen!\n");
8427 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8429 element = Feld[x][y];
8431 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8433 graphic = el2img(element);
8439 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8441 element = graphic = 0;
8445 if (graphic_info[graphic].anim_global_sync)
8446 GfxFrame[x][y] = FrameCounter;
8448 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8449 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8450 ResetRandomAnimationValue(x, y);
8452 SetRandomAnimationValue(x, y);
8455 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8458 if (IS_INACTIVE(element))
8460 if (IS_ANIMATED(graphic))
8461 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8467 /* this may take place after moving, so 'element' may have changed */
8469 if (IS_CHANGING(x, y))
8471 if (IS_CHANGING(x, y) &&
8472 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8476 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8477 element_info[element].event_page_nr[CE_DELAY]);
8479 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8482 element = Feld[x][y];
8483 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8487 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8492 element = Feld[x][y];
8493 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8495 if (element == EL_MOLE)
8496 printf("::: %d, %d, %d [%d]\n",
8497 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8501 if (element == EL_YAMYAM)
8502 printf("::: %d, %d, %d\n",
8503 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8507 if (IS_ANIMATED(graphic) &&
8511 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8514 if (element == EL_BUG)
8515 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8519 if (element == EL_MOLE)
8520 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8524 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8525 EdelsteinFunkeln(x, y);
8527 else if ((element == EL_ACID ||
8528 element == EL_EXIT_OPEN ||
8529 element == EL_SP_EXIT_OPEN ||
8530 element == EL_SP_TERMINAL ||
8531 element == EL_SP_TERMINAL_ACTIVE ||
8532 element == EL_EXTRA_TIME ||
8533 element == EL_SHIELD_NORMAL ||
8534 element == EL_SHIELD_DEADLY) &&
8535 IS_ANIMATED(graphic))
8536 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8537 else if (IS_MOVING(x, y))
8538 ContinueMoving(x, y);
8539 else if (IS_ACTIVE_BOMB(element))
8540 CheckDynamite(x, y);
8542 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8543 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8545 else if (element == EL_AMOEBA_GROWING)
8546 AmoebeWaechst(x, y);
8547 else if (element == EL_AMOEBA_SHRINKING)
8548 AmoebaDisappearing(x, y);
8550 #if !USE_NEW_AMOEBA_CODE
8551 else if (IS_AMOEBALIVE(element))
8552 AmoebeAbleger(x, y);
8555 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8557 else if (element == EL_EXIT_CLOSED)
8559 else if (element == EL_SP_EXIT_CLOSED)
8561 else if (element == EL_EXPANDABLE_WALL_GROWING)
8563 else if (element == EL_EXPANDABLE_WALL ||
8564 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8565 element == EL_EXPANDABLE_WALL_VERTICAL ||
8566 element == EL_EXPANDABLE_WALL_ANY)
8568 else if (element == EL_FLAMES)
8569 CheckForDragon(x, y);
8571 else if (IS_AUTO_CHANGING(element))
8572 ChangeElement(x, y);
8574 else if (element == EL_EXPLOSION)
8575 ; /* drawing of correct explosion animation is handled separately */
8576 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8577 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8580 /* this may take place after moving, so 'element' may have changed */
8581 if (IS_AUTO_CHANGING(Feld[x][y]))
8582 ChangeElement(x, y);
8585 if (IS_BELT_ACTIVE(element))
8586 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8588 if (game.magic_wall_active)
8590 int jx = local_player->jx, jy = local_player->jy;
8592 /* play the element sound at the position nearest to the player */
8593 if ((element == EL_MAGIC_WALL_FULL ||
8594 element == EL_MAGIC_WALL_ACTIVE ||
8595 element == EL_MAGIC_WALL_EMPTYING ||
8596 element == EL_BD_MAGIC_WALL_FULL ||
8597 element == EL_BD_MAGIC_WALL_ACTIVE ||
8598 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8599 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8607 #if USE_NEW_AMOEBA_CODE
8608 /* new experimental amoeba growth stuff */
8610 if (!(FrameCounter % 8))
8613 static unsigned long random = 1684108901;
8615 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8618 x = (random >> 10) % lev_fieldx;
8619 y = (random >> 20) % lev_fieldy;
8621 x = RND(lev_fieldx);
8622 y = RND(lev_fieldy);
8624 element = Feld[x][y];
8627 if (!IS_PLAYER(x,y) &&
8628 (element == EL_EMPTY ||
8629 CAN_GROW_INTO(element) ||
8630 element == EL_QUICKSAND_EMPTY ||
8631 element == EL_ACID_SPLASH_LEFT ||
8632 element == EL_ACID_SPLASH_RIGHT))
8634 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8635 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8636 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8637 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8638 Feld[x][y] = EL_AMOEBA_DROP;
8641 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8642 if (!IS_PLAYER(x,y) &&
8643 (element == EL_EMPTY ||
8644 element == EL_SAND ||
8645 element == EL_QUICKSAND_EMPTY ||
8646 element == EL_ACID_SPLASH_LEFT ||
8647 element == EL_ACID_SPLASH_RIGHT))
8649 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8650 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8651 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8652 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8653 Feld[x][y] = EL_AMOEBA_DROP;
8657 random = random * 129 + 1;
8663 if (game.explosions_delayed)
8666 game.explosions_delayed = FALSE;
8668 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8670 element = Feld[x][y];
8672 if (ExplodeField[x][y])
8673 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8674 else if (element == EL_EXPLOSION)
8675 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8677 ExplodeField[x][y] = EX_TYPE_NONE;
8680 game.explosions_delayed = TRUE;
8683 if (game.magic_wall_active)
8685 if (!(game.magic_wall_time_left % 4))
8687 int element = Feld[magic_wall_x][magic_wall_y];
8689 if (element == EL_BD_MAGIC_WALL_FULL ||
8690 element == EL_BD_MAGIC_WALL_ACTIVE ||
8691 element == EL_BD_MAGIC_WALL_EMPTYING)
8692 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8694 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8697 if (game.magic_wall_time_left > 0)
8699 game.magic_wall_time_left--;
8700 if (!game.magic_wall_time_left)
8702 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8704 element = Feld[x][y];
8706 if (element == EL_MAGIC_WALL_ACTIVE ||
8707 element == EL_MAGIC_WALL_FULL)
8709 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8710 DrawLevelField(x, y);
8712 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8713 element == EL_BD_MAGIC_WALL_FULL)
8715 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8716 DrawLevelField(x, y);
8720 game.magic_wall_active = FALSE;
8725 if (game.light_time_left > 0)
8727 game.light_time_left--;
8729 if (game.light_time_left == 0)
8730 RedrawAllLightSwitchesAndInvisibleElements();
8733 if (game.timegate_time_left > 0)
8735 game.timegate_time_left--;
8737 if (game.timegate_time_left == 0)
8738 CloseAllOpenTimegates();
8741 for (i = 0; i < MAX_PLAYERS; i++)
8743 struct PlayerInfo *player = &stored_player[i];
8745 if (SHIELD_ON(player))
8747 if (player->shield_deadly_time_left)
8748 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8749 else if (player->shield_normal_time_left)
8750 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8754 if (TimeFrames >= FRAMES_PER_SECOND)
8759 if (!level.use_step_counter)
8763 for (i = 0; i < MAX_PLAYERS; i++)
8765 struct PlayerInfo *player = &stored_player[i];
8767 if (SHIELD_ON(player))
8769 player->shield_normal_time_left--;
8771 if (player->shield_deadly_time_left > 0)
8772 player->shield_deadly_time_left--;
8780 if (TimeLeft <= 10 && setup.time_limit)
8781 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8783 DrawGameValue_Time(TimeLeft);
8785 if (!TimeLeft && setup.time_limit)
8786 for (i = 0; i < MAX_PLAYERS; i++)
8787 KillHero(&stored_player[i]);
8789 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8790 DrawGameValue_Time(TimePlayed);
8793 if (tape.recording || tape.playing)
8794 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8798 PlayAllPlayersSound();
8800 if (options.debug) /* calculate frames per second */
8802 static unsigned long fps_counter = 0;
8803 static int fps_frames = 0;
8804 unsigned long fps_delay_ms = Counter() - fps_counter;
8808 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8810 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8813 fps_counter = Counter();
8816 redraw_mask |= REDRAW_FPS;
8820 if (stored_player[0].jx != stored_player[0].last_jx ||
8821 stored_player[0].jy != stored_player[0].last_jy)
8822 printf("::: %d, %d, %d, %d, %d\n",
8823 stored_player[0].MovDir,
8824 stored_player[0].MovPos,
8825 stored_player[0].GfxPos,
8826 stored_player[0].Frame,
8827 stored_player[0].StepFrame);
8834 for (i = 0; i < MAX_PLAYERS; i++)
8837 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8839 stored_player[i].Frame += move_frames;
8841 if (stored_player[i].MovPos != 0)
8842 stored_player[i].StepFrame += move_frames;
8844 if (stored_player[i].drop_delay > 0)
8845 stored_player[i].drop_delay--;
8850 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8852 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8854 local_player->show_envelope = 0;
8859 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8861 int min_x = x, min_y = y, max_x = x, max_y = y;
8864 for (i = 0; i < MAX_PLAYERS; i++)
8866 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8868 if (!stored_player[i].active || &stored_player[i] == player)
8871 min_x = MIN(min_x, jx);
8872 min_y = MIN(min_y, jy);
8873 max_x = MAX(max_x, jx);
8874 max_y = MAX(max_y, jy);
8877 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8880 static boolean AllPlayersInVisibleScreen()
8884 for (i = 0; i < MAX_PLAYERS; i++)
8886 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8888 if (!stored_player[i].active)
8891 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8898 void ScrollLevel(int dx, int dy)
8900 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8903 BlitBitmap(drawto_field, drawto_field,
8904 FX + TILEX * (dx == -1) - softscroll_offset,
8905 FY + TILEY * (dy == -1) - softscroll_offset,
8906 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8907 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8908 FX + TILEX * (dx == 1) - softscroll_offset,
8909 FY + TILEY * (dy == 1) - softscroll_offset);
8913 x = (dx == 1 ? BX1 : BX2);
8914 for (y = BY1; y <= BY2; y++)
8915 DrawScreenField(x, y);
8920 y = (dy == 1 ? BY1 : BY2);
8921 for (x = BX1; x <= BX2; x++)
8922 DrawScreenField(x, y);
8925 redraw_mask |= REDRAW_FIELD;
8929 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8931 int nextx = x + dx, nexty = y + dy;
8932 int element = Feld[x][y];
8935 element != EL_SP_PORT_LEFT &&
8936 element != EL_SP_GRAVITY_PORT_LEFT &&
8937 element != EL_SP_PORT_HORIZONTAL &&
8938 element != EL_SP_PORT_ANY) ||
8940 element != EL_SP_PORT_RIGHT &&
8941 element != EL_SP_GRAVITY_PORT_RIGHT &&
8942 element != EL_SP_PORT_HORIZONTAL &&
8943 element != EL_SP_PORT_ANY) ||
8945 element != EL_SP_PORT_UP &&
8946 element != EL_SP_GRAVITY_PORT_UP &&
8947 element != EL_SP_PORT_VERTICAL &&
8948 element != EL_SP_PORT_ANY) ||
8950 element != EL_SP_PORT_DOWN &&
8951 element != EL_SP_GRAVITY_PORT_DOWN &&
8952 element != EL_SP_PORT_VERTICAL &&
8953 element != EL_SP_PORT_ANY) ||
8954 !IN_LEV_FIELD(nextx, nexty) ||
8955 !IS_FREE(nextx, nexty))
8962 static boolean canFallDown(struct PlayerInfo *player)
8964 int jx = player->jx, jy = player->jy;
8966 return (IN_LEV_FIELD(jx, jy + 1) &&
8967 (IS_FREE(jx, jy + 1) ||
8968 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8969 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8970 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8973 static boolean canPassField(int x, int y, int move_dir)
8975 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8976 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8977 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8980 int element = Feld[x][y];
8982 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8983 !CAN_MOVE(element) &&
8984 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8985 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8986 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8989 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8991 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8992 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8993 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8997 int nextx = newx + dx;
8998 int nexty = newy + dy;
9002 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9003 (IS_DIGGABLE(Feld[newx][newy]) ||
9004 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9005 canPassField(newx, newy, move_dir)));
9007 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9008 (IS_DIGGABLE(Feld[newx][newy]) ||
9009 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9010 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9011 !CAN_MOVE(Feld[newx][newy]) &&
9012 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9013 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9014 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9018 static void CheckGravityMovement(struct PlayerInfo *player)
9020 if (game.gravity && !player->programmed_action)
9023 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9024 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9026 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9027 int move_dir_vertical = player->action & MV_VERTICAL;
9031 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9033 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9036 int jx = player->jx, jy = player->jy;
9038 boolean player_is_moving_to_valid_field =
9039 (!player_is_snapping &&
9040 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9041 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9045 (player->last_move_dir & MV_HORIZONTAL ?
9046 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9047 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9051 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9052 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9053 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9054 int new_jx = jx + dx, new_jy = jy + dy;
9055 int nextx = new_jx + dx, nexty = new_jy + dy;
9061 boolean player_can_fall_down = canFallDown(player);
9063 boolean player_can_fall_down =
9064 (IN_LEV_FIELD(jx, jy + 1) &&
9065 (IS_FREE(jx, jy + 1) ||
9066 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9070 boolean player_can_fall_down =
9071 (IN_LEV_FIELD(jx, jy + 1) &&
9072 (IS_FREE(jx, jy + 1)));
9076 boolean player_is_moving_to_valid_field =
9079 !player_is_snapping &&
9083 IN_LEV_FIELD(new_jx, new_jy) &&
9084 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9085 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9086 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9087 IN_LEV_FIELD(nextx, nexty) &&
9088 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9090 IN_LEV_FIELD(new_jx, new_jy) &&
9091 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9092 Feld[new_jx][new_jy] == EL_SAND ||
9093 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9094 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9095 /* !!! extend EL_SAND to anything diggable !!! */
9101 boolean player_is_standing_on_valid_field =
9102 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9103 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9107 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9108 player_can_fall_down,
9109 player_is_standing_on_valid_field,
9110 player_is_moving_to_valid_field,
9111 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9112 player->effective_action,
9113 player->can_fall_into_acid);
9116 if (player_can_fall_down &&
9118 !player_is_standing_on_valid_field &&
9120 !player_is_moving_to_valid_field)
9123 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9124 jx, jy, FrameCounter);
9127 player->programmed_action = MV_DOWN;
9132 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9135 return CheckGravityMovement(player);
9138 if (game.gravity && !player->programmed_action)
9140 int jx = player->jx, jy = player->jy;
9141 boolean field_under_player_is_free =
9142 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9143 boolean player_is_standing_on_valid_field =
9144 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9145 (IS_WALKABLE(Feld[jx][jy]) &&
9146 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9148 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9149 player->programmed_action = MV_DOWN;
9155 -----------------------------------------------------------------------------
9156 dx, dy: direction (non-diagonal) to try to move the player to
9157 real_dx, real_dy: direction as read from input device (can be diagonal)
9160 boolean MovePlayerOneStep(struct PlayerInfo *player,
9161 int dx, int dy, int real_dx, int real_dy)
9164 static int trigger_sides[4][2] =
9166 /* enter side leave side */
9167 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9168 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9169 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9170 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9172 int move_direction = (dx == -1 ? MV_LEFT :
9173 dx == +1 ? MV_RIGHT :
9175 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9176 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9177 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9179 int jx = player->jx, jy = player->jy;
9180 int new_jx = jx + dx, new_jy = jy + dy;
9184 if (!player->active || (!dx && !dy))
9185 return MF_NO_ACTION;
9187 player->MovDir = (dx < 0 ? MV_LEFT :
9190 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9192 if (!IN_LEV_FIELD(new_jx, new_jy))
9193 return MF_NO_ACTION;
9195 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9196 return MF_NO_ACTION;
9199 element = MovingOrBlocked2Element(new_jx, new_jy);
9201 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9204 if (DONT_RUN_INTO(element))
9206 if (element == EL_ACID && dx == 0 && dy == 1)
9208 SplashAcid(new_jx, new_jy);
9209 Feld[jx][jy] = EL_PLAYER_1;
9210 InitMovingField(jx, jy, MV_DOWN);
9211 Store[jx][jy] = EL_ACID;
9212 ContinueMoving(jx, jy);
9216 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9221 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9222 if (can_move != MF_MOVING)
9225 /* check if DigField() has caused relocation of the player */
9226 if (player->jx != jx || player->jy != jy)
9227 return MF_NO_ACTION;
9229 StorePlayer[jx][jy] = 0;
9230 player->last_jx = jx;
9231 player->last_jy = jy;
9232 player->jx = new_jx;
9233 player->jy = new_jy;
9234 StorePlayer[new_jx][new_jy] = player->element_nr;
9237 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9239 player->step_counter++;
9242 player->drop_delay = 0;
9245 PlayerVisit[jx][jy] = FrameCounter;
9247 ScrollPlayer(player, SCROLL_INIT);
9250 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9252 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9254 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9257 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9259 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9260 CE_OTHER_GETS_ENTERED, enter_side);
9261 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9262 CE_ENTERED_BY_PLAYER, enter_side);
9269 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9271 int jx = player->jx, jy = player->jy;
9272 int old_jx = jx, old_jy = jy;
9273 int moved = MF_NO_ACTION;
9276 if (!player->active)
9281 if (player->MovPos == 0)
9283 player->is_moving = FALSE;
9284 player->is_digging = FALSE;
9285 player->is_collecting = FALSE;
9286 player->is_snapping = FALSE;
9287 player->is_pushing = FALSE;
9293 if (!player->active || (!dx && !dy))
9298 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9304 if (!FrameReached(&player->move_delay, player->move_delay_value))
9307 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9308 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9314 /* store if player is automatically moved to next field */
9315 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9317 /* remove the last programmed player action */
9318 player->programmed_action = 0;
9322 /* should only happen if pre-1.2 tape recordings are played */
9323 /* this is only for backward compatibility */
9325 int original_move_delay_value = player->move_delay_value;
9328 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9332 /* scroll remaining steps with finest movement resolution */
9333 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9335 while (player->MovPos)
9337 ScrollPlayer(player, SCROLL_GO_ON);
9338 ScrollScreen(NULL, SCROLL_GO_ON);
9344 player->move_delay_value = original_move_delay_value;
9347 if (player->last_move_dir & MV_HORIZONTAL)
9349 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9350 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9354 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9355 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9361 if (moved & MF_MOVING && !ScreenMovPos &&
9362 (player == local_player || !options.network))
9364 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9365 int offset = (setup.scroll_delay ? 3 : 0);
9367 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9369 /* actual player has left the screen -- scroll in that direction */
9370 if (jx != old_jx) /* player has moved horizontally */
9371 scroll_x += (jx - old_jx);
9372 else /* player has moved vertically */
9373 scroll_y += (jy - old_jy);
9377 if (jx != old_jx) /* player has moved horizontally */
9379 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9380 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9381 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9383 /* don't scroll over playfield boundaries */
9384 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9385 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9387 /* don't scroll more than one field at a time */
9388 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9390 /* don't scroll against the player's moving direction */
9391 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9392 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9393 scroll_x = old_scroll_x;
9395 else /* player has moved vertically */
9397 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9398 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9399 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9401 /* don't scroll over playfield boundaries */
9402 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9403 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9405 /* don't scroll more than one field at a time */
9406 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9408 /* don't scroll against the player's moving direction */
9409 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9410 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9411 scroll_y = old_scroll_y;
9415 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9417 if (!options.network && !AllPlayersInVisibleScreen())
9419 scroll_x = old_scroll_x;
9420 scroll_y = old_scroll_y;
9424 ScrollScreen(player, SCROLL_INIT);
9425 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9432 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9434 if (!(moved & MF_MOVING) && !player->is_pushing)
9439 player->StepFrame = 0;
9441 if (moved & MF_MOVING)
9443 if (old_jx != jx && old_jy == jy)
9444 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9445 else if (old_jx == jx && old_jy != jy)
9446 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9448 DrawLevelField(jx, jy); /* for "crumbled sand" */
9450 player->last_move_dir = player->MovDir;
9451 player->is_moving = TRUE;
9453 player->is_snapping = FALSE;
9457 player->is_switching = FALSE;
9460 player->is_dropping = FALSE;
9464 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9467 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9470 static int trigger_sides[4][2] =
9472 /* enter side leave side */
9473 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9474 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9475 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9476 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9478 int move_direction = player->MovDir;
9479 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9480 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9481 int old_element = Feld[old_jx][old_jy];
9482 int new_element = Feld[jx][jy];
9485 /* !!! TEST ONLY !!! */
9486 if (IS_CUSTOM_ELEMENT(old_element))
9487 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9489 player->index_bit, leave_side);
9491 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9493 player->index_bit, leave_side);
9495 if (IS_CUSTOM_ELEMENT(new_element))
9496 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9497 player->index_bit, enter_side);
9499 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9500 CE_OTHER_GETS_ENTERED,
9501 player->index_bit, enter_side);
9511 CheckGravityMovementWhenNotMoving(player);
9514 player->last_move_dir = MV_NO_MOVING;
9516 player->is_moving = FALSE;
9519 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9521 TestIfHeroTouchesBadThing(jx, jy);
9522 TestIfPlayerTouchesCustomElement(jx, jy);
9525 if (!player->active)
9531 void ScrollPlayer(struct PlayerInfo *player, int mode)
9533 int jx = player->jx, jy = player->jy;
9534 int last_jx = player->last_jx, last_jy = player->last_jy;
9535 int move_stepsize = TILEX / player->move_delay_value;
9537 if (!player->active || !player->MovPos)
9540 if (mode == SCROLL_INIT)
9542 player->actual_frame_counter = FrameCounter;
9543 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9545 if (Feld[last_jx][last_jy] == EL_EMPTY)
9546 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9554 else if (!FrameReached(&player->actual_frame_counter, 1))
9557 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9558 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9560 if (!player->block_last_field &&
9561 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9562 Feld[last_jx][last_jy] = EL_EMPTY;
9564 /* before DrawPlayer() to draw correct player graphic for this case */
9565 if (player->MovPos == 0)
9566 CheckGravityMovement(player);
9569 DrawPlayer(player); /* needed here only to cleanup last field */
9572 if (player->MovPos == 0) /* player reached destination field */
9575 if (player->move_delay_reset_counter > 0)
9577 player->move_delay_reset_counter--;
9579 if (player->move_delay_reset_counter == 0)
9581 /* continue with normal speed after quickly moving through gate */
9582 HALVE_PLAYER_SPEED(player);
9584 /* be able to make the next move without delay */
9585 player->move_delay = 0;
9589 if (IS_PASSABLE(Feld[last_jx][last_jy]))
9591 /* continue with normal speed after quickly moving through gate */
9592 HALVE_PLAYER_SPEED(player);
9594 /* be able to make the next move without delay */
9595 player->move_delay = 0;
9599 if (player->block_last_field &&
9600 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9601 Feld[last_jx][last_jy] = EL_EMPTY;
9603 player->last_jx = jx;
9604 player->last_jy = jy;
9606 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9607 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9608 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9610 DrawPlayer(player); /* needed here only to cleanup last field */
9613 if (local_player->friends_still_needed == 0 ||
9614 IS_SP_ELEMENT(Feld[jx][jy]))
9615 player->LevelSolved = player->GameOver = TRUE;
9619 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9620 /* this breaks one level: "machine", level 000 */
9622 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9625 static int trigger_sides[4][2] =
9627 /* enter side leave side */
9628 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9629 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9630 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9631 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9633 int move_direction = player->MovDir;
9634 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9635 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9636 int old_jx = last_jx;
9637 int old_jy = last_jy;
9638 int old_element = Feld[old_jx][old_jy];
9639 int new_element = Feld[jx][jy];
9642 /* !!! TEST ONLY !!! */
9643 if (IS_CUSTOM_ELEMENT(old_element))
9644 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9646 player->index_bit, leave_side);
9648 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9650 player->index_bit, leave_side);
9652 if (IS_CUSTOM_ELEMENT(new_element))
9653 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9654 player->index_bit, enter_side);
9656 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9657 CE_OTHER_GETS_ENTERED,
9658 player->index_bit, enter_side);
9664 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9666 TestIfHeroTouchesBadThing(jx, jy);
9667 TestIfPlayerTouchesCustomElement(jx, jy);
9669 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9672 if (!player->active)
9676 if (level.use_step_counter)
9682 for (i = 0; i < MAX_PLAYERS; i++)
9684 struct PlayerInfo *player = &stored_player[i];
9686 if (SHIELD_ON(player))
9688 player->shield_normal_time_left--;
9690 if (player->shield_deadly_time_left > 0)
9691 player->shield_deadly_time_left--;
9699 if (TimeLeft <= 10 && setup.time_limit)
9700 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9702 DrawGameValue_Time(TimeLeft);
9704 if (!TimeLeft && setup.time_limit)
9705 for (i = 0; i < MAX_PLAYERS; i++)
9706 KillHero(&stored_player[i]);
9708 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9709 DrawGameValue_Time(TimePlayed);
9712 if (tape.single_step && tape.recording && !tape.pausing &&
9713 !player->programmed_action)
9714 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9718 void ScrollScreen(struct PlayerInfo *player, int mode)
9720 static unsigned long screen_frame_counter = 0;
9722 if (mode == SCROLL_INIT)
9724 /* set scrolling step size according to actual player's moving speed */
9725 ScrollStepSize = TILEX / player->move_delay_value;
9727 screen_frame_counter = FrameCounter;
9728 ScreenMovDir = player->MovDir;
9729 ScreenMovPos = player->MovPos;
9730 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9733 else if (!FrameReached(&screen_frame_counter, 1))
9738 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9739 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9740 redraw_mask |= REDRAW_FIELD;
9743 ScreenMovDir = MV_NO_MOVING;
9746 void TestIfPlayerTouchesCustomElement(int x, int y)
9748 static int xy[4][2] =
9755 static int trigger_sides[4][2] =
9757 /* center side border side */
9758 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9759 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9760 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9761 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9763 static int touch_dir[4] =
9770 int center_element = Feld[x][y]; /* should always be non-moving! */
9773 for (i = 0; i < NUM_DIRECTIONS; i++)
9775 int xx = x + xy[i][0];
9776 int yy = y + xy[i][1];
9777 int center_side = trigger_sides[i][0];
9778 int border_side = trigger_sides[i][1];
9781 if (!IN_LEV_FIELD(xx, yy))
9784 if (IS_PLAYER(x, y))
9786 struct PlayerInfo *player = PLAYERINFO(x, y);
9788 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9789 border_element = Feld[xx][yy]; /* may be moving! */
9790 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9791 border_element = Feld[xx][yy];
9792 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9793 border_element = MovingOrBlocked2Element(xx, yy);
9795 continue; /* center and border element do not touch */
9798 /* !!! TEST ONLY !!! */
9799 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9800 player->index_bit, border_side);
9801 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9802 CE_OTHER_GETS_TOUCHED,
9803 player->index_bit, border_side);
9805 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9806 CE_OTHER_GETS_TOUCHED,
9807 player->index_bit, border_side);
9808 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9809 player->index_bit, border_side);
9812 else if (IS_PLAYER(xx, yy))
9814 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9816 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9818 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9819 continue; /* center and border element do not touch */
9823 /* !!! TEST ONLY !!! */
9824 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9825 player->index_bit, center_side);
9826 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9827 CE_OTHER_GETS_TOUCHED,
9828 player->index_bit, center_side);
9830 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9831 CE_OTHER_GETS_TOUCHED,
9832 player->index_bit, center_side);
9833 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9834 player->index_bit, center_side);
9842 void TestIfElementTouchesCustomElement(int x, int y)
9844 static int xy[4][2] =
9851 static int trigger_sides[4][2] =
9853 /* center side border side */
9854 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9855 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9856 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9857 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9859 static int touch_dir[4] =
9866 boolean change_center_element = FALSE;
9867 int center_element_change_page = 0;
9868 int center_element = Feld[x][y]; /* should always be non-moving! */
9869 int border_trigger_element;
9872 for (i = 0; i < NUM_DIRECTIONS; i++)
9874 int xx = x + xy[i][0];
9875 int yy = y + xy[i][1];
9876 int center_side = trigger_sides[i][0];
9877 int border_side = trigger_sides[i][1];
9880 if (!IN_LEV_FIELD(xx, yy))
9883 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9884 border_element = Feld[xx][yy]; /* may be moving! */
9885 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9886 border_element = Feld[xx][yy];
9887 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9888 border_element = MovingOrBlocked2Element(xx, yy);
9890 continue; /* center and border element do not touch */
9892 /* check for change of center element (but change it only once) */
9893 if (IS_CUSTOM_ELEMENT(center_element) &&
9894 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9895 !change_center_element)
9897 for (j = 0; j < element_info[center_element].num_change_pages; j++)
9899 struct ElementChangeInfo *change =
9900 &element_info[center_element].change_page[j];
9902 if (change->can_change &&
9903 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9904 change->trigger_side & border_side &&
9906 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9908 change->trigger_element == border_element
9912 change_center_element = TRUE;
9913 center_element_change_page = j;
9914 border_trigger_element = border_element;
9921 /* check for change of border element */
9922 if (IS_CUSTOM_ELEMENT(border_element) &&
9923 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9925 for (j = 0; j < element_info[border_element].num_change_pages; j++)
9927 struct ElementChangeInfo *change =
9928 &element_info[border_element].change_page[j];
9930 if (change->can_change &&
9931 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9932 change->trigger_side & center_side &&
9934 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9936 change->trigger_element == center_element
9941 printf("::: border_element %d, %d\n", x, y);
9944 CheckElementChangeByPage(xx, yy, border_element, center_element,
9945 CE_OTHER_IS_TOUCHING, j);
9952 if (change_center_element)
9955 printf("::: center_element %d, %d\n", x, y);
9958 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
9959 CE_OTHER_IS_TOUCHING, center_element_change_page);
9963 void TestIfElementHitsCustomElement(int x, int y, int direction)
9965 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9966 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9967 int hitx = x + dx, hity = y + dy;
9968 int hitting_element = Feld[x][y];
9969 int touched_element;
9971 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9972 !IS_FREE(hitx, hity) &&
9973 (!IS_MOVING(hitx, hity) ||
9974 MovDir[hitx][hity] != direction ||
9975 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9978 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9982 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9986 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9987 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9989 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9990 CE_HITTING_SOMETHING, direction);
9992 if (IN_LEV_FIELD(hitx, hity))
9994 int opposite_direction = MV_DIR_OPPOSITE(direction);
9995 int hitting_side = direction;
9996 int touched_side = opposite_direction;
9998 int touched_element = MovingOrBlocked2Element(hitx, hity);
10001 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10002 MovDir[hitx][hity] != direction ||
10003 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10012 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10013 CE_HIT_BY_SOMETHING, opposite_direction);
10015 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10016 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10018 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10020 struct ElementChangeInfo *change =
10021 &element_info[hitting_element].change_page[i];
10023 if (change->can_change &&
10024 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10025 change->trigger_side & touched_side &&
10028 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10030 change->trigger_element == touched_element
10034 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10035 CE_OTHER_IS_HITTING, i);
10041 if (IS_CUSTOM_ELEMENT(touched_element) &&
10042 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10044 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10046 struct ElementChangeInfo *change =
10047 &element_info[touched_element].change_page[i];
10049 if (change->can_change &&
10050 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10051 change->trigger_side & hitting_side &&
10053 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10055 change->trigger_element == hitting_element
10059 CheckElementChangeByPage(hitx, hity, touched_element,
10060 hitting_element, CE_OTHER_GETS_HIT, i);
10070 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10072 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10073 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10074 int hitx = x + dx, hity = y + dy;
10075 int hitting_element = Feld[x][y];
10076 int touched_element;
10078 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10079 !IS_FREE(hitx, hity) &&
10080 (!IS_MOVING(hitx, hity) ||
10081 MovDir[hitx][hity] != direction ||
10082 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10085 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10089 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10093 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10094 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10096 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10097 EP_CAN_SMASH_EVERYTHING, direction);
10099 if (IN_LEV_FIELD(hitx, hity))
10101 int opposite_direction = MV_DIR_OPPOSITE(direction);
10102 int hitting_side = direction;
10103 int touched_side = opposite_direction;
10105 int touched_element = MovingOrBlocked2Element(hitx, hity);
10108 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10109 MovDir[hitx][hity] != direction ||
10110 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10119 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10120 CE_SMASHED_BY_SOMETHING, opposite_direction);
10122 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10123 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10125 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10127 struct ElementChangeInfo *change =
10128 &element_info[hitting_element].change_page[i];
10130 if (change->can_change &&
10131 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10132 change->trigger_side & touched_side &&
10135 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10137 change->trigger_element == touched_element
10141 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10142 CE_OTHER_IS_SMASHING, i);
10148 if (IS_CUSTOM_ELEMENT(touched_element) &&
10149 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10151 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10153 struct ElementChangeInfo *change =
10154 &element_info[touched_element].change_page[i];
10156 if (change->can_change &&
10157 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10158 change->trigger_side & hitting_side &&
10160 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10162 change->trigger_element == hitting_element
10166 CheckElementChangeByPage(hitx, hity, touched_element,
10167 hitting_element, CE_OTHER_GETS_SMASHED,i);
10177 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10179 int i, kill_x = -1, kill_y = -1;
10180 int bad_element = -1;
10181 static int test_xy[4][2] =
10188 static int test_dir[4] =
10196 for (i = 0; i < NUM_DIRECTIONS; i++)
10198 int test_x, test_y, test_move_dir, test_element;
10200 test_x = good_x + test_xy[i][0];
10201 test_y = good_y + test_xy[i][1];
10203 if (!IN_LEV_FIELD(test_x, test_y))
10207 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10210 test_element = Feld[test_x][test_y];
10212 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10215 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10216 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10218 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10219 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10223 bad_element = test_element;
10229 if (kill_x != -1 || kill_y != -1)
10231 if (IS_PLAYER(good_x, good_y))
10233 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10236 if (player->shield_deadly_time_left > 0 &&
10237 !IS_INDESTRUCTIBLE(bad_element))
10238 Bang(kill_x, kill_y);
10239 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10242 if (player->shield_deadly_time_left > 0)
10243 Bang(kill_x, kill_y);
10244 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10249 Bang(good_x, good_y);
10253 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10255 int i, kill_x = -1, kill_y = -1;
10256 int bad_element = Feld[bad_x][bad_y];
10257 static int test_xy[4][2] =
10264 static int touch_dir[4] =
10266 MV_LEFT | MV_RIGHT,
10271 static int test_dir[4] =
10279 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10282 for (i = 0; i < NUM_DIRECTIONS; i++)
10284 int test_x, test_y, test_move_dir, test_element;
10286 test_x = bad_x + test_xy[i][0];
10287 test_y = bad_y + test_xy[i][1];
10288 if (!IN_LEV_FIELD(test_x, test_y))
10292 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10294 test_element = Feld[test_x][test_y];
10296 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10297 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10299 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10300 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10302 /* good thing is player or penguin that does not move away */
10303 if (IS_PLAYER(test_x, test_y))
10305 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10307 if (bad_element == EL_ROBOT && player->is_moving)
10308 continue; /* robot does not kill player if he is moving */
10310 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10312 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10313 continue; /* center and border element do not touch */
10320 else if (test_element == EL_PENGUIN)
10329 if (kill_x != -1 || kill_y != -1)
10331 if (IS_PLAYER(kill_x, kill_y))
10333 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10336 if (player->shield_deadly_time_left > 0 &&
10337 !IS_INDESTRUCTIBLE(bad_element))
10338 Bang(bad_x, bad_y);
10339 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10342 if (player->shield_deadly_time_left > 0)
10343 Bang(bad_x, bad_y);
10344 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10349 Bang(kill_x, kill_y);
10353 void TestIfHeroTouchesBadThing(int x, int y)
10355 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10358 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10360 TestIfGoodThingHitsBadThing(x, y, move_dir);
10363 void TestIfBadThingTouchesHero(int x, int y)
10365 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10368 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10370 TestIfBadThingHitsGoodThing(x, y, move_dir);
10373 void TestIfFriendTouchesBadThing(int x, int y)
10375 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10378 void TestIfBadThingTouchesFriend(int x, int y)
10380 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10383 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10385 int i, kill_x = bad_x, kill_y = bad_y;
10386 static int xy[4][2] =
10394 for (i = 0; i < NUM_DIRECTIONS; i++)
10398 x = bad_x + xy[i][0];
10399 y = bad_y + xy[i][1];
10400 if (!IN_LEV_FIELD(x, y))
10403 element = Feld[x][y];
10404 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10405 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10413 if (kill_x != bad_x || kill_y != bad_y)
10414 Bang(bad_x, bad_y);
10417 void KillHero(struct PlayerInfo *player)
10419 int jx = player->jx, jy = player->jy;
10421 if (!player->active)
10424 /* remove accessible field at the player's position */
10425 Feld[jx][jy] = EL_EMPTY;
10427 /* deactivate shield (else Bang()/Explode() would not work right) */
10428 player->shield_normal_time_left = 0;
10429 player->shield_deadly_time_left = 0;
10435 static void KillHeroUnlessEnemyProtected(int x, int y)
10437 if (!PLAYER_ENEMY_PROTECTED(x, y))
10438 KillHero(PLAYERINFO(x, y));
10441 static void KillHeroUnlessExplosionProtected(int x, int y)
10443 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10444 KillHero(PLAYERINFO(x, y));
10447 void BuryHero(struct PlayerInfo *player)
10449 int jx = player->jx, jy = player->jy;
10451 if (!player->active)
10455 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10457 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10459 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10461 player->GameOver = TRUE;
10462 RemoveHero(player);
10465 void RemoveHero(struct PlayerInfo *player)
10467 int jx = player->jx, jy = player->jy;
10468 int i, found = FALSE;
10470 player->present = FALSE;
10471 player->active = FALSE;
10473 if (!ExplodeField[jx][jy])
10474 StorePlayer[jx][jy] = 0;
10476 for (i = 0; i < MAX_PLAYERS; i++)
10477 if (stored_player[i].active)
10481 AllPlayersGone = TRUE;
10488 =============================================================================
10489 checkDiagonalPushing()
10490 -----------------------------------------------------------------------------
10491 check if diagonal input device direction results in pushing of object
10492 (by checking if the alternative direction is walkable, diggable, ...)
10493 =============================================================================
10496 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10497 int x, int y, int real_dx, int real_dy)
10499 int jx, jy, dx, dy, xx, yy;
10501 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10504 /* diagonal direction: check alternative direction */
10509 xx = jx + (dx == 0 ? real_dx : 0);
10510 yy = jy + (dy == 0 ? real_dy : 0);
10512 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10516 =============================================================================
10518 -----------------------------------------------------------------------------
10519 x, y: field next to player (non-diagonal) to try to dig to
10520 real_dx, real_dy: direction as read from input device (can be diagonal)
10521 =============================================================================
10524 int DigField(struct PlayerInfo *player,
10525 int oldx, int oldy, int x, int y,
10526 int real_dx, int real_dy, int mode)
10528 static int trigger_sides[4] =
10530 CH_SIDE_RIGHT, /* moving left */
10531 CH_SIDE_LEFT, /* moving right */
10532 CH_SIDE_BOTTOM, /* moving up */
10533 CH_SIDE_TOP, /* moving down */
10536 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10538 int jx = oldx, jy = oldy;
10539 int dx = x - jx, dy = y - jy;
10540 int nextx = x + dx, nexty = y + dy;
10541 int move_direction = (dx == -1 ? MV_LEFT :
10542 dx == +1 ? MV_RIGHT :
10544 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10545 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10546 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10547 int old_element = Feld[jx][jy];
10550 if (player->MovPos == 0)
10552 player->is_digging = FALSE;
10553 player->is_collecting = FALSE;
10556 if (player->MovPos == 0) /* last pushing move finished */
10557 player->is_pushing = FALSE;
10559 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10561 player->is_switching = FALSE;
10562 player->push_delay = 0;
10564 return MF_NO_ACTION;
10567 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10568 return MF_NO_ACTION;
10573 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10575 if (IS_TUBE(Feld[jx][jy]) ||
10576 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10580 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10581 int tube_leave_directions[][2] =
10583 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10584 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10585 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10586 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
10587 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
10588 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
10589 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
10590 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
10591 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
10592 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
10593 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
10594 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10597 while (tube_leave_directions[i][0] != tube_element)
10600 if (tube_leave_directions[i][0] == -1) /* should not happen */
10604 if (!(tube_leave_directions[i][1] & move_direction))
10605 return MF_NO_ACTION; /* tube has no opening in this direction */
10610 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10611 old_element = Back[jx][jy];
10615 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10616 return MF_NO_ACTION; /* field has no opening in this direction */
10618 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10619 return MF_NO_ACTION; /* field has no opening in this direction */
10621 element = Feld[x][y];
10623 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10624 game.engine_version >= VERSION_IDENT(2,2,0,0))
10625 return MF_NO_ACTION;
10628 if (game.gravity && !player->is_auto_moving &&
10629 canFallDown(player) && move_direction != MV_DOWN &&
10630 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10631 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10635 if (element == EL_EMPTY_SPACE &&
10636 game.gravity && !player->is_auto_moving &&
10637 canFallDown(player) && move_direction != MV_DOWN)
10638 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10644 case EL_SP_PORT_LEFT:
10645 case EL_SP_PORT_RIGHT:
10646 case EL_SP_PORT_UP:
10647 case EL_SP_PORT_DOWN:
10648 case EL_SP_PORT_HORIZONTAL:
10649 case EL_SP_PORT_VERTICAL:
10650 case EL_SP_PORT_ANY:
10651 case EL_SP_GRAVITY_PORT_LEFT:
10652 case EL_SP_GRAVITY_PORT_RIGHT:
10653 case EL_SP_GRAVITY_PORT_UP:
10654 case EL_SP_GRAVITY_PORT_DOWN:
10656 if (!canEnterSupaplexPort(x, y, dx, dy))
10657 return MF_NO_ACTION;
10660 element != EL_SP_PORT_LEFT &&
10661 element != EL_SP_GRAVITY_PORT_LEFT &&
10662 element != EL_SP_PORT_HORIZONTAL &&
10663 element != EL_SP_PORT_ANY) ||
10665 element != EL_SP_PORT_RIGHT &&
10666 element != EL_SP_GRAVITY_PORT_RIGHT &&
10667 element != EL_SP_PORT_HORIZONTAL &&
10668 element != EL_SP_PORT_ANY) ||
10670 element != EL_SP_PORT_UP &&
10671 element != EL_SP_GRAVITY_PORT_UP &&
10672 element != EL_SP_PORT_VERTICAL &&
10673 element != EL_SP_PORT_ANY) ||
10675 element != EL_SP_PORT_DOWN &&
10676 element != EL_SP_GRAVITY_PORT_DOWN &&
10677 element != EL_SP_PORT_VERTICAL &&
10678 element != EL_SP_PORT_ANY) ||
10679 !IN_LEV_FIELD(nextx, nexty) ||
10680 !IS_FREE(nextx, nexty))
10681 return MF_NO_ACTION;
10684 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10685 element == EL_SP_GRAVITY_PORT_RIGHT ||
10686 element == EL_SP_GRAVITY_PORT_UP ||
10687 element == EL_SP_GRAVITY_PORT_DOWN)
10688 game.gravity = !game.gravity;
10690 /* automatically move to the next field with double speed */
10691 player->programmed_action = move_direction;
10693 if (player->move_delay_reset_counter == 0)
10695 player->move_delay_reset_counter = 2; /* two double speed steps */
10697 DOUBLE_PLAYER_SPEED(player);
10700 player->move_delay_reset_counter = 2;
10702 DOUBLE_PLAYER_SPEED(player);
10706 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10709 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10715 case EL_TUBE_VERTICAL:
10716 case EL_TUBE_HORIZONTAL:
10717 case EL_TUBE_VERTICAL_LEFT:
10718 case EL_TUBE_VERTICAL_RIGHT:
10719 case EL_TUBE_HORIZONTAL_UP:
10720 case EL_TUBE_HORIZONTAL_DOWN:
10721 case EL_TUBE_LEFT_UP:
10722 case EL_TUBE_LEFT_DOWN:
10723 case EL_TUBE_RIGHT_UP:
10724 case EL_TUBE_RIGHT_DOWN:
10727 int tube_enter_directions[][2] =
10729 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10730 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10731 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10732 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
10733 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
10734 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
10735 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
10736 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
10737 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
10738 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
10739 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
10740 { -1, MV_NO_MOVING }
10743 while (tube_enter_directions[i][0] != element)
10746 if (tube_enter_directions[i][0] == -1) /* should not happen */
10750 if (!(tube_enter_directions[i][1] & move_direction))
10751 return MF_NO_ACTION; /* tube has no opening in this direction */
10753 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10761 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10763 if (IS_WALKABLE(element))
10766 int sound_action = ACTION_WALKING;
10769 if (!ACCESS_FROM(element, opposite_direction))
10770 return MF_NO_ACTION; /* field not accessible from this direction */
10774 if (element == EL_EMPTY_SPACE &&
10775 game.gravity && !player->is_auto_moving &&
10776 canFallDown(player) && move_direction != MV_DOWN)
10777 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10780 if (IS_GATE(element))
10782 if (!player->key[element - EL_GATE_1])
10783 return MF_NO_ACTION;
10785 else if (IS_GATE_GRAY(element))
10787 if (!player->key[element - EL_GATE_1_GRAY])
10788 return MF_NO_ACTION;
10790 else if (element == EL_EXIT_OPEN ||
10791 element == EL_SP_EXIT_OPEN ||
10792 element == EL_SP_EXIT_OPENING)
10794 sound_action = ACTION_PASSING; /* player is passing exit */
10796 else if (element == EL_EMPTY)
10798 sound_action = ACTION_MOVING; /* nothing to walk on */
10801 /* play sound from background or player, whatever is available */
10802 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
10803 PlayLevelSoundElementAction(x, y, element, sound_action);
10805 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10810 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
10812 else if (IS_PASSABLE(element))
10816 if (!canPassField(x, y, move_direction))
10817 return MF_NO_ACTION;
10822 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
10823 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
10824 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
10825 return MF_NO_ACTION;
10827 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10828 return MF_NO_ACTION;
10833 if (!ACCESS_FROM(element, opposite_direction))
10834 return MF_NO_ACTION; /* field not accessible from this direction */
10836 if (IS_CUSTOM_ELEMENT(element) &&
10837 !ACCESS_FROM(element, opposite_direction))
10838 return MF_NO_ACTION; /* field not accessible from this direction */
10842 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10843 return MF_NO_ACTION;
10848 if (IS_EM_GATE(element))
10850 if (!player->key[element - EL_EM_GATE_1])
10851 return MF_NO_ACTION;
10853 else if (IS_EM_GATE_GRAY(element))
10855 if (!player->key[element - EL_EM_GATE_1_GRAY])
10856 return MF_NO_ACTION;
10858 else if (IS_SP_PORT(element))
10860 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10861 element == EL_SP_GRAVITY_PORT_RIGHT ||
10862 element == EL_SP_GRAVITY_PORT_UP ||
10863 element == EL_SP_GRAVITY_PORT_DOWN)
10864 game.gravity = !game.gravity;
10867 /* automatically move to the next field with double speed */
10868 player->programmed_action = move_direction;
10870 if (player->move_delay_reset_counter == 0)
10872 player->move_delay_reset_counter = 2; /* two double speed steps */
10874 DOUBLE_PLAYER_SPEED(player);
10877 player->move_delay_reset_counter = 2;
10879 DOUBLE_PLAYER_SPEED(player);
10882 PlayLevelSoundAction(x, y, ACTION_PASSING);
10886 else if (IS_DIGGABLE(element))
10890 if (mode != DF_SNAP)
10893 GfxElement[x][y] = GFX_ELEMENT(element);
10896 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
10898 player->is_digging = TRUE;
10901 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10903 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
10904 player->index_bit, dig_side);
10907 if (mode == DF_SNAP)
10908 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10913 else if (IS_COLLECTIBLE(element))
10917 if (mode != DF_SNAP)
10919 GfxElement[x][y] = element;
10920 player->is_collecting = TRUE;
10923 if (element == EL_SPEED_PILL)
10924 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10925 else if (element == EL_EXTRA_TIME && level.time > 0)
10928 DrawGameValue_Time(TimeLeft);
10930 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10932 player->shield_normal_time_left += 10;
10933 if (element == EL_SHIELD_DEADLY)
10934 player->shield_deadly_time_left += 10;
10936 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10938 if (player->inventory_size < MAX_INVENTORY_SIZE)
10939 player->inventory_element[player->inventory_size++] = element;
10941 DrawGameValue_Dynamite(local_player->inventory_size);
10943 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10945 player->dynabomb_count++;
10946 player->dynabombs_left++;
10948 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10950 player->dynabomb_size++;
10952 else if (element == EL_DYNABOMB_INCREASE_POWER)
10954 player->dynabomb_xl = TRUE;
10956 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
10957 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
10959 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
10960 element - EL_KEY_1 : element - EL_EM_KEY_1);
10962 player->key[key_nr] = TRUE;
10964 DrawGameValue_Keys(player);
10966 redraw_mask |= REDRAW_DOOR_1;
10968 else if (IS_ENVELOPE(element))
10971 player->show_envelope = element;
10973 ShowEnvelope(element - EL_ENVELOPE_1);
10976 else if (IS_DROPPABLE(element) ||
10977 IS_THROWABLE(element)) /* can be collected and dropped */
10981 if (element_info[element].collect_count == 0)
10982 player->inventory_infinite_element = element;
10984 for (i = 0; i < element_info[element].collect_count; i++)
10985 if (player->inventory_size < MAX_INVENTORY_SIZE)
10986 player->inventory_element[player->inventory_size++] = element;
10988 DrawGameValue_Dynamite(local_player->inventory_size);
10990 else if (element_info[element].collect_count > 0)
10992 local_player->gems_still_needed -=
10993 element_info[element].collect_count;
10994 if (local_player->gems_still_needed < 0)
10995 local_player->gems_still_needed = 0;
10997 DrawGameValue_Emeralds(local_player->gems_still_needed);
11000 RaiseScoreElement(element);
11001 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11003 CheckTriggeredElementChangeByPlayer(x, y, element,
11004 CE_OTHER_GETS_COLLECTED,
11005 player->index_bit, dig_side);
11008 if (mode == DF_SNAP)
11009 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11014 else if (IS_PUSHABLE(element))
11016 if (mode == DF_SNAP && element != EL_BD_ROCK)
11017 return MF_NO_ACTION;
11019 if (CAN_FALL(element) && dy)
11020 return MF_NO_ACTION;
11022 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11023 !(element == EL_SPRING && level.use_spring_bug))
11024 return MF_NO_ACTION;
11027 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11028 ((move_direction & MV_VERTICAL &&
11029 ((element_info[element].move_pattern & MV_LEFT &&
11030 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11031 (element_info[element].move_pattern & MV_RIGHT &&
11032 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11033 (move_direction & MV_HORIZONTAL &&
11034 ((element_info[element].move_pattern & MV_UP &&
11035 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11036 (element_info[element].move_pattern & MV_DOWN &&
11037 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11038 return MF_NO_ACTION;
11042 /* do not push elements already moving away faster than player */
11043 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11044 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11045 return MF_NO_ACTION;
11047 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11048 return MF_NO_ACTION;
11052 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11054 if (player->push_delay_value == -1)
11055 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11057 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11059 if (!player->is_pushing)
11060 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11064 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11065 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11066 !player_is_pushing))
11067 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11070 if (!player->is_pushing &&
11071 game.engine_version >= VERSION_IDENT(2,2,0,7))
11072 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11076 printf("::: push delay: %ld [%d, %d] [%d]\n",
11077 player->push_delay_value, FrameCounter, game.engine_version,
11078 player->is_pushing);
11081 player->is_pushing = TRUE;
11083 if (!(IN_LEV_FIELD(nextx, nexty) &&
11084 (IS_FREE(nextx, nexty) ||
11085 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11086 IS_SB_ELEMENT(element)))))
11087 return MF_NO_ACTION;
11089 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11090 return MF_NO_ACTION;
11092 if (player->push_delay == 0) /* new pushing; restart delay */
11093 player->push_delay = FrameCounter;
11095 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11096 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11097 element != EL_SPRING && element != EL_BALLOON)
11099 /* make sure that there is no move delay before next try to push */
11100 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11101 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11103 return MF_NO_ACTION;
11107 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11110 if (IS_SB_ELEMENT(element))
11112 if (element == EL_SOKOBAN_FIELD_FULL)
11114 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11115 local_player->sokobanfields_still_needed++;
11118 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11120 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11121 local_player->sokobanfields_still_needed--;
11124 Feld[x][y] = EL_SOKOBAN_OBJECT;
11126 if (Back[x][y] == Back[nextx][nexty])
11127 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11128 else if (Back[x][y] != 0)
11129 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11132 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11135 if (local_player->sokobanfields_still_needed == 0 &&
11136 game.emulation == EMU_SOKOBAN)
11138 player->LevelSolved = player->GameOver = TRUE;
11139 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11143 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11145 InitMovingField(x, y, move_direction);
11146 GfxAction[x][y] = ACTION_PUSHING;
11148 if (mode == DF_SNAP)
11149 ContinueMoving(x, y);
11151 MovPos[x][y] = (dx != 0 ? dx : dy);
11153 Pushed[x][y] = TRUE;
11154 Pushed[nextx][nexty] = TRUE;
11156 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11157 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11159 player->push_delay_value = -1; /* get new value later */
11162 /* check for element change _after_ element has been pushed! */
11166 /* !!! TEST ONLY !!! */
11167 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11168 player->index_bit, dig_side);
11169 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11170 player->index_bit, dig_side);
11172 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11173 player->index_bit, dig_side);
11174 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11175 player->index_bit, dig_side);
11181 else if (IS_SWITCHABLE(element))
11183 if (PLAYER_SWITCHING(player, x, y))
11186 player->is_switching = TRUE;
11187 player->switch_x = x;
11188 player->switch_y = y;
11190 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11192 if (element == EL_ROBOT_WHEEL)
11194 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11198 DrawLevelField(x, y);
11200 else if (element == EL_SP_TERMINAL)
11204 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11206 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11208 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11209 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11212 else if (IS_BELT_SWITCH(element))
11214 ToggleBeltSwitch(x, y);
11216 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11217 element == EL_SWITCHGATE_SWITCH_DOWN)
11219 ToggleSwitchgateSwitch(x, y);
11221 else if (element == EL_LIGHT_SWITCH ||
11222 element == EL_LIGHT_SWITCH_ACTIVE)
11224 ToggleLightSwitch(x, y);
11227 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11228 SND_LIGHT_SWITCH_ACTIVATING :
11229 SND_LIGHT_SWITCH_DEACTIVATING);
11232 else if (element == EL_TIMEGATE_SWITCH)
11234 ActivateTimegateSwitch(x, y);
11236 else if (element == EL_BALLOON_SWITCH_LEFT ||
11237 element == EL_BALLOON_SWITCH_RIGHT ||
11238 element == EL_BALLOON_SWITCH_UP ||
11239 element == EL_BALLOON_SWITCH_DOWN ||
11240 element == EL_BALLOON_SWITCH_ANY)
11242 if (element == EL_BALLOON_SWITCH_ANY)
11243 game.balloon_dir = move_direction;
11245 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11246 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11247 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11248 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11251 else if (element == EL_LAMP)
11253 Feld[x][y] = EL_LAMP_ACTIVE;
11254 local_player->lights_still_needed--;
11256 DrawLevelField(x, y);
11258 else if (element == EL_TIME_ORB_FULL)
11260 Feld[x][y] = EL_TIME_ORB_EMPTY;
11262 DrawGameValue_Time(TimeLeft);
11264 DrawLevelField(x, y);
11267 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11275 if (!PLAYER_SWITCHING(player, x, y))
11277 player->is_switching = TRUE;
11278 player->switch_x = x;
11279 player->switch_y = y;
11282 /* !!! TEST ONLY !!! */
11283 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11284 player->index_bit, dig_side);
11285 CheckTriggeredElementChangeByPlayer(x, y, element,
11286 CE_OTHER_IS_SWITCHING,
11287 player->index_bit, dig_side);
11289 CheckTriggeredElementChangeByPlayer(x, y, element,
11290 CE_OTHER_IS_SWITCHING,
11291 player->index_bit, dig_side);
11292 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11293 player->index_bit, dig_side);
11298 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11299 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11300 player->index_bit, dig_side);
11301 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11302 player->index_bit, dig_side);
11304 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11305 player->index_bit, dig_side);
11306 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11307 player->index_bit, dig_side);
11311 return MF_NO_ACTION;
11314 player->push_delay = 0;
11316 if (Feld[x][y] != element) /* really digged/collected something */
11317 player->is_collecting = !player->is_digging;
11322 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11324 int jx = player->jx, jy = player->jy;
11325 int x = jx + dx, y = jy + dy;
11326 int snap_direction = (dx == -1 ? MV_LEFT :
11327 dx == +1 ? MV_RIGHT :
11329 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11332 if (player->MovPos != 0)
11335 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11339 if (!player->active || !IN_LEV_FIELD(x, y))
11347 if (player->MovPos == 0)
11348 player->is_pushing = FALSE;
11350 player->is_snapping = FALSE;
11352 if (player->MovPos == 0)
11354 player->is_moving = FALSE;
11355 player->is_digging = FALSE;
11356 player->is_collecting = FALSE;
11362 if (player->is_snapping)
11365 player->MovDir = snap_direction;
11368 if (player->MovPos == 0)
11371 player->is_moving = FALSE;
11372 player->is_digging = FALSE;
11373 player->is_collecting = FALSE;
11376 player->is_dropping = FALSE;
11378 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11381 player->is_snapping = TRUE;
11384 if (player->MovPos == 0)
11387 player->is_moving = FALSE;
11388 player->is_digging = FALSE;
11389 player->is_collecting = FALSE;
11393 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11394 DrawLevelField(player->last_jx, player->last_jy);
11397 DrawLevelField(x, y);
11406 boolean DropElement(struct PlayerInfo *player)
11408 static int trigger_sides[4] =
11410 CH_SIDE_LEFT, /* dropping left */
11411 CH_SIDE_RIGHT, /* dropping right */
11412 CH_SIDE_TOP, /* dropping up */
11413 CH_SIDE_BOTTOM, /* dropping down */
11415 int old_element, new_element;
11416 int dropx = player->jx, dropy = player->jy;
11417 int drop_direction = player->MovDir;
11418 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11419 int drop_element = (player->inventory_size > 0 ?
11420 player->inventory_element[player->inventory_size - 1] :
11421 player->inventory_infinite_element != EL_UNDEFINED ?
11422 player->inventory_infinite_element :
11423 player->dynabombs_left > 0 ?
11424 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11427 if (IS_THROWABLE(drop_element))
11429 dropx += GET_DX_FROM_DIR(drop_direction);
11430 dropy += GET_DY_FROM_DIR(drop_direction);
11432 if (!IN_LEV_FIELD(dropx, dropy))
11436 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11437 new_element = drop_element; /* default: no change when dropping */
11439 /* check if player is active, not moving and ready to drop */
11440 if (!player->active || player->MovPos || player->drop_delay > 0)
11443 /* check if player has anything that can be dropped */
11445 if (new_element == EL_UNDEFINED)
11448 if (player->inventory_size == 0 &&
11449 player->inventory_infinite_element == EL_UNDEFINED &&
11450 player->dynabombs_left == 0)
11454 /* check if anything can be dropped at the current position */
11455 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11458 /* collected custom elements can only be dropped on empty fields */
11460 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11463 if (player->inventory_size > 0 &&
11464 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11465 && old_element != EL_EMPTY)
11469 if (old_element != EL_EMPTY)
11470 Back[dropx][dropy] = old_element; /* store old element on this field */
11472 ResetGfxAnimation(dropx, dropy);
11473 ResetRandomAnimationValue(dropx, dropy);
11475 if (player->inventory_size > 0 ||
11476 player->inventory_infinite_element != EL_UNDEFINED)
11478 if (player->inventory_size > 0)
11480 player->inventory_size--;
11483 new_element = player->inventory_element[player->inventory_size];
11486 DrawGameValue_Dynamite(local_player->inventory_size);
11488 if (new_element == EL_DYNAMITE)
11489 new_element = EL_DYNAMITE_ACTIVE;
11490 else if (new_element == EL_SP_DISK_RED)
11491 new_element = EL_SP_DISK_RED_ACTIVE;
11494 Feld[dropx][dropy] = new_element;
11496 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11497 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11498 el2img(Feld[dropx][dropy]), 0);
11500 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11503 /* needed if previous element just changed to "empty" in the last frame */
11504 Changed[dropx][dropy] = 0; /* allow another change */
11508 /* !!! TEST ONLY !!! */
11509 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11510 player->index_bit, drop_side);
11511 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11512 CE_OTHER_GETS_DROPPED,
11513 player->index_bit, drop_side);
11515 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11516 CE_OTHER_GETS_DROPPED,
11517 player->index_bit, drop_side);
11518 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11519 player->index_bit, drop_side);
11522 TestIfElementTouchesCustomElement(dropx, dropy);
11524 else /* player is dropping a dyna bomb */
11526 player->dynabombs_left--;
11529 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
11532 Feld[dropx][dropy] = new_element;
11534 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11535 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11536 el2img(Feld[dropx][dropy]), 0);
11538 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11545 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11548 InitField_WithBug1(dropx, dropy, FALSE);
11550 InitField(dropx, dropy, FALSE);
11551 if (CAN_MOVE(Feld[dropx][dropy]))
11552 InitMovDir(dropx, dropy);
11556 new_element = Feld[dropx][dropy]; /* element might have changed */
11558 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11559 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11562 int move_stepsize = element_info[new_element].move_stepsize;
11564 int move_direction, nextx, nexty;
11566 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11567 MovDir[dropx][dropy] = drop_direction;
11569 move_direction = MovDir[dropx][dropy];
11570 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11571 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11574 Changed[dropx][dropy] = 0; /* allow another change */
11575 CheckCollision[dropx][dropy] = 2;
11578 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
11581 WasJustMoving[dropx][dropy] = 3;
11584 InitMovingField(dropx, dropy, move_direction);
11585 ContinueMoving(dropx, dropy);
11592 Changed[dropx][dropy] = 0; /* allow another change */
11595 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
11597 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
11598 CE_HITTING_SOMETHING, move_direction);
11606 player->drop_delay = 2 * TILEX / move_stepsize + 1;
11611 player->drop_delay = 8 + 8 + 8;
11615 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11620 player->is_dropping = TRUE;
11626 /* ------------------------------------------------------------------------- */
11627 /* game sound playing functions */
11628 /* ------------------------------------------------------------------------- */
11630 static int *loop_sound_frame = NULL;
11631 static int *loop_sound_volume = NULL;
11633 void InitPlayLevelSound()
11635 int num_sounds = getSoundListSize();
11637 checked_free(loop_sound_frame);
11638 checked_free(loop_sound_volume);
11640 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11641 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11644 static void PlayLevelSound(int x, int y, int nr)
11646 int sx = SCREENX(x), sy = SCREENY(y);
11647 int volume, stereo_position;
11648 int max_distance = 8;
11649 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11651 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11652 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11655 if (!IN_LEV_FIELD(x, y) ||
11656 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11657 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11660 volume = SOUND_MAX_VOLUME;
11662 if (!IN_SCR_FIELD(sx, sy))
11664 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11665 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11667 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11670 stereo_position = (SOUND_MAX_LEFT +
11671 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11672 (SCR_FIELDX + 2 * max_distance));
11674 if (IS_LOOP_SOUND(nr))
11676 /* This assures that quieter loop sounds do not overwrite louder ones,
11677 while restarting sound volume comparison with each new game frame. */
11679 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11682 loop_sound_volume[nr] = volume;
11683 loop_sound_frame[nr] = FrameCounter;
11686 PlaySoundExt(nr, volume, stereo_position, type);
11689 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11691 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11692 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11693 y < LEVELY(BY1) ? LEVELY(BY1) :
11694 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11698 static void PlayLevelSoundAction(int x, int y, int action)
11700 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11703 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11705 int sound_effect = element_info[element].sound[action];
11707 if (sound_effect != SND_UNDEFINED)
11708 PlayLevelSound(x, y, sound_effect);
11711 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11714 int sound_effect = element_info[element].sound[action];
11716 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11717 PlayLevelSound(x, y, sound_effect);
11720 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11722 int sound_effect = element_info[Feld[x][y]].sound[action];
11724 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11725 PlayLevelSound(x, y, sound_effect);
11728 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11730 int sound_effect = element_info[Feld[x][y]].sound[action];
11732 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11733 StopSound(sound_effect);
11736 static void PlayLevelMusic()
11738 if (levelset.music[level_nr] != MUS_UNDEFINED)
11739 PlayMusic(levelset.music[level_nr]); /* from config file */
11741 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11744 void RaiseScore(int value)
11746 local_player->score += value;
11748 DrawGameValue_Score(local_player->score);
11751 void RaiseScoreElement(int element)
11756 case EL_BD_DIAMOND:
11757 case EL_EMERALD_YELLOW:
11758 case EL_EMERALD_RED:
11759 case EL_EMERALD_PURPLE:
11760 case EL_SP_INFOTRON:
11761 RaiseScore(level.score[SC_EMERALD]);
11764 RaiseScore(level.score[SC_DIAMOND]);
11767 RaiseScore(level.score[SC_CRYSTAL]);
11770 RaiseScore(level.score[SC_PEARL]);
11773 case EL_BD_BUTTERFLY:
11774 case EL_SP_ELECTRON:
11775 RaiseScore(level.score[SC_BUG]);
11778 case EL_BD_FIREFLY:
11779 case EL_SP_SNIKSNAK:
11780 RaiseScore(level.score[SC_SPACESHIP]);
11783 case EL_DARK_YAMYAM:
11784 RaiseScore(level.score[SC_YAMYAM]);
11787 RaiseScore(level.score[SC_ROBOT]);
11790 RaiseScore(level.score[SC_PACMAN]);
11793 RaiseScore(level.score[SC_NUT]);
11796 case EL_SP_DISK_RED:
11797 case EL_DYNABOMB_INCREASE_NUMBER:
11798 case EL_DYNABOMB_INCREASE_SIZE:
11799 case EL_DYNABOMB_INCREASE_POWER:
11800 RaiseScore(level.score[SC_DYNAMITE]);
11802 case EL_SHIELD_NORMAL:
11803 case EL_SHIELD_DEADLY:
11804 RaiseScore(level.score[SC_SHIELD]);
11806 case EL_EXTRA_TIME:
11807 RaiseScore(level.score[SC_TIME_BONUS]);
11813 RaiseScore(level.score[SC_KEY]);
11816 RaiseScore(element_info[element].collect_score);
11821 void RequestQuitGame(boolean ask_if_really_quit)
11823 if (AllPlayersGone ||
11824 !ask_if_really_quit ||
11825 level_editor_test_game ||
11826 Request("Do you really want to quit the game ?",
11827 REQ_ASK | REQ_STAY_CLOSED))
11829 #if defined(PLATFORM_UNIX)
11830 if (options.network)
11831 SendToServer_StopPlaying();
11835 game_status = GAME_MODE_MAIN;
11843 if (tape.playing && tape.deactivate_display)
11844 TapeDeactivateDisplayOff(TRUE);
11847 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11850 if (tape.playing && tape.deactivate_display)
11851 TapeDeactivateDisplayOn();
11858 /* ---------- new game button stuff ---------------------------------------- */
11860 /* graphic position values for game buttons */
11861 #define GAME_BUTTON_XSIZE 30
11862 #define GAME_BUTTON_YSIZE 30
11863 #define GAME_BUTTON_XPOS 5
11864 #define GAME_BUTTON_YPOS 215
11865 #define SOUND_BUTTON_XPOS 5
11866 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11868 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11869 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11870 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11871 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11872 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11873 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11880 } gamebutton_info[NUM_GAME_BUTTONS] =
11883 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11888 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11889 GAME_CTRL_ID_PAUSE,
11893 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11898 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11899 SOUND_CTRL_ID_MUSIC,
11900 "background music on/off"
11903 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11904 SOUND_CTRL_ID_LOOPS,
11905 "sound loops on/off"
11908 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11909 SOUND_CTRL_ID_SIMPLE,
11910 "normal sounds on/off"
11914 void CreateGameButtons()
11918 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11920 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11921 struct GadgetInfo *gi;
11924 unsigned long event_mask;
11925 int gd_xoffset, gd_yoffset;
11926 int gd_x1, gd_x2, gd_y1, gd_y2;
11929 gd_xoffset = gamebutton_info[i].x;
11930 gd_yoffset = gamebutton_info[i].y;
11931 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11932 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11934 if (id == GAME_CTRL_ID_STOP ||
11935 id == GAME_CTRL_ID_PAUSE ||
11936 id == GAME_CTRL_ID_PLAY)
11938 button_type = GD_TYPE_NORMAL_BUTTON;
11940 event_mask = GD_EVENT_RELEASED;
11941 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11942 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11946 button_type = GD_TYPE_CHECK_BUTTON;
11948 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11949 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11950 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11951 event_mask = GD_EVENT_PRESSED;
11952 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11953 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11956 gi = CreateGadget(GDI_CUSTOM_ID, id,
11957 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11958 GDI_X, DX + gd_xoffset,
11959 GDI_Y, DY + gd_yoffset,
11960 GDI_WIDTH, GAME_BUTTON_XSIZE,
11961 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11962 GDI_TYPE, button_type,
11963 GDI_STATE, GD_BUTTON_UNPRESSED,
11964 GDI_CHECKED, checked,
11965 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11966 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11967 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11968 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11969 GDI_EVENT_MASK, event_mask,
11970 GDI_CALLBACK_ACTION, HandleGameButtons,
11974 Error(ERR_EXIT, "cannot create gadget");
11976 game_gadget[id] = gi;
11980 void FreeGameButtons()
11984 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11985 FreeGadget(game_gadget[i]);
11988 static void MapGameButtons()
11992 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11993 MapGadget(game_gadget[i]);
11996 void UnmapGameButtons()
12000 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12001 UnmapGadget(game_gadget[i]);
12004 static void HandleGameButtons(struct GadgetInfo *gi)
12006 int id = gi->custom_id;
12008 if (game_status != GAME_MODE_PLAYING)
12013 case GAME_CTRL_ID_STOP:
12014 RequestQuitGame(TRUE);
12017 case GAME_CTRL_ID_PAUSE:
12018 if (options.network)
12020 #if defined(PLATFORM_UNIX)
12022 SendToServer_ContinuePlaying();
12024 SendToServer_PausePlaying();
12028 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12031 case GAME_CTRL_ID_PLAY:
12034 #if defined(PLATFORM_UNIX)
12035 if (options.network)
12036 SendToServer_ContinuePlaying();
12040 tape.pausing = FALSE;
12041 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12046 case SOUND_CTRL_ID_MUSIC:
12047 if (setup.sound_music)
12049 setup.sound_music = FALSE;
12052 else if (audio.music_available)
12054 setup.sound = setup.sound_music = TRUE;
12056 SetAudioMode(setup.sound);
12062 case SOUND_CTRL_ID_LOOPS:
12063 if (setup.sound_loops)
12064 setup.sound_loops = FALSE;
12065 else if (audio.loops_available)
12067 setup.sound = setup.sound_loops = TRUE;
12068 SetAudioMode(setup.sound);
12072 case SOUND_CTRL_ID_SIMPLE:
12073 if (setup.sound_simple)
12074 setup.sound_simple = FALSE;
12075 else if (audio.sound_available)
12077 setup.sound = setup.sound_simple = TRUE;
12078 SetAudioMode(setup.sound);