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(NETWORK_AVALIABLE)
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;
3071 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3072 (x != ex || y != ey))
3073 Back[x][y] = element;
3075 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3076 Back[x][y] = element;
3080 /* ignite explodable elements reached by other explosion */
3081 if (element == EL_EXPLOSION)
3082 element = Store2[x][y];
3085 if (AmoebaNr[x][y] &&
3086 (element == EL_AMOEBA_FULL ||
3087 element == EL_BD_AMOEBA ||
3088 element == EL_AMOEBA_GROWING))
3090 AmoebaCnt[AmoebaNr[x][y]]--;
3091 AmoebaCnt2[AmoebaNr[x][y]]--;
3097 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3099 switch(StorePlayer[ex][ey])
3102 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3105 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3108 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3112 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3117 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3118 Store[x][y] = EL_EMPTY;
3120 if (game.emulation == EMU_SUPAPLEX)
3121 Store[x][y] = EL_EMPTY;
3124 else if (center_element == EL_MOLE)
3125 Store[x][y] = EL_EMERALD_RED;
3126 else if (center_element == EL_PENGUIN)
3127 Store[x][y] = EL_EMERALD_PURPLE;
3128 else if (center_element == EL_BUG)
3129 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3130 else if (center_element == EL_BD_BUTTERFLY)
3131 Store[x][y] = EL_BD_DIAMOND;
3132 else if (center_element == EL_SP_ELECTRON)
3133 Store[x][y] = EL_SP_INFOTRON;
3134 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3135 Store[x][y] = level.amoeba_content;
3136 else if (center_element == EL_YAMYAM)
3137 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3138 else if (IS_CUSTOM_ELEMENT(center_element) &&
3139 element_info[center_element].content[xx][yy] != EL_EMPTY)
3140 Store[x][y] = element_info[center_element].content[xx][yy];
3141 else if (element == EL_WALL_EMERALD)
3142 Store[x][y] = EL_EMERALD;
3143 else if (element == EL_WALL_DIAMOND)
3144 Store[x][y] = EL_DIAMOND;
3145 else if (element == EL_WALL_BD_DIAMOND)
3146 Store[x][y] = EL_BD_DIAMOND;
3147 else if (element == EL_WALL_EMERALD_YELLOW)
3148 Store[x][y] = EL_EMERALD_YELLOW;
3149 else if (element == EL_WALL_EMERALD_RED)
3150 Store[x][y] = EL_EMERALD_RED;
3151 else if (element == EL_WALL_EMERALD_PURPLE)
3152 Store[x][y] = EL_EMERALD_PURPLE;
3153 else if (element == EL_WALL_PEARL)
3154 Store[x][y] = EL_PEARL;
3155 else if (element == EL_WALL_CRYSTAL)
3156 Store[x][y] = EL_CRYSTAL;
3157 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3158 Store[x][y] = element_info[element].content[1][1];
3160 Store[x][y] = EL_EMPTY;
3162 if (x != ex || y != ey ||
3163 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_TYPE_BORDER)
3164 Store2[x][y] = element;
3167 if (AmoebaNr[x][y] &&
3168 (element == EL_AMOEBA_FULL ||
3169 element == EL_BD_AMOEBA ||
3170 element == EL_AMOEBA_GROWING))
3172 AmoebaCnt[AmoebaNr[x][y]]--;
3173 AmoebaCnt2[AmoebaNr[x][y]]--;
3179 MovDir[x][y] = MovPos[x][y] = 0;
3180 GfxDir[x][y] = MovDir[x][y];
3185 Feld[x][y] = EL_EXPLOSION;
3187 GfxElement[x][y] = center_element;
3189 GfxElement[x][y] = EL_UNDEFINED;
3192 ExplodePhase[x][y] = 1;
3194 ExplodeDelay[x][y] = last_phase;
3199 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3201 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3208 if (center_element == EL_YAMYAM)
3209 game.yamyam_content_nr =
3210 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3223 GfxFrame[x][y] = 0; /* restart explosion animation */
3227 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3231 last_phase = ExplodeDelay[x][y];
3234 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3238 /* activate this even in non-DEBUG version until cause for crash in
3239 getGraphicAnimationFrame() (see below) is found and eliminated */
3243 if (GfxElement[x][y] == EL_UNDEFINED)
3246 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3247 printf("Explode(): This should never happen!\n");
3250 GfxElement[x][y] = EL_EMPTY;
3256 border_element = Store2[x][y];
3257 if (IS_PLAYER(x, y))
3258 border_element = StorePlayer[x][y];
3261 printf("::: phase == %d\n", phase);
3264 if (phase == element_info[border_element].ignition_delay ||
3265 phase == last_phase)
3267 boolean border_explosion = FALSE;
3270 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3272 if (IS_PLAYER(x, y))
3275 KillHeroUnlessExplosionProtected(x, y);
3276 border_explosion = TRUE;
3279 if (phase == last_phase)
3280 printf("::: IS_PLAYER\n");
3283 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3285 Feld[x][y] = Store2[x][y];
3288 border_explosion = TRUE;
3291 if (phase == last_phase)
3292 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3295 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3297 AmoebeUmwandeln(x, y);
3299 border_explosion = TRUE;
3302 if (phase == last_phase)
3303 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3304 element_info[border_element].explosion_delay,
3305 element_info[border_element].ignition_delay,
3311 /* if an element just explodes due to another explosion (chain-reaction),
3312 do not immediately end the new explosion when it was the last frame of
3313 the explosion (as it would be done in the following "if"-statement!) */
3314 if (border_explosion && phase == last_phase)
3321 if (phase == first_phase_after_start)
3323 int element = Store2[x][y];
3325 if (element == EL_BLACK_ORB)
3327 Feld[x][y] = Store2[x][y];
3332 else if (phase == half_phase)
3334 int element = Store2[x][y];
3336 if (IS_PLAYER(x, y))
3337 KillHeroUnlessExplosionProtected(x, y);
3338 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3340 Feld[x][y] = Store2[x][y];
3344 else if (element == EL_AMOEBA_TO_DIAMOND)
3345 AmoebeUmwandeln(x, y);
3349 if (phase == last_phase)
3354 printf("::: done: phase == %d\n", phase);
3358 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3361 element = Feld[x][y] = Store[x][y];
3362 Store[x][y] = Store2[x][y] = 0;
3363 GfxElement[x][y] = EL_UNDEFINED;
3365 /* player can escape from explosions and might therefore be still alive */
3366 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3367 element <= EL_PLAYER_IS_EXPLODING_4)
3368 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3370 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3371 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3372 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3375 /* restore probably existing indestructible background element */
3376 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3377 element = Feld[x][y] = Back[x][y];
3380 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3381 GfxDir[x][y] = MV_NO_MOVING;
3382 ChangeDelay[x][y] = 0;
3383 ChangePage[x][y] = -1;
3386 InitField_WithBug2(x, y, FALSE);
3388 InitField(x, y, FALSE);
3390 /* !!! not needed !!! */
3392 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3393 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3396 if (CAN_MOVE(element))
3401 DrawLevelField(x, y);
3403 TestIfElementTouchesCustomElement(x, y);
3405 if (GFX_CRUMBLED(element))
3406 DrawLevelFieldCrumbledSandNeighbours(x, y);
3408 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3409 StorePlayer[x][y] = 0;
3411 if (ELEM_IS_PLAYER(element))
3412 RelocatePlayer(x, y, element);
3415 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3417 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3421 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3423 int stored = Store[x][y];
3424 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3425 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3429 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3431 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3435 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3439 printf("::: %d / %d [%d - %d]\n",
3440 GfxFrame[x][y], phase - delay, phase, delay);
3444 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3445 element_info[GfxElement[x][y]].token_name,
3450 DrawLevelFieldCrumbledSand(x, y);
3452 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3454 DrawLevelElement(x, y, Back[x][y]);
3455 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3457 else if (IS_WALKABLE_UNDER(Back[x][y]))
3459 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3460 DrawLevelElementThruMask(x, y, Back[x][y]);
3462 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3463 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3467 void DynaExplode(int ex, int ey)
3470 int dynabomb_element = Feld[ex][ey];
3471 int dynabomb_size = 1;
3472 boolean dynabomb_xl = FALSE;
3473 struct PlayerInfo *player;
3474 static int xy[4][2] =
3482 if (IS_ACTIVE_BOMB(dynabomb_element))
3484 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3485 dynabomb_size = player->dynabomb_size;
3486 dynabomb_xl = player->dynabomb_xl;
3487 player->dynabombs_left++;
3490 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3492 for (i = 0; i < NUM_DIRECTIONS; i++)
3494 for (j = 1; j <= dynabomb_size; j++)
3496 int x = ex + j * xy[i][0];
3497 int y = ey + j * xy[i][1];
3500 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3503 element = Feld[x][y];
3505 /* do not restart explosions of fields with active bombs */
3506 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3509 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3513 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3514 !IS_DIGGABLE(element) && !dynabomb_xl)
3517 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3518 !CAN_GROW_INTO(element) && !dynabomb_xl)
3522 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3523 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3524 element != EL_SAND && !dynabomb_xl)
3531 void Bang(int x, int y)
3534 int element = MovingOrBlocked2Element(x, y);
3536 int element = Feld[x][y];
3540 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3542 if (IS_PLAYER(x, y))
3545 struct PlayerInfo *player = PLAYERINFO(x, y);
3547 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3548 player->element_nr);
3553 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3555 if (game.emulation == EMU_SUPAPLEX)
3556 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3558 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3563 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3571 case EL_BD_BUTTERFLY:
3574 case EL_DARK_YAMYAM:
3578 RaiseScoreElement(element);
3579 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3581 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3582 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3583 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3584 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3585 case EL_DYNABOMB_INCREASE_NUMBER:
3586 case EL_DYNABOMB_INCREASE_SIZE:
3587 case EL_DYNABOMB_INCREASE_POWER:
3592 case EL_LAMP_ACTIVE:
3594 case EL_AMOEBA_TO_DIAMOND:
3596 if (IS_PLAYER(x, y))
3597 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3599 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3603 if (element_info[element].explosion_type == EXPLODES_CROSS)
3605 if (CAN_EXPLODE_CROSS(element))
3608 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3613 else if (element_info[element].explosion_type == EXPLODES_1X1)
3615 else if (CAN_EXPLODE_1X1(element))
3617 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3619 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3623 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3626 void SplashAcid(int x, int y)
3629 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3630 (!IN_LEV_FIELD(x - 1, y - 2) ||
3631 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3632 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3634 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3635 (!IN_LEV_FIELD(x + 1, y - 2) ||
3636 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3637 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3639 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3641 /* input: position of element entering acid (obsolete) */
3643 int element = Feld[x][y];
3645 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3648 if (element != EL_ACID_SPLASH_LEFT &&
3649 element != EL_ACID_SPLASH_RIGHT)
3651 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3653 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3654 (!IN_LEV_FIELD(x - 1, y - 1) ||
3655 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3656 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3658 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3659 (!IN_LEV_FIELD(x + 1, y - 1) ||
3660 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3661 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3666 static void InitBeltMovement()
3668 static int belt_base_element[4] =
3670 EL_CONVEYOR_BELT_1_LEFT,
3671 EL_CONVEYOR_BELT_2_LEFT,
3672 EL_CONVEYOR_BELT_3_LEFT,
3673 EL_CONVEYOR_BELT_4_LEFT
3675 static int belt_base_active_element[4] =
3677 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3678 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3679 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3680 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3685 /* set frame order for belt animation graphic according to belt direction */
3686 for (i = 0; i < NUM_BELTS; i++)
3690 for (j = 0; j < NUM_BELT_PARTS; j++)
3692 int element = belt_base_active_element[belt_nr] + j;
3693 int graphic = el2img(element);
3695 if (game.belt_dir[i] == MV_LEFT)
3696 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3698 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3702 for (y = 0; y < lev_fieldy; y++)
3704 for (x = 0; x < lev_fieldx; x++)
3706 int element = Feld[x][y];
3708 for (i = 0; i < NUM_BELTS; i++)
3710 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3712 int e_belt_nr = getBeltNrFromBeltElement(element);
3715 if (e_belt_nr == belt_nr)
3717 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3719 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3727 static void ToggleBeltSwitch(int x, int y)
3729 static int belt_base_element[4] =
3731 EL_CONVEYOR_BELT_1_LEFT,
3732 EL_CONVEYOR_BELT_2_LEFT,
3733 EL_CONVEYOR_BELT_3_LEFT,
3734 EL_CONVEYOR_BELT_4_LEFT
3736 static int belt_base_active_element[4] =
3738 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3739 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3740 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3741 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3743 static int belt_base_switch_element[4] =
3745 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3746 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3747 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3748 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3750 static int belt_move_dir[4] =
3758 int element = Feld[x][y];
3759 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3760 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3761 int belt_dir = belt_move_dir[belt_dir_nr];
3764 if (!IS_BELT_SWITCH(element))
3767 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3768 game.belt_dir[belt_nr] = belt_dir;
3770 if (belt_dir_nr == 3)
3773 /* set frame order for belt animation graphic according to belt direction */
3774 for (i = 0; i < NUM_BELT_PARTS; i++)
3776 int element = belt_base_active_element[belt_nr] + i;
3777 int graphic = el2img(element);
3779 if (belt_dir == MV_LEFT)
3780 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3782 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3785 for (yy = 0; yy < lev_fieldy; yy++)
3787 for (xx = 0; xx < lev_fieldx; xx++)
3789 int element = Feld[xx][yy];
3791 if (IS_BELT_SWITCH(element))
3793 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3795 if (e_belt_nr == belt_nr)
3797 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3798 DrawLevelField(xx, yy);
3801 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3803 int e_belt_nr = getBeltNrFromBeltElement(element);
3805 if (e_belt_nr == belt_nr)
3807 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3809 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3810 DrawLevelField(xx, yy);
3813 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3815 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3817 if (e_belt_nr == belt_nr)
3819 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3821 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3822 DrawLevelField(xx, yy);
3829 static void ToggleSwitchgateSwitch(int x, int y)
3833 game.switchgate_pos = !game.switchgate_pos;
3835 for (yy = 0; yy < lev_fieldy; yy++)
3837 for (xx = 0; xx < lev_fieldx; xx++)
3839 int element = Feld[xx][yy];
3841 if (element == EL_SWITCHGATE_SWITCH_UP ||
3842 element == EL_SWITCHGATE_SWITCH_DOWN)
3844 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3845 DrawLevelField(xx, yy);
3847 else if (element == EL_SWITCHGATE_OPEN ||
3848 element == EL_SWITCHGATE_OPENING)
3850 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3852 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3854 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3857 else if (element == EL_SWITCHGATE_CLOSED ||
3858 element == EL_SWITCHGATE_CLOSING)
3860 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3862 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3864 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3871 static int getInvisibleActiveFromInvisibleElement(int element)
3873 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3874 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3875 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3879 static int getInvisibleFromInvisibleActiveElement(int element)
3881 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3882 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3883 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3887 static void RedrawAllLightSwitchesAndInvisibleElements()
3891 for (y = 0; y < lev_fieldy; y++)
3893 for (x = 0; x < lev_fieldx; x++)
3895 int element = Feld[x][y];
3897 if (element == EL_LIGHT_SWITCH &&
3898 game.light_time_left > 0)
3900 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3901 DrawLevelField(x, y);
3903 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3904 game.light_time_left == 0)
3906 Feld[x][y] = EL_LIGHT_SWITCH;
3907 DrawLevelField(x, y);
3909 else if (element == EL_INVISIBLE_STEELWALL ||
3910 element == EL_INVISIBLE_WALL ||
3911 element == EL_INVISIBLE_SAND)
3913 if (game.light_time_left > 0)
3914 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3916 DrawLevelField(x, y);
3918 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3919 element == EL_INVISIBLE_WALL_ACTIVE ||
3920 element == EL_INVISIBLE_SAND_ACTIVE)
3922 if (game.light_time_left == 0)
3923 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3925 DrawLevelField(x, y);
3931 static void ToggleLightSwitch(int x, int y)
3933 int element = Feld[x][y];
3935 game.light_time_left =
3936 (element == EL_LIGHT_SWITCH ?
3937 level.time_light * FRAMES_PER_SECOND : 0);
3939 RedrawAllLightSwitchesAndInvisibleElements();
3942 static void ActivateTimegateSwitch(int x, int y)
3946 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3948 for (yy = 0; yy < lev_fieldy; yy++)
3950 for (xx = 0; xx < lev_fieldx; xx++)
3952 int element = Feld[xx][yy];
3954 if (element == EL_TIMEGATE_CLOSED ||
3955 element == EL_TIMEGATE_CLOSING)
3957 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3958 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3962 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3964 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3965 DrawLevelField(xx, yy);
3972 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3975 inline static int getElementMoveStepsize(int x, int y)
3977 int element = Feld[x][y];
3978 int direction = MovDir[x][y];
3979 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3980 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3981 int horiz_move = (dx != 0);
3982 int sign = (horiz_move ? dx : dy);
3983 int step = sign * element_info[element].move_stepsize;
3985 /* special values for move stepsize for spring and things on conveyor belt */
3989 if (element == EL_SPRING)
3990 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3991 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3992 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3993 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3995 if (CAN_FALL(element) &&
3996 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3997 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3998 else if (element == EL_SPRING)
3999 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4006 void Impact(int x, int y)
4008 boolean lastline = (y == lev_fieldy-1);
4009 boolean object_hit = FALSE;
4010 boolean impact = (lastline || object_hit);
4011 int element = Feld[x][y];
4012 int smashed = EL_STEELWALL;
4014 if (!lastline) /* check if element below was hit */
4016 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4019 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4020 MovDir[x][y + 1] != MV_DOWN ||
4021 MovPos[x][y + 1] <= TILEY / 2));
4024 object_hit = !IS_FREE(x, y + 1);
4027 /* do not smash moving elements that left the smashed field in time */
4028 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4029 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4033 smashed = MovingOrBlocked2Element(x, y + 1);
4035 impact = (lastline || object_hit);
4038 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4040 SplashAcid(x, y + 1);
4044 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4045 /* only reset graphic animation if graphic really changes after impact */
4047 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4049 ResetGfxAnimation(x, y);
4050 DrawLevelField(x, y);
4053 if (impact && CAN_EXPLODE_IMPACT(element))
4058 else if (impact && element == EL_PEARL)
4060 ResetGfxAnimation(x, y);
4062 Feld[x][y] = EL_PEARL_BREAKING;
4063 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4066 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4068 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4073 if (impact && element == EL_AMOEBA_DROP)
4075 if (object_hit && IS_PLAYER(x, y + 1))
4076 KillHeroUnlessEnemyProtected(x, y + 1);
4077 else if (object_hit && smashed == EL_PENGUIN)
4081 Feld[x][y] = EL_AMOEBA_GROWING;
4082 Store[x][y] = EL_AMOEBA_WET;
4084 ResetRandomAnimationValue(x, y);
4089 if (object_hit) /* check which object was hit */
4091 if (CAN_PASS_MAGIC_WALL(element) &&
4092 (smashed == EL_MAGIC_WALL ||
4093 smashed == EL_BD_MAGIC_WALL))
4096 int activated_magic_wall =
4097 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4098 EL_BD_MAGIC_WALL_ACTIVE);
4100 /* activate magic wall / mill */
4101 for (yy = 0; yy < lev_fieldy; yy++)
4102 for (xx = 0; xx < lev_fieldx; xx++)
4103 if (Feld[xx][yy] == smashed)
4104 Feld[xx][yy] = activated_magic_wall;
4106 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4107 game.magic_wall_active = TRUE;
4109 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4110 SND_MAGIC_WALL_ACTIVATING :
4111 SND_BD_MAGIC_WALL_ACTIVATING));
4114 if (IS_PLAYER(x, y + 1))
4116 if (CAN_SMASH_PLAYER(element))
4118 KillHeroUnlessEnemyProtected(x, y + 1);
4122 else if (smashed == EL_PENGUIN)
4124 if (CAN_SMASH_PLAYER(element))
4130 else if (element == EL_BD_DIAMOND)
4132 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4138 else if (((element == EL_SP_INFOTRON ||
4139 element == EL_SP_ZONK) &&
4140 (smashed == EL_SP_SNIKSNAK ||
4141 smashed == EL_SP_ELECTRON ||
4142 smashed == EL_SP_DISK_ORANGE)) ||
4143 (element == EL_SP_INFOTRON &&
4144 smashed == EL_SP_DISK_YELLOW))
4150 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4156 else if (CAN_SMASH_EVERYTHING(element))
4158 if (IS_CLASSIC_ENEMY(smashed) ||
4159 CAN_EXPLODE_SMASHED(smashed))
4164 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4166 if (smashed == EL_LAMP ||
4167 smashed == EL_LAMP_ACTIVE)
4172 else if (smashed == EL_NUT)
4174 Feld[x][y + 1] = EL_NUT_BREAKING;
4175 PlayLevelSound(x, y, SND_NUT_BREAKING);
4176 RaiseScoreElement(EL_NUT);
4179 else if (smashed == EL_PEARL)
4181 ResetGfxAnimation(x, y);
4183 Feld[x][y + 1] = EL_PEARL_BREAKING;
4184 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4187 else if (smashed == EL_DIAMOND)
4189 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4190 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4193 else if (IS_BELT_SWITCH(smashed))
4195 ToggleBeltSwitch(x, y + 1);
4197 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4198 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4200 ToggleSwitchgateSwitch(x, y + 1);
4202 else if (smashed == EL_LIGHT_SWITCH ||
4203 smashed == EL_LIGHT_SWITCH_ACTIVE)
4205 ToggleLightSwitch(x, y + 1);
4210 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4213 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4216 /* !!! TEST ONLY !!! */
4217 CheckElementChangeBySide(x, y + 1, smashed, element,
4218 CE_SWITCHED, CH_SIDE_TOP);
4219 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4220 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4222 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4223 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4224 CheckElementChangeBySide(x, y + 1, smashed, element,
4225 CE_SWITCHED, CH_SIDE_TOP);
4231 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4236 /* play sound of magic wall / mill */
4238 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4239 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4241 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4242 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4243 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4244 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4249 /* play sound of object that hits the ground */
4250 if (lastline || object_hit)
4251 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4254 inline static void TurnRoundExt(int x, int y)
4266 { 0, 0 }, { 0, 0 }, { 0, 0 },
4271 int left, right, back;
4275 { MV_DOWN, MV_UP, MV_RIGHT },
4276 { MV_UP, MV_DOWN, MV_LEFT },
4278 { MV_LEFT, MV_RIGHT, MV_DOWN },
4282 { MV_RIGHT, MV_LEFT, MV_UP }
4285 int element = Feld[x][y];
4286 int move_pattern = element_info[element].move_pattern;
4288 int old_move_dir = MovDir[x][y];
4289 int left_dir = turn[old_move_dir].left;
4290 int right_dir = turn[old_move_dir].right;
4291 int back_dir = turn[old_move_dir].back;
4293 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4294 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4295 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4296 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4298 int left_x = x + left_dx, left_y = y + left_dy;
4299 int right_x = x + right_dx, right_y = y + right_dy;
4300 int move_x = x + move_dx, move_y = y + move_dy;
4304 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4306 TestIfBadThingTouchesOtherBadThing(x, y);
4308 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4309 MovDir[x][y] = right_dir;
4310 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4311 MovDir[x][y] = left_dir;
4313 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4315 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4319 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4320 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4322 TestIfBadThingTouchesOtherBadThing(x, y);
4324 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4325 MovDir[x][y] = left_dir;
4326 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4327 MovDir[x][y] = right_dir;
4329 if ((element == EL_SPACESHIP ||
4330 element == EL_SP_SNIKSNAK ||
4331 element == EL_SP_ELECTRON)
4332 && MovDir[x][y] != old_move_dir)
4334 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4338 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4340 TestIfBadThingTouchesOtherBadThing(x, y);
4342 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4343 MovDir[x][y] = left_dir;
4344 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4345 MovDir[x][y] = right_dir;
4347 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4349 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4352 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4354 TestIfBadThingTouchesOtherBadThing(x, y);
4356 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4357 MovDir[x][y] = left_dir;
4358 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4359 MovDir[x][y] = right_dir;
4361 if (MovDir[x][y] != old_move_dir)
4365 else if (element == EL_YAMYAM)
4367 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4368 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4370 if (can_turn_left && can_turn_right)
4371 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4372 else if (can_turn_left)
4373 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4374 else if (can_turn_right)
4375 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4377 MovDir[x][y] = back_dir;
4379 MovDelay[x][y] = 16 + 16 * RND(3);
4381 else if (element == EL_DARK_YAMYAM)
4383 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4385 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4388 if (can_turn_left && can_turn_right)
4389 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4390 else if (can_turn_left)
4391 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4392 else if (can_turn_right)
4393 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4395 MovDir[x][y] = back_dir;
4397 MovDelay[x][y] = 16 + 16 * RND(3);
4399 else if (element == EL_PACMAN)
4401 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4402 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4404 if (can_turn_left && can_turn_right)
4405 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4406 else if (can_turn_left)
4407 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4408 else if (can_turn_right)
4409 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4411 MovDir[x][y] = back_dir;
4413 MovDelay[x][y] = 6 + RND(40);
4415 else if (element == EL_PIG)
4417 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4418 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4419 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4420 boolean should_turn_left, should_turn_right, should_move_on;
4422 int rnd = RND(rnd_value);
4424 should_turn_left = (can_turn_left &&
4426 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4427 y + back_dy + left_dy)));
4428 should_turn_right = (can_turn_right &&
4430 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4431 y + back_dy + right_dy)));
4432 should_move_on = (can_move_on &&
4435 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4436 y + move_dy + left_dy) ||
4437 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4438 y + move_dy + right_dy)));
4440 if (should_turn_left || should_turn_right || should_move_on)
4442 if (should_turn_left && should_turn_right && should_move_on)
4443 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4444 rnd < 2 * rnd_value / 3 ? right_dir :
4446 else if (should_turn_left && should_turn_right)
4447 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4448 else if (should_turn_left && should_move_on)
4449 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4450 else if (should_turn_right && should_move_on)
4451 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4452 else if (should_turn_left)
4453 MovDir[x][y] = left_dir;
4454 else if (should_turn_right)
4455 MovDir[x][y] = right_dir;
4456 else if (should_move_on)
4457 MovDir[x][y] = old_move_dir;
4459 else if (can_move_on && rnd > rnd_value / 8)
4460 MovDir[x][y] = old_move_dir;
4461 else if (can_turn_left && can_turn_right)
4462 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4463 else if (can_turn_left && rnd > rnd_value / 8)
4464 MovDir[x][y] = left_dir;
4465 else if (can_turn_right && rnd > rnd_value/8)
4466 MovDir[x][y] = right_dir;
4468 MovDir[x][y] = back_dir;
4470 xx = x + move_xy[MovDir[x][y]].x;
4471 yy = y + move_xy[MovDir[x][y]].y;
4473 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4474 MovDir[x][y] = old_move_dir;
4478 else if (element == EL_DRAGON)
4480 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4481 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4482 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4484 int rnd = RND(rnd_value);
4487 if (FrameCounter < 1 && x == 0 && y == 29)
4488 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4491 if (can_move_on && rnd > rnd_value / 8)
4492 MovDir[x][y] = old_move_dir;
4493 else if (can_turn_left && can_turn_right)
4494 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4495 else if (can_turn_left && rnd > rnd_value / 8)
4496 MovDir[x][y] = left_dir;
4497 else if (can_turn_right && rnd > rnd_value / 8)
4498 MovDir[x][y] = right_dir;
4500 MovDir[x][y] = back_dir;
4502 xx = x + move_xy[MovDir[x][y]].x;
4503 yy = y + move_xy[MovDir[x][y]].y;
4506 if (FrameCounter < 1 && x == 0 && y == 29)
4507 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4508 xx, yy, Feld[xx][yy],
4513 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4514 MovDir[x][y] = old_move_dir;
4516 if (!IS_FREE(xx, yy))
4517 MovDir[x][y] = old_move_dir;
4521 if (FrameCounter < 1 && x == 0 && y == 29)
4522 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4527 else if (element == EL_MOLE)
4529 boolean can_move_on =
4530 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4531 IS_AMOEBOID(Feld[move_x][move_y]) ||
4532 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4535 boolean can_turn_left =
4536 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4537 IS_AMOEBOID(Feld[left_x][left_y])));
4539 boolean can_turn_right =
4540 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4541 IS_AMOEBOID(Feld[right_x][right_y])));
4543 if (can_turn_left && can_turn_right)
4544 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4545 else if (can_turn_left)
4546 MovDir[x][y] = left_dir;
4548 MovDir[x][y] = right_dir;
4551 if (MovDir[x][y] != old_move_dir)
4554 else if (element == EL_BALLOON)
4556 MovDir[x][y] = game.balloon_dir;
4559 else if (element == EL_SPRING)
4562 if (MovDir[x][y] & MV_HORIZONTAL &&
4563 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4564 MovDir[x][y] = MV_NO_MOVING;
4566 if (MovDir[x][y] & MV_HORIZONTAL &&
4567 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4568 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4569 MovDir[x][y] = MV_NO_MOVING;
4574 else if (element == EL_ROBOT ||
4575 element == EL_SATELLITE ||
4576 element == EL_PENGUIN)
4578 int attr_x = -1, attr_y = -1;
4589 for (i = 0; i < MAX_PLAYERS; i++)
4591 struct PlayerInfo *player = &stored_player[i];
4592 int jx = player->jx, jy = player->jy;
4594 if (!player->active)
4598 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4607 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4608 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4609 game.engine_version < VERSION_IDENT(3,1,0,0)))
4611 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4618 if (element == EL_PENGUIN)
4621 static int xy[4][2] =
4629 for (i = 0; i < NUM_DIRECTIONS; i++)
4631 int ex = x + xy[i][0];
4632 int ey = y + xy[i][1];
4634 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4643 MovDir[x][y] = MV_NO_MOVING;
4645 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4646 else if (attr_x > x)
4647 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4649 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4650 else if (attr_y > y)
4651 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4653 if (element == EL_ROBOT)
4657 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4658 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4659 Moving2Blocked(x, y, &newx, &newy);
4661 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4662 MovDelay[x][y] = 8 + 8 * !RND(3);
4664 MovDelay[x][y] = 16;
4666 else if (element == EL_PENGUIN)
4672 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4674 boolean first_horiz = RND(2);
4675 int new_move_dir = MovDir[x][y];
4678 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4679 Moving2Blocked(x, y, &newx, &newy);
4681 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4685 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4686 Moving2Blocked(x, y, &newx, &newy);
4688 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4691 MovDir[x][y] = old_move_dir;
4695 else /* (element == EL_SATELLITE) */
4701 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4703 boolean first_horiz = RND(2);
4704 int new_move_dir = MovDir[x][y];
4707 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4708 Moving2Blocked(x, y, &newx, &newy);
4710 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4714 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4715 Moving2Blocked(x, y, &newx, &newy);
4717 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4720 MovDir[x][y] = old_move_dir;
4725 else if (move_pattern == MV_TURNING_LEFT ||
4726 move_pattern == MV_TURNING_RIGHT ||
4727 move_pattern == MV_TURNING_LEFT_RIGHT ||
4728 move_pattern == MV_TURNING_RIGHT_LEFT ||
4729 move_pattern == MV_TURNING_RANDOM ||
4730 move_pattern == MV_ALL_DIRECTIONS)
4732 boolean can_turn_left =
4733 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4734 boolean can_turn_right =
4735 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4737 if (move_pattern == MV_TURNING_LEFT)
4738 MovDir[x][y] = left_dir;
4739 else if (move_pattern == MV_TURNING_RIGHT)
4740 MovDir[x][y] = right_dir;
4741 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4742 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4743 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4744 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4745 else if (move_pattern == MV_TURNING_RANDOM)
4746 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4747 can_turn_right && !can_turn_left ? right_dir :
4748 RND(2) ? left_dir : right_dir);
4749 else if (can_turn_left && can_turn_right)
4750 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4751 else if (can_turn_left)
4752 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4753 else if (can_turn_right)
4754 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4756 MovDir[x][y] = back_dir;
4758 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4760 else if (move_pattern == MV_HORIZONTAL ||
4761 move_pattern == MV_VERTICAL)
4763 if (move_pattern & old_move_dir)
4764 MovDir[x][y] = back_dir;
4765 else if (move_pattern == MV_HORIZONTAL)
4766 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4767 else if (move_pattern == MV_VERTICAL)
4768 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4770 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4772 else if (move_pattern & MV_ANY_DIRECTION)
4774 MovDir[x][y] = move_pattern;
4775 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4777 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4779 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4780 MovDir[x][y] = left_dir;
4781 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4782 MovDir[x][y] = right_dir;
4784 if (MovDir[x][y] != old_move_dir)
4785 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4787 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4789 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4790 MovDir[x][y] = right_dir;
4791 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4792 MovDir[x][y] = left_dir;
4794 if (MovDir[x][y] != old_move_dir)
4795 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4797 else if (move_pattern == MV_TOWARDS_PLAYER ||
4798 move_pattern == MV_AWAY_FROM_PLAYER)
4800 int attr_x = -1, attr_y = -1;
4802 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4813 for (i = 0; i < MAX_PLAYERS; i++)
4815 struct PlayerInfo *player = &stored_player[i];
4816 int jx = player->jx, jy = player->jy;
4818 if (!player->active)
4822 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4830 MovDir[x][y] = MV_NO_MOVING;
4832 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4833 else if (attr_x > x)
4834 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4836 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4837 else if (attr_y > y)
4838 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4840 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4842 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4844 boolean first_horiz = RND(2);
4845 int new_move_dir = MovDir[x][y];
4848 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4849 Moving2Blocked(x, y, &newx, &newy);
4851 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4855 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4856 Moving2Blocked(x, y, &newx, &newy);
4858 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4861 MovDir[x][y] = old_move_dir;
4864 else if (move_pattern == MV_WHEN_PUSHED ||
4865 move_pattern == MV_WHEN_DROPPED)
4867 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4868 MovDir[x][y] = MV_NO_MOVING;
4872 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4874 static int test_xy[7][2] =
4884 static int test_dir[7] =
4894 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4895 int move_preference = -1000000; /* start with very low preference */
4896 int new_move_dir = MV_NO_MOVING;
4897 int start_test = RND(4);
4900 for (i = 0; i < NUM_DIRECTIONS; i++)
4902 int move_dir = test_dir[start_test + i];
4903 int move_dir_preference;
4905 xx = x + test_xy[start_test + i][0];
4906 yy = y + test_xy[start_test + i][1];
4908 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4909 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4911 new_move_dir = move_dir;
4916 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4919 move_dir_preference = -1 * RunnerVisit[xx][yy];
4920 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4921 move_dir_preference = PlayerVisit[xx][yy];
4923 if (move_dir_preference > move_preference)
4925 /* prefer field that has not been visited for the longest time */
4926 move_preference = move_dir_preference;
4927 new_move_dir = move_dir;
4929 else if (move_dir_preference == move_preference &&
4930 move_dir == old_move_dir)
4932 /* prefer last direction when all directions are preferred equally */
4933 move_preference = move_dir_preference;
4934 new_move_dir = move_dir;
4938 MovDir[x][y] = new_move_dir;
4939 if (old_move_dir != new_move_dir)
4944 static void TurnRound(int x, int y)
4946 int direction = MovDir[x][y];
4949 GfxDir[x][y] = MovDir[x][y];
4955 GfxDir[x][y] = MovDir[x][y];
4958 if (direction != MovDir[x][y])
4963 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4966 GfxAction[x][y] = ACTION_WAITING;
4970 static boolean JustBeingPushed(int x, int y)
4974 for (i = 0; i < MAX_PLAYERS; i++)
4976 struct PlayerInfo *player = &stored_player[i];
4978 if (player->active && player->is_pushing && player->MovPos)
4980 int next_jx = player->jx + (player->jx - player->last_jx);
4981 int next_jy = player->jy + (player->jy - player->last_jy);
4983 if (x == next_jx && y == next_jy)
4991 void StartMoving(int x, int y)
4994 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4996 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4997 int element = Feld[x][y];
5003 if (MovDelay[x][y] == 0)
5004 GfxAction[x][y] = ACTION_DEFAULT;
5006 /* !!! this should be handled more generic (not only for mole) !!! */
5007 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5008 GfxAction[x][y] = ACTION_DEFAULT;
5011 if (CAN_FALL(element) && y < lev_fieldy - 1)
5013 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5014 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5015 if (JustBeingPushed(x, y))
5018 if (element == EL_QUICKSAND_FULL)
5020 if (IS_FREE(x, y + 1))
5022 InitMovingField(x, y, MV_DOWN);
5023 started_moving = TRUE;
5025 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5026 Store[x][y] = EL_ROCK;
5028 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5030 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5033 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5035 if (!MovDelay[x][y])
5036 MovDelay[x][y] = TILEY + 1;
5045 Feld[x][y] = EL_QUICKSAND_EMPTY;
5046 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5047 Store[x][y + 1] = Store[x][y];
5050 PlayLevelSoundAction(x, y, ACTION_FILLING);
5052 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5056 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5057 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5059 InitMovingField(x, y, MV_DOWN);
5060 started_moving = TRUE;
5062 Feld[x][y] = EL_QUICKSAND_FILLING;
5063 Store[x][y] = element;
5065 PlayLevelSoundAction(x, y, ACTION_FILLING);
5067 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5070 else if (element == EL_MAGIC_WALL_FULL)
5072 if (IS_FREE(x, y + 1))
5074 InitMovingField(x, y, MV_DOWN);
5075 started_moving = TRUE;
5077 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5078 Store[x][y] = EL_CHANGED(Store[x][y]);
5080 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5082 if (!MovDelay[x][y])
5083 MovDelay[x][y] = TILEY/4 + 1;
5092 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5093 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5094 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5098 else if (element == EL_BD_MAGIC_WALL_FULL)
5100 if (IS_FREE(x, y + 1))
5102 InitMovingField(x, y, MV_DOWN);
5103 started_moving = TRUE;
5105 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5106 Store[x][y] = EL_CHANGED2(Store[x][y]);
5108 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5110 if (!MovDelay[x][y])
5111 MovDelay[x][y] = TILEY/4 + 1;
5120 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5121 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5122 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5126 else if (CAN_PASS_MAGIC_WALL(element) &&
5127 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5128 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5130 InitMovingField(x, y, MV_DOWN);
5131 started_moving = TRUE;
5134 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5135 EL_BD_MAGIC_WALL_FILLING);
5136 Store[x][y] = element;
5139 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5141 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5144 SplashAcid(x, y + 1);
5146 InitMovingField(x, y, MV_DOWN);
5147 started_moving = TRUE;
5149 Store[x][y] = EL_ACID;
5151 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5152 GfxAction[x][y + 1] = ACTION_ACTIVE;
5156 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5157 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5159 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5160 CAN_SMASH(element) && WasJustFalling[x][y] &&
5161 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5163 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5164 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5165 (Feld[x][y + 1] == EL_BLOCKED)))
5169 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5170 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5171 WasJustMoving[x][y] && !Pushed[x][y + 1])
5173 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5174 WasJustMoving[x][y])
5179 /* this is needed for a special case not covered by calling "Impact()"
5180 from "ContinueMoving()": if an element moves to a tile directly below
5181 another element which was just falling on that tile (which was empty
5182 in the previous frame), the falling element above would just stop
5183 instead of smashing the element below (in previous version, the above
5184 element was just checked for "moving" instead of "falling", resulting
5185 in incorrect smashes caused by horizontal movement of the above
5186 element; also, the case of the player being the element to smash was
5187 simply not covered here... :-/ ) */
5190 WasJustMoving[x][y] = 0;
5191 WasJustFalling[x][y] = 0;
5194 CheckCollision[x][y] = 0;
5198 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5200 if (MovDir[x][y] == MV_NO_MOVING)
5202 InitMovingField(x, y, MV_DOWN);
5203 started_moving = TRUE;
5206 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5208 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5209 MovDir[x][y] = MV_DOWN;
5211 InitMovingField(x, y, MV_DOWN);
5212 started_moving = TRUE;
5214 else if (element == EL_AMOEBA_DROP)
5216 Feld[x][y] = EL_AMOEBA_GROWING;
5217 Store[x][y] = EL_AMOEBA_WET;
5219 /* Store[x][y + 1] must be zero, because:
5220 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5223 #if OLD_GAME_BEHAVIOUR
5224 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5226 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5227 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5228 element != EL_DX_SUPABOMB)
5231 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5232 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5233 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5234 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5237 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5238 (IS_FREE(x - 1, y + 1) ||
5239 Feld[x - 1][y + 1] == EL_ACID));
5240 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5241 (IS_FREE(x + 1, y + 1) ||
5242 Feld[x + 1][y + 1] == EL_ACID));
5243 boolean can_fall_any = (can_fall_left || can_fall_right);
5244 boolean can_fall_both = (can_fall_left && can_fall_right);
5246 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5248 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5250 if (slippery_type == SLIPPERY_ONLY_LEFT)
5251 can_fall_right = FALSE;
5252 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5253 can_fall_left = FALSE;
5254 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5255 can_fall_right = FALSE;
5256 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5257 can_fall_left = FALSE;
5259 can_fall_any = (can_fall_left || can_fall_right);
5260 can_fall_both = (can_fall_left && can_fall_right);
5265 if (can_fall_both &&
5266 (game.emulation != EMU_BOULDERDASH &&
5267 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5268 can_fall_left = !(can_fall_right = RND(2));
5270 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5271 started_moving = TRUE;
5275 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5277 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5280 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5281 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5282 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5283 int belt_dir = game.belt_dir[belt_nr];
5285 if ((belt_dir == MV_LEFT && left_is_free) ||
5286 (belt_dir == MV_RIGHT && right_is_free))
5289 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5292 InitMovingField(x, y, belt_dir);
5293 started_moving = TRUE;
5296 Pushed[x][y] = TRUE;
5297 Pushed[nextx][y] = TRUE;
5300 GfxAction[x][y] = ACTION_DEFAULT;
5304 MovDir[x][y] = 0; /* if element was moving, stop it */
5309 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5311 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5313 if (CAN_MOVE(element) && !started_moving)
5316 int move_pattern = element_info[element].move_pattern;
5321 if (MovDir[x][y] == MV_NO_MOVING)
5323 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5324 x, y, element, element_info[element].token_name);
5325 printf("StartMoving(): This should never happen!\n");
5330 Moving2Blocked(x, y, &newx, &newy);
5333 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5336 if ((element == EL_SATELLITE ||
5337 element == EL_BALLOON ||
5338 element == EL_SPRING)
5339 && JustBeingPushed(x, y))
5346 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5347 CheckCollision[x][y] && IN_LEV_FIELD_AND_NOT_FREE(newx, newy))
5349 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5350 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5351 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5355 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5356 element, element_info[element].token_name,
5357 WasJustMoving[x][y],
5358 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5359 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5360 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5361 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5365 WasJustMoving[x][y] = 0;
5368 CheckCollision[x][y] = 0;
5370 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5373 if (Feld[x][y] != element) /* element has changed */
5375 element = Feld[x][y];
5376 move_pattern = element_info[element].move_pattern;
5378 if (!CAN_MOVE(element))
5382 if (Feld[x][y] != element) /* element has changed */
5390 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5391 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5393 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5395 Moving2Blocked(x, y, &newx, &newy);
5396 if (Feld[newx][newy] == EL_BLOCKED)
5397 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5403 if (FrameCounter < 1 && x == 0 && y == 29)
5404 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5407 if (!MovDelay[x][y]) /* start new movement phase */
5409 /* all objects that can change their move direction after each step
5410 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5412 if (element != EL_YAMYAM &&
5413 element != EL_DARK_YAMYAM &&
5414 element != EL_PACMAN &&
5415 !(move_pattern & MV_ANY_DIRECTION) &&
5416 move_pattern != MV_TURNING_LEFT &&
5417 move_pattern != MV_TURNING_RIGHT &&
5418 move_pattern != MV_TURNING_LEFT_RIGHT &&
5419 move_pattern != MV_TURNING_RIGHT_LEFT &&
5420 move_pattern != MV_TURNING_RANDOM)
5425 if (FrameCounter < 1 && x == 0 && y == 29)
5426 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5429 if (MovDelay[x][y] && (element == EL_BUG ||
5430 element == EL_SPACESHIP ||
5431 element == EL_SP_SNIKSNAK ||
5432 element == EL_SP_ELECTRON ||
5433 element == EL_MOLE))
5434 DrawLevelField(x, y);
5438 if (MovDelay[x][y]) /* wait some time before next movement */
5443 if (element == EL_YAMYAM)
5446 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5447 DrawLevelElementAnimation(x, y, element);
5451 if (MovDelay[x][y]) /* element still has to wait some time */
5454 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5455 ResetGfxAnimation(x, y);
5459 if (GfxAction[x][y] != ACTION_WAITING)
5460 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5462 GfxAction[x][y] = ACTION_WAITING;
5466 if (element == EL_ROBOT ||
5468 element == EL_PACMAN ||
5470 element == EL_YAMYAM ||
5471 element == EL_DARK_YAMYAM)
5474 DrawLevelElementAnimation(x, y, element);
5476 DrawLevelElementAnimationIfNeeded(x, y, element);
5478 PlayLevelSoundAction(x, y, ACTION_WAITING);
5480 else if (element == EL_SP_ELECTRON)
5481 DrawLevelElementAnimationIfNeeded(x, y, element);
5482 else if (element == EL_DRAGON)
5485 int dir = MovDir[x][y];
5486 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5487 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5488 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5489 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5490 dir == MV_UP ? IMG_FLAMES_1_UP :
5491 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5492 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5495 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5498 GfxAction[x][y] = ACTION_ATTACKING;
5500 if (IS_PLAYER(x, y))
5501 DrawPlayerField(x, y);
5503 DrawLevelField(x, y);
5505 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5507 for (i = 1; i <= 3; i++)
5509 int xx = x + i * dx;
5510 int yy = y + i * dy;
5511 int sx = SCREENX(xx);
5512 int sy = SCREENY(yy);
5513 int flame_graphic = graphic + (i - 1);
5515 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5520 int flamed = MovingOrBlocked2Element(xx, yy);
5524 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5526 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5527 RemoveMovingField(xx, yy);
5529 RemoveField(xx, yy);
5531 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5534 RemoveMovingField(xx, yy);
5538 if (ChangeDelay[xx][yy])
5539 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5540 Feld[xx][yy] == EL_BLOCKED));
5544 ChangeDelay[xx][yy] = 0;
5546 Feld[xx][yy] = EL_FLAMES;
5547 if (IN_SCR_FIELD(sx, sy))
5549 DrawLevelFieldCrumbledSand(xx, yy);
5550 DrawGraphic(sx, sy, flame_graphic, frame);
5555 if (Feld[xx][yy] == EL_FLAMES)
5556 Feld[xx][yy] = EL_EMPTY;
5557 DrawLevelField(xx, yy);
5562 if (MovDelay[x][y]) /* element still has to wait some time */
5564 PlayLevelSoundAction(x, y, ACTION_WAITING);
5570 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5571 for all other elements GfxAction will be set by InitMovingField() */
5572 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5573 GfxAction[x][y] = ACTION_MOVING;
5577 /* now make next step */
5579 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5581 if (DONT_COLLIDE_WITH(element) &&
5582 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5583 !PLAYER_ENEMY_PROTECTED(newx, newy))
5586 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5590 /* player killed by element which is deadly when colliding with */
5592 KillHero(PLAYERINFO(newx, newy));
5599 else if (CAN_MOVE_INTO_ACID(element) &&
5600 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5601 (MovDir[x][y] == MV_DOWN ||
5602 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5604 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5605 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5609 else if ((element == EL_PENGUIN ||
5610 element == EL_ROBOT ||
5611 element == EL_SATELLITE ||
5612 element == EL_BALLOON ||
5613 IS_CUSTOM_ELEMENT(element)) &&
5614 IN_LEV_FIELD(newx, newy) &&
5615 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5618 SplashAcid(newx, newy);
5619 Store[x][y] = EL_ACID;
5621 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5623 if (Feld[newx][newy] == EL_EXIT_OPEN)
5627 DrawLevelField(x, y);
5629 Feld[x][y] = EL_EMPTY;
5630 DrawLevelField(x, y);
5633 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5634 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5635 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5637 local_player->friends_still_needed--;
5638 if (!local_player->friends_still_needed &&
5639 !local_player->GameOver && AllPlayersGone)
5640 local_player->LevelSolved = local_player->GameOver = TRUE;
5644 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5646 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5647 DrawLevelField(newx, newy);
5649 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5651 else if (!IS_FREE(newx, newy))
5653 GfxAction[x][y] = ACTION_WAITING;
5655 if (IS_PLAYER(x, y))
5656 DrawPlayerField(x, y);
5658 DrawLevelField(x, y);
5663 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5665 if (IS_FOOD_PIG(Feld[newx][newy]))
5667 if (IS_MOVING(newx, newy))
5668 RemoveMovingField(newx, newy);
5671 Feld[newx][newy] = EL_EMPTY;
5672 DrawLevelField(newx, newy);
5675 PlayLevelSound(x, y, SND_PIG_DIGGING);
5677 else if (!IS_FREE(newx, newy))
5679 if (IS_PLAYER(x, y))
5680 DrawPlayerField(x, y);
5682 DrawLevelField(x, y);
5691 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5694 else if (IS_CUSTOM_ELEMENT(element) &&
5695 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5699 !IS_FREE(newx, newy)
5704 int new_element = Feld[newx][newy];
5707 printf("::: '%s' digs '%s' [%d]\n",
5708 element_info[element].token_name,
5709 element_info[Feld[newx][newy]].token_name,
5710 StorePlayer[newx][newy]);
5713 if (!IS_FREE(newx, newy))
5715 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5716 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5719 /* no element can dig solid indestructible elements */
5720 if (IS_INDESTRUCTIBLE(new_element) &&
5721 !IS_DIGGABLE(new_element) &&
5722 !IS_COLLECTIBLE(new_element))
5725 if (AmoebaNr[newx][newy] &&
5726 (new_element == EL_AMOEBA_FULL ||
5727 new_element == EL_BD_AMOEBA ||
5728 new_element == EL_AMOEBA_GROWING))
5730 AmoebaCnt[AmoebaNr[newx][newy]]--;
5731 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5734 if (IS_MOVING(newx, newy))
5735 RemoveMovingField(newx, newy);
5738 RemoveField(newx, newy);
5739 DrawLevelField(newx, newy);
5742 /* if digged element was about to explode, prevent the explosion */
5743 ExplodeField[newx][newy] = EX_TYPE_NONE;
5745 PlayLevelSoundAction(x, y, action);
5750 Store[newx][newy] = EL_EMPTY;
5751 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5752 Store[newx][newy] = element_info[element].move_leave_element;
5754 Store[newx][newy] = EL_EMPTY;
5755 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5756 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5757 Store[newx][newy] = element_info[element].move_leave_element;
5760 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5761 element_info[element].can_leave_element = TRUE;
5764 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5766 RunnerVisit[x][y] = FrameCounter;
5767 PlayerVisit[x][y] /= 8; /* expire player visit path */
5773 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5775 if (!IS_FREE(newx, newy))
5777 if (IS_PLAYER(x, y))
5778 DrawPlayerField(x, y);
5780 DrawLevelField(x, y);
5786 boolean wanna_flame = !RND(10);
5787 int dx = newx - x, dy = newy - y;
5788 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5789 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5790 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5791 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5792 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5793 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5796 IS_CLASSIC_ENEMY(element1) ||
5797 IS_CLASSIC_ENEMY(element2)) &&
5798 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5799 element1 != EL_FLAMES && element2 != EL_FLAMES)
5802 ResetGfxAnimation(x, y);
5803 GfxAction[x][y] = ACTION_ATTACKING;
5806 if (IS_PLAYER(x, y))
5807 DrawPlayerField(x, y);
5809 DrawLevelField(x, y);
5811 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5813 MovDelay[x][y] = 50;
5817 RemoveField(newx, newy);
5819 Feld[newx][newy] = EL_FLAMES;
5820 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5823 RemoveField(newx1, newy1);
5825 Feld[newx1][newy1] = EL_FLAMES;
5827 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5830 RemoveField(newx2, newy2);
5832 Feld[newx2][newy2] = EL_FLAMES;
5839 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5840 Feld[newx][newy] == EL_DIAMOND)
5842 if (IS_MOVING(newx, newy))
5843 RemoveMovingField(newx, newy);
5846 Feld[newx][newy] = EL_EMPTY;
5847 DrawLevelField(newx, newy);
5850 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5852 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5853 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5855 if (AmoebaNr[newx][newy])
5857 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5858 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5859 Feld[newx][newy] == EL_BD_AMOEBA)
5860 AmoebaCnt[AmoebaNr[newx][newy]]--;
5865 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5867 if (IS_MOVING(newx, newy))
5870 RemoveMovingField(newx, newy);
5874 Feld[newx][newy] = EL_EMPTY;
5875 DrawLevelField(newx, newy);
5878 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5880 else if ((element == EL_PACMAN || element == EL_MOLE)
5881 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5883 if (AmoebaNr[newx][newy])
5885 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5886 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5887 Feld[newx][newy] == EL_BD_AMOEBA)
5888 AmoebaCnt[AmoebaNr[newx][newy]]--;
5891 if (element == EL_MOLE)
5893 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5894 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5896 ResetGfxAnimation(x, y);
5897 GfxAction[x][y] = ACTION_DIGGING;
5898 DrawLevelField(x, y);
5900 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5902 return; /* wait for shrinking amoeba */
5904 else /* element == EL_PACMAN */
5906 Feld[newx][newy] = EL_EMPTY;
5907 DrawLevelField(newx, newy);
5908 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5911 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5912 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5913 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5915 /* wait for shrinking amoeba to completely disappear */
5918 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5920 /* object was running against a wall */
5925 if (move_pattern & MV_ANY_DIRECTION &&
5926 move_pattern == MovDir[x][y])
5928 int blocking_element =
5929 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5932 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
5933 element_info[element].token_name,
5934 element_info[blocking_element].token_name,
5938 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5941 element = Feld[x][y]; /* element might have changed */
5946 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5947 DrawLevelElementAnimation(x, y, element);
5949 if (element == EL_BUG ||
5950 element == EL_SPACESHIP ||
5951 element == EL_SP_SNIKSNAK)
5952 DrawLevelField(x, y);
5953 else if (element == EL_MOLE)
5954 DrawLevelField(x, y);
5955 else if (element == EL_BD_BUTTERFLY ||
5956 element == EL_BD_FIREFLY)
5957 DrawLevelElementAnimationIfNeeded(x, y, element);
5958 else if (element == EL_SATELLITE)
5959 DrawLevelElementAnimationIfNeeded(x, y, element);
5960 else if (element == EL_SP_ELECTRON)
5961 DrawLevelElementAnimationIfNeeded(x, y, element);
5964 if (DONT_TOUCH(element))
5965 TestIfBadThingTouchesHero(x, y);
5968 PlayLevelSoundAction(x, y, ACTION_WAITING);
5974 InitMovingField(x, y, MovDir[x][y]);
5976 PlayLevelSoundAction(x, y, ACTION_MOVING);
5980 ContinueMoving(x, y);
5983 void ContinueMoving(int x, int y)
5985 int element = Feld[x][y];
5986 int stored = Store[x][y];
5987 struct ElementInfo *ei = &element_info[element];
5988 int direction = MovDir[x][y];
5989 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5990 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5991 int newx = x + dx, newy = y + dy;
5993 int nextx = newx + dx, nexty = newy + dy;
5996 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5997 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5999 boolean pushed_by_player = Pushed[x][y];
6002 MovPos[x][y] += getElementMoveStepsize(x, y);
6005 if (pushed_by_player && IS_PLAYER(x, y))
6007 /* special case: moving object pushed by player */
6008 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6011 if (pushed_by_player) /* special case: moving object pushed by player */
6012 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6015 if (ABS(MovPos[x][y]) < TILEX)
6017 DrawLevelField(x, y);
6019 return; /* element is still moving */
6022 /* element reached destination field */
6024 Feld[x][y] = EL_EMPTY;
6025 Feld[newx][newy] = element;
6026 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6029 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6031 element = Feld[newx][newy] = EL_ACID;
6034 else if (element == EL_MOLE)
6036 Feld[x][y] = EL_SAND;
6038 DrawLevelFieldCrumbledSandNeighbours(x, y);
6040 else if (element == EL_QUICKSAND_FILLING)
6042 element = Feld[newx][newy] = get_next_element(element);
6043 Store[newx][newy] = Store[x][y];
6045 else if (element == EL_QUICKSAND_EMPTYING)
6047 Feld[x][y] = get_next_element(element);
6048 element = Feld[newx][newy] = Store[x][y];
6050 else if (element == EL_MAGIC_WALL_FILLING)
6052 element = Feld[newx][newy] = get_next_element(element);
6053 if (!game.magic_wall_active)
6054 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6055 Store[newx][newy] = Store[x][y];
6057 else if (element == EL_MAGIC_WALL_EMPTYING)
6059 Feld[x][y] = get_next_element(element);
6060 if (!game.magic_wall_active)
6061 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6062 element = Feld[newx][newy] = Store[x][y];
6064 else if (element == EL_BD_MAGIC_WALL_FILLING)
6066 element = Feld[newx][newy] = get_next_element(element);
6067 if (!game.magic_wall_active)
6068 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6069 Store[newx][newy] = Store[x][y];
6071 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6073 Feld[x][y] = get_next_element(element);
6074 if (!game.magic_wall_active)
6075 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6076 element = Feld[newx][newy] = Store[x][y];
6078 else if (element == EL_AMOEBA_DROPPING)
6080 Feld[x][y] = get_next_element(element);
6081 element = Feld[newx][newy] = Store[x][y];
6083 else if (element == EL_SOKOBAN_OBJECT)
6086 Feld[x][y] = Back[x][y];
6088 if (Back[newx][newy])
6089 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6091 Back[x][y] = Back[newx][newy] = 0;
6094 else if (Store[x][y] == EL_ACID)
6096 element = Feld[newx][newy] = EL_ACID;
6100 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6101 ei->move_leave_element != EL_EMPTY &&
6102 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6103 Store[x][y] != EL_EMPTY))
6105 /* some elements can leave other elements behind after moving */
6107 Feld[x][y] = ei->move_leave_element;
6108 InitField(x, y, FALSE);
6110 if (GFX_CRUMBLED(Feld[x][y]))
6111 DrawLevelFieldCrumbledSandNeighbours(x, y);
6115 Store[x][y] = EL_EMPTY;
6116 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6117 MovDelay[newx][newy] = 0;
6119 if (CAN_CHANGE(element))
6121 /* copy element change control values to new field */
6122 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6123 ChangePage[newx][newy] = ChangePage[x][y];
6124 Changed[newx][newy] = Changed[x][y];
6125 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6128 ChangeDelay[x][y] = 0;
6129 ChangePage[x][y] = -1;
6130 Changed[x][y] = CE_BITMASK_DEFAULT;
6131 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6133 /* copy animation control values to new field */
6134 GfxFrame[newx][newy] = GfxFrame[x][y];
6135 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6136 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6137 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6139 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6141 ResetGfxAnimation(x, y); /* reset animation values for old field */
6144 /* some elements can leave other elements behind after moving */
6146 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6147 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6148 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6150 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6151 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6155 int move_leave_element = ei->move_leave_element;
6157 Feld[x][y] = move_leave_element;
6158 InitField(x, y, FALSE);
6160 if (GFX_CRUMBLED(Feld[x][y]))
6161 DrawLevelFieldCrumbledSandNeighbours(x, y);
6163 if (ELEM_IS_PLAYER(move_leave_element))
6164 RelocatePlayer(x, y, move_leave_element);
6169 /* some elements can leave other elements behind after moving */
6170 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6171 ei->move_leave_element != EL_EMPTY &&
6172 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6173 ei->can_leave_element_last))
6175 Feld[x][y] = ei->move_leave_element;
6176 InitField(x, y, FALSE);
6178 if (GFX_CRUMBLED(Feld[x][y]))
6179 DrawLevelFieldCrumbledSandNeighbours(x, y);
6182 ei->can_leave_element_last = ei->can_leave_element;
6183 ei->can_leave_element = FALSE;
6187 /* 2.1.1 (does not work correctly for spring) */
6188 if (!CAN_MOVE(element))
6189 MovDir[newx][newy] = 0;
6193 /* (does not work for falling objects that slide horizontally) */
6194 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6195 MovDir[newx][newy] = 0;
6198 if (!CAN_MOVE(element) ||
6199 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6200 MovDir[newx][newy] = 0;
6204 if (!CAN_MOVE(element) ||
6205 (CAN_FALL(element) && direction == MV_DOWN))
6206 GfxDir[x][y] = MovDir[newx][newy] = 0;
6208 if (!CAN_MOVE(element) ||
6209 (CAN_FALL(element) && direction == MV_DOWN &&
6210 (element == EL_SPRING ||
6211 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6212 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6213 GfxDir[x][y] = MovDir[newx][newy] = 0;
6219 DrawLevelField(x, y);
6220 DrawLevelField(newx, newy);
6222 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6224 /* prevent pushed element from moving on in pushed direction */
6225 if (pushed_by_player && CAN_MOVE(element) &&
6226 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6227 !(element_info[element].move_pattern & direction))
6228 TurnRound(newx, newy);
6231 /* prevent elements on conveyor belt from moving on in last direction */
6232 if (pushed_by_conveyor && CAN_FALL(element) &&
6233 direction & MV_HORIZONTAL)
6236 if (CAN_MOVE(element))
6237 InitMovDir(newx, newy);
6239 MovDir[newx][newy] = 0;
6241 MovDir[newx][newy] = 0;
6246 if (!pushed_by_player)
6248 int nextx = newx + dx, nexty = newy + dy;
6249 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6251 WasJustMoving[newx][newy] = 3;
6253 if (CAN_FALL(element) && direction == MV_DOWN)
6254 WasJustFalling[newx][newy] = 3;
6256 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6257 CheckCollision[newx][newy] = 2;
6260 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6262 TestIfBadThingTouchesHero(newx, newy);
6263 TestIfBadThingTouchesFriend(newx, newy);
6265 if (!IS_CUSTOM_ELEMENT(element))
6266 TestIfBadThingTouchesOtherBadThing(newx, newy);
6268 else if (element == EL_PENGUIN)
6269 TestIfFriendTouchesBadThing(newx, newy);
6271 if (CAN_FALL(element) && direction == MV_DOWN &&
6272 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6276 if (pushed_by_player)
6278 static int trigger_sides[4] =
6280 CH_SIDE_RIGHT, /* moving left */
6281 CH_SIDE_LEFT, /* moving right */
6282 CH_SIDE_BOTTOM, /* moving up */
6283 CH_SIDE_TOP, /* moving down */
6285 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6286 struct PlayerInfo *player = PLAYERINFO(x, y);
6288 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6289 player->index_bit, dig_side);
6290 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6291 player->index_bit, dig_side);
6296 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6300 if (ChangePage[newx][newy] != -1) /* delayed change */
6301 ChangeElement(newx, newy, ChangePage[newx][newy]);
6306 TestIfElementHitsCustomElement(newx, newy, direction);
6310 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6312 int hitting_element = Feld[newx][newy];
6314 /* !!! fix side (direction) orientation here and elsewhere !!! */
6315 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6319 if (IN_LEV_FIELD(nextx, nexty))
6321 int opposite_direction = MV_DIR_OPPOSITE(direction);
6322 int hitting_side = direction;
6323 int touched_side = opposite_direction;
6324 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6325 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6326 MovDir[nextx][nexty] != direction ||
6327 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6333 CheckElementChangeBySide(nextx, nexty, touched_element,
6334 CE_HIT_BY_SOMETHING, opposite_direction);
6336 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6337 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6339 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6341 struct ElementChangeInfo *change =
6342 &element_info[hitting_element].change_page[i];
6344 if (change->can_change &&
6345 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6346 change->trigger_side & touched_side &&
6347 change->trigger_element == touched_element)
6349 CheckElementChangeByPage(newx, newy, hitting_element,
6350 touched_element, CE_OTHER_IS_HITTING,i);
6356 if (IS_CUSTOM_ELEMENT(touched_element) &&
6357 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6359 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6361 struct ElementChangeInfo *change =
6362 &element_info[touched_element].change_page[i];
6364 if (change->can_change &&
6365 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6366 change->trigger_side & hitting_side &&
6367 change->trigger_element == hitting_element)
6369 CheckElementChangeByPage(nextx, nexty, touched_element,
6370 hitting_element, CE_OTHER_GETS_HIT, i);
6381 TestIfPlayerTouchesCustomElement(newx, newy);
6382 TestIfElementTouchesCustomElement(newx, newy);
6385 int AmoebeNachbarNr(int ax, int ay)
6388 int element = Feld[ax][ay];
6390 static int xy[4][2] =
6398 for (i = 0; i < NUM_DIRECTIONS; i++)
6400 int x = ax + xy[i][0];
6401 int y = ay + xy[i][1];
6403 if (!IN_LEV_FIELD(x, y))
6406 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6407 group_nr = AmoebaNr[x][y];
6413 void AmoebenVereinigen(int ax, int ay)
6415 int i, x, y, xx, yy;
6416 int new_group_nr = AmoebaNr[ax][ay];
6417 static int xy[4][2] =
6425 if (new_group_nr == 0)
6428 for (i = 0; i < NUM_DIRECTIONS; i++)
6433 if (!IN_LEV_FIELD(x, y))
6436 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6437 Feld[x][y] == EL_BD_AMOEBA ||
6438 Feld[x][y] == EL_AMOEBA_DEAD) &&
6439 AmoebaNr[x][y] != new_group_nr)
6441 int old_group_nr = AmoebaNr[x][y];
6443 if (old_group_nr == 0)
6446 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6447 AmoebaCnt[old_group_nr] = 0;
6448 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6449 AmoebaCnt2[old_group_nr] = 0;
6451 for (yy = 0; yy < lev_fieldy; yy++)
6453 for (xx = 0; xx < lev_fieldx; xx++)
6455 if (AmoebaNr[xx][yy] == old_group_nr)
6456 AmoebaNr[xx][yy] = new_group_nr;
6463 void AmoebeUmwandeln(int ax, int ay)
6467 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6469 int group_nr = AmoebaNr[ax][ay];
6474 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6475 printf("AmoebeUmwandeln(): This should never happen!\n");
6480 for (y = 0; y < lev_fieldy; y++)
6482 for (x = 0; x < lev_fieldx; x++)
6484 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6487 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6491 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6492 SND_AMOEBA_TURNING_TO_GEM :
6493 SND_AMOEBA_TURNING_TO_ROCK));
6498 static int xy[4][2] =
6506 for (i = 0; i < NUM_DIRECTIONS; i++)
6511 if (!IN_LEV_FIELD(x, y))
6514 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6516 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6517 SND_AMOEBA_TURNING_TO_GEM :
6518 SND_AMOEBA_TURNING_TO_ROCK));
6525 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6528 int group_nr = AmoebaNr[ax][ay];
6529 boolean done = FALSE;
6534 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6535 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6540 for (y = 0; y < lev_fieldy; y++)
6542 for (x = 0; x < lev_fieldx; x++)
6544 if (AmoebaNr[x][y] == group_nr &&
6545 (Feld[x][y] == EL_AMOEBA_DEAD ||
6546 Feld[x][y] == EL_BD_AMOEBA ||
6547 Feld[x][y] == EL_AMOEBA_GROWING))
6550 Feld[x][y] = new_element;
6551 InitField(x, y, FALSE);
6552 DrawLevelField(x, y);
6559 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6560 SND_BD_AMOEBA_TURNING_TO_ROCK :
6561 SND_BD_AMOEBA_TURNING_TO_GEM));
6564 void AmoebeWaechst(int x, int y)
6566 static unsigned long sound_delay = 0;
6567 static unsigned long sound_delay_value = 0;
6569 if (!MovDelay[x][y]) /* start new growing cycle */
6573 if (DelayReached(&sound_delay, sound_delay_value))
6576 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6578 if (Store[x][y] == EL_BD_AMOEBA)
6579 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6581 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6583 sound_delay_value = 30;
6587 if (MovDelay[x][y]) /* wait some time before growing bigger */
6590 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6592 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6593 6 - MovDelay[x][y]);
6595 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6598 if (!MovDelay[x][y])
6600 Feld[x][y] = Store[x][y];
6602 DrawLevelField(x, y);
6607 void AmoebaDisappearing(int x, int y)
6609 static unsigned long sound_delay = 0;
6610 static unsigned long sound_delay_value = 0;
6612 if (!MovDelay[x][y]) /* start new shrinking cycle */
6616 if (DelayReached(&sound_delay, sound_delay_value))
6617 sound_delay_value = 30;
6620 if (MovDelay[x][y]) /* wait some time before shrinking */
6623 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6625 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6626 6 - MovDelay[x][y]);
6628 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6631 if (!MovDelay[x][y])
6633 Feld[x][y] = EL_EMPTY;
6634 DrawLevelField(x, y);
6636 /* don't let mole enter this field in this cycle;
6637 (give priority to objects falling to this field from above) */
6643 void AmoebeAbleger(int ax, int ay)
6646 int element = Feld[ax][ay];
6647 int graphic = el2img(element);
6648 int newax = ax, neway = ay;
6649 static int xy[4][2] =
6657 if (!level.amoeba_speed)
6659 Feld[ax][ay] = EL_AMOEBA_DEAD;
6660 DrawLevelField(ax, ay);
6664 if (IS_ANIMATED(graphic))
6665 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6667 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6668 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6670 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6673 if (MovDelay[ax][ay])
6677 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6680 int x = ax + xy[start][0];
6681 int y = ay + xy[start][1];
6683 if (!IN_LEV_FIELD(x, y))
6687 if (IS_FREE(x, y) ||
6688 CAN_GROW_INTO(Feld[x][y]) ||
6689 Feld[x][y] == EL_QUICKSAND_EMPTY)
6695 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6696 if (IS_FREE(x, y) ||
6697 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6704 if (newax == ax && neway == ay)
6707 else /* normal or "filled" (BD style) amoeba */
6710 boolean waiting_for_player = FALSE;
6712 for (i = 0; i < NUM_DIRECTIONS; i++)
6714 int j = (start + i) % 4;
6715 int x = ax + xy[j][0];
6716 int y = ay + xy[j][1];
6718 if (!IN_LEV_FIELD(x, y))
6722 if (IS_FREE(x, y) ||
6723 CAN_GROW_INTO(Feld[x][y]) ||
6724 Feld[x][y] == EL_QUICKSAND_EMPTY)
6731 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6732 if (IS_FREE(x, y) ||
6733 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6740 else if (IS_PLAYER(x, y))
6741 waiting_for_player = TRUE;
6744 if (newax == ax && neway == ay) /* amoeba cannot grow */
6747 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6749 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6752 Feld[ax][ay] = EL_AMOEBA_DEAD;
6753 DrawLevelField(ax, ay);
6754 AmoebaCnt[AmoebaNr[ax][ay]]--;
6756 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6758 if (element == EL_AMOEBA_FULL)
6759 AmoebeUmwandeln(ax, ay);
6760 else if (element == EL_BD_AMOEBA)
6761 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6766 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6768 /* amoeba gets larger by growing in some direction */
6770 int new_group_nr = AmoebaNr[ax][ay];
6773 if (new_group_nr == 0)
6775 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6776 printf("AmoebeAbleger(): This should never happen!\n");
6781 AmoebaNr[newax][neway] = new_group_nr;
6782 AmoebaCnt[new_group_nr]++;
6783 AmoebaCnt2[new_group_nr]++;
6785 /* if amoeba touches other amoeba(s) after growing, unify them */
6786 AmoebenVereinigen(newax, neway);
6788 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6790 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6796 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6797 (neway == lev_fieldy - 1 && newax != ax))
6799 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6800 Store[newax][neway] = element;
6802 else if (neway == ay)
6804 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6806 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6808 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6813 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6814 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6815 Store[ax][ay] = EL_AMOEBA_DROP;
6816 ContinueMoving(ax, ay);
6820 DrawLevelField(newax, neway);
6823 void Life(int ax, int ay)
6826 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6828 int element = Feld[ax][ay];
6829 int graphic = el2img(element);
6830 boolean changed = FALSE;
6832 if (IS_ANIMATED(graphic))
6833 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6838 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6839 MovDelay[ax][ay] = life_time;
6841 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6844 if (MovDelay[ax][ay])
6848 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6850 int xx = ax+x1, yy = ay+y1;
6853 if (!IN_LEV_FIELD(xx, yy))
6856 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6858 int x = xx+x2, y = yy+y2;
6860 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6863 if (((Feld[x][y] == element ||
6864 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6866 (IS_FREE(x, y) && Stop[x][y]))
6870 if (xx == ax && yy == ay) /* field in the middle */
6872 if (nachbarn < life[0] || nachbarn > life[1])
6874 Feld[xx][yy] = EL_EMPTY;
6876 DrawLevelField(xx, yy);
6877 Stop[xx][yy] = TRUE;
6882 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6883 { /* free border field */
6884 if (nachbarn >= life[2] && nachbarn <= life[3])
6886 Feld[xx][yy] = element;
6887 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6889 DrawLevelField(xx, yy);
6890 Stop[xx][yy] = TRUE;
6895 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6896 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6897 { /* free border field */
6898 if (nachbarn >= life[2] && nachbarn <= life[3])
6900 Feld[xx][yy] = element;
6901 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6903 DrawLevelField(xx, yy);
6904 Stop[xx][yy] = TRUE;
6912 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6913 SND_GAME_OF_LIFE_GROWING);
6916 static void InitRobotWheel(int x, int y)
6918 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6921 static void RunRobotWheel(int x, int y)
6923 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6926 static void StopRobotWheel(int x, int y)
6928 if (ZX == x && ZY == y)
6932 static void InitTimegateWheel(int x, int y)
6935 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6937 /* another brainless, "type style" bug ... :-( */
6938 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6942 static void RunTimegateWheel(int x, int y)
6944 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6947 void CheckExit(int x, int y)
6949 if (local_player->gems_still_needed > 0 ||
6950 local_player->sokobanfields_still_needed > 0 ||
6951 local_player->lights_still_needed > 0)
6953 int element = Feld[x][y];
6954 int graphic = el2img(element);
6956 if (IS_ANIMATED(graphic))
6957 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6962 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6965 Feld[x][y] = EL_EXIT_OPENING;
6967 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6970 void CheckExitSP(int x, int y)
6972 if (local_player->gems_still_needed > 0)
6974 int element = Feld[x][y];
6975 int graphic = el2img(element);
6977 if (IS_ANIMATED(graphic))
6978 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6983 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6986 Feld[x][y] = EL_SP_EXIT_OPENING;
6988 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6991 static void CloseAllOpenTimegates()
6995 for (y = 0; y < lev_fieldy; y++)
6997 for (x = 0; x < lev_fieldx; x++)
6999 int element = Feld[x][y];
7001 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7003 Feld[x][y] = EL_TIMEGATE_CLOSING;
7005 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7007 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7014 void EdelsteinFunkeln(int x, int y)
7016 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7019 if (Feld[x][y] == EL_BD_DIAMOND)
7022 if (MovDelay[x][y] == 0) /* next animation frame */
7023 MovDelay[x][y] = 11 * !SimpleRND(500);
7025 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7029 if (setup.direct_draw && MovDelay[x][y])
7030 SetDrawtoField(DRAW_BUFFERED);
7032 DrawLevelElementAnimation(x, y, Feld[x][y]);
7034 if (MovDelay[x][y] != 0)
7036 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7037 10 - MovDelay[x][y]);
7039 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7041 if (setup.direct_draw)
7045 dest_x = FX + SCREENX(x) * TILEX;
7046 dest_y = FY + SCREENY(y) * TILEY;
7048 BlitBitmap(drawto_field, window,
7049 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7050 SetDrawtoField(DRAW_DIRECT);
7056 void MauerWaechst(int x, int y)
7060 if (!MovDelay[x][y]) /* next animation frame */
7061 MovDelay[x][y] = 3 * delay;
7063 if (MovDelay[x][y]) /* wait some time before next frame */
7067 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7069 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7070 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7072 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7075 if (!MovDelay[x][y])
7077 if (MovDir[x][y] == MV_LEFT)
7079 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7080 DrawLevelField(x - 1, y);
7082 else if (MovDir[x][y] == MV_RIGHT)
7084 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7085 DrawLevelField(x + 1, y);
7087 else if (MovDir[x][y] == MV_UP)
7089 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7090 DrawLevelField(x, y - 1);
7094 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7095 DrawLevelField(x, y + 1);
7098 Feld[x][y] = Store[x][y];
7100 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7101 DrawLevelField(x, y);
7106 void MauerAbleger(int ax, int ay)
7108 int element = Feld[ax][ay];
7109 int graphic = el2img(element);
7110 boolean oben_frei = FALSE, unten_frei = FALSE;
7111 boolean links_frei = FALSE, rechts_frei = FALSE;
7112 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7113 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7114 boolean new_wall = FALSE;
7116 if (IS_ANIMATED(graphic))
7117 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7119 if (!MovDelay[ax][ay]) /* start building new wall */
7120 MovDelay[ax][ay] = 6;
7122 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7125 if (MovDelay[ax][ay])
7129 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7131 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7133 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7135 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7138 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7139 element == EL_EXPANDABLE_WALL_ANY)
7143 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7144 Store[ax][ay-1] = element;
7145 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7146 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7147 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7148 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7153 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7154 Store[ax][ay+1] = element;
7155 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7156 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7157 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7158 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7163 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7164 element == EL_EXPANDABLE_WALL_ANY ||
7165 element == EL_EXPANDABLE_WALL)
7169 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7170 Store[ax-1][ay] = element;
7171 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7172 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7173 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7174 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7180 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7181 Store[ax+1][ay] = element;
7182 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7183 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7184 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7185 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7190 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7191 DrawLevelField(ax, ay);
7193 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7195 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7196 unten_massiv = TRUE;
7197 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7198 links_massiv = TRUE;
7199 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7200 rechts_massiv = TRUE;
7202 if (((oben_massiv && unten_massiv) ||
7203 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7204 element == EL_EXPANDABLE_WALL) &&
7205 ((links_massiv && rechts_massiv) ||
7206 element == EL_EXPANDABLE_WALL_VERTICAL))
7207 Feld[ax][ay] = EL_WALL;
7211 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7213 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7217 void CheckForDragon(int x, int y)
7220 boolean dragon_found = FALSE;
7221 static int xy[4][2] =
7229 for (i = 0; i < NUM_DIRECTIONS; i++)
7231 for (j = 0; j < 4; j++)
7233 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7235 if (IN_LEV_FIELD(xx, yy) &&
7236 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7238 if (Feld[xx][yy] == EL_DRAGON)
7239 dragon_found = TRUE;
7248 for (i = 0; i < NUM_DIRECTIONS; i++)
7250 for (j = 0; j < 3; j++)
7252 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7254 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7256 Feld[xx][yy] = EL_EMPTY;
7257 DrawLevelField(xx, yy);
7266 static void InitBuggyBase(int x, int y)
7268 int element = Feld[x][y];
7269 int activating_delay = FRAMES_PER_SECOND / 4;
7272 (element == EL_SP_BUGGY_BASE ?
7273 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7274 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7276 element == EL_SP_BUGGY_BASE_ACTIVE ?
7277 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7280 static void WarnBuggyBase(int x, int y)
7283 static int xy[4][2] =
7291 for (i = 0; i < NUM_DIRECTIONS; i++)
7293 int xx = x + xy[i][0], yy = y + xy[i][1];
7295 if (IS_PLAYER(xx, yy))
7297 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7304 static void InitTrap(int x, int y)
7306 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7309 static void ActivateTrap(int x, int y)
7311 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7314 static void ChangeActiveTrap(int x, int y)
7316 int graphic = IMG_TRAP_ACTIVE;
7318 /* if new animation frame was drawn, correct crumbled sand border */
7319 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7320 DrawLevelFieldCrumbledSand(x, y);
7323 static void ChangeElementNowExt(int x, int y, int target_element)
7325 int previous_move_direction = MovDir[x][y];
7327 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7328 IS_WALKABLE(Feld[x][y]));
7330 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7331 IS_WALKABLE(Feld[x][y]) &&
7335 /* check if element under player changes from accessible to unaccessible
7336 (needed for special case of dropping element which then changes) */
7337 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7338 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7349 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7350 RemoveMovingField(x, y);
7354 Feld[x][y] = target_element;
7357 Feld[x][y] = target_element;
7360 ResetGfxAnimation(x, y);
7361 ResetRandomAnimationValue(x, y);
7363 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7364 MovDir[x][y] = previous_move_direction;
7367 InitField_WithBug1(x, y, FALSE);
7369 InitField(x, y, FALSE);
7370 if (CAN_MOVE(Feld[x][y]))
7374 DrawLevelField(x, y);
7376 if (GFX_CRUMBLED(Feld[x][y]))
7377 DrawLevelFieldCrumbledSandNeighbours(x, y);
7380 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7383 TestIfBadThingTouchesHero(x, y);
7384 TestIfPlayerTouchesCustomElement(x, y);
7385 TestIfElementTouchesCustomElement(x, y);
7388 if (ELEM_IS_PLAYER(target_element))
7389 RelocatePlayer(x, y, target_element);
7392 TestIfBadThingTouchesHero(x, y);
7393 TestIfPlayerTouchesCustomElement(x, y);
7394 TestIfElementTouchesCustomElement(x, y);
7398 static boolean ChangeElementNow(int x, int y, int element, int page)
7400 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7402 int old_element = Feld[x][y];
7404 /* always use default change event to prevent running into a loop */
7405 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7406 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7408 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7410 /* reset actual trigger element and player */
7411 change->actual_trigger_element = EL_EMPTY;
7412 change->actual_trigger_player = EL_PLAYER_1;
7415 /* do not change already changed elements with same change event */
7417 if (Changed[x][y] & ChangeEvent[x][y])
7424 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7427 /* !!! indirect change before direct change !!! */
7428 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7431 if (change->explode)
7438 if (change->use_target_content)
7440 boolean complete_replace = TRUE;
7441 boolean can_replace[3][3];
7444 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7447 boolean is_walkable;
7448 boolean is_diggable;
7449 boolean is_collectible;
7450 boolean is_removable;
7451 boolean is_destructible;
7452 int ex = x + xx - 1;
7453 int ey = y + yy - 1;
7454 int content_element = change->target_content[xx][yy];
7457 can_replace[xx][yy] = TRUE;
7459 if (ex == x && ey == y) /* do not check changing element itself */
7462 if (content_element == EL_EMPTY_SPACE)
7464 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7469 if (!IN_LEV_FIELD(ex, ey))
7471 can_replace[xx][yy] = FALSE;
7472 complete_replace = FALSE;
7479 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7480 e = MovingOrBlocked2Element(ex, ey);
7485 is_empty = (IS_FREE(ex, ey) ||
7486 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7487 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7488 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7490 is_empty = (IS_FREE(ex, ey) ||
7491 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7493 is_walkable = (is_empty || IS_WALKABLE(e));
7494 is_diggable = (is_empty || IS_DIGGABLE(e));
7495 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7496 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7497 is_removable = (is_diggable || is_collectible);
7499 can_replace[xx][yy] =
7500 ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7501 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7502 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7503 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7504 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7505 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7507 if (!can_replace[xx][yy])
7508 complete_replace = FALSE;
7510 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7511 IS_WALKABLE(content_element)));
7513 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7515 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7518 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7519 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7520 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7522 can_replace[xx][yy] = FALSE;
7523 complete_replace = FALSE;
7528 if (!change->only_if_complete || complete_replace)
7530 boolean something_has_changed = FALSE;
7532 if (change->only_if_complete && change->use_random_replace &&
7533 RND(100) < change->random_percentage)
7536 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7538 int ex = x + xx - 1;
7539 int ey = y + yy - 1;
7540 int content_element;
7542 if (can_replace[xx][yy] && (!change->use_random_replace ||
7543 RND(100) < change->random_percentage))
7545 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7546 RemoveMovingField(ex, ey);
7548 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7550 content_element = change->target_content[xx][yy];
7551 target_element = GET_TARGET_ELEMENT(content_element, change);
7553 ChangeElementNowExt(ex, ey, target_element);
7555 something_has_changed = TRUE;
7557 /* for symmetry reasons, freeze newly created border elements */
7558 if (ex != x || ey != y)
7559 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7563 if (something_has_changed)
7564 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7569 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7571 ChangeElementNowExt(x, y, target_element);
7573 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7577 /* this uses direct change before indirect change */
7578 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7584 static void ChangeElement(int x, int y, int page)
7586 int element = MovingOrBlocked2Element(x, y);
7587 struct ElementInfo *ei = &element_info[element];
7588 struct ElementChangeInfo *change = &ei->change_page[page];
7591 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7594 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7595 x, y, element, element_info[element].token_name);
7596 printf("ChangeElement(): This should never happen!\n");
7601 /* this can happen with classic bombs on walkable, changing elements */
7602 if (!CAN_CHANGE(element))
7605 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7606 ChangeDelay[x][y] = 0;
7612 if (ChangeDelay[x][y] == 0) /* initialize element change */
7614 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7615 RND(change->delay_random * change->delay_frames)) + 1;
7617 ResetGfxAnimation(x, y);
7618 ResetRandomAnimationValue(x, y);
7620 if (change->pre_change_function)
7621 change->pre_change_function(x, y);
7624 ChangeDelay[x][y]--;
7626 if (ChangeDelay[x][y] != 0) /* continue element change */
7628 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7630 if (IS_ANIMATED(graphic))
7631 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7633 if (change->change_function)
7634 change->change_function(x, y);
7636 else /* finish element change */
7638 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7640 page = ChangePage[x][y];
7641 ChangePage[x][y] = -1;
7643 change = &ei->change_page[page];
7647 if (IS_MOVING(x, y) && !change->explode)
7649 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7652 ChangeDelay[x][y] = 1; /* try change after next move step */
7653 ChangePage[x][y] = page; /* remember page to use for change */
7658 if (ChangeElementNow(x, y, element, page))
7660 if (change->post_change_function)
7661 change->post_change_function(x, y);
7666 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7667 int trigger_element,
7674 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7676 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7679 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7681 int element = EL_CUSTOM_START + i;
7683 boolean change_element = FALSE;
7686 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7689 for (j = 0; j < element_info[element].num_change_pages; j++)
7691 struct ElementChangeInfo *change = &element_info[element].change_page[j];
7693 if (change->can_change &&
7694 change->events & CH_EVENT_BIT(trigger_event) &&
7695 change->trigger_side & trigger_side &&
7696 change->trigger_player & trigger_player &&
7697 change->trigger_page & trigger_page_bits &&
7698 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7701 if (!(change->events & CH_EVENT_BIT(trigger_event)))
7702 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7703 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7706 change_element = TRUE;
7709 change->actual_trigger_element = trigger_element;
7710 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7716 if (!change_element)
7719 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7722 if (x == lx && y == ly) /* do not change trigger element itself */
7726 if (Feld[x][y] == element)
7728 ChangeDelay[x][y] = 1;
7729 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7730 ChangeElement(x, y, page);
7738 static boolean CheckElementChangeExt(int x, int y,
7740 int trigger_element,
7746 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7749 if (Feld[x][y] == EL_BLOCKED)
7751 Blocked2Moving(x, y, &x, &y);
7752 element = Feld[x][y];
7756 if (Feld[x][y] != element) /* check if element has already changed */
7759 printf("::: %d ('%s') != %d ('%s') [%d]\n",
7760 Feld[x][y], element_info[Feld[x][y]].token_name,
7761 element, element_info[element].token_name,
7770 if (trigger_page < 0)
7772 boolean change_element = FALSE;
7775 for (i = 0; i < element_info[element].num_change_pages; i++)
7777 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7779 if (change->can_change &&
7780 change->events & CH_EVENT_BIT(trigger_event) &&
7781 change->trigger_side & trigger_side &&
7782 change->trigger_player & trigger_player)
7784 change_element = TRUE;
7787 change->actual_trigger_element = trigger_element;
7788 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7794 if (!change_element)
7799 struct ElementInfo *ei = &element_info[element];
7800 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7802 change->actual_trigger_element = trigger_element;
7803 change->actual_trigger_player = EL_PLAYER_1; /* unused */
7808 /* !!! this check misses pages with same event, but different side !!! */
7810 if (trigger_page < 0)
7811 trigger_page = element_info[element].event_page_nr[trigger_event];
7813 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7817 ChangeDelay[x][y] = 1;
7818 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7819 ChangeElement(x, y, trigger_page);
7824 static void PlayPlayerSound(struct PlayerInfo *player)
7826 int jx = player->jx, jy = player->jy;
7827 int element = player->element_nr;
7828 int last_action = player->last_action_waiting;
7829 int action = player->action_waiting;
7831 if (player->is_waiting)
7833 if (action != last_action)
7834 PlayLevelSoundElementAction(jx, jy, element, action);
7836 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7840 if (action != last_action)
7841 StopSound(element_info[element].sound[last_action]);
7843 if (last_action == ACTION_SLEEPING)
7844 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7848 static void PlayAllPlayersSound()
7852 for (i = 0; i < MAX_PLAYERS; i++)
7853 if (stored_player[i].active)
7854 PlayPlayerSound(&stored_player[i]);
7857 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7859 boolean last_waiting = player->is_waiting;
7860 int move_dir = player->MovDir;
7862 player->last_action_waiting = player->action_waiting;
7866 if (!last_waiting) /* not waiting -> waiting */
7868 player->is_waiting = TRUE;
7870 player->frame_counter_bored =
7872 game.player_boring_delay_fixed +
7873 SimpleRND(game.player_boring_delay_random);
7874 player->frame_counter_sleeping =
7876 game.player_sleeping_delay_fixed +
7877 SimpleRND(game.player_sleeping_delay_random);
7879 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7882 if (game.player_sleeping_delay_fixed +
7883 game.player_sleeping_delay_random > 0 &&
7884 player->anim_delay_counter == 0 &&
7885 player->post_delay_counter == 0 &&
7886 FrameCounter >= player->frame_counter_sleeping)
7887 player->is_sleeping = TRUE;
7888 else if (game.player_boring_delay_fixed +
7889 game.player_boring_delay_random > 0 &&
7890 FrameCounter >= player->frame_counter_bored)
7891 player->is_bored = TRUE;
7893 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7894 player->is_bored ? ACTION_BORING :
7897 if (player->is_sleeping)
7899 if (player->num_special_action_sleeping > 0)
7901 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7903 int last_special_action = player->special_action_sleeping;
7904 int num_special_action = player->num_special_action_sleeping;
7905 int special_action =
7906 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7907 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7908 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7909 last_special_action + 1 : ACTION_SLEEPING);
7910 int special_graphic =
7911 el_act_dir2img(player->element_nr, special_action, move_dir);
7913 player->anim_delay_counter =
7914 graphic_info[special_graphic].anim_delay_fixed +
7915 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7916 player->post_delay_counter =
7917 graphic_info[special_graphic].post_delay_fixed +
7918 SimpleRND(graphic_info[special_graphic].post_delay_random);
7920 player->special_action_sleeping = special_action;
7923 if (player->anim_delay_counter > 0)
7925 player->action_waiting = player->special_action_sleeping;
7926 player->anim_delay_counter--;
7928 else if (player->post_delay_counter > 0)
7930 player->post_delay_counter--;
7934 else if (player->is_bored)
7936 if (player->num_special_action_bored > 0)
7938 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7940 int special_action =
7941 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7942 int special_graphic =
7943 el_act_dir2img(player->element_nr, special_action, move_dir);
7945 player->anim_delay_counter =
7946 graphic_info[special_graphic].anim_delay_fixed +
7947 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7948 player->post_delay_counter =
7949 graphic_info[special_graphic].post_delay_fixed +
7950 SimpleRND(graphic_info[special_graphic].post_delay_random);
7952 player->special_action_bored = special_action;
7955 if (player->anim_delay_counter > 0)
7957 player->action_waiting = player->special_action_bored;
7958 player->anim_delay_counter--;
7960 else if (player->post_delay_counter > 0)
7962 player->post_delay_counter--;
7967 else if (last_waiting) /* waiting -> not waiting */
7969 player->is_waiting = FALSE;
7970 player->is_bored = FALSE;
7971 player->is_sleeping = FALSE;
7973 player->frame_counter_bored = -1;
7974 player->frame_counter_sleeping = -1;
7976 player->anim_delay_counter = 0;
7977 player->post_delay_counter = 0;
7979 player->action_waiting = ACTION_DEFAULT;
7981 player->special_action_bored = ACTION_DEFAULT;
7982 player->special_action_sleeping = ACTION_DEFAULT;
7987 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7990 static byte stored_player_action[MAX_PLAYERS];
7991 static int num_stored_actions = 0;
7993 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7994 int left = player_action & JOY_LEFT;
7995 int right = player_action & JOY_RIGHT;
7996 int up = player_action & JOY_UP;
7997 int down = player_action & JOY_DOWN;
7998 int button1 = player_action & JOY_BUTTON_1;
7999 int button2 = player_action & JOY_BUTTON_2;
8000 int dx = (left ? -1 : right ? 1 : 0);
8001 int dy = (up ? -1 : down ? 1 : 0);
8004 stored_player_action[player->index_nr] = 0;
8005 num_stored_actions++;
8009 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8012 if (!player->active || tape.pausing)
8016 printf("::: [%d %d %d %d] [%d %d]\n",
8017 left, right, up, down, button1, button2);
8023 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8028 if (player->MovPos == 0)
8029 CheckGravityMovement(player);
8032 snapped = SnapField(player, dx, dy);
8036 dropped = DropElement(player);
8038 moved = MovePlayer(player, dx, dy);
8041 if (tape.single_step && tape.recording && !tape.pausing)
8043 if (button1 || (dropped && !moved))
8045 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8046 SnapField(player, 0, 0); /* stop snapping */
8050 SetPlayerWaiting(player, FALSE);
8053 return player_action;
8055 stored_player_action[player->index_nr] = player_action;
8061 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8064 /* no actions for this player (no input at player's configured device) */
8066 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8067 SnapField(player, 0, 0);
8068 CheckGravityMovementWhenNotMoving(player);
8070 if (player->MovPos == 0)
8071 SetPlayerWaiting(player, TRUE);
8073 if (player->MovPos == 0) /* needed for tape.playing */
8074 player->is_moving = FALSE;
8076 player->is_dropping = FALSE;
8082 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8084 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8086 TapeRecordAction(stored_player_action);
8087 num_stored_actions = 0;
8094 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8096 static byte stored_player_action[MAX_PLAYERS];
8097 static int num_stored_actions = 0;
8098 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8099 int left = player_action & JOY_LEFT;
8100 int right = player_action & JOY_RIGHT;
8101 int up = player_action & JOY_UP;
8102 int down = player_action & JOY_DOWN;
8103 int button1 = player_action & JOY_BUTTON_1;
8104 int button2 = player_action & JOY_BUTTON_2;
8105 int dx = (left ? -1 : right ? 1 : 0);
8106 int dy = (up ? -1 : down ? 1 : 0);
8108 stored_player_action[player->index_nr] = 0;
8109 num_stored_actions++;
8111 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8113 if (!player->active || tape.pausing)
8118 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8121 snapped = SnapField(player, dx, dy);
8125 dropped = DropElement(player);
8127 moved = MovePlayer(player, dx, dy);
8130 if (tape.single_step && tape.recording && !tape.pausing)
8132 if (button1 || (dropped && !moved))
8134 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8135 SnapField(player, 0, 0); /* stop snapping */
8139 stored_player_action[player->index_nr] = player_action;
8143 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8145 /* no actions for this player (no input at player's configured device) */
8147 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8148 SnapField(player, 0, 0);
8149 CheckGravityMovementWhenNotMoving(player);
8151 if (player->MovPos == 0)
8152 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8154 if (player->MovPos == 0) /* needed for tape.playing */
8155 player->is_moving = FALSE;
8158 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8160 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8162 TapeRecordAction(stored_player_action);
8163 num_stored_actions = 0;
8170 static unsigned long action_delay = 0;
8171 unsigned long action_delay_value;
8172 int magic_wall_x = 0, magic_wall_y = 0;
8173 int i, x, y, element, graphic;
8174 byte *recorded_player_action;
8175 byte summarized_player_action = 0;
8177 byte tape_action[MAX_PLAYERS];
8180 if (game_status != GAME_MODE_PLAYING)
8183 action_delay_value =
8184 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8186 if (tape.playing && tape.warp_forward && !tape.pausing)
8187 action_delay_value = 0;
8189 /* ---------- main game synchronization point ---------- */
8191 WaitUntilDelayReached(&action_delay, action_delay_value);
8193 if (network_playing && !network_player_action_received)
8197 printf("DEBUG: try to get network player actions in time\n");
8201 #if defined(NETWORK_AVALIABLE)
8202 /* last chance to get network player actions without main loop delay */
8206 if (game_status != GAME_MODE_PLAYING)
8209 if (!network_player_action_received)
8213 printf("DEBUG: failed to get network player actions in time\n");
8224 printf("::: getting new tape action [%d]\n", FrameCounter);
8227 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8230 if (recorded_player_action == NULL && tape.pausing)
8235 printf("::: %d\n", stored_player[0].action);
8239 if (recorded_player_action != NULL)
8240 for (i = 0; i < MAX_PLAYERS; i++)
8241 stored_player[i].action = recorded_player_action[i];
8244 for (i = 0; i < MAX_PLAYERS; i++)
8246 summarized_player_action |= stored_player[i].action;
8248 if (!network_playing)
8249 stored_player[i].effective_action = stored_player[i].action;
8252 #if defined(NETWORK_AVALIABLE)
8253 if (network_playing)
8254 SendToServer_MovePlayer(summarized_player_action);
8257 if (!options.network && !setup.team_mode)
8258 local_player->effective_action = summarized_player_action;
8261 if (recorded_player_action != NULL)
8262 for (i = 0; i < MAX_PLAYERS; i++)
8263 stored_player[i].effective_action = recorded_player_action[i];
8267 for (i = 0; i < MAX_PLAYERS; i++)
8269 tape_action[i] = stored_player[i].effective_action;
8271 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8272 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8275 /* only save actions from input devices, but not programmed actions */
8277 TapeRecordAction(tape_action);
8280 for (i = 0; i < MAX_PLAYERS; i++)
8282 int actual_player_action = stored_player[i].effective_action;
8285 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8286 - rnd_equinox_tetrachloride 048
8287 - rnd_equinox_tetrachloride_ii 096
8288 - rnd_emanuel_schmieg 002
8289 - doctor_sloan_ww 001, 020
8291 if (stored_player[i].MovPos == 0)
8292 CheckGravityMovement(&stored_player[i]);
8296 /* overwrite programmed action with tape action */
8297 if (stored_player[i].programmed_action)
8298 actual_player_action = stored_player[i].programmed_action;
8302 if (stored_player[i].programmed_action)
8303 printf("::: %d\n", stored_player[i].programmed_action);
8306 if (recorded_player_action)
8309 if (stored_player[i].programmed_action &&
8310 stored_player[i].programmed_action != recorded_player_action[i])
8311 printf("::: %d: %d <-> %d\n", i,
8312 stored_player[i].programmed_action, recorded_player_action[i]);
8316 actual_player_action = recorded_player_action[i];
8321 /* overwrite tape action with programmed action */
8322 if (stored_player[i].programmed_action)
8323 actual_player_action = stored_player[i].programmed_action;
8328 printf("::: action: %d: %x [%d]\n",
8329 stored_player[i].MovPos, actual_player_action, FrameCounter);
8333 PlayerActions(&stored_player[i], actual_player_action);
8335 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8337 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8338 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8341 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8346 TapeRecordAction(tape_action);
8349 network_player_action_received = FALSE;
8351 ScrollScreen(NULL, SCROLL_GO_ON);
8357 for (i = 0; i < MAX_PLAYERS; i++)
8358 stored_player[i].Frame++;
8362 /* for downwards compatibility, the following code emulates a fixed bug that
8363 occured when pushing elements (causing elements that just made their last
8364 pushing step to already (if possible) make their first falling step in the
8365 same game frame, which is bad); this code is also needed to use the famous
8366 "spring push bug" which is used in older levels and might be wanted to be
8367 used also in newer levels, but in this case the buggy pushing code is only
8368 affecting the "spring" element and no other elements */
8371 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8373 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8376 for (i = 0; i < MAX_PLAYERS; i++)
8378 struct PlayerInfo *player = &stored_player[i];
8383 if (player->active && player->is_pushing && player->is_moving &&
8385 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8386 Feld[x][y] == EL_SPRING))
8388 if (player->active && player->is_pushing && player->is_moving &&
8392 ContinueMoving(x, y);
8394 /* continue moving after pushing (this is actually a bug) */
8395 if (!IS_MOVING(x, y))
8404 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8406 Changed[x][y] = CE_BITMASK_DEFAULT;
8407 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8410 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8412 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8413 printf("GameActions(): This should never happen!\n");
8415 ChangePage[x][y] = -1;
8420 if (WasJustMoving[x][y] > 0)
8421 WasJustMoving[x][y]--;
8422 if (WasJustFalling[x][y] > 0)
8423 WasJustFalling[x][y]--;
8424 if (CheckCollision[x][y] > 0)
8425 CheckCollision[x][y]--;
8430 /* reset finished pushing action (not done in ContinueMoving() to allow
8431 continous pushing animation for elements with zero push delay) */
8432 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8434 ResetGfxAnimation(x, y);
8435 DrawLevelField(x, y);
8440 if (IS_BLOCKED(x, y))
8444 Blocked2Moving(x, y, &oldx, &oldy);
8445 if (!IS_MOVING(oldx, oldy))
8447 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8448 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8449 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8450 printf("GameActions(): This should never happen!\n");
8456 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8458 element = Feld[x][y];
8460 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8462 graphic = el2img(element);
8468 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8470 element = graphic = 0;
8474 if (graphic_info[graphic].anim_global_sync)
8475 GfxFrame[x][y] = FrameCounter;
8477 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8478 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8479 ResetRandomAnimationValue(x, y);
8481 SetRandomAnimationValue(x, y);
8484 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8487 if (IS_INACTIVE(element))
8489 if (IS_ANIMATED(graphic))
8490 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8496 /* this may take place after moving, so 'element' may have changed */
8498 if (IS_CHANGING(x, y))
8500 if (IS_CHANGING(x, y) &&
8501 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8505 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8506 element_info[element].event_page_nr[CE_DELAY]);
8508 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8511 element = Feld[x][y];
8512 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8516 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8521 element = Feld[x][y];
8522 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8524 if (element == EL_MOLE)
8525 printf("::: %d, %d, %d [%d]\n",
8526 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8530 if (element == EL_YAMYAM)
8531 printf("::: %d, %d, %d\n",
8532 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8536 if (IS_ANIMATED(graphic) &&
8540 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8543 if (element == EL_BUG)
8544 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8548 if (element == EL_MOLE)
8549 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8553 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8554 EdelsteinFunkeln(x, y);
8556 else if ((element == EL_ACID ||
8557 element == EL_EXIT_OPEN ||
8558 element == EL_SP_EXIT_OPEN ||
8559 element == EL_SP_TERMINAL ||
8560 element == EL_SP_TERMINAL_ACTIVE ||
8561 element == EL_EXTRA_TIME ||
8562 element == EL_SHIELD_NORMAL ||
8563 element == EL_SHIELD_DEADLY) &&
8564 IS_ANIMATED(graphic))
8565 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8566 else if (IS_MOVING(x, y))
8567 ContinueMoving(x, y);
8568 else if (IS_ACTIVE_BOMB(element))
8569 CheckDynamite(x, y);
8571 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8572 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8574 else if (element == EL_AMOEBA_GROWING)
8575 AmoebeWaechst(x, y);
8576 else if (element == EL_AMOEBA_SHRINKING)
8577 AmoebaDisappearing(x, y);
8579 #if !USE_NEW_AMOEBA_CODE
8580 else if (IS_AMOEBALIVE(element))
8581 AmoebeAbleger(x, y);
8584 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8586 else if (element == EL_EXIT_CLOSED)
8588 else if (element == EL_SP_EXIT_CLOSED)
8590 else if (element == EL_EXPANDABLE_WALL_GROWING)
8592 else if (element == EL_EXPANDABLE_WALL ||
8593 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8594 element == EL_EXPANDABLE_WALL_VERTICAL ||
8595 element == EL_EXPANDABLE_WALL_ANY)
8597 else if (element == EL_FLAMES)
8598 CheckForDragon(x, y);
8600 else if (IS_AUTO_CHANGING(element))
8601 ChangeElement(x, y);
8603 else if (element == EL_EXPLOSION)
8604 ; /* drawing of correct explosion animation is handled separately */
8605 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8606 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8609 /* this may take place after moving, so 'element' may have changed */
8610 if (IS_AUTO_CHANGING(Feld[x][y]))
8611 ChangeElement(x, y);
8614 if (IS_BELT_ACTIVE(element))
8615 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8617 if (game.magic_wall_active)
8619 int jx = local_player->jx, jy = local_player->jy;
8621 /* play the element sound at the position nearest to the player */
8622 if ((element == EL_MAGIC_WALL_FULL ||
8623 element == EL_MAGIC_WALL_ACTIVE ||
8624 element == EL_MAGIC_WALL_EMPTYING ||
8625 element == EL_BD_MAGIC_WALL_FULL ||
8626 element == EL_BD_MAGIC_WALL_ACTIVE ||
8627 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8628 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8636 #if USE_NEW_AMOEBA_CODE
8637 /* new experimental amoeba growth stuff */
8639 if (!(FrameCounter % 8))
8642 static unsigned long random = 1684108901;
8644 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8647 x = (random >> 10) % lev_fieldx;
8648 y = (random >> 20) % lev_fieldy;
8650 x = RND(lev_fieldx);
8651 y = RND(lev_fieldy);
8653 element = Feld[x][y];
8656 if (!IS_PLAYER(x,y) &&
8657 (element == EL_EMPTY ||
8658 CAN_GROW_INTO(element) ||
8659 element == EL_QUICKSAND_EMPTY ||
8660 element == EL_ACID_SPLASH_LEFT ||
8661 element == EL_ACID_SPLASH_RIGHT))
8663 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8664 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8665 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8666 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8667 Feld[x][y] = EL_AMOEBA_DROP;
8670 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8671 if (!IS_PLAYER(x,y) &&
8672 (element == EL_EMPTY ||
8673 element == EL_SAND ||
8674 element == EL_QUICKSAND_EMPTY ||
8675 element == EL_ACID_SPLASH_LEFT ||
8676 element == EL_ACID_SPLASH_RIGHT))
8678 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8679 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8680 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8681 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8682 Feld[x][y] = EL_AMOEBA_DROP;
8686 random = random * 129 + 1;
8692 if (game.explosions_delayed)
8695 game.explosions_delayed = FALSE;
8697 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8699 element = Feld[x][y];
8701 if (ExplodeField[x][y])
8702 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8703 else if (element == EL_EXPLOSION)
8704 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8706 ExplodeField[x][y] = EX_TYPE_NONE;
8709 game.explosions_delayed = TRUE;
8712 if (game.magic_wall_active)
8714 if (!(game.magic_wall_time_left % 4))
8716 int element = Feld[magic_wall_x][magic_wall_y];
8718 if (element == EL_BD_MAGIC_WALL_FULL ||
8719 element == EL_BD_MAGIC_WALL_ACTIVE ||
8720 element == EL_BD_MAGIC_WALL_EMPTYING)
8721 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8723 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8726 if (game.magic_wall_time_left > 0)
8728 game.magic_wall_time_left--;
8729 if (!game.magic_wall_time_left)
8731 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8733 element = Feld[x][y];
8735 if (element == EL_MAGIC_WALL_ACTIVE ||
8736 element == EL_MAGIC_WALL_FULL)
8738 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8739 DrawLevelField(x, y);
8741 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8742 element == EL_BD_MAGIC_WALL_FULL)
8744 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8745 DrawLevelField(x, y);
8749 game.magic_wall_active = FALSE;
8754 if (game.light_time_left > 0)
8756 game.light_time_left--;
8758 if (game.light_time_left == 0)
8759 RedrawAllLightSwitchesAndInvisibleElements();
8762 if (game.timegate_time_left > 0)
8764 game.timegate_time_left--;
8766 if (game.timegate_time_left == 0)
8767 CloseAllOpenTimegates();
8770 for (i = 0; i < MAX_PLAYERS; i++)
8772 struct PlayerInfo *player = &stored_player[i];
8774 if (SHIELD_ON(player))
8776 if (player->shield_deadly_time_left)
8777 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8778 else if (player->shield_normal_time_left)
8779 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8783 if (TimeFrames >= FRAMES_PER_SECOND)
8788 for (i = 0; i < MAX_PLAYERS; i++)
8790 struct PlayerInfo *player = &stored_player[i];
8792 if (SHIELD_ON(player))
8794 player->shield_normal_time_left--;
8796 if (player->shield_deadly_time_left > 0)
8797 player->shield_deadly_time_left--;
8801 if (!level.use_step_counter)
8809 if (TimeLeft <= 10 && setup.time_limit)
8810 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8812 DrawGameValue_Time(TimeLeft);
8814 if (!TimeLeft && setup.time_limit)
8815 for (i = 0; i < MAX_PLAYERS; i++)
8816 KillHero(&stored_player[i]);
8818 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8819 DrawGameValue_Time(TimePlayed);
8822 if (tape.recording || tape.playing)
8823 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8827 PlayAllPlayersSound();
8829 if (options.debug) /* calculate frames per second */
8831 static unsigned long fps_counter = 0;
8832 static int fps_frames = 0;
8833 unsigned long fps_delay_ms = Counter() - fps_counter;
8837 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8839 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8842 fps_counter = Counter();
8845 redraw_mask |= REDRAW_FPS;
8849 if (stored_player[0].jx != stored_player[0].last_jx ||
8850 stored_player[0].jy != stored_player[0].last_jy)
8851 printf("::: %d, %d, %d, %d, %d\n",
8852 stored_player[0].MovDir,
8853 stored_player[0].MovPos,
8854 stored_player[0].GfxPos,
8855 stored_player[0].Frame,
8856 stored_player[0].StepFrame);
8863 for (i = 0; i < MAX_PLAYERS; i++)
8866 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8868 stored_player[i].Frame += move_frames;
8870 if (stored_player[i].MovPos != 0)
8871 stored_player[i].StepFrame += move_frames;
8873 if (stored_player[i].drop_delay > 0)
8874 stored_player[i].drop_delay--;
8879 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8881 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8883 local_player->show_envelope = 0;
8888 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8890 int min_x = x, min_y = y, max_x = x, max_y = y;
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 || &stored_player[i] == player)
8900 min_x = MIN(min_x, jx);
8901 min_y = MIN(min_y, jy);
8902 max_x = MAX(max_x, jx);
8903 max_y = MAX(max_y, jy);
8906 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8909 static boolean AllPlayersInVisibleScreen()
8913 for (i = 0; i < MAX_PLAYERS; i++)
8915 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8917 if (!stored_player[i].active)
8920 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8927 void ScrollLevel(int dx, int dy)
8929 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8932 BlitBitmap(drawto_field, drawto_field,
8933 FX + TILEX * (dx == -1) - softscroll_offset,
8934 FY + TILEY * (dy == -1) - softscroll_offset,
8935 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8936 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8937 FX + TILEX * (dx == 1) - softscroll_offset,
8938 FY + TILEY * (dy == 1) - softscroll_offset);
8942 x = (dx == 1 ? BX1 : BX2);
8943 for (y = BY1; y <= BY2; y++)
8944 DrawScreenField(x, y);
8949 y = (dy == 1 ? BY1 : BY2);
8950 for (x = BX1; x <= BX2; x++)
8951 DrawScreenField(x, y);
8954 redraw_mask |= REDRAW_FIELD;
8958 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8960 int nextx = x + dx, nexty = y + dy;
8961 int element = Feld[x][y];
8964 element != EL_SP_PORT_LEFT &&
8965 element != EL_SP_GRAVITY_PORT_LEFT &&
8966 element != EL_SP_PORT_HORIZONTAL &&
8967 element != EL_SP_PORT_ANY) ||
8969 element != EL_SP_PORT_RIGHT &&
8970 element != EL_SP_GRAVITY_PORT_RIGHT &&
8971 element != EL_SP_PORT_HORIZONTAL &&
8972 element != EL_SP_PORT_ANY) ||
8974 element != EL_SP_PORT_UP &&
8975 element != EL_SP_GRAVITY_PORT_UP &&
8976 element != EL_SP_PORT_VERTICAL &&
8977 element != EL_SP_PORT_ANY) ||
8979 element != EL_SP_PORT_DOWN &&
8980 element != EL_SP_GRAVITY_PORT_DOWN &&
8981 element != EL_SP_PORT_VERTICAL &&
8982 element != EL_SP_PORT_ANY) ||
8983 !IN_LEV_FIELD(nextx, nexty) ||
8984 !IS_FREE(nextx, nexty))
8991 static boolean canFallDown(struct PlayerInfo *player)
8993 int jx = player->jx, jy = player->jy;
8995 return (IN_LEV_FIELD(jx, jy + 1) &&
8996 (IS_FREE(jx, jy + 1) ||
8997 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8998 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8999 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9002 static boolean canPassField(int x, int y, int move_dir)
9004 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9005 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9006 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9009 int element = Feld[x][y];
9011 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9012 !CAN_MOVE(element) &&
9013 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9014 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9015 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9018 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9020 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9021 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9022 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9026 int nextx = newx + dx;
9027 int nexty = newy + dy;
9031 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9032 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9033 (IS_DIGGABLE(Feld[newx][newy]) ||
9034 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9035 canPassField(newx, newy, move_dir)));
9038 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9039 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9040 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9041 canPassField(newx, newy, move_dir)));
9043 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9044 (IS_DIGGABLE(Feld[newx][newy]) ||
9045 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9046 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9047 !CAN_MOVE(Feld[newx][newy]) &&
9048 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9049 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9050 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9055 static void CheckGravityMovement(struct PlayerInfo *player)
9057 if (game.gravity && !player->programmed_action)
9060 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9061 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9063 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9064 int move_dir_vertical = player->action & MV_VERTICAL;
9068 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9070 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9073 int jx = player->jx, jy = player->jy;
9075 boolean player_is_moving_to_valid_field =
9076 (!player_is_snapping &&
9077 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9078 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9082 (player->last_move_dir & MV_HORIZONTAL ?
9083 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9084 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9088 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9089 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9090 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9091 int new_jx = jx + dx, new_jy = jy + dy;
9092 int nextx = new_jx + dx, nexty = new_jy + dy;
9098 boolean player_can_fall_down = canFallDown(player);
9100 boolean player_can_fall_down =
9101 (IN_LEV_FIELD(jx, jy + 1) &&
9102 (IS_FREE(jx, jy + 1) ||
9103 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9107 boolean player_can_fall_down =
9108 (IN_LEV_FIELD(jx, jy + 1) &&
9109 (IS_FREE(jx, jy + 1)));
9113 boolean player_is_moving_to_valid_field =
9116 !player_is_snapping &&
9120 IN_LEV_FIELD(new_jx, new_jy) &&
9121 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9122 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9123 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9124 IN_LEV_FIELD(nextx, nexty) &&
9125 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9127 IN_LEV_FIELD(new_jx, new_jy) &&
9128 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9129 Feld[new_jx][new_jy] == EL_SAND ||
9130 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9131 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9132 /* !!! extend EL_SAND to anything diggable !!! */
9138 boolean player_is_standing_on_valid_field =
9139 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9140 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9144 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9145 player_can_fall_down,
9146 player_is_standing_on_valid_field,
9147 player_is_moving_to_valid_field,
9148 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9149 player->effective_action,
9150 player->can_fall_into_acid);
9153 if (player_can_fall_down &&
9155 !player_is_standing_on_valid_field &&
9157 !player_is_moving_to_valid_field)
9160 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9161 jx, jy, FrameCounter);
9164 player->programmed_action = MV_DOWN;
9169 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9172 return CheckGravityMovement(player);
9175 if (game.gravity && !player->programmed_action)
9177 int jx = player->jx, jy = player->jy;
9178 boolean field_under_player_is_free =
9179 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9180 boolean player_is_standing_on_valid_field =
9181 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9182 (IS_WALKABLE(Feld[jx][jy]) &&
9183 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9185 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9186 player->programmed_action = MV_DOWN;
9192 -----------------------------------------------------------------------------
9193 dx, dy: direction (non-diagonal) to try to move the player to
9194 real_dx, real_dy: direction as read from input device (can be diagonal)
9197 boolean MovePlayerOneStep(struct PlayerInfo *player,
9198 int dx, int dy, int real_dx, int real_dy)
9201 static int trigger_sides[4][2] =
9203 /* enter side leave side */
9204 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9205 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9206 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9207 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9209 int move_direction = (dx == -1 ? MV_LEFT :
9210 dx == +1 ? MV_RIGHT :
9212 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9213 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9214 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9216 int jx = player->jx, jy = player->jy;
9217 int new_jx = jx + dx, new_jy = jy + dy;
9221 if (!player->active || (!dx && !dy))
9222 return MF_NO_ACTION;
9224 player->MovDir = (dx < 0 ? MV_LEFT :
9227 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9229 if (!IN_LEV_FIELD(new_jx, new_jy))
9230 return MF_NO_ACTION;
9232 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9233 return MF_NO_ACTION;
9236 element = MovingOrBlocked2Element(new_jx, new_jy);
9238 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9241 if (DONT_RUN_INTO(element))
9243 if (element == EL_ACID && dx == 0 && dy == 1)
9245 SplashAcid(new_jx, new_jy);
9246 Feld[jx][jy] = EL_PLAYER_1;
9247 InitMovingField(jx, jy, MV_DOWN);
9248 Store[jx][jy] = EL_ACID;
9249 ContinueMoving(jx, jy);
9253 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9258 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9259 if (can_move != MF_MOVING)
9262 /* check if DigField() has caused relocation of the player */
9263 if (player->jx != jx || player->jy != jy)
9264 return MF_NO_ACTION;
9266 StorePlayer[jx][jy] = 0;
9267 player->last_jx = jx;
9268 player->last_jy = jy;
9269 player->jx = new_jx;
9270 player->jy = new_jy;
9271 StorePlayer[new_jx][new_jy] = player->element_nr;
9274 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9276 player->step_counter++;
9279 player->drop_delay = 0;
9282 PlayerVisit[jx][jy] = FrameCounter;
9284 ScrollPlayer(player, SCROLL_INIT);
9287 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9289 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9291 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9294 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9296 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9297 CE_OTHER_GETS_ENTERED, enter_side);
9298 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9299 CE_ENTERED_BY_PLAYER, enter_side);
9306 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9308 int jx = player->jx, jy = player->jy;
9309 int old_jx = jx, old_jy = jy;
9310 int moved = MF_NO_ACTION;
9313 if (!player->active)
9318 if (player->MovPos == 0)
9320 player->is_moving = FALSE;
9321 player->is_digging = FALSE;
9322 player->is_collecting = FALSE;
9323 player->is_snapping = FALSE;
9324 player->is_pushing = FALSE;
9330 if (!player->active || (!dx && !dy))
9335 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9341 if (!FrameReached(&player->move_delay, player->move_delay_value))
9344 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9345 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9351 /* store if player is automatically moved to next field */
9352 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9354 /* remove the last programmed player action */
9355 player->programmed_action = 0;
9359 /* should only happen if pre-1.2 tape recordings are played */
9360 /* this is only for backward compatibility */
9362 int original_move_delay_value = player->move_delay_value;
9365 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9369 /* scroll remaining steps with finest movement resolution */
9370 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9372 while (player->MovPos)
9374 ScrollPlayer(player, SCROLL_GO_ON);
9375 ScrollScreen(NULL, SCROLL_GO_ON);
9381 player->move_delay_value = original_move_delay_value;
9384 if (player->last_move_dir & MV_HORIZONTAL)
9386 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9387 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9391 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9392 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9398 if (moved & MF_MOVING && !ScreenMovPos &&
9399 (player == local_player || !options.network))
9401 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9402 int offset = (setup.scroll_delay ? 3 : 0);
9404 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9406 /* actual player has left the screen -- scroll in that direction */
9407 if (jx != old_jx) /* player has moved horizontally */
9408 scroll_x += (jx - old_jx);
9409 else /* player has moved vertically */
9410 scroll_y += (jy - old_jy);
9414 if (jx != old_jx) /* player has moved horizontally */
9416 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9417 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9418 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9420 /* don't scroll over playfield boundaries */
9421 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9422 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9424 /* don't scroll more than one field at a time */
9425 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9427 /* don't scroll against the player's moving direction */
9428 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9429 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9430 scroll_x = old_scroll_x;
9432 else /* player has moved vertically */
9434 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9435 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9436 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9438 /* don't scroll over playfield boundaries */
9439 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9440 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9442 /* don't scroll more than one field at a time */
9443 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9445 /* don't scroll against the player's moving direction */
9446 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9447 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9448 scroll_y = old_scroll_y;
9452 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9454 if (!options.network && !AllPlayersInVisibleScreen())
9456 scroll_x = old_scroll_x;
9457 scroll_y = old_scroll_y;
9461 ScrollScreen(player, SCROLL_INIT);
9462 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9469 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9471 if (!(moved & MF_MOVING) && !player->is_pushing)
9476 player->StepFrame = 0;
9478 if (moved & MF_MOVING)
9480 if (old_jx != jx && old_jy == jy)
9481 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9482 else if (old_jx == jx && old_jy != jy)
9483 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9485 DrawLevelField(jx, jy); /* for "crumbled sand" */
9487 player->last_move_dir = player->MovDir;
9488 player->is_moving = TRUE;
9490 player->is_snapping = FALSE;
9494 player->is_switching = FALSE;
9497 player->is_dropping = FALSE;
9501 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9504 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9507 static int trigger_sides[4][2] =
9509 /* enter side leave side */
9510 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9511 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9512 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9513 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9515 int move_direction = player->MovDir;
9516 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9517 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9518 int old_element = Feld[old_jx][old_jy];
9519 int new_element = Feld[jx][jy];
9522 /* !!! TEST ONLY !!! */
9523 if (IS_CUSTOM_ELEMENT(old_element))
9524 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9526 player->index_bit, leave_side);
9528 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9530 player->index_bit, leave_side);
9532 if (IS_CUSTOM_ELEMENT(new_element))
9533 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9534 player->index_bit, enter_side);
9536 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9537 CE_OTHER_GETS_ENTERED,
9538 player->index_bit, enter_side);
9548 CheckGravityMovementWhenNotMoving(player);
9551 player->last_move_dir = MV_NO_MOVING;
9553 player->is_moving = FALSE;
9556 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9558 TestIfHeroTouchesBadThing(jx, jy);
9559 TestIfPlayerTouchesCustomElement(jx, jy);
9562 if (!player->active)
9568 void ScrollPlayer(struct PlayerInfo *player, int mode)
9570 int jx = player->jx, jy = player->jy;
9571 int last_jx = player->last_jx, last_jy = player->last_jy;
9572 int move_stepsize = TILEX / player->move_delay_value;
9574 if (!player->active || !player->MovPos)
9577 if (mode == SCROLL_INIT)
9579 player->actual_frame_counter = FrameCounter;
9580 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9582 if (Feld[last_jx][last_jy] == EL_EMPTY)
9583 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9591 else if (!FrameReached(&player->actual_frame_counter, 1))
9594 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9595 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9597 if (!player->block_last_field &&
9598 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9599 Feld[last_jx][last_jy] = EL_EMPTY;
9601 /* before DrawPlayer() to draw correct player graphic for this case */
9602 if (player->MovPos == 0)
9603 CheckGravityMovement(player);
9606 DrawPlayer(player); /* needed here only to cleanup last field */
9609 if (player->MovPos == 0) /* player reached destination field */
9612 if (player->move_delay_reset_counter > 0)
9614 player->move_delay_reset_counter--;
9616 if (player->move_delay_reset_counter == 0)
9618 /* continue with normal speed after quickly moving through gate */
9619 HALVE_PLAYER_SPEED(player);
9621 /* be able to make the next move without delay */
9622 player->move_delay = 0;
9626 if (IS_PASSABLE(Feld[last_jx][last_jy]))
9628 /* continue with normal speed after quickly moving through gate */
9629 HALVE_PLAYER_SPEED(player);
9631 /* be able to make the next move without delay */
9632 player->move_delay = 0;
9636 if (player->block_last_field &&
9637 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9638 Feld[last_jx][last_jy] = EL_EMPTY;
9640 player->last_jx = jx;
9641 player->last_jy = jy;
9643 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9644 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9645 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9647 DrawPlayer(player); /* needed here only to cleanup last field */
9650 if (local_player->friends_still_needed == 0 ||
9651 IS_SP_ELEMENT(Feld[jx][jy]))
9652 player->LevelSolved = player->GameOver = TRUE;
9656 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9657 /* this breaks one level: "machine", level 000 */
9659 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9662 static int trigger_sides[4][2] =
9664 /* enter side leave side */
9665 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9666 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9667 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9668 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9670 int move_direction = player->MovDir;
9671 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9672 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9673 int old_jx = last_jx;
9674 int old_jy = last_jy;
9675 int old_element = Feld[old_jx][old_jy];
9676 int new_element = Feld[jx][jy];
9679 /* !!! TEST ONLY !!! */
9680 if (IS_CUSTOM_ELEMENT(old_element))
9681 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9683 player->index_bit, leave_side);
9685 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9687 player->index_bit, leave_side);
9689 if (IS_CUSTOM_ELEMENT(new_element))
9690 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9691 player->index_bit, enter_side);
9693 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9694 CE_OTHER_GETS_ENTERED,
9695 player->index_bit, enter_side);
9701 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9703 TestIfHeroTouchesBadThing(jx, jy);
9704 TestIfPlayerTouchesCustomElement(jx, jy);
9706 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9709 if (!player->active)
9713 if (level.use_step_counter)
9723 if (TimeLeft <= 10 && setup.time_limit)
9724 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9726 DrawGameValue_Time(TimeLeft);
9728 if (!TimeLeft && setup.time_limit)
9729 for (i = 0; i < MAX_PLAYERS; i++)
9730 KillHero(&stored_player[i]);
9732 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9733 DrawGameValue_Time(TimePlayed);
9736 if (tape.single_step && tape.recording && !tape.pausing &&
9737 !player->programmed_action)
9738 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9742 void ScrollScreen(struct PlayerInfo *player, int mode)
9744 static unsigned long screen_frame_counter = 0;
9746 if (mode == SCROLL_INIT)
9748 /* set scrolling step size according to actual player's moving speed */
9749 ScrollStepSize = TILEX / player->move_delay_value;
9751 screen_frame_counter = FrameCounter;
9752 ScreenMovDir = player->MovDir;
9753 ScreenMovPos = player->MovPos;
9754 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9757 else if (!FrameReached(&screen_frame_counter, 1))
9762 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9763 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9764 redraw_mask |= REDRAW_FIELD;
9767 ScreenMovDir = MV_NO_MOVING;
9770 void TestIfPlayerTouchesCustomElement(int x, int y)
9772 static int xy[4][2] =
9779 static int trigger_sides[4][2] =
9781 /* center side border side */
9782 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9783 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9784 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9785 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9787 static int touch_dir[4] =
9794 int center_element = Feld[x][y]; /* should always be non-moving! */
9797 for (i = 0; i < NUM_DIRECTIONS; i++)
9799 int xx = x + xy[i][0];
9800 int yy = y + xy[i][1];
9801 int center_side = trigger_sides[i][0];
9802 int border_side = trigger_sides[i][1];
9805 if (!IN_LEV_FIELD(xx, yy))
9808 if (IS_PLAYER(x, y))
9810 struct PlayerInfo *player = PLAYERINFO(x, y);
9812 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9813 border_element = Feld[xx][yy]; /* may be moving! */
9814 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9815 border_element = Feld[xx][yy];
9816 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9817 border_element = MovingOrBlocked2Element(xx, yy);
9819 continue; /* center and border element do not touch */
9822 /* !!! TEST ONLY !!! */
9823 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9824 player->index_bit, border_side);
9825 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9826 CE_OTHER_GETS_TOUCHED,
9827 player->index_bit, border_side);
9829 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9830 CE_OTHER_GETS_TOUCHED,
9831 player->index_bit, border_side);
9832 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9833 player->index_bit, border_side);
9836 else if (IS_PLAYER(xx, yy))
9838 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9840 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9842 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9843 continue; /* center and border element do not touch */
9847 /* !!! TEST ONLY !!! */
9848 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9849 player->index_bit, center_side);
9850 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9851 CE_OTHER_GETS_TOUCHED,
9852 player->index_bit, center_side);
9854 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9855 CE_OTHER_GETS_TOUCHED,
9856 player->index_bit, center_side);
9857 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9858 player->index_bit, center_side);
9866 void TestIfElementTouchesCustomElement(int x, int y)
9868 static int xy[4][2] =
9875 static int trigger_sides[4][2] =
9877 /* center side border side */
9878 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9879 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9880 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9881 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9883 static int touch_dir[4] =
9890 boolean change_center_element = FALSE;
9891 int center_element_change_page = 0;
9892 int center_element = Feld[x][y]; /* should always be non-moving! */
9893 int border_trigger_element;
9896 for (i = 0; i < NUM_DIRECTIONS; i++)
9898 int xx = x + xy[i][0];
9899 int yy = y + xy[i][1];
9900 int center_side = trigger_sides[i][0];
9901 int border_side = trigger_sides[i][1];
9904 if (!IN_LEV_FIELD(xx, yy))
9907 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9908 border_element = Feld[xx][yy]; /* may be moving! */
9909 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9910 border_element = Feld[xx][yy];
9911 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9912 border_element = MovingOrBlocked2Element(xx, yy);
9914 continue; /* center and border element do not touch */
9916 /* check for change of center element (but change it only once) */
9917 if (IS_CUSTOM_ELEMENT(center_element) &&
9918 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9919 !change_center_element)
9921 for (j = 0; j < element_info[center_element].num_change_pages; j++)
9923 struct ElementChangeInfo *change =
9924 &element_info[center_element].change_page[j];
9926 if (change->can_change &&
9927 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9928 change->trigger_side & border_side &&
9930 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9932 change->trigger_element == border_element
9936 change_center_element = TRUE;
9937 center_element_change_page = j;
9938 border_trigger_element = border_element;
9945 /* check for change of border element */
9946 if (IS_CUSTOM_ELEMENT(border_element) &&
9947 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9949 for (j = 0; j < element_info[border_element].num_change_pages; j++)
9951 struct ElementChangeInfo *change =
9952 &element_info[border_element].change_page[j];
9954 if (change->can_change &&
9955 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9956 change->trigger_side & center_side &&
9958 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9960 change->trigger_element == center_element
9965 printf("::: border_element %d, %d\n", x, y);
9968 CheckElementChangeByPage(xx, yy, border_element, center_element,
9969 CE_OTHER_IS_TOUCHING, j);
9976 if (change_center_element)
9979 printf("::: center_element %d, %d\n", x, y);
9982 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
9983 CE_OTHER_IS_TOUCHING, center_element_change_page);
9987 void TestIfElementHitsCustomElement(int x, int y, int direction)
9989 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9990 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9991 int hitx = x + dx, hity = y + dy;
9992 int hitting_element = Feld[x][y];
9993 int touched_element;
9995 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9996 !IS_FREE(hitx, hity) &&
9997 (!IS_MOVING(hitx, hity) ||
9998 MovDir[hitx][hity] != direction ||
9999 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10002 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10006 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10010 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10011 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10013 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10014 CE_HITTING_SOMETHING, direction);
10016 if (IN_LEV_FIELD(hitx, hity))
10018 int opposite_direction = MV_DIR_OPPOSITE(direction);
10019 int hitting_side = direction;
10020 int touched_side = opposite_direction;
10022 int touched_element = MovingOrBlocked2Element(hitx, hity);
10025 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10026 MovDir[hitx][hity] != direction ||
10027 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10036 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10037 CE_HIT_BY_SOMETHING, opposite_direction);
10039 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10040 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10042 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10044 struct ElementChangeInfo *change =
10045 &element_info[hitting_element].change_page[i];
10047 if (change->can_change &&
10048 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10049 change->trigger_side & touched_side &&
10052 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10054 change->trigger_element == touched_element
10058 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10059 CE_OTHER_IS_HITTING, i);
10065 if (IS_CUSTOM_ELEMENT(touched_element) &&
10066 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10068 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10070 struct ElementChangeInfo *change =
10071 &element_info[touched_element].change_page[i];
10073 if (change->can_change &&
10074 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10075 change->trigger_side & hitting_side &&
10077 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10079 change->trigger_element == hitting_element
10083 CheckElementChangeByPage(hitx, hity, touched_element,
10084 hitting_element, CE_OTHER_GETS_HIT, i);
10094 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10096 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10097 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10098 int hitx = x + dx, hity = y + dy;
10099 int hitting_element = Feld[x][y];
10100 int touched_element;
10102 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10103 !IS_FREE(hitx, hity) &&
10104 (!IS_MOVING(hitx, hity) ||
10105 MovDir[hitx][hity] != direction ||
10106 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10109 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10113 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10117 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10118 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10120 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10121 EP_CAN_SMASH_EVERYTHING, direction);
10123 if (IN_LEV_FIELD(hitx, hity))
10125 int opposite_direction = MV_DIR_OPPOSITE(direction);
10126 int hitting_side = direction;
10127 int touched_side = opposite_direction;
10129 int touched_element = MovingOrBlocked2Element(hitx, hity);
10132 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10133 MovDir[hitx][hity] != direction ||
10134 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10143 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10144 CE_SMASHED_BY_SOMETHING, opposite_direction);
10146 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10147 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10149 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10151 struct ElementChangeInfo *change =
10152 &element_info[hitting_element].change_page[i];
10154 if (change->can_change &&
10155 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10156 change->trigger_side & touched_side &&
10159 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10161 change->trigger_element == touched_element
10165 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10166 CE_OTHER_IS_SMASHING, i);
10172 if (IS_CUSTOM_ELEMENT(touched_element) &&
10173 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10175 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10177 struct ElementChangeInfo *change =
10178 &element_info[touched_element].change_page[i];
10180 if (change->can_change &&
10181 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10182 change->trigger_side & hitting_side &&
10184 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10186 change->trigger_element == hitting_element
10190 CheckElementChangeByPage(hitx, hity, touched_element,
10191 hitting_element, CE_OTHER_GETS_SMASHED,i);
10201 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10203 int i, kill_x = -1, kill_y = -1;
10204 int bad_element = -1;
10205 static int test_xy[4][2] =
10212 static int test_dir[4] =
10220 for (i = 0; i < NUM_DIRECTIONS; i++)
10222 int test_x, test_y, test_move_dir, test_element;
10224 test_x = good_x + test_xy[i][0];
10225 test_y = good_y + test_xy[i][1];
10227 if (!IN_LEV_FIELD(test_x, test_y))
10231 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10234 test_element = Feld[test_x][test_y];
10236 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10239 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10240 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10242 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10243 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10247 bad_element = test_element;
10253 if (kill_x != -1 || kill_y != -1)
10255 if (IS_PLAYER(good_x, good_y))
10257 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10260 if (player->shield_deadly_time_left > 0 &&
10261 !IS_INDESTRUCTIBLE(bad_element))
10262 Bang(kill_x, kill_y);
10263 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10266 if (player->shield_deadly_time_left > 0)
10267 Bang(kill_x, kill_y);
10268 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10273 Bang(good_x, good_y);
10277 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10279 int i, kill_x = -1, kill_y = -1;
10280 int bad_element = Feld[bad_x][bad_y];
10281 static int test_xy[4][2] =
10288 static int touch_dir[4] =
10290 MV_LEFT | MV_RIGHT,
10295 static int test_dir[4] =
10303 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10306 for (i = 0; i < NUM_DIRECTIONS; i++)
10308 int test_x, test_y, test_move_dir, test_element;
10310 test_x = bad_x + test_xy[i][0];
10311 test_y = bad_y + test_xy[i][1];
10312 if (!IN_LEV_FIELD(test_x, test_y))
10316 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10318 test_element = Feld[test_x][test_y];
10320 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10321 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10323 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10324 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10326 /* good thing is player or penguin that does not move away */
10327 if (IS_PLAYER(test_x, test_y))
10329 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10331 if (bad_element == EL_ROBOT && player->is_moving)
10332 continue; /* robot does not kill player if he is moving */
10334 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10336 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10337 continue; /* center and border element do not touch */
10344 else if (test_element == EL_PENGUIN)
10353 if (kill_x != -1 || kill_y != -1)
10355 if (IS_PLAYER(kill_x, kill_y))
10357 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10360 if (player->shield_deadly_time_left > 0 &&
10361 !IS_INDESTRUCTIBLE(bad_element))
10362 Bang(bad_x, bad_y);
10363 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10366 if (player->shield_deadly_time_left > 0)
10367 Bang(bad_x, bad_y);
10368 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10373 Bang(kill_x, kill_y);
10377 void TestIfHeroTouchesBadThing(int x, int y)
10379 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10382 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10384 TestIfGoodThingHitsBadThing(x, y, move_dir);
10387 void TestIfBadThingTouchesHero(int x, int y)
10389 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10392 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10394 TestIfBadThingHitsGoodThing(x, y, move_dir);
10397 void TestIfFriendTouchesBadThing(int x, int y)
10399 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10402 void TestIfBadThingTouchesFriend(int x, int y)
10404 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10407 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10409 int i, kill_x = bad_x, kill_y = bad_y;
10410 static int xy[4][2] =
10418 for (i = 0; i < NUM_DIRECTIONS; i++)
10422 x = bad_x + xy[i][0];
10423 y = bad_y + xy[i][1];
10424 if (!IN_LEV_FIELD(x, y))
10427 element = Feld[x][y];
10428 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10429 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10437 if (kill_x != bad_x || kill_y != bad_y)
10438 Bang(bad_x, bad_y);
10441 void KillHero(struct PlayerInfo *player)
10443 int jx = player->jx, jy = player->jy;
10445 if (!player->active)
10448 /* remove accessible field at the player's position */
10449 Feld[jx][jy] = EL_EMPTY;
10451 /* deactivate shield (else Bang()/Explode() would not work right) */
10452 player->shield_normal_time_left = 0;
10453 player->shield_deadly_time_left = 0;
10459 static void KillHeroUnlessEnemyProtected(int x, int y)
10461 if (!PLAYER_ENEMY_PROTECTED(x, y))
10462 KillHero(PLAYERINFO(x, y));
10465 static void KillHeroUnlessExplosionProtected(int x, int y)
10467 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10468 KillHero(PLAYERINFO(x, y));
10471 void BuryHero(struct PlayerInfo *player)
10473 int jx = player->jx, jy = player->jy;
10475 if (!player->active)
10479 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10481 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10483 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10485 player->GameOver = TRUE;
10486 RemoveHero(player);
10489 void RemoveHero(struct PlayerInfo *player)
10491 int jx = player->jx, jy = player->jy;
10492 int i, found = FALSE;
10494 player->present = FALSE;
10495 player->active = FALSE;
10497 if (!ExplodeField[jx][jy])
10498 StorePlayer[jx][jy] = 0;
10500 for (i = 0; i < MAX_PLAYERS; i++)
10501 if (stored_player[i].active)
10505 AllPlayersGone = TRUE;
10512 =============================================================================
10513 checkDiagonalPushing()
10514 -----------------------------------------------------------------------------
10515 check if diagonal input device direction results in pushing of object
10516 (by checking if the alternative direction is walkable, diggable, ...)
10517 =============================================================================
10520 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10521 int x, int y, int real_dx, int real_dy)
10523 int jx, jy, dx, dy, xx, yy;
10525 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10528 /* diagonal direction: check alternative direction */
10533 xx = jx + (dx == 0 ? real_dx : 0);
10534 yy = jy + (dy == 0 ? real_dy : 0);
10536 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10540 =============================================================================
10542 -----------------------------------------------------------------------------
10543 x, y: field next to player (non-diagonal) to try to dig to
10544 real_dx, real_dy: direction as read from input device (can be diagonal)
10545 =============================================================================
10548 int DigField(struct PlayerInfo *player,
10549 int oldx, int oldy, int x, int y,
10550 int real_dx, int real_dy, int mode)
10552 static int trigger_sides[4] =
10554 CH_SIDE_RIGHT, /* moving left */
10555 CH_SIDE_LEFT, /* moving right */
10556 CH_SIDE_BOTTOM, /* moving up */
10557 CH_SIDE_TOP, /* moving down */
10560 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10562 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10563 boolean player_was_pushing = player->is_pushing;
10564 int jx = oldx, jy = oldy;
10565 int dx = x - jx, dy = y - jy;
10566 int nextx = x + dx, nexty = y + dy;
10567 int move_direction = (dx == -1 ? MV_LEFT :
10568 dx == +1 ? MV_RIGHT :
10570 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10571 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10572 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10573 int old_element = Feld[jx][jy];
10576 if (is_player) /* function can also be called by EL_PENGUIN */
10578 if (player->MovPos == 0)
10580 player->is_digging = FALSE;
10581 player->is_collecting = FALSE;
10584 if (player->MovPos == 0) /* last pushing move finished */
10585 player->is_pushing = FALSE;
10587 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10589 player->is_switching = FALSE;
10590 player->push_delay = 0;
10592 return MF_NO_ACTION;
10596 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10597 return MF_NO_ACTION;
10602 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10604 if (IS_TUBE(Feld[jx][jy]) ||
10605 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10609 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10610 int tube_leave_directions[][2] =
10612 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10613 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10614 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10615 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
10616 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
10617 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
10618 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
10619 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
10620 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
10621 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
10622 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
10623 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10626 while (tube_leave_directions[i][0] != tube_element)
10629 if (tube_leave_directions[i][0] == -1) /* should not happen */
10633 if (!(tube_leave_directions[i][1] & move_direction))
10634 return MF_NO_ACTION; /* tube has no opening in this direction */
10639 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10640 old_element = Back[jx][jy];
10644 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10645 return MF_NO_ACTION; /* field has no opening in this direction */
10647 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10648 return MF_NO_ACTION; /* field has no opening in this direction */
10650 element = Feld[x][y];
10652 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
10653 return MF_NO_ACTION;
10655 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10656 game.engine_version >= VERSION_IDENT(2,2,0,0))
10657 return MF_NO_ACTION;
10660 if (game.gravity && is_player && !player->is_auto_moving &&
10661 canFallDown(player) && move_direction != MV_DOWN &&
10662 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10663 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10667 if (element == EL_EMPTY_SPACE &&
10668 game.gravity && !player->is_auto_moving &&
10669 canFallDown(player) && move_direction != MV_DOWN)
10670 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10676 case EL_SP_PORT_LEFT:
10677 case EL_SP_PORT_RIGHT:
10678 case EL_SP_PORT_UP:
10679 case EL_SP_PORT_DOWN:
10680 case EL_SP_PORT_HORIZONTAL:
10681 case EL_SP_PORT_VERTICAL:
10682 case EL_SP_PORT_ANY:
10683 case EL_SP_GRAVITY_PORT_LEFT:
10684 case EL_SP_GRAVITY_PORT_RIGHT:
10685 case EL_SP_GRAVITY_PORT_UP:
10686 case EL_SP_GRAVITY_PORT_DOWN:
10688 if (!canEnterSupaplexPort(x, y, dx, dy))
10689 return MF_NO_ACTION;
10692 element != EL_SP_PORT_LEFT &&
10693 element != EL_SP_GRAVITY_PORT_LEFT &&
10694 element != EL_SP_PORT_HORIZONTAL &&
10695 element != EL_SP_PORT_ANY) ||
10697 element != EL_SP_PORT_RIGHT &&
10698 element != EL_SP_GRAVITY_PORT_RIGHT &&
10699 element != EL_SP_PORT_HORIZONTAL &&
10700 element != EL_SP_PORT_ANY) ||
10702 element != EL_SP_PORT_UP &&
10703 element != EL_SP_GRAVITY_PORT_UP &&
10704 element != EL_SP_PORT_VERTICAL &&
10705 element != EL_SP_PORT_ANY) ||
10707 element != EL_SP_PORT_DOWN &&
10708 element != EL_SP_GRAVITY_PORT_DOWN &&
10709 element != EL_SP_PORT_VERTICAL &&
10710 element != EL_SP_PORT_ANY) ||
10711 !IN_LEV_FIELD(nextx, nexty) ||
10712 !IS_FREE(nextx, nexty))
10713 return MF_NO_ACTION;
10716 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10717 element == EL_SP_GRAVITY_PORT_RIGHT ||
10718 element == EL_SP_GRAVITY_PORT_UP ||
10719 element == EL_SP_GRAVITY_PORT_DOWN)
10720 game.gravity = !game.gravity;
10722 /* automatically move to the next field with double speed */
10723 player->programmed_action = move_direction;
10725 if (player->move_delay_reset_counter == 0)
10727 player->move_delay_reset_counter = 2; /* two double speed steps */
10729 DOUBLE_PLAYER_SPEED(player);
10732 player->move_delay_reset_counter = 2;
10734 DOUBLE_PLAYER_SPEED(player);
10738 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10741 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10747 case EL_TUBE_VERTICAL:
10748 case EL_TUBE_HORIZONTAL:
10749 case EL_TUBE_VERTICAL_LEFT:
10750 case EL_TUBE_VERTICAL_RIGHT:
10751 case EL_TUBE_HORIZONTAL_UP:
10752 case EL_TUBE_HORIZONTAL_DOWN:
10753 case EL_TUBE_LEFT_UP:
10754 case EL_TUBE_LEFT_DOWN:
10755 case EL_TUBE_RIGHT_UP:
10756 case EL_TUBE_RIGHT_DOWN:
10759 int tube_enter_directions[][2] =
10761 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10762 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10763 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10764 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
10765 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
10766 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
10767 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
10768 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
10769 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
10770 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
10771 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
10772 { -1, MV_NO_MOVING }
10775 while (tube_enter_directions[i][0] != element)
10778 if (tube_enter_directions[i][0] == -1) /* should not happen */
10782 if (!(tube_enter_directions[i][1] & move_direction))
10783 return MF_NO_ACTION; /* tube has no opening in this direction */
10785 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10793 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10795 if (IS_WALKABLE(element))
10798 int sound_element = SND_ELEMENT(element);
10799 int sound_action = ACTION_WALKING;
10802 if (!ACCESS_FROM(element, opposite_direction))
10803 return MF_NO_ACTION; /* field not accessible from this direction */
10807 if (element == EL_EMPTY_SPACE &&
10808 game.gravity && !player->is_auto_moving &&
10809 canFallDown(player) && move_direction != MV_DOWN)
10810 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10813 if (IS_GATE(element))
10815 if (!player->key[element - EL_GATE_1])
10816 return MF_NO_ACTION;
10818 else if (IS_GATE_GRAY(element))
10820 if (!player->key[element - EL_GATE_1_GRAY])
10821 return MF_NO_ACTION;
10823 else if (element == EL_EXIT_OPEN ||
10824 element == EL_SP_EXIT_OPEN ||
10825 element == EL_SP_EXIT_OPENING)
10827 sound_action = ACTION_PASSING; /* player is passing exit */
10829 else if (element == EL_EMPTY)
10831 sound_action = ACTION_MOVING; /* nothing to walk on */
10834 /* play sound from background or player, whatever is available */
10835 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
10836 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
10838 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10843 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
10845 else if (IS_PASSABLE(element))
10849 if (!canPassField(x, y, move_direction))
10850 return MF_NO_ACTION;
10855 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
10856 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
10857 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
10858 return MF_NO_ACTION;
10860 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10861 return MF_NO_ACTION;
10866 if (!ACCESS_FROM(element, opposite_direction))
10867 return MF_NO_ACTION; /* field not accessible from this direction */
10869 if (IS_CUSTOM_ELEMENT(element) &&
10870 !ACCESS_FROM(element, opposite_direction))
10871 return MF_NO_ACTION; /* field not accessible from this direction */
10875 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10876 return MF_NO_ACTION;
10881 if (IS_EM_GATE(element))
10883 if (!player->key[element - EL_EM_GATE_1])
10884 return MF_NO_ACTION;
10886 else if (IS_EM_GATE_GRAY(element))
10888 if (!player->key[element - EL_EM_GATE_1_GRAY])
10889 return MF_NO_ACTION;
10891 else if (IS_SP_PORT(element))
10893 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10894 element == EL_SP_GRAVITY_PORT_RIGHT ||
10895 element == EL_SP_GRAVITY_PORT_UP ||
10896 element == EL_SP_GRAVITY_PORT_DOWN)
10897 game.gravity = !game.gravity;
10900 /* automatically move to the next field with double speed */
10901 player->programmed_action = move_direction;
10903 if (player->move_delay_reset_counter == 0)
10905 player->move_delay_reset_counter = 2; /* two double speed steps */
10907 DOUBLE_PLAYER_SPEED(player);
10910 player->move_delay_reset_counter = 2;
10912 DOUBLE_PLAYER_SPEED(player);
10915 PlayLevelSoundAction(x, y, ACTION_PASSING);
10919 else if (IS_DIGGABLE(element))
10923 if (mode != DF_SNAP)
10926 GfxElement[x][y] = GFX_ELEMENT(element);
10929 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
10931 player->is_digging = TRUE;
10934 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10936 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
10937 player->index_bit, dig_side);
10940 if (mode == DF_SNAP)
10941 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10946 else if (IS_COLLECTIBLE(element))
10950 if (is_player && mode != DF_SNAP)
10952 GfxElement[x][y] = element;
10953 player->is_collecting = TRUE;
10956 if (element == EL_SPEED_PILL)
10957 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10958 else if (element == EL_EXTRA_TIME && level.time > 0)
10961 DrawGameValue_Time(TimeLeft);
10963 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10965 player->shield_normal_time_left += 10;
10966 if (element == EL_SHIELD_DEADLY)
10967 player->shield_deadly_time_left += 10;
10969 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10971 if (player->inventory_size < MAX_INVENTORY_SIZE)
10972 player->inventory_element[player->inventory_size++] = element;
10974 DrawGameValue_Dynamite(local_player->inventory_size);
10976 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10978 player->dynabomb_count++;
10979 player->dynabombs_left++;
10981 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10983 player->dynabomb_size++;
10985 else if (element == EL_DYNABOMB_INCREASE_POWER)
10987 player->dynabomb_xl = TRUE;
10989 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
10990 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
10992 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
10993 element - EL_KEY_1 : element - EL_EM_KEY_1);
10995 player->key[key_nr] = TRUE;
10997 DrawGameValue_Keys(player);
10999 redraw_mask |= REDRAW_DOOR_1;
11001 else if (IS_ENVELOPE(element))
11004 player->show_envelope = element;
11006 ShowEnvelope(element - EL_ENVELOPE_1);
11009 else if (IS_DROPPABLE(element) ||
11010 IS_THROWABLE(element)) /* can be collected and dropped */
11014 if (element_info[element].collect_count == 0)
11015 player->inventory_infinite_element = element;
11017 for (i = 0; i < element_info[element].collect_count; i++)
11018 if (player->inventory_size < MAX_INVENTORY_SIZE)
11019 player->inventory_element[player->inventory_size++] = element;
11021 DrawGameValue_Dynamite(local_player->inventory_size);
11023 else if (element_info[element].collect_count > 0)
11025 local_player->gems_still_needed -=
11026 element_info[element].collect_count;
11027 if (local_player->gems_still_needed < 0)
11028 local_player->gems_still_needed = 0;
11030 DrawGameValue_Emeralds(local_player->gems_still_needed);
11033 RaiseScoreElement(element);
11034 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11037 CheckTriggeredElementChangeByPlayer(x, y, element,
11038 CE_OTHER_GETS_COLLECTED,
11039 player->index_bit, dig_side);
11042 if (mode == DF_SNAP)
11043 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11048 else if (IS_PUSHABLE(element))
11050 if (mode == DF_SNAP && element != EL_BD_ROCK)
11051 return MF_NO_ACTION;
11053 if (CAN_FALL(element) && dy)
11054 return MF_NO_ACTION;
11056 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11057 !(element == EL_SPRING && level.use_spring_bug))
11058 return MF_NO_ACTION;
11061 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11062 ((move_direction & MV_VERTICAL &&
11063 ((element_info[element].move_pattern & MV_LEFT &&
11064 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11065 (element_info[element].move_pattern & MV_RIGHT &&
11066 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11067 (move_direction & MV_HORIZONTAL &&
11068 ((element_info[element].move_pattern & MV_UP &&
11069 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11070 (element_info[element].move_pattern & MV_DOWN &&
11071 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11072 return MF_NO_ACTION;
11076 /* do not push elements already moving away faster than player */
11077 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11078 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11079 return MF_NO_ACTION;
11081 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11082 return MF_NO_ACTION;
11088 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11090 if (player->push_delay_value == -1 || !player_was_pushing)
11091 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11093 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11095 if (player->push_delay_value == -1)
11096 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11099 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11101 if (player->push_delay_value == -1 || !player_was_pushing)
11102 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11105 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11107 if (!player->is_pushing)
11108 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11112 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11113 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11114 !player_is_pushing))
11115 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11118 if (!player->is_pushing &&
11119 game.engine_version >= VERSION_IDENT(2,2,0,7))
11120 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11124 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11125 player->push_delay, player->push_delay_value,
11126 FrameCounter, game.engine_version,
11127 player_was_pushing, player->is_pushing,
11128 element, element_info[element].token_name,
11129 GET_NEW_PUSH_DELAY(element));
11132 player->is_pushing = TRUE;
11134 if (!(IN_LEV_FIELD(nextx, nexty) &&
11135 (IS_FREE(nextx, nexty) ||
11136 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11137 IS_SB_ELEMENT(element)))))
11138 return MF_NO_ACTION;
11140 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11141 return MF_NO_ACTION;
11143 if (player->push_delay == 0) /* new pushing; restart delay */
11144 player->push_delay = FrameCounter;
11146 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11147 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11148 element != EL_SPRING && element != EL_BALLOON)
11150 /* make sure that there is no move delay before next try to push */
11151 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11152 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11154 return MF_NO_ACTION;
11158 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11161 if (IS_SB_ELEMENT(element))
11163 if (element == EL_SOKOBAN_FIELD_FULL)
11165 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11166 local_player->sokobanfields_still_needed++;
11169 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11171 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11172 local_player->sokobanfields_still_needed--;
11175 Feld[x][y] = EL_SOKOBAN_OBJECT;
11177 if (Back[x][y] == Back[nextx][nexty])
11178 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11179 else if (Back[x][y] != 0)
11180 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11183 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11186 if (local_player->sokobanfields_still_needed == 0 &&
11187 game.emulation == EMU_SOKOBAN)
11189 player->LevelSolved = player->GameOver = TRUE;
11190 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11194 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11196 InitMovingField(x, y, move_direction);
11197 GfxAction[x][y] = ACTION_PUSHING;
11199 if (mode == DF_SNAP)
11200 ContinueMoving(x, y);
11202 MovPos[x][y] = (dx != 0 ? dx : dy);
11204 Pushed[x][y] = TRUE;
11205 Pushed[nextx][nexty] = TRUE;
11207 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11208 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11210 player->push_delay_value = -1; /* get new value later */
11213 /* check for element change _after_ element has been pushed! */
11217 /* !!! TEST ONLY !!! */
11218 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11219 player->index_bit, dig_side);
11220 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11221 player->index_bit, dig_side);
11223 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11224 player->index_bit, dig_side);
11225 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11226 player->index_bit, dig_side);
11232 else if (IS_SWITCHABLE(element))
11234 if (PLAYER_SWITCHING(player, x, y))
11237 player->is_switching = TRUE;
11238 player->switch_x = x;
11239 player->switch_y = y;
11241 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11243 if (element == EL_ROBOT_WHEEL)
11245 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11249 DrawLevelField(x, y);
11251 else if (element == EL_SP_TERMINAL)
11255 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11257 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11259 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11260 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11263 else if (IS_BELT_SWITCH(element))
11265 ToggleBeltSwitch(x, y);
11267 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11268 element == EL_SWITCHGATE_SWITCH_DOWN)
11270 ToggleSwitchgateSwitch(x, y);
11272 else if (element == EL_LIGHT_SWITCH ||
11273 element == EL_LIGHT_SWITCH_ACTIVE)
11275 ToggleLightSwitch(x, y);
11278 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11279 SND_LIGHT_SWITCH_ACTIVATING :
11280 SND_LIGHT_SWITCH_DEACTIVATING);
11283 else if (element == EL_TIMEGATE_SWITCH)
11285 ActivateTimegateSwitch(x, y);
11287 else if (element == EL_BALLOON_SWITCH_LEFT ||
11288 element == EL_BALLOON_SWITCH_RIGHT ||
11289 element == EL_BALLOON_SWITCH_UP ||
11290 element == EL_BALLOON_SWITCH_DOWN ||
11291 element == EL_BALLOON_SWITCH_ANY)
11293 if (element == EL_BALLOON_SWITCH_ANY)
11294 game.balloon_dir = move_direction;
11296 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11297 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11298 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11299 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11302 else if (element == EL_LAMP)
11304 Feld[x][y] = EL_LAMP_ACTIVE;
11305 local_player->lights_still_needed--;
11307 DrawLevelField(x, y);
11309 else if (element == EL_TIME_ORB_FULL)
11311 Feld[x][y] = EL_TIME_ORB_EMPTY;
11313 DrawGameValue_Time(TimeLeft);
11315 DrawLevelField(x, y);
11318 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11326 if (!PLAYER_SWITCHING(player, x, y))
11328 player->is_switching = TRUE;
11329 player->switch_x = x;
11330 player->switch_y = y;
11333 /* !!! TEST ONLY !!! */
11334 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11335 player->index_bit, dig_side);
11336 CheckTriggeredElementChangeByPlayer(x, y, element,
11337 CE_OTHER_IS_SWITCHING,
11338 player->index_bit, dig_side);
11340 CheckTriggeredElementChangeByPlayer(x, y, element,
11341 CE_OTHER_IS_SWITCHING,
11342 player->index_bit, dig_side);
11343 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11344 player->index_bit, dig_side);
11349 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11350 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11351 player->index_bit, dig_side);
11352 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11353 player->index_bit, dig_side);
11355 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11356 player->index_bit, dig_side);
11357 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11358 player->index_bit, dig_side);
11362 return MF_NO_ACTION;
11365 player->push_delay = 0;
11367 if (Feld[x][y] != element) /* really digged/collected something */
11368 player->is_collecting = !player->is_digging;
11373 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11375 int jx = player->jx, jy = player->jy;
11376 int x = jx + dx, y = jy + dy;
11377 int snap_direction = (dx == -1 ? MV_LEFT :
11378 dx == +1 ? MV_RIGHT :
11380 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11383 if (player->MovPos != 0)
11386 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11390 if (!player->active || !IN_LEV_FIELD(x, y))
11398 if (player->MovPos == 0)
11399 player->is_pushing = FALSE;
11401 player->is_snapping = FALSE;
11403 if (player->MovPos == 0)
11405 player->is_moving = FALSE;
11406 player->is_digging = FALSE;
11407 player->is_collecting = FALSE;
11413 if (player->is_snapping)
11416 player->MovDir = snap_direction;
11419 if (player->MovPos == 0)
11422 player->is_moving = FALSE;
11423 player->is_digging = FALSE;
11424 player->is_collecting = FALSE;
11427 player->is_dropping = FALSE;
11429 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11432 player->is_snapping = TRUE;
11435 if (player->MovPos == 0)
11438 player->is_moving = FALSE;
11439 player->is_digging = FALSE;
11440 player->is_collecting = FALSE;
11444 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11445 DrawLevelField(player->last_jx, player->last_jy);
11448 DrawLevelField(x, y);
11457 boolean DropElement(struct PlayerInfo *player)
11459 static int trigger_sides[4] =
11461 CH_SIDE_LEFT, /* dropping left */
11462 CH_SIDE_RIGHT, /* dropping right */
11463 CH_SIDE_TOP, /* dropping up */
11464 CH_SIDE_BOTTOM, /* dropping down */
11466 int old_element, new_element;
11467 int dropx = player->jx, dropy = player->jy;
11468 int drop_direction = player->MovDir;
11469 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11470 int drop_element = (player->inventory_size > 0 ?
11471 player->inventory_element[player->inventory_size - 1] :
11472 player->inventory_infinite_element != EL_UNDEFINED ?
11473 player->inventory_infinite_element :
11474 player->dynabombs_left > 0 ?
11475 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11478 if (IS_THROWABLE(drop_element))
11480 dropx += GET_DX_FROM_DIR(drop_direction);
11481 dropy += GET_DY_FROM_DIR(drop_direction);
11483 if (!IN_LEV_FIELD(dropx, dropy))
11487 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11488 new_element = drop_element; /* default: no change when dropping */
11490 /* check if player is active, not moving and ready to drop */
11491 if (!player->active || player->MovPos || player->drop_delay > 0)
11494 /* check if player has anything that can be dropped */
11496 if (new_element == EL_UNDEFINED)
11499 if (player->inventory_size == 0 &&
11500 player->inventory_infinite_element == EL_UNDEFINED &&
11501 player->dynabombs_left == 0)
11505 /* check if anything can be dropped at the current position */
11506 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11509 /* collected custom elements can only be dropped on empty fields */
11511 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11514 if (player->inventory_size > 0 &&
11515 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11516 && old_element != EL_EMPTY)
11520 if (old_element != EL_EMPTY)
11521 Back[dropx][dropy] = old_element; /* store old element on this field */
11523 ResetGfxAnimation(dropx, dropy);
11524 ResetRandomAnimationValue(dropx, dropy);
11526 if (player->inventory_size > 0 ||
11527 player->inventory_infinite_element != EL_UNDEFINED)
11529 if (player->inventory_size > 0)
11531 player->inventory_size--;
11534 new_element = player->inventory_element[player->inventory_size];
11537 DrawGameValue_Dynamite(local_player->inventory_size);
11539 if (new_element == EL_DYNAMITE)
11540 new_element = EL_DYNAMITE_ACTIVE;
11541 else if (new_element == EL_SP_DISK_RED)
11542 new_element = EL_SP_DISK_RED_ACTIVE;
11545 Feld[dropx][dropy] = new_element;
11547 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11548 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11549 el2img(Feld[dropx][dropy]), 0);
11551 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11554 /* needed if previous element just changed to "empty" in the last frame */
11555 Changed[dropx][dropy] = 0; /* allow another change */
11559 /* !!! TEST ONLY !!! */
11560 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11561 player->index_bit, drop_side);
11562 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11563 CE_OTHER_GETS_DROPPED,
11564 player->index_bit, drop_side);
11566 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11567 CE_OTHER_GETS_DROPPED,
11568 player->index_bit, drop_side);
11569 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11570 player->index_bit, drop_side);
11573 TestIfElementTouchesCustomElement(dropx, dropy);
11575 else /* player is dropping a dyna bomb */
11577 player->dynabombs_left--;
11580 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
11583 Feld[dropx][dropy] = new_element;
11585 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11586 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11587 el2img(Feld[dropx][dropy]), 0);
11589 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11596 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11599 InitField_WithBug1(dropx, dropy, FALSE);
11601 InitField(dropx, dropy, FALSE);
11602 if (CAN_MOVE(Feld[dropx][dropy]))
11603 InitMovDir(dropx, dropy);
11607 new_element = Feld[dropx][dropy]; /* element might have changed */
11609 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11610 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11613 int move_stepsize = element_info[new_element].move_stepsize;
11615 int move_direction, nextx, nexty;
11617 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11618 MovDir[dropx][dropy] = drop_direction;
11620 move_direction = MovDir[dropx][dropy];
11621 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11622 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11625 Changed[dropx][dropy] = 0; /* allow another change */
11626 CheckCollision[dropx][dropy] = 2;
11629 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
11632 WasJustMoving[dropx][dropy] = 3;
11635 InitMovingField(dropx, dropy, move_direction);
11636 ContinueMoving(dropx, dropy);
11643 Changed[dropx][dropy] = 0; /* allow another change */
11646 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
11648 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
11649 CE_HITTING_SOMETHING, move_direction);
11657 player->drop_delay = 2 * TILEX / move_stepsize + 1;
11662 player->drop_delay = 8 + 8 + 8;
11666 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11671 player->is_dropping = TRUE;
11677 /* ------------------------------------------------------------------------- */
11678 /* game sound playing functions */
11679 /* ------------------------------------------------------------------------- */
11681 static int *loop_sound_frame = NULL;
11682 static int *loop_sound_volume = NULL;
11684 void InitPlayLevelSound()
11686 int num_sounds = getSoundListSize();
11688 checked_free(loop_sound_frame);
11689 checked_free(loop_sound_volume);
11691 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11692 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11695 static void PlayLevelSound(int x, int y, int nr)
11697 int sx = SCREENX(x), sy = SCREENY(y);
11698 int volume, stereo_position;
11699 int max_distance = 8;
11700 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11702 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11703 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11706 if (!IN_LEV_FIELD(x, y) ||
11707 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11708 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11711 volume = SOUND_MAX_VOLUME;
11713 if (!IN_SCR_FIELD(sx, sy))
11715 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11716 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11718 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11721 stereo_position = (SOUND_MAX_LEFT +
11722 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11723 (SCR_FIELDX + 2 * max_distance));
11725 if (IS_LOOP_SOUND(nr))
11727 /* This assures that quieter loop sounds do not overwrite louder ones,
11728 while restarting sound volume comparison with each new game frame. */
11730 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11733 loop_sound_volume[nr] = volume;
11734 loop_sound_frame[nr] = FrameCounter;
11737 PlaySoundExt(nr, volume, stereo_position, type);
11740 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11742 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11743 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11744 y < LEVELY(BY1) ? LEVELY(BY1) :
11745 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11749 static void PlayLevelSoundAction(int x, int y, int action)
11751 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11754 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11756 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11758 if (sound_effect != SND_UNDEFINED)
11759 PlayLevelSound(x, y, sound_effect);
11762 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11765 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11767 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11768 PlayLevelSound(x, y, sound_effect);
11771 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11773 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11775 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11776 PlayLevelSound(x, y, sound_effect);
11779 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11781 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11783 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11784 StopSound(sound_effect);
11787 static void PlayLevelMusic()
11789 if (levelset.music[level_nr] != MUS_UNDEFINED)
11790 PlayMusic(levelset.music[level_nr]); /* from config file */
11792 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11795 void RaiseScore(int value)
11797 local_player->score += value;
11799 DrawGameValue_Score(local_player->score);
11802 void RaiseScoreElement(int element)
11807 case EL_BD_DIAMOND:
11808 case EL_EMERALD_YELLOW:
11809 case EL_EMERALD_RED:
11810 case EL_EMERALD_PURPLE:
11811 case EL_SP_INFOTRON:
11812 RaiseScore(level.score[SC_EMERALD]);
11815 RaiseScore(level.score[SC_DIAMOND]);
11818 RaiseScore(level.score[SC_CRYSTAL]);
11821 RaiseScore(level.score[SC_PEARL]);
11824 case EL_BD_BUTTERFLY:
11825 case EL_SP_ELECTRON:
11826 RaiseScore(level.score[SC_BUG]);
11829 case EL_BD_FIREFLY:
11830 case EL_SP_SNIKSNAK:
11831 RaiseScore(level.score[SC_SPACESHIP]);
11834 case EL_DARK_YAMYAM:
11835 RaiseScore(level.score[SC_YAMYAM]);
11838 RaiseScore(level.score[SC_ROBOT]);
11841 RaiseScore(level.score[SC_PACMAN]);
11844 RaiseScore(level.score[SC_NUT]);
11847 case EL_SP_DISK_RED:
11848 case EL_DYNABOMB_INCREASE_NUMBER:
11849 case EL_DYNABOMB_INCREASE_SIZE:
11850 case EL_DYNABOMB_INCREASE_POWER:
11851 RaiseScore(level.score[SC_DYNAMITE]);
11853 case EL_SHIELD_NORMAL:
11854 case EL_SHIELD_DEADLY:
11855 RaiseScore(level.score[SC_SHIELD]);
11857 case EL_EXTRA_TIME:
11858 RaiseScore(level.score[SC_TIME_BONUS]);
11864 RaiseScore(level.score[SC_KEY]);
11867 RaiseScore(element_info[element].collect_score);
11872 void RequestQuitGame(boolean ask_if_really_quit)
11874 if (AllPlayersGone ||
11875 !ask_if_really_quit ||
11876 level_editor_test_game ||
11877 Request("Do you really want to quit the game ?",
11878 REQ_ASK | REQ_STAY_CLOSED))
11880 #if defined(NETWORK_AVALIABLE)
11881 if (options.network)
11882 SendToServer_StopPlaying();
11886 game_status = GAME_MODE_MAIN;
11894 if (tape.playing && tape.deactivate_display)
11895 TapeDeactivateDisplayOff(TRUE);
11898 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11901 if (tape.playing && tape.deactivate_display)
11902 TapeDeactivateDisplayOn();
11909 /* ---------- new game button stuff ---------------------------------------- */
11911 /* graphic position values for game buttons */
11912 #define GAME_BUTTON_XSIZE 30
11913 #define GAME_BUTTON_YSIZE 30
11914 #define GAME_BUTTON_XPOS 5
11915 #define GAME_BUTTON_YPOS 215
11916 #define SOUND_BUTTON_XPOS 5
11917 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11919 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11920 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11921 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11922 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11923 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11924 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11931 } gamebutton_info[NUM_GAME_BUTTONS] =
11934 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11939 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11940 GAME_CTRL_ID_PAUSE,
11944 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11949 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11950 SOUND_CTRL_ID_MUSIC,
11951 "background music on/off"
11954 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11955 SOUND_CTRL_ID_LOOPS,
11956 "sound loops on/off"
11959 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11960 SOUND_CTRL_ID_SIMPLE,
11961 "normal sounds on/off"
11965 void CreateGameButtons()
11969 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11971 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11972 struct GadgetInfo *gi;
11975 unsigned long event_mask;
11976 int gd_xoffset, gd_yoffset;
11977 int gd_x1, gd_x2, gd_y1, gd_y2;
11980 gd_xoffset = gamebutton_info[i].x;
11981 gd_yoffset = gamebutton_info[i].y;
11982 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11983 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11985 if (id == GAME_CTRL_ID_STOP ||
11986 id == GAME_CTRL_ID_PAUSE ||
11987 id == GAME_CTRL_ID_PLAY)
11989 button_type = GD_TYPE_NORMAL_BUTTON;
11991 event_mask = GD_EVENT_RELEASED;
11992 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11993 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11997 button_type = GD_TYPE_CHECK_BUTTON;
11999 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12000 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12001 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12002 event_mask = GD_EVENT_PRESSED;
12003 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12004 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12007 gi = CreateGadget(GDI_CUSTOM_ID, id,
12008 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12009 GDI_X, DX + gd_xoffset,
12010 GDI_Y, DY + gd_yoffset,
12011 GDI_WIDTH, GAME_BUTTON_XSIZE,
12012 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12013 GDI_TYPE, button_type,
12014 GDI_STATE, GD_BUTTON_UNPRESSED,
12015 GDI_CHECKED, checked,
12016 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12017 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12018 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12019 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12020 GDI_EVENT_MASK, event_mask,
12021 GDI_CALLBACK_ACTION, HandleGameButtons,
12025 Error(ERR_EXIT, "cannot create gadget");
12027 game_gadget[id] = gi;
12031 void FreeGameButtons()
12035 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12036 FreeGadget(game_gadget[i]);
12039 static void MapGameButtons()
12043 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12044 MapGadget(game_gadget[i]);
12047 void UnmapGameButtons()
12051 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12052 UnmapGadget(game_gadget[i]);
12055 static void HandleGameButtons(struct GadgetInfo *gi)
12057 int id = gi->custom_id;
12059 if (game_status != GAME_MODE_PLAYING)
12064 case GAME_CTRL_ID_STOP:
12065 RequestQuitGame(TRUE);
12068 case GAME_CTRL_ID_PAUSE:
12069 if (options.network)
12071 #if defined(NETWORK_AVALIABLE)
12073 SendToServer_ContinuePlaying();
12075 SendToServer_PausePlaying();
12079 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12082 case GAME_CTRL_ID_PLAY:
12085 #if defined(NETWORK_AVALIABLE)
12086 if (options.network)
12087 SendToServer_ContinuePlaying();
12091 tape.pausing = FALSE;
12092 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12097 case SOUND_CTRL_ID_MUSIC:
12098 if (setup.sound_music)
12100 setup.sound_music = FALSE;
12103 else if (audio.music_available)
12105 setup.sound = setup.sound_music = TRUE;
12107 SetAudioMode(setup.sound);
12113 case SOUND_CTRL_ID_LOOPS:
12114 if (setup.sound_loops)
12115 setup.sound_loops = FALSE;
12116 else if (audio.loops_available)
12118 setup.sound = setup.sound_loops = TRUE;
12119 SetAudioMode(setup.sound);
12123 case SOUND_CTRL_ID_SIMPLE:
12124 if (setup.sound_simple)
12125 setup.sound_simple = FALSE;
12126 else if (audio.sound_available)
12128 setup.sound = setup.sound_simple = TRUE;
12129 SetAudioMode(setup.sound);