1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
35 /* for MovePlayer() */
36 #define MF_NO_ACTION 0
40 /* for ScrollPlayer() */
42 #define SCROLL_GO_ON 1
45 #define EX_PHASE_START 0
46 #define EX_TYPE_NONE 0
47 #define EX_TYPE_NORMAL (1 << 0)
48 #define EX_TYPE_CENTER (1 << 1)
49 #define EX_TYPE_BORDER (1 << 2)
50 #define EX_TYPE_CROSS (1 << 3)
51 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
53 /* special positions in the game control window (relative to control window) */
56 #define XX_EMERALDS 29
57 #define YY_EMERALDS 54
58 #define XX_DYNAMITE 29
59 #define YY_DYNAMITE 89
68 /* special positions in the game control window (relative to main window) */
69 #define DX_LEVEL (DX + XX_LEVEL)
70 #define DY_LEVEL (DY + YY_LEVEL)
71 #define DX_EMERALDS (DX + XX_EMERALDS)
72 #define DY_EMERALDS (DY + YY_EMERALDS)
73 #define DX_DYNAMITE (DX + XX_DYNAMITE)
74 #define DY_DYNAMITE (DY + YY_DYNAMITE)
75 #define DX_KEYS (DX + XX_KEYS)
76 #define DY_KEYS (DY + YY_KEYS)
77 #define DX_SCORE (DX + XX_SCORE)
78 #define DY_SCORE (DY + YY_SCORE)
79 #define DX_TIME1 (DX + XX_TIME1)
80 #define DX_TIME2 (DX + XX_TIME2)
81 #define DY_TIME (DY + YY_TIME)
83 /* values for initial player move delay (initial delay counter value) */
84 #define INITIAL_MOVE_DELAY_OFF -1
85 #define INITIAL_MOVE_DELAY_ON 0
87 /* values for player movement speed (which is in fact a delay value) */
88 #define MOVE_DELAY_NORMAL_SPEED 8
89 #define MOVE_DELAY_HIGH_SPEED 4
91 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
92 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
93 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
94 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
96 /* values for other actions */
97 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
99 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
100 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
102 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
104 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
105 RND(element_info[e].push_delay_random))
106 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
107 RND(element_info[e].drop_delay_random))
108 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
109 RND(element_info[e].move_delay_random))
110 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
111 (element_info[e].move_delay_random))
113 #define GET_TARGET_ELEMENT(e, ch) \
114 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
115 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
117 #define CAN_GROW_INTO(e) \
118 (e == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
120 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
121 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
124 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
125 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
126 (CAN_MOVE_INTO_ACID(e) && \
127 Feld[x][y] == EL_ACID) || \
130 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
131 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
132 (CAN_MOVE_INTO_ACID(e) && \
133 Feld[x][y] == EL_ACID) || \
136 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
137 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
139 (CAN_MOVE_INTO_ACID(e) && \
140 Feld[x][y] == EL_ACID) || \
141 (DONT_COLLIDE_WITH(e) && \
143 !PLAYER_ENEMY_PROTECTED(x, y))))
146 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
147 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
149 (DONT_COLLIDE_WITH(e) && \
151 !PLAYER_ENEMY_PROTECTED(x, y))))
154 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
155 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
158 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
159 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
161 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
162 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
166 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
169 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
170 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
174 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
175 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
177 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
178 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
180 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
181 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
183 #define PIG_CAN_ENTER_FIELD(e, x, y) \
184 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
186 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
187 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
188 IS_FOOD_PENGUIN(Feld[x][y])))
189 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
192 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
193 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
195 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
196 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
200 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
201 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
202 (CAN_MOVE_INTO_ACID(e) && \
203 Feld[x][y] == EL_ACID) || \
204 Feld[x][y] == EL_DIAMOND))
206 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
207 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
208 (CAN_MOVE_INTO_ACID(e) && \
209 Feld[x][y] == EL_ACID) || \
210 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
212 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
213 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
214 (CAN_MOVE_INTO_ACID(e) && \
215 Feld[x][y] == EL_ACID) || \
216 IS_AMOEBOID(Feld[x][y])))
218 #define PIG_CAN_ENTER_FIELD(e, x, y) \
219 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
220 (CAN_MOVE_INTO_ACID(e) && \
221 Feld[x][y] == EL_ACID) || \
222 IS_FOOD_PIG(Feld[x][y])))
224 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
225 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
226 (CAN_MOVE_INTO_ACID(e) && \
227 Feld[x][y] == EL_ACID) || \
228 IS_FOOD_PENGUIN(Feld[x][y]) || \
229 Feld[x][y] == EL_EXIT_OPEN))
231 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
232 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
233 (CAN_MOVE_INTO_ACID(e) && \
234 Feld[x][y] == EL_ACID)))
236 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
237 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
238 (CAN_MOVE_INTO_ACID(e) && \
239 Feld[x][y] == EL_ACID) || \
242 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
243 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
244 (CAN_MOVE_INTO_ACID(e) && \
245 Feld[x][y] == EL_ACID)))
249 #define GROUP_NR(e) ((e) - EL_GROUP_START)
250 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
251 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
252 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
254 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
255 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
258 #define CE_ENTER_FIELD_COND(e, x, y) \
259 (!IS_PLAYER(x, y) && \
260 (Feld[x][y] == EL_ACID || \
261 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
263 #define CE_ENTER_FIELD_COND(e, x, y) \
264 (!IS_PLAYER(x, y) && \
265 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
268 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
269 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
271 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
272 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
274 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
275 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
276 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
277 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
279 /* game button identifiers */
280 #define GAME_CTRL_ID_STOP 0
281 #define GAME_CTRL_ID_PAUSE 1
282 #define GAME_CTRL_ID_PLAY 2
283 #define SOUND_CTRL_ID_MUSIC 3
284 #define SOUND_CTRL_ID_LOOPS 4
285 #define SOUND_CTRL_ID_SIMPLE 5
287 #define NUM_GAME_BUTTONS 6
290 /* forward declaration for internal use */
292 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
293 static boolean MovePlayer(struct PlayerInfo *, int, int);
294 static void ScrollPlayer(struct PlayerInfo *, int);
295 static void ScrollScreen(struct PlayerInfo *, int);
297 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
299 static void InitBeltMovement(void);
300 static void CloseAllOpenTimegates(void);
301 static void CheckGravityMovement(struct PlayerInfo *);
302 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
303 static void KillHeroUnlessEnemyProtected(int, int);
304 static void KillHeroUnlessExplosionProtected(int, int);
306 static void TestIfPlayerTouchesCustomElement(int, int);
307 static void TestIfElementTouchesCustomElement(int, int);
308 static void TestIfElementHitsCustomElement(int, int, int);
310 static void TestIfElementSmashesCustomElement(int, int, int);
313 static void ChangeElement(int, int, int);
315 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
316 #define CheckTriggeredElementChange(x, y, e, ev) \
317 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
319 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
320 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
321 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
322 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
323 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
324 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
327 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
328 #define CheckElementChange(x, y, e, te, ev) \
329 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
330 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
331 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
332 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
333 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
334 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
335 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
337 static void PlayLevelSound(int, int, int);
338 static void PlayLevelSoundNearest(int, int, int);
339 static void PlayLevelSoundAction(int, int, int);
340 static void PlayLevelSoundElementAction(int, int, int, int);
341 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
342 static void PlayLevelSoundActionIfLoop(int, int, int);
343 static void StopLevelSoundActionIfLoop(int, int, int);
344 static void PlayLevelMusic();
346 static void MapGameButtons();
347 static void HandleGameButtons(struct GadgetInfo *);
349 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
352 /* ------------------------------------------------------------------------- */
353 /* definition of elements that automatically change to other elements after */
354 /* a specified time, eventually calling a function when changing */
355 /* ------------------------------------------------------------------------- */
357 /* forward declaration for changer functions */
358 static void InitBuggyBase(int x, int y);
359 static void WarnBuggyBase(int x, int y);
361 static void InitTrap(int x, int y);
362 static void ActivateTrap(int x, int y);
363 static void ChangeActiveTrap(int x, int y);
365 static void InitRobotWheel(int x, int y);
366 static void RunRobotWheel(int x, int y);
367 static void StopRobotWheel(int x, int y);
369 static void InitTimegateWheel(int x, int y);
370 static void RunTimegateWheel(int x, int y);
372 struct ChangingElementInfo
377 void (*pre_change_function)(int x, int y);
378 void (*change_function)(int x, int y);
379 void (*post_change_function)(int x, int y);
382 static struct ChangingElementInfo change_delay_list[] =
433 EL_SWITCHGATE_OPENING,
441 EL_SWITCHGATE_CLOSING,
442 EL_SWITCHGATE_CLOSED,
474 EL_ACID_SPLASH_RIGHT,
483 EL_SP_BUGGY_BASE_ACTIVATING,
490 EL_SP_BUGGY_BASE_ACTIVATING,
491 EL_SP_BUGGY_BASE_ACTIVE,
498 EL_SP_BUGGY_BASE_ACTIVE,
522 EL_ROBOT_WHEEL_ACTIVE,
530 EL_TIMEGATE_SWITCH_ACTIVE,
551 int push_delay_fixed, push_delay_random;
556 { EL_BALLOON, 0, 0 },
558 { EL_SOKOBAN_OBJECT, 2, 0 },
559 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
560 { EL_SATELLITE, 2, 0 },
561 { EL_SP_DISK_YELLOW, 2, 0 },
563 { EL_UNDEFINED, 0, 0 },
571 move_stepsize_list[] =
573 { EL_AMOEBA_DROP, 2 },
574 { EL_AMOEBA_DROPPING, 2 },
575 { EL_QUICKSAND_FILLING, 1 },
576 { EL_QUICKSAND_EMPTYING, 1 },
577 { EL_MAGIC_WALL_FILLING, 2 },
578 { EL_BD_MAGIC_WALL_FILLING, 2 },
579 { EL_MAGIC_WALL_EMPTYING, 2 },
580 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
590 collect_count_list[] =
593 { EL_BD_DIAMOND, 1 },
594 { EL_EMERALD_YELLOW, 1 },
595 { EL_EMERALD_RED, 1 },
596 { EL_EMERALD_PURPLE, 1 },
598 { EL_SP_INFOTRON, 1 },
610 access_direction_list[] =
612 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
613 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
614 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
615 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
616 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
617 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
618 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
619 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
620 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
621 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
622 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
624 { EL_SP_PORT_LEFT, MV_RIGHT },
625 { EL_SP_PORT_RIGHT, MV_LEFT },
626 { EL_SP_PORT_UP, MV_DOWN },
627 { EL_SP_PORT_DOWN, MV_UP },
628 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
629 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
630 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
631 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
632 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
633 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
634 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
636 { EL_UNDEFINED, MV_NO_MOVING }
639 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
641 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
642 CH_EVENT_BIT(CE_DELAY))
643 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
644 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
645 IS_JUST_CHANGING(x, y))
647 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
650 void GetPlayerConfig()
652 if (!audio.sound_available)
653 setup.sound_simple = FALSE;
655 if (!audio.loops_available)
656 setup.sound_loops = FALSE;
658 if (!audio.music_available)
659 setup.sound_music = FALSE;
661 if (!video.fullscreen_available)
662 setup.fullscreen = FALSE;
664 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
666 SetAudioMode(setup.sound);
670 static int getBeltNrFromBeltElement(int element)
672 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
673 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
674 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
677 static int getBeltNrFromBeltActiveElement(int element)
679 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
680 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
681 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
684 static int getBeltNrFromBeltSwitchElement(int element)
686 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
687 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
688 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
691 static int getBeltDirNrFromBeltSwitchElement(int element)
693 static int belt_base_element[4] =
695 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
696 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
697 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
698 EL_CONVEYOR_BELT_4_SWITCH_LEFT
701 int belt_nr = getBeltNrFromBeltSwitchElement(element);
702 int belt_dir_nr = element - belt_base_element[belt_nr];
704 return (belt_dir_nr % 3);
707 static int getBeltDirFromBeltSwitchElement(int element)
709 static int belt_move_dir[3] =
716 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
718 return belt_move_dir[belt_dir_nr];
721 static void InitPlayerField(int x, int y, int element, boolean init_game)
723 if (element == EL_SP_MURPHY)
727 if (stored_player[0].present)
729 Feld[x][y] = EL_SP_MURPHY_CLONE;
735 stored_player[0].use_murphy_graphic = TRUE;
738 Feld[x][y] = EL_PLAYER_1;
744 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
745 int jx = player->jx, jy = player->jy;
747 player->present = TRUE;
749 player->block_last_field = (element == EL_SP_MURPHY ?
750 level.sp_block_last_field :
751 level.block_last_field);
753 if (!options.network || player->connected)
755 player->active = TRUE;
757 /* remove potentially duplicate players */
758 if (StorePlayer[jx][jy] == Feld[x][y])
759 StorePlayer[jx][jy] = 0;
761 StorePlayer[x][y] = Feld[x][y];
765 printf("Player %d activated.\n", player->element_nr);
766 printf("[Local player is %d and currently %s.]\n",
767 local_player->element_nr,
768 local_player->active ? "active" : "not active");
772 Feld[x][y] = EL_EMPTY;
774 player->jx = player->last_jx = x;
775 player->jy = player->last_jy = y;
779 static void InitField(int x, int y, boolean init_game)
781 int element = Feld[x][y];
790 InitPlayerField(x, y, element, init_game);
793 case EL_SOKOBAN_FIELD_PLAYER:
794 element = Feld[x][y] = EL_PLAYER_1;
795 InitField(x, y, init_game);
797 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
798 InitField(x, y, init_game);
801 case EL_SOKOBAN_FIELD_EMPTY:
802 local_player->sokobanfields_still_needed++;
806 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
807 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
808 else if (x > 0 && Feld[x-1][y] == EL_ACID)
809 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
810 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
811 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
812 else if (y > 0 && Feld[x][y-1] == EL_ACID)
813 Feld[x][y] = EL_ACID_POOL_BOTTOM;
814 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
815 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
823 case EL_SPACESHIP_RIGHT:
824 case EL_SPACESHIP_UP:
825 case EL_SPACESHIP_LEFT:
826 case EL_SPACESHIP_DOWN:
828 case EL_BD_BUTTERFLY_RIGHT:
829 case EL_BD_BUTTERFLY_UP:
830 case EL_BD_BUTTERFLY_LEFT:
831 case EL_BD_BUTTERFLY_DOWN:
832 case EL_BD_BUTTERFLY:
833 case EL_BD_FIREFLY_RIGHT:
834 case EL_BD_FIREFLY_UP:
835 case EL_BD_FIREFLY_LEFT:
836 case EL_BD_FIREFLY_DOWN:
838 case EL_PACMAN_RIGHT:
862 if (y == lev_fieldy - 1)
864 Feld[x][y] = EL_AMOEBA_GROWING;
865 Store[x][y] = EL_AMOEBA_WET;
869 case EL_DYNAMITE_ACTIVE:
870 case EL_SP_DISK_RED_ACTIVE:
871 case EL_DYNABOMB_PLAYER_1_ACTIVE:
872 case EL_DYNABOMB_PLAYER_2_ACTIVE:
873 case EL_DYNABOMB_PLAYER_3_ACTIVE:
874 case EL_DYNABOMB_PLAYER_4_ACTIVE:
879 local_player->lights_still_needed++;
883 local_player->friends_still_needed++;
888 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
893 Feld[x][y] = EL_EMPTY;
898 case EL_EM_KEY_1_FILE:
899 Feld[x][y] = EL_EM_KEY_1;
901 case EL_EM_KEY_2_FILE:
902 Feld[x][y] = EL_EM_KEY_2;
904 case EL_EM_KEY_3_FILE:
905 Feld[x][y] = EL_EM_KEY_3;
907 case EL_EM_KEY_4_FILE:
908 Feld[x][y] = EL_EM_KEY_4;
912 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
913 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
914 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
915 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
916 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
917 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
918 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
919 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
920 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
921 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
922 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
923 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
926 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
927 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
928 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
930 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
932 game.belt_dir[belt_nr] = belt_dir;
933 game.belt_dir_nr[belt_nr] = belt_dir_nr;
935 else /* more than one switch -- set it like the first switch */
937 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
942 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
944 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
947 case EL_LIGHT_SWITCH_ACTIVE:
949 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
953 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
955 else if (IS_GROUP_ELEMENT(element))
957 struct ElementGroupInfo *group = element_info[element].group;
958 int last_anim_random_frame = gfx.anim_random_frame;
961 if (group->choice_mode == ANIM_RANDOM)
962 gfx.anim_random_frame = RND(group->num_elements_resolved);
964 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
965 group->choice_mode, 0,
968 if (group->choice_mode == ANIM_RANDOM)
969 gfx.anim_random_frame = last_anim_random_frame;
973 Feld[x][y] = group->element_resolved[element_pos];
975 InitField(x, y, init_game);
981 static inline void InitField_WithBug1(int x, int y, boolean init_game)
983 InitField(x, y, init_game);
985 /* not needed to call InitMovDir() -- already done by InitField()! */
986 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
987 CAN_MOVE(Feld[x][y]))
991 static inline void InitField_WithBug2(int x, int y, boolean init_game)
993 int old_element = Feld[x][y];
995 InitField(x, y, init_game);
997 /* not needed to call InitMovDir() -- already done by InitField()! */
998 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
999 CAN_MOVE(old_element) &&
1000 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1003 /* this case is in fact a combination of not less than three bugs:
1004 first, it calls InitMovDir() for elements that can move, although this is
1005 already done by InitField(); then, it checks the element that was at this
1006 field _before_ the call to InitField() (which can change it); lastly, it
1007 was not called for "mole with direction" elements, which were treated as
1008 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1012 inline void DrawGameValue_Emeralds(int value)
1014 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1017 inline void DrawGameValue_Dynamite(int value)
1019 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1022 inline void DrawGameValue_Keys(struct PlayerInfo *player)
1026 for (i = 0; i < MAX_KEYS; i++)
1028 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1029 el2edimg(EL_KEY_1 + i));
1032 inline void DrawGameValue_Score(int value)
1034 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1037 inline void DrawGameValue_Time(int value)
1040 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1042 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1045 inline void DrawGameValue_Level(int value)
1048 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1051 /* misuse area for displaying emeralds to draw bigger level number */
1052 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1053 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1055 /* now copy it to the area for displaying level number */
1056 BlitBitmap(drawto, drawto,
1057 DX_EMERALDS, DY_EMERALDS + 1,
1058 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1059 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1060 DX_LEVEL - 1, DY_LEVEL + 1);
1062 /* restore the area for displaying emeralds */
1063 DrawGameValue_Emeralds(local_player->gems_still_needed);
1065 /* yes, this is all really ugly :-) */
1069 void DrawGameDoorValues()
1073 DrawGameValue_Level(level_nr);
1075 for (i = 0; i < MAX_PLAYERS; i++)
1076 DrawGameValue_Keys(&stored_player[i]);
1078 DrawGameValue_Emeralds(local_player->gems_still_needed);
1079 DrawGameValue_Dynamite(local_player->inventory_size);
1080 DrawGameValue_Score(local_player->score);
1081 DrawGameValue_Time(TimeLeft);
1084 static void resolve_group_element(int group_element, int recursion_depth)
1086 static int group_nr;
1087 static struct ElementGroupInfo *group;
1088 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1091 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1093 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1094 group_element - EL_GROUP_START + 1);
1096 /* replace element which caused too deep recursion by question mark */
1097 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1102 if (recursion_depth == 0) /* initialization */
1104 group = element_info[group_element].group;
1105 group_nr = group_element - EL_GROUP_START;
1107 group->num_elements_resolved = 0;
1108 group->choice_pos = 0;
1111 for (i = 0; i < actual_group->num_elements; i++)
1113 int element = actual_group->element[i];
1115 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1118 if (IS_GROUP_ELEMENT(element))
1119 resolve_group_element(element, recursion_depth + 1);
1122 group->element_resolved[group->num_elements_resolved++] = element;
1123 element_info[element].in_group[group_nr] = TRUE;
1128 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1130 printf("::: group %d: %d resolved elements\n",
1131 group_element - EL_GROUP_START, group->num_elements_resolved);
1132 for (i = 0; i < group->num_elements_resolved; i++)
1133 printf("::: - %d ['%s']\n", group->element_resolved[i],
1134 element_info[group->element_resolved[i]].token_name);
1141 =============================================================================
1143 -----------------------------------------------------------------------------
1144 initialize game engine due to level / tape version number
1145 =============================================================================
1148 static void InitGameEngine()
1152 /* set game engine from tape file when re-playing, else from level file */
1153 game.engine_version = (tape.playing ? tape.engine_version :
1154 level.game_version);
1156 /* dynamically adjust element properties according to game engine version */
1157 InitElementPropertiesEngine(game.engine_version);
1160 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1161 printf(" tape version == %06d [%s] [file: %06d]\n",
1162 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1164 printf(" => game.engine_version == %06d\n", game.engine_version);
1167 /* ---------- recursively resolve group elements ------------------------- */
1169 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1170 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1171 element_info[i].in_group[j] = FALSE;
1173 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1174 resolve_group_element(EL_GROUP_START + i, 0);
1176 /* ---------- initialize player's initial move delay --------------------- */
1178 /* dynamically adjust player properties according to game engine version */
1179 game.initial_move_delay =
1180 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1181 INITIAL_MOVE_DELAY_OFF);
1183 /* dynamically adjust player properties according to level information */
1184 game.initial_move_delay_value =
1185 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1187 /* ---------- initialize player's initial push delay --------------------- */
1189 /* dynamically adjust player properties according to game engine version */
1190 game.initial_push_delay_value =
1191 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1193 /* ---------- initialize changing elements ------------------------------- */
1195 /* initialize changing elements information */
1196 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1198 struct ElementInfo *ei = &element_info[i];
1200 /* this pointer might have been changed in the level editor */
1201 ei->change = &ei->change_page[0];
1203 if (!IS_CUSTOM_ELEMENT(i))
1205 ei->change->target_element = EL_EMPTY_SPACE;
1206 ei->change->delay_fixed = 0;
1207 ei->change->delay_random = 0;
1208 ei->change->delay_frames = 1;
1211 ei->change_events = CE_BITMASK_DEFAULT;
1212 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1214 ei->event_page_nr[j] = 0;
1215 ei->event_page[j] = &ei->change_page[0];
1219 /* add changing elements from pre-defined list */
1220 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1222 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1223 struct ElementInfo *ei = &element_info[ch_delay->element];
1225 ei->change->target_element = ch_delay->target_element;
1226 ei->change->delay_fixed = ch_delay->change_delay;
1228 ei->change->pre_change_function = ch_delay->pre_change_function;
1229 ei->change->change_function = ch_delay->change_function;
1230 ei->change->post_change_function = ch_delay->post_change_function;
1232 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1235 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1240 /* add change events from custom element configuration */
1241 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1243 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1245 for (j = 0; j < ei->num_change_pages; j++)
1247 if (!ei->change_page[j].can_change)
1250 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1252 /* only add event page for the first page found with this event */
1253 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1254 !(ei->change_events & CH_EVENT_BIT(k)))
1256 ei->change_events |= CH_EVENT_BIT(k);
1257 ei->event_page_nr[k] = j;
1258 ei->event_page[k] = &ei->change_page[j];
1266 /* add change events from custom element configuration */
1267 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1269 int element = EL_CUSTOM_START + i;
1271 /* only add custom elements that change after fixed/random frame delay */
1272 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1273 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1277 /* ---------- initialize run-time trigger player and element ------------- */
1279 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1281 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1283 for (j = 0; j < ei->num_change_pages; j++)
1285 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1286 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1290 /* ---------- initialize trigger events ---------------------------------- */
1292 /* initialize trigger events information */
1293 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1294 trigger_events[i] = EP_BITMASK_DEFAULT;
1297 /* add trigger events from element change event properties */
1298 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1300 struct ElementInfo *ei = &element_info[i];
1302 for (j = 0; j < ei->num_change_pages; j++)
1304 if (!ei->change_page[j].can_change)
1307 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1309 int trigger_element = ei->change_page[j].trigger_element;
1311 if (IS_GROUP_ELEMENT(trigger_element))
1313 struct ElementGroupInfo *group = element_info[trigger_element].group;
1315 for (k = 0; k < group->num_elements_resolved; k++)
1316 trigger_events[group->element_resolved[k]]
1317 |= ei->change_page[j].events;
1320 trigger_events[trigger_element] |= ei->change_page[j].events;
1325 /* add trigger events from element change event properties */
1326 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1327 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1328 trigger_events[element_info[i].change->trigger_element] |=
1329 element_info[i].change->events;
1332 /* ---------- initialize push delay -------------------------------------- */
1334 /* initialize push delay values to default */
1335 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1337 if (!IS_CUSTOM_ELEMENT(i))
1339 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1340 element_info[i].push_delay_random = game.default_push_delay_random;
1344 /* set push delay value for certain elements from pre-defined list */
1345 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1347 int e = push_delay_list[i].element;
1349 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1350 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1353 /* set push delay value for Supaplex elements for newer engine versions */
1354 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1356 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1358 if (IS_SP_ELEMENT(i))
1360 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1361 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1366 /* ---------- initialize move stepsize ----------------------------------- */
1368 /* initialize move stepsize values to default */
1369 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1370 if (!IS_CUSTOM_ELEMENT(i))
1371 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1373 /* set move stepsize value for certain elements from pre-defined list */
1374 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1376 int e = move_stepsize_list[i].element;
1378 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1382 /* ---------- initialize move dig/leave ---------------------------------- */
1384 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1386 element_info[i].can_leave_element = FALSE;
1387 element_info[i].can_leave_element_last = FALSE;
1391 /* ---------- initialize gem count --------------------------------------- */
1393 /* initialize gem count values for each element */
1394 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1395 if (!IS_CUSTOM_ELEMENT(i))
1396 element_info[i].collect_count = 0;
1398 /* add gem count values for all elements from pre-defined list */
1399 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1400 element_info[collect_count_list[i].element].collect_count =
1401 collect_count_list[i].count;
1403 /* ---------- initialize access direction -------------------------------- */
1405 /* initialize access direction values to default (access from every side) */
1406 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1407 if (!IS_CUSTOM_ELEMENT(i))
1408 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1410 /* set access direction value for certain elements from pre-defined list */
1411 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1412 element_info[access_direction_list[i].element].access_direction =
1413 access_direction_list[i].direction;
1418 =============================================================================
1420 -----------------------------------------------------------------------------
1421 initialize and start new game
1422 =============================================================================
1427 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1428 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1429 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1436 #if USE_NEW_AMOEBA_CODE
1437 printf("Using new amoeba code.\n");
1439 printf("Using old amoeba code.\n");
1444 /* don't play tapes over network */
1445 network_playing = (options.network && !tape.playing);
1447 for (i = 0; i < MAX_PLAYERS; i++)
1449 struct PlayerInfo *player = &stored_player[i];
1451 player->index_nr = i;
1452 player->index_bit = (1 << i);
1453 player->element_nr = EL_PLAYER_1 + i;
1455 player->present = FALSE;
1456 player->active = FALSE;
1459 player->effective_action = 0;
1460 player->programmed_action = 0;
1463 player->gems_still_needed = level.gems_needed;
1464 player->sokobanfields_still_needed = 0;
1465 player->lights_still_needed = 0;
1466 player->friends_still_needed = 0;
1468 for (j = 0; j < MAX_KEYS; j++)
1469 player->key[j] = FALSE;
1471 player->dynabomb_count = 0;
1472 player->dynabomb_size = 1;
1473 player->dynabombs_left = 0;
1474 player->dynabomb_xl = FALSE;
1476 player->MovDir = MV_NO_MOVING;
1479 player->GfxDir = MV_NO_MOVING;
1480 player->GfxAction = ACTION_DEFAULT;
1482 player->StepFrame = 0;
1484 player->use_murphy_graphic = FALSE;
1486 player->block_last_field = FALSE;
1487 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1489 player->actual_frame_counter = 0;
1491 player->step_counter = 0;
1493 player->last_move_dir = MV_NO_MOVING;
1495 player->is_waiting = FALSE;
1496 player->is_moving = FALSE;
1497 player->is_auto_moving = FALSE;
1498 player->is_digging = FALSE;
1499 player->is_snapping = FALSE;
1500 player->is_collecting = FALSE;
1501 player->is_pushing = FALSE;
1502 player->is_switching = FALSE;
1503 player->is_dropping = FALSE;
1505 player->is_bored = FALSE;
1506 player->is_sleeping = FALSE;
1508 player->frame_counter_bored = -1;
1509 player->frame_counter_sleeping = -1;
1511 player->anim_delay_counter = 0;
1512 player->post_delay_counter = 0;
1514 player->action_waiting = ACTION_DEFAULT;
1515 player->last_action_waiting = ACTION_DEFAULT;
1516 player->special_action_bored = ACTION_DEFAULT;
1517 player->special_action_sleeping = ACTION_DEFAULT;
1519 player->num_special_action_bored = 0;
1520 player->num_special_action_sleeping = 0;
1522 /* determine number of special actions for bored and sleeping animation */
1523 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1525 boolean found = FALSE;
1527 for (k = 0; k < NUM_DIRECTIONS; k++)
1528 if (el_act_dir2img(player->element_nr, j, k) !=
1529 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1533 player->num_special_action_bored++;
1537 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1539 boolean found = FALSE;
1541 for (k = 0; k < NUM_DIRECTIONS; k++)
1542 if (el_act_dir2img(player->element_nr, j, k) !=
1543 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1547 player->num_special_action_sleeping++;
1552 player->switch_x = -1;
1553 player->switch_y = -1;
1555 player->show_envelope = 0;
1557 player->move_delay = game.initial_move_delay;
1558 player->move_delay_value = game.initial_move_delay_value;
1560 player->move_delay_reset_counter = 0;
1562 player->push_delay = 0;
1563 player->push_delay_value = game.initial_push_delay_value;
1565 player->drop_delay = 0;
1567 player->last_jx = player->last_jy = 0;
1568 player->jx = player->jy = 0;
1570 player->shield_normal_time_left = 0;
1571 player->shield_deadly_time_left = 0;
1573 player->inventory_infinite_element = EL_UNDEFINED;
1574 player->inventory_size = 0;
1576 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1577 SnapField(player, 0, 0);
1579 player->LevelSolved = FALSE;
1580 player->GameOver = FALSE;
1583 network_player_action_received = FALSE;
1585 #if defined(PLATFORM_UNIX)
1586 /* initial null action */
1587 if (network_playing)
1588 SendToServer_MovePlayer(MV_NO_MOVING);
1596 TimeLeft = level.time;
1599 ScreenMovDir = MV_NO_MOVING;
1603 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1605 AllPlayersGone = FALSE;
1607 game.yamyam_content_nr = 0;
1608 game.magic_wall_active = FALSE;
1609 game.magic_wall_time_left = 0;
1610 game.light_time_left = 0;
1611 game.timegate_time_left = 0;
1612 game.switchgate_pos = 0;
1613 game.balloon_dir = MV_NO_MOVING;
1614 game.gravity = level.initial_gravity;
1615 game.explosions_delayed = TRUE;
1617 game.envelope_active = FALSE;
1619 for (i = 0; i < NUM_BELTS; i++)
1621 game.belt_dir[i] = MV_NO_MOVING;
1622 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1625 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1626 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1628 for (x = 0; x < lev_fieldx; x++)
1630 for (y = 0; y < lev_fieldy; y++)
1632 Feld[x][y] = level.field[x][y];
1633 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1634 ChangeDelay[x][y] = 0;
1635 ChangePage[x][y] = -1;
1636 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1638 WasJustMoving[x][y] = 0;
1639 WasJustFalling[x][y] = 0;
1640 CheckCollision[x][y] = 0;
1642 Pushed[x][y] = FALSE;
1644 Changed[x][y] = CE_BITMASK_DEFAULT;
1645 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1647 ExplodePhase[x][y] = 0;
1648 ExplodeDelay[x][y] = 0;
1649 ExplodeField[x][y] = EX_TYPE_NONE;
1651 RunnerVisit[x][y] = 0;
1652 PlayerVisit[x][y] = 0;
1655 GfxRandom[x][y] = INIT_GFX_RANDOM();
1656 GfxElement[x][y] = EL_UNDEFINED;
1657 GfxAction[x][y] = ACTION_DEFAULT;
1658 GfxDir[x][y] = MV_NO_MOVING;
1662 for (y = 0; y < lev_fieldy; y++)
1664 for (x = 0; x < lev_fieldx; x++)
1666 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1668 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1670 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1673 InitField(x, y, TRUE);
1679 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1680 emulate_sb ? EMU_SOKOBAN :
1681 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1683 /* initialize explosion and ignition delay */
1684 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1686 if (!IS_CUSTOM_ELEMENT(i))
1689 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1690 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1691 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1692 int last_phase = (num_phase + 1) * delay;
1693 int half_phase = (num_phase / 2) * delay;
1695 element_info[i].explosion_delay = last_phase - 1;
1696 element_info[i].ignition_delay = half_phase;
1699 if (i == EL_BLACK_ORB)
1700 element_info[i].ignition_delay = 0;
1702 if (i == EL_BLACK_ORB)
1703 element_info[i].ignition_delay = 1;
1708 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1709 element_info[i].explosion_delay = 1;
1711 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1712 element_info[i].ignition_delay = 1;
1716 /* correct non-moving belts to start moving left */
1717 for (i = 0; i < NUM_BELTS; i++)
1718 if (game.belt_dir[i] == MV_NO_MOVING)
1719 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1721 /* check if any connected player was not found in playfield */
1722 for (i = 0; i < MAX_PLAYERS; i++)
1724 struct PlayerInfo *player = &stored_player[i];
1726 if (player->connected && !player->present)
1728 for (j = 0; j < MAX_PLAYERS; j++)
1730 struct PlayerInfo *some_player = &stored_player[j];
1731 int jx = some_player->jx, jy = some_player->jy;
1733 /* assign first free player found that is present in the playfield */
1734 if (some_player->present && !some_player->connected)
1736 player->present = TRUE;
1737 player->active = TRUE;
1739 some_player->present = FALSE;
1740 some_player->active = FALSE;
1743 player->element_nr = some_player->element_nr;
1746 StorePlayer[jx][jy] = player->element_nr;
1747 player->jx = player->last_jx = jx;
1748 player->jy = player->last_jy = jy;
1758 /* when playing a tape, eliminate all players which do not participate */
1760 for (i = 0; i < MAX_PLAYERS; i++)
1762 if (stored_player[i].active && !tape.player_participates[i])
1764 struct PlayerInfo *player = &stored_player[i];
1765 int jx = player->jx, jy = player->jy;
1767 player->active = FALSE;
1768 StorePlayer[jx][jy] = 0;
1769 Feld[jx][jy] = EL_EMPTY;
1773 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1775 /* when in single player mode, eliminate all but the first active player */
1777 for (i = 0; i < MAX_PLAYERS; i++)
1779 if (stored_player[i].active)
1781 for (j = i + 1; j < MAX_PLAYERS; j++)
1783 if (stored_player[j].active)
1785 struct PlayerInfo *player = &stored_player[j];
1786 int jx = player->jx, jy = player->jy;
1788 player->active = FALSE;
1789 player->present = FALSE;
1791 StorePlayer[jx][jy] = 0;
1792 Feld[jx][jy] = EL_EMPTY;
1799 /* when recording the game, store which players take part in the game */
1802 for (i = 0; i < MAX_PLAYERS; i++)
1803 if (stored_player[i].active)
1804 tape.player_participates[i] = TRUE;
1809 for (i = 0; i < MAX_PLAYERS; i++)
1811 struct PlayerInfo *player = &stored_player[i];
1813 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1818 if (local_player == player)
1819 printf("Player %d is local player.\n", i+1);
1823 if (BorderElement == EL_EMPTY)
1826 SBX_Right = lev_fieldx - SCR_FIELDX;
1828 SBY_Lower = lev_fieldy - SCR_FIELDY;
1833 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1835 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1838 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1839 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1841 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1842 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1844 /* if local player not found, look for custom element that might create
1845 the player (make some assumptions about the right custom element) */
1846 if (!local_player->present)
1848 int start_x = 0, start_y = 0;
1849 int found_rating = 0;
1850 int found_element = EL_UNDEFINED;
1852 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1854 int element = Feld[x][y];
1859 if (!IS_CUSTOM_ELEMENT(element))
1862 if (CAN_CHANGE(element))
1864 for (i = 0; i < element_info[element].num_change_pages; i++)
1866 content = element_info[element].change_page[i].target_element;
1867 is_player = ELEM_IS_PLAYER(content);
1869 if (is_player && (found_rating < 3 || element < found_element))
1875 found_element = element;
1880 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1882 content = element_info[element].content[xx][yy];
1883 is_player = ELEM_IS_PLAYER(content);
1885 if (is_player && (found_rating < 2 || element < found_element))
1887 start_x = x + xx - 1;
1888 start_y = y + yy - 1;
1891 found_element = element;
1894 if (!CAN_CHANGE(element))
1897 for (i = 0; i < element_info[element].num_change_pages; i++)
1899 content= element_info[element].change_page[i].target_content[xx][yy];
1900 is_player = ELEM_IS_PLAYER(content);
1902 if (is_player && (found_rating < 1 || element < found_element))
1904 start_x = x + xx - 1;
1905 start_y = y + yy - 1;
1908 found_element = element;
1914 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1915 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1918 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1919 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1925 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1926 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1927 local_player->jx - MIDPOSX);
1929 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1930 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1931 local_player->jy - MIDPOSY);
1933 scroll_x = SBX_Left;
1934 scroll_y = SBY_Upper;
1935 if (local_player->jx >= SBX_Left + MIDPOSX)
1936 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1937 local_player->jx - MIDPOSX :
1939 if (local_player->jy >= SBY_Upper + MIDPOSY)
1940 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1941 local_player->jy - MIDPOSY :
1946 CloseDoor(DOOR_CLOSE_1);
1951 /* after drawing the level, correct some elements */
1952 if (game.timegate_time_left == 0)
1953 CloseAllOpenTimegates();
1955 if (setup.soft_scrolling)
1956 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1958 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1961 /* copy default game door content to main double buffer */
1962 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1963 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1965 DrawGameDoorValues();
1969 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1970 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1971 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1975 /* copy actual game door content to door double buffer for OpenDoor() */
1976 BlitBitmap(drawto, bitmap_db_door,
1977 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1979 OpenDoor(DOOR_OPEN_ALL);
1981 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1983 if (setup.sound_music)
1986 KeyboardAutoRepeatOffUnlessAutoplay();
1990 for (i = 0; i < MAX_PLAYERS; i++)
1991 printf("Player %d %sactive.\n",
1992 i + 1, (stored_player[i].active ? "" : "not "));
1996 printf("::: starting game [%d]\n", FrameCounter);
2000 void InitMovDir(int x, int y)
2002 int i, element = Feld[x][y];
2003 static int xy[4][2] =
2010 static int direction[3][4] =
2012 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2013 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2014 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2023 Feld[x][y] = EL_BUG;
2024 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2027 case EL_SPACESHIP_RIGHT:
2028 case EL_SPACESHIP_UP:
2029 case EL_SPACESHIP_LEFT:
2030 case EL_SPACESHIP_DOWN:
2031 Feld[x][y] = EL_SPACESHIP;
2032 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2035 case EL_BD_BUTTERFLY_RIGHT:
2036 case EL_BD_BUTTERFLY_UP:
2037 case EL_BD_BUTTERFLY_LEFT:
2038 case EL_BD_BUTTERFLY_DOWN:
2039 Feld[x][y] = EL_BD_BUTTERFLY;
2040 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2043 case EL_BD_FIREFLY_RIGHT:
2044 case EL_BD_FIREFLY_UP:
2045 case EL_BD_FIREFLY_LEFT:
2046 case EL_BD_FIREFLY_DOWN:
2047 Feld[x][y] = EL_BD_FIREFLY;
2048 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2051 case EL_PACMAN_RIGHT:
2053 case EL_PACMAN_LEFT:
2054 case EL_PACMAN_DOWN:
2055 Feld[x][y] = EL_PACMAN;
2056 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2059 case EL_SP_SNIKSNAK:
2060 MovDir[x][y] = MV_UP;
2063 case EL_SP_ELECTRON:
2064 MovDir[x][y] = MV_LEFT;
2071 Feld[x][y] = EL_MOLE;
2072 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2076 if (IS_CUSTOM_ELEMENT(element))
2078 struct ElementInfo *ei = &element_info[element];
2079 int move_direction_initial = ei->move_direction_initial;
2080 int move_pattern = ei->move_pattern;
2082 if (move_direction_initial == MV_START_PREVIOUS)
2084 if (MovDir[x][y] != MV_NO_MOVING)
2087 move_direction_initial = MV_START_AUTOMATIC;
2090 if (move_direction_initial == MV_START_RANDOM)
2091 MovDir[x][y] = 1 << RND(4);
2092 else if (move_direction_initial & MV_ANY_DIRECTION)
2093 MovDir[x][y] = move_direction_initial;
2094 else if (move_pattern == MV_ALL_DIRECTIONS ||
2095 move_pattern == MV_TURNING_LEFT ||
2096 move_pattern == MV_TURNING_RIGHT ||
2097 move_pattern == MV_TURNING_LEFT_RIGHT ||
2098 move_pattern == MV_TURNING_RIGHT_LEFT ||
2099 move_pattern == MV_TURNING_RANDOM)
2100 MovDir[x][y] = 1 << RND(4);
2101 else if (move_pattern == MV_HORIZONTAL)
2102 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2103 else if (move_pattern == MV_VERTICAL)
2104 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2105 else if (move_pattern & MV_ANY_DIRECTION)
2106 MovDir[x][y] = element_info[element].move_pattern;
2107 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2108 move_pattern == MV_ALONG_RIGHT_SIDE)
2111 /* use random direction as default start direction */
2112 if (game.engine_version >= VERSION_IDENT(3,1,0,2))
2113 MovDir[x][y] = 1 << RND(4);
2116 for (i = 0; i < NUM_DIRECTIONS; i++)
2118 int x1 = x + xy[i][0];
2119 int y1 = y + xy[i][1];
2121 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2123 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2124 MovDir[x][y] = direction[0][i];
2126 MovDir[x][y] = direction[1][i];
2135 MovDir[x][y] = 1 << RND(4);
2137 if (element != EL_BUG &&
2138 element != EL_SPACESHIP &&
2139 element != EL_BD_BUTTERFLY &&
2140 element != EL_BD_FIREFLY)
2143 for (i = 0; i < NUM_DIRECTIONS; i++)
2145 int x1 = x + xy[i][0];
2146 int y1 = y + xy[i][1];
2148 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2150 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2152 MovDir[x][y] = direction[0][i];
2155 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2156 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2158 MovDir[x][y] = direction[1][i];
2167 GfxDir[x][y] = MovDir[x][y];
2170 void InitAmoebaNr(int x, int y)
2173 int group_nr = AmoebeNachbarNr(x, y);
2177 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2179 if (AmoebaCnt[i] == 0)
2187 AmoebaNr[x][y] = group_nr;
2188 AmoebaCnt[group_nr]++;
2189 AmoebaCnt2[group_nr]++;
2195 boolean raise_level = FALSE;
2197 if (local_player->MovPos)
2201 if (tape.auto_play) /* tape might already be stopped here */
2202 tape.auto_play_level_solved = TRUE;
2204 if (tape.playing && tape.auto_play)
2205 tape.auto_play_level_solved = TRUE;
2208 local_player->LevelSolved = FALSE;
2210 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2214 if (!tape.playing && setup.sound_loops)
2215 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2216 SND_CTRL_PLAY_LOOP);
2218 while (TimeLeft > 0)
2220 if (!tape.playing && !setup.sound_loops)
2221 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2222 if (TimeLeft > 0 && !(TimeLeft % 10))
2223 RaiseScore(level.score[SC_TIME_BONUS]);
2224 if (TimeLeft > 100 && !(TimeLeft % 10))
2229 DrawGameValue_Time(TimeLeft);
2237 if (!tape.playing && setup.sound_loops)
2238 StopSound(SND_GAME_LEVELTIME_BONUS);
2240 else if (level.time == 0) /* level without time limit */
2242 if (!tape.playing && setup.sound_loops)
2243 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2244 SND_CTRL_PLAY_LOOP);
2246 while (TimePlayed < 999)
2248 if (!tape.playing && !setup.sound_loops)
2249 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2250 if (TimePlayed < 999 && !(TimePlayed % 10))
2251 RaiseScore(level.score[SC_TIME_BONUS]);
2252 if (TimePlayed < 900 && !(TimePlayed % 10))
2257 DrawGameValue_Time(TimePlayed);
2265 if (!tape.playing && setup.sound_loops)
2266 StopSound(SND_GAME_LEVELTIME_BONUS);
2269 /* close exit door after last player */
2270 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2271 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2273 int element = Feld[ExitX][ExitY];
2275 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2276 EL_SP_EXIT_CLOSING);
2278 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2281 /* Hero disappears */
2282 DrawLevelField(ExitX, ExitY);
2288 CloseDoor(DOOR_CLOSE_1);
2293 SaveTape(tape.level_nr); /* Ask to save tape */
2296 if (level_nr == leveldir_current->handicap_level)
2298 leveldir_current->handicap_level++;
2299 SaveLevelSetup_SeriesInfo();
2302 if (level_editor_test_game)
2303 local_player->score = -1; /* no highscore when playing from editor */
2304 else if (level_nr < leveldir_current->last_level)
2305 raise_level = TRUE; /* advance to next level */
2307 if ((hi_pos = NewHiScore()) >= 0)
2309 game_status = GAME_MODE_SCORES;
2310 DrawHallOfFame(hi_pos);
2319 game_status = GAME_MODE_MAIN;
2336 LoadScore(level_nr);
2338 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2339 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2342 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2344 if (local_player->score > highscore[k].Score)
2346 /* player has made it to the hall of fame */
2348 if (k < MAX_SCORE_ENTRIES - 1)
2350 int m = MAX_SCORE_ENTRIES - 1;
2353 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2354 if (!strcmp(setup.player_name, highscore[l].Name))
2356 if (m == k) /* player's new highscore overwrites his old one */
2360 for (l = m; l > k; l--)
2362 strcpy(highscore[l].Name, highscore[l - 1].Name);
2363 highscore[l].Score = highscore[l - 1].Score;
2370 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2371 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2372 highscore[k].Score = local_player->score;
2378 else if (!strncmp(setup.player_name, highscore[k].Name,
2379 MAX_PLAYER_NAME_LEN))
2380 break; /* player already there with a higher score */
2386 SaveScore(level_nr);
2391 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2393 if (player->GfxAction != action || player->GfxDir != dir)
2396 printf("Player frame reset! (%d => %d, %d => %d)\n",
2397 player->GfxAction, action, player->GfxDir, dir);
2400 player->GfxAction = action;
2401 player->GfxDir = dir;
2403 player->StepFrame = 0;
2407 static void ResetRandomAnimationValue(int x, int y)
2409 GfxRandom[x][y] = INIT_GFX_RANDOM();
2412 static void ResetGfxAnimation(int x, int y)
2415 GfxAction[x][y] = ACTION_DEFAULT;
2416 GfxDir[x][y] = MovDir[x][y];
2419 void InitMovingField(int x, int y, int direction)
2421 int element = Feld[x][y];
2422 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2423 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2427 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2428 ResetGfxAnimation(x, y);
2430 MovDir[newx][newy] = MovDir[x][y] = direction;
2431 GfxDir[x][y] = direction;
2433 if (Feld[newx][newy] == EL_EMPTY)
2434 Feld[newx][newy] = EL_BLOCKED;
2436 if (direction == MV_DOWN && CAN_FALL(element))
2437 GfxAction[x][y] = ACTION_FALLING;
2439 GfxAction[x][y] = ACTION_MOVING;
2441 GfxFrame[newx][newy] = GfxFrame[x][y];
2442 GfxRandom[newx][newy] = GfxRandom[x][y];
2443 GfxAction[newx][newy] = GfxAction[x][y];
2444 GfxDir[newx][newy] = GfxDir[x][y];
2447 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2449 int direction = MovDir[x][y];
2450 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2451 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2457 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2459 int oldx = x, oldy = y;
2460 int direction = MovDir[x][y];
2462 if (direction == MV_LEFT)
2464 else if (direction == MV_RIGHT)
2466 else if (direction == MV_UP)
2468 else if (direction == MV_DOWN)
2471 *comes_from_x = oldx;
2472 *comes_from_y = oldy;
2475 int MovingOrBlocked2Element(int x, int y)
2477 int element = Feld[x][y];
2479 if (element == EL_BLOCKED)
2483 Blocked2Moving(x, y, &oldx, &oldy);
2484 return Feld[oldx][oldy];
2490 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2492 /* like MovingOrBlocked2Element(), but if element is moving
2493 and (x,y) is the field the moving element is just leaving,
2494 return EL_BLOCKED instead of the element value */
2495 int element = Feld[x][y];
2497 if (IS_MOVING(x, y))
2499 if (element == EL_BLOCKED)
2503 Blocked2Moving(x, y, &oldx, &oldy);
2504 return Feld[oldx][oldy];
2513 static void RemoveField(int x, int y)
2515 Feld[x][y] = EL_EMPTY;
2522 ChangeDelay[x][y] = 0;
2523 ChangePage[x][y] = -1;
2524 Pushed[x][y] = FALSE;
2527 ExplodeField[x][y] = EX_TYPE_NONE;
2530 GfxElement[x][y] = EL_UNDEFINED;
2531 GfxAction[x][y] = ACTION_DEFAULT;
2532 GfxDir[x][y] = MV_NO_MOVING;
2535 void RemoveMovingField(int x, int y)
2537 int oldx = x, oldy = y, newx = x, newy = y;
2538 int element = Feld[x][y];
2539 int next_element = EL_UNDEFINED;
2541 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2544 if (IS_MOVING(x, y))
2546 Moving2Blocked(x, y, &newx, &newy);
2548 if (Feld[newx][newy] != EL_BLOCKED)
2551 if (Feld[newx][newy] != EL_BLOCKED)
2553 /* element is moving, but target field is not free (blocked), but
2554 already occupied by something different (example: acid pool);
2555 in this case, only remove the moving field, but not the target */
2557 RemoveField(oldx, oldy);
2559 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2561 DrawLevelField(oldx, oldy);
2567 else if (element == EL_BLOCKED)
2569 Blocked2Moving(x, y, &oldx, &oldy);
2570 if (!IS_MOVING(oldx, oldy))
2574 if (element == EL_BLOCKED &&
2575 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2576 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2577 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2578 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2579 next_element = get_next_element(Feld[oldx][oldy]);
2581 RemoveField(oldx, oldy);
2582 RemoveField(newx, newy);
2584 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2586 if (next_element != EL_UNDEFINED)
2587 Feld[oldx][oldy] = next_element;
2589 DrawLevelField(oldx, oldy);
2590 DrawLevelField(newx, newy);
2593 void DrawDynamite(int x, int y)
2595 int sx = SCREENX(x), sy = SCREENY(y);
2596 int graphic = el2img(Feld[x][y]);
2599 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2602 if (IS_WALKABLE_INSIDE(Back[x][y]))
2606 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2607 else if (Store[x][y])
2608 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2610 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2613 if (Back[x][y] || Store[x][y])
2614 DrawGraphicThruMask(sx, sy, graphic, frame);
2616 DrawGraphic(sx, sy, graphic, frame);
2618 if (game.emulation == EMU_SUPAPLEX)
2619 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2620 else if (Store[x][y])
2621 DrawGraphicThruMask(sx, sy, graphic, frame);
2623 DrawGraphic(sx, sy, graphic, frame);
2627 void CheckDynamite(int x, int y)
2629 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2633 if (MovDelay[x][y] != 0)
2636 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2643 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2645 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2646 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2647 StopSound(SND_DYNAMITE_ACTIVE);
2649 StopSound(SND_DYNABOMB_ACTIVE);
2655 void DrawRelocatePlayer(struct PlayerInfo *player)
2657 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2658 boolean no_delay = (tape.warp_forward);
2659 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2660 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2661 int jx = player->jx;
2662 int jy = player->jy;
2664 if (level.instant_relocation)
2667 int offset = (setup.scroll_delay ? 3 : 0);
2669 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2671 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2672 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2673 local_player->jx - MIDPOSX);
2675 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2676 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2677 local_player->jy - MIDPOSY);
2681 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2682 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2683 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2685 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2686 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2687 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2689 /* don't scroll over playfield boundaries */
2690 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2691 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2693 /* don't scroll over playfield boundaries */
2694 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2695 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2698 scroll_x += (local_player->jx - old_jx);
2699 scroll_y += (local_player->jy - old_jy);
2701 /* don't scroll over playfield boundaries */
2702 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2703 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2705 /* don't scroll over playfield boundaries */
2706 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2707 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2710 RedrawPlayfield(TRUE, 0,0,0,0);
2716 int offset = (setup.scroll_delay ? 3 : 0);
2718 int scroll_xx = -999, scroll_yy = -999;
2720 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2722 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2725 int fx = FX, fy = FY;
2727 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2728 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2729 local_player->jx - MIDPOSX);
2731 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2732 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2733 local_player->jy - MIDPOSY);
2735 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2736 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2739 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2742 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2749 fx += dx * TILEX / 2;
2750 fy += dy * TILEY / 2;
2752 ScrollLevel(dx, dy);
2755 /* scroll in two steps of half tile size to make things smoother */
2756 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2758 Delay(wait_delay_value);
2760 /* scroll second step to align at full tile size */
2762 Delay(wait_delay_value);
2765 int scroll_xx = -999, scroll_yy = -999;
2767 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2769 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2772 int fx = FX, fy = FY;
2774 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2775 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2776 local_player->jx - MIDPOSX);
2778 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2779 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2780 local_player->jy - MIDPOSY);
2782 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2783 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2786 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2789 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2796 fx += dx * TILEX / 2;
2797 fy += dy * TILEY / 2;
2799 ScrollLevel(dx, dy);
2802 /* scroll in two steps of half tile size to make things smoother */
2803 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2805 Delay(wait_delay_value);
2807 /* scroll second step to align at full tile size */
2809 Delay(wait_delay_value);
2815 Delay(wait_delay_value);
2819 void RelocatePlayer(int jx, int jy, int el_player_raw)
2821 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
2822 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2823 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2824 boolean no_delay = (tape.warp_forward);
2825 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2826 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2827 int old_jx = player->jx;
2828 int old_jy = player->jy;
2829 int old_element = Feld[old_jx][old_jy];
2830 int element = Feld[jx][jy];
2831 boolean player_relocated = (old_jx != jx || old_jy != jy);
2833 static int trigger_sides[4][2] =
2835 /* enter side leave side */
2836 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
2837 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
2838 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
2839 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
2841 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2842 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2843 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
2844 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
2845 int enter_side = enter_side_horiz | enter_side_vert;
2846 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
2847 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
2848 int leave_side = leave_side_horiz | leave_side_vert;
2850 if (player->GameOver) /* do not reanimate dead player */
2853 if (!player_relocated) /* no need to relocate the player */
2856 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2858 RemoveField(jx, jy); /* temporarily remove newly placed player */
2859 DrawLevelField(jx, jy);
2862 if (player->present)
2864 while (player->MovPos)
2866 ScrollPlayer(player, SCROLL_GO_ON);
2867 ScrollScreen(NULL, SCROLL_GO_ON);
2873 Delay(wait_delay_value);
2876 DrawPlayer(player); /* needed here only to cleanup last field */
2877 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2879 player->is_moving = FALSE;
2883 if (IS_CUSTOM_ELEMENT(old_element))
2884 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
2886 player->index_bit, leave_side);
2888 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
2890 player->index_bit, leave_side);
2893 Feld[jx][jy] = el_player;
2894 InitPlayerField(jx, jy, el_player, TRUE);
2896 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
2898 Feld[jx][jy] = element;
2899 InitField(jx, jy, FALSE);
2903 if (player == local_player) /* only visually relocate local player */
2904 DrawRelocatePlayer(player);
2908 TestIfHeroTouchesBadThing(jx, jy);
2909 TestIfPlayerTouchesCustomElement(jx, jy);
2913 /* needed to allow change of walkable custom element by entering player */
2914 Changed[jx][jy] = 0; /* allow another change */
2918 if (IS_CUSTOM_ELEMENT(element))
2919 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
2920 player->index_bit, enter_side);
2922 CheckTriggeredElementChangeByPlayer(jx, jy, element,
2923 CE_OTHER_GETS_ENTERED,
2924 player->index_bit, enter_side);
2928 void Explode(int ex, int ey, int phase, int mode)
2935 /* !!! eliminate this variable !!! */
2936 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2941 int last_phase = num_phase * delay;
2942 int half_phase = (num_phase / 2) * delay;
2943 int first_phase_after_start = EX_PHASE_START + 1;
2947 if (game.explosions_delayed)
2949 ExplodeField[ex][ey] = mode;
2953 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2955 int center_element = Feld[ex][ey];
2958 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
2962 /* --- This is only really needed (and now handled) in "Impact()". --- */
2963 /* do not explode moving elements that left the explode field in time */
2964 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2965 center_element == EL_EMPTY &&
2966 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
2970 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
2971 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2973 /* remove things displayed in background while burning dynamite */
2974 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2977 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2979 /* put moving element to center field (and let it explode there) */
2980 center_element = MovingOrBlocked2Element(ex, ey);
2981 RemoveMovingField(ex, ey);
2982 Feld[ex][ey] = center_element;
2988 last_phase = element_info[center_element].explosion_delay + 1;
2990 last_phase = element_info[center_element].explosion_delay;
2994 printf("::: %d -> %d\n", center_element, last_phase);
2998 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3000 int xx = x - ex + 1;
3001 int yy = y - ey + 1;
3006 if (!IN_LEV_FIELD(x, y) ||
3007 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3008 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3011 if (!IN_LEV_FIELD(x, y) ||
3012 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3016 if (!IN_LEV_FIELD(x, y) ||
3017 ((mode != EX_TYPE_NORMAL ||
3018 center_element == EL_AMOEBA_TO_DIAMOND) &&
3019 (x != ex || y != ey)))
3023 element = Feld[x][y];
3025 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3027 element = MovingOrBlocked2Element(x, y);
3029 if (!IS_EXPLOSION_PROOF(element))
3030 RemoveMovingField(x, y);
3036 if (IS_EXPLOSION_PROOF(element))
3039 /* indestructible elements can only explode in center (but not flames) */
3040 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3041 element == EL_FLAMES)
3046 if ((IS_INDESTRUCTIBLE(element) &&
3047 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3048 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3049 element == EL_FLAMES)
3053 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3055 if (IS_ACTIVE_BOMB(element))
3057 /* re-activate things under the bomb like gate or penguin */
3058 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3065 /* save walkable background elements while explosion on same tile */
3067 if (IS_INDESTRUCTIBLE(element))
3068 Back[x][y] = element;
3070 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3071 Back[x][y] = element;
3074 /* ignite explodable elements reached by other explosion */
3075 if (element == EL_EXPLOSION)
3076 element = Store2[x][y];
3079 if (AmoebaNr[x][y] &&
3080 (element == EL_AMOEBA_FULL ||
3081 element == EL_BD_AMOEBA ||
3082 element == EL_AMOEBA_GROWING))
3084 AmoebaCnt[AmoebaNr[x][y]]--;
3085 AmoebaCnt2[AmoebaNr[x][y]]--;
3091 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3093 switch(StorePlayer[ex][ey])
3096 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3099 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3102 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3106 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3111 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3112 Store[x][y] = EL_EMPTY;
3114 if (game.emulation == EMU_SUPAPLEX)
3115 Store[x][y] = EL_EMPTY;
3118 else if (center_element == EL_MOLE)
3119 Store[x][y] = EL_EMERALD_RED;
3120 else if (center_element == EL_PENGUIN)
3121 Store[x][y] = EL_EMERALD_PURPLE;
3122 else if (center_element == EL_BUG)
3123 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3124 else if (center_element == EL_BD_BUTTERFLY)
3125 Store[x][y] = EL_BD_DIAMOND;
3126 else if (center_element == EL_SP_ELECTRON)
3127 Store[x][y] = EL_SP_INFOTRON;
3128 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3129 Store[x][y] = level.amoeba_content;
3130 else if (center_element == EL_YAMYAM)
3131 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3132 else if (IS_CUSTOM_ELEMENT(center_element) &&
3133 element_info[center_element].content[xx][yy] != EL_EMPTY)
3134 Store[x][y] = element_info[center_element].content[xx][yy];
3135 else if (element == EL_WALL_EMERALD)
3136 Store[x][y] = EL_EMERALD;
3137 else if (element == EL_WALL_DIAMOND)
3138 Store[x][y] = EL_DIAMOND;
3139 else if (element == EL_WALL_BD_DIAMOND)
3140 Store[x][y] = EL_BD_DIAMOND;
3141 else if (element == EL_WALL_EMERALD_YELLOW)
3142 Store[x][y] = EL_EMERALD_YELLOW;
3143 else if (element == EL_WALL_EMERALD_RED)
3144 Store[x][y] = EL_EMERALD_RED;
3145 else if (element == EL_WALL_EMERALD_PURPLE)
3146 Store[x][y] = EL_EMERALD_PURPLE;
3147 else if (element == EL_WALL_PEARL)
3148 Store[x][y] = EL_PEARL;
3149 else if (element == EL_WALL_CRYSTAL)
3150 Store[x][y] = EL_CRYSTAL;
3151 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3152 Store[x][y] = element_info[element].content[1][1];
3154 Store[x][y] = EL_EMPTY;
3156 if (x != ex || y != ey ||
3157 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_TYPE_BORDER)
3158 Store2[x][y] = element;
3161 if (AmoebaNr[x][y] &&
3162 (element == EL_AMOEBA_FULL ||
3163 element == EL_BD_AMOEBA ||
3164 element == EL_AMOEBA_GROWING))
3166 AmoebaCnt[AmoebaNr[x][y]]--;
3167 AmoebaCnt2[AmoebaNr[x][y]]--;
3173 MovDir[x][y] = MovPos[x][y] = 0;
3174 GfxDir[x][y] = MovDir[x][y];
3179 Feld[x][y] = EL_EXPLOSION;
3181 GfxElement[x][y] = center_element;
3183 GfxElement[x][y] = EL_UNDEFINED;
3186 ExplodePhase[x][y] = 1;
3188 ExplodeDelay[x][y] = last_phase;
3193 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3195 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3202 if (center_element == EL_YAMYAM)
3203 game.yamyam_content_nr =
3204 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3217 GfxFrame[x][y] = 0; /* restart explosion animation */
3221 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3225 last_phase = ExplodeDelay[x][y];
3228 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3232 /* activate this even in non-DEBUG version until cause for crash in
3233 getGraphicAnimationFrame() (see below) is found and eliminated */
3237 if (GfxElement[x][y] == EL_UNDEFINED)
3240 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3241 printf("Explode(): This should never happen!\n");
3244 GfxElement[x][y] = EL_EMPTY;
3250 border_element = Store2[x][y];
3251 if (IS_PLAYER(x, y))
3252 border_element = StorePlayer[x][y];
3255 printf("::: phase == %d\n", phase);
3258 if (phase == element_info[border_element].ignition_delay ||
3259 phase == last_phase)
3261 boolean border_explosion = FALSE;
3264 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3266 if (IS_PLAYER(x, y))
3269 KillHeroUnlessExplosionProtected(x, y);
3270 border_explosion = TRUE;
3273 if (phase == last_phase)
3274 printf("::: IS_PLAYER\n");
3277 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3279 Feld[x][y] = Store2[x][y];
3282 border_explosion = TRUE;
3285 if (phase == last_phase)
3286 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3289 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3291 AmoebeUmwandeln(x, y);
3293 border_explosion = TRUE;
3296 if (phase == last_phase)
3297 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3298 element_info[border_element].explosion_delay,
3299 element_info[border_element].ignition_delay,
3305 /* if an element just explodes due to another explosion (chain-reaction),
3306 do not immediately end the new explosion when it was the last frame of
3307 the explosion (as it would be done in the following "if"-statement!) */
3308 if (border_explosion && phase == last_phase)
3315 if (phase == first_phase_after_start)
3317 int element = Store2[x][y];
3319 if (element == EL_BLACK_ORB)
3321 Feld[x][y] = Store2[x][y];
3326 else if (phase == half_phase)
3328 int element = Store2[x][y];
3330 if (IS_PLAYER(x, y))
3331 KillHeroUnlessExplosionProtected(x, y);
3332 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3334 Feld[x][y] = Store2[x][y];
3338 else if (element == EL_AMOEBA_TO_DIAMOND)
3339 AmoebeUmwandeln(x, y);
3343 if (phase == last_phase)
3348 printf("::: done: phase == %d\n", phase);
3352 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3355 element = Feld[x][y] = Store[x][y];
3356 Store[x][y] = Store2[x][y] = 0;
3357 GfxElement[x][y] = EL_UNDEFINED;
3359 /* player can escape from explosions and might therefore be still alive */
3360 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3361 element <= EL_PLAYER_IS_EXPLODING_4)
3362 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3364 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3365 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3366 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3369 /* restore probably existing indestructible background element */
3370 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3371 element = Feld[x][y] = Back[x][y];
3374 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3375 GfxDir[x][y] = MV_NO_MOVING;
3376 ChangeDelay[x][y] = 0;
3377 ChangePage[x][y] = -1;
3380 InitField_WithBug2(x, y, FALSE);
3382 InitField(x, y, FALSE);
3384 /* !!! not needed !!! */
3386 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3387 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3390 if (CAN_MOVE(element))
3395 DrawLevelField(x, y);
3397 TestIfElementTouchesCustomElement(x, y);
3399 if (GFX_CRUMBLED(element))
3400 DrawLevelFieldCrumbledSandNeighbours(x, y);
3402 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3403 StorePlayer[x][y] = 0;
3405 if (ELEM_IS_PLAYER(element))
3406 RelocatePlayer(x, y, element);
3409 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3411 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3415 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3417 int stored = Store[x][y];
3418 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3419 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3423 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3425 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3429 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3433 printf("::: %d / %d [%d - %d]\n",
3434 GfxFrame[x][y], phase - delay, phase, delay);
3438 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3439 element_info[GfxElement[x][y]].token_name,
3444 DrawLevelFieldCrumbledSand(x, y);
3446 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3448 DrawLevelElement(x, y, Back[x][y]);
3449 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3451 else if (IS_WALKABLE_UNDER(Back[x][y]))
3453 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3454 DrawLevelElementThruMask(x, y, Back[x][y]);
3456 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3457 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3461 void DynaExplode(int ex, int ey)
3464 int dynabomb_element = Feld[ex][ey];
3465 int dynabomb_size = 1;
3466 boolean dynabomb_xl = FALSE;
3467 struct PlayerInfo *player;
3468 static int xy[4][2] =
3476 if (IS_ACTIVE_BOMB(dynabomb_element))
3478 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3479 dynabomb_size = player->dynabomb_size;
3480 dynabomb_xl = player->dynabomb_xl;
3481 player->dynabombs_left++;
3484 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3486 for (i = 0; i < NUM_DIRECTIONS; i++)
3488 for (j = 1; j <= dynabomb_size; j++)
3490 int x = ex + j * xy[i][0];
3491 int y = ey + j * xy[i][1];
3494 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3497 element = Feld[x][y];
3499 /* do not restart explosions of fields with active bombs */
3500 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3503 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3506 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3507 !CAN_GROW_INTO(element) && !dynabomb_xl)
3510 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3511 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3512 element != EL_SAND && !dynabomb_xl)
3519 void Bang(int x, int y)
3522 int element = MovingOrBlocked2Element(x, y);
3524 int element = Feld[x][y];
3528 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3530 if (IS_PLAYER(x, y))
3533 struct PlayerInfo *player = PLAYERINFO(x, y);
3535 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3536 player->element_nr);
3541 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3543 if (game.emulation == EMU_SUPAPLEX)
3544 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3546 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3551 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3559 case EL_BD_BUTTERFLY:
3562 case EL_DARK_YAMYAM:
3566 RaiseScoreElement(element);
3567 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3569 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3570 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3571 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3572 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3573 case EL_DYNABOMB_INCREASE_NUMBER:
3574 case EL_DYNABOMB_INCREASE_SIZE:
3575 case EL_DYNABOMB_INCREASE_POWER:
3580 case EL_LAMP_ACTIVE:
3582 case EL_AMOEBA_TO_DIAMOND:
3584 if (IS_PLAYER(x, y))
3585 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3587 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3590 if (CAN_EXPLODE_CROSS(element))
3592 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3596 else if (CAN_EXPLODE_1X1(element))
3597 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3599 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3603 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3606 void SplashAcid(int x, int y)
3609 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3610 (!IN_LEV_FIELD(x - 1, y - 2) ||
3611 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3612 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3614 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3615 (!IN_LEV_FIELD(x + 1, y - 2) ||
3616 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3617 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3619 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3621 /* input: position of element entering acid (obsolete) */
3623 int element = Feld[x][y];
3625 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3628 if (element != EL_ACID_SPLASH_LEFT &&
3629 element != EL_ACID_SPLASH_RIGHT)
3631 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3633 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3634 (!IN_LEV_FIELD(x - 1, y - 1) ||
3635 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3636 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3638 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3639 (!IN_LEV_FIELD(x + 1, y - 1) ||
3640 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3641 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3646 static void InitBeltMovement()
3648 static int belt_base_element[4] =
3650 EL_CONVEYOR_BELT_1_LEFT,
3651 EL_CONVEYOR_BELT_2_LEFT,
3652 EL_CONVEYOR_BELT_3_LEFT,
3653 EL_CONVEYOR_BELT_4_LEFT
3655 static int belt_base_active_element[4] =
3657 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3658 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3659 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3660 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3665 /* set frame order for belt animation graphic according to belt direction */
3666 for (i = 0; i < NUM_BELTS; i++)
3670 for (j = 0; j < NUM_BELT_PARTS; j++)
3672 int element = belt_base_active_element[belt_nr] + j;
3673 int graphic = el2img(element);
3675 if (game.belt_dir[i] == MV_LEFT)
3676 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3678 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3682 for (y = 0; y < lev_fieldy; y++)
3684 for (x = 0; x < lev_fieldx; x++)
3686 int element = Feld[x][y];
3688 for (i = 0; i < NUM_BELTS; i++)
3690 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3692 int e_belt_nr = getBeltNrFromBeltElement(element);
3695 if (e_belt_nr == belt_nr)
3697 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3699 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3707 static void ToggleBeltSwitch(int x, int y)
3709 static int belt_base_element[4] =
3711 EL_CONVEYOR_BELT_1_LEFT,
3712 EL_CONVEYOR_BELT_2_LEFT,
3713 EL_CONVEYOR_BELT_3_LEFT,
3714 EL_CONVEYOR_BELT_4_LEFT
3716 static int belt_base_active_element[4] =
3718 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3719 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3720 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3721 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3723 static int belt_base_switch_element[4] =
3725 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3726 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3727 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3728 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3730 static int belt_move_dir[4] =
3738 int element = Feld[x][y];
3739 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3740 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3741 int belt_dir = belt_move_dir[belt_dir_nr];
3744 if (!IS_BELT_SWITCH(element))
3747 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3748 game.belt_dir[belt_nr] = belt_dir;
3750 if (belt_dir_nr == 3)
3753 /* set frame order for belt animation graphic according to belt direction */
3754 for (i = 0; i < NUM_BELT_PARTS; i++)
3756 int element = belt_base_active_element[belt_nr] + i;
3757 int graphic = el2img(element);
3759 if (belt_dir == MV_LEFT)
3760 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3762 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3765 for (yy = 0; yy < lev_fieldy; yy++)
3767 for (xx = 0; xx < lev_fieldx; xx++)
3769 int element = Feld[xx][yy];
3771 if (IS_BELT_SWITCH(element))
3773 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3775 if (e_belt_nr == belt_nr)
3777 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3778 DrawLevelField(xx, yy);
3781 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3783 int e_belt_nr = getBeltNrFromBeltElement(element);
3785 if (e_belt_nr == belt_nr)
3787 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3789 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3790 DrawLevelField(xx, yy);
3793 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3795 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3797 if (e_belt_nr == belt_nr)
3799 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3801 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3802 DrawLevelField(xx, yy);
3809 static void ToggleSwitchgateSwitch(int x, int y)
3813 game.switchgate_pos = !game.switchgate_pos;
3815 for (yy = 0; yy < lev_fieldy; yy++)
3817 for (xx = 0; xx < lev_fieldx; xx++)
3819 int element = Feld[xx][yy];
3821 if (element == EL_SWITCHGATE_SWITCH_UP ||
3822 element == EL_SWITCHGATE_SWITCH_DOWN)
3824 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3825 DrawLevelField(xx, yy);
3827 else if (element == EL_SWITCHGATE_OPEN ||
3828 element == EL_SWITCHGATE_OPENING)
3830 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3832 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3834 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3837 else if (element == EL_SWITCHGATE_CLOSED ||
3838 element == EL_SWITCHGATE_CLOSING)
3840 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3842 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3844 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3851 static int getInvisibleActiveFromInvisibleElement(int element)
3853 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3854 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3855 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3859 static int getInvisibleFromInvisibleActiveElement(int element)
3861 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3862 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3863 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3867 static void RedrawAllLightSwitchesAndInvisibleElements()
3871 for (y = 0; y < lev_fieldy; y++)
3873 for (x = 0; x < lev_fieldx; x++)
3875 int element = Feld[x][y];
3877 if (element == EL_LIGHT_SWITCH &&
3878 game.light_time_left > 0)
3880 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3881 DrawLevelField(x, y);
3883 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3884 game.light_time_left == 0)
3886 Feld[x][y] = EL_LIGHT_SWITCH;
3887 DrawLevelField(x, y);
3889 else if (element == EL_INVISIBLE_STEELWALL ||
3890 element == EL_INVISIBLE_WALL ||
3891 element == EL_INVISIBLE_SAND)
3893 if (game.light_time_left > 0)
3894 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3896 DrawLevelField(x, y);
3898 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3899 element == EL_INVISIBLE_WALL_ACTIVE ||
3900 element == EL_INVISIBLE_SAND_ACTIVE)
3902 if (game.light_time_left == 0)
3903 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3905 DrawLevelField(x, y);
3911 static void ToggleLightSwitch(int x, int y)
3913 int element = Feld[x][y];
3915 game.light_time_left =
3916 (element == EL_LIGHT_SWITCH ?
3917 level.time_light * FRAMES_PER_SECOND : 0);
3919 RedrawAllLightSwitchesAndInvisibleElements();
3922 static void ActivateTimegateSwitch(int x, int y)
3926 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3928 for (yy = 0; yy < lev_fieldy; yy++)
3930 for (xx = 0; xx < lev_fieldx; xx++)
3932 int element = Feld[xx][yy];
3934 if (element == EL_TIMEGATE_CLOSED ||
3935 element == EL_TIMEGATE_CLOSING)
3937 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3938 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3942 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3944 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3945 DrawLevelField(xx, yy);
3952 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3955 inline static int getElementMoveStepsize(int x, int y)
3957 int element = Feld[x][y];
3958 int direction = MovDir[x][y];
3959 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3960 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3961 int horiz_move = (dx != 0);
3962 int sign = (horiz_move ? dx : dy);
3963 int step = sign * element_info[element].move_stepsize;
3965 /* special values for move stepsize for spring and things on conveyor belt */
3969 if (element == EL_SPRING)
3970 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3971 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3972 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3973 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3975 if (CAN_FALL(element) &&
3976 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3977 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3978 else if (element == EL_SPRING)
3979 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3986 void Impact(int x, int y)
3988 boolean lastline = (y == lev_fieldy-1);
3989 boolean object_hit = FALSE;
3990 boolean impact = (lastline || object_hit);
3991 int element = Feld[x][y];
3992 int smashed = EL_STEELWALL;
3994 if (!lastline) /* check if element below was hit */
3996 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3999 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4000 MovDir[x][y + 1] != MV_DOWN ||
4001 MovPos[x][y + 1] <= TILEY / 2));
4004 object_hit = !IS_FREE(x, y + 1);
4007 /* do not smash moving elements that left the smashed field in time */
4008 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4009 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4013 smashed = MovingOrBlocked2Element(x, y + 1);
4015 impact = (lastline || object_hit);
4018 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4020 SplashAcid(x, y + 1);
4024 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4025 /* only reset graphic animation if graphic really changes after impact */
4027 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4029 ResetGfxAnimation(x, y);
4030 DrawLevelField(x, y);
4033 if (impact && CAN_EXPLODE_IMPACT(element))
4038 else if (impact && element == EL_PEARL)
4040 ResetGfxAnimation(x, y);
4042 Feld[x][y] = EL_PEARL_BREAKING;
4043 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4046 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4048 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4053 if (impact && element == EL_AMOEBA_DROP)
4055 if (object_hit && IS_PLAYER(x, y + 1))
4056 KillHeroUnlessEnemyProtected(x, y + 1);
4057 else if (object_hit && smashed == EL_PENGUIN)
4061 Feld[x][y] = EL_AMOEBA_GROWING;
4062 Store[x][y] = EL_AMOEBA_WET;
4064 ResetRandomAnimationValue(x, y);
4069 if (object_hit) /* check which object was hit */
4071 if (CAN_PASS_MAGIC_WALL(element) &&
4072 (smashed == EL_MAGIC_WALL ||
4073 smashed == EL_BD_MAGIC_WALL))
4076 int activated_magic_wall =
4077 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4078 EL_BD_MAGIC_WALL_ACTIVE);
4080 /* activate magic wall / mill */
4081 for (yy = 0; yy < lev_fieldy; yy++)
4082 for (xx = 0; xx < lev_fieldx; xx++)
4083 if (Feld[xx][yy] == smashed)
4084 Feld[xx][yy] = activated_magic_wall;
4086 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4087 game.magic_wall_active = TRUE;
4089 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4090 SND_MAGIC_WALL_ACTIVATING :
4091 SND_BD_MAGIC_WALL_ACTIVATING));
4094 if (IS_PLAYER(x, y + 1))
4096 if (CAN_SMASH_PLAYER(element))
4098 KillHeroUnlessEnemyProtected(x, y + 1);
4102 else if (smashed == EL_PENGUIN)
4104 if (CAN_SMASH_PLAYER(element))
4110 else if (element == EL_BD_DIAMOND)
4112 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4118 else if (((element == EL_SP_INFOTRON ||
4119 element == EL_SP_ZONK) &&
4120 (smashed == EL_SP_SNIKSNAK ||
4121 smashed == EL_SP_ELECTRON ||
4122 smashed == EL_SP_DISK_ORANGE)) ||
4123 (element == EL_SP_INFOTRON &&
4124 smashed == EL_SP_DISK_YELLOW))
4130 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4136 else if (CAN_SMASH_EVERYTHING(element))
4138 if (IS_CLASSIC_ENEMY(smashed) ||
4139 CAN_EXPLODE_SMASHED(smashed))
4144 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4146 if (smashed == EL_LAMP ||
4147 smashed == EL_LAMP_ACTIVE)
4152 else if (smashed == EL_NUT)
4154 Feld[x][y + 1] = EL_NUT_BREAKING;
4155 PlayLevelSound(x, y, SND_NUT_BREAKING);
4156 RaiseScoreElement(EL_NUT);
4159 else if (smashed == EL_PEARL)
4161 ResetGfxAnimation(x, y);
4163 Feld[x][y + 1] = EL_PEARL_BREAKING;
4164 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4167 else if (smashed == EL_DIAMOND)
4169 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4170 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4173 else if (IS_BELT_SWITCH(smashed))
4175 ToggleBeltSwitch(x, y + 1);
4177 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4178 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4180 ToggleSwitchgateSwitch(x, y + 1);
4182 else if (smashed == EL_LIGHT_SWITCH ||
4183 smashed == EL_LIGHT_SWITCH_ACTIVE)
4185 ToggleLightSwitch(x, y + 1);
4190 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4193 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4196 /* !!! TEST ONLY !!! */
4197 CheckElementChangeBySide(x, y + 1, smashed, element,
4198 CE_SWITCHED, CH_SIDE_TOP);
4199 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4200 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4202 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4203 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4204 CheckElementChangeBySide(x, y + 1, smashed, element,
4205 CE_SWITCHED, CH_SIDE_TOP);
4211 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4216 /* play sound of magic wall / mill */
4218 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4219 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4221 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4222 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4223 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4224 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4229 /* play sound of object that hits the ground */
4230 if (lastline || object_hit)
4231 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4234 inline static void TurnRoundExt(int x, int y)
4246 { 0, 0 }, { 0, 0 }, { 0, 0 },
4251 int left, right, back;
4255 { MV_DOWN, MV_UP, MV_RIGHT },
4256 { MV_UP, MV_DOWN, MV_LEFT },
4258 { MV_LEFT, MV_RIGHT, MV_DOWN },
4262 { MV_RIGHT, MV_LEFT, MV_UP }
4265 int element = Feld[x][y];
4266 int move_pattern = element_info[element].move_pattern;
4268 int old_move_dir = MovDir[x][y];
4269 int left_dir = turn[old_move_dir].left;
4270 int right_dir = turn[old_move_dir].right;
4271 int back_dir = turn[old_move_dir].back;
4273 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4274 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4275 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4276 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4278 int left_x = x + left_dx, left_y = y + left_dy;
4279 int right_x = x + right_dx, right_y = y + right_dy;
4280 int move_x = x + move_dx, move_y = y + move_dy;
4284 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4286 TestIfBadThingTouchesOtherBadThing(x, y);
4288 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4289 MovDir[x][y] = right_dir;
4290 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4291 MovDir[x][y] = left_dir;
4293 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4295 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4299 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4300 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4302 TestIfBadThingTouchesOtherBadThing(x, y);
4304 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4305 MovDir[x][y] = left_dir;
4306 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4307 MovDir[x][y] = right_dir;
4309 if ((element == EL_SPACESHIP ||
4310 element == EL_SP_SNIKSNAK ||
4311 element == EL_SP_ELECTRON)
4312 && MovDir[x][y] != old_move_dir)
4314 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4318 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4320 TestIfBadThingTouchesOtherBadThing(x, y);
4322 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4323 MovDir[x][y] = left_dir;
4324 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4325 MovDir[x][y] = right_dir;
4327 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4329 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4332 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4334 TestIfBadThingTouchesOtherBadThing(x, y);
4336 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4337 MovDir[x][y] = left_dir;
4338 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4339 MovDir[x][y] = right_dir;
4341 if (MovDir[x][y] != old_move_dir)
4345 else if (element == EL_YAMYAM)
4347 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4348 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4350 if (can_turn_left && can_turn_right)
4351 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4352 else if (can_turn_left)
4353 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4354 else if (can_turn_right)
4355 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4357 MovDir[x][y] = back_dir;
4359 MovDelay[x][y] = 16 + 16 * RND(3);
4361 else if (element == EL_DARK_YAMYAM)
4363 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4365 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4368 if (can_turn_left && can_turn_right)
4369 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4370 else if (can_turn_left)
4371 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4372 else if (can_turn_right)
4373 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4375 MovDir[x][y] = back_dir;
4377 MovDelay[x][y] = 16 + 16 * RND(3);
4379 else if (element == EL_PACMAN)
4381 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4382 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4384 if (can_turn_left && can_turn_right)
4385 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4386 else if (can_turn_left)
4387 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4388 else if (can_turn_right)
4389 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4391 MovDir[x][y] = back_dir;
4393 MovDelay[x][y] = 6 + RND(40);
4395 else if (element == EL_PIG)
4397 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4398 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4399 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4400 boolean should_turn_left, should_turn_right, should_move_on;
4402 int rnd = RND(rnd_value);
4404 should_turn_left = (can_turn_left &&
4406 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4407 y + back_dy + left_dy)));
4408 should_turn_right = (can_turn_right &&
4410 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4411 y + back_dy + right_dy)));
4412 should_move_on = (can_move_on &&
4415 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4416 y + move_dy + left_dy) ||
4417 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4418 y + move_dy + right_dy)));
4420 if (should_turn_left || should_turn_right || should_move_on)
4422 if (should_turn_left && should_turn_right && should_move_on)
4423 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4424 rnd < 2 * rnd_value / 3 ? right_dir :
4426 else if (should_turn_left && should_turn_right)
4427 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4428 else if (should_turn_left && should_move_on)
4429 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4430 else if (should_turn_right && should_move_on)
4431 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4432 else if (should_turn_left)
4433 MovDir[x][y] = left_dir;
4434 else if (should_turn_right)
4435 MovDir[x][y] = right_dir;
4436 else if (should_move_on)
4437 MovDir[x][y] = old_move_dir;
4439 else if (can_move_on && rnd > rnd_value / 8)
4440 MovDir[x][y] = old_move_dir;
4441 else if (can_turn_left && can_turn_right)
4442 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4443 else if (can_turn_left && rnd > rnd_value / 8)
4444 MovDir[x][y] = left_dir;
4445 else if (can_turn_right && rnd > rnd_value/8)
4446 MovDir[x][y] = right_dir;
4448 MovDir[x][y] = back_dir;
4450 xx = x + move_xy[MovDir[x][y]].x;
4451 yy = y + move_xy[MovDir[x][y]].y;
4453 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4454 MovDir[x][y] = old_move_dir;
4458 else if (element == EL_DRAGON)
4460 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4461 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4462 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4464 int rnd = RND(rnd_value);
4467 if (FrameCounter < 1 && x == 0 && y == 29)
4468 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4471 if (can_move_on && rnd > rnd_value / 8)
4472 MovDir[x][y] = old_move_dir;
4473 else if (can_turn_left && can_turn_right)
4474 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4475 else if (can_turn_left && rnd > rnd_value / 8)
4476 MovDir[x][y] = left_dir;
4477 else if (can_turn_right && rnd > rnd_value / 8)
4478 MovDir[x][y] = right_dir;
4480 MovDir[x][y] = back_dir;
4482 xx = x + move_xy[MovDir[x][y]].x;
4483 yy = y + move_xy[MovDir[x][y]].y;
4486 if (FrameCounter < 1 && x == 0 && y == 29)
4487 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4488 xx, yy, Feld[xx][yy],
4493 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4494 MovDir[x][y] = old_move_dir;
4496 if (!IS_FREE(xx, yy))
4497 MovDir[x][y] = old_move_dir;
4501 if (FrameCounter < 1 && x == 0 && y == 29)
4502 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4507 else if (element == EL_MOLE)
4509 boolean can_move_on =
4510 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4511 IS_AMOEBOID(Feld[move_x][move_y]) ||
4512 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4515 boolean can_turn_left =
4516 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4517 IS_AMOEBOID(Feld[left_x][left_y])));
4519 boolean can_turn_right =
4520 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4521 IS_AMOEBOID(Feld[right_x][right_y])));
4523 if (can_turn_left && can_turn_right)
4524 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4525 else if (can_turn_left)
4526 MovDir[x][y] = left_dir;
4528 MovDir[x][y] = right_dir;
4531 if (MovDir[x][y] != old_move_dir)
4534 else if (element == EL_BALLOON)
4536 MovDir[x][y] = game.balloon_dir;
4539 else if (element == EL_SPRING)
4542 if (MovDir[x][y] & MV_HORIZONTAL &&
4543 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4544 MovDir[x][y] = MV_NO_MOVING;
4546 if (MovDir[x][y] & MV_HORIZONTAL &&
4547 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4548 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4549 MovDir[x][y] = MV_NO_MOVING;
4554 else if (element == EL_ROBOT ||
4555 element == EL_SATELLITE ||
4556 element == EL_PENGUIN)
4558 int attr_x = -1, attr_y = -1;
4569 for (i = 0; i < MAX_PLAYERS; i++)
4571 struct PlayerInfo *player = &stored_player[i];
4572 int jx = player->jx, jy = player->jy;
4574 if (!player->active)
4578 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4587 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4588 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4589 game.engine_version < VERSION_IDENT(3,1,0,0)))
4591 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4598 if (element == EL_PENGUIN)
4601 static int xy[4][2] =
4609 for (i = 0; i < NUM_DIRECTIONS; i++)
4611 int ex = x + xy[i][0];
4612 int ey = y + xy[i][1];
4614 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4623 MovDir[x][y] = MV_NO_MOVING;
4625 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4626 else if (attr_x > x)
4627 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4629 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4630 else if (attr_y > y)
4631 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4633 if (element == EL_ROBOT)
4637 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4638 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4639 Moving2Blocked(x, y, &newx, &newy);
4641 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4642 MovDelay[x][y] = 8 + 8 * !RND(3);
4644 MovDelay[x][y] = 16;
4646 else if (element == EL_PENGUIN)
4652 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4654 boolean first_horiz = RND(2);
4655 int new_move_dir = MovDir[x][y];
4658 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4659 Moving2Blocked(x, y, &newx, &newy);
4661 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4665 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4666 Moving2Blocked(x, y, &newx, &newy);
4668 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4671 MovDir[x][y] = old_move_dir;
4675 else /* (element == EL_SATELLITE) */
4681 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4683 boolean first_horiz = RND(2);
4684 int new_move_dir = MovDir[x][y];
4687 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4688 Moving2Blocked(x, y, &newx, &newy);
4690 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4694 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4695 Moving2Blocked(x, y, &newx, &newy);
4697 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4700 MovDir[x][y] = old_move_dir;
4705 else if (move_pattern == MV_TURNING_LEFT ||
4706 move_pattern == MV_TURNING_RIGHT ||
4707 move_pattern == MV_TURNING_LEFT_RIGHT ||
4708 move_pattern == MV_TURNING_RIGHT_LEFT ||
4709 move_pattern == MV_TURNING_RANDOM ||
4710 move_pattern == MV_ALL_DIRECTIONS)
4712 boolean can_turn_left =
4713 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4714 boolean can_turn_right =
4715 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4717 if (move_pattern == MV_TURNING_LEFT)
4718 MovDir[x][y] = left_dir;
4719 else if (move_pattern == MV_TURNING_RIGHT)
4720 MovDir[x][y] = right_dir;
4721 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4722 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4723 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4724 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4725 else if (move_pattern == MV_TURNING_RANDOM)
4726 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4727 can_turn_right && !can_turn_left ? right_dir :
4728 RND(2) ? left_dir : right_dir);
4729 else if (can_turn_left && can_turn_right)
4730 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4731 else if (can_turn_left)
4732 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4733 else if (can_turn_right)
4734 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4736 MovDir[x][y] = back_dir;
4738 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4740 else if (move_pattern == MV_HORIZONTAL ||
4741 move_pattern == MV_VERTICAL)
4743 if (move_pattern & old_move_dir)
4744 MovDir[x][y] = back_dir;
4745 else if (move_pattern == MV_HORIZONTAL)
4746 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4747 else if (move_pattern == MV_VERTICAL)
4748 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4750 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4752 else if (move_pattern & MV_ANY_DIRECTION)
4754 MovDir[x][y] = move_pattern;
4755 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4757 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4759 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4760 MovDir[x][y] = left_dir;
4761 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4762 MovDir[x][y] = right_dir;
4764 if (MovDir[x][y] != old_move_dir)
4765 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4767 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4769 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4770 MovDir[x][y] = right_dir;
4771 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4772 MovDir[x][y] = left_dir;
4774 if (MovDir[x][y] != old_move_dir)
4775 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4777 else if (move_pattern == MV_TOWARDS_PLAYER ||
4778 move_pattern == MV_AWAY_FROM_PLAYER)
4780 int attr_x = -1, attr_y = -1;
4782 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4793 for (i = 0; i < MAX_PLAYERS; i++)
4795 struct PlayerInfo *player = &stored_player[i];
4796 int jx = player->jx, jy = player->jy;
4798 if (!player->active)
4802 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4810 MovDir[x][y] = MV_NO_MOVING;
4812 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4813 else if (attr_x > x)
4814 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4816 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4817 else if (attr_y > y)
4818 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4820 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4822 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4824 boolean first_horiz = RND(2);
4825 int new_move_dir = MovDir[x][y];
4828 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4829 Moving2Blocked(x, y, &newx, &newy);
4831 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4835 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4836 Moving2Blocked(x, y, &newx, &newy);
4838 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4841 MovDir[x][y] = old_move_dir;
4844 else if (move_pattern == MV_WHEN_PUSHED ||
4845 move_pattern == MV_WHEN_DROPPED)
4847 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4848 MovDir[x][y] = MV_NO_MOVING;
4852 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4854 static int test_xy[7][2] =
4864 static int test_dir[7] =
4874 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4875 int move_preference = -1000000; /* start with very low preference */
4876 int new_move_dir = MV_NO_MOVING;
4877 int start_test = RND(4);
4880 for (i = 0; i < NUM_DIRECTIONS; i++)
4882 int move_dir = test_dir[start_test + i];
4883 int move_dir_preference;
4885 xx = x + test_xy[start_test + i][0];
4886 yy = y + test_xy[start_test + i][1];
4888 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4889 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4891 new_move_dir = move_dir;
4896 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4899 move_dir_preference = -1 * RunnerVisit[xx][yy];
4900 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4901 move_dir_preference = PlayerVisit[xx][yy];
4903 if (move_dir_preference > move_preference)
4905 /* prefer field that has not been visited for the longest time */
4906 move_preference = move_dir_preference;
4907 new_move_dir = move_dir;
4909 else if (move_dir_preference == move_preference &&
4910 move_dir == old_move_dir)
4912 /* prefer last direction when all directions are preferred equally */
4913 move_preference = move_dir_preference;
4914 new_move_dir = move_dir;
4918 MovDir[x][y] = new_move_dir;
4919 if (old_move_dir != new_move_dir)
4924 static void TurnRound(int x, int y)
4926 int direction = MovDir[x][y];
4929 GfxDir[x][y] = MovDir[x][y];
4935 GfxDir[x][y] = MovDir[x][y];
4938 if (direction != MovDir[x][y])
4943 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4946 GfxAction[x][y] = ACTION_WAITING;
4950 static boolean JustBeingPushed(int x, int y)
4954 for (i = 0; i < MAX_PLAYERS; i++)
4956 struct PlayerInfo *player = &stored_player[i];
4958 if (player->active && player->is_pushing && player->MovPos)
4960 int next_jx = player->jx + (player->jx - player->last_jx);
4961 int next_jy = player->jy + (player->jy - player->last_jy);
4963 if (x == next_jx && y == next_jy)
4971 void StartMoving(int x, int y)
4974 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4976 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4977 int element = Feld[x][y];
4983 if (MovDelay[x][y] == 0)
4984 GfxAction[x][y] = ACTION_DEFAULT;
4986 /* !!! this should be handled more generic (not only for mole) !!! */
4987 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4988 GfxAction[x][y] = ACTION_DEFAULT;
4991 if (CAN_FALL(element) && y < lev_fieldy - 1)
4993 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4994 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4995 if (JustBeingPushed(x, y))
4998 if (element == EL_QUICKSAND_FULL)
5000 if (IS_FREE(x, y + 1))
5002 InitMovingField(x, y, MV_DOWN);
5003 started_moving = TRUE;
5005 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5006 Store[x][y] = EL_ROCK;
5008 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5010 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5013 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5015 if (!MovDelay[x][y])
5016 MovDelay[x][y] = TILEY + 1;
5025 Feld[x][y] = EL_QUICKSAND_EMPTY;
5026 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5027 Store[x][y + 1] = Store[x][y];
5030 PlayLevelSoundAction(x, y, ACTION_FILLING);
5032 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5036 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5037 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5039 InitMovingField(x, y, MV_DOWN);
5040 started_moving = TRUE;
5042 Feld[x][y] = EL_QUICKSAND_FILLING;
5043 Store[x][y] = element;
5045 PlayLevelSoundAction(x, y, ACTION_FILLING);
5047 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5050 else if (element == EL_MAGIC_WALL_FULL)
5052 if (IS_FREE(x, y + 1))
5054 InitMovingField(x, y, MV_DOWN);
5055 started_moving = TRUE;
5057 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5058 Store[x][y] = EL_CHANGED(Store[x][y]);
5060 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5062 if (!MovDelay[x][y])
5063 MovDelay[x][y] = TILEY/4 + 1;
5072 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5073 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5074 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5078 else if (element == EL_BD_MAGIC_WALL_FULL)
5080 if (IS_FREE(x, y + 1))
5082 InitMovingField(x, y, MV_DOWN);
5083 started_moving = TRUE;
5085 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5086 Store[x][y] = EL_CHANGED2(Store[x][y]);
5088 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5090 if (!MovDelay[x][y])
5091 MovDelay[x][y] = TILEY/4 + 1;
5100 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5101 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5102 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5106 else if (CAN_PASS_MAGIC_WALL(element) &&
5107 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5108 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5110 InitMovingField(x, y, MV_DOWN);
5111 started_moving = TRUE;
5114 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5115 EL_BD_MAGIC_WALL_FILLING);
5116 Store[x][y] = element;
5119 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5121 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5124 SplashAcid(x, y + 1);
5126 InitMovingField(x, y, MV_DOWN);
5127 started_moving = TRUE;
5129 Store[x][y] = EL_ACID;
5131 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5132 GfxAction[x][y + 1] = ACTION_ACTIVE;
5136 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5137 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5139 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5140 CAN_SMASH(element) && WasJustFalling[x][y] &&
5141 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5143 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5144 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5145 (Feld[x][y + 1] == EL_BLOCKED)))
5149 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5150 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5151 WasJustMoving[x][y] && !Pushed[x][y + 1])
5153 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5154 WasJustMoving[x][y])
5159 /* this is needed for a special case not covered by calling "Impact()"
5160 from "ContinueMoving()": if an element moves to a tile directly below
5161 another element which was just falling on that tile (which was empty
5162 in the previous frame), the falling element above would just stop
5163 instead of smashing the element below (in previous version, the above
5164 element was just checked for "moving" instead of "falling", resulting
5165 in incorrect smashes caused by horizontal movement of the above
5166 element; also, the case of the player being the element to smash was
5167 simply not covered here... :-/ ) */
5170 WasJustMoving[x][y] = 0;
5171 WasJustFalling[x][y] = 0;
5174 CheckCollision[x][y] = 0;
5178 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5180 if (MovDir[x][y] == MV_NO_MOVING)
5182 InitMovingField(x, y, MV_DOWN);
5183 started_moving = TRUE;
5186 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5188 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5189 MovDir[x][y] = MV_DOWN;
5191 InitMovingField(x, y, MV_DOWN);
5192 started_moving = TRUE;
5194 else if (element == EL_AMOEBA_DROP)
5196 Feld[x][y] = EL_AMOEBA_GROWING;
5197 Store[x][y] = EL_AMOEBA_WET;
5199 /* Store[x][y + 1] must be zero, because:
5200 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5203 #if OLD_GAME_BEHAVIOUR
5204 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5206 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5207 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5208 element != EL_DX_SUPABOMB)
5211 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5212 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5213 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5214 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5217 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5218 (IS_FREE(x - 1, y + 1) ||
5219 Feld[x - 1][y + 1] == EL_ACID));
5220 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5221 (IS_FREE(x + 1, y + 1) ||
5222 Feld[x + 1][y + 1] == EL_ACID));
5223 boolean can_fall_any = (can_fall_left || can_fall_right);
5224 boolean can_fall_both = (can_fall_left && can_fall_right);
5226 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5228 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5230 if (slippery_type == SLIPPERY_ONLY_LEFT)
5231 can_fall_right = FALSE;
5232 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5233 can_fall_left = FALSE;
5234 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5235 can_fall_right = FALSE;
5236 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5237 can_fall_left = FALSE;
5239 can_fall_any = (can_fall_left || can_fall_right);
5240 can_fall_both = (can_fall_left && can_fall_right);
5245 if (can_fall_both &&
5246 (game.emulation != EMU_BOULDERDASH &&
5247 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5248 can_fall_left = !(can_fall_right = RND(2));
5250 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5251 started_moving = TRUE;
5255 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5257 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5260 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5261 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5262 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5263 int belt_dir = game.belt_dir[belt_nr];
5265 if ((belt_dir == MV_LEFT && left_is_free) ||
5266 (belt_dir == MV_RIGHT && right_is_free))
5269 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5272 InitMovingField(x, y, belt_dir);
5273 started_moving = TRUE;
5276 Pushed[x][y] = TRUE;
5277 Pushed[nextx][y] = TRUE;
5280 GfxAction[x][y] = ACTION_DEFAULT;
5284 MovDir[x][y] = 0; /* if element was moving, stop it */
5289 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5291 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5293 if (CAN_MOVE(element) && !started_moving)
5296 int move_pattern = element_info[element].move_pattern;
5301 if (MovDir[x][y] == MV_NO_MOVING)
5303 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5304 x, y, element, element_info[element].token_name);
5305 printf("StartMoving(): This should never happen!\n");
5310 Moving2Blocked(x, y, &newx, &newy);
5313 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5316 if ((element == EL_SATELLITE ||
5317 element == EL_BALLOON ||
5318 element == EL_SPRING)
5319 && JustBeingPushed(x, y))
5326 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5327 CheckCollision[x][y] && IN_LEV_FIELD_AND_NOT_FREE(newx, newy))
5329 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5330 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5331 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5335 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5336 element, element_info[element].token_name,
5337 WasJustMoving[x][y],
5338 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5339 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5340 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5341 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5345 WasJustMoving[x][y] = 0;
5348 CheckCollision[x][y] = 0;
5350 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5353 if (Feld[x][y] != element) /* element has changed */
5355 element = Feld[x][y];
5356 move_pattern = element_info[element].move_pattern;
5358 if (!CAN_MOVE(element))
5362 if (Feld[x][y] != element) /* element has changed */
5370 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5371 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5373 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5375 Moving2Blocked(x, y, &newx, &newy);
5376 if (Feld[newx][newy] == EL_BLOCKED)
5377 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5383 if (FrameCounter < 1 && x == 0 && y == 29)
5384 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5387 if (!MovDelay[x][y]) /* start new movement phase */
5389 /* all objects that can change their move direction after each step
5390 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5392 if (element != EL_YAMYAM &&
5393 element != EL_DARK_YAMYAM &&
5394 element != EL_PACMAN &&
5395 !(move_pattern & MV_ANY_DIRECTION) &&
5396 move_pattern != MV_TURNING_LEFT &&
5397 move_pattern != MV_TURNING_RIGHT &&
5398 move_pattern != MV_TURNING_LEFT_RIGHT &&
5399 move_pattern != MV_TURNING_RIGHT_LEFT &&
5400 move_pattern != MV_TURNING_RANDOM)
5405 if (FrameCounter < 1 && x == 0 && y == 29)
5406 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5409 if (MovDelay[x][y] && (element == EL_BUG ||
5410 element == EL_SPACESHIP ||
5411 element == EL_SP_SNIKSNAK ||
5412 element == EL_SP_ELECTRON ||
5413 element == EL_MOLE))
5414 DrawLevelField(x, y);
5418 if (MovDelay[x][y]) /* wait some time before next movement */
5423 if (element == EL_YAMYAM)
5426 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5427 DrawLevelElementAnimation(x, y, element);
5431 if (MovDelay[x][y]) /* element still has to wait some time */
5434 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5435 ResetGfxAnimation(x, y);
5439 if (GfxAction[x][y] != ACTION_WAITING)
5440 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5442 GfxAction[x][y] = ACTION_WAITING;
5446 if (element == EL_ROBOT ||
5448 element == EL_PACMAN ||
5450 element == EL_YAMYAM ||
5451 element == EL_DARK_YAMYAM)
5454 DrawLevelElementAnimation(x, y, element);
5456 DrawLevelElementAnimationIfNeeded(x, y, element);
5458 PlayLevelSoundAction(x, y, ACTION_WAITING);
5460 else if (element == EL_SP_ELECTRON)
5461 DrawLevelElementAnimationIfNeeded(x, y, element);
5462 else if (element == EL_DRAGON)
5465 int dir = MovDir[x][y];
5466 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5467 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5468 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5469 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5470 dir == MV_UP ? IMG_FLAMES_1_UP :
5471 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5472 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5475 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5478 GfxAction[x][y] = ACTION_ATTACKING;
5480 if (IS_PLAYER(x, y))
5481 DrawPlayerField(x, y);
5483 DrawLevelField(x, y);
5485 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5487 for (i = 1; i <= 3; i++)
5489 int xx = x + i * dx;
5490 int yy = y + i * dy;
5491 int sx = SCREENX(xx);
5492 int sy = SCREENY(yy);
5493 int flame_graphic = graphic + (i - 1);
5495 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5500 int flamed = MovingOrBlocked2Element(xx, yy);
5504 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5506 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5507 RemoveMovingField(xx, yy);
5509 RemoveField(xx, yy);
5511 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5514 RemoveMovingField(xx, yy);
5518 if (ChangeDelay[xx][yy])
5519 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5520 Feld[xx][yy] == EL_BLOCKED));
5524 ChangeDelay[xx][yy] = 0;
5526 Feld[xx][yy] = EL_FLAMES;
5527 if (IN_SCR_FIELD(sx, sy))
5529 DrawLevelFieldCrumbledSand(xx, yy);
5530 DrawGraphic(sx, sy, flame_graphic, frame);
5535 if (Feld[xx][yy] == EL_FLAMES)
5536 Feld[xx][yy] = EL_EMPTY;
5537 DrawLevelField(xx, yy);
5542 if (MovDelay[x][y]) /* element still has to wait some time */
5544 PlayLevelSoundAction(x, y, ACTION_WAITING);
5550 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5551 for all other elements GfxAction will be set by InitMovingField() */
5552 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5553 GfxAction[x][y] = ACTION_MOVING;
5557 /* now make next step */
5559 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5561 if (DONT_COLLIDE_WITH(element) &&
5562 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5563 !PLAYER_ENEMY_PROTECTED(newx, newy))
5566 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5570 /* player killed by element which is deadly when colliding with */
5572 KillHero(PLAYERINFO(newx, newy));
5579 else if (CAN_MOVE_INTO_ACID(element) &&
5580 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5581 (MovDir[x][y] == MV_DOWN ||
5582 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5584 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5585 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5589 else if ((element == EL_PENGUIN ||
5590 element == EL_ROBOT ||
5591 element == EL_SATELLITE ||
5592 element == EL_BALLOON ||
5593 IS_CUSTOM_ELEMENT(element)) &&
5594 IN_LEV_FIELD(newx, newy) &&
5595 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5598 SplashAcid(newx, newy);
5599 Store[x][y] = EL_ACID;
5601 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5603 if (Feld[newx][newy] == EL_EXIT_OPEN)
5607 DrawLevelField(x, y);
5609 Feld[x][y] = EL_EMPTY;
5610 DrawLevelField(x, y);
5613 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5614 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5615 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5617 local_player->friends_still_needed--;
5618 if (!local_player->friends_still_needed &&
5619 !local_player->GameOver && AllPlayersGone)
5620 local_player->LevelSolved = local_player->GameOver = TRUE;
5624 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5626 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5627 DrawLevelField(newx, newy);
5629 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5631 else if (!IS_FREE(newx, newy))
5633 GfxAction[x][y] = ACTION_WAITING;
5635 if (IS_PLAYER(x, y))
5636 DrawPlayerField(x, y);
5638 DrawLevelField(x, y);
5643 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5645 if (IS_FOOD_PIG(Feld[newx][newy]))
5647 if (IS_MOVING(newx, newy))
5648 RemoveMovingField(newx, newy);
5651 Feld[newx][newy] = EL_EMPTY;
5652 DrawLevelField(newx, newy);
5655 PlayLevelSound(x, y, SND_PIG_DIGGING);
5657 else if (!IS_FREE(newx, newy))
5659 if (IS_PLAYER(x, y))
5660 DrawPlayerField(x, y);
5662 DrawLevelField(x, y);
5671 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5674 else if (IS_CUSTOM_ELEMENT(element) &&
5675 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5679 !IS_FREE(newx, newy)
5684 int new_element = Feld[newx][newy];
5687 printf("::: '%s' digs '%s' [%d]\n",
5688 element_info[element].token_name,
5689 element_info[Feld[newx][newy]].token_name,
5690 StorePlayer[newx][newy]);
5693 if (!IS_FREE(newx, newy))
5695 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5696 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5699 /* no element can dig solid indestructible elements */
5700 if (IS_INDESTRUCTIBLE(new_element) &&
5701 !IS_DIGGABLE(new_element) &&
5702 !IS_COLLECTIBLE(new_element))
5705 if (AmoebaNr[newx][newy] &&
5706 (new_element == EL_AMOEBA_FULL ||
5707 new_element == EL_BD_AMOEBA ||
5708 new_element == EL_AMOEBA_GROWING))
5710 AmoebaCnt[AmoebaNr[newx][newy]]--;
5711 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5714 if (IS_MOVING(newx, newy))
5715 RemoveMovingField(newx, newy);
5718 RemoveField(newx, newy);
5719 DrawLevelField(newx, newy);
5722 /* if digged element was about to explode, prevent the explosion */
5723 ExplodeField[newx][newy] = EX_TYPE_NONE;
5725 PlayLevelSoundAction(x, y, action);
5730 Store[newx][newy] = EL_EMPTY;
5731 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5732 Store[newx][newy] = element_info[element].move_leave_element;
5734 Store[newx][newy] = EL_EMPTY;
5735 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5736 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5737 Store[newx][newy] = element_info[element].move_leave_element;
5740 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5741 element_info[element].can_leave_element = TRUE;
5744 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5746 RunnerVisit[x][y] = FrameCounter;
5747 PlayerVisit[x][y] /= 8; /* expire player visit path */
5753 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5755 if (!IS_FREE(newx, newy))
5757 if (IS_PLAYER(x, y))
5758 DrawPlayerField(x, y);
5760 DrawLevelField(x, y);
5766 boolean wanna_flame = !RND(10);
5767 int dx = newx - x, dy = newy - y;
5768 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5769 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5770 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5771 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5772 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5773 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5776 IS_CLASSIC_ENEMY(element1) ||
5777 IS_CLASSIC_ENEMY(element2)) &&
5778 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5779 element1 != EL_FLAMES && element2 != EL_FLAMES)
5782 ResetGfxAnimation(x, y);
5783 GfxAction[x][y] = ACTION_ATTACKING;
5786 if (IS_PLAYER(x, y))
5787 DrawPlayerField(x, y);
5789 DrawLevelField(x, y);
5791 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5793 MovDelay[x][y] = 50;
5797 RemoveField(newx, newy);
5799 Feld[newx][newy] = EL_FLAMES;
5800 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5803 RemoveField(newx1, newy1);
5805 Feld[newx1][newy1] = EL_FLAMES;
5807 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5810 RemoveField(newx2, newy2);
5812 Feld[newx2][newy2] = EL_FLAMES;
5819 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5820 Feld[newx][newy] == EL_DIAMOND)
5822 if (IS_MOVING(newx, newy))
5823 RemoveMovingField(newx, newy);
5826 Feld[newx][newy] = EL_EMPTY;
5827 DrawLevelField(newx, newy);
5830 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5832 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5833 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5835 if (AmoebaNr[newx][newy])
5837 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5838 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5839 Feld[newx][newy] == EL_BD_AMOEBA)
5840 AmoebaCnt[AmoebaNr[newx][newy]]--;
5845 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5847 if (IS_MOVING(newx, newy))
5850 RemoveMovingField(newx, newy);
5854 Feld[newx][newy] = EL_EMPTY;
5855 DrawLevelField(newx, newy);
5858 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5860 else if ((element == EL_PACMAN || element == EL_MOLE)
5861 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5863 if (AmoebaNr[newx][newy])
5865 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5866 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5867 Feld[newx][newy] == EL_BD_AMOEBA)
5868 AmoebaCnt[AmoebaNr[newx][newy]]--;
5871 if (element == EL_MOLE)
5873 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5874 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5876 ResetGfxAnimation(x, y);
5877 GfxAction[x][y] = ACTION_DIGGING;
5878 DrawLevelField(x, y);
5880 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5882 return; /* wait for shrinking amoeba */
5884 else /* element == EL_PACMAN */
5886 Feld[newx][newy] = EL_EMPTY;
5887 DrawLevelField(newx, newy);
5888 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5891 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5892 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5893 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5895 /* wait for shrinking amoeba to completely disappear */
5898 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5900 /* object was running against a wall */
5905 if (move_pattern & MV_ANY_DIRECTION &&
5906 move_pattern == MovDir[x][y])
5908 int blocking_element =
5909 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5912 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
5913 element_info[element].token_name,
5914 element_info[blocking_element].token_name,
5918 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5921 element = Feld[x][y]; /* element might have changed */
5926 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5927 DrawLevelElementAnimation(x, y, element);
5929 if (element == EL_BUG ||
5930 element == EL_SPACESHIP ||
5931 element == EL_SP_SNIKSNAK)
5932 DrawLevelField(x, y);
5933 else if (element == EL_MOLE)
5934 DrawLevelField(x, y);
5935 else if (element == EL_BD_BUTTERFLY ||
5936 element == EL_BD_FIREFLY)
5937 DrawLevelElementAnimationIfNeeded(x, y, element);
5938 else if (element == EL_SATELLITE)
5939 DrawLevelElementAnimationIfNeeded(x, y, element);
5940 else if (element == EL_SP_ELECTRON)
5941 DrawLevelElementAnimationIfNeeded(x, y, element);
5944 if (DONT_TOUCH(element))
5945 TestIfBadThingTouchesHero(x, y);
5948 PlayLevelSoundAction(x, y, ACTION_WAITING);
5954 InitMovingField(x, y, MovDir[x][y]);
5956 PlayLevelSoundAction(x, y, ACTION_MOVING);
5960 ContinueMoving(x, y);
5963 void ContinueMoving(int x, int y)
5965 int element = Feld[x][y];
5966 int stored = Store[x][y];
5967 struct ElementInfo *ei = &element_info[element];
5968 int direction = MovDir[x][y];
5969 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5970 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5971 int newx = x + dx, newy = y + dy;
5973 int nextx = newx + dx, nexty = newy + dy;
5976 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5977 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5979 boolean pushed_by_player = Pushed[x][y];
5982 MovPos[x][y] += getElementMoveStepsize(x, y);
5985 if (pushed_by_player && IS_PLAYER(x, y))
5987 /* special case: moving object pushed by player */
5988 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5991 if (pushed_by_player) /* special case: moving object pushed by player */
5992 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5995 if (ABS(MovPos[x][y]) < TILEX)
5997 DrawLevelField(x, y);
5999 return; /* element is still moving */
6002 /* element reached destination field */
6004 Feld[x][y] = EL_EMPTY;
6005 Feld[newx][newy] = element;
6006 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6009 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6011 element = Feld[newx][newy] = EL_ACID;
6014 else if (element == EL_MOLE)
6016 Feld[x][y] = EL_SAND;
6018 DrawLevelFieldCrumbledSandNeighbours(x, y);
6020 else if (element == EL_QUICKSAND_FILLING)
6022 element = Feld[newx][newy] = get_next_element(element);
6023 Store[newx][newy] = Store[x][y];
6025 else if (element == EL_QUICKSAND_EMPTYING)
6027 Feld[x][y] = get_next_element(element);
6028 element = Feld[newx][newy] = Store[x][y];
6030 else if (element == EL_MAGIC_WALL_FILLING)
6032 element = Feld[newx][newy] = get_next_element(element);
6033 if (!game.magic_wall_active)
6034 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6035 Store[newx][newy] = Store[x][y];
6037 else if (element == EL_MAGIC_WALL_EMPTYING)
6039 Feld[x][y] = get_next_element(element);
6040 if (!game.magic_wall_active)
6041 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6042 element = Feld[newx][newy] = Store[x][y];
6044 else if (element == EL_BD_MAGIC_WALL_FILLING)
6046 element = Feld[newx][newy] = get_next_element(element);
6047 if (!game.magic_wall_active)
6048 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6049 Store[newx][newy] = Store[x][y];
6051 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6053 Feld[x][y] = get_next_element(element);
6054 if (!game.magic_wall_active)
6055 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6056 element = Feld[newx][newy] = Store[x][y];
6058 else if (element == EL_AMOEBA_DROPPING)
6060 Feld[x][y] = get_next_element(element);
6061 element = Feld[newx][newy] = Store[x][y];
6063 else if (element == EL_SOKOBAN_OBJECT)
6066 Feld[x][y] = Back[x][y];
6068 if (Back[newx][newy])
6069 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6071 Back[x][y] = Back[newx][newy] = 0;
6074 else if (Store[x][y] == EL_ACID)
6076 element = Feld[newx][newy] = EL_ACID;
6080 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6081 ei->move_leave_element != EL_EMPTY &&
6082 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6083 Store[x][y] != EL_EMPTY))
6085 /* some elements can leave other elements behind after moving */
6087 Feld[x][y] = ei->move_leave_element;
6088 InitField(x, y, FALSE);
6090 if (GFX_CRUMBLED(Feld[x][y]))
6091 DrawLevelFieldCrumbledSandNeighbours(x, y);
6095 Store[x][y] = EL_EMPTY;
6096 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6097 MovDelay[newx][newy] = 0;
6099 if (CAN_CHANGE(element))
6101 /* copy element change control values to new field */
6102 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6103 ChangePage[newx][newy] = ChangePage[x][y];
6104 Changed[newx][newy] = Changed[x][y];
6105 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6108 ChangeDelay[x][y] = 0;
6109 ChangePage[x][y] = -1;
6110 Changed[x][y] = CE_BITMASK_DEFAULT;
6111 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6113 /* copy animation control values to new field */
6114 GfxFrame[newx][newy] = GfxFrame[x][y];
6115 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6116 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6117 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6119 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6121 ResetGfxAnimation(x, y); /* reset animation values for old field */
6124 /* some elements can leave other elements behind after moving */
6126 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6127 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6128 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6130 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6131 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6135 int move_leave_element = ei->move_leave_element;
6137 Feld[x][y] = move_leave_element;
6138 InitField(x, y, FALSE);
6140 if (GFX_CRUMBLED(Feld[x][y]))
6141 DrawLevelFieldCrumbledSandNeighbours(x, y);
6143 if (ELEM_IS_PLAYER(move_leave_element))
6144 RelocatePlayer(x, y, move_leave_element);
6149 /* some elements can leave other elements behind after moving */
6150 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6151 ei->move_leave_element != EL_EMPTY &&
6152 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6153 ei->can_leave_element_last))
6155 Feld[x][y] = ei->move_leave_element;
6156 InitField(x, y, FALSE);
6158 if (GFX_CRUMBLED(Feld[x][y]))
6159 DrawLevelFieldCrumbledSandNeighbours(x, y);
6162 ei->can_leave_element_last = ei->can_leave_element;
6163 ei->can_leave_element = FALSE;
6167 /* 2.1.1 (does not work correctly for spring) */
6168 if (!CAN_MOVE(element))
6169 MovDir[newx][newy] = 0;
6173 /* (does not work for falling objects that slide horizontally) */
6174 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6175 MovDir[newx][newy] = 0;
6178 if (!CAN_MOVE(element) ||
6179 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6180 MovDir[newx][newy] = 0;
6184 if (!CAN_MOVE(element) ||
6185 (CAN_FALL(element) && direction == MV_DOWN))
6186 GfxDir[x][y] = MovDir[newx][newy] = 0;
6188 if (!CAN_MOVE(element) ||
6189 (CAN_FALL(element) && direction == MV_DOWN &&
6190 (element == EL_SPRING ||
6191 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6192 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6193 GfxDir[x][y] = MovDir[newx][newy] = 0;
6199 DrawLevelField(x, y);
6200 DrawLevelField(newx, newy);
6202 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6204 /* prevent pushed element from moving on in pushed direction */
6205 if (pushed_by_player && CAN_MOVE(element) &&
6206 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6207 !(element_info[element].move_pattern & direction))
6208 TurnRound(newx, newy);
6211 /* prevent elements on conveyor belt from moving on in last direction */
6212 if (pushed_by_conveyor && CAN_FALL(element) &&
6213 direction & MV_HORIZONTAL)
6216 if (CAN_MOVE(element))
6217 InitMovDir(newx, newy);
6219 MovDir[newx][newy] = 0;
6221 MovDir[newx][newy] = 0;
6226 if (!pushed_by_player)
6228 int nextx = newx + dx, nexty = newy + dy;
6229 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6231 WasJustMoving[newx][newy] = 3;
6233 if (CAN_FALL(element) && direction == MV_DOWN)
6234 WasJustFalling[newx][newy] = 3;
6236 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6237 CheckCollision[newx][newy] = 2;
6240 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6242 TestIfBadThingTouchesHero(newx, newy);
6243 TestIfBadThingTouchesFriend(newx, newy);
6245 if (!IS_CUSTOM_ELEMENT(element))
6246 TestIfBadThingTouchesOtherBadThing(newx, newy);
6248 else if (element == EL_PENGUIN)
6249 TestIfFriendTouchesBadThing(newx, newy);
6251 if (CAN_FALL(element) && direction == MV_DOWN &&
6252 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6256 if (pushed_by_player)
6258 static int trigger_sides[4] =
6260 CH_SIDE_RIGHT, /* moving left */
6261 CH_SIDE_LEFT, /* moving right */
6262 CH_SIDE_BOTTOM, /* moving up */
6263 CH_SIDE_TOP, /* moving down */
6265 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6266 struct PlayerInfo *player = PLAYERINFO(x, y);
6268 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6269 player->index_bit, dig_side);
6270 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6271 player->index_bit, dig_side);
6276 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6280 if (ChangePage[newx][newy] != -1) /* delayed change */
6281 ChangeElement(newx, newy, ChangePage[newx][newy]);
6286 TestIfElementHitsCustomElement(newx, newy, direction);
6290 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6292 int hitting_element = Feld[newx][newy];
6294 /* !!! fix side (direction) orientation here and elsewhere !!! */
6295 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6299 if (IN_LEV_FIELD(nextx, nexty))
6301 int opposite_direction = MV_DIR_OPPOSITE(direction);
6302 int hitting_side = direction;
6303 int touched_side = opposite_direction;
6304 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6305 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6306 MovDir[nextx][nexty] != direction ||
6307 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6313 CheckElementChangeBySide(nextx, nexty, touched_element,
6314 CE_HIT_BY_SOMETHING, opposite_direction);
6316 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6317 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6319 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6321 struct ElementChangeInfo *change =
6322 &element_info[hitting_element].change_page[i];
6324 if (change->can_change &&
6325 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6326 change->trigger_side & touched_side &&
6327 change->trigger_element == touched_element)
6329 CheckElementChangeByPage(newx, newy, hitting_element,
6330 touched_element, CE_OTHER_IS_HITTING,i);
6336 if (IS_CUSTOM_ELEMENT(touched_element) &&
6337 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6339 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6341 struct ElementChangeInfo *change =
6342 &element_info[touched_element].change_page[i];
6344 if (change->can_change &&
6345 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6346 change->trigger_side & hitting_side &&
6347 change->trigger_element == hitting_element)
6349 CheckElementChangeByPage(nextx, nexty, touched_element,
6350 hitting_element, CE_OTHER_GETS_HIT, i);
6361 TestIfPlayerTouchesCustomElement(newx, newy);
6362 TestIfElementTouchesCustomElement(newx, newy);
6365 int AmoebeNachbarNr(int ax, int ay)
6368 int element = Feld[ax][ay];
6370 static int xy[4][2] =
6378 for (i = 0; i < NUM_DIRECTIONS; i++)
6380 int x = ax + xy[i][0];
6381 int y = ay + xy[i][1];
6383 if (!IN_LEV_FIELD(x, y))
6386 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6387 group_nr = AmoebaNr[x][y];
6393 void AmoebenVereinigen(int ax, int ay)
6395 int i, x, y, xx, yy;
6396 int new_group_nr = AmoebaNr[ax][ay];
6397 static int xy[4][2] =
6405 if (new_group_nr == 0)
6408 for (i = 0; i < NUM_DIRECTIONS; i++)
6413 if (!IN_LEV_FIELD(x, y))
6416 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6417 Feld[x][y] == EL_BD_AMOEBA ||
6418 Feld[x][y] == EL_AMOEBA_DEAD) &&
6419 AmoebaNr[x][y] != new_group_nr)
6421 int old_group_nr = AmoebaNr[x][y];
6423 if (old_group_nr == 0)
6426 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6427 AmoebaCnt[old_group_nr] = 0;
6428 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6429 AmoebaCnt2[old_group_nr] = 0;
6431 for (yy = 0; yy < lev_fieldy; yy++)
6433 for (xx = 0; xx < lev_fieldx; xx++)
6435 if (AmoebaNr[xx][yy] == old_group_nr)
6436 AmoebaNr[xx][yy] = new_group_nr;
6443 void AmoebeUmwandeln(int ax, int ay)
6447 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6449 int group_nr = AmoebaNr[ax][ay];
6454 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6455 printf("AmoebeUmwandeln(): This should never happen!\n");
6460 for (y = 0; y < lev_fieldy; y++)
6462 for (x = 0; x < lev_fieldx; x++)
6464 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6467 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6471 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6472 SND_AMOEBA_TURNING_TO_GEM :
6473 SND_AMOEBA_TURNING_TO_ROCK));
6478 static int xy[4][2] =
6486 for (i = 0; i < NUM_DIRECTIONS; i++)
6491 if (!IN_LEV_FIELD(x, y))
6494 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6496 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6497 SND_AMOEBA_TURNING_TO_GEM :
6498 SND_AMOEBA_TURNING_TO_ROCK));
6505 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6508 int group_nr = AmoebaNr[ax][ay];
6509 boolean done = FALSE;
6514 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6515 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6520 for (y = 0; y < lev_fieldy; y++)
6522 for (x = 0; x < lev_fieldx; x++)
6524 if (AmoebaNr[x][y] == group_nr &&
6525 (Feld[x][y] == EL_AMOEBA_DEAD ||
6526 Feld[x][y] == EL_BD_AMOEBA ||
6527 Feld[x][y] == EL_AMOEBA_GROWING))
6530 Feld[x][y] = new_element;
6531 InitField(x, y, FALSE);
6532 DrawLevelField(x, y);
6539 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6540 SND_BD_AMOEBA_TURNING_TO_ROCK :
6541 SND_BD_AMOEBA_TURNING_TO_GEM));
6544 void AmoebeWaechst(int x, int y)
6546 static unsigned long sound_delay = 0;
6547 static unsigned long sound_delay_value = 0;
6549 if (!MovDelay[x][y]) /* start new growing cycle */
6553 if (DelayReached(&sound_delay, sound_delay_value))
6556 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6558 if (Store[x][y] == EL_BD_AMOEBA)
6559 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6561 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6563 sound_delay_value = 30;
6567 if (MovDelay[x][y]) /* wait some time before growing bigger */
6570 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6572 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6573 6 - MovDelay[x][y]);
6575 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6578 if (!MovDelay[x][y])
6580 Feld[x][y] = Store[x][y];
6582 DrawLevelField(x, y);
6587 void AmoebaDisappearing(int x, int y)
6589 static unsigned long sound_delay = 0;
6590 static unsigned long sound_delay_value = 0;
6592 if (!MovDelay[x][y]) /* start new shrinking cycle */
6596 if (DelayReached(&sound_delay, sound_delay_value))
6597 sound_delay_value = 30;
6600 if (MovDelay[x][y]) /* wait some time before shrinking */
6603 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6605 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6606 6 - MovDelay[x][y]);
6608 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6611 if (!MovDelay[x][y])
6613 Feld[x][y] = EL_EMPTY;
6614 DrawLevelField(x, y);
6616 /* don't let mole enter this field in this cycle;
6617 (give priority to objects falling to this field from above) */
6623 void AmoebeAbleger(int ax, int ay)
6626 int element = Feld[ax][ay];
6627 int graphic = el2img(element);
6628 int newax = ax, neway = ay;
6629 static int xy[4][2] =
6637 if (!level.amoeba_speed)
6639 Feld[ax][ay] = EL_AMOEBA_DEAD;
6640 DrawLevelField(ax, ay);
6644 if (IS_ANIMATED(graphic))
6645 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6647 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6648 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6650 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6653 if (MovDelay[ax][ay])
6657 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6660 int x = ax + xy[start][0];
6661 int y = ay + xy[start][1];
6663 if (!IN_LEV_FIELD(x, y))
6667 if (IS_FREE(x, y) ||
6668 CAN_GROW_INTO(Feld[x][y]) ||
6669 Feld[x][y] == EL_QUICKSAND_EMPTY)
6675 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6676 if (IS_FREE(x, y) ||
6677 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6684 if (newax == ax && neway == ay)
6687 else /* normal or "filled" (BD style) amoeba */
6690 boolean waiting_for_player = FALSE;
6692 for (i = 0; i < NUM_DIRECTIONS; i++)
6694 int j = (start + i) % 4;
6695 int x = ax + xy[j][0];
6696 int y = ay + xy[j][1];
6698 if (!IN_LEV_FIELD(x, y))
6702 if (IS_FREE(x, y) ||
6703 CAN_GROW_INTO(Feld[x][y]) ||
6704 Feld[x][y] == EL_QUICKSAND_EMPTY)
6711 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6712 if (IS_FREE(x, y) ||
6713 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6720 else if (IS_PLAYER(x, y))
6721 waiting_for_player = TRUE;
6724 if (newax == ax && neway == ay) /* amoeba cannot grow */
6727 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6729 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6732 Feld[ax][ay] = EL_AMOEBA_DEAD;
6733 DrawLevelField(ax, ay);
6734 AmoebaCnt[AmoebaNr[ax][ay]]--;
6736 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6738 if (element == EL_AMOEBA_FULL)
6739 AmoebeUmwandeln(ax, ay);
6740 else if (element == EL_BD_AMOEBA)
6741 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6746 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6748 /* amoeba gets larger by growing in some direction */
6750 int new_group_nr = AmoebaNr[ax][ay];
6753 if (new_group_nr == 0)
6755 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6756 printf("AmoebeAbleger(): This should never happen!\n");
6761 AmoebaNr[newax][neway] = new_group_nr;
6762 AmoebaCnt[new_group_nr]++;
6763 AmoebaCnt2[new_group_nr]++;
6765 /* if amoeba touches other amoeba(s) after growing, unify them */
6766 AmoebenVereinigen(newax, neway);
6768 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6770 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6776 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6777 (neway == lev_fieldy - 1 && newax != ax))
6779 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6780 Store[newax][neway] = element;
6782 else if (neway == ay)
6784 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6786 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6788 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6793 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6794 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6795 Store[ax][ay] = EL_AMOEBA_DROP;
6796 ContinueMoving(ax, ay);
6800 DrawLevelField(newax, neway);
6803 void Life(int ax, int ay)
6806 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6808 int element = Feld[ax][ay];
6809 int graphic = el2img(element);
6810 boolean changed = FALSE;
6812 if (IS_ANIMATED(graphic))
6813 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6818 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6819 MovDelay[ax][ay] = life_time;
6821 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6824 if (MovDelay[ax][ay])
6828 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6830 int xx = ax+x1, yy = ay+y1;
6833 if (!IN_LEV_FIELD(xx, yy))
6836 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6838 int x = xx+x2, y = yy+y2;
6840 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6843 if (((Feld[x][y] == element ||
6844 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6846 (IS_FREE(x, y) && Stop[x][y]))
6850 if (xx == ax && yy == ay) /* field in the middle */
6852 if (nachbarn < life[0] || nachbarn > life[1])
6854 Feld[xx][yy] = EL_EMPTY;
6856 DrawLevelField(xx, yy);
6857 Stop[xx][yy] = TRUE;
6862 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6863 { /* free border field */
6864 if (nachbarn >= life[2] && nachbarn <= life[3])
6866 Feld[xx][yy] = element;
6867 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6869 DrawLevelField(xx, yy);
6870 Stop[xx][yy] = TRUE;
6875 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6876 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6877 { /* free border field */
6878 if (nachbarn >= life[2] && nachbarn <= life[3])
6880 Feld[xx][yy] = element;
6881 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6883 DrawLevelField(xx, yy);
6884 Stop[xx][yy] = TRUE;
6892 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6893 SND_GAME_OF_LIFE_GROWING);
6896 static void InitRobotWheel(int x, int y)
6898 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6901 static void RunRobotWheel(int x, int y)
6903 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6906 static void StopRobotWheel(int x, int y)
6908 if (ZX == x && ZY == y)
6912 static void InitTimegateWheel(int x, int y)
6915 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6917 /* another brainless, "type style" bug ... :-( */
6918 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6922 static void RunTimegateWheel(int x, int y)
6924 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6927 void CheckExit(int x, int y)
6929 if (local_player->gems_still_needed > 0 ||
6930 local_player->sokobanfields_still_needed > 0 ||
6931 local_player->lights_still_needed > 0)
6933 int element = Feld[x][y];
6934 int graphic = el2img(element);
6936 if (IS_ANIMATED(graphic))
6937 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6942 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6945 Feld[x][y] = EL_EXIT_OPENING;
6947 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6950 void CheckExitSP(int x, int y)
6952 if (local_player->gems_still_needed > 0)
6954 int element = Feld[x][y];
6955 int graphic = el2img(element);
6957 if (IS_ANIMATED(graphic))
6958 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6963 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6966 Feld[x][y] = EL_SP_EXIT_OPENING;
6968 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6971 static void CloseAllOpenTimegates()
6975 for (y = 0; y < lev_fieldy; y++)
6977 for (x = 0; x < lev_fieldx; x++)
6979 int element = Feld[x][y];
6981 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6983 Feld[x][y] = EL_TIMEGATE_CLOSING;
6985 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6987 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6994 void EdelsteinFunkeln(int x, int y)
6996 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6999 if (Feld[x][y] == EL_BD_DIAMOND)
7002 if (MovDelay[x][y] == 0) /* next animation frame */
7003 MovDelay[x][y] = 11 * !SimpleRND(500);
7005 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7009 if (setup.direct_draw && MovDelay[x][y])
7010 SetDrawtoField(DRAW_BUFFERED);
7012 DrawLevelElementAnimation(x, y, Feld[x][y]);
7014 if (MovDelay[x][y] != 0)
7016 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7017 10 - MovDelay[x][y]);
7019 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7021 if (setup.direct_draw)
7025 dest_x = FX + SCREENX(x) * TILEX;
7026 dest_y = FY + SCREENY(y) * TILEY;
7028 BlitBitmap(drawto_field, window,
7029 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7030 SetDrawtoField(DRAW_DIRECT);
7036 void MauerWaechst(int x, int y)
7040 if (!MovDelay[x][y]) /* next animation frame */
7041 MovDelay[x][y] = 3 * delay;
7043 if (MovDelay[x][y]) /* wait some time before next frame */
7047 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7049 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7050 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7052 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7055 if (!MovDelay[x][y])
7057 if (MovDir[x][y] == MV_LEFT)
7059 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7060 DrawLevelField(x - 1, y);
7062 else if (MovDir[x][y] == MV_RIGHT)
7064 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7065 DrawLevelField(x + 1, y);
7067 else if (MovDir[x][y] == MV_UP)
7069 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7070 DrawLevelField(x, y - 1);
7074 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7075 DrawLevelField(x, y + 1);
7078 Feld[x][y] = Store[x][y];
7080 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7081 DrawLevelField(x, y);
7086 void MauerAbleger(int ax, int ay)
7088 int element = Feld[ax][ay];
7089 int graphic = el2img(element);
7090 boolean oben_frei = FALSE, unten_frei = FALSE;
7091 boolean links_frei = FALSE, rechts_frei = FALSE;
7092 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7093 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7094 boolean new_wall = FALSE;
7096 if (IS_ANIMATED(graphic))
7097 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7099 if (!MovDelay[ax][ay]) /* start building new wall */
7100 MovDelay[ax][ay] = 6;
7102 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7105 if (MovDelay[ax][ay])
7109 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7111 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7113 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7115 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7118 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7119 element == EL_EXPANDABLE_WALL_ANY)
7123 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7124 Store[ax][ay-1] = element;
7125 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7126 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7127 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7128 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7133 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7134 Store[ax][ay+1] = element;
7135 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7136 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7137 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7138 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7143 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7144 element == EL_EXPANDABLE_WALL_ANY ||
7145 element == EL_EXPANDABLE_WALL)
7149 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7150 Store[ax-1][ay] = element;
7151 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7152 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7153 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7154 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7160 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7161 Store[ax+1][ay] = element;
7162 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7163 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7164 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7165 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7170 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7171 DrawLevelField(ax, ay);
7173 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7175 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7176 unten_massiv = TRUE;
7177 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7178 links_massiv = TRUE;
7179 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7180 rechts_massiv = TRUE;
7182 if (((oben_massiv && unten_massiv) ||
7183 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7184 element == EL_EXPANDABLE_WALL) &&
7185 ((links_massiv && rechts_massiv) ||
7186 element == EL_EXPANDABLE_WALL_VERTICAL))
7187 Feld[ax][ay] = EL_WALL;
7191 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7193 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7197 void CheckForDragon(int x, int y)
7200 boolean dragon_found = FALSE;
7201 static int xy[4][2] =
7209 for (i = 0; i < NUM_DIRECTIONS; i++)
7211 for (j = 0; j < 4; j++)
7213 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7215 if (IN_LEV_FIELD(xx, yy) &&
7216 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7218 if (Feld[xx][yy] == EL_DRAGON)
7219 dragon_found = TRUE;
7228 for (i = 0; i < NUM_DIRECTIONS; i++)
7230 for (j = 0; j < 3; j++)
7232 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7234 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7236 Feld[xx][yy] = EL_EMPTY;
7237 DrawLevelField(xx, yy);
7246 static void InitBuggyBase(int x, int y)
7248 int element = Feld[x][y];
7249 int activating_delay = FRAMES_PER_SECOND / 4;
7252 (element == EL_SP_BUGGY_BASE ?
7253 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7254 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7256 element == EL_SP_BUGGY_BASE_ACTIVE ?
7257 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7260 static void WarnBuggyBase(int x, int y)
7263 static int xy[4][2] =
7271 for (i = 0; i < NUM_DIRECTIONS; i++)
7273 int xx = x + xy[i][0], yy = y + xy[i][1];
7275 if (IS_PLAYER(xx, yy))
7277 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7284 static void InitTrap(int x, int y)
7286 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7289 static void ActivateTrap(int x, int y)
7291 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7294 static void ChangeActiveTrap(int x, int y)
7296 int graphic = IMG_TRAP_ACTIVE;
7298 /* if new animation frame was drawn, correct crumbled sand border */
7299 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7300 DrawLevelFieldCrumbledSand(x, y);
7303 static void ChangeElementNowExt(int x, int y, int target_element)
7305 int previous_move_direction = MovDir[x][y];
7307 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7308 IS_WALKABLE(Feld[x][y]));
7310 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7311 IS_WALKABLE(Feld[x][y]) &&
7315 /* check if element under player changes from accessible to unaccessible
7316 (needed for special case of dropping element which then changes) */
7317 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7318 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7329 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7330 RemoveMovingField(x, y);
7334 Feld[x][y] = target_element;
7337 Feld[x][y] = target_element;
7340 ResetGfxAnimation(x, y);
7341 ResetRandomAnimationValue(x, y);
7343 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7344 MovDir[x][y] = previous_move_direction;
7347 InitField_WithBug1(x, y, FALSE);
7349 InitField(x, y, FALSE);
7350 if (CAN_MOVE(Feld[x][y]))
7354 DrawLevelField(x, y);
7356 if (GFX_CRUMBLED(Feld[x][y]))
7357 DrawLevelFieldCrumbledSandNeighbours(x, y);
7360 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7363 TestIfBadThingTouchesHero(x, y);
7364 TestIfPlayerTouchesCustomElement(x, y);
7365 TestIfElementTouchesCustomElement(x, y);
7368 if (ELEM_IS_PLAYER(target_element))
7369 RelocatePlayer(x, y, target_element);
7372 TestIfBadThingTouchesHero(x, y);
7373 TestIfPlayerTouchesCustomElement(x, y);
7374 TestIfElementTouchesCustomElement(x, y);
7378 static boolean ChangeElementNow(int x, int y, int element, int page)
7380 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7382 int old_element = Feld[x][y];
7384 /* always use default change event to prevent running into a loop */
7385 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7386 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7388 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7390 /* reset actual trigger element and player */
7391 change->actual_trigger_element = EL_EMPTY;
7392 change->actual_trigger_player = EL_PLAYER_1;
7395 /* do not change already changed elements with same change event */
7397 if (Changed[x][y] & ChangeEvent[x][y])
7404 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7407 /* !!! indirect change before direct change !!! */
7408 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7411 if (change->explode)
7418 if (change->use_target_content)
7420 boolean complete_replace = TRUE;
7421 boolean can_replace[3][3];
7424 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7427 boolean is_walkable;
7428 boolean is_diggable;
7429 boolean is_collectible;
7430 boolean is_removable;
7431 boolean is_destructible;
7432 int ex = x + xx - 1;
7433 int ey = y + yy - 1;
7434 int content_element = change->target_content[xx][yy];
7437 can_replace[xx][yy] = TRUE;
7439 if (ex == x && ey == y) /* do not check changing element itself */
7442 if (content_element == EL_EMPTY_SPACE)
7444 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7449 if (!IN_LEV_FIELD(ex, ey))
7451 can_replace[xx][yy] = FALSE;
7452 complete_replace = FALSE;
7459 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7460 e = MovingOrBlocked2Element(ex, ey);
7465 is_empty = (IS_FREE(ex, ey) ||
7466 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7467 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7468 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7470 is_empty = (IS_FREE(ex, ey) ||
7471 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7473 is_walkable = (is_empty || IS_WALKABLE(e));
7474 is_diggable = (is_empty || IS_DIGGABLE(e));
7475 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7476 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7477 is_removable = (is_diggable || is_collectible);
7479 can_replace[xx][yy] =
7480 ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7481 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7482 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7483 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7484 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7485 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7487 if (!can_replace[xx][yy])
7488 complete_replace = FALSE;
7490 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7491 IS_WALKABLE(content_element)));
7493 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7495 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7498 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7499 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7500 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7502 can_replace[xx][yy] = FALSE;
7503 complete_replace = FALSE;
7508 if (!change->only_if_complete || complete_replace)
7510 boolean something_has_changed = FALSE;
7512 if (change->only_if_complete && change->use_random_replace &&
7513 RND(100) < change->random_percentage)
7516 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7518 int ex = x + xx - 1;
7519 int ey = y + yy - 1;
7520 int content_element;
7522 if (can_replace[xx][yy] && (!change->use_random_replace ||
7523 RND(100) < change->random_percentage))
7525 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7526 RemoveMovingField(ex, ey);
7528 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7530 content_element = change->target_content[xx][yy];
7531 target_element = GET_TARGET_ELEMENT(content_element, change);
7533 ChangeElementNowExt(ex, ey, target_element);
7535 something_has_changed = TRUE;
7537 /* for symmetry reasons, freeze newly created border elements */
7538 if (ex != x || ey != y)
7539 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7543 if (something_has_changed)
7544 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7549 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7551 ChangeElementNowExt(x, y, target_element);
7553 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7557 /* !!! indirect change before direct change !!! */
7558 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7564 static void ChangeElement(int x, int y, int page)
7566 int element = MovingOrBlocked2Element(x, y);
7567 struct ElementInfo *ei = &element_info[element];
7568 struct ElementChangeInfo *change = &ei->change_page[page];
7571 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7574 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7575 x, y, element, element_info[element].token_name);
7576 printf("ChangeElement(): This should never happen!\n");
7581 /* this can happen with classic bombs on walkable, changing elements */
7582 if (!CAN_CHANGE(element))
7585 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7586 ChangeDelay[x][y] = 0;
7592 if (ChangeDelay[x][y] == 0) /* initialize element change */
7594 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7595 RND(change->delay_random * change->delay_frames)) + 1;
7597 ResetGfxAnimation(x, y);
7598 ResetRandomAnimationValue(x, y);
7600 if (change->pre_change_function)
7601 change->pre_change_function(x, y);
7604 ChangeDelay[x][y]--;
7606 if (ChangeDelay[x][y] != 0) /* continue element change */
7608 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7610 if (IS_ANIMATED(graphic))
7611 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7613 if (change->change_function)
7614 change->change_function(x, y);
7616 else /* finish element change */
7618 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7620 page = ChangePage[x][y];
7621 ChangePage[x][y] = -1;
7623 change = &ei->change_page[page];
7627 if (IS_MOVING(x, y) && !change->explode)
7629 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7632 ChangeDelay[x][y] = 1; /* try change after next move step */
7633 ChangePage[x][y] = page; /* remember page to use for change */
7638 if (ChangeElementNow(x, y, element, page))
7640 if (change->post_change_function)
7641 change->post_change_function(x, y);
7646 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7647 int trigger_element,
7654 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7656 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7659 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7661 int element = EL_CUSTOM_START + i;
7663 boolean change_element = FALSE;
7666 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7669 for (j = 0; j < element_info[element].num_change_pages; j++)
7671 struct ElementChangeInfo *change = &element_info[element].change_page[j];
7673 if (change->can_change &&
7674 change->events & CH_EVENT_BIT(trigger_event) &&
7675 change->trigger_side & trigger_side &&
7676 change->trigger_player & trigger_player &&
7677 change->trigger_page & trigger_page_bits &&
7678 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7681 if (!(change->events & CH_EVENT_BIT(trigger_event)))
7682 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7683 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7686 change_element = TRUE;
7689 change->actual_trigger_element = trigger_element;
7690 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7696 if (!change_element)
7699 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7702 if (x == lx && y == ly) /* do not change trigger element itself */
7706 if (Feld[x][y] == element)
7708 ChangeDelay[x][y] = 1;
7709 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7710 ChangeElement(x, y, page);
7718 static boolean CheckElementChangeExt(int x, int y,
7720 int trigger_element,
7726 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7729 if (Feld[x][y] == EL_BLOCKED)
7731 Blocked2Moving(x, y, &x, &y);
7732 element = Feld[x][y];
7736 if (Feld[x][y] != element) /* check if element has already changed */
7739 printf("::: %d ('%s') != %d ('%s') [%d]\n",
7740 Feld[x][y], element_info[Feld[x][y]].token_name,
7741 element, element_info[element].token_name,
7750 if (trigger_page < 0)
7752 boolean change_element = FALSE;
7755 for (i = 0; i < element_info[element].num_change_pages; i++)
7757 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7759 if (change->can_change &&
7760 change->events & CH_EVENT_BIT(trigger_event) &&
7761 change->trigger_side & trigger_side &&
7762 change->trigger_player & trigger_player)
7764 change_element = TRUE;
7767 change->actual_trigger_element = trigger_element;
7768 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7774 if (!change_element)
7779 struct ElementInfo *ei = &element_info[element];
7780 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7782 change->actual_trigger_element = trigger_element;
7783 change->actual_trigger_player = EL_PLAYER_1; /* unused */
7788 /* !!! this check misses pages with same event, but different side !!! */
7790 if (trigger_page < 0)
7791 trigger_page = element_info[element].event_page_nr[trigger_event];
7793 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7797 ChangeDelay[x][y] = 1;
7798 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7799 ChangeElement(x, y, trigger_page);
7804 static void PlayPlayerSound(struct PlayerInfo *player)
7806 int jx = player->jx, jy = player->jy;
7807 int element = player->element_nr;
7808 int last_action = player->last_action_waiting;
7809 int action = player->action_waiting;
7811 if (player->is_waiting)
7813 if (action != last_action)
7814 PlayLevelSoundElementAction(jx, jy, element, action);
7816 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7820 if (action != last_action)
7821 StopSound(element_info[element].sound[last_action]);
7823 if (last_action == ACTION_SLEEPING)
7824 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7828 static void PlayAllPlayersSound()
7832 for (i = 0; i < MAX_PLAYERS; i++)
7833 if (stored_player[i].active)
7834 PlayPlayerSound(&stored_player[i]);
7837 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7839 boolean last_waiting = player->is_waiting;
7840 int move_dir = player->MovDir;
7842 player->last_action_waiting = player->action_waiting;
7846 if (!last_waiting) /* not waiting -> waiting */
7848 player->is_waiting = TRUE;
7850 player->frame_counter_bored =
7852 game.player_boring_delay_fixed +
7853 SimpleRND(game.player_boring_delay_random);
7854 player->frame_counter_sleeping =
7856 game.player_sleeping_delay_fixed +
7857 SimpleRND(game.player_sleeping_delay_random);
7859 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7862 if (game.player_sleeping_delay_fixed +
7863 game.player_sleeping_delay_random > 0 &&
7864 player->anim_delay_counter == 0 &&
7865 player->post_delay_counter == 0 &&
7866 FrameCounter >= player->frame_counter_sleeping)
7867 player->is_sleeping = TRUE;
7868 else if (game.player_boring_delay_fixed +
7869 game.player_boring_delay_random > 0 &&
7870 FrameCounter >= player->frame_counter_bored)
7871 player->is_bored = TRUE;
7873 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7874 player->is_bored ? ACTION_BORING :
7877 if (player->is_sleeping)
7879 if (player->num_special_action_sleeping > 0)
7881 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7883 int last_special_action = player->special_action_sleeping;
7884 int num_special_action = player->num_special_action_sleeping;
7885 int special_action =
7886 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7887 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7888 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7889 last_special_action + 1 : ACTION_SLEEPING);
7890 int special_graphic =
7891 el_act_dir2img(player->element_nr, special_action, move_dir);
7893 player->anim_delay_counter =
7894 graphic_info[special_graphic].anim_delay_fixed +
7895 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7896 player->post_delay_counter =
7897 graphic_info[special_graphic].post_delay_fixed +
7898 SimpleRND(graphic_info[special_graphic].post_delay_random);
7900 player->special_action_sleeping = special_action;
7903 if (player->anim_delay_counter > 0)
7905 player->action_waiting = player->special_action_sleeping;
7906 player->anim_delay_counter--;
7908 else if (player->post_delay_counter > 0)
7910 player->post_delay_counter--;
7914 else if (player->is_bored)
7916 if (player->num_special_action_bored > 0)
7918 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7920 int special_action =
7921 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7922 int special_graphic =
7923 el_act_dir2img(player->element_nr, special_action, move_dir);
7925 player->anim_delay_counter =
7926 graphic_info[special_graphic].anim_delay_fixed +
7927 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7928 player->post_delay_counter =
7929 graphic_info[special_graphic].post_delay_fixed +
7930 SimpleRND(graphic_info[special_graphic].post_delay_random);
7932 player->special_action_bored = special_action;
7935 if (player->anim_delay_counter > 0)
7937 player->action_waiting = player->special_action_bored;
7938 player->anim_delay_counter--;
7940 else if (player->post_delay_counter > 0)
7942 player->post_delay_counter--;
7947 else if (last_waiting) /* waiting -> not waiting */
7949 player->is_waiting = FALSE;
7950 player->is_bored = FALSE;
7951 player->is_sleeping = FALSE;
7953 player->frame_counter_bored = -1;
7954 player->frame_counter_sleeping = -1;
7956 player->anim_delay_counter = 0;
7957 player->post_delay_counter = 0;
7959 player->action_waiting = ACTION_DEFAULT;
7961 player->special_action_bored = ACTION_DEFAULT;
7962 player->special_action_sleeping = ACTION_DEFAULT;
7967 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7970 static byte stored_player_action[MAX_PLAYERS];
7971 static int num_stored_actions = 0;
7973 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7974 int left = player_action & JOY_LEFT;
7975 int right = player_action & JOY_RIGHT;
7976 int up = player_action & JOY_UP;
7977 int down = player_action & JOY_DOWN;
7978 int button1 = player_action & JOY_BUTTON_1;
7979 int button2 = player_action & JOY_BUTTON_2;
7980 int dx = (left ? -1 : right ? 1 : 0);
7981 int dy = (up ? -1 : down ? 1 : 0);
7984 stored_player_action[player->index_nr] = 0;
7985 num_stored_actions++;
7989 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7992 if (!player->active || tape.pausing)
7996 printf("::: [%d %d %d %d] [%d %d]\n",
7997 left, right, up, down, button1, button2);
8003 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8008 if (player->MovPos == 0)
8009 CheckGravityMovement(player);
8012 snapped = SnapField(player, dx, dy);
8016 dropped = DropElement(player);
8018 moved = MovePlayer(player, dx, dy);
8021 if (tape.single_step && tape.recording && !tape.pausing)
8023 if (button1 || (dropped && !moved))
8025 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8026 SnapField(player, 0, 0); /* stop snapping */
8030 SetPlayerWaiting(player, FALSE);
8033 return player_action;
8035 stored_player_action[player->index_nr] = player_action;
8041 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8044 /* no actions for this player (no input at player's configured device) */
8046 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8047 SnapField(player, 0, 0);
8048 CheckGravityMovementWhenNotMoving(player);
8050 if (player->MovPos == 0)
8051 SetPlayerWaiting(player, TRUE);
8053 if (player->MovPos == 0) /* needed for tape.playing */
8054 player->is_moving = FALSE;
8056 player->is_dropping = FALSE;
8062 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8064 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8066 TapeRecordAction(stored_player_action);
8067 num_stored_actions = 0;
8074 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8076 static byte stored_player_action[MAX_PLAYERS];
8077 static int num_stored_actions = 0;
8078 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8079 int left = player_action & JOY_LEFT;
8080 int right = player_action & JOY_RIGHT;
8081 int up = player_action & JOY_UP;
8082 int down = player_action & JOY_DOWN;
8083 int button1 = player_action & JOY_BUTTON_1;
8084 int button2 = player_action & JOY_BUTTON_2;
8085 int dx = (left ? -1 : right ? 1 : 0);
8086 int dy = (up ? -1 : down ? 1 : 0);
8088 stored_player_action[player->index_nr] = 0;
8089 num_stored_actions++;
8091 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8093 if (!player->active || tape.pausing)
8098 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8101 snapped = SnapField(player, dx, dy);
8105 dropped = DropElement(player);
8107 moved = MovePlayer(player, dx, dy);
8110 if (tape.single_step && tape.recording && !tape.pausing)
8112 if (button1 || (dropped && !moved))
8114 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8115 SnapField(player, 0, 0); /* stop snapping */
8119 stored_player_action[player->index_nr] = player_action;
8123 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8125 /* no actions for this player (no input at player's configured device) */
8127 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8128 SnapField(player, 0, 0);
8129 CheckGravityMovementWhenNotMoving(player);
8131 if (player->MovPos == 0)
8132 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8134 if (player->MovPos == 0) /* needed for tape.playing */
8135 player->is_moving = FALSE;
8138 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8140 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8142 TapeRecordAction(stored_player_action);
8143 num_stored_actions = 0;
8150 static unsigned long action_delay = 0;
8151 unsigned long action_delay_value;
8152 int magic_wall_x = 0, magic_wall_y = 0;
8153 int i, x, y, element, graphic;
8154 byte *recorded_player_action;
8155 byte summarized_player_action = 0;
8157 byte tape_action[MAX_PLAYERS];
8160 if (game_status != GAME_MODE_PLAYING)
8163 action_delay_value =
8164 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8166 if (tape.playing && tape.warp_forward && !tape.pausing)
8167 action_delay_value = 0;
8169 /* ---------- main game synchronization point ---------- */
8171 WaitUntilDelayReached(&action_delay, action_delay_value);
8173 if (network_playing && !network_player_action_received)
8177 printf("DEBUG: try to get network player actions in time\n");
8181 #if defined(PLATFORM_UNIX)
8182 /* last chance to get network player actions without main loop delay */
8186 if (game_status != GAME_MODE_PLAYING)
8189 if (!network_player_action_received)
8193 printf("DEBUG: failed to get network player actions in time\n");
8204 printf("::: getting new tape action [%d]\n", FrameCounter);
8207 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8210 if (recorded_player_action == NULL && tape.pausing)
8215 printf("::: %d\n", stored_player[0].action);
8219 if (recorded_player_action != NULL)
8220 for (i = 0; i < MAX_PLAYERS; i++)
8221 stored_player[i].action = recorded_player_action[i];
8224 for (i = 0; i < MAX_PLAYERS; i++)
8226 summarized_player_action |= stored_player[i].action;
8228 if (!network_playing)
8229 stored_player[i].effective_action = stored_player[i].action;
8232 #if defined(PLATFORM_UNIX)
8233 if (network_playing)
8234 SendToServer_MovePlayer(summarized_player_action);
8237 if (!options.network && !setup.team_mode)
8238 local_player->effective_action = summarized_player_action;
8241 if (recorded_player_action != NULL)
8242 for (i = 0; i < MAX_PLAYERS; i++)
8243 stored_player[i].effective_action = recorded_player_action[i];
8247 for (i = 0; i < MAX_PLAYERS; i++)
8249 tape_action[i] = stored_player[i].effective_action;
8251 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8252 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8255 /* only save actions from input devices, but not programmed actions */
8257 TapeRecordAction(tape_action);
8260 for (i = 0; i < MAX_PLAYERS; i++)
8262 int actual_player_action = stored_player[i].effective_action;
8265 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8266 - rnd_equinox_tetrachloride 048
8267 - rnd_equinox_tetrachloride_ii 096
8268 - rnd_emanuel_schmieg 002
8269 - doctor_sloan_ww 001, 020
8271 if (stored_player[i].MovPos == 0)
8272 CheckGravityMovement(&stored_player[i]);
8276 /* overwrite programmed action with tape action */
8277 if (stored_player[i].programmed_action)
8278 actual_player_action = stored_player[i].programmed_action;
8282 if (stored_player[i].programmed_action)
8283 printf("::: %d\n", stored_player[i].programmed_action);
8286 if (recorded_player_action)
8289 if (stored_player[i].programmed_action &&
8290 stored_player[i].programmed_action != recorded_player_action[i])
8291 printf("::: %d: %d <-> %d\n", i,
8292 stored_player[i].programmed_action, recorded_player_action[i]);
8296 actual_player_action = recorded_player_action[i];
8301 /* overwrite tape action with programmed action */
8302 if (stored_player[i].programmed_action)
8303 actual_player_action = stored_player[i].programmed_action;
8308 printf("::: action: %d: %x [%d]\n",
8309 stored_player[i].MovPos, actual_player_action, FrameCounter);
8313 PlayerActions(&stored_player[i], actual_player_action);
8315 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8317 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8318 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8321 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8326 TapeRecordAction(tape_action);
8329 network_player_action_received = FALSE;
8331 ScrollScreen(NULL, SCROLL_GO_ON);
8337 for (i = 0; i < MAX_PLAYERS; i++)
8338 stored_player[i].Frame++;
8342 /* for downwards compatibility, the following code emulates a fixed bug that
8343 occured when pushing elements (causing elements that just made their last
8344 pushing step to already (if possible) make their first falling step in the
8345 same game frame, which is bad); this code is also needed to use the famous
8346 "spring push bug" which is used in older levels and might be wanted to be
8347 used also in newer levels, but in this case the buggy pushing code is only
8348 affecting the "spring" element and no other elements */
8351 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8353 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8356 for (i = 0; i < MAX_PLAYERS; i++)
8358 struct PlayerInfo *player = &stored_player[i];
8363 if (player->active && player->is_pushing && player->is_moving &&
8365 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8366 Feld[x][y] == EL_SPRING))
8368 if (player->active && player->is_pushing && player->is_moving &&
8372 ContinueMoving(x, y);
8374 /* continue moving after pushing (this is actually a bug) */
8375 if (!IS_MOVING(x, y))
8384 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8386 Changed[x][y] = CE_BITMASK_DEFAULT;
8387 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8390 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8392 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8393 printf("GameActions(): This should never happen!\n");
8395 ChangePage[x][y] = -1;
8400 if (WasJustMoving[x][y] > 0)
8401 WasJustMoving[x][y]--;
8402 if (WasJustFalling[x][y] > 0)
8403 WasJustFalling[x][y]--;
8404 if (CheckCollision[x][y] > 0)
8405 CheckCollision[x][y]--;
8410 /* reset finished pushing action (not done in ContinueMoving() to allow
8411 continous pushing animation for elements with zero push delay) */
8412 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8414 ResetGfxAnimation(x, y);
8415 DrawLevelField(x, y);
8420 if (IS_BLOCKED(x, y))
8424 Blocked2Moving(x, y, &oldx, &oldy);
8425 if (!IS_MOVING(oldx, oldy))
8427 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8428 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8429 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8430 printf("GameActions(): This should never happen!\n");
8436 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8438 element = Feld[x][y];
8440 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8442 graphic = el2img(element);
8448 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8450 element = graphic = 0;
8454 if (graphic_info[graphic].anim_global_sync)
8455 GfxFrame[x][y] = FrameCounter;
8457 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8458 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8459 ResetRandomAnimationValue(x, y);
8461 SetRandomAnimationValue(x, y);
8464 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8467 if (IS_INACTIVE(element))
8469 if (IS_ANIMATED(graphic))
8470 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8476 /* this may take place after moving, so 'element' may have changed */
8478 if (IS_CHANGING(x, y))
8480 if (IS_CHANGING(x, y) &&
8481 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8485 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8486 element_info[element].event_page_nr[CE_DELAY]);
8488 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8491 element = Feld[x][y];
8492 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8496 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8501 element = Feld[x][y];
8502 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8504 if (element == EL_MOLE)
8505 printf("::: %d, %d, %d [%d]\n",
8506 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8510 if (element == EL_YAMYAM)
8511 printf("::: %d, %d, %d\n",
8512 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8516 if (IS_ANIMATED(graphic) &&
8520 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8523 if (element == EL_BUG)
8524 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8528 if (element == EL_MOLE)
8529 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8533 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8534 EdelsteinFunkeln(x, y);
8536 else if ((element == EL_ACID ||
8537 element == EL_EXIT_OPEN ||
8538 element == EL_SP_EXIT_OPEN ||
8539 element == EL_SP_TERMINAL ||
8540 element == EL_SP_TERMINAL_ACTIVE ||
8541 element == EL_EXTRA_TIME ||
8542 element == EL_SHIELD_NORMAL ||
8543 element == EL_SHIELD_DEADLY) &&
8544 IS_ANIMATED(graphic))
8545 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8546 else if (IS_MOVING(x, y))
8547 ContinueMoving(x, y);
8548 else if (IS_ACTIVE_BOMB(element))
8549 CheckDynamite(x, y);
8551 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8552 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8554 else if (element == EL_AMOEBA_GROWING)
8555 AmoebeWaechst(x, y);
8556 else if (element == EL_AMOEBA_SHRINKING)
8557 AmoebaDisappearing(x, y);
8559 #if !USE_NEW_AMOEBA_CODE
8560 else if (IS_AMOEBALIVE(element))
8561 AmoebeAbleger(x, y);
8564 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8566 else if (element == EL_EXIT_CLOSED)
8568 else if (element == EL_SP_EXIT_CLOSED)
8570 else if (element == EL_EXPANDABLE_WALL_GROWING)
8572 else if (element == EL_EXPANDABLE_WALL ||
8573 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8574 element == EL_EXPANDABLE_WALL_VERTICAL ||
8575 element == EL_EXPANDABLE_WALL_ANY)
8577 else if (element == EL_FLAMES)
8578 CheckForDragon(x, y);
8580 else if (IS_AUTO_CHANGING(element))
8581 ChangeElement(x, y);
8583 else if (element == EL_EXPLOSION)
8584 ; /* drawing of correct explosion animation is handled separately */
8585 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8586 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8589 /* this may take place after moving, so 'element' may have changed */
8590 if (IS_AUTO_CHANGING(Feld[x][y]))
8591 ChangeElement(x, y);
8594 if (IS_BELT_ACTIVE(element))
8595 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8597 if (game.magic_wall_active)
8599 int jx = local_player->jx, jy = local_player->jy;
8601 /* play the element sound at the position nearest to the player */
8602 if ((element == EL_MAGIC_WALL_FULL ||
8603 element == EL_MAGIC_WALL_ACTIVE ||
8604 element == EL_MAGIC_WALL_EMPTYING ||
8605 element == EL_BD_MAGIC_WALL_FULL ||
8606 element == EL_BD_MAGIC_WALL_ACTIVE ||
8607 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8608 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8616 #if USE_NEW_AMOEBA_CODE
8617 /* new experimental amoeba growth stuff */
8619 if (!(FrameCounter % 8))
8622 static unsigned long random = 1684108901;
8624 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8627 x = (random >> 10) % lev_fieldx;
8628 y = (random >> 20) % lev_fieldy;
8630 x = RND(lev_fieldx);
8631 y = RND(lev_fieldy);
8633 element = Feld[x][y];
8636 if (!IS_PLAYER(x,y) &&
8637 (element == EL_EMPTY ||
8638 CAN_GROW_INTO(element) ||
8639 element == EL_QUICKSAND_EMPTY ||
8640 element == EL_ACID_SPLASH_LEFT ||
8641 element == EL_ACID_SPLASH_RIGHT))
8643 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8644 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8645 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8646 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8647 Feld[x][y] = EL_AMOEBA_DROP;
8650 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8651 if (!IS_PLAYER(x,y) &&
8652 (element == EL_EMPTY ||
8653 element == EL_SAND ||
8654 element == EL_QUICKSAND_EMPTY ||
8655 element == EL_ACID_SPLASH_LEFT ||
8656 element == EL_ACID_SPLASH_RIGHT))
8658 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8659 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8660 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8661 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8662 Feld[x][y] = EL_AMOEBA_DROP;
8666 random = random * 129 + 1;
8672 if (game.explosions_delayed)
8675 game.explosions_delayed = FALSE;
8677 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8679 element = Feld[x][y];
8681 if (ExplodeField[x][y])
8682 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8683 else if (element == EL_EXPLOSION)
8684 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8686 ExplodeField[x][y] = EX_TYPE_NONE;
8689 game.explosions_delayed = TRUE;
8692 if (game.magic_wall_active)
8694 if (!(game.magic_wall_time_left % 4))
8696 int element = Feld[magic_wall_x][magic_wall_y];
8698 if (element == EL_BD_MAGIC_WALL_FULL ||
8699 element == EL_BD_MAGIC_WALL_ACTIVE ||
8700 element == EL_BD_MAGIC_WALL_EMPTYING)
8701 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8703 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8706 if (game.magic_wall_time_left > 0)
8708 game.magic_wall_time_left--;
8709 if (!game.magic_wall_time_left)
8711 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8713 element = Feld[x][y];
8715 if (element == EL_MAGIC_WALL_ACTIVE ||
8716 element == EL_MAGIC_WALL_FULL)
8718 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8719 DrawLevelField(x, y);
8721 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8722 element == EL_BD_MAGIC_WALL_FULL)
8724 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8725 DrawLevelField(x, y);
8729 game.magic_wall_active = FALSE;
8734 if (game.light_time_left > 0)
8736 game.light_time_left--;
8738 if (game.light_time_left == 0)
8739 RedrawAllLightSwitchesAndInvisibleElements();
8742 if (game.timegate_time_left > 0)
8744 game.timegate_time_left--;
8746 if (game.timegate_time_left == 0)
8747 CloseAllOpenTimegates();
8750 for (i = 0; i < MAX_PLAYERS; i++)
8752 struct PlayerInfo *player = &stored_player[i];
8754 if (SHIELD_ON(player))
8756 if (player->shield_deadly_time_left)
8757 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8758 else if (player->shield_normal_time_left)
8759 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8763 if (TimeFrames >= FRAMES_PER_SECOND)
8768 if (!level.use_step_counter)
8772 for (i = 0; i < MAX_PLAYERS; i++)
8774 struct PlayerInfo *player = &stored_player[i];
8776 if (SHIELD_ON(player))
8778 player->shield_normal_time_left--;
8780 if (player->shield_deadly_time_left > 0)
8781 player->shield_deadly_time_left--;
8789 if (TimeLeft <= 10 && setup.time_limit)
8790 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8792 DrawGameValue_Time(TimeLeft);
8794 if (!TimeLeft && setup.time_limit)
8795 for (i = 0; i < MAX_PLAYERS; i++)
8796 KillHero(&stored_player[i]);
8798 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8799 DrawGameValue_Time(TimePlayed);
8802 if (tape.recording || tape.playing)
8803 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8807 PlayAllPlayersSound();
8809 if (options.debug) /* calculate frames per second */
8811 static unsigned long fps_counter = 0;
8812 static int fps_frames = 0;
8813 unsigned long fps_delay_ms = Counter() - fps_counter;
8817 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8819 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8822 fps_counter = Counter();
8825 redraw_mask |= REDRAW_FPS;
8829 if (stored_player[0].jx != stored_player[0].last_jx ||
8830 stored_player[0].jy != stored_player[0].last_jy)
8831 printf("::: %d, %d, %d, %d, %d\n",
8832 stored_player[0].MovDir,
8833 stored_player[0].MovPos,
8834 stored_player[0].GfxPos,
8835 stored_player[0].Frame,
8836 stored_player[0].StepFrame);
8843 for (i = 0; i < MAX_PLAYERS; i++)
8846 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8848 stored_player[i].Frame += move_frames;
8850 if (stored_player[i].MovPos != 0)
8851 stored_player[i].StepFrame += move_frames;
8853 if (stored_player[i].drop_delay > 0)
8854 stored_player[i].drop_delay--;
8859 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8861 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8863 local_player->show_envelope = 0;
8868 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8870 int min_x = x, min_y = y, max_x = x, max_y = y;
8873 for (i = 0; i < MAX_PLAYERS; i++)
8875 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8877 if (!stored_player[i].active || &stored_player[i] == player)
8880 min_x = MIN(min_x, jx);
8881 min_y = MIN(min_y, jy);
8882 max_x = MAX(max_x, jx);
8883 max_y = MAX(max_y, jy);
8886 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8889 static boolean AllPlayersInVisibleScreen()
8893 for (i = 0; i < MAX_PLAYERS; i++)
8895 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8897 if (!stored_player[i].active)
8900 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8907 void ScrollLevel(int dx, int dy)
8909 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8912 BlitBitmap(drawto_field, drawto_field,
8913 FX + TILEX * (dx == -1) - softscroll_offset,
8914 FY + TILEY * (dy == -1) - softscroll_offset,
8915 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8916 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8917 FX + TILEX * (dx == 1) - softscroll_offset,
8918 FY + TILEY * (dy == 1) - softscroll_offset);
8922 x = (dx == 1 ? BX1 : BX2);
8923 for (y = BY1; y <= BY2; y++)
8924 DrawScreenField(x, y);
8929 y = (dy == 1 ? BY1 : BY2);
8930 for (x = BX1; x <= BX2; x++)
8931 DrawScreenField(x, y);
8934 redraw_mask |= REDRAW_FIELD;
8938 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8940 int nextx = x + dx, nexty = y + dy;
8941 int element = Feld[x][y];
8944 element != EL_SP_PORT_LEFT &&
8945 element != EL_SP_GRAVITY_PORT_LEFT &&
8946 element != EL_SP_PORT_HORIZONTAL &&
8947 element != EL_SP_PORT_ANY) ||
8949 element != EL_SP_PORT_RIGHT &&
8950 element != EL_SP_GRAVITY_PORT_RIGHT &&
8951 element != EL_SP_PORT_HORIZONTAL &&
8952 element != EL_SP_PORT_ANY) ||
8954 element != EL_SP_PORT_UP &&
8955 element != EL_SP_GRAVITY_PORT_UP &&
8956 element != EL_SP_PORT_VERTICAL &&
8957 element != EL_SP_PORT_ANY) ||
8959 element != EL_SP_PORT_DOWN &&
8960 element != EL_SP_GRAVITY_PORT_DOWN &&
8961 element != EL_SP_PORT_VERTICAL &&
8962 element != EL_SP_PORT_ANY) ||
8963 !IN_LEV_FIELD(nextx, nexty) ||
8964 !IS_FREE(nextx, nexty))
8971 static boolean canFallDown(struct PlayerInfo *player)
8973 int jx = player->jx, jy = player->jy;
8975 return (IN_LEV_FIELD(jx, jy + 1) &&
8976 (IS_FREE(jx, jy + 1) ||
8977 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8978 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8979 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8982 static boolean canPassField(int x, int y, int move_dir)
8984 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8985 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8986 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8989 int element = Feld[x][y];
8991 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8992 !CAN_MOVE(element) &&
8993 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8994 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8995 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8998 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9000 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9001 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9002 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9006 int nextx = newx + dx;
9007 int nexty = newy + dy;
9011 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9012 (IS_DIGGABLE(Feld[newx][newy]) ||
9013 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9014 canPassField(newx, newy, move_dir)));
9016 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9017 (IS_DIGGABLE(Feld[newx][newy]) ||
9018 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9019 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9020 !CAN_MOVE(Feld[newx][newy]) &&
9021 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9022 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9023 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9027 static void CheckGravityMovement(struct PlayerInfo *player)
9029 if (game.gravity && !player->programmed_action)
9032 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9033 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9035 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9036 int move_dir_vertical = player->action & MV_VERTICAL;
9040 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9042 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9045 int jx = player->jx, jy = player->jy;
9047 boolean player_is_moving_to_valid_field =
9048 (!player_is_snapping &&
9049 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9050 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9054 (player->last_move_dir & MV_HORIZONTAL ?
9055 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9056 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9060 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9061 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9062 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9063 int new_jx = jx + dx, new_jy = jy + dy;
9064 int nextx = new_jx + dx, nexty = new_jy + dy;
9070 boolean player_can_fall_down = canFallDown(player);
9072 boolean player_can_fall_down =
9073 (IN_LEV_FIELD(jx, jy + 1) &&
9074 (IS_FREE(jx, jy + 1) ||
9075 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9079 boolean player_can_fall_down =
9080 (IN_LEV_FIELD(jx, jy + 1) &&
9081 (IS_FREE(jx, jy + 1)));
9085 boolean player_is_moving_to_valid_field =
9088 !player_is_snapping &&
9092 IN_LEV_FIELD(new_jx, new_jy) &&
9093 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9094 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9095 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9096 IN_LEV_FIELD(nextx, nexty) &&
9097 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9099 IN_LEV_FIELD(new_jx, new_jy) &&
9100 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9101 Feld[new_jx][new_jy] == EL_SAND ||
9102 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9103 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9104 /* !!! extend EL_SAND to anything diggable !!! */
9110 boolean player_is_standing_on_valid_field =
9111 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9112 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9116 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9117 player_can_fall_down,
9118 player_is_standing_on_valid_field,
9119 player_is_moving_to_valid_field,
9120 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9121 player->effective_action,
9122 player->can_fall_into_acid);
9125 if (player_can_fall_down &&
9127 !player_is_standing_on_valid_field &&
9129 !player_is_moving_to_valid_field)
9132 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9133 jx, jy, FrameCounter);
9136 player->programmed_action = MV_DOWN;
9141 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9144 return CheckGravityMovement(player);
9147 if (game.gravity && !player->programmed_action)
9149 int jx = player->jx, jy = player->jy;
9150 boolean field_under_player_is_free =
9151 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9152 boolean player_is_standing_on_valid_field =
9153 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9154 (IS_WALKABLE(Feld[jx][jy]) &&
9155 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9157 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9158 player->programmed_action = MV_DOWN;
9164 -----------------------------------------------------------------------------
9165 dx, dy: direction (non-diagonal) to try to move the player to
9166 real_dx, real_dy: direction as read from input device (can be diagonal)
9169 boolean MovePlayerOneStep(struct PlayerInfo *player,
9170 int dx, int dy, int real_dx, int real_dy)
9173 static int trigger_sides[4][2] =
9175 /* enter side leave side */
9176 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9177 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9178 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9179 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9181 int move_direction = (dx == -1 ? MV_LEFT :
9182 dx == +1 ? MV_RIGHT :
9184 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9185 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9186 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9188 int jx = player->jx, jy = player->jy;
9189 int new_jx = jx + dx, new_jy = jy + dy;
9193 if (!player->active || (!dx && !dy))
9194 return MF_NO_ACTION;
9196 player->MovDir = (dx < 0 ? MV_LEFT :
9199 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9201 if (!IN_LEV_FIELD(new_jx, new_jy))
9202 return MF_NO_ACTION;
9204 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9205 return MF_NO_ACTION;
9208 element = MovingOrBlocked2Element(new_jx, new_jy);
9210 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9213 if (DONT_RUN_INTO(element))
9215 if (element == EL_ACID && dx == 0 && dy == 1)
9217 SplashAcid(new_jx, new_jy);
9218 Feld[jx][jy] = EL_PLAYER_1;
9219 InitMovingField(jx, jy, MV_DOWN);
9220 Store[jx][jy] = EL_ACID;
9221 ContinueMoving(jx, jy);
9225 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9230 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9231 if (can_move != MF_MOVING)
9234 /* check if DigField() has caused relocation of the player */
9235 if (player->jx != jx || player->jy != jy)
9236 return MF_NO_ACTION;
9238 StorePlayer[jx][jy] = 0;
9239 player->last_jx = jx;
9240 player->last_jy = jy;
9241 player->jx = new_jx;
9242 player->jy = new_jy;
9243 StorePlayer[new_jx][new_jy] = player->element_nr;
9246 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9248 player->step_counter++;
9251 player->drop_delay = 0;
9254 PlayerVisit[jx][jy] = FrameCounter;
9256 ScrollPlayer(player, SCROLL_INIT);
9259 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9261 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9263 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9266 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9268 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9269 CE_OTHER_GETS_ENTERED, enter_side);
9270 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9271 CE_ENTERED_BY_PLAYER, enter_side);
9278 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9280 int jx = player->jx, jy = player->jy;
9281 int old_jx = jx, old_jy = jy;
9282 int moved = MF_NO_ACTION;
9285 if (!player->active)
9290 if (player->MovPos == 0)
9292 player->is_moving = FALSE;
9293 player->is_digging = FALSE;
9294 player->is_collecting = FALSE;
9295 player->is_snapping = FALSE;
9296 player->is_pushing = FALSE;
9302 if (!player->active || (!dx && !dy))
9307 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9313 if (!FrameReached(&player->move_delay, player->move_delay_value))
9316 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9317 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9323 /* store if player is automatically moved to next field */
9324 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9326 /* remove the last programmed player action */
9327 player->programmed_action = 0;
9331 /* should only happen if pre-1.2 tape recordings are played */
9332 /* this is only for backward compatibility */
9334 int original_move_delay_value = player->move_delay_value;
9337 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9341 /* scroll remaining steps with finest movement resolution */
9342 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9344 while (player->MovPos)
9346 ScrollPlayer(player, SCROLL_GO_ON);
9347 ScrollScreen(NULL, SCROLL_GO_ON);
9353 player->move_delay_value = original_move_delay_value;
9356 if (player->last_move_dir & MV_HORIZONTAL)
9358 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9359 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9363 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9364 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9370 if (moved & MF_MOVING && !ScreenMovPos &&
9371 (player == local_player || !options.network))
9373 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9374 int offset = (setup.scroll_delay ? 3 : 0);
9376 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9378 /* actual player has left the screen -- scroll in that direction */
9379 if (jx != old_jx) /* player has moved horizontally */
9380 scroll_x += (jx - old_jx);
9381 else /* player has moved vertically */
9382 scroll_y += (jy - old_jy);
9386 if (jx != old_jx) /* player has moved horizontally */
9388 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9389 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9390 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9392 /* don't scroll over playfield boundaries */
9393 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9394 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9396 /* don't scroll more than one field at a time */
9397 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9399 /* don't scroll against the player's moving direction */
9400 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9401 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9402 scroll_x = old_scroll_x;
9404 else /* player has moved vertically */
9406 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9407 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9408 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9410 /* don't scroll over playfield boundaries */
9411 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9412 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9414 /* don't scroll more than one field at a time */
9415 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9417 /* don't scroll against the player's moving direction */
9418 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9419 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9420 scroll_y = old_scroll_y;
9424 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9426 if (!options.network && !AllPlayersInVisibleScreen())
9428 scroll_x = old_scroll_x;
9429 scroll_y = old_scroll_y;
9433 ScrollScreen(player, SCROLL_INIT);
9434 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9441 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9443 if (!(moved & MF_MOVING) && !player->is_pushing)
9448 player->StepFrame = 0;
9450 if (moved & MF_MOVING)
9452 if (old_jx != jx && old_jy == jy)
9453 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9454 else if (old_jx == jx && old_jy != jy)
9455 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9457 DrawLevelField(jx, jy); /* for "crumbled sand" */
9459 player->last_move_dir = player->MovDir;
9460 player->is_moving = TRUE;
9462 player->is_snapping = FALSE;
9466 player->is_switching = FALSE;
9469 player->is_dropping = FALSE;
9473 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9476 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9479 static int trigger_sides[4][2] =
9481 /* enter side leave side */
9482 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9483 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9484 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9485 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9487 int move_direction = player->MovDir;
9488 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9489 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9490 int old_element = Feld[old_jx][old_jy];
9491 int new_element = Feld[jx][jy];
9494 /* !!! TEST ONLY !!! */
9495 if (IS_CUSTOM_ELEMENT(old_element))
9496 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9498 player->index_bit, leave_side);
9500 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9502 player->index_bit, leave_side);
9504 if (IS_CUSTOM_ELEMENT(new_element))
9505 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9506 player->index_bit, enter_side);
9508 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9509 CE_OTHER_GETS_ENTERED,
9510 player->index_bit, enter_side);
9520 CheckGravityMovementWhenNotMoving(player);
9523 player->last_move_dir = MV_NO_MOVING;
9525 player->is_moving = FALSE;
9528 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9530 TestIfHeroTouchesBadThing(jx, jy);
9531 TestIfPlayerTouchesCustomElement(jx, jy);
9534 if (!player->active)
9540 void ScrollPlayer(struct PlayerInfo *player, int mode)
9542 int jx = player->jx, jy = player->jy;
9543 int last_jx = player->last_jx, last_jy = player->last_jy;
9544 int move_stepsize = TILEX / player->move_delay_value;
9546 if (!player->active || !player->MovPos)
9549 if (mode == SCROLL_INIT)
9551 player->actual_frame_counter = FrameCounter;
9552 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9554 if (Feld[last_jx][last_jy] == EL_EMPTY)
9555 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9563 else if (!FrameReached(&player->actual_frame_counter, 1))
9566 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9567 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9569 if (!player->block_last_field &&
9570 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9571 Feld[last_jx][last_jy] = EL_EMPTY;
9573 /* before DrawPlayer() to draw correct player graphic for this case */
9574 if (player->MovPos == 0)
9575 CheckGravityMovement(player);
9578 DrawPlayer(player); /* needed here only to cleanup last field */
9581 if (player->MovPos == 0) /* player reached destination field */
9584 if (player->move_delay_reset_counter > 0)
9586 player->move_delay_reset_counter--;
9588 if (player->move_delay_reset_counter == 0)
9590 /* continue with normal speed after quickly moving through gate */
9591 HALVE_PLAYER_SPEED(player);
9593 /* be able to make the next move without delay */
9594 player->move_delay = 0;
9598 if (IS_PASSABLE(Feld[last_jx][last_jy]))
9600 /* continue with normal speed after quickly moving through gate */
9601 HALVE_PLAYER_SPEED(player);
9603 /* be able to make the next move without delay */
9604 player->move_delay = 0;
9608 if (player->block_last_field &&
9609 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9610 Feld[last_jx][last_jy] = EL_EMPTY;
9612 player->last_jx = jx;
9613 player->last_jy = jy;
9615 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9616 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9617 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9619 DrawPlayer(player); /* needed here only to cleanup last field */
9622 if (local_player->friends_still_needed == 0 ||
9623 IS_SP_ELEMENT(Feld[jx][jy]))
9624 player->LevelSolved = player->GameOver = TRUE;
9628 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9629 /* this breaks one level: "machine", level 000 */
9631 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9634 static int trigger_sides[4][2] =
9636 /* enter side leave side */
9637 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9638 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9639 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9640 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9642 int move_direction = player->MovDir;
9643 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9644 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9645 int old_jx = last_jx;
9646 int old_jy = last_jy;
9647 int old_element = Feld[old_jx][old_jy];
9648 int new_element = Feld[jx][jy];
9651 /* !!! TEST ONLY !!! */
9652 if (IS_CUSTOM_ELEMENT(old_element))
9653 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9655 player->index_bit, leave_side);
9657 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9659 player->index_bit, leave_side);
9661 if (IS_CUSTOM_ELEMENT(new_element))
9662 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9663 player->index_bit, enter_side);
9665 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9666 CE_OTHER_GETS_ENTERED,
9667 player->index_bit, enter_side);
9673 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9675 TestIfHeroTouchesBadThing(jx, jy);
9676 TestIfPlayerTouchesCustomElement(jx, jy);
9678 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9681 if (!player->active)
9685 if (level.use_step_counter)
9691 for (i = 0; i < MAX_PLAYERS; i++)
9693 struct PlayerInfo *player = &stored_player[i];
9695 if (SHIELD_ON(player))
9697 player->shield_normal_time_left--;
9699 if (player->shield_deadly_time_left > 0)
9700 player->shield_deadly_time_left--;
9708 if (TimeLeft <= 10 && setup.time_limit)
9709 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9711 DrawGameValue_Time(TimeLeft);
9713 if (!TimeLeft && setup.time_limit)
9714 for (i = 0; i < MAX_PLAYERS; i++)
9715 KillHero(&stored_player[i]);
9717 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9718 DrawGameValue_Time(TimePlayed);
9721 if (tape.single_step && tape.recording && !tape.pausing &&
9722 !player->programmed_action)
9723 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9727 void ScrollScreen(struct PlayerInfo *player, int mode)
9729 static unsigned long screen_frame_counter = 0;
9731 if (mode == SCROLL_INIT)
9733 /* set scrolling step size according to actual player's moving speed */
9734 ScrollStepSize = TILEX / player->move_delay_value;
9736 screen_frame_counter = FrameCounter;
9737 ScreenMovDir = player->MovDir;
9738 ScreenMovPos = player->MovPos;
9739 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9742 else if (!FrameReached(&screen_frame_counter, 1))
9747 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9748 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9749 redraw_mask |= REDRAW_FIELD;
9752 ScreenMovDir = MV_NO_MOVING;
9755 void TestIfPlayerTouchesCustomElement(int x, int y)
9757 static int xy[4][2] =
9764 static int trigger_sides[4][2] =
9766 /* center side border side */
9767 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9768 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9769 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9770 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9772 static int touch_dir[4] =
9779 int center_element = Feld[x][y]; /* should always be non-moving! */
9782 for (i = 0; i < NUM_DIRECTIONS; i++)
9784 int xx = x + xy[i][0];
9785 int yy = y + xy[i][1];
9786 int center_side = trigger_sides[i][0];
9787 int border_side = trigger_sides[i][1];
9790 if (!IN_LEV_FIELD(xx, yy))
9793 if (IS_PLAYER(x, y))
9795 struct PlayerInfo *player = PLAYERINFO(x, y);
9797 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9798 border_element = Feld[xx][yy]; /* may be moving! */
9799 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9800 border_element = Feld[xx][yy];
9801 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9802 border_element = MovingOrBlocked2Element(xx, yy);
9804 continue; /* center and border element do not touch */
9807 /* !!! TEST ONLY !!! */
9808 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9809 player->index_bit, border_side);
9810 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9811 CE_OTHER_GETS_TOUCHED,
9812 player->index_bit, border_side);
9814 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9815 CE_OTHER_GETS_TOUCHED,
9816 player->index_bit, border_side);
9817 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9818 player->index_bit, border_side);
9821 else if (IS_PLAYER(xx, yy))
9823 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9825 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9827 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9828 continue; /* center and border element do not touch */
9832 /* !!! TEST ONLY !!! */
9833 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9834 player->index_bit, center_side);
9835 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9836 CE_OTHER_GETS_TOUCHED,
9837 player->index_bit, center_side);
9839 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9840 CE_OTHER_GETS_TOUCHED,
9841 player->index_bit, center_side);
9842 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9843 player->index_bit, center_side);
9851 void TestIfElementTouchesCustomElement(int x, int y)
9853 static int xy[4][2] =
9860 static int trigger_sides[4][2] =
9862 /* center side border side */
9863 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9864 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9865 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9866 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9868 static int touch_dir[4] =
9875 boolean change_center_element = FALSE;
9876 int center_element_change_page = 0;
9877 int center_element = Feld[x][y]; /* should always be non-moving! */
9878 int border_trigger_element;
9881 for (i = 0; i < NUM_DIRECTIONS; i++)
9883 int xx = x + xy[i][0];
9884 int yy = y + xy[i][1];
9885 int center_side = trigger_sides[i][0];
9886 int border_side = trigger_sides[i][1];
9889 if (!IN_LEV_FIELD(xx, yy))
9892 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9893 border_element = Feld[xx][yy]; /* may be moving! */
9894 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9895 border_element = Feld[xx][yy];
9896 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9897 border_element = MovingOrBlocked2Element(xx, yy);
9899 continue; /* center and border element do not touch */
9901 /* check for change of center element (but change it only once) */
9902 if (IS_CUSTOM_ELEMENT(center_element) &&
9903 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9904 !change_center_element)
9906 for (j = 0; j < element_info[center_element].num_change_pages; j++)
9908 struct ElementChangeInfo *change =
9909 &element_info[center_element].change_page[j];
9911 if (change->can_change &&
9912 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9913 change->trigger_side & border_side &&
9915 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9917 change->trigger_element == border_element
9921 change_center_element = TRUE;
9922 center_element_change_page = j;
9923 border_trigger_element = border_element;
9930 /* check for change of border element */
9931 if (IS_CUSTOM_ELEMENT(border_element) &&
9932 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9934 for (j = 0; j < element_info[border_element].num_change_pages; j++)
9936 struct ElementChangeInfo *change =
9937 &element_info[border_element].change_page[j];
9939 if (change->can_change &&
9940 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9941 change->trigger_side & center_side &&
9943 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9945 change->trigger_element == center_element
9950 printf("::: border_element %d, %d\n", x, y);
9953 CheckElementChangeByPage(xx, yy, border_element, center_element,
9954 CE_OTHER_IS_TOUCHING, j);
9961 if (change_center_element)
9964 printf("::: center_element %d, %d\n", x, y);
9967 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
9968 CE_OTHER_IS_TOUCHING, center_element_change_page);
9972 void TestIfElementHitsCustomElement(int x, int y, int direction)
9974 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9975 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9976 int hitx = x + dx, hity = y + dy;
9977 int hitting_element = Feld[x][y];
9978 int touched_element;
9980 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9981 !IS_FREE(hitx, hity) &&
9982 (!IS_MOVING(hitx, hity) ||
9983 MovDir[hitx][hity] != direction ||
9984 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9987 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9991 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9995 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9996 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9998 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9999 CE_HITTING_SOMETHING, direction);
10001 if (IN_LEV_FIELD(hitx, hity))
10003 int opposite_direction = MV_DIR_OPPOSITE(direction);
10004 int hitting_side = direction;
10005 int touched_side = opposite_direction;
10007 int touched_element = MovingOrBlocked2Element(hitx, hity);
10010 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10011 MovDir[hitx][hity] != direction ||
10012 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10021 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10022 CE_HIT_BY_SOMETHING, opposite_direction);
10024 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10025 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10027 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10029 struct ElementChangeInfo *change =
10030 &element_info[hitting_element].change_page[i];
10032 if (change->can_change &&
10033 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10034 change->trigger_side & touched_side &&
10037 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10039 change->trigger_element == touched_element
10043 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10044 CE_OTHER_IS_HITTING, i);
10050 if (IS_CUSTOM_ELEMENT(touched_element) &&
10051 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10053 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10055 struct ElementChangeInfo *change =
10056 &element_info[touched_element].change_page[i];
10058 if (change->can_change &&
10059 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10060 change->trigger_side & hitting_side &&
10062 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10064 change->trigger_element == hitting_element
10068 CheckElementChangeByPage(hitx, hity, touched_element,
10069 hitting_element, CE_OTHER_GETS_HIT, i);
10079 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10081 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10082 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10083 int hitx = x + dx, hity = y + dy;
10084 int hitting_element = Feld[x][y];
10085 int touched_element;
10087 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10088 !IS_FREE(hitx, hity) &&
10089 (!IS_MOVING(hitx, hity) ||
10090 MovDir[hitx][hity] != direction ||
10091 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10094 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10098 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10102 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10103 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10105 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10106 EP_CAN_SMASH_EVERYTHING, direction);
10108 if (IN_LEV_FIELD(hitx, hity))
10110 int opposite_direction = MV_DIR_OPPOSITE(direction);
10111 int hitting_side = direction;
10112 int touched_side = opposite_direction;
10114 int touched_element = MovingOrBlocked2Element(hitx, hity);
10117 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10118 MovDir[hitx][hity] != direction ||
10119 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10128 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10129 CE_SMASHED_BY_SOMETHING, opposite_direction);
10131 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10132 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10134 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10136 struct ElementChangeInfo *change =
10137 &element_info[hitting_element].change_page[i];
10139 if (change->can_change &&
10140 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10141 change->trigger_side & touched_side &&
10144 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10146 change->trigger_element == touched_element
10150 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10151 CE_OTHER_IS_SMASHING, i);
10157 if (IS_CUSTOM_ELEMENT(touched_element) &&
10158 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10160 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10162 struct ElementChangeInfo *change =
10163 &element_info[touched_element].change_page[i];
10165 if (change->can_change &&
10166 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10167 change->trigger_side & hitting_side &&
10169 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10171 change->trigger_element == hitting_element
10175 CheckElementChangeByPage(hitx, hity, touched_element,
10176 hitting_element, CE_OTHER_GETS_SMASHED,i);
10186 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10188 int i, kill_x = -1, kill_y = -1;
10189 int bad_element = -1;
10190 static int test_xy[4][2] =
10197 static int test_dir[4] =
10205 for (i = 0; i < NUM_DIRECTIONS; i++)
10207 int test_x, test_y, test_move_dir, test_element;
10209 test_x = good_x + test_xy[i][0];
10210 test_y = good_y + test_xy[i][1];
10212 if (!IN_LEV_FIELD(test_x, test_y))
10216 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10219 test_element = Feld[test_x][test_y];
10221 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10224 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10225 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10227 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10228 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10232 bad_element = test_element;
10238 if (kill_x != -1 || kill_y != -1)
10240 if (IS_PLAYER(good_x, good_y))
10242 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10245 if (player->shield_deadly_time_left > 0 &&
10246 !IS_INDESTRUCTIBLE(bad_element))
10247 Bang(kill_x, kill_y);
10248 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10251 if (player->shield_deadly_time_left > 0)
10252 Bang(kill_x, kill_y);
10253 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10258 Bang(good_x, good_y);
10262 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10264 int i, kill_x = -1, kill_y = -1;
10265 int bad_element = Feld[bad_x][bad_y];
10266 static int test_xy[4][2] =
10273 static int touch_dir[4] =
10275 MV_LEFT | MV_RIGHT,
10280 static int test_dir[4] =
10288 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10291 for (i = 0; i < NUM_DIRECTIONS; i++)
10293 int test_x, test_y, test_move_dir, test_element;
10295 test_x = bad_x + test_xy[i][0];
10296 test_y = bad_y + test_xy[i][1];
10297 if (!IN_LEV_FIELD(test_x, test_y))
10301 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10303 test_element = Feld[test_x][test_y];
10305 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10306 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10308 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10309 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10311 /* good thing is player or penguin that does not move away */
10312 if (IS_PLAYER(test_x, test_y))
10314 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10316 if (bad_element == EL_ROBOT && player->is_moving)
10317 continue; /* robot does not kill player if he is moving */
10319 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10321 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10322 continue; /* center and border element do not touch */
10329 else if (test_element == EL_PENGUIN)
10338 if (kill_x != -1 || kill_y != -1)
10340 if (IS_PLAYER(kill_x, kill_y))
10342 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10345 if (player->shield_deadly_time_left > 0 &&
10346 !IS_INDESTRUCTIBLE(bad_element))
10347 Bang(bad_x, bad_y);
10348 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10351 if (player->shield_deadly_time_left > 0)
10352 Bang(bad_x, bad_y);
10353 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10358 Bang(kill_x, kill_y);
10362 void TestIfHeroTouchesBadThing(int x, int y)
10364 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10367 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10369 TestIfGoodThingHitsBadThing(x, y, move_dir);
10372 void TestIfBadThingTouchesHero(int x, int y)
10374 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10377 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10379 TestIfBadThingHitsGoodThing(x, y, move_dir);
10382 void TestIfFriendTouchesBadThing(int x, int y)
10384 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10387 void TestIfBadThingTouchesFriend(int x, int y)
10389 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10392 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10394 int i, kill_x = bad_x, kill_y = bad_y;
10395 static int xy[4][2] =
10403 for (i = 0; i < NUM_DIRECTIONS; i++)
10407 x = bad_x + xy[i][0];
10408 y = bad_y + xy[i][1];
10409 if (!IN_LEV_FIELD(x, y))
10412 element = Feld[x][y];
10413 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10414 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10422 if (kill_x != bad_x || kill_y != bad_y)
10423 Bang(bad_x, bad_y);
10426 void KillHero(struct PlayerInfo *player)
10428 int jx = player->jx, jy = player->jy;
10430 if (!player->active)
10433 /* remove accessible field at the player's position */
10434 Feld[jx][jy] = EL_EMPTY;
10436 /* deactivate shield (else Bang()/Explode() would not work right) */
10437 player->shield_normal_time_left = 0;
10438 player->shield_deadly_time_left = 0;
10444 static void KillHeroUnlessEnemyProtected(int x, int y)
10446 if (!PLAYER_ENEMY_PROTECTED(x, y))
10447 KillHero(PLAYERINFO(x, y));
10450 static void KillHeroUnlessExplosionProtected(int x, int y)
10452 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10453 KillHero(PLAYERINFO(x, y));
10456 void BuryHero(struct PlayerInfo *player)
10458 int jx = player->jx, jy = player->jy;
10460 if (!player->active)
10464 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10466 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10468 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10470 player->GameOver = TRUE;
10471 RemoveHero(player);
10474 void RemoveHero(struct PlayerInfo *player)
10476 int jx = player->jx, jy = player->jy;
10477 int i, found = FALSE;
10479 player->present = FALSE;
10480 player->active = FALSE;
10482 if (!ExplodeField[jx][jy])
10483 StorePlayer[jx][jy] = 0;
10485 for (i = 0; i < MAX_PLAYERS; i++)
10486 if (stored_player[i].active)
10490 AllPlayersGone = TRUE;
10497 =============================================================================
10498 checkDiagonalPushing()
10499 -----------------------------------------------------------------------------
10500 check if diagonal input device direction results in pushing of object
10501 (by checking if the alternative direction is walkable, diggable, ...)
10502 =============================================================================
10505 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10506 int x, int y, int real_dx, int real_dy)
10508 int jx, jy, dx, dy, xx, yy;
10510 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10513 /* diagonal direction: check alternative direction */
10518 xx = jx + (dx == 0 ? real_dx : 0);
10519 yy = jy + (dy == 0 ? real_dy : 0);
10521 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10525 =============================================================================
10527 -----------------------------------------------------------------------------
10528 x, y: field next to player (non-diagonal) to try to dig to
10529 real_dx, real_dy: direction as read from input device (can be diagonal)
10530 =============================================================================
10533 int DigField(struct PlayerInfo *player,
10534 int oldx, int oldy, int x, int y,
10535 int real_dx, int real_dy, int mode)
10537 static int trigger_sides[4] =
10539 CH_SIDE_RIGHT, /* moving left */
10540 CH_SIDE_LEFT, /* moving right */
10541 CH_SIDE_BOTTOM, /* moving up */
10542 CH_SIDE_TOP, /* moving down */
10545 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10547 int jx = oldx, jy = oldy;
10548 int dx = x - jx, dy = y - jy;
10549 int nextx = x + dx, nexty = y + dy;
10550 int move_direction = (dx == -1 ? MV_LEFT :
10551 dx == +1 ? MV_RIGHT :
10553 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10554 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10555 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10556 int old_element = Feld[jx][jy];
10559 if (player->MovPos == 0)
10561 player->is_digging = FALSE;
10562 player->is_collecting = FALSE;
10565 if (player->MovPos == 0) /* last pushing move finished */
10566 player->is_pushing = FALSE;
10568 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10570 player->is_switching = FALSE;
10571 player->push_delay = 0;
10573 return MF_NO_ACTION;
10576 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10577 return MF_NO_ACTION;
10582 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10584 if (IS_TUBE(Feld[jx][jy]) ||
10585 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10589 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10590 int tube_leave_directions[][2] =
10592 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10593 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10594 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10595 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
10596 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
10597 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
10598 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
10599 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
10600 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
10601 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
10602 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
10603 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10606 while (tube_leave_directions[i][0] != tube_element)
10609 if (tube_leave_directions[i][0] == -1) /* should not happen */
10613 if (!(tube_leave_directions[i][1] & move_direction))
10614 return MF_NO_ACTION; /* tube has no opening in this direction */
10619 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10620 old_element = Back[jx][jy];
10624 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10625 return MF_NO_ACTION; /* field has no opening in this direction */
10627 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10628 return MF_NO_ACTION; /* field has no opening in this direction */
10630 element = Feld[x][y];
10632 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10633 game.engine_version >= VERSION_IDENT(2,2,0,0))
10634 return MF_NO_ACTION;
10637 if (game.gravity && !player->is_auto_moving &&
10638 canFallDown(player) && move_direction != MV_DOWN &&
10639 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10640 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10644 if (element == EL_EMPTY_SPACE &&
10645 game.gravity && !player->is_auto_moving &&
10646 canFallDown(player) && move_direction != MV_DOWN)
10647 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10653 case EL_SP_PORT_LEFT:
10654 case EL_SP_PORT_RIGHT:
10655 case EL_SP_PORT_UP:
10656 case EL_SP_PORT_DOWN:
10657 case EL_SP_PORT_HORIZONTAL:
10658 case EL_SP_PORT_VERTICAL:
10659 case EL_SP_PORT_ANY:
10660 case EL_SP_GRAVITY_PORT_LEFT:
10661 case EL_SP_GRAVITY_PORT_RIGHT:
10662 case EL_SP_GRAVITY_PORT_UP:
10663 case EL_SP_GRAVITY_PORT_DOWN:
10665 if (!canEnterSupaplexPort(x, y, dx, dy))
10666 return MF_NO_ACTION;
10669 element != EL_SP_PORT_LEFT &&
10670 element != EL_SP_GRAVITY_PORT_LEFT &&
10671 element != EL_SP_PORT_HORIZONTAL &&
10672 element != EL_SP_PORT_ANY) ||
10674 element != EL_SP_PORT_RIGHT &&
10675 element != EL_SP_GRAVITY_PORT_RIGHT &&
10676 element != EL_SP_PORT_HORIZONTAL &&
10677 element != EL_SP_PORT_ANY) ||
10679 element != EL_SP_PORT_UP &&
10680 element != EL_SP_GRAVITY_PORT_UP &&
10681 element != EL_SP_PORT_VERTICAL &&
10682 element != EL_SP_PORT_ANY) ||
10684 element != EL_SP_PORT_DOWN &&
10685 element != EL_SP_GRAVITY_PORT_DOWN &&
10686 element != EL_SP_PORT_VERTICAL &&
10687 element != EL_SP_PORT_ANY) ||
10688 !IN_LEV_FIELD(nextx, nexty) ||
10689 !IS_FREE(nextx, nexty))
10690 return MF_NO_ACTION;
10693 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10694 element == EL_SP_GRAVITY_PORT_RIGHT ||
10695 element == EL_SP_GRAVITY_PORT_UP ||
10696 element == EL_SP_GRAVITY_PORT_DOWN)
10697 game.gravity = !game.gravity;
10699 /* automatically move to the next field with double speed */
10700 player->programmed_action = move_direction;
10702 if (player->move_delay_reset_counter == 0)
10704 player->move_delay_reset_counter = 2; /* two double speed steps */
10706 DOUBLE_PLAYER_SPEED(player);
10709 player->move_delay_reset_counter = 2;
10711 DOUBLE_PLAYER_SPEED(player);
10715 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10718 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10724 case EL_TUBE_VERTICAL:
10725 case EL_TUBE_HORIZONTAL:
10726 case EL_TUBE_VERTICAL_LEFT:
10727 case EL_TUBE_VERTICAL_RIGHT:
10728 case EL_TUBE_HORIZONTAL_UP:
10729 case EL_TUBE_HORIZONTAL_DOWN:
10730 case EL_TUBE_LEFT_UP:
10731 case EL_TUBE_LEFT_DOWN:
10732 case EL_TUBE_RIGHT_UP:
10733 case EL_TUBE_RIGHT_DOWN:
10736 int tube_enter_directions[][2] =
10738 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10739 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10740 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10741 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
10742 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
10743 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
10744 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
10745 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
10746 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
10747 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
10748 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
10749 { -1, MV_NO_MOVING }
10752 while (tube_enter_directions[i][0] != element)
10755 if (tube_enter_directions[i][0] == -1) /* should not happen */
10759 if (!(tube_enter_directions[i][1] & move_direction))
10760 return MF_NO_ACTION; /* tube has no opening in this direction */
10762 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10770 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10772 if (IS_WALKABLE(element))
10775 int sound_action = ACTION_WALKING;
10778 if (!ACCESS_FROM(element, opposite_direction))
10779 return MF_NO_ACTION; /* field not accessible from this direction */
10783 if (element == EL_EMPTY_SPACE &&
10784 game.gravity && !player->is_auto_moving &&
10785 canFallDown(player) && move_direction != MV_DOWN)
10786 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10789 if (IS_GATE(element))
10791 if (!player->key[element - EL_GATE_1])
10792 return MF_NO_ACTION;
10794 else if (IS_GATE_GRAY(element))
10796 if (!player->key[element - EL_GATE_1_GRAY])
10797 return MF_NO_ACTION;
10799 else if (element == EL_EXIT_OPEN ||
10800 element == EL_SP_EXIT_OPEN ||
10801 element == EL_SP_EXIT_OPENING)
10803 sound_action = ACTION_PASSING; /* player is passing exit */
10805 else if (element == EL_EMPTY)
10807 sound_action = ACTION_MOVING; /* nothing to walk on */
10810 /* play sound from background or player, whatever is available */
10811 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
10812 PlayLevelSoundElementAction(x, y, element, sound_action);
10814 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10819 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
10821 else if (IS_PASSABLE(element))
10825 if (!canPassField(x, y, move_direction))
10826 return MF_NO_ACTION;
10831 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
10832 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
10833 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
10834 return MF_NO_ACTION;
10836 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10837 return MF_NO_ACTION;
10842 if (!ACCESS_FROM(element, opposite_direction))
10843 return MF_NO_ACTION; /* field not accessible from this direction */
10845 if (IS_CUSTOM_ELEMENT(element) &&
10846 !ACCESS_FROM(element, opposite_direction))
10847 return MF_NO_ACTION; /* field not accessible from this direction */
10851 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10852 return MF_NO_ACTION;
10857 if (IS_EM_GATE(element))
10859 if (!player->key[element - EL_EM_GATE_1])
10860 return MF_NO_ACTION;
10862 else if (IS_EM_GATE_GRAY(element))
10864 if (!player->key[element - EL_EM_GATE_1_GRAY])
10865 return MF_NO_ACTION;
10867 else if (IS_SP_PORT(element))
10869 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10870 element == EL_SP_GRAVITY_PORT_RIGHT ||
10871 element == EL_SP_GRAVITY_PORT_UP ||
10872 element == EL_SP_GRAVITY_PORT_DOWN)
10873 game.gravity = !game.gravity;
10876 /* automatically move to the next field with double speed */
10877 player->programmed_action = move_direction;
10879 if (player->move_delay_reset_counter == 0)
10881 player->move_delay_reset_counter = 2; /* two double speed steps */
10883 DOUBLE_PLAYER_SPEED(player);
10886 player->move_delay_reset_counter = 2;
10888 DOUBLE_PLAYER_SPEED(player);
10891 PlayLevelSoundAction(x, y, ACTION_PASSING);
10895 else if (IS_DIGGABLE(element))
10899 if (mode != DF_SNAP)
10902 GfxElement[x][y] = GFX_ELEMENT(element);
10905 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
10907 player->is_digging = TRUE;
10910 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10912 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
10913 player->index_bit, dig_side);
10916 if (mode == DF_SNAP)
10917 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10922 else if (IS_COLLECTIBLE(element))
10926 if (mode != DF_SNAP)
10928 GfxElement[x][y] = element;
10929 player->is_collecting = TRUE;
10932 if (element == EL_SPEED_PILL)
10933 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10934 else if (element == EL_EXTRA_TIME && level.time > 0)
10937 DrawGameValue_Time(TimeLeft);
10939 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10941 player->shield_normal_time_left += 10;
10942 if (element == EL_SHIELD_DEADLY)
10943 player->shield_deadly_time_left += 10;
10945 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10947 if (player->inventory_size < MAX_INVENTORY_SIZE)
10948 player->inventory_element[player->inventory_size++] = element;
10950 DrawGameValue_Dynamite(local_player->inventory_size);
10952 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10954 player->dynabomb_count++;
10955 player->dynabombs_left++;
10957 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10959 player->dynabomb_size++;
10961 else if (element == EL_DYNABOMB_INCREASE_POWER)
10963 player->dynabomb_xl = TRUE;
10965 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
10966 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
10968 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
10969 element - EL_KEY_1 : element - EL_EM_KEY_1);
10971 player->key[key_nr] = TRUE;
10973 DrawGameValue_Keys(player);
10975 redraw_mask |= REDRAW_DOOR_1;
10977 else if (IS_ENVELOPE(element))
10980 player->show_envelope = element;
10982 ShowEnvelope(element - EL_ENVELOPE_1);
10985 else if (IS_DROPPABLE(element) ||
10986 IS_THROWABLE(element)) /* can be collected and dropped */
10990 if (element_info[element].collect_count == 0)
10991 player->inventory_infinite_element = element;
10993 for (i = 0; i < element_info[element].collect_count; i++)
10994 if (player->inventory_size < MAX_INVENTORY_SIZE)
10995 player->inventory_element[player->inventory_size++] = element;
10997 DrawGameValue_Dynamite(local_player->inventory_size);
10999 else if (element_info[element].collect_count > 0)
11001 local_player->gems_still_needed -=
11002 element_info[element].collect_count;
11003 if (local_player->gems_still_needed < 0)
11004 local_player->gems_still_needed = 0;
11006 DrawGameValue_Emeralds(local_player->gems_still_needed);
11009 RaiseScoreElement(element);
11010 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11012 CheckTriggeredElementChangeByPlayer(x, y, element,
11013 CE_OTHER_GETS_COLLECTED,
11014 player->index_bit, dig_side);
11017 if (mode == DF_SNAP)
11018 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11023 else if (IS_PUSHABLE(element))
11025 if (mode == DF_SNAP && element != EL_BD_ROCK)
11026 return MF_NO_ACTION;
11028 if (CAN_FALL(element) && dy)
11029 return MF_NO_ACTION;
11031 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11032 !(element == EL_SPRING && level.use_spring_bug))
11033 return MF_NO_ACTION;
11036 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11037 ((move_direction & MV_VERTICAL &&
11038 ((element_info[element].move_pattern & MV_LEFT &&
11039 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11040 (element_info[element].move_pattern & MV_RIGHT &&
11041 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11042 (move_direction & MV_HORIZONTAL &&
11043 ((element_info[element].move_pattern & MV_UP &&
11044 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11045 (element_info[element].move_pattern & MV_DOWN &&
11046 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11047 return MF_NO_ACTION;
11051 /* do not push elements already moving away faster than player */
11052 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11053 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11054 return MF_NO_ACTION;
11056 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11057 return MF_NO_ACTION;
11061 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11063 if (player->push_delay_value == -1)
11064 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11066 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11068 if (!player->is_pushing)
11069 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11073 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11074 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11075 !player_is_pushing))
11076 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11079 if (!player->is_pushing &&
11080 game.engine_version >= VERSION_IDENT(2,2,0,7))
11081 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11085 printf("::: push delay: %ld [%d, %d] [%d]\n",
11086 player->push_delay_value, FrameCounter, game.engine_version,
11087 player->is_pushing);
11090 player->is_pushing = TRUE;
11092 if (!(IN_LEV_FIELD(nextx, nexty) &&
11093 (IS_FREE(nextx, nexty) ||
11094 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11095 IS_SB_ELEMENT(element)))))
11096 return MF_NO_ACTION;
11098 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11099 return MF_NO_ACTION;
11101 if (player->push_delay == 0) /* new pushing; restart delay */
11102 player->push_delay = FrameCounter;
11104 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11105 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11106 element != EL_SPRING && element != EL_BALLOON)
11108 /* make sure that there is no move delay before next try to push */
11109 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11110 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11112 return MF_NO_ACTION;
11116 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11119 if (IS_SB_ELEMENT(element))
11121 if (element == EL_SOKOBAN_FIELD_FULL)
11123 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11124 local_player->sokobanfields_still_needed++;
11127 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11129 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11130 local_player->sokobanfields_still_needed--;
11133 Feld[x][y] = EL_SOKOBAN_OBJECT;
11135 if (Back[x][y] == Back[nextx][nexty])
11136 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11137 else if (Back[x][y] != 0)
11138 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11141 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11144 if (local_player->sokobanfields_still_needed == 0 &&
11145 game.emulation == EMU_SOKOBAN)
11147 player->LevelSolved = player->GameOver = TRUE;
11148 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11152 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11154 InitMovingField(x, y, move_direction);
11155 GfxAction[x][y] = ACTION_PUSHING;
11157 if (mode == DF_SNAP)
11158 ContinueMoving(x, y);
11160 MovPos[x][y] = (dx != 0 ? dx : dy);
11162 Pushed[x][y] = TRUE;
11163 Pushed[nextx][nexty] = TRUE;
11165 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11166 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11168 player->push_delay_value = -1; /* get new value later */
11171 /* check for element change _after_ element has been pushed! */
11175 /* !!! TEST ONLY !!! */
11176 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11177 player->index_bit, dig_side);
11178 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11179 player->index_bit, dig_side);
11181 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11182 player->index_bit, dig_side);
11183 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11184 player->index_bit, dig_side);
11190 else if (IS_SWITCHABLE(element))
11192 if (PLAYER_SWITCHING(player, x, y))
11195 player->is_switching = TRUE;
11196 player->switch_x = x;
11197 player->switch_y = y;
11199 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11201 if (element == EL_ROBOT_WHEEL)
11203 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11207 DrawLevelField(x, y);
11209 else if (element == EL_SP_TERMINAL)
11213 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11215 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11217 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11218 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11221 else if (IS_BELT_SWITCH(element))
11223 ToggleBeltSwitch(x, y);
11225 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11226 element == EL_SWITCHGATE_SWITCH_DOWN)
11228 ToggleSwitchgateSwitch(x, y);
11230 else if (element == EL_LIGHT_SWITCH ||
11231 element == EL_LIGHT_SWITCH_ACTIVE)
11233 ToggleLightSwitch(x, y);
11236 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11237 SND_LIGHT_SWITCH_ACTIVATING :
11238 SND_LIGHT_SWITCH_DEACTIVATING);
11241 else if (element == EL_TIMEGATE_SWITCH)
11243 ActivateTimegateSwitch(x, y);
11245 else if (element == EL_BALLOON_SWITCH_LEFT ||
11246 element == EL_BALLOON_SWITCH_RIGHT ||
11247 element == EL_BALLOON_SWITCH_UP ||
11248 element == EL_BALLOON_SWITCH_DOWN ||
11249 element == EL_BALLOON_SWITCH_ANY)
11251 if (element == EL_BALLOON_SWITCH_ANY)
11252 game.balloon_dir = move_direction;
11254 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11255 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11256 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11257 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11260 else if (element == EL_LAMP)
11262 Feld[x][y] = EL_LAMP_ACTIVE;
11263 local_player->lights_still_needed--;
11265 DrawLevelField(x, y);
11267 else if (element == EL_TIME_ORB_FULL)
11269 Feld[x][y] = EL_TIME_ORB_EMPTY;
11271 DrawGameValue_Time(TimeLeft);
11273 DrawLevelField(x, y);
11276 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11284 if (!PLAYER_SWITCHING(player, x, y))
11286 player->is_switching = TRUE;
11287 player->switch_x = x;
11288 player->switch_y = y;
11291 /* !!! TEST ONLY !!! */
11292 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11293 player->index_bit, dig_side);
11294 CheckTriggeredElementChangeByPlayer(x, y, element,
11295 CE_OTHER_IS_SWITCHING,
11296 player->index_bit, dig_side);
11298 CheckTriggeredElementChangeByPlayer(x, y, element,
11299 CE_OTHER_IS_SWITCHING,
11300 player->index_bit, dig_side);
11301 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11302 player->index_bit, dig_side);
11307 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11308 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11309 player->index_bit, dig_side);
11310 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11311 player->index_bit, dig_side);
11313 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11314 player->index_bit, dig_side);
11315 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11316 player->index_bit, dig_side);
11320 return MF_NO_ACTION;
11323 player->push_delay = 0;
11325 if (Feld[x][y] != element) /* really digged/collected something */
11326 player->is_collecting = !player->is_digging;
11331 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11333 int jx = player->jx, jy = player->jy;
11334 int x = jx + dx, y = jy + dy;
11335 int snap_direction = (dx == -1 ? MV_LEFT :
11336 dx == +1 ? MV_RIGHT :
11338 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11341 if (player->MovPos != 0)
11344 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11348 if (!player->active || !IN_LEV_FIELD(x, y))
11356 if (player->MovPos == 0)
11357 player->is_pushing = FALSE;
11359 player->is_snapping = FALSE;
11361 if (player->MovPos == 0)
11363 player->is_moving = FALSE;
11364 player->is_digging = FALSE;
11365 player->is_collecting = FALSE;
11371 if (player->is_snapping)
11374 player->MovDir = snap_direction;
11377 if (player->MovPos == 0)
11380 player->is_moving = FALSE;
11381 player->is_digging = FALSE;
11382 player->is_collecting = FALSE;
11385 player->is_dropping = FALSE;
11387 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11390 player->is_snapping = TRUE;
11393 if (player->MovPos == 0)
11396 player->is_moving = FALSE;
11397 player->is_digging = FALSE;
11398 player->is_collecting = FALSE;
11402 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11403 DrawLevelField(player->last_jx, player->last_jy);
11406 DrawLevelField(x, y);
11415 boolean DropElement(struct PlayerInfo *player)
11417 static int trigger_sides[4] =
11419 CH_SIDE_LEFT, /* dropping left */
11420 CH_SIDE_RIGHT, /* dropping right */
11421 CH_SIDE_TOP, /* dropping up */
11422 CH_SIDE_BOTTOM, /* dropping down */
11424 int old_element, new_element;
11425 int dropx = player->jx, dropy = player->jy;
11426 int drop_direction = player->MovDir;
11427 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11428 int drop_element = (player->inventory_size > 0 ?
11429 player->inventory_element[player->inventory_size - 1] :
11430 player->inventory_infinite_element != EL_UNDEFINED ?
11431 player->inventory_infinite_element :
11432 player->dynabombs_left > 0 ?
11433 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11436 if (IS_THROWABLE(drop_element))
11438 dropx += GET_DX_FROM_DIR(drop_direction);
11439 dropy += GET_DY_FROM_DIR(drop_direction);
11441 if (!IN_LEV_FIELD(dropx, dropy))
11445 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11446 new_element = drop_element; /* default: no change when dropping */
11448 /* check if player is active, not moving and ready to drop */
11449 if (!player->active || player->MovPos || player->drop_delay > 0)
11452 /* check if player has anything that can be dropped */
11454 if (new_element == EL_UNDEFINED)
11457 if (player->inventory_size == 0 &&
11458 player->inventory_infinite_element == EL_UNDEFINED &&
11459 player->dynabombs_left == 0)
11463 /* check if anything can be dropped at the current position */
11464 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11467 /* collected custom elements can only be dropped on empty fields */
11469 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11472 if (player->inventory_size > 0 &&
11473 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11474 && old_element != EL_EMPTY)
11478 if (old_element != EL_EMPTY)
11479 Back[dropx][dropy] = old_element; /* store old element on this field */
11481 ResetGfxAnimation(dropx, dropy);
11482 ResetRandomAnimationValue(dropx, dropy);
11484 if (player->inventory_size > 0 ||
11485 player->inventory_infinite_element != EL_UNDEFINED)
11487 if (player->inventory_size > 0)
11489 player->inventory_size--;
11492 new_element = player->inventory_element[player->inventory_size];
11495 DrawGameValue_Dynamite(local_player->inventory_size);
11497 if (new_element == EL_DYNAMITE)
11498 new_element = EL_DYNAMITE_ACTIVE;
11499 else if (new_element == EL_SP_DISK_RED)
11500 new_element = EL_SP_DISK_RED_ACTIVE;
11503 Feld[dropx][dropy] = new_element;
11505 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11506 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11507 el2img(Feld[dropx][dropy]), 0);
11509 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11512 /* needed if previous element just changed to "empty" in the last frame */
11513 Changed[dropx][dropy] = 0; /* allow another change */
11517 /* !!! TEST ONLY !!! */
11518 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11519 player->index_bit, drop_side);
11520 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11521 CE_OTHER_GETS_DROPPED,
11522 player->index_bit, drop_side);
11524 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11525 CE_OTHER_GETS_DROPPED,
11526 player->index_bit, drop_side);
11527 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11528 player->index_bit, drop_side);
11531 TestIfElementTouchesCustomElement(dropx, dropy);
11533 else /* player is dropping a dyna bomb */
11535 player->dynabombs_left--;
11538 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
11541 Feld[dropx][dropy] = new_element;
11543 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11544 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11545 el2img(Feld[dropx][dropy]), 0);
11547 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11554 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11557 InitField_WithBug1(dropx, dropy, FALSE);
11559 InitField(dropx, dropy, FALSE);
11560 if (CAN_MOVE(Feld[dropx][dropy]))
11561 InitMovDir(dropx, dropy);
11565 new_element = Feld[dropx][dropy]; /* element might have changed */
11567 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11568 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11571 int move_stepsize = element_info[new_element].move_stepsize;
11573 int move_direction, nextx, nexty;
11575 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11576 MovDir[dropx][dropy] = drop_direction;
11578 move_direction = MovDir[dropx][dropy];
11579 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11580 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11583 Changed[dropx][dropy] = 0; /* allow another change */
11584 CheckCollision[dropx][dropy] = 2;
11587 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
11590 WasJustMoving[dropx][dropy] = 3;
11593 InitMovingField(dropx, dropy, move_direction);
11594 ContinueMoving(dropx, dropy);
11601 Changed[dropx][dropy] = 0; /* allow another change */
11604 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
11606 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
11607 CE_HITTING_SOMETHING, move_direction);
11615 player->drop_delay = 2 * TILEX / move_stepsize + 1;
11620 player->drop_delay = 8 + 8 + 8;
11624 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11629 player->is_dropping = TRUE;
11635 /* ------------------------------------------------------------------------- */
11636 /* game sound playing functions */
11637 /* ------------------------------------------------------------------------- */
11639 static int *loop_sound_frame = NULL;
11640 static int *loop_sound_volume = NULL;
11642 void InitPlayLevelSound()
11644 int num_sounds = getSoundListSize();
11646 checked_free(loop_sound_frame);
11647 checked_free(loop_sound_volume);
11649 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11650 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11653 static void PlayLevelSound(int x, int y, int nr)
11655 int sx = SCREENX(x), sy = SCREENY(y);
11656 int volume, stereo_position;
11657 int max_distance = 8;
11658 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11660 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11661 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11664 if (!IN_LEV_FIELD(x, y) ||
11665 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11666 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11669 volume = SOUND_MAX_VOLUME;
11671 if (!IN_SCR_FIELD(sx, sy))
11673 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11674 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11676 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11679 stereo_position = (SOUND_MAX_LEFT +
11680 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11681 (SCR_FIELDX + 2 * max_distance));
11683 if (IS_LOOP_SOUND(nr))
11685 /* This assures that quieter loop sounds do not overwrite louder ones,
11686 while restarting sound volume comparison with each new game frame. */
11688 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11691 loop_sound_volume[nr] = volume;
11692 loop_sound_frame[nr] = FrameCounter;
11695 PlaySoundExt(nr, volume, stereo_position, type);
11698 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11700 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11701 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11702 y < LEVELY(BY1) ? LEVELY(BY1) :
11703 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11707 static void PlayLevelSoundAction(int x, int y, int action)
11709 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11712 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11714 int sound_effect = element_info[element].sound[action];
11716 if (sound_effect != SND_UNDEFINED)
11717 PlayLevelSound(x, y, sound_effect);
11720 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11723 int sound_effect = element_info[element].sound[action];
11725 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11726 PlayLevelSound(x, y, sound_effect);
11729 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11731 int sound_effect = element_info[Feld[x][y]].sound[action];
11733 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11734 PlayLevelSound(x, y, sound_effect);
11737 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11739 int sound_effect = element_info[Feld[x][y]].sound[action];
11741 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11742 StopSound(sound_effect);
11745 static void PlayLevelMusic()
11747 if (levelset.music[level_nr] != MUS_UNDEFINED)
11748 PlayMusic(levelset.music[level_nr]); /* from config file */
11750 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11753 void RaiseScore(int value)
11755 local_player->score += value;
11757 DrawGameValue_Score(local_player->score);
11760 void RaiseScoreElement(int element)
11765 case EL_BD_DIAMOND:
11766 case EL_EMERALD_YELLOW:
11767 case EL_EMERALD_RED:
11768 case EL_EMERALD_PURPLE:
11769 case EL_SP_INFOTRON:
11770 RaiseScore(level.score[SC_EMERALD]);
11773 RaiseScore(level.score[SC_DIAMOND]);
11776 RaiseScore(level.score[SC_CRYSTAL]);
11779 RaiseScore(level.score[SC_PEARL]);
11782 case EL_BD_BUTTERFLY:
11783 case EL_SP_ELECTRON:
11784 RaiseScore(level.score[SC_BUG]);
11787 case EL_BD_FIREFLY:
11788 case EL_SP_SNIKSNAK:
11789 RaiseScore(level.score[SC_SPACESHIP]);
11792 case EL_DARK_YAMYAM:
11793 RaiseScore(level.score[SC_YAMYAM]);
11796 RaiseScore(level.score[SC_ROBOT]);
11799 RaiseScore(level.score[SC_PACMAN]);
11802 RaiseScore(level.score[SC_NUT]);
11805 case EL_SP_DISK_RED:
11806 case EL_DYNABOMB_INCREASE_NUMBER:
11807 case EL_DYNABOMB_INCREASE_SIZE:
11808 case EL_DYNABOMB_INCREASE_POWER:
11809 RaiseScore(level.score[SC_DYNAMITE]);
11811 case EL_SHIELD_NORMAL:
11812 case EL_SHIELD_DEADLY:
11813 RaiseScore(level.score[SC_SHIELD]);
11815 case EL_EXTRA_TIME:
11816 RaiseScore(level.score[SC_TIME_BONUS]);
11822 RaiseScore(level.score[SC_KEY]);
11825 RaiseScore(element_info[element].collect_score);
11830 void RequestQuitGame(boolean ask_if_really_quit)
11832 if (AllPlayersGone ||
11833 !ask_if_really_quit ||
11834 level_editor_test_game ||
11835 Request("Do you really want to quit the game ?",
11836 REQ_ASK | REQ_STAY_CLOSED))
11838 #if defined(PLATFORM_UNIX)
11839 if (options.network)
11840 SendToServer_StopPlaying();
11844 game_status = GAME_MODE_MAIN;
11852 if (tape.playing && tape.deactivate_display)
11853 TapeDeactivateDisplayOff(TRUE);
11856 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11859 if (tape.playing && tape.deactivate_display)
11860 TapeDeactivateDisplayOn();
11867 /* ---------- new game button stuff ---------------------------------------- */
11869 /* graphic position values for game buttons */
11870 #define GAME_BUTTON_XSIZE 30
11871 #define GAME_BUTTON_YSIZE 30
11872 #define GAME_BUTTON_XPOS 5
11873 #define GAME_BUTTON_YPOS 215
11874 #define SOUND_BUTTON_XPOS 5
11875 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11877 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11878 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11879 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11880 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11881 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11882 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11889 } gamebutton_info[NUM_GAME_BUTTONS] =
11892 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11897 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11898 GAME_CTRL_ID_PAUSE,
11902 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11907 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11908 SOUND_CTRL_ID_MUSIC,
11909 "background music on/off"
11912 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11913 SOUND_CTRL_ID_LOOPS,
11914 "sound loops on/off"
11917 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11918 SOUND_CTRL_ID_SIMPLE,
11919 "normal sounds on/off"
11923 void CreateGameButtons()
11927 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11929 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11930 struct GadgetInfo *gi;
11933 unsigned long event_mask;
11934 int gd_xoffset, gd_yoffset;
11935 int gd_x1, gd_x2, gd_y1, gd_y2;
11938 gd_xoffset = gamebutton_info[i].x;
11939 gd_yoffset = gamebutton_info[i].y;
11940 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11941 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11943 if (id == GAME_CTRL_ID_STOP ||
11944 id == GAME_CTRL_ID_PAUSE ||
11945 id == GAME_CTRL_ID_PLAY)
11947 button_type = GD_TYPE_NORMAL_BUTTON;
11949 event_mask = GD_EVENT_RELEASED;
11950 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11951 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11955 button_type = GD_TYPE_CHECK_BUTTON;
11957 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11958 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11959 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11960 event_mask = GD_EVENT_PRESSED;
11961 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11962 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11965 gi = CreateGadget(GDI_CUSTOM_ID, id,
11966 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11967 GDI_X, DX + gd_xoffset,
11968 GDI_Y, DY + gd_yoffset,
11969 GDI_WIDTH, GAME_BUTTON_XSIZE,
11970 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11971 GDI_TYPE, button_type,
11972 GDI_STATE, GD_BUTTON_UNPRESSED,
11973 GDI_CHECKED, checked,
11974 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11975 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11976 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11977 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11978 GDI_EVENT_MASK, event_mask,
11979 GDI_CALLBACK_ACTION, HandleGameButtons,
11983 Error(ERR_EXIT, "cannot create gadget");
11985 game_gadget[id] = gi;
11989 void FreeGameButtons()
11993 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11994 FreeGadget(game_gadget[i]);
11997 static void MapGameButtons()
12001 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12002 MapGadget(game_gadget[i]);
12005 void UnmapGameButtons()
12009 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12010 UnmapGadget(game_gadget[i]);
12013 static void HandleGameButtons(struct GadgetInfo *gi)
12015 int id = gi->custom_id;
12017 if (game_status != GAME_MODE_PLAYING)
12022 case GAME_CTRL_ID_STOP:
12023 RequestQuitGame(TRUE);
12026 case GAME_CTRL_ID_PAUSE:
12027 if (options.network)
12029 #if defined(PLATFORM_UNIX)
12031 SendToServer_ContinuePlaying();
12033 SendToServer_PausePlaying();
12037 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12040 case GAME_CTRL_ID_PLAY:
12043 #if defined(PLATFORM_UNIX)
12044 if (options.network)
12045 SendToServer_ContinuePlaying();
12049 tape.pausing = FALSE;
12050 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12055 case SOUND_CTRL_ID_MUSIC:
12056 if (setup.sound_music)
12058 setup.sound_music = FALSE;
12061 else if (audio.music_available)
12063 setup.sound = setup.sound_music = TRUE;
12065 SetAudioMode(setup.sound);
12071 case SOUND_CTRL_ID_LOOPS:
12072 if (setup.sound_loops)
12073 setup.sound_loops = FALSE;
12074 else if (audio.loops_available)
12076 setup.sound = setup.sound_loops = TRUE;
12077 SetAudioMode(setup.sound);
12081 case SOUND_CTRL_ID_SIMPLE:
12082 if (setup.sound_simple)
12083 setup.sound_simple = FALSE;
12084 else if (audio.sound_available)
12086 setup.sound = setup.sound_simple = TRUE;
12087 SetAudioMode(setup.sound);