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 INIT_GFX_RANDOM() (SimpleRND(1000000))
101 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
102 RND(element_info[e].push_delay_random))
103 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
104 RND(element_info[e].drop_delay_random))
105 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
106 RND(element_info[e].move_delay_random))
107 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
108 (element_info[e].move_delay_random))
110 #define GET_TARGET_ELEMENT(e, ch) \
111 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
112 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
114 #define CAN_GROW_INTO(e) \
115 (e == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
117 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
118 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
121 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
122 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
123 (CAN_MOVE_INTO_ACID(e) && \
124 Feld[x][y] == EL_ACID) || \
127 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
128 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
129 (CAN_MOVE_INTO_ACID(e) && \
130 Feld[x][y] == EL_ACID) || \
133 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
134 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
136 (CAN_MOVE_INTO_ACID(e) && \
137 Feld[x][y] == EL_ACID) || \
138 (DONT_COLLIDE_WITH(e) && \
140 !PLAYER_ENEMY_PROTECTED(x, y))))
143 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
144 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
146 (DONT_COLLIDE_WITH(e) && \
148 !PLAYER_ENEMY_PROTECTED(x, y))))
151 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
152 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
155 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
156 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
158 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
159 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
163 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
166 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
167 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
171 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
172 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
174 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
175 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
177 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
178 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
180 #define PIG_CAN_ENTER_FIELD(e, x, y) \
181 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
183 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
184 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
185 IS_FOOD_PENGUIN(Feld[x][y])))
186 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
187 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
189 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
192 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
193 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
197 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
198 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
199 (CAN_MOVE_INTO_ACID(e) && \
200 Feld[x][y] == EL_ACID) || \
201 Feld[x][y] == EL_DIAMOND))
203 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
204 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
205 (CAN_MOVE_INTO_ACID(e) && \
206 Feld[x][y] == EL_ACID) || \
207 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
209 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
210 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
211 (CAN_MOVE_INTO_ACID(e) && \
212 Feld[x][y] == EL_ACID) || \
213 IS_AMOEBOID(Feld[x][y])))
215 #define PIG_CAN_ENTER_FIELD(e, x, y) \
216 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
217 (CAN_MOVE_INTO_ACID(e) && \
218 Feld[x][y] == EL_ACID) || \
219 IS_FOOD_PIG(Feld[x][y])))
221 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
222 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
223 (CAN_MOVE_INTO_ACID(e) && \
224 Feld[x][y] == EL_ACID) || \
225 IS_FOOD_PENGUIN(Feld[x][y]) || \
226 Feld[x][y] == EL_EXIT_OPEN))
228 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
229 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
230 (CAN_MOVE_INTO_ACID(e) && \
231 Feld[x][y] == EL_ACID)))
233 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
234 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
235 (CAN_MOVE_INTO_ACID(e) && \
236 Feld[x][y] == EL_ACID) || \
239 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
240 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
241 (CAN_MOVE_INTO_ACID(e) && \
242 Feld[x][y] == EL_ACID)))
246 #define GROUP_NR(e) ((e) - EL_GROUP_START)
247 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
248 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
249 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
251 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
252 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
255 #define CE_ENTER_FIELD_COND(e, x, y) \
256 (!IS_PLAYER(x, y) && \
257 (Feld[x][y] == EL_ACID || \
258 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
260 #define CE_ENTER_FIELD_COND(e, x, y) \
261 (!IS_PLAYER(x, y) && \
262 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
265 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
266 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
268 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
269 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
271 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
272 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
273 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
274 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
276 /* game button identifiers */
277 #define GAME_CTRL_ID_STOP 0
278 #define GAME_CTRL_ID_PAUSE 1
279 #define GAME_CTRL_ID_PLAY 2
280 #define SOUND_CTRL_ID_MUSIC 3
281 #define SOUND_CTRL_ID_LOOPS 4
282 #define SOUND_CTRL_ID_SIMPLE 5
284 #define NUM_GAME_BUTTONS 6
287 /* forward declaration for internal use */
289 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
290 static boolean MovePlayer(struct PlayerInfo *, int, int);
291 static void ScrollPlayer(struct PlayerInfo *, int);
292 static void ScrollScreen(struct PlayerInfo *, int);
294 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
296 static void InitBeltMovement(void);
297 static void CloseAllOpenTimegates(void);
298 static void CheckGravityMovement(struct PlayerInfo *);
299 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
300 static void KillHeroUnlessEnemyProtected(int, int);
301 static void KillHeroUnlessExplosionProtected(int, int);
303 static void TestIfPlayerTouchesCustomElement(int, int);
304 static void TestIfElementTouchesCustomElement(int, int);
305 static void TestIfElementHitsCustomElement(int, int, int);
307 static void TestIfElementSmashesCustomElement(int, int, int);
310 static void ChangeElement(int, int, int);
312 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
313 #define CheckTriggeredElementChange(x, y, e, ev) \
314 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
316 #define CheckTriggeredElementChangePlayer(x, y, e, ev, p, s) \
317 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
318 #define CheckTriggeredElementChangeSide(x, y, e, ev, s) \
319 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
320 #define CheckTriggeredElementChangePage(x, y, e, ev, p) \
321 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
324 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
325 #define CheckElementChange(x, y, e, te, ev) \
326 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
327 #define CheckElementChangePlayer(x, y, e, ev, p, s) \
328 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
329 #define CheckElementChangeSide(x, y, e, te, ev, s) \
330 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
331 #define CheckElementChangePage(x, y, e, te, ev, p) \
332 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
334 static void PlayLevelSound(int, int, int);
335 static void PlayLevelSoundNearest(int, int, int);
336 static void PlayLevelSoundAction(int, int, int);
337 static void PlayLevelSoundElementAction(int, int, int, int);
338 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
339 static void PlayLevelSoundActionIfLoop(int, int, int);
340 static void StopLevelSoundActionIfLoop(int, int, int);
341 static void PlayLevelMusic();
343 static void MapGameButtons();
344 static void HandleGameButtons(struct GadgetInfo *);
346 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
349 /* ------------------------------------------------------------------------- */
350 /* definition of elements that automatically change to other elements after */
351 /* a specified time, eventually calling a function when changing */
352 /* ------------------------------------------------------------------------- */
354 /* forward declaration for changer functions */
355 static void InitBuggyBase(int x, int y);
356 static void WarnBuggyBase(int x, int y);
358 static void InitTrap(int x, int y);
359 static void ActivateTrap(int x, int y);
360 static void ChangeActiveTrap(int x, int y);
362 static void InitRobotWheel(int x, int y);
363 static void RunRobotWheel(int x, int y);
364 static void StopRobotWheel(int x, int y);
366 static void InitTimegateWheel(int x, int y);
367 static void RunTimegateWheel(int x, int y);
369 struct ChangingElementInfo
374 void (*pre_change_function)(int x, int y);
375 void (*change_function)(int x, int y);
376 void (*post_change_function)(int x, int y);
379 static struct ChangingElementInfo change_delay_list[] =
430 EL_SWITCHGATE_OPENING,
438 EL_SWITCHGATE_CLOSING,
439 EL_SWITCHGATE_CLOSED,
471 EL_ACID_SPLASH_RIGHT,
480 EL_SP_BUGGY_BASE_ACTIVATING,
487 EL_SP_BUGGY_BASE_ACTIVATING,
488 EL_SP_BUGGY_BASE_ACTIVE,
495 EL_SP_BUGGY_BASE_ACTIVE,
519 EL_ROBOT_WHEEL_ACTIVE,
527 EL_TIMEGATE_SWITCH_ACTIVE,
548 int push_delay_fixed, push_delay_random;
553 { EL_BALLOON, 0, 0 },
555 { EL_SOKOBAN_OBJECT, 2, 0 },
556 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
557 { EL_SATELLITE, 2, 0 },
558 { EL_SP_DISK_YELLOW, 2, 0 },
560 { EL_UNDEFINED, 0, 0 },
568 move_stepsize_list[] =
570 { EL_AMOEBA_DROP, 2 },
571 { EL_AMOEBA_DROPPING, 2 },
572 { EL_QUICKSAND_FILLING, 1 },
573 { EL_QUICKSAND_EMPTYING, 1 },
574 { EL_MAGIC_WALL_FILLING, 2 },
575 { EL_BD_MAGIC_WALL_FILLING, 2 },
576 { EL_MAGIC_WALL_EMPTYING, 2 },
577 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
587 collect_count_list[] =
590 { EL_BD_DIAMOND, 1 },
591 { EL_EMERALD_YELLOW, 1 },
592 { EL_EMERALD_RED, 1 },
593 { EL_EMERALD_PURPLE, 1 },
595 { EL_SP_INFOTRON, 1 },
607 access_direction_list[] =
609 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
610 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
611 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
612 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
613 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
614 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
615 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
616 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
617 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
618 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
619 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
621 { EL_SP_PORT_LEFT, MV_RIGHT },
622 { EL_SP_PORT_RIGHT, MV_LEFT },
623 { EL_SP_PORT_UP, MV_DOWN },
624 { EL_SP_PORT_DOWN, MV_UP },
625 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
626 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
627 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
628 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
629 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
630 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
631 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
633 { EL_UNDEFINED, MV_NO_MOVING }
636 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
638 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
639 CH_EVENT_BIT(CE_DELAY))
640 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
641 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
642 IS_JUST_CHANGING(x, y))
644 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
647 void GetPlayerConfig()
649 if (!audio.sound_available)
650 setup.sound_simple = FALSE;
652 if (!audio.loops_available)
653 setup.sound_loops = FALSE;
655 if (!audio.music_available)
656 setup.sound_music = FALSE;
658 if (!video.fullscreen_available)
659 setup.fullscreen = FALSE;
661 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
663 SetAudioMode(setup.sound);
667 static int getBeltNrFromBeltElement(int element)
669 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
670 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
671 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
674 static int getBeltNrFromBeltActiveElement(int element)
676 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
677 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
678 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
681 static int getBeltNrFromBeltSwitchElement(int element)
683 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
684 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
685 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
688 static int getBeltDirNrFromBeltSwitchElement(int element)
690 static int belt_base_element[4] =
692 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
693 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
694 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
695 EL_CONVEYOR_BELT_4_SWITCH_LEFT
698 int belt_nr = getBeltNrFromBeltSwitchElement(element);
699 int belt_dir_nr = element - belt_base_element[belt_nr];
701 return (belt_dir_nr % 3);
704 static int getBeltDirFromBeltSwitchElement(int element)
706 static int belt_move_dir[3] =
713 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
715 return belt_move_dir[belt_dir_nr];
718 static void InitPlayerField(int x, int y, int element, boolean init_game)
720 if (element == EL_SP_MURPHY)
724 if (stored_player[0].present)
726 Feld[x][y] = EL_SP_MURPHY_CLONE;
732 stored_player[0].use_murphy_graphic = TRUE;
735 Feld[x][y] = EL_PLAYER_1;
741 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
742 int jx = player->jx, jy = player->jy;
744 player->present = TRUE;
746 player->block_last_field = (element == EL_SP_MURPHY ?
747 level.sp_block_last_field :
748 level.block_last_field);
750 if (!options.network || player->connected)
752 player->active = TRUE;
754 /* remove potentially duplicate players */
755 if (StorePlayer[jx][jy] == Feld[x][y])
756 StorePlayer[jx][jy] = 0;
758 StorePlayer[x][y] = Feld[x][y];
762 printf("Player %d activated.\n", player->element_nr);
763 printf("[Local player is %d and currently %s.]\n",
764 local_player->element_nr,
765 local_player->active ? "active" : "not active");
769 Feld[x][y] = EL_EMPTY;
770 player->jx = player->last_jx = x;
771 player->jy = player->last_jy = y;
775 static void InitField(int x, int y, boolean init_game)
777 int element = Feld[x][y];
786 InitPlayerField(x, y, element, init_game);
789 case EL_SOKOBAN_FIELD_PLAYER:
790 element = Feld[x][y] = EL_PLAYER_1;
791 InitField(x, y, init_game);
793 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
794 InitField(x, y, init_game);
797 case EL_SOKOBAN_FIELD_EMPTY:
798 local_player->sokobanfields_still_needed++;
802 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
803 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
804 else if (x > 0 && Feld[x-1][y] == EL_ACID)
805 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
806 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
807 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
808 else if (y > 0 && Feld[x][y-1] == EL_ACID)
809 Feld[x][y] = EL_ACID_POOL_BOTTOM;
810 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
811 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
819 case EL_SPACESHIP_RIGHT:
820 case EL_SPACESHIP_UP:
821 case EL_SPACESHIP_LEFT:
822 case EL_SPACESHIP_DOWN:
824 case EL_BD_BUTTERFLY_RIGHT:
825 case EL_BD_BUTTERFLY_UP:
826 case EL_BD_BUTTERFLY_LEFT:
827 case EL_BD_BUTTERFLY_DOWN:
828 case EL_BD_BUTTERFLY:
829 case EL_BD_FIREFLY_RIGHT:
830 case EL_BD_FIREFLY_UP:
831 case EL_BD_FIREFLY_LEFT:
832 case EL_BD_FIREFLY_DOWN:
834 case EL_PACMAN_RIGHT:
858 if (y == lev_fieldy - 1)
860 Feld[x][y] = EL_AMOEBA_GROWING;
861 Store[x][y] = EL_AMOEBA_WET;
865 case EL_DYNAMITE_ACTIVE:
866 case EL_SP_DISK_RED_ACTIVE:
867 case EL_DYNABOMB_PLAYER_1_ACTIVE:
868 case EL_DYNABOMB_PLAYER_2_ACTIVE:
869 case EL_DYNABOMB_PLAYER_3_ACTIVE:
870 case EL_DYNABOMB_PLAYER_4_ACTIVE:
875 local_player->lights_still_needed++;
879 local_player->friends_still_needed++;
884 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
889 Feld[x][y] = EL_EMPTY;
894 case EL_EM_KEY_1_FILE:
895 Feld[x][y] = EL_EM_KEY_1;
897 case EL_EM_KEY_2_FILE:
898 Feld[x][y] = EL_EM_KEY_2;
900 case EL_EM_KEY_3_FILE:
901 Feld[x][y] = EL_EM_KEY_3;
903 case EL_EM_KEY_4_FILE:
904 Feld[x][y] = EL_EM_KEY_4;
908 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
909 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
910 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
911 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
912 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
913 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
914 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
915 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
916 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
917 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
918 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
919 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
922 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
923 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
924 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
926 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
928 game.belt_dir[belt_nr] = belt_dir;
929 game.belt_dir_nr[belt_nr] = belt_dir_nr;
931 else /* more than one switch -- set it like the first switch */
933 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
938 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
940 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
943 case EL_LIGHT_SWITCH_ACTIVE:
945 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
949 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
951 else if (IS_GROUP_ELEMENT(element))
953 struct ElementGroupInfo *group = element_info[element].group;
954 int last_anim_random_frame = gfx.anim_random_frame;
957 if (group->choice_mode == ANIM_RANDOM)
958 gfx.anim_random_frame = RND(group->num_elements_resolved);
960 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
961 group->choice_mode, 0,
964 if (group->choice_mode == ANIM_RANDOM)
965 gfx.anim_random_frame = last_anim_random_frame;
969 Feld[x][y] = group->element_resolved[element_pos];
971 InitField(x, y, init_game);
977 static inline void InitField_WithBug1(int x, int y, boolean init_game)
979 InitField(x, y, init_game);
981 /* not needed to call InitMovDir() -- already done by InitField()! */
982 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
983 CAN_MOVE(Feld[x][y]))
987 static inline void InitField_WithBug2(int x, int y, boolean init_game)
989 int old_element = Feld[x][y];
991 InitField(x, y, init_game);
993 /* not needed to call InitMovDir() -- already done by InitField()! */
994 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
995 CAN_MOVE(old_element) &&
996 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
999 /* this case is in fact a combination of not less than three bugs:
1000 first, it calls InitMovDir() for elements that can move, although this is
1001 already done by InitField(); then, it checks the element that was at this
1002 field _before_ the call to InitField() (which can change it); lastly, it
1003 was not called for "mole with direction" elements, which were treated as
1004 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1008 inline void DrawGameValue_Emeralds(int value)
1010 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1013 inline void DrawGameValue_Dynamite(int value)
1015 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1018 inline void DrawGameValue_Keys(struct PlayerInfo *player)
1022 for (i = 0; i < MAX_KEYS; i++)
1024 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1025 el2edimg(EL_KEY_1 + i));
1028 inline void DrawGameValue_Score(int value)
1030 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1033 inline void DrawGameValue_Time(int value)
1036 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1038 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1041 inline void DrawGameValue_Level(int value)
1044 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1047 /* misuse area for displaying emeralds to draw bigger level number */
1048 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1049 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1051 /* now copy it to the area for displaying level number */
1052 BlitBitmap(drawto, drawto,
1053 DX_EMERALDS, DY_EMERALDS + 1,
1054 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1055 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1056 DX_LEVEL - 1, DY_LEVEL + 1);
1058 /* restore the area for displaying emeralds */
1059 DrawGameValue_Emeralds(local_player->gems_still_needed);
1061 /* yes, this is all really ugly :-) */
1065 void DrawGameDoorValues()
1069 DrawGameValue_Level(level_nr);
1071 for (i = 0; i < MAX_PLAYERS; i++)
1072 DrawGameValue_Keys(&stored_player[i]);
1074 DrawGameValue_Emeralds(local_player->gems_still_needed);
1075 DrawGameValue_Dynamite(local_player->inventory_size);
1076 DrawGameValue_Score(local_player->score);
1077 DrawGameValue_Time(TimeLeft);
1080 static void resolve_group_element(int group_element, int recursion_depth)
1082 static int group_nr;
1083 static struct ElementGroupInfo *group;
1084 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1087 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1089 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1090 group_element - EL_GROUP_START + 1);
1092 /* replace element which caused too deep recursion by question mark */
1093 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1098 if (recursion_depth == 0) /* initialization */
1100 group = element_info[group_element].group;
1101 group_nr = group_element - EL_GROUP_START;
1103 group->num_elements_resolved = 0;
1104 group->choice_pos = 0;
1107 for (i = 0; i < actual_group->num_elements; i++)
1109 int element = actual_group->element[i];
1111 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1114 if (IS_GROUP_ELEMENT(element))
1115 resolve_group_element(element, recursion_depth + 1);
1118 group->element_resolved[group->num_elements_resolved++] = element;
1119 element_info[element].in_group[group_nr] = TRUE;
1124 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1126 printf("::: group %d: %d resolved elements\n",
1127 group_element - EL_GROUP_START, group->num_elements_resolved);
1128 for (i = 0; i < group->num_elements_resolved; i++)
1129 printf("::: - %d ['%s']\n", group->element_resolved[i],
1130 element_info[group->element_resolved[i]].token_name);
1137 =============================================================================
1139 -----------------------------------------------------------------------------
1140 initialize game engine due to level / tape version number
1141 =============================================================================
1144 static void InitGameEngine()
1148 /* set game engine from tape file when re-playing, else from level file */
1149 game.engine_version = (tape.playing ? tape.engine_version :
1150 level.game_version);
1152 /* dynamically adjust element properties according to game engine version */
1153 InitElementPropertiesEngine(game.engine_version);
1156 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1157 printf(" tape version == %06d [%s] [file: %06d]\n",
1158 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1160 printf(" => game.engine_version == %06d\n", game.engine_version);
1163 /* ---------- recursively resolve group elements ------------------------- */
1165 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1166 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1167 element_info[i].in_group[j] = FALSE;
1169 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1170 resolve_group_element(EL_GROUP_START + i, 0);
1172 /* ---------- initialize player's initial move delay --------------------- */
1174 /* dynamically adjust player properties according to game engine version */
1175 game.initial_move_delay =
1176 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1177 INITIAL_MOVE_DELAY_OFF);
1179 /* dynamically adjust player properties according to level information */
1180 game.initial_move_delay_value =
1181 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1183 /* ---------- initialize player's initial push delay --------------------- */
1185 /* dynamically adjust player properties according to game engine version */
1186 game.initial_push_delay_value =
1187 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1189 /* ---------- initialize changing elements ------------------------------- */
1191 /* initialize changing elements information */
1192 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1194 struct ElementInfo *ei = &element_info[i];
1196 /* this pointer might have been changed in the level editor */
1197 ei->change = &ei->change_page[0];
1199 if (!IS_CUSTOM_ELEMENT(i))
1201 ei->change->target_element = EL_EMPTY_SPACE;
1202 ei->change->delay_fixed = 0;
1203 ei->change->delay_random = 0;
1204 ei->change->delay_frames = 1;
1207 ei->change_events = CE_BITMASK_DEFAULT;
1208 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1210 ei->event_page_nr[j] = 0;
1211 ei->event_page[j] = &ei->change_page[0];
1215 /* add changing elements from pre-defined list */
1216 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1218 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1219 struct ElementInfo *ei = &element_info[ch_delay->element];
1221 ei->change->target_element = ch_delay->target_element;
1222 ei->change->delay_fixed = ch_delay->change_delay;
1224 ei->change->pre_change_function = ch_delay->pre_change_function;
1225 ei->change->change_function = ch_delay->change_function;
1226 ei->change->post_change_function = ch_delay->post_change_function;
1228 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1231 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1236 /* add change events from custom element configuration */
1237 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1239 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1241 for (j = 0; j < ei->num_change_pages; j++)
1243 if (!ei->change_page[j].can_change)
1246 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1248 /* only add event page for the first page found with this event */
1249 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1250 !(ei->change_events & CH_EVENT_BIT(k)))
1252 ei->change_events |= CH_EVENT_BIT(k);
1253 ei->event_page_nr[k] = j;
1254 ei->event_page[k] = &ei->change_page[j];
1262 /* add change events from custom element configuration */
1263 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1265 int element = EL_CUSTOM_START + i;
1267 /* only add custom elements that change after fixed/random frame delay */
1268 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1269 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1273 /* ---------- initialize run-time trigger player and element ------------- */
1275 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1277 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1279 for (j = 0; j < ei->num_change_pages; j++)
1281 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1282 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1286 /* ---------- initialize trigger events ---------------------------------- */
1288 /* initialize trigger events information */
1289 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1290 trigger_events[i] = EP_BITMASK_DEFAULT;
1293 /* add trigger events from element change event properties */
1294 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1296 struct ElementInfo *ei = &element_info[i];
1298 for (j = 0; j < ei->num_change_pages; j++)
1300 if (!ei->change_page[j].can_change)
1303 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1305 int trigger_element = ei->change_page[j].trigger_element;
1307 if (IS_GROUP_ELEMENT(trigger_element))
1309 struct ElementGroupInfo *group = element_info[trigger_element].group;
1311 for (k = 0; k < group->num_elements_resolved; k++)
1312 trigger_events[group->element_resolved[k]]
1313 |= ei->change_page[j].events;
1316 trigger_events[trigger_element] |= ei->change_page[j].events;
1321 /* add trigger events from element change event properties */
1322 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1323 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1324 trigger_events[element_info[i].change->trigger_element] |=
1325 element_info[i].change->events;
1328 /* ---------- initialize push delay -------------------------------------- */
1330 /* initialize push delay values to default */
1331 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1333 if (!IS_CUSTOM_ELEMENT(i))
1335 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1336 element_info[i].push_delay_random = game.default_push_delay_random;
1340 /* set push delay value for certain elements from pre-defined list */
1341 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1343 int e = push_delay_list[i].element;
1345 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1346 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1349 /* set push delay value for Supaplex elements for newer engine versions */
1350 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1352 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1354 if (IS_SP_ELEMENT(i))
1356 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1357 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1362 /* ---------- initialize move stepsize ----------------------------------- */
1364 /* initialize move stepsize values to default */
1365 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1366 if (!IS_CUSTOM_ELEMENT(i))
1367 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1369 /* set move stepsize value for certain elements from pre-defined list */
1370 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1372 int e = move_stepsize_list[i].element;
1374 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1378 /* ---------- initialize move dig/leave ---------------------------------- */
1380 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1382 element_info[i].can_leave_element = FALSE;
1383 element_info[i].can_leave_element_last = FALSE;
1387 /* ---------- initialize gem count --------------------------------------- */
1389 /* initialize gem count values for each element */
1390 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1391 if (!IS_CUSTOM_ELEMENT(i))
1392 element_info[i].collect_count = 0;
1394 /* add gem count values for all elements from pre-defined list */
1395 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1396 element_info[collect_count_list[i].element].collect_count =
1397 collect_count_list[i].count;
1399 /* ---------- initialize access direction -------------------------------- */
1401 /* initialize access direction values to default (access from every side) */
1402 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1403 if (!IS_CUSTOM_ELEMENT(i))
1404 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1406 /* set access direction value for certain elements from pre-defined list */
1407 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1408 element_info[access_direction_list[i].element].access_direction =
1409 access_direction_list[i].direction;
1414 =============================================================================
1416 -----------------------------------------------------------------------------
1417 initialize and start new game
1418 =============================================================================
1423 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1424 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1425 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1432 #if USE_NEW_AMOEBA_CODE
1433 printf("Using new amoeba code.\n");
1435 printf("Using old amoeba code.\n");
1440 /* don't play tapes over network */
1441 network_playing = (options.network && !tape.playing);
1443 for (i = 0; i < MAX_PLAYERS; i++)
1445 struct PlayerInfo *player = &stored_player[i];
1447 player->index_nr = i;
1448 player->index_bit = (1 << i);
1449 player->element_nr = EL_PLAYER_1 + i;
1451 player->present = FALSE;
1452 player->active = FALSE;
1455 player->effective_action = 0;
1456 player->programmed_action = 0;
1459 player->gems_still_needed = level.gems_needed;
1460 player->sokobanfields_still_needed = 0;
1461 player->lights_still_needed = 0;
1462 player->friends_still_needed = 0;
1464 for (j = 0; j < MAX_KEYS; j++)
1465 player->key[j] = FALSE;
1467 player->dynabomb_count = 0;
1468 player->dynabomb_size = 1;
1469 player->dynabombs_left = 0;
1470 player->dynabomb_xl = FALSE;
1472 player->MovDir = MV_NO_MOVING;
1475 player->GfxDir = MV_NO_MOVING;
1476 player->GfxAction = ACTION_DEFAULT;
1478 player->StepFrame = 0;
1480 player->use_murphy_graphic = FALSE;
1482 player->block_last_field = FALSE;
1483 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1485 player->actual_frame_counter = 0;
1487 player->step_counter = 0;
1489 player->last_move_dir = MV_NO_MOVING;
1491 player->is_waiting = FALSE;
1492 player->is_moving = FALSE;
1493 player->is_auto_moving = FALSE;
1494 player->is_digging = FALSE;
1495 player->is_snapping = FALSE;
1496 player->is_collecting = FALSE;
1497 player->is_pushing = FALSE;
1498 player->is_switching = FALSE;
1499 player->is_dropping = FALSE;
1501 player->is_bored = FALSE;
1502 player->is_sleeping = FALSE;
1504 player->frame_counter_bored = -1;
1505 player->frame_counter_sleeping = -1;
1507 player->anim_delay_counter = 0;
1508 player->post_delay_counter = 0;
1510 player->action_waiting = ACTION_DEFAULT;
1511 player->last_action_waiting = ACTION_DEFAULT;
1512 player->special_action_bored = ACTION_DEFAULT;
1513 player->special_action_sleeping = ACTION_DEFAULT;
1515 player->num_special_action_bored = 0;
1516 player->num_special_action_sleeping = 0;
1518 /* determine number of special actions for bored and sleeping animation */
1519 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1521 boolean found = FALSE;
1523 for (k = 0; k < NUM_DIRECTIONS; k++)
1524 if (el_act_dir2img(player->element_nr, j, k) !=
1525 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1529 player->num_special_action_bored++;
1533 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1535 boolean found = FALSE;
1537 for (k = 0; k < NUM_DIRECTIONS; k++)
1538 if (el_act_dir2img(player->element_nr, j, k) !=
1539 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1543 player->num_special_action_sleeping++;
1548 player->switch_x = -1;
1549 player->switch_y = -1;
1551 player->show_envelope = 0;
1553 player->move_delay = game.initial_move_delay;
1554 player->move_delay_value = game.initial_move_delay_value;
1556 player->move_delay_reset_counter = 0;
1558 player->push_delay = 0;
1559 player->push_delay_value = game.initial_push_delay_value;
1561 player->drop_delay = 0;
1563 player->last_jx = player->last_jy = 0;
1564 player->jx = player->jy = 0;
1566 player->shield_normal_time_left = 0;
1567 player->shield_deadly_time_left = 0;
1569 player->inventory_infinite_element = EL_UNDEFINED;
1570 player->inventory_size = 0;
1572 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1573 SnapField(player, 0, 0);
1575 player->LevelSolved = FALSE;
1576 player->GameOver = FALSE;
1579 network_player_action_received = FALSE;
1581 #if defined(PLATFORM_UNIX)
1582 /* initial null action */
1583 if (network_playing)
1584 SendToServer_MovePlayer(MV_NO_MOVING);
1592 TimeLeft = level.time;
1595 ScreenMovDir = MV_NO_MOVING;
1599 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1601 AllPlayersGone = FALSE;
1603 game.yamyam_content_nr = 0;
1604 game.magic_wall_active = FALSE;
1605 game.magic_wall_time_left = 0;
1606 game.light_time_left = 0;
1607 game.timegate_time_left = 0;
1608 game.switchgate_pos = 0;
1609 game.balloon_dir = MV_NO_MOVING;
1610 game.gravity = level.initial_gravity;
1611 game.explosions_delayed = TRUE;
1613 game.envelope_active = FALSE;
1615 for (i = 0; i < NUM_BELTS; i++)
1617 game.belt_dir[i] = MV_NO_MOVING;
1618 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1621 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1622 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1624 for (x = 0; x < lev_fieldx; x++)
1626 for (y = 0; y < lev_fieldy; y++)
1628 Feld[x][y] = level.field[x][y];
1629 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1630 ChangeDelay[x][y] = 0;
1631 ChangePage[x][y] = -1;
1632 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1634 WasJustMoving[x][y] = 0;
1635 WasJustFalling[x][y] = 0;
1637 Pushed[x][y] = FALSE;
1639 Changed[x][y] = CE_BITMASK_DEFAULT;
1640 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1642 ExplodePhase[x][y] = 0;
1643 ExplodeDelay[x][y] = 0;
1644 ExplodeField[x][y] = EX_TYPE_NONE;
1646 RunnerVisit[x][y] = 0;
1647 PlayerVisit[x][y] = 0;
1650 GfxRandom[x][y] = INIT_GFX_RANDOM();
1651 GfxElement[x][y] = EL_UNDEFINED;
1652 GfxAction[x][y] = ACTION_DEFAULT;
1653 GfxDir[x][y] = MV_NO_MOVING;
1657 for (y = 0; y < lev_fieldy; y++)
1659 for (x = 0; x < lev_fieldx; x++)
1661 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1663 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1665 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1668 InitField(x, y, TRUE);
1674 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1675 emulate_sb ? EMU_SOKOBAN :
1676 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1678 /* initialize explosion and ignition delay */
1679 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1681 if (!IS_CUSTOM_ELEMENT(i))
1684 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1685 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1686 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1687 int last_phase = (num_phase + 1) * delay;
1688 int half_phase = (num_phase / 2) * delay;
1690 element_info[i].explosion_delay = last_phase - 1;
1691 element_info[i].ignition_delay = half_phase;
1694 if (i == EL_BLACK_ORB)
1695 element_info[i].ignition_delay = 0;
1697 if (i == EL_BLACK_ORB)
1698 element_info[i].ignition_delay = 1;
1703 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1704 element_info[i].explosion_delay = 1;
1706 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1707 element_info[i].ignition_delay = 1;
1711 /* correct non-moving belts to start moving left */
1712 for (i = 0; i < NUM_BELTS; i++)
1713 if (game.belt_dir[i] == MV_NO_MOVING)
1714 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1716 /* check if any connected player was not found in playfield */
1717 for (i = 0; i < MAX_PLAYERS; i++)
1719 struct PlayerInfo *player = &stored_player[i];
1721 if (player->connected && !player->present)
1723 for (j = 0; j < MAX_PLAYERS; j++)
1725 struct PlayerInfo *some_player = &stored_player[j];
1726 int jx = some_player->jx, jy = some_player->jy;
1728 /* assign first free player found that is present in the playfield */
1729 if (some_player->present && !some_player->connected)
1731 player->present = TRUE;
1732 player->active = TRUE;
1734 some_player->present = FALSE;
1735 some_player->active = FALSE;
1738 player->element_nr = some_player->element_nr;
1741 StorePlayer[jx][jy] = player->element_nr;
1742 player->jx = player->last_jx = jx;
1743 player->jy = player->last_jy = jy;
1753 /* when playing a tape, eliminate all players which do not participate */
1755 for (i = 0; i < MAX_PLAYERS; i++)
1757 if (stored_player[i].active && !tape.player_participates[i])
1759 struct PlayerInfo *player = &stored_player[i];
1760 int jx = player->jx, jy = player->jy;
1762 player->active = FALSE;
1763 StorePlayer[jx][jy] = 0;
1764 Feld[jx][jy] = EL_EMPTY;
1768 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1770 /* when in single player mode, eliminate all but the first active player */
1772 for (i = 0; i < MAX_PLAYERS; i++)
1774 if (stored_player[i].active)
1776 for (j = i + 1; j < MAX_PLAYERS; j++)
1778 if (stored_player[j].active)
1780 struct PlayerInfo *player = &stored_player[j];
1781 int jx = player->jx, jy = player->jy;
1783 player->active = FALSE;
1784 player->present = FALSE;
1786 StorePlayer[jx][jy] = 0;
1787 Feld[jx][jy] = EL_EMPTY;
1794 /* when recording the game, store which players take part in the game */
1797 for (i = 0; i < MAX_PLAYERS; i++)
1798 if (stored_player[i].active)
1799 tape.player_participates[i] = TRUE;
1804 for (i = 0; i < MAX_PLAYERS; i++)
1806 struct PlayerInfo *player = &stored_player[i];
1808 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1813 if (local_player == player)
1814 printf("Player %d is local player.\n", i+1);
1818 if (BorderElement == EL_EMPTY)
1821 SBX_Right = lev_fieldx - SCR_FIELDX;
1823 SBY_Lower = lev_fieldy - SCR_FIELDY;
1828 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1830 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1833 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1834 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1836 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1837 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1839 /* if local player not found, look for custom element that might create
1840 the player (make some assumptions about the right custom element) */
1841 if (!local_player->present)
1843 int start_x = 0, start_y = 0;
1844 int found_rating = 0;
1845 int found_element = EL_UNDEFINED;
1847 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1849 int element = Feld[x][y];
1854 if (!IS_CUSTOM_ELEMENT(element))
1857 if (CAN_CHANGE(element))
1859 for (i = 0; i < element_info[element].num_change_pages; i++)
1861 content = element_info[element].change_page[i].target_element;
1862 is_player = ELEM_IS_PLAYER(content);
1864 if (is_player && (found_rating < 3 || element < found_element))
1870 found_element = element;
1875 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1877 content = element_info[element].content[xx][yy];
1878 is_player = ELEM_IS_PLAYER(content);
1880 if (is_player && (found_rating < 2 || element < found_element))
1882 start_x = x + xx - 1;
1883 start_y = y + yy - 1;
1886 found_element = element;
1889 if (!CAN_CHANGE(element))
1892 for (i = 0; i < element_info[element].num_change_pages; i++)
1894 content= element_info[element].change_page[i].target_content[xx][yy];
1895 is_player = ELEM_IS_PLAYER(content);
1897 if (is_player && (found_rating < 1 || element < found_element))
1899 start_x = x + xx - 1;
1900 start_y = y + yy - 1;
1903 found_element = element;
1909 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1910 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1913 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1914 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1920 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1921 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1922 local_player->jx - MIDPOSX);
1924 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1925 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1926 local_player->jy - MIDPOSY);
1928 scroll_x = SBX_Left;
1929 scroll_y = SBY_Upper;
1930 if (local_player->jx >= SBX_Left + MIDPOSX)
1931 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1932 local_player->jx - MIDPOSX :
1934 if (local_player->jy >= SBY_Upper + MIDPOSY)
1935 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1936 local_player->jy - MIDPOSY :
1941 CloseDoor(DOOR_CLOSE_1);
1946 /* after drawing the level, correct some elements */
1947 if (game.timegate_time_left == 0)
1948 CloseAllOpenTimegates();
1950 if (setup.soft_scrolling)
1951 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1953 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1956 /* copy default game door content to main double buffer */
1957 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1958 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1960 DrawGameDoorValues();
1964 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1965 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1966 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1970 /* copy actual game door content to door double buffer for OpenDoor() */
1971 BlitBitmap(drawto, bitmap_db_door,
1972 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1974 OpenDoor(DOOR_OPEN_ALL);
1976 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1978 if (setup.sound_music)
1981 KeyboardAutoRepeatOffUnlessAutoplay();
1985 for (i = 0; i < MAX_PLAYERS; i++)
1986 printf("Player %d %sactive.\n",
1987 i + 1, (stored_player[i].active ? "" : "not "));
1991 printf("::: starting game [%d]\n", FrameCounter);
1995 void InitMovDir(int x, int y)
1997 int i, element = Feld[x][y];
1998 static int xy[4][2] =
2005 static int direction[3][4] =
2007 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2008 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2009 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2018 Feld[x][y] = EL_BUG;
2019 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2022 case EL_SPACESHIP_RIGHT:
2023 case EL_SPACESHIP_UP:
2024 case EL_SPACESHIP_LEFT:
2025 case EL_SPACESHIP_DOWN:
2026 Feld[x][y] = EL_SPACESHIP;
2027 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2030 case EL_BD_BUTTERFLY_RIGHT:
2031 case EL_BD_BUTTERFLY_UP:
2032 case EL_BD_BUTTERFLY_LEFT:
2033 case EL_BD_BUTTERFLY_DOWN:
2034 Feld[x][y] = EL_BD_BUTTERFLY;
2035 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2038 case EL_BD_FIREFLY_RIGHT:
2039 case EL_BD_FIREFLY_UP:
2040 case EL_BD_FIREFLY_LEFT:
2041 case EL_BD_FIREFLY_DOWN:
2042 Feld[x][y] = EL_BD_FIREFLY;
2043 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2046 case EL_PACMAN_RIGHT:
2048 case EL_PACMAN_LEFT:
2049 case EL_PACMAN_DOWN:
2050 Feld[x][y] = EL_PACMAN;
2051 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2054 case EL_SP_SNIKSNAK:
2055 MovDir[x][y] = MV_UP;
2058 case EL_SP_ELECTRON:
2059 MovDir[x][y] = MV_LEFT;
2066 Feld[x][y] = EL_MOLE;
2067 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2071 if (IS_CUSTOM_ELEMENT(element))
2073 struct ElementInfo *ei = &element_info[element];
2074 int move_direction_initial = ei->move_direction_initial;
2075 int move_pattern = ei->move_pattern;
2077 if (move_direction_initial == MV_START_PREVIOUS)
2079 if (MovDir[x][y] != MV_NO_MOVING)
2082 move_direction_initial = MV_START_AUTOMATIC;
2085 if (move_direction_initial == MV_START_RANDOM)
2086 MovDir[x][y] = 1 << RND(4);
2087 else if (move_direction_initial & MV_ANY_DIRECTION)
2088 MovDir[x][y] = move_direction_initial;
2089 else if (move_pattern == MV_ALL_DIRECTIONS ||
2090 move_pattern == MV_TURNING_LEFT ||
2091 move_pattern == MV_TURNING_RIGHT ||
2092 move_pattern == MV_TURNING_LEFT_RIGHT ||
2093 move_pattern == MV_TURNING_RIGHT_LEFT ||
2094 move_pattern == MV_TURNING_RANDOM)
2095 MovDir[x][y] = 1 << RND(4);
2096 else if (move_pattern == MV_HORIZONTAL)
2097 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2098 else if (move_pattern == MV_VERTICAL)
2099 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2100 else if (move_pattern & MV_ANY_DIRECTION)
2101 MovDir[x][y] = element_info[element].move_pattern;
2102 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2103 move_pattern == MV_ALONG_RIGHT_SIDE)
2106 /* use random direction as default start direction */
2107 if (game.engine_version >= VERSION_IDENT(3,1,0,2))
2108 MovDir[x][y] = 1 << RND(4);
2111 for (i = 0; i < NUM_DIRECTIONS; i++)
2113 int x1 = x + xy[i][0];
2114 int y1 = y + xy[i][1];
2116 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2118 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2119 MovDir[x][y] = direction[0][i];
2121 MovDir[x][y] = direction[1][i];
2130 MovDir[x][y] = 1 << RND(4);
2132 if (element != EL_BUG &&
2133 element != EL_SPACESHIP &&
2134 element != EL_BD_BUTTERFLY &&
2135 element != EL_BD_FIREFLY)
2138 for (i = 0; i < NUM_DIRECTIONS; i++)
2140 int x1 = x + xy[i][0];
2141 int y1 = y + xy[i][1];
2143 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2145 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2147 MovDir[x][y] = direction[0][i];
2150 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2151 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2153 MovDir[x][y] = direction[1][i];
2162 GfxDir[x][y] = MovDir[x][y];
2165 void InitAmoebaNr(int x, int y)
2168 int group_nr = AmoebeNachbarNr(x, y);
2172 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2174 if (AmoebaCnt[i] == 0)
2182 AmoebaNr[x][y] = group_nr;
2183 AmoebaCnt[group_nr]++;
2184 AmoebaCnt2[group_nr]++;
2190 boolean raise_level = FALSE;
2192 if (local_player->MovPos)
2196 if (tape.auto_play) /* tape might already be stopped here */
2197 tape.auto_play_level_solved = TRUE;
2199 if (tape.playing && tape.auto_play)
2200 tape.auto_play_level_solved = TRUE;
2203 local_player->LevelSolved = FALSE;
2205 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2209 if (!tape.playing && setup.sound_loops)
2210 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2211 SND_CTRL_PLAY_LOOP);
2213 while (TimeLeft > 0)
2215 if (!tape.playing && !setup.sound_loops)
2216 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2217 if (TimeLeft > 0 && !(TimeLeft % 10))
2218 RaiseScore(level.score[SC_TIME_BONUS]);
2219 if (TimeLeft > 100 && !(TimeLeft % 10))
2224 DrawGameValue_Time(TimeLeft);
2232 if (!tape.playing && setup.sound_loops)
2233 StopSound(SND_GAME_LEVELTIME_BONUS);
2235 else if (level.time == 0) /* level without time limit */
2237 if (!tape.playing && setup.sound_loops)
2238 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2239 SND_CTRL_PLAY_LOOP);
2241 while (TimePlayed < 999)
2243 if (!tape.playing && !setup.sound_loops)
2244 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2245 if (TimePlayed < 999 && !(TimePlayed % 10))
2246 RaiseScore(level.score[SC_TIME_BONUS]);
2247 if (TimePlayed < 900 && !(TimePlayed % 10))
2252 DrawGameValue_Time(TimePlayed);
2260 if (!tape.playing && setup.sound_loops)
2261 StopSound(SND_GAME_LEVELTIME_BONUS);
2264 /* close exit door after last player */
2265 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2266 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2268 int element = Feld[ExitX][ExitY];
2270 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2271 EL_SP_EXIT_CLOSING);
2273 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2276 /* Hero disappears */
2277 DrawLevelField(ExitX, ExitY);
2283 CloseDoor(DOOR_CLOSE_1);
2288 SaveTape(tape.level_nr); /* Ask to save tape */
2291 if (level_nr == leveldir_current->handicap_level)
2293 leveldir_current->handicap_level++;
2294 SaveLevelSetup_SeriesInfo();
2297 if (level_editor_test_game)
2298 local_player->score = -1; /* no highscore when playing from editor */
2299 else if (level_nr < leveldir_current->last_level)
2300 raise_level = TRUE; /* advance to next level */
2302 if ((hi_pos = NewHiScore()) >= 0)
2304 game_status = GAME_MODE_SCORES;
2305 DrawHallOfFame(hi_pos);
2314 game_status = GAME_MODE_MAIN;
2331 LoadScore(level_nr);
2333 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2334 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2337 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2339 if (local_player->score > highscore[k].Score)
2341 /* player has made it to the hall of fame */
2343 if (k < MAX_SCORE_ENTRIES - 1)
2345 int m = MAX_SCORE_ENTRIES - 1;
2348 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2349 if (!strcmp(setup.player_name, highscore[l].Name))
2351 if (m == k) /* player's new highscore overwrites his old one */
2355 for (l = m; l > k; l--)
2357 strcpy(highscore[l].Name, highscore[l - 1].Name);
2358 highscore[l].Score = highscore[l - 1].Score;
2365 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2366 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2367 highscore[k].Score = local_player->score;
2373 else if (!strncmp(setup.player_name, highscore[k].Name,
2374 MAX_PLAYER_NAME_LEN))
2375 break; /* player already there with a higher score */
2381 SaveScore(level_nr);
2386 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2388 if (player->GfxAction != action || player->GfxDir != dir)
2391 printf("Player frame reset! (%d => %d, %d => %d)\n",
2392 player->GfxAction, action, player->GfxDir, dir);
2395 player->GfxAction = action;
2396 player->GfxDir = dir;
2398 player->StepFrame = 0;
2402 static void ResetRandomAnimationValue(int x, int y)
2404 GfxRandom[x][y] = INIT_GFX_RANDOM();
2407 static void ResetGfxAnimation(int x, int y)
2410 GfxAction[x][y] = ACTION_DEFAULT;
2411 GfxDir[x][y] = MovDir[x][y];
2414 void InitMovingField(int x, int y, int direction)
2416 int element = Feld[x][y];
2417 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2418 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2422 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2423 ResetGfxAnimation(x, y);
2425 MovDir[newx][newy] = MovDir[x][y] = direction;
2426 GfxDir[x][y] = direction;
2428 if (Feld[newx][newy] == EL_EMPTY)
2429 Feld[newx][newy] = EL_BLOCKED;
2431 if (direction == MV_DOWN && CAN_FALL(element))
2432 GfxAction[x][y] = ACTION_FALLING;
2434 GfxAction[x][y] = ACTION_MOVING;
2436 GfxFrame[newx][newy] = GfxFrame[x][y];
2437 GfxRandom[newx][newy] = GfxRandom[x][y];
2438 GfxAction[newx][newy] = GfxAction[x][y];
2439 GfxDir[newx][newy] = GfxDir[x][y];
2442 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2444 int direction = MovDir[x][y];
2445 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2446 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2452 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2454 int oldx = x, oldy = y;
2455 int direction = MovDir[x][y];
2457 if (direction == MV_LEFT)
2459 else if (direction == MV_RIGHT)
2461 else if (direction == MV_UP)
2463 else if (direction == MV_DOWN)
2466 *comes_from_x = oldx;
2467 *comes_from_y = oldy;
2470 int MovingOrBlocked2Element(int x, int y)
2472 int element = Feld[x][y];
2474 if (element == EL_BLOCKED)
2478 Blocked2Moving(x, y, &oldx, &oldy);
2479 return Feld[oldx][oldy];
2485 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2487 /* like MovingOrBlocked2Element(), but if element is moving
2488 and (x,y) is the field the moving element is just leaving,
2489 return EL_BLOCKED instead of the element value */
2490 int element = Feld[x][y];
2492 if (IS_MOVING(x, y))
2494 if (element == EL_BLOCKED)
2498 Blocked2Moving(x, y, &oldx, &oldy);
2499 return Feld[oldx][oldy];
2508 static void RemoveField(int x, int y)
2510 Feld[x][y] = EL_EMPTY;
2517 ChangeDelay[x][y] = 0;
2518 ChangePage[x][y] = -1;
2519 Pushed[x][y] = FALSE;
2521 GfxElement[x][y] = EL_UNDEFINED;
2522 GfxAction[x][y] = ACTION_DEFAULT;
2523 GfxDir[x][y] = MV_NO_MOVING;
2526 void RemoveMovingField(int x, int y)
2528 int oldx = x, oldy = y, newx = x, newy = y;
2529 int element = Feld[x][y];
2530 int next_element = EL_UNDEFINED;
2532 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2535 if (IS_MOVING(x, y))
2537 Moving2Blocked(x, y, &newx, &newy);
2539 if (Feld[newx][newy] != EL_BLOCKED)
2542 if (Feld[newx][newy] != EL_BLOCKED)
2544 /* element is moving, but target field is not free (blocked), but
2545 already occupied by something different (example: acid pool);
2546 in this case, only remove the moving field, but not the target */
2548 RemoveField(oldx, oldy);
2550 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2552 DrawLevelField(oldx, oldy);
2558 else if (element == EL_BLOCKED)
2560 Blocked2Moving(x, y, &oldx, &oldy);
2561 if (!IS_MOVING(oldx, oldy))
2565 if (element == EL_BLOCKED &&
2566 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2567 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2568 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2569 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2570 next_element = get_next_element(Feld[oldx][oldy]);
2572 RemoveField(oldx, oldy);
2573 RemoveField(newx, newy);
2575 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2577 if (next_element != EL_UNDEFINED)
2578 Feld[oldx][oldy] = next_element;
2580 DrawLevelField(oldx, oldy);
2581 DrawLevelField(newx, newy);
2584 void DrawDynamite(int x, int y)
2586 int sx = SCREENX(x), sy = SCREENY(y);
2587 int graphic = el2img(Feld[x][y]);
2590 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2593 if (IS_WALKABLE_INSIDE(Back[x][y]))
2597 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2598 else if (Store[x][y])
2599 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2601 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2604 if (Back[x][y] || Store[x][y])
2605 DrawGraphicThruMask(sx, sy, graphic, frame);
2607 DrawGraphic(sx, sy, graphic, frame);
2609 if (game.emulation == EMU_SUPAPLEX)
2610 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2611 else if (Store[x][y])
2612 DrawGraphicThruMask(sx, sy, graphic, frame);
2614 DrawGraphic(sx, sy, graphic, frame);
2618 void CheckDynamite(int x, int y)
2620 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2624 if (MovDelay[x][y] != 0)
2627 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2634 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2636 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2637 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2638 StopSound(SND_DYNAMITE_ACTIVE);
2640 StopSound(SND_DYNABOMB_ACTIVE);
2646 void RelocatePlayer(int x, int y, int element_raw)
2648 int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
2649 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2650 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2651 boolean no_delay = (tape.warp_forward);
2652 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2653 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2656 if (player->GameOver) /* do not reanimate dead player */
2659 RemoveField(x, y); /* temporarily remove newly placed player */
2660 DrawLevelField(x, y);
2662 if (player->present)
2664 while (player->MovPos)
2666 ScrollPlayer(player, SCROLL_GO_ON);
2667 ScrollScreen(NULL, SCROLL_GO_ON);
2673 Delay(wait_delay_value);
2676 DrawPlayer(player); /* needed here only to cleanup last field */
2677 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2679 player->is_moving = FALSE;
2682 old_jx = player->jx;
2683 old_jy = player->jy;
2685 Feld[x][y] = element;
2686 InitPlayerField(x, y, element, TRUE);
2688 if (player != local_player) /* do not visually relocate other players */
2691 if (level.instant_relocation)
2694 int offset = (setup.scroll_delay ? 3 : 0);
2695 int jx = local_player->jx;
2696 int jy = local_player->jy;
2698 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2700 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2701 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2702 local_player->jx - MIDPOSX);
2704 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2705 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2706 local_player->jy - MIDPOSY);
2710 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2711 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2712 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2714 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2715 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2716 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2718 /* don't scroll over playfield boundaries */
2719 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2720 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2722 /* don't scroll over playfield boundaries */
2723 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2724 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2727 scroll_x += (local_player->jx - old_jx);
2728 scroll_y += (local_player->jy - old_jy);
2730 /* don't scroll over playfield boundaries */
2731 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2732 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2734 /* don't scroll over playfield boundaries */
2735 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2736 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2739 RedrawPlayfield(TRUE, 0,0,0,0);
2745 int offset = (setup.scroll_delay ? 3 : 0);
2746 int jx = local_player->jx;
2747 int jy = local_player->jy;
2749 int scroll_xx = -999, scroll_yy = -999;
2751 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2753 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2756 int fx = FX, fy = FY;
2758 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2759 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2760 local_player->jx - MIDPOSX);
2762 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2763 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2764 local_player->jy - MIDPOSY);
2766 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2767 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2770 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2773 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2780 fx += dx * TILEX / 2;
2781 fy += dy * TILEY / 2;
2783 ScrollLevel(dx, dy);
2786 /* scroll in two steps of half tile size to make things smoother */
2787 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2789 Delay(wait_delay_value);
2791 /* scroll second step to align at full tile size */
2793 Delay(wait_delay_value);
2796 int scroll_xx = -999, scroll_yy = -999;
2798 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2800 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2803 int fx = FX, fy = FY;
2805 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2806 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2807 local_player->jx - MIDPOSX);
2809 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2810 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2811 local_player->jy - MIDPOSY);
2813 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2814 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2817 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2820 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2827 fx += dx * TILEX / 2;
2828 fy += dy * TILEY / 2;
2830 ScrollLevel(dx, dy);
2833 /* scroll in two steps of half tile size to make things smoother */
2834 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2836 Delay(wait_delay_value);
2838 /* scroll second step to align at full tile size */
2840 Delay(wait_delay_value);
2846 void Explode(int ex, int ey, int phase, int mode)
2853 /* !!! eliminate this variable !!! */
2854 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2859 int last_phase = num_phase * delay;
2860 int half_phase = (num_phase / 2) * delay;
2861 int first_phase_after_start = EX_PHASE_START + 1;
2865 if (game.explosions_delayed)
2867 ExplodeField[ex][ey] = mode;
2871 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2873 int center_element = Feld[ex][ey];
2876 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
2880 /* --- This is only really needed (and now handled) in "Impact()". --- */
2881 /* do not explode moving elements that left the explode field in time */
2882 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2883 center_element == EL_EMPTY &&
2884 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
2888 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
2889 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2891 /* remove things displayed in background while burning dynamite */
2892 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2895 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2897 /* put moving element to center field (and let it explode there) */
2898 center_element = MovingOrBlocked2Element(ex, ey);
2899 RemoveMovingField(ex, ey);
2900 Feld[ex][ey] = center_element;
2906 last_phase = element_info[center_element].explosion_delay + 1;
2908 last_phase = element_info[center_element].explosion_delay;
2912 printf("::: %d -> %d\n", center_element, last_phase);
2916 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2918 int xx = x - ex + 1;
2919 int yy = y - ey + 1;
2924 if (!IN_LEV_FIELD(x, y) ||
2925 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
2926 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
2929 if (!IN_LEV_FIELD(x, y) ||
2930 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
2934 if (!IN_LEV_FIELD(x, y) ||
2935 ((mode != EX_TYPE_NORMAL ||
2936 center_element == EL_AMOEBA_TO_DIAMOND) &&
2937 (x != ex || y != ey)))
2941 element = Feld[x][y];
2943 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2945 element = MovingOrBlocked2Element(x, y);
2947 if (!IS_EXPLOSION_PROOF(element))
2948 RemoveMovingField(x, y);
2954 if (IS_EXPLOSION_PROOF(element))
2957 /* indestructible elements can only explode in center (but not flames) */
2958 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2959 element == EL_FLAMES)
2964 if ((IS_INDESTRUCTIBLE(element) &&
2965 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2966 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2967 element == EL_FLAMES)
2971 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2973 if (IS_ACTIVE_BOMB(element))
2975 /* re-activate things under the bomb like gate or penguin */
2976 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2983 /* save walkable background elements while explosion on same tile */
2985 if (IS_INDESTRUCTIBLE(element))
2986 Back[x][y] = element;
2988 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2989 Back[x][y] = element;
2992 /* ignite explodable elements reached by other explosion */
2993 if (element == EL_EXPLOSION)
2994 element = Store2[x][y];
2997 if (AmoebaNr[x][y] &&
2998 (element == EL_AMOEBA_FULL ||
2999 element == EL_BD_AMOEBA ||
3000 element == EL_AMOEBA_GROWING))
3002 AmoebaCnt[AmoebaNr[x][y]]--;
3003 AmoebaCnt2[AmoebaNr[x][y]]--;
3009 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3011 switch(StorePlayer[ex][ey])
3014 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3017 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3020 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3024 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3029 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3030 Store[x][y] = EL_EMPTY;
3032 if (game.emulation == EMU_SUPAPLEX)
3033 Store[x][y] = EL_EMPTY;
3036 else if (center_element == EL_MOLE)
3037 Store[x][y] = EL_EMERALD_RED;
3038 else if (center_element == EL_PENGUIN)
3039 Store[x][y] = EL_EMERALD_PURPLE;
3040 else if (center_element == EL_BUG)
3041 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3042 else if (center_element == EL_BD_BUTTERFLY)
3043 Store[x][y] = EL_BD_DIAMOND;
3044 else if (center_element == EL_SP_ELECTRON)
3045 Store[x][y] = EL_SP_INFOTRON;
3046 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3047 Store[x][y] = level.amoeba_content;
3048 else if (center_element == EL_YAMYAM)
3049 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3050 else if (IS_CUSTOM_ELEMENT(center_element) &&
3051 element_info[center_element].content[xx][yy] != EL_EMPTY)
3052 Store[x][y] = element_info[center_element].content[xx][yy];
3053 else if (element == EL_WALL_EMERALD)
3054 Store[x][y] = EL_EMERALD;
3055 else if (element == EL_WALL_DIAMOND)
3056 Store[x][y] = EL_DIAMOND;
3057 else if (element == EL_WALL_BD_DIAMOND)
3058 Store[x][y] = EL_BD_DIAMOND;
3059 else if (element == EL_WALL_EMERALD_YELLOW)
3060 Store[x][y] = EL_EMERALD_YELLOW;
3061 else if (element == EL_WALL_EMERALD_RED)
3062 Store[x][y] = EL_EMERALD_RED;
3063 else if (element == EL_WALL_EMERALD_PURPLE)
3064 Store[x][y] = EL_EMERALD_PURPLE;
3065 else if (element == EL_WALL_PEARL)
3066 Store[x][y] = EL_PEARL;
3067 else if (element == EL_WALL_CRYSTAL)
3068 Store[x][y] = EL_CRYSTAL;
3069 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3070 Store[x][y] = element_info[element].content[1][1];
3072 Store[x][y] = EL_EMPTY;
3074 if (x != ex || y != ey ||
3075 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_TYPE_BORDER)
3076 Store2[x][y] = element;
3079 if (AmoebaNr[x][y] &&
3080 (element == EL_AMOEBA_FULL ||
3081 element == EL_BD_AMOEBA ||
3082 element == EL_AMOEBA_GROWING))
3084 AmoebaCnt[AmoebaNr[x][y]]--;
3085 AmoebaCnt2[AmoebaNr[x][y]]--;
3091 MovDir[x][y] = MovPos[x][y] = 0;
3092 GfxDir[x][y] = MovDir[x][y];
3097 Feld[x][y] = EL_EXPLOSION;
3099 GfxElement[x][y] = center_element;
3101 GfxElement[x][y] = EL_UNDEFINED;
3104 ExplodePhase[x][y] = 1;
3106 ExplodeDelay[x][y] = last_phase;
3111 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3113 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3120 if (center_element == EL_YAMYAM)
3121 game.yamyam_content_nr =
3122 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3135 GfxFrame[x][y] = 0; /* restart explosion animation */
3139 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3143 last_phase = ExplodeDelay[x][y];
3146 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3150 /* activate this even in non-DEBUG version until cause for crash in
3151 getGraphicAnimationFrame() (see below) is found and eliminated */
3155 if (GfxElement[x][y] == EL_UNDEFINED)
3158 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3159 printf("Explode(): This should never happen!\n");
3162 GfxElement[x][y] = EL_EMPTY;
3168 border_element = Store2[x][y];
3169 if (IS_PLAYER(x, y))
3170 border_element = StorePlayer[x][y];
3173 printf("::: phase == %d\n", phase);
3176 if (phase == element_info[border_element].ignition_delay ||
3177 phase == last_phase)
3179 boolean border_explosion = FALSE;
3182 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3184 if (IS_PLAYER(x, y))
3187 KillHeroUnlessExplosionProtected(x, y);
3188 border_explosion = TRUE;
3191 if (phase == last_phase)
3192 printf("::: IS_PLAYER\n");
3195 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3197 Feld[x][y] = Store2[x][y];
3200 border_explosion = TRUE;
3203 if (phase == last_phase)
3204 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3207 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3209 AmoebeUmwandeln(x, y);
3211 border_explosion = TRUE;
3214 if (phase == last_phase)
3215 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3216 element_info[border_element].explosion_delay,
3217 element_info[border_element].ignition_delay,
3223 /* if an element just explodes due to another explosion (chain-reaction),
3224 do not immediately end the new explosion when it was the last frame of
3225 the explosion (as it would be done in the following "if"-statement!) */
3226 if (border_explosion && phase == last_phase)
3233 if (phase == first_phase_after_start)
3235 int element = Store2[x][y];
3237 if (element == EL_BLACK_ORB)
3239 Feld[x][y] = Store2[x][y];
3244 else if (phase == half_phase)
3246 int element = Store2[x][y];
3248 if (IS_PLAYER(x, y))
3249 KillHeroUnlessExplosionProtected(x, y);
3250 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3252 Feld[x][y] = Store2[x][y];
3256 else if (element == EL_AMOEBA_TO_DIAMOND)
3257 AmoebeUmwandeln(x, y);
3261 if (phase == last_phase)
3266 printf("::: done: phase == %d\n", phase);
3270 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3273 element = Feld[x][y] = Store[x][y];
3274 Store[x][y] = Store2[x][y] = 0;
3275 GfxElement[x][y] = EL_UNDEFINED;
3277 /* player can escape from explosions and might therefore be still alive */
3278 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3279 element <= EL_PLAYER_IS_EXPLODING_4)
3280 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3282 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3283 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3284 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3287 /* restore probably existing indestructible background element */
3288 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3289 element = Feld[x][y] = Back[x][y];
3292 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3293 GfxDir[x][y] = MV_NO_MOVING;
3294 ChangeDelay[x][y] = 0;
3295 ChangePage[x][y] = -1;
3298 InitField_WithBug2(x, y, FALSE);
3300 InitField(x, y, FALSE);
3302 /* !!! not needed !!! */
3304 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3305 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3308 if (CAN_MOVE(element))
3313 DrawLevelField(x, y);
3315 TestIfElementTouchesCustomElement(x, y);
3317 if (GFX_CRUMBLED(element))
3318 DrawLevelFieldCrumbledSandNeighbours(x, y);
3320 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3321 StorePlayer[x][y] = 0;
3323 if (ELEM_IS_PLAYER(element))
3324 RelocatePlayer(x, y, element);
3327 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3329 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3333 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3335 int stored = Store[x][y];
3336 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3337 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3341 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3343 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3347 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3351 printf("::: %d / %d [%d - %d]\n",
3352 GfxFrame[x][y], phase - delay, phase, delay);
3356 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3357 element_info[GfxElement[x][y]].token_name,
3362 DrawLevelFieldCrumbledSand(x, y);
3364 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3366 DrawLevelElement(x, y, Back[x][y]);
3367 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3369 else if (IS_WALKABLE_UNDER(Back[x][y]))
3371 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3372 DrawLevelElementThruMask(x, y, Back[x][y]);
3374 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3375 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3379 void DynaExplode(int ex, int ey)
3382 int dynabomb_element = Feld[ex][ey];
3383 int dynabomb_size = 1;
3384 boolean dynabomb_xl = FALSE;
3385 struct PlayerInfo *player;
3386 static int xy[4][2] =
3394 if (IS_ACTIVE_BOMB(dynabomb_element))
3396 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3397 dynabomb_size = player->dynabomb_size;
3398 dynabomb_xl = player->dynabomb_xl;
3399 player->dynabombs_left++;
3402 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3404 for (i = 0; i < NUM_DIRECTIONS; i++)
3406 for (j = 1; j <= dynabomb_size; j++)
3408 int x = ex + j * xy[i][0];
3409 int y = ey + j * xy[i][1];
3412 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3415 element = Feld[x][y];
3417 /* do not restart explosions of fields with active bombs */
3418 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3421 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3424 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3425 !CAN_GROW_INTO(element) && !dynabomb_xl)
3428 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3429 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3430 element != EL_SAND && !dynabomb_xl)
3437 void Bang(int x, int y)
3440 int element = MovingOrBlocked2Element(x, y);
3442 int element = Feld[x][y];
3446 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3448 if (IS_PLAYER(x, y))
3451 struct PlayerInfo *player = PLAYERINFO(x, y);
3453 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3454 player->element_nr);
3459 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3461 if (game.emulation == EMU_SUPAPLEX)
3462 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3464 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3469 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3477 case EL_BD_BUTTERFLY:
3480 case EL_DARK_YAMYAM:
3484 RaiseScoreElement(element);
3485 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3487 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3488 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3489 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3490 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3491 case EL_DYNABOMB_INCREASE_NUMBER:
3492 case EL_DYNABOMB_INCREASE_SIZE:
3493 case EL_DYNABOMB_INCREASE_POWER:
3498 case EL_LAMP_ACTIVE:
3500 case EL_AMOEBA_TO_DIAMOND:
3502 if (IS_PLAYER(x, y))
3503 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3505 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3508 if (CAN_EXPLODE_CROSS(element))
3510 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3514 else if (CAN_EXPLODE_1X1(element))
3515 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3517 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3521 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3524 void SplashAcid(int x, int y)
3527 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3528 (!IN_LEV_FIELD(x - 1, y - 2) ||
3529 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3530 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3532 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3533 (!IN_LEV_FIELD(x + 1, y - 2) ||
3534 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3535 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3537 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3539 /* input: position of element entering acid (obsolete) */
3541 int element = Feld[x][y];
3543 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3546 if (element != EL_ACID_SPLASH_LEFT &&
3547 element != EL_ACID_SPLASH_RIGHT)
3549 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3551 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3552 (!IN_LEV_FIELD(x - 1, y - 1) ||
3553 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3554 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3556 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3557 (!IN_LEV_FIELD(x + 1, y - 1) ||
3558 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3559 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3564 static void InitBeltMovement()
3566 static int belt_base_element[4] =
3568 EL_CONVEYOR_BELT_1_LEFT,
3569 EL_CONVEYOR_BELT_2_LEFT,
3570 EL_CONVEYOR_BELT_3_LEFT,
3571 EL_CONVEYOR_BELT_4_LEFT
3573 static int belt_base_active_element[4] =
3575 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3576 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3577 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3578 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3583 /* set frame order for belt animation graphic according to belt direction */
3584 for (i = 0; i < NUM_BELTS; i++)
3588 for (j = 0; j < NUM_BELT_PARTS; j++)
3590 int element = belt_base_active_element[belt_nr] + j;
3591 int graphic = el2img(element);
3593 if (game.belt_dir[i] == MV_LEFT)
3594 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3596 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3600 for (y = 0; y < lev_fieldy; y++)
3602 for (x = 0; x < lev_fieldx; x++)
3604 int element = Feld[x][y];
3606 for (i = 0; i < NUM_BELTS; i++)
3608 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3610 int e_belt_nr = getBeltNrFromBeltElement(element);
3613 if (e_belt_nr == belt_nr)
3615 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3617 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3625 static void ToggleBeltSwitch(int x, int y)
3627 static int belt_base_element[4] =
3629 EL_CONVEYOR_BELT_1_LEFT,
3630 EL_CONVEYOR_BELT_2_LEFT,
3631 EL_CONVEYOR_BELT_3_LEFT,
3632 EL_CONVEYOR_BELT_4_LEFT
3634 static int belt_base_active_element[4] =
3636 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3637 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3638 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3639 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3641 static int belt_base_switch_element[4] =
3643 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3644 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3645 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3646 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3648 static int belt_move_dir[4] =
3656 int element = Feld[x][y];
3657 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3658 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3659 int belt_dir = belt_move_dir[belt_dir_nr];
3662 if (!IS_BELT_SWITCH(element))
3665 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3666 game.belt_dir[belt_nr] = belt_dir;
3668 if (belt_dir_nr == 3)
3671 /* set frame order for belt animation graphic according to belt direction */
3672 for (i = 0; i < NUM_BELT_PARTS; i++)
3674 int element = belt_base_active_element[belt_nr] + i;
3675 int graphic = el2img(element);
3677 if (belt_dir == MV_LEFT)
3678 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3680 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3683 for (yy = 0; yy < lev_fieldy; yy++)
3685 for (xx = 0; xx < lev_fieldx; xx++)
3687 int element = Feld[xx][yy];
3689 if (IS_BELT_SWITCH(element))
3691 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3693 if (e_belt_nr == belt_nr)
3695 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3696 DrawLevelField(xx, yy);
3699 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3701 int e_belt_nr = getBeltNrFromBeltElement(element);
3703 if (e_belt_nr == belt_nr)
3705 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3707 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3708 DrawLevelField(xx, yy);
3711 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3713 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3715 if (e_belt_nr == belt_nr)
3717 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3719 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3720 DrawLevelField(xx, yy);
3727 static void ToggleSwitchgateSwitch(int x, int y)
3731 game.switchgate_pos = !game.switchgate_pos;
3733 for (yy = 0; yy < lev_fieldy; yy++)
3735 for (xx = 0; xx < lev_fieldx; xx++)
3737 int element = Feld[xx][yy];
3739 if (element == EL_SWITCHGATE_SWITCH_UP ||
3740 element == EL_SWITCHGATE_SWITCH_DOWN)
3742 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3743 DrawLevelField(xx, yy);
3745 else if (element == EL_SWITCHGATE_OPEN ||
3746 element == EL_SWITCHGATE_OPENING)
3748 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3750 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3752 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3755 else if (element == EL_SWITCHGATE_CLOSED ||
3756 element == EL_SWITCHGATE_CLOSING)
3758 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3760 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3762 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3769 static int getInvisibleActiveFromInvisibleElement(int element)
3771 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3772 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3773 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3777 static int getInvisibleFromInvisibleActiveElement(int element)
3779 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3780 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3781 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3785 static void RedrawAllLightSwitchesAndInvisibleElements()
3789 for (y = 0; y < lev_fieldy; y++)
3791 for (x = 0; x < lev_fieldx; x++)
3793 int element = Feld[x][y];
3795 if (element == EL_LIGHT_SWITCH &&
3796 game.light_time_left > 0)
3798 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3799 DrawLevelField(x, y);
3801 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3802 game.light_time_left == 0)
3804 Feld[x][y] = EL_LIGHT_SWITCH;
3805 DrawLevelField(x, y);
3807 else if (element == EL_INVISIBLE_STEELWALL ||
3808 element == EL_INVISIBLE_WALL ||
3809 element == EL_INVISIBLE_SAND)
3811 if (game.light_time_left > 0)
3812 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3814 DrawLevelField(x, y);
3816 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3817 element == EL_INVISIBLE_WALL_ACTIVE ||
3818 element == EL_INVISIBLE_SAND_ACTIVE)
3820 if (game.light_time_left == 0)
3821 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3823 DrawLevelField(x, y);
3829 static void ToggleLightSwitch(int x, int y)
3831 int element = Feld[x][y];
3833 game.light_time_left =
3834 (element == EL_LIGHT_SWITCH ?
3835 level.time_light * FRAMES_PER_SECOND : 0);
3837 RedrawAllLightSwitchesAndInvisibleElements();
3840 static void ActivateTimegateSwitch(int x, int y)
3844 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3846 for (yy = 0; yy < lev_fieldy; yy++)
3848 for (xx = 0; xx < lev_fieldx; xx++)
3850 int element = Feld[xx][yy];
3852 if (element == EL_TIMEGATE_CLOSED ||
3853 element == EL_TIMEGATE_CLOSING)
3855 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3856 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3860 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3862 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3863 DrawLevelField(xx, yy);
3870 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3873 inline static int getElementMoveStepsize(int x, int y)
3875 int element = Feld[x][y];
3876 int direction = MovDir[x][y];
3877 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3878 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3879 int horiz_move = (dx != 0);
3880 int sign = (horiz_move ? dx : dy);
3881 int step = sign * element_info[element].move_stepsize;
3883 /* special values for move stepsize for spring and things on conveyor belt */
3887 if (element == EL_SPRING)
3888 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3889 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3890 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3891 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3893 if (CAN_FALL(element) &&
3894 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3895 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3896 else if (element == EL_SPRING)
3897 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3904 void Impact(int x, int y)
3906 boolean lastline = (y == lev_fieldy-1);
3907 boolean object_hit = FALSE;
3908 boolean impact = (lastline || object_hit);
3909 int element = Feld[x][y];
3910 int smashed = EL_STEELWALL;
3912 if (!lastline) /* check if element below was hit */
3914 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3917 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3918 MovDir[x][y + 1] != MV_DOWN ||
3919 MovPos[x][y + 1] <= TILEY / 2));
3922 object_hit = !IS_FREE(x, y + 1);
3925 /* do not smash moving elements that left the smashed field in time */
3926 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3927 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3931 smashed = MovingOrBlocked2Element(x, y + 1);
3933 impact = (lastline || object_hit);
3936 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3938 SplashAcid(x, y + 1);
3942 /* only reset graphic animation if graphic really changes after impact */
3944 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3946 ResetGfxAnimation(x, y);
3947 DrawLevelField(x, y);
3950 if (impact && CAN_EXPLODE_IMPACT(element))
3955 else if (impact && element == EL_PEARL)
3957 Feld[x][y] = EL_PEARL_BREAKING;
3958 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3961 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
3963 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3968 if (impact && element == EL_AMOEBA_DROP)
3970 if (object_hit && IS_PLAYER(x, y + 1))
3971 KillHeroUnlessEnemyProtected(x, y + 1);
3972 else if (object_hit && smashed == EL_PENGUIN)
3976 Feld[x][y] = EL_AMOEBA_GROWING;
3977 Store[x][y] = EL_AMOEBA_WET;
3979 ResetRandomAnimationValue(x, y);
3984 if (object_hit) /* check which object was hit */
3986 if (CAN_PASS_MAGIC_WALL(element) &&
3987 (smashed == EL_MAGIC_WALL ||
3988 smashed == EL_BD_MAGIC_WALL))
3991 int activated_magic_wall =
3992 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3993 EL_BD_MAGIC_WALL_ACTIVE);
3995 /* activate magic wall / mill */
3996 for (yy = 0; yy < lev_fieldy; yy++)
3997 for (xx = 0; xx < lev_fieldx; xx++)
3998 if (Feld[xx][yy] == smashed)
3999 Feld[xx][yy] = activated_magic_wall;
4001 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4002 game.magic_wall_active = TRUE;
4004 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4005 SND_MAGIC_WALL_ACTIVATING :
4006 SND_BD_MAGIC_WALL_ACTIVATING));
4009 if (IS_PLAYER(x, y + 1))
4011 if (CAN_SMASH_PLAYER(element))
4013 KillHeroUnlessEnemyProtected(x, y + 1);
4017 else if (smashed == EL_PENGUIN)
4019 if (CAN_SMASH_PLAYER(element))
4025 else if (element == EL_BD_DIAMOND)
4027 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4033 else if (((element == EL_SP_INFOTRON ||
4034 element == EL_SP_ZONK) &&
4035 (smashed == EL_SP_SNIKSNAK ||
4036 smashed == EL_SP_ELECTRON ||
4037 smashed == EL_SP_DISK_ORANGE)) ||
4038 (element == EL_SP_INFOTRON &&
4039 smashed == EL_SP_DISK_YELLOW))
4045 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4051 else if (CAN_SMASH_EVERYTHING(element))
4053 if (IS_CLASSIC_ENEMY(smashed) ||
4054 CAN_EXPLODE_SMASHED(smashed))
4059 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4061 if (smashed == EL_LAMP ||
4062 smashed == EL_LAMP_ACTIVE)
4067 else if (smashed == EL_NUT)
4069 Feld[x][y + 1] = EL_NUT_BREAKING;
4070 PlayLevelSound(x, y, SND_NUT_BREAKING);
4071 RaiseScoreElement(EL_NUT);
4074 else if (smashed == EL_PEARL)
4076 Feld[x][y + 1] = EL_PEARL_BREAKING;
4077 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4080 else if (smashed == EL_DIAMOND)
4082 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4083 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4086 else if (IS_BELT_SWITCH(smashed))
4088 ToggleBeltSwitch(x, y + 1);
4090 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4091 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4093 ToggleSwitchgateSwitch(x, y + 1);
4095 else if (smashed == EL_LIGHT_SWITCH ||
4096 smashed == EL_LIGHT_SWITCH_ACTIVE)
4098 ToggleLightSwitch(x, y + 1);
4103 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4106 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4108 CheckTriggeredElementChangeSide(x, y + 1, smashed,
4109 CE_OTHER_IS_SWITCHING, CH_SIDE_TOP);
4110 CheckElementChangeSide(x, y + 1, smashed, element,
4111 CE_SWITCHED, CH_SIDE_TOP);
4116 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4121 /* play sound of magic wall / mill */
4123 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4124 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4126 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4127 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4128 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4129 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4134 /* play sound of object that hits the ground */
4135 if (lastline || object_hit)
4136 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4139 inline static void TurnRoundExt(int x, int y)
4151 { 0, 0 }, { 0, 0 }, { 0, 0 },
4156 int left, right, back;
4160 { MV_DOWN, MV_UP, MV_RIGHT },
4161 { MV_UP, MV_DOWN, MV_LEFT },
4163 { MV_LEFT, MV_RIGHT, MV_DOWN },
4167 { MV_RIGHT, MV_LEFT, MV_UP }
4170 int element = Feld[x][y];
4171 int move_pattern = element_info[element].move_pattern;
4173 int old_move_dir = MovDir[x][y];
4174 int left_dir = turn[old_move_dir].left;
4175 int right_dir = turn[old_move_dir].right;
4176 int back_dir = turn[old_move_dir].back;
4178 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4179 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4180 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4181 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4183 int left_x = x + left_dx, left_y = y + left_dy;
4184 int right_x = x + right_dx, right_y = y + right_dy;
4185 int move_x = x + move_dx, move_y = y + move_dy;
4189 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4191 TestIfBadThingTouchesOtherBadThing(x, y);
4193 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4194 MovDir[x][y] = right_dir;
4195 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4196 MovDir[x][y] = left_dir;
4198 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4200 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4204 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4205 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4207 TestIfBadThingTouchesOtherBadThing(x, y);
4209 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4210 MovDir[x][y] = left_dir;
4211 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4212 MovDir[x][y] = right_dir;
4214 if ((element == EL_SPACESHIP ||
4215 element == EL_SP_SNIKSNAK ||
4216 element == EL_SP_ELECTRON)
4217 && MovDir[x][y] != old_move_dir)
4219 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4223 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4225 TestIfBadThingTouchesOtherBadThing(x, y);
4227 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4228 MovDir[x][y] = left_dir;
4229 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4230 MovDir[x][y] = right_dir;
4232 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4234 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4237 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4239 TestIfBadThingTouchesOtherBadThing(x, y);
4241 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4242 MovDir[x][y] = left_dir;
4243 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4244 MovDir[x][y] = right_dir;
4246 if (MovDir[x][y] != old_move_dir)
4250 else if (element == EL_YAMYAM)
4252 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4253 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4255 if (can_turn_left && can_turn_right)
4256 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4257 else if (can_turn_left)
4258 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4259 else if (can_turn_right)
4260 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4262 MovDir[x][y] = back_dir;
4264 MovDelay[x][y] = 16 + 16 * RND(3);
4266 else if (element == EL_DARK_YAMYAM)
4268 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4270 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4273 if (can_turn_left && can_turn_right)
4274 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4275 else if (can_turn_left)
4276 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4277 else if (can_turn_right)
4278 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4280 MovDir[x][y] = back_dir;
4282 MovDelay[x][y] = 16 + 16 * RND(3);
4284 else if (element == EL_PACMAN)
4286 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4287 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4289 if (can_turn_left && can_turn_right)
4290 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4291 else if (can_turn_left)
4292 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4293 else if (can_turn_right)
4294 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4296 MovDir[x][y] = back_dir;
4298 MovDelay[x][y] = 6 + RND(40);
4300 else if (element == EL_PIG)
4302 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4303 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4304 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4305 boolean should_turn_left, should_turn_right, should_move_on;
4307 int rnd = RND(rnd_value);
4309 should_turn_left = (can_turn_left &&
4311 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4312 y + back_dy + left_dy)));
4313 should_turn_right = (can_turn_right &&
4315 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4316 y + back_dy + right_dy)));
4317 should_move_on = (can_move_on &&
4320 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4321 y + move_dy + left_dy) ||
4322 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4323 y + move_dy + right_dy)));
4325 if (should_turn_left || should_turn_right || should_move_on)
4327 if (should_turn_left && should_turn_right && should_move_on)
4328 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4329 rnd < 2 * rnd_value / 3 ? right_dir :
4331 else if (should_turn_left && should_turn_right)
4332 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4333 else if (should_turn_left && should_move_on)
4334 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4335 else if (should_turn_right && should_move_on)
4336 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4337 else if (should_turn_left)
4338 MovDir[x][y] = left_dir;
4339 else if (should_turn_right)
4340 MovDir[x][y] = right_dir;
4341 else if (should_move_on)
4342 MovDir[x][y] = old_move_dir;
4344 else if (can_move_on && rnd > rnd_value / 8)
4345 MovDir[x][y] = old_move_dir;
4346 else if (can_turn_left && can_turn_right)
4347 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4348 else if (can_turn_left && rnd > rnd_value / 8)
4349 MovDir[x][y] = left_dir;
4350 else if (can_turn_right && rnd > rnd_value/8)
4351 MovDir[x][y] = right_dir;
4353 MovDir[x][y] = back_dir;
4355 xx = x + move_xy[MovDir[x][y]].x;
4356 yy = y + move_xy[MovDir[x][y]].y;
4358 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4359 MovDir[x][y] = old_move_dir;
4363 else if (element == EL_DRAGON)
4365 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4366 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4367 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4369 int rnd = RND(rnd_value);
4372 if (FrameCounter < 1 && x == 0 && y == 29)
4373 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4376 if (can_move_on && rnd > rnd_value / 8)
4377 MovDir[x][y] = old_move_dir;
4378 else if (can_turn_left && can_turn_right)
4379 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4380 else if (can_turn_left && rnd > rnd_value / 8)
4381 MovDir[x][y] = left_dir;
4382 else if (can_turn_right && rnd > rnd_value / 8)
4383 MovDir[x][y] = right_dir;
4385 MovDir[x][y] = back_dir;
4387 xx = x + move_xy[MovDir[x][y]].x;
4388 yy = y + move_xy[MovDir[x][y]].y;
4391 if (FrameCounter < 1 && x == 0 && y == 29)
4392 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4393 xx, yy, Feld[xx][yy],
4398 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4399 MovDir[x][y] = old_move_dir;
4401 if (!IS_FREE(xx, yy))
4402 MovDir[x][y] = old_move_dir;
4406 if (FrameCounter < 1 && x == 0 && y == 29)
4407 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4412 else if (element == EL_MOLE)
4414 boolean can_move_on =
4415 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4416 IS_AMOEBOID(Feld[move_x][move_y]) ||
4417 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4420 boolean can_turn_left =
4421 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4422 IS_AMOEBOID(Feld[left_x][left_y])));
4424 boolean can_turn_right =
4425 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4426 IS_AMOEBOID(Feld[right_x][right_y])));
4428 if (can_turn_left && can_turn_right)
4429 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4430 else if (can_turn_left)
4431 MovDir[x][y] = left_dir;
4433 MovDir[x][y] = right_dir;
4436 if (MovDir[x][y] != old_move_dir)
4439 else if (element == EL_BALLOON)
4441 MovDir[x][y] = game.balloon_dir;
4444 else if (element == EL_SPRING)
4447 if (MovDir[x][y] & MV_HORIZONTAL &&
4448 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4449 MovDir[x][y] = MV_NO_MOVING;
4451 if (MovDir[x][y] & MV_HORIZONTAL &&
4452 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4453 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4454 MovDir[x][y] = MV_NO_MOVING;
4459 else if (element == EL_ROBOT ||
4460 element == EL_SATELLITE ||
4461 element == EL_PENGUIN)
4463 int attr_x = -1, attr_y = -1;
4474 for (i = 0; i < MAX_PLAYERS; i++)
4476 struct PlayerInfo *player = &stored_player[i];
4477 int jx = player->jx, jy = player->jy;
4479 if (!player->active)
4483 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4492 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4493 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4494 game.engine_version < VERSION_IDENT(3,1,0,0)))
4496 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4503 if (element == EL_PENGUIN)
4506 static int xy[4][2] =
4514 for (i = 0; i < NUM_DIRECTIONS; i++)
4516 int ex = x + xy[i][0];
4517 int ey = y + xy[i][1];
4519 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4528 MovDir[x][y] = MV_NO_MOVING;
4530 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4531 else if (attr_x > x)
4532 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4534 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4535 else if (attr_y > y)
4536 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4538 if (element == EL_ROBOT)
4542 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4543 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4544 Moving2Blocked(x, y, &newx, &newy);
4546 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4547 MovDelay[x][y] = 8 + 8 * !RND(3);
4549 MovDelay[x][y] = 16;
4551 else if (element == EL_PENGUIN)
4557 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4559 boolean first_horiz = RND(2);
4560 int new_move_dir = MovDir[x][y];
4563 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4564 Moving2Blocked(x, y, &newx, &newy);
4566 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4570 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4571 Moving2Blocked(x, y, &newx, &newy);
4573 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4576 MovDir[x][y] = old_move_dir;
4580 else /* (element == EL_SATELLITE) */
4586 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4588 boolean first_horiz = RND(2);
4589 int new_move_dir = MovDir[x][y];
4592 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4593 Moving2Blocked(x, y, &newx, &newy);
4595 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4599 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4600 Moving2Blocked(x, y, &newx, &newy);
4602 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4605 MovDir[x][y] = old_move_dir;
4610 else if (move_pattern == MV_TURNING_LEFT ||
4611 move_pattern == MV_TURNING_RIGHT ||
4612 move_pattern == MV_TURNING_LEFT_RIGHT ||
4613 move_pattern == MV_TURNING_RIGHT_LEFT ||
4614 move_pattern == MV_TURNING_RANDOM ||
4615 move_pattern == MV_ALL_DIRECTIONS)
4617 boolean can_turn_left =
4618 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4619 boolean can_turn_right =
4620 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4622 if (move_pattern == MV_TURNING_LEFT)
4623 MovDir[x][y] = left_dir;
4624 else if (move_pattern == MV_TURNING_RIGHT)
4625 MovDir[x][y] = right_dir;
4626 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4627 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4628 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4629 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4630 else if (move_pattern == MV_TURNING_RANDOM)
4631 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4632 can_turn_right && !can_turn_left ? right_dir :
4633 RND(2) ? left_dir : right_dir);
4634 else if (can_turn_left && can_turn_right)
4635 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4636 else if (can_turn_left)
4637 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4638 else if (can_turn_right)
4639 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4641 MovDir[x][y] = back_dir;
4643 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4645 else if (move_pattern == MV_HORIZONTAL ||
4646 move_pattern == MV_VERTICAL)
4648 if (move_pattern & old_move_dir)
4649 MovDir[x][y] = back_dir;
4650 else if (move_pattern == MV_HORIZONTAL)
4651 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4652 else if (move_pattern == MV_VERTICAL)
4653 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4655 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4657 else if (move_pattern & MV_ANY_DIRECTION)
4659 MovDir[x][y] = move_pattern;
4660 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4662 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4664 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4665 MovDir[x][y] = left_dir;
4666 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4667 MovDir[x][y] = right_dir;
4669 if (MovDir[x][y] != old_move_dir)
4670 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4672 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4674 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4675 MovDir[x][y] = right_dir;
4676 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4677 MovDir[x][y] = left_dir;
4679 if (MovDir[x][y] != old_move_dir)
4680 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4682 else if (move_pattern == MV_TOWARDS_PLAYER ||
4683 move_pattern == MV_AWAY_FROM_PLAYER)
4685 int attr_x = -1, attr_y = -1;
4687 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4698 for (i = 0; i < MAX_PLAYERS; i++)
4700 struct PlayerInfo *player = &stored_player[i];
4701 int jx = player->jx, jy = player->jy;
4703 if (!player->active)
4707 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4715 MovDir[x][y] = MV_NO_MOVING;
4717 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4718 else if (attr_x > x)
4719 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4721 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4722 else if (attr_y > y)
4723 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4725 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4727 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4729 boolean first_horiz = RND(2);
4730 int new_move_dir = MovDir[x][y];
4733 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4734 Moving2Blocked(x, y, &newx, &newy);
4736 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4740 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4741 Moving2Blocked(x, y, &newx, &newy);
4743 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4746 MovDir[x][y] = old_move_dir;
4749 else if (move_pattern == MV_WHEN_PUSHED ||
4750 move_pattern == MV_WHEN_DROPPED)
4752 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4753 MovDir[x][y] = MV_NO_MOVING;
4757 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4759 static int test_xy[7][2] =
4769 static int test_dir[7] =
4779 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4780 int move_preference = -1000000; /* start with very low preference */
4781 int new_move_dir = MV_NO_MOVING;
4782 int start_test = RND(4);
4785 for (i = 0; i < NUM_DIRECTIONS; i++)
4787 int move_dir = test_dir[start_test + i];
4788 int move_dir_preference;
4790 xx = x + test_xy[start_test + i][0];
4791 yy = y + test_xy[start_test + i][1];
4793 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4794 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4796 new_move_dir = move_dir;
4801 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4804 move_dir_preference = -1 * RunnerVisit[xx][yy];
4805 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4806 move_dir_preference = PlayerVisit[xx][yy];
4808 if (move_dir_preference > move_preference)
4810 /* prefer field that has not been visited for the longest time */
4811 move_preference = move_dir_preference;
4812 new_move_dir = move_dir;
4814 else if (move_dir_preference == move_preference &&
4815 move_dir == old_move_dir)
4817 /* prefer last direction when all directions are preferred equally */
4818 move_preference = move_dir_preference;
4819 new_move_dir = move_dir;
4823 MovDir[x][y] = new_move_dir;
4824 if (old_move_dir != new_move_dir)
4829 static void TurnRound(int x, int y)
4831 int direction = MovDir[x][y];
4834 GfxDir[x][y] = MovDir[x][y];
4840 GfxDir[x][y] = MovDir[x][y];
4843 if (direction != MovDir[x][y])
4848 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4851 GfxAction[x][y] = ACTION_WAITING;
4855 static boolean JustBeingPushed(int x, int y)
4859 for (i = 0; i < MAX_PLAYERS; i++)
4861 struct PlayerInfo *player = &stored_player[i];
4863 if (player->active && player->is_pushing && player->MovPos)
4865 int next_jx = player->jx + (player->jx - player->last_jx);
4866 int next_jy = player->jy + (player->jy - player->last_jy);
4868 if (x == next_jx && y == next_jy)
4876 void StartMoving(int x, int y)
4879 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4881 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4882 int element = Feld[x][y];
4888 if (MovDelay[x][y] == 0)
4889 GfxAction[x][y] = ACTION_DEFAULT;
4891 /* !!! this should be handled more generic (not only for mole) !!! */
4892 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4893 GfxAction[x][y] = ACTION_DEFAULT;
4896 if (CAN_FALL(element) && y < lev_fieldy - 1)
4898 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4899 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4900 if (JustBeingPushed(x, y))
4903 if (element == EL_QUICKSAND_FULL)
4905 if (IS_FREE(x, y + 1))
4907 InitMovingField(x, y, MV_DOWN);
4908 started_moving = TRUE;
4910 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4911 Store[x][y] = EL_ROCK;
4913 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4915 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4918 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4920 if (!MovDelay[x][y])
4921 MovDelay[x][y] = TILEY + 1;
4930 Feld[x][y] = EL_QUICKSAND_EMPTY;
4931 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4932 Store[x][y + 1] = Store[x][y];
4935 PlayLevelSoundAction(x, y, ACTION_FILLING);
4937 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4941 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4942 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4944 InitMovingField(x, y, MV_DOWN);
4945 started_moving = TRUE;
4947 Feld[x][y] = EL_QUICKSAND_FILLING;
4948 Store[x][y] = element;
4950 PlayLevelSoundAction(x, y, ACTION_FILLING);
4952 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4955 else if (element == EL_MAGIC_WALL_FULL)
4957 if (IS_FREE(x, y + 1))
4959 InitMovingField(x, y, MV_DOWN);
4960 started_moving = TRUE;
4962 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4963 Store[x][y] = EL_CHANGED(Store[x][y]);
4965 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4967 if (!MovDelay[x][y])
4968 MovDelay[x][y] = TILEY/4 + 1;
4977 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4978 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4979 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4983 else if (element == EL_BD_MAGIC_WALL_FULL)
4985 if (IS_FREE(x, y + 1))
4987 InitMovingField(x, y, MV_DOWN);
4988 started_moving = TRUE;
4990 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4991 Store[x][y] = EL_CHANGED2(Store[x][y]);
4993 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4995 if (!MovDelay[x][y])
4996 MovDelay[x][y] = TILEY/4 + 1;
5005 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5006 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5007 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5011 else if (CAN_PASS_MAGIC_WALL(element) &&
5012 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5013 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5015 InitMovingField(x, y, MV_DOWN);
5016 started_moving = TRUE;
5019 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5020 EL_BD_MAGIC_WALL_FILLING);
5021 Store[x][y] = element;
5024 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5026 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5029 SplashAcid(x, y + 1);
5031 InitMovingField(x, y, MV_DOWN);
5032 started_moving = TRUE;
5034 Store[x][y] = EL_ACID;
5036 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5037 GfxAction[x][y + 1] = ACTION_ACTIVE;
5041 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
5042 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5043 (Feld[x][y + 1] == EL_BLOCKED)) ||
5044 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5045 CAN_SMASH(element) && WasJustFalling[x][y] &&
5046 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
5050 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5051 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5052 WasJustMoving[x][y] && !Pushed[x][y + 1])
5054 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5055 WasJustMoving[x][y])
5060 /* this is needed for a special case not covered by calling "Impact()"
5061 from "ContinueMoving()": if an element moves to a tile directly below
5062 another element which was just falling on that tile (which was empty
5063 in the previous frame), the falling element above would just stop
5064 instead of smashing the element below (in previous version, the above
5065 element was just checked for "moving" instead of "falling", resulting
5066 in incorrect smashes caused by horizontal movement of the above
5067 element; also, the case of the player being the element to smash was
5068 simply not covered here... :-/ ) */
5071 WasJustMoving[x][y] = 0;
5072 WasJustFalling[x][y] = 0;
5077 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5079 if (MovDir[x][y] == MV_NO_MOVING)
5081 InitMovingField(x, y, MV_DOWN);
5082 started_moving = TRUE;
5085 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5087 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5088 MovDir[x][y] = MV_DOWN;
5090 InitMovingField(x, y, MV_DOWN);
5091 started_moving = TRUE;
5093 else if (element == EL_AMOEBA_DROP)
5095 Feld[x][y] = EL_AMOEBA_GROWING;
5096 Store[x][y] = EL_AMOEBA_WET;
5098 /* Store[x][y + 1] must be zero, because:
5099 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5102 #if OLD_GAME_BEHAVIOUR
5103 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5105 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5106 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5107 element != EL_DX_SUPABOMB)
5110 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5111 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5112 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5113 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5116 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5117 (IS_FREE(x - 1, y + 1) ||
5118 Feld[x - 1][y + 1] == EL_ACID));
5119 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5120 (IS_FREE(x + 1, y + 1) ||
5121 Feld[x + 1][y + 1] == EL_ACID));
5122 boolean can_fall_any = (can_fall_left || can_fall_right);
5123 boolean can_fall_both = (can_fall_left && can_fall_right);
5125 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5127 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5129 if (slippery_type == SLIPPERY_ONLY_LEFT)
5130 can_fall_right = FALSE;
5131 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5132 can_fall_left = FALSE;
5133 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5134 can_fall_right = FALSE;
5135 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5136 can_fall_left = FALSE;
5138 can_fall_any = (can_fall_left || can_fall_right);
5139 can_fall_both = (can_fall_left && can_fall_right);
5144 if (can_fall_both &&
5145 (game.emulation != EMU_BOULDERDASH &&
5146 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5147 can_fall_left = !(can_fall_right = RND(2));
5149 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5150 started_moving = TRUE;
5154 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5156 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5159 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5160 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5161 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5162 int belt_dir = game.belt_dir[belt_nr];
5164 if ((belt_dir == MV_LEFT && left_is_free) ||
5165 (belt_dir == MV_RIGHT && right_is_free))
5168 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5171 InitMovingField(x, y, belt_dir);
5172 started_moving = TRUE;
5175 Pushed[x][y] = TRUE;
5176 Pushed[nextx][y] = TRUE;
5179 GfxAction[x][y] = ACTION_DEFAULT;
5183 MovDir[x][y] = 0; /* if element was moving, stop it */
5188 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5189 if (CAN_MOVE(element) && !started_moving)
5191 int move_pattern = element_info[element].move_pattern;
5194 Moving2Blocked(x, y, &newx, &newy);
5197 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5200 if ((element == EL_SATELLITE ||
5201 element == EL_BALLOON ||
5202 element == EL_SPRING)
5203 && JustBeingPushed(x, y))
5208 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5209 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5210 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5213 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5214 element, element_info[element].token_name,
5215 WasJustMoving[x][y],
5216 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5217 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5218 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5219 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5223 WasJustMoving[x][y] = 0;
5226 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5229 if (Feld[x][y] != element) /* element has changed */
5231 element = Feld[x][y];
5232 move_pattern = element_info[element].move_pattern;
5234 if (!CAN_MOVE(element))
5238 if (Feld[x][y] != element) /* element has changed */
5246 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5247 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5249 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5251 Moving2Blocked(x, y, &newx, &newy);
5252 if (Feld[newx][newy] == EL_BLOCKED)
5253 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5259 if (FrameCounter < 1 && x == 0 && y == 29)
5260 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5263 if (!MovDelay[x][y]) /* start new movement phase */
5265 /* all objects that can change their move direction after each step
5266 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5268 if (element != EL_YAMYAM &&
5269 element != EL_DARK_YAMYAM &&
5270 element != EL_PACMAN &&
5271 !(move_pattern & MV_ANY_DIRECTION) &&
5272 move_pattern != MV_TURNING_LEFT &&
5273 move_pattern != MV_TURNING_RIGHT &&
5274 move_pattern != MV_TURNING_LEFT_RIGHT &&
5275 move_pattern != MV_TURNING_RIGHT_LEFT &&
5276 move_pattern != MV_TURNING_RANDOM)
5281 if (FrameCounter < 1 && x == 0 && y == 29)
5282 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5285 if (MovDelay[x][y] && (element == EL_BUG ||
5286 element == EL_SPACESHIP ||
5287 element == EL_SP_SNIKSNAK ||
5288 element == EL_SP_ELECTRON ||
5289 element == EL_MOLE))
5290 DrawLevelField(x, y);
5294 if (MovDelay[x][y]) /* wait some time before next movement */
5299 if (element == EL_YAMYAM)
5302 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5303 DrawLevelElementAnimation(x, y, element);
5307 if (MovDelay[x][y]) /* element still has to wait some time */
5310 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5311 ResetGfxAnimation(x, y);
5315 if (GfxAction[x][y] != ACTION_WAITING)
5316 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5318 GfxAction[x][y] = ACTION_WAITING;
5322 if (element == EL_ROBOT ||
5324 element == EL_PACMAN ||
5326 element == EL_YAMYAM ||
5327 element == EL_DARK_YAMYAM)
5330 DrawLevelElementAnimation(x, y, element);
5332 DrawLevelElementAnimationIfNeeded(x, y, element);
5334 PlayLevelSoundAction(x, y, ACTION_WAITING);
5336 else if (element == EL_SP_ELECTRON)
5337 DrawLevelElementAnimationIfNeeded(x, y, element);
5338 else if (element == EL_DRAGON)
5341 int dir = MovDir[x][y];
5342 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5343 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5344 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5345 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5346 dir == MV_UP ? IMG_FLAMES_1_UP :
5347 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5348 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5351 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5354 GfxAction[x][y] = ACTION_ATTACKING;
5356 if (IS_PLAYER(x, y))
5357 DrawPlayerField(x, y);
5359 DrawLevelField(x, y);
5361 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5363 for (i = 1; i <= 3; i++)
5365 int xx = x + i * dx;
5366 int yy = y + i * dy;
5367 int sx = SCREENX(xx);
5368 int sy = SCREENY(yy);
5369 int flame_graphic = graphic + (i - 1);
5371 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5376 int flamed = MovingOrBlocked2Element(xx, yy);
5380 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5382 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5383 RemoveMovingField(xx, yy);
5385 RemoveField(xx, yy);
5387 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5390 RemoveMovingField(xx, yy);
5394 if (ChangeDelay[xx][yy])
5395 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5396 Feld[xx][yy] == EL_BLOCKED));
5400 ChangeDelay[xx][yy] = 0;
5402 Feld[xx][yy] = EL_FLAMES;
5403 if (IN_SCR_FIELD(sx, sy))
5405 DrawLevelFieldCrumbledSand(xx, yy);
5406 DrawGraphic(sx, sy, flame_graphic, frame);
5411 if (Feld[xx][yy] == EL_FLAMES)
5412 Feld[xx][yy] = EL_EMPTY;
5413 DrawLevelField(xx, yy);
5418 if (MovDelay[x][y]) /* element still has to wait some time */
5420 PlayLevelSoundAction(x, y, ACTION_WAITING);
5426 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5427 for all other elements GfxAction will be set by InitMovingField() */
5428 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5429 GfxAction[x][y] = ACTION_MOVING;
5433 /* now make next step */
5435 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5437 if (DONT_COLLIDE_WITH(element) &&
5438 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5439 !PLAYER_ENEMY_PROTECTED(newx, newy))
5442 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5446 /* player killed by element which is deadly when colliding with */
5448 KillHero(PLAYERINFO(newx, newy));
5455 else if (CAN_MOVE_INTO_ACID(element) &&
5456 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5457 (MovDir[x][y] == MV_DOWN ||
5458 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5460 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5461 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5465 else if ((element == EL_PENGUIN ||
5466 element == EL_ROBOT ||
5467 element == EL_SATELLITE ||
5468 element == EL_BALLOON ||
5469 IS_CUSTOM_ELEMENT(element)) &&
5470 IN_LEV_FIELD(newx, newy) &&
5471 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5474 SplashAcid(newx, newy);
5475 Store[x][y] = EL_ACID;
5477 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5479 if (Feld[newx][newy] == EL_EXIT_OPEN)
5483 DrawLevelField(x, y);
5485 Feld[x][y] = EL_EMPTY;
5486 DrawLevelField(x, y);
5489 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5490 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5491 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5493 local_player->friends_still_needed--;
5494 if (!local_player->friends_still_needed &&
5495 !local_player->GameOver && AllPlayersGone)
5496 local_player->LevelSolved = local_player->GameOver = TRUE;
5500 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5502 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5503 DrawLevelField(newx, newy);
5505 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5507 else if (!IS_FREE(newx, newy))
5509 GfxAction[x][y] = ACTION_WAITING;
5511 if (IS_PLAYER(x, y))
5512 DrawPlayerField(x, y);
5514 DrawLevelField(x, y);
5519 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5521 if (IS_FOOD_PIG(Feld[newx][newy]))
5523 if (IS_MOVING(newx, newy))
5524 RemoveMovingField(newx, newy);
5527 Feld[newx][newy] = EL_EMPTY;
5528 DrawLevelField(newx, newy);
5531 PlayLevelSound(x, y, SND_PIG_DIGGING);
5533 else if (!IS_FREE(newx, newy))
5535 if (IS_PLAYER(x, y))
5536 DrawPlayerField(x, y);
5538 DrawLevelField(x, y);
5547 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5550 else if (IS_CUSTOM_ELEMENT(element) &&
5551 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5555 !IS_FREE(newx, newy)
5560 int new_element = Feld[newx][newy];
5563 printf("::: '%s' digs '%s' [%d]\n",
5564 element_info[element].token_name,
5565 element_info[Feld[newx][newy]].token_name,
5566 StorePlayer[newx][newy]);
5569 if (!IS_FREE(newx, newy))
5571 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5572 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5575 /* no element can dig solid indestructible elements */
5576 if (IS_INDESTRUCTIBLE(new_element) &&
5577 !IS_DIGGABLE(new_element) &&
5578 !IS_COLLECTIBLE(new_element))
5581 if (AmoebaNr[newx][newy] &&
5582 (new_element == EL_AMOEBA_FULL ||
5583 new_element == EL_BD_AMOEBA ||
5584 new_element == EL_AMOEBA_GROWING))
5586 AmoebaCnt[AmoebaNr[newx][newy]]--;
5587 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5590 if (IS_MOVING(newx, newy))
5591 RemoveMovingField(newx, newy);
5594 RemoveField(newx, newy);
5595 DrawLevelField(newx, newy);
5598 PlayLevelSoundAction(x, y, action);
5603 Store[newx][newy] = EL_EMPTY;
5604 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5605 Store[newx][newy] = element_info[element].move_leave_element;
5607 Store[newx][newy] = EL_EMPTY;
5608 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5609 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5610 Store[newx][newy] = element_info[element].move_leave_element;
5613 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5614 element_info[element].can_leave_element = TRUE;
5617 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5619 RunnerVisit[x][y] = FrameCounter;
5620 PlayerVisit[x][y] /= 8; /* expire player visit path */
5626 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5628 if (!IS_FREE(newx, newy))
5630 if (IS_PLAYER(x, y))
5631 DrawPlayerField(x, y);
5633 DrawLevelField(x, y);
5639 boolean wanna_flame = !RND(10);
5640 int dx = newx - x, dy = newy - y;
5641 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5642 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5643 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5644 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5645 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5646 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5649 IS_CLASSIC_ENEMY(element1) ||
5650 IS_CLASSIC_ENEMY(element2)) &&
5651 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5652 element1 != EL_FLAMES && element2 != EL_FLAMES)
5655 ResetGfxAnimation(x, y);
5656 GfxAction[x][y] = ACTION_ATTACKING;
5659 if (IS_PLAYER(x, y))
5660 DrawPlayerField(x, y);
5662 DrawLevelField(x, y);
5664 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5666 MovDelay[x][y] = 50;
5670 RemoveField(newx, newy);
5672 Feld[newx][newy] = EL_FLAMES;
5673 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5676 RemoveField(newx1, newy1);
5678 Feld[newx1][newy1] = EL_FLAMES;
5680 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5683 RemoveField(newx2, newy2);
5685 Feld[newx2][newy2] = EL_FLAMES;
5692 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5693 Feld[newx][newy] == EL_DIAMOND)
5695 if (IS_MOVING(newx, newy))
5696 RemoveMovingField(newx, newy);
5699 Feld[newx][newy] = EL_EMPTY;
5700 DrawLevelField(newx, newy);
5703 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5705 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5706 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5708 if (AmoebaNr[newx][newy])
5710 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5711 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5712 Feld[newx][newy] == EL_BD_AMOEBA)
5713 AmoebaCnt[AmoebaNr[newx][newy]]--;
5718 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5720 if (IS_MOVING(newx, newy))
5723 RemoveMovingField(newx, newy);
5727 Feld[newx][newy] = EL_EMPTY;
5728 DrawLevelField(newx, newy);
5731 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5733 else if ((element == EL_PACMAN || element == EL_MOLE)
5734 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5736 if (AmoebaNr[newx][newy])
5738 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5739 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5740 Feld[newx][newy] == EL_BD_AMOEBA)
5741 AmoebaCnt[AmoebaNr[newx][newy]]--;
5744 if (element == EL_MOLE)
5746 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5747 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5749 ResetGfxAnimation(x, y);
5750 GfxAction[x][y] = ACTION_DIGGING;
5751 DrawLevelField(x, y);
5753 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5755 return; /* wait for shrinking amoeba */
5757 else /* element == EL_PACMAN */
5759 Feld[newx][newy] = EL_EMPTY;
5760 DrawLevelField(newx, newy);
5761 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5764 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5765 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5766 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5768 /* wait for shrinking amoeba to completely disappear */
5771 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5773 /* object was running against a wall */
5778 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5779 DrawLevelElementAnimation(x, y, element);
5781 if (element == EL_BUG ||
5782 element == EL_SPACESHIP ||
5783 element == EL_SP_SNIKSNAK)
5784 DrawLevelField(x, y);
5785 else if (element == EL_MOLE)
5786 DrawLevelField(x, y);
5787 else if (element == EL_BD_BUTTERFLY ||
5788 element == EL_BD_FIREFLY)
5789 DrawLevelElementAnimationIfNeeded(x, y, element);
5790 else if (element == EL_SATELLITE)
5791 DrawLevelElementAnimationIfNeeded(x, y, element);
5792 else if (element == EL_SP_ELECTRON)
5793 DrawLevelElementAnimationIfNeeded(x, y, element);
5796 if (DONT_TOUCH(element))
5797 TestIfBadThingTouchesHero(x, y);
5800 PlayLevelSoundAction(x, y, ACTION_WAITING);
5806 InitMovingField(x, y, MovDir[x][y]);
5808 PlayLevelSoundAction(x, y, ACTION_MOVING);
5812 ContinueMoving(x, y);
5815 void ContinueMoving(int x, int y)
5817 int element = Feld[x][y];
5818 int stored = Store[x][y];
5819 struct ElementInfo *ei = &element_info[element];
5820 int direction = MovDir[x][y];
5821 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5822 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5823 int newx = x + dx, newy = y + dy;
5825 int nextx = newx + dx, nexty = newy + dy;
5828 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5829 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5831 boolean pushed_by_player = Pushed[x][y];
5834 MovPos[x][y] += getElementMoveStepsize(x, y);
5837 if (pushed_by_player && IS_PLAYER(x, y))
5839 /* special case: moving object pushed by player */
5840 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5843 if (pushed_by_player) /* special case: moving object pushed by player */
5844 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5847 if (ABS(MovPos[x][y]) < TILEX)
5849 DrawLevelField(x, y);
5851 return; /* element is still moving */
5854 /* element reached destination field */
5856 Feld[x][y] = EL_EMPTY;
5857 Feld[newx][newy] = element;
5858 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5860 if (element == EL_MOLE)
5862 Feld[x][y] = EL_SAND;
5864 DrawLevelFieldCrumbledSandNeighbours(x, y);
5866 else if (element == EL_QUICKSAND_FILLING)
5868 element = Feld[newx][newy] = get_next_element(element);
5869 Store[newx][newy] = Store[x][y];
5871 else if (element == EL_QUICKSAND_EMPTYING)
5873 Feld[x][y] = get_next_element(element);
5874 element = Feld[newx][newy] = Store[x][y];
5876 else if (element == EL_MAGIC_WALL_FILLING)
5878 element = Feld[newx][newy] = get_next_element(element);
5879 if (!game.magic_wall_active)
5880 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5881 Store[newx][newy] = Store[x][y];
5883 else if (element == EL_MAGIC_WALL_EMPTYING)
5885 Feld[x][y] = get_next_element(element);
5886 if (!game.magic_wall_active)
5887 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5888 element = Feld[newx][newy] = Store[x][y];
5890 else if (element == EL_BD_MAGIC_WALL_FILLING)
5892 element = Feld[newx][newy] = get_next_element(element);
5893 if (!game.magic_wall_active)
5894 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5895 Store[newx][newy] = Store[x][y];
5897 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5899 Feld[x][y] = get_next_element(element);
5900 if (!game.magic_wall_active)
5901 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5902 element = Feld[newx][newy] = Store[x][y];
5904 else if (element == EL_AMOEBA_DROPPING)
5906 Feld[x][y] = get_next_element(element);
5907 element = Feld[newx][newy] = Store[x][y];
5909 else if (element == EL_SOKOBAN_OBJECT)
5912 Feld[x][y] = Back[x][y];
5914 if (Back[newx][newy])
5915 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5917 Back[x][y] = Back[newx][newy] = 0;
5919 else if (Store[x][y] == EL_ACID)
5921 element = Feld[newx][newy] = EL_ACID;
5924 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5925 ei->move_leave_element != EL_EMPTY &&
5926 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5927 Store[x][y] != EL_EMPTY))
5929 /* some elements can leave other elements behind after moving */
5931 Feld[x][y] = ei->move_leave_element;
5932 InitField(x, y, FALSE);
5934 if (GFX_CRUMBLED(Feld[x][y]))
5935 DrawLevelFieldCrumbledSandNeighbours(x, y);
5939 Store[x][y] = EL_EMPTY;
5940 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5941 MovDelay[newx][newy] = 0;
5943 if (CAN_CHANGE(element))
5945 /* copy element change control values to new field */
5946 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5947 ChangePage[newx][newy] = ChangePage[x][y];
5948 Changed[newx][newy] = Changed[x][y];
5949 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5952 ChangeDelay[x][y] = 0;
5953 ChangePage[x][y] = -1;
5954 Changed[x][y] = CE_BITMASK_DEFAULT;
5955 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5957 /* copy animation control values to new field */
5958 GfxFrame[newx][newy] = GfxFrame[x][y];
5959 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5960 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5961 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5963 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5965 ResetGfxAnimation(x, y); /* reset animation values for old field */
5968 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5969 ei->move_leave_element != EL_EMPTY &&
5970 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5971 stored != EL_EMPTY))
5973 /* some elements can leave other elements behind after moving */
5975 Feld[x][y] = ei->move_leave_element;
5976 InitField(x, y, FALSE);
5978 if (GFX_CRUMBLED(Feld[x][y]))
5979 DrawLevelFieldCrumbledSandNeighbours(x, y);
5984 /* some elements can leave other elements behind after moving */
5985 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5986 ei->move_leave_element != EL_EMPTY &&
5987 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5988 ei->can_leave_element_last))
5990 Feld[x][y] = ei->move_leave_element;
5991 InitField(x, y, FALSE);
5993 if (GFX_CRUMBLED(Feld[x][y]))
5994 DrawLevelFieldCrumbledSandNeighbours(x, y);
5997 ei->can_leave_element_last = ei->can_leave_element;
5998 ei->can_leave_element = FALSE;
6002 /* 2.1.1 (does not work correctly for spring) */
6003 if (!CAN_MOVE(element))
6004 MovDir[newx][newy] = 0;
6008 /* (does not work for falling objects that slide horizontally) */
6009 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6010 MovDir[newx][newy] = 0;
6013 if (!CAN_MOVE(element) ||
6014 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6015 MovDir[newx][newy] = 0;
6019 if (!CAN_MOVE(element) ||
6020 (CAN_FALL(element) && direction == MV_DOWN))
6021 GfxDir[x][y] = MovDir[newx][newy] = 0;
6023 if (!CAN_MOVE(element) ||
6024 (CAN_FALL(element) && direction == MV_DOWN &&
6025 (element == EL_SPRING ||
6026 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6027 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6028 GfxDir[x][y] = MovDir[newx][newy] = 0;
6034 DrawLevelField(x, y);
6035 DrawLevelField(newx, newy);
6037 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6039 /* prevent pushed element from moving on in pushed direction */
6040 if (pushed_by_player && CAN_MOVE(element) &&
6041 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6042 !(element_info[element].move_pattern & direction))
6043 TurnRound(newx, newy);
6046 /* prevent elements on conveyor belt from moving on in last direction */
6047 if (pushed_by_conveyor && CAN_FALL(element) &&
6048 direction & MV_HORIZONTAL)
6049 MovDir[newx][newy] = 0;
6052 if (!pushed_by_player)
6054 WasJustMoving[newx][newy] = 3;
6056 if (CAN_FALL(element) && direction == MV_DOWN)
6057 WasJustFalling[newx][newy] = 3;
6060 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6062 TestIfBadThingTouchesHero(newx, newy);
6063 TestIfBadThingTouchesFriend(newx, newy);
6065 if (!IS_CUSTOM_ELEMENT(element))
6066 TestIfBadThingTouchesOtherBadThing(newx, newy);
6068 else if (element == EL_PENGUIN)
6069 TestIfFriendTouchesBadThing(newx, newy);
6071 if (CAN_FALL(element) && direction == MV_DOWN &&
6072 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6076 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6080 if (ChangePage[newx][newy] != -1) /* delayed change */
6081 ChangeElement(newx, newy, ChangePage[newx][newy]);
6086 TestIfElementHitsCustomElement(newx, newy, direction);
6090 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6092 int hitting_element = Feld[newx][newy];
6094 /* !!! fix side (direction) orientation here and elsewhere !!! */
6095 CheckElementChangeSide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6099 if (IN_LEV_FIELD(nextx, nexty))
6101 int opposite_direction = MV_DIR_OPPOSITE(direction);
6102 int hitting_side = direction;
6103 int touched_side = opposite_direction;
6104 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6105 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6106 MovDir[nextx][nexty] != direction ||
6107 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6113 CheckElementChangeSide(nextx, nexty, touched_element,
6114 CE_HIT_BY_SOMETHING, opposite_direction);
6116 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6117 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6119 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6121 struct ElementChangeInfo *change =
6122 &element_info[hitting_element].change_page[i];
6124 if (change->can_change &&
6125 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6126 change->trigger_side & touched_side &&
6127 change->trigger_element == touched_element)
6129 CheckElementChangePage(newx, newy, hitting_element,
6130 touched_element, CE_OTHER_IS_HITTING, i);
6136 if (IS_CUSTOM_ELEMENT(touched_element) &&
6137 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6139 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6141 struct ElementChangeInfo *change =
6142 &element_info[touched_element].change_page[i];
6144 if (change->can_change &&
6145 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6146 change->trigger_side & hitting_side &&
6147 change->trigger_element == hitting_element)
6149 CheckElementChangePage(nextx, nexty, touched_element,
6150 hitting_element, CE_OTHER_GETS_HIT, i);
6161 TestIfPlayerTouchesCustomElement(newx, newy);
6162 TestIfElementTouchesCustomElement(newx, newy);
6165 int AmoebeNachbarNr(int ax, int ay)
6168 int element = Feld[ax][ay];
6170 static int xy[4][2] =
6178 for (i = 0; i < NUM_DIRECTIONS; i++)
6180 int x = ax + xy[i][0];
6181 int y = ay + xy[i][1];
6183 if (!IN_LEV_FIELD(x, y))
6186 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6187 group_nr = AmoebaNr[x][y];
6193 void AmoebenVereinigen(int ax, int ay)
6195 int i, x, y, xx, yy;
6196 int new_group_nr = AmoebaNr[ax][ay];
6197 static int xy[4][2] =
6205 if (new_group_nr == 0)
6208 for (i = 0; i < NUM_DIRECTIONS; i++)
6213 if (!IN_LEV_FIELD(x, y))
6216 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6217 Feld[x][y] == EL_BD_AMOEBA ||
6218 Feld[x][y] == EL_AMOEBA_DEAD) &&
6219 AmoebaNr[x][y] != new_group_nr)
6221 int old_group_nr = AmoebaNr[x][y];
6223 if (old_group_nr == 0)
6226 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6227 AmoebaCnt[old_group_nr] = 0;
6228 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6229 AmoebaCnt2[old_group_nr] = 0;
6231 for (yy = 0; yy < lev_fieldy; yy++)
6233 for (xx = 0; xx < lev_fieldx; xx++)
6235 if (AmoebaNr[xx][yy] == old_group_nr)
6236 AmoebaNr[xx][yy] = new_group_nr;
6243 void AmoebeUmwandeln(int ax, int ay)
6247 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6249 int group_nr = AmoebaNr[ax][ay];
6254 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6255 printf("AmoebeUmwandeln(): This should never happen!\n");
6260 for (y = 0; y < lev_fieldy; y++)
6262 for (x = 0; x < lev_fieldx; x++)
6264 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6267 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6271 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6272 SND_AMOEBA_TURNING_TO_GEM :
6273 SND_AMOEBA_TURNING_TO_ROCK));
6278 static int xy[4][2] =
6286 for (i = 0; i < NUM_DIRECTIONS; i++)
6291 if (!IN_LEV_FIELD(x, y))
6294 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6296 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6297 SND_AMOEBA_TURNING_TO_GEM :
6298 SND_AMOEBA_TURNING_TO_ROCK));
6305 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6308 int group_nr = AmoebaNr[ax][ay];
6309 boolean done = FALSE;
6314 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6315 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6320 for (y = 0; y < lev_fieldy; y++)
6322 for (x = 0; x < lev_fieldx; x++)
6324 if (AmoebaNr[x][y] == group_nr &&
6325 (Feld[x][y] == EL_AMOEBA_DEAD ||
6326 Feld[x][y] == EL_BD_AMOEBA ||
6327 Feld[x][y] == EL_AMOEBA_GROWING))
6330 Feld[x][y] = new_element;
6331 InitField(x, y, FALSE);
6332 DrawLevelField(x, y);
6339 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6340 SND_BD_AMOEBA_TURNING_TO_ROCK :
6341 SND_BD_AMOEBA_TURNING_TO_GEM));
6344 void AmoebeWaechst(int x, int y)
6346 static unsigned long sound_delay = 0;
6347 static unsigned long sound_delay_value = 0;
6349 if (!MovDelay[x][y]) /* start new growing cycle */
6353 if (DelayReached(&sound_delay, sound_delay_value))
6356 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6358 if (Store[x][y] == EL_BD_AMOEBA)
6359 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6361 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6363 sound_delay_value = 30;
6367 if (MovDelay[x][y]) /* wait some time before growing bigger */
6370 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6372 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6373 6 - MovDelay[x][y]);
6375 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6378 if (!MovDelay[x][y])
6380 Feld[x][y] = Store[x][y];
6382 DrawLevelField(x, y);
6387 void AmoebaDisappearing(int x, int y)
6389 static unsigned long sound_delay = 0;
6390 static unsigned long sound_delay_value = 0;
6392 if (!MovDelay[x][y]) /* start new shrinking cycle */
6396 if (DelayReached(&sound_delay, sound_delay_value))
6397 sound_delay_value = 30;
6400 if (MovDelay[x][y]) /* wait some time before shrinking */
6403 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6405 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6406 6 - MovDelay[x][y]);
6408 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6411 if (!MovDelay[x][y])
6413 Feld[x][y] = EL_EMPTY;
6414 DrawLevelField(x, y);
6416 /* don't let mole enter this field in this cycle;
6417 (give priority to objects falling to this field from above) */
6423 void AmoebeAbleger(int ax, int ay)
6426 int element = Feld[ax][ay];
6427 int graphic = el2img(element);
6428 int newax = ax, neway = ay;
6429 static int xy[4][2] =
6437 if (!level.amoeba_speed)
6439 Feld[ax][ay] = EL_AMOEBA_DEAD;
6440 DrawLevelField(ax, ay);
6444 if (IS_ANIMATED(graphic))
6445 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6447 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6448 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6450 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6453 if (MovDelay[ax][ay])
6457 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6460 int x = ax + xy[start][0];
6461 int y = ay + xy[start][1];
6463 if (!IN_LEV_FIELD(x, y))
6467 if (IS_FREE(x, y) ||
6468 CAN_GROW_INTO(Feld[x][y]) ||
6469 Feld[x][y] == EL_QUICKSAND_EMPTY)
6475 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6476 if (IS_FREE(x, y) ||
6477 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6484 if (newax == ax && neway == ay)
6487 else /* normal or "filled" (BD style) amoeba */
6490 boolean waiting_for_player = FALSE;
6492 for (i = 0; i < NUM_DIRECTIONS; i++)
6494 int j = (start + i) % 4;
6495 int x = ax + xy[j][0];
6496 int y = ay + xy[j][1];
6498 if (!IN_LEV_FIELD(x, y))
6502 if (IS_FREE(x, y) ||
6503 CAN_GROW_INTO(Feld[x][y]) ||
6504 Feld[x][y] == EL_QUICKSAND_EMPTY)
6511 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6512 if (IS_FREE(x, y) ||
6513 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6520 else if (IS_PLAYER(x, y))
6521 waiting_for_player = TRUE;
6524 if (newax == ax && neway == ay) /* amoeba cannot grow */
6527 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6529 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6532 Feld[ax][ay] = EL_AMOEBA_DEAD;
6533 DrawLevelField(ax, ay);
6534 AmoebaCnt[AmoebaNr[ax][ay]]--;
6536 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6538 if (element == EL_AMOEBA_FULL)
6539 AmoebeUmwandeln(ax, ay);
6540 else if (element == EL_BD_AMOEBA)
6541 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6546 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6548 /* amoeba gets larger by growing in some direction */
6550 int new_group_nr = AmoebaNr[ax][ay];
6553 if (new_group_nr == 0)
6555 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6556 printf("AmoebeAbleger(): This should never happen!\n");
6561 AmoebaNr[newax][neway] = new_group_nr;
6562 AmoebaCnt[new_group_nr]++;
6563 AmoebaCnt2[new_group_nr]++;
6565 /* if amoeba touches other amoeba(s) after growing, unify them */
6566 AmoebenVereinigen(newax, neway);
6568 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6570 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6576 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6577 (neway == lev_fieldy - 1 && newax != ax))
6579 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6580 Store[newax][neway] = element;
6582 else if (neway == ay)
6584 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6586 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6588 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6593 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6594 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6595 Store[ax][ay] = EL_AMOEBA_DROP;
6596 ContinueMoving(ax, ay);
6600 DrawLevelField(newax, neway);
6603 void Life(int ax, int ay)
6606 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6608 int element = Feld[ax][ay];
6609 int graphic = el2img(element);
6610 boolean changed = FALSE;
6612 if (IS_ANIMATED(graphic))
6613 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6618 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6619 MovDelay[ax][ay] = life_time;
6621 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6624 if (MovDelay[ax][ay])
6628 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6630 int xx = ax+x1, yy = ay+y1;
6633 if (!IN_LEV_FIELD(xx, yy))
6636 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6638 int x = xx+x2, y = yy+y2;
6640 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6643 if (((Feld[x][y] == element ||
6644 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6646 (IS_FREE(x, y) && Stop[x][y]))
6650 if (xx == ax && yy == ay) /* field in the middle */
6652 if (nachbarn < life[0] || nachbarn > life[1])
6654 Feld[xx][yy] = EL_EMPTY;
6656 DrawLevelField(xx, yy);
6657 Stop[xx][yy] = TRUE;
6662 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6663 { /* free border field */
6664 if (nachbarn >= life[2] && nachbarn <= life[3])
6666 Feld[xx][yy] = element;
6667 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6669 DrawLevelField(xx, yy);
6670 Stop[xx][yy] = TRUE;
6675 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6676 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6677 { /* free border field */
6678 if (nachbarn >= life[2] && nachbarn <= life[3])
6680 Feld[xx][yy] = element;
6681 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6683 DrawLevelField(xx, yy);
6684 Stop[xx][yy] = TRUE;
6692 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6693 SND_GAME_OF_LIFE_GROWING);
6696 static void InitRobotWheel(int x, int y)
6698 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6701 static void RunRobotWheel(int x, int y)
6703 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6706 static void StopRobotWheel(int x, int y)
6708 if (ZX == x && ZY == y)
6712 static void InitTimegateWheel(int x, int y)
6715 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6717 /* another brainless, "type style" bug ... :-( */
6718 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6722 static void RunTimegateWheel(int x, int y)
6724 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6727 void CheckExit(int x, int y)
6729 if (local_player->gems_still_needed > 0 ||
6730 local_player->sokobanfields_still_needed > 0 ||
6731 local_player->lights_still_needed > 0)
6733 int element = Feld[x][y];
6734 int graphic = el2img(element);
6736 if (IS_ANIMATED(graphic))
6737 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6742 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6745 Feld[x][y] = EL_EXIT_OPENING;
6747 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6750 void CheckExitSP(int x, int y)
6752 if (local_player->gems_still_needed > 0)
6754 int element = Feld[x][y];
6755 int graphic = el2img(element);
6757 if (IS_ANIMATED(graphic))
6758 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6763 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6766 Feld[x][y] = EL_SP_EXIT_OPENING;
6768 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6771 static void CloseAllOpenTimegates()
6775 for (y = 0; y < lev_fieldy; y++)
6777 for (x = 0; x < lev_fieldx; x++)
6779 int element = Feld[x][y];
6781 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6783 Feld[x][y] = EL_TIMEGATE_CLOSING;
6785 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6787 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6794 void EdelsteinFunkeln(int x, int y)
6796 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6799 if (Feld[x][y] == EL_BD_DIAMOND)
6802 if (MovDelay[x][y] == 0) /* next animation frame */
6803 MovDelay[x][y] = 11 * !SimpleRND(500);
6805 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6809 if (setup.direct_draw && MovDelay[x][y])
6810 SetDrawtoField(DRAW_BUFFERED);
6812 DrawLevelElementAnimation(x, y, Feld[x][y]);
6814 if (MovDelay[x][y] != 0)
6816 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6817 10 - MovDelay[x][y]);
6819 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6821 if (setup.direct_draw)
6825 dest_x = FX + SCREENX(x) * TILEX;
6826 dest_y = FY + SCREENY(y) * TILEY;
6828 BlitBitmap(drawto_field, window,
6829 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6830 SetDrawtoField(DRAW_DIRECT);
6836 void MauerWaechst(int x, int y)
6840 if (!MovDelay[x][y]) /* next animation frame */
6841 MovDelay[x][y] = 3 * delay;
6843 if (MovDelay[x][y]) /* wait some time before next frame */
6847 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6849 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6850 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6852 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6855 if (!MovDelay[x][y])
6857 if (MovDir[x][y] == MV_LEFT)
6859 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6860 DrawLevelField(x - 1, y);
6862 else if (MovDir[x][y] == MV_RIGHT)
6864 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6865 DrawLevelField(x + 1, y);
6867 else if (MovDir[x][y] == MV_UP)
6869 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6870 DrawLevelField(x, y - 1);
6874 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6875 DrawLevelField(x, y + 1);
6878 Feld[x][y] = Store[x][y];
6880 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6881 DrawLevelField(x, y);
6886 void MauerAbleger(int ax, int ay)
6888 int element = Feld[ax][ay];
6889 int graphic = el2img(element);
6890 boolean oben_frei = FALSE, unten_frei = FALSE;
6891 boolean links_frei = FALSE, rechts_frei = FALSE;
6892 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6893 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6894 boolean new_wall = FALSE;
6896 if (IS_ANIMATED(graphic))
6897 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6899 if (!MovDelay[ax][ay]) /* start building new wall */
6900 MovDelay[ax][ay] = 6;
6902 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6905 if (MovDelay[ax][ay])
6909 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6911 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6913 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6915 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6918 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6919 element == EL_EXPANDABLE_WALL_ANY)
6923 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6924 Store[ax][ay-1] = element;
6925 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6926 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6927 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6928 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6933 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6934 Store[ax][ay+1] = element;
6935 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6936 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6937 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6938 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6943 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6944 element == EL_EXPANDABLE_WALL_ANY ||
6945 element == EL_EXPANDABLE_WALL)
6949 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6950 Store[ax-1][ay] = element;
6951 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6952 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6953 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6954 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6960 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6961 Store[ax+1][ay] = element;
6962 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6963 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6964 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6965 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6970 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6971 DrawLevelField(ax, ay);
6973 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6975 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6976 unten_massiv = TRUE;
6977 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6978 links_massiv = TRUE;
6979 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6980 rechts_massiv = TRUE;
6982 if (((oben_massiv && unten_massiv) ||
6983 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6984 element == EL_EXPANDABLE_WALL) &&
6985 ((links_massiv && rechts_massiv) ||
6986 element == EL_EXPANDABLE_WALL_VERTICAL))
6987 Feld[ax][ay] = EL_WALL;
6991 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6993 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6997 void CheckForDragon(int x, int y)
7000 boolean dragon_found = FALSE;
7001 static int xy[4][2] =
7009 for (i = 0; i < NUM_DIRECTIONS; i++)
7011 for (j = 0; j < 4; j++)
7013 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7015 if (IN_LEV_FIELD(xx, yy) &&
7016 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7018 if (Feld[xx][yy] == EL_DRAGON)
7019 dragon_found = TRUE;
7028 for (i = 0; i < NUM_DIRECTIONS; i++)
7030 for (j = 0; j < 3; j++)
7032 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7034 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7036 Feld[xx][yy] = EL_EMPTY;
7037 DrawLevelField(xx, yy);
7046 static void InitBuggyBase(int x, int y)
7048 int element = Feld[x][y];
7049 int activating_delay = FRAMES_PER_SECOND / 4;
7052 (element == EL_SP_BUGGY_BASE ?
7053 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7054 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7056 element == EL_SP_BUGGY_BASE_ACTIVE ?
7057 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7060 static void WarnBuggyBase(int x, int y)
7063 static int xy[4][2] =
7071 for (i = 0; i < NUM_DIRECTIONS; i++)
7073 int xx = x + xy[i][0], yy = y + xy[i][1];
7075 if (IS_PLAYER(xx, yy))
7077 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7084 static void InitTrap(int x, int y)
7086 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7089 static void ActivateTrap(int x, int y)
7091 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7094 static void ChangeActiveTrap(int x, int y)
7096 int graphic = IMG_TRAP_ACTIVE;
7098 /* if new animation frame was drawn, correct crumbled sand border */
7099 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7100 DrawLevelFieldCrumbledSand(x, y);
7103 static void ChangeElementNowExt(int x, int y, int target_element)
7105 int previous_move_direction = MovDir[x][y];
7107 /* check if element under player changes from accessible to unaccessible
7108 (needed for special case of dropping element which then changes) */
7109 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7110 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7117 Feld[x][y] = target_element;
7119 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7121 ResetGfxAnimation(x, y);
7122 ResetRandomAnimationValue(x, y);
7124 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7125 MovDir[x][y] = previous_move_direction;
7128 InitField_WithBug1(x, y, FALSE);
7130 InitField(x, y, FALSE);
7131 if (CAN_MOVE(Feld[x][y]))
7135 DrawLevelField(x, y);
7137 if (GFX_CRUMBLED(Feld[x][y]))
7138 DrawLevelFieldCrumbledSandNeighbours(x, y);
7140 TestIfBadThingTouchesHero(x, y);
7141 TestIfPlayerTouchesCustomElement(x, y);
7142 TestIfElementTouchesCustomElement(x, y);
7144 if (ELEM_IS_PLAYER(target_element))
7145 RelocatePlayer(x, y, target_element);
7148 static boolean ChangeElementNow(int x, int y, int element, int page)
7150 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7153 /* always use default change event to prevent running into a loop */
7154 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7155 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7157 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7159 /* reset actual trigger element and player */
7160 change->actual_trigger_element = EL_EMPTY;
7161 change->actual_trigger_player = EL_PLAYER_1;
7164 /* do not change already changed elements with same change event */
7166 if (Changed[x][y] & ChangeEvent[x][y])
7173 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7175 CheckTriggeredElementChangePage(x,y, Feld[x][y], CE_OTHER_IS_CHANGING, page);
7177 if (change->explode)
7184 if (change->use_target_content)
7186 boolean complete_replace = TRUE;
7187 boolean can_replace[3][3];
7190 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7193 boolean is_diggable;
7194 boolean is_destructible;
7195 int ex = x + xx - 1;
7196 int ey = y + yy - 1;
7197 int content_element = change->target_content[xx][yy];
7200 can_replace[xx][yy] = TRUE;
7202 if (ex == x && ey == y) /* do not check changing element itself */
7205 if (content_element == EL_EMPTY_SPACE)
7207 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7212 if (!IN_LEV_FIELD(ex, ey))
7214 can_replace[xx][yy] = FALSE;
7215 complete_replace = FALSE;
7222 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7223 e = MovingOrBlocked2Element(ex, ey);
7226 is_empty = (IS_FREE(ex, ey) || (IS_PLAYER(ex, ey) &&
7227 IS_WALKABLE(content_element)));
7228 is_diggable = (is_empty || IS_DIGGABLE(e));
7229 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7231 can_replace[xx][yy] =
7232 ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7233 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7234 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7236 if (!can_replace[xx][yy])
7237 complete_replace = FALSE;
7239 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7240 IS_WALKABLE(content_element)));
7242 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7244 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7247 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7248 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7249 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7251 can_replace[xx][yy] = FALSE;
7252 complete_replace = FALSE;
7257 if (!change->only_if_complete || complete_replace)
7259 boolean something_has_changed = FALSE;
7261 if (change->only_if_complete && change->use_random_replace &&
7262 RND(100) < change->random_percentage)
7265 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7267 int ex = x + xx - 1;
7268 int ey = y + yy - 1;
7269 int content_element;
7271 if (can_replace[xx][yy] && (!change->use_random_replace ||
7272 RND(100) < change->random_percentage))
7274 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7275 RemoveMovingField(ex, ey);
7277 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7279 content_element = change->target_content[xx][yy];
7280 target_element = GET_TARGET_ELEMENT(content_element, change);
7282 ChangeElementNowExt(ex, ey, target_element);
7284 something_has_changed = TRUE;
7286 /* for symmetry reasons, freeze newly created border elements */
7287 if (ex != x || ey != y)
7288 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7292 if (something_has_changed)
7293 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7298 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7300 ChangeElementNowExt(x, y, target_element);
7302 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7308 static void ChangeElement(int x, int y, int page)
7310 int element = MovingOrBlocked2Element(x, y);
7311 struct ElementInfo *ei = &element_info[element];
7312 struct ElementChangeInfo *change = &ei->change_page[page];
7315 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7318 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7319 x, y, element, element_info[element].token_name);
7320 printf("ChangeElement(): This should never happen!\n");
7325 /* this can happen with classic bombs on walkable, changing elements */
7326 if (!CAN_CHANGE(element))
7329 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7330 ChangeDelay[x][y] = 0;
7336 if (ChangeDelay[x][y] == 0) /* initialize element change */
7338 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7339 RND(change->delay_random * change->delay_frames)) + 1;
7341 ResetGfxAnimation(x, y);
7342 ResetRandomAnimationValue(x, y);
7344 if (change->pre_change_function)
7345 change->pre_change_function(x, y);
7348 ChangeDelay[x][y]--;
7350 if (ChangeDelay[x][y] != 0) /* continue element change */
7352 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7354 if (IS_ANIMATED(graphic))
7355 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7357 if (change->change_function)
7358 change->change_function(x, y);
7360 else /* finish element change */
7362 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7364 page = ChangePage[x][y];
7365 ChangePage[x][y] = -1;
7367 change = &ei->change_page[page];
7371 if (IS_MOVING(x, y) && !change->explode)
7373 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7376 ChangeDelay[x][y] = 1; /* try change after next move step */
7377 ChangePage[x][y] = page; /* remember page to use for change */
7382 if (ChangeElementNow(x, y, element, page))
7384 if (change->post_change_function)
7385 change->post_change_function(x, y);
7390 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7391 int trigger_element,
7398 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7400 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7403 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7405 int element = EL_CUSTOM_START + i;
7407 boolean change_element = FALSE;
7410 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7413 for (j = 0; j < element_info[element].num_change_pages; j++)
7415 struct ElementChangeInfo *change = &element_info[element].change_page[j];
7417 if (change->can_change &&
7418 change->events & CH_EVENT_BIT(trigger_event) &&
7419 change->trigger_side & trigger_side &&
7420 change->trigger_player & trigger_player &&
7421 change->trigger_page & trigger_page_bits &&
7422 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7425 if (!(change->events & CH_EVENT_BIT(trigger_event)))
7426 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7427 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7430 change_element = TRUE;
7433 change->actual_trigger_element = trigger_element;
7434 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7440 if (!change_element)
7443 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7446 if (x == lx && y == ly) /* do not change trigger element itself */
7450 if (Feld[x][y] == element)
7452 ChangeDelay[x][y] = 1;
7453 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7454 ChangeElement(x, y, page);
7462 static boolean CheckElementChangeExt(int x, int y,
7464 int trigger_element,
7470 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7473 if (Feld[x][y] == EL_BLOCKED)
7475 Blocked2Moving(x, y, &x, &y);
7476 element = Feld[x][y];
7480 if (Feld[x][y] != element) /* check if element has already changed */
7483 printf("::: %d ('%s') != %d ('%s') [%d]\n",
7484 Feld[x][y], element_info[Feld[x][y]].token_name,
7485 element, element_info[element].token_name,
7494 if (trigger_page < 0)
7496 boolean change_element = FALSE;
7499 for (i = 0; i < element_info[element].num_change_pages; i++)
7501 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7503 if (change->can_change &&
7504 change->events & CH_EVENT_BIT(trigger_event) &&
7505 change->trigger_side & trigger_side &&
7506 change->trigger_player & trigger_player)
7508 change_element = TRUE;
7511 change->actual_trigger_element = trigger_element;
7512 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7518 if (!change_element)
7523 struct ElementInfo *ei = &element_info[element];
7524 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7526 change->actual_trigger_element = trigger_element;
7527 change->actual_trigger_player = EL_PLAYER_1; /* unused */
7532 /* !!! this check misses pages with same event, but different side !!! */
7534 if (trigger_page < 0)
7535 trigger_page = element_info[element].event_page_nr[trigger_event];
7537 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7541 ChangeDelay[x][y] = 1;
7542 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7543 ChangeElement(x, y, trigger_page);
7548 static void PlayPlayerSound(struct PlayerInfo *player)
7550 int jx = player->jx, jy = player->jy;
7551 int element = player->element_nr;
7552 int last_action = player->last_action_waiting;
7553 int action = player->action_waiting;
7555 if (player->is_waiting)
7557 if (action != last_action)
7558 PlayLevelSoundElementAction(jx, jy, element, action);
7560 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7564 if (action != last_action)
7565 StopSound(element_info[element].sound[last_action]);
7567 if (last_action == ACTION_SLEEPING)
7568 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7572 static void PlayAllPlayersSound()
7576 for (i = 0; i < MAX_PLAYERS; i++)
7577 if (stored_player[i].active)
7578 PlayPlayerSound(&stored_player[i]);
7581 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7583 boolean last_waiting = player->is_waiting;
7584 int move_dir = player->MovDir;
7586 player->last_action_waiting = player->action_waiting;
7590 if (!last_waiting) /* not waiting -> waiting */
7592 player->is_waiting = TRUE;
7594 player->frame_counter_bored =
7596 game.player_boring_delay_fixed +
7597 SimpleRND(game.player_boring_delay_random);
7598 player->frame_counter_sleeping =
7600 game.player_sleeping_delay_fixed +
7601 SimpleRND(game.player_sleeping_delay_random);
7603 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7606 if (game.player_sleeping_delay_fixed +
7607 game.player_sleeping_delay_random > 0 &&
7608 player->anim_delay_counter == 0 &&
7609 player->post_delay_counter == 0 &&
7610 FrameCounter >= player->frame_counter_sleeping)
7611 player->is_sleeping = TRUE;
7612 else if (game.player_boring_delay_fixed +
7613 game.player_boring_delay_random > 0 &&
7614 FrameCounter >= player->frame_counter_bored)
7615 player->is_bored = TRUE;
7617 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7618 player->is_bored ? ACTION_BORING :
7621 if (player->is_sleeping)
7623 if (player->num_special_action_sleeping > 0)
7625 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7627 int last_special_action = player->special_action_sleeping;
7628 int num_special_action = player->num_special_action_sleeping;
7629 int special_action =
7630 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7631 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7632 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7633 last_special_action + 1 : ACTION_SLEEPING);
7634 int special_graphic =
7635 el_act_dir2img(player->element_nr, special_action, move_dir);
7637 player->anim_delay_counter =
7638 graphic_info[special_graphic].anim_delay_fixed +
7639 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7640 player->post_delay_counter =
7641 graphic_info[special_graphic].post_delay_fixed +
7642 SimpleRND(graphic_info[special_graphic].post_delay_random);
7644 player->special_action_sleeping = special_action;
7647 if (player->anim_delay_counter > 0)
7649 player->action_waiting = player->special_action_sleeping;
7650 player->anim_delay_counter--;
7652 else if (player->post_delay_counter > 0)
7654 player->post_delay_counter--;
7658 else if (player->is_bored)
7660 if (player->num_special_action_bored > 0)
7662 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7664 int special_action =
7665 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7666 int special_graphic =
7667 el_act_dir2img(player->element_nr, special_action, move_dir);
7669 player->anim_delay_counter =
7670 graphic_info[special_graphic].anim_delay_fixed +
7671 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7672 player->post_delay_counter =
7673 graphic_info[special_graphic].post_delay_fixed +
7674 SimpleRND(graphic_info[special_graphic].post_delay_random);
7676 player->special_action_bored = special_action;
7679 if (player->anim_delay_counter > 0)
7681 player->action_waiting = player->special_action_bored;
7682 player->anim_delay_counter--;
7684 else if (player->post_delay_counter > 0)
7686 player->post_delay_counter--;
7691 else if (last_waiting) /* waiting -> not waiting */
7693 player->is_waiting = FALSE;
7694 player->is_bored = FALSE;
7695 player->is_sleeping = FALSE;
7697 player->frame_counter_bored = -1;
7698 player->frame_counter_sleeping = -1;
7700 player->anim_delay_counter = 0;
7701 player->post_delay_counter = 0;
7703 player->action_waiting = ACTION_DEFAULT;
7705 player->special_action_bored = ACTION_DEFAULT;
7706 player->special_action_sleeping = ACTION_DEFAULT;
7711 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7714 static byte stored_player_action[MAX_PLAYERS];
7715 static int num_stored_actions = 0;
7717 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7718 int left = player_action & JOY_LEFT;
7719 int right = player_action & JOY_RIGHT;
7720 int up = player_action & JOY_UP;
7721 int down = player_action & JOY_DOWN;
7722 int button1 = player_action & JOY_BUTTON_1;
7723 int button2 = player_action & JOY_BUTTON_2;
7724 int dx = (left ? -1 : right ? 1 : 0);
7725 int dy = (up ? -1 : down ? 1 : 0);
7728 stored_player_action[player->index_nr] = 0;
7729 num_stored_actions++;
7733 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7736 if (!player->active || tape.pausing)
7740 printf("::: [%d %d %d %d] [%d %d]\n",
7741 left, right, up, down, button1, button2);
7747 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7752 if (player->MovPos == 0)
7753 CheckGravityMovement(player);
7756 snapped = SnapField(player, dx, dy);
7760 dropped = DropElement(player);
7762 moved = MovePlayer(player, dx, dy);
7765 if (tape.single_step && tape.recording && !tape.pausing)
7767 if (button1 || (dropped && !moved))
7769 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7770 SnapField(player, 0, 0); /* stop snapping */
7774 SetPlayerWaiting(player, FALSE);
7777 return player_action;
7779 stored_player_action[player->index_nr] = player_action;
7785 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7788 /* no actions for this player (no input at player's configured device) */
7790 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7791 SnapField(player, 0, 0);
7792 CheckGravityMovementWhenNotMoving(player);
7794 if (player->MovPos == 0)
7795 SetPlayerWaiting(player, TRUE);
7797 if (player->MovPos == 0) /* needed for tape.playing */
7798 player->is_moving = FALSE;
7800 player->is_dropping = FALSE;
7806 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7808 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7810 TapeRecordAction(stored_player_action);
7811 num_stored_actions = 0;
7818 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7820 static byte stored_player_action[MAX_PLAYERS];
7821 static int num_stored_actions = 0;
7822 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7823 int left = player_action & JOY_LEFT;
7824 int right = player_action & JOY_RIGHT;
7825 int up = player_action & JOY_UP;
7826 int down = player_action & JOY_DOWN;
7827 int button1 = player_action & JOY_BUTTON_1;
7828 int button2 = player_action & JOY_BUTTON_2;
7829 int dx = (left ? -1 : right ? 1 : 0);
7830 int dy = (up ? -1 : down ? 1 : 0);
7832 stored_player_action[player->index_nr] = 0;
7833 num_stored_actions++;
7835 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7837 if (!player->active || tape.pausing)
7842 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7845 snapped = SnapField(player, dx, dy);
7849 dropped = DropElement(player);
7851 moved = MovePlayer(player, dx, dy);
7854 if (tape.single_step && tape.recording && !tape.pausing)
7856 if (button1 || (dropped && !moved))
7858 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7859 SnapField(player, 0, 0); /* stop snapping */
7863 stored_player_action[player->index_nr] = player_action;
7867 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7869 /* no actions for this player (no input at player's configured device) */
7871 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7872 SnapField(player, 0, 0);
7873 CheckGravityMovementWhenNotMoving(player);
7875 if (player->MovPos == 0)
7876 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7878 if (player->MovPos == 0) /* needed for tape.playing */
7879 player->is_moving = FALSE;
7882 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7884 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7886 TapeRecordAction(stored_player_action);
7887 num_stored_actions = 0;
7894 static unsigned long action_delay = 0;
7895 unsigned long action_delay_value;
7896 int magic_wall_x = 0, magic_wall_y = 0;
7897 int i, x, y, element, graphic;
7898 byte *recorded_player_action;
7899 byte summarized_player_action = 0;
7901 byte tape_action[MAX_PLAYERS];
7904 if (game_status != GAME_MODE_PLAYING)
7907 action_delay_value =
7908 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7910 if (tape.playing && tape.warp_forward && !tape.pausing)
7911 action_delay_value = 0;
7913 /* ---------- main game synchronization point ---------- */
7915 WaitUntilDelayReached(&action_delay, action_delay_value);
7917 if (network_playing && !network_player_action_received)
7921 printf("DEBUG: try to get network player actions in time\n");
7925 #if defined(PLATFORM_UNIX)
7926 /* last chance to get network player actions without main loop delay */
7930 if (game_status != GAME_MODE_PLAYING)
7933 if (!network_player_action_received)
7937 printf("DEBUG: failed to get network player actions in time\n");
7948 printf("::: getting new tape action [%d]\n", FrameCounter);
7951 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7954 if (recorded_player_action == NULL && tape.pausing)
7959 printf("::: %d\n", stored_player[0].action);
7963 if (recorded_player_action != NULL)
7964 for (i = 0; i < MAX_PLAYERS; i++)
7965 stored_player[i].action = recorded_player_action[i];
7968 for (i = 0; i < MAX_PLAYERS; i++)
7970 summarized_player_action |= stored_player[i].action;
7972 if (!network_playing)
7973 stored_player[i].effective_action = stored_player[i].action;
7976 #if defined(PLATFORM_UNIX)
7977 if (network_playing)
7978 SendToServer_MovePlayer(summarized_player_action);
7981 if (!options.network && !setup.team_mode)
7982 local_player->effective_action = summarized_player_action;
7985 if (recorded_player_action != NULL)
7986 for (i = 0; i < MAX_PLAYERS; i++)
7987 stored_player[i].effective_action = recorded_player_action[i];
7991 for (i = 0; i < MAX_PLAYERS; i++)
7993 tape_action[i] = stored_player[i].effective_action;
7995 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7996 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7999 /* only save actions from input devices, but not programmed actions */
8001 TapeRecordAction(tape_action);
8004 for (i = 0; i < MAX_PLAYERS; i++)
8006 int actual_player_action = stored_player[i].effective_action;
8009 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8010 - rnd_equinox_tetrachloride 048
8011 - rnd_equinox_tetrachloride_ii 096
8012 - rnd_emanuel_schmieg 002
8013 - doctor_sloan_ww 001, 020
8015 if (stored_player[i].MovPos == 0)
8016 CheckGravityMovement(&stored_player[i]);
8020 /* overwrite programmed action with tape action */
8021 if (stored_player[i].programmed_action)
8022 actual_player_action = stored_player[i].programmed_action;
8026 if (stored_player[i].programmed_action)
8027 printf("::: %d\n", stored_player[i].programmed_action);
8030 if (recorded_player_action)
8033 if (stored_player[i].programmed_action &&
8034 stored_player[i].programmed_action != recorded_player_action[i])
8035 printf("::: %d: %d <-> %d\n", i,
8036 stored_player[i].programmed_action, recorded_player_action[i]);
8040 actual_player_action = recorded_player_action[i];
8045 /* overwrite tape action with programmed action */
8046 if (stored_player[i].programmed_action)
8047 actual_player_action = stored_player[i].programmed_action;
8052 printf("::: action: %d: %x [%d]\n",
8053 stored_player[i].MovPos, actual_player_action, FrameCounter);
8057 PlayerActions(&stored_player[i], actual_player_action);
8059 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8061 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8062 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8065 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8070 TapeRecordAction(tape_action);
8073 network_player_action_received = FALSE;
8075 ScrollScreen(NULL, SCROLL_GO_ON);
8081 for (i = 0; i < MAX_PLAYERS; i++)
8082 stored_player[i].Frame++;
8086 /* for downwards compatibility, the following code emulates a fixed bug that
8087 occured when pushing elements (causing elements that just made their last
8088 pushing step to already (if possible) make their first falling step in the
8089 same game frame, which is bad); this code is also needed to use the famous
8090 "spring push bug" which is used in older levels and might be wanted to be
8091 used also in newer levels, but in this case the buggy pushing code is only
8092 affecting the "spring" element and no other elements */
8095 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8097 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8100 for (i = 0; i < MAX_PLAYERS; i++)
8102 struct PlayerInfo *player = &stored_player[i];
8107 if (player->active && player->is_pushing && player->is_moving &&
8109 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8110 Feld[x][y] == EL_SPRING))
8112 if (player->active && player->is_pushing && player->is_moving &&
8116 ContinueMoving(x, y);
8118 /* continue moving after pushing (this is actually a bug) */
8119 if (!IS_MOVING(x, y))
8128 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8130 Changed[x][y] = CE_BITMASK_DEFAULT;
8131 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8134 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8136 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8137 printf("GameActions(): This should never happen!\n");
8139 ChangePage[x][y] = -1;
8144 if (WasJustMoving[x][y] > 0)
8145 WasJustMoving[x][y]--;
8146 if (WasJustFalling[x][y] > 0)
8147 WasJustFalling[x][y]--;
8152 /* reset finished pushing action (not done in ContinueMoving() to allow
8153 continous pushing animation for elements with zero push delay) */
8154 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8156 ResetGfxAnimation(x, y);
8157 DrawLevelField(x, y);
8162 if (IS_BLOCKED(x, y))
8166 Blocked2Moving(x, y, &oldx, &oldy);
8167 if (!IS_MOVING(oldx, oldy))
8169 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8170 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8171 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8172 printf("GameActions(): This should never happen!\n");
8178 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8180 element = Feld[x][y];
8182 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8184 graphic = el2img(element);
8190 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8192 element = graphic = 0;
8196 if (graphic_info[graphic].anim_global_sync)
8197 GfxFrame[x][y] = FrameCounter;
8199 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8200 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8201 ResetRandomAnimationValue(x, y);
8203 SetRandomAnimationValue(x, y);
8206 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8209 if (IS_INACTIVE(element))
8211 if (IS_ANIMATED(graphic))
8212 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8218 /* this may take place after moving, so 'element' may have changed */
8220 if (IS_CHANGING(x, y))
8222 if (IS_CHANGING(x, y) &&
8223 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8227 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8228 element_info[element].event_page_nr[CE_DELAY]);
8230 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8233 element = Feld[x][y];
8234 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8238 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8243 element = Feld[x][y];
8244 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8246 if (element == EL_MOLE)
8247 printf("::: %d, %d, %d [%d]\n",
8248 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8252 if (element == EL_YAMYAM)
8253 printf("::: %d, %d, %d\n",
8254 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8258 if (IS_ANIMATED(graphic) &&
8262 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8265 if (element == EL_BUG)
8266 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8270 if (element == EL_MOLE)
8271 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8275 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8276 EdelsteinFunkeln(x, y);
8278 else if ((element == EL_ACID ||
8279 element == EL_EXIT_OPEN ||
8280 element == EL_SP_EXIT_OPEN ||
8281 element == EL_SP_TERMINAL ||
8282 element == EL_SP_TERMINAL_ACTIVE ||
8283 element == EL_EXTRA_TIME ||
8284 element == EL_SHIELD_NORMAL ||
8285 element == EL_SHIELD_DEADLY) &&
8286 IS_ANIMATED(graphic))
8287 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8288 else if (IS_MOVING(x, y))
8289 ContinueMoving(x, y);
8290 else if (IS_ACTIVE_BOMB(element))
8291 CheckDynamite(x, y);
8293 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8294 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8296 else if (element == EL_AMOEBA_GROWING)
8297 AmoebeWaechst(x, y);
8298 else if (element == EL_AMOEBA_SHRINKING)
8299 AmoebaDisappearing(x, y);
8301 #if !USE_NEW_AMOEBA_CODE
8302 else if (IS_AMOEBALIVE(element))
8303 AmoebeAbleger(x, y);
8306 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8308 else if (element == EL_EXIT_CLOSED)
8310 else if (element == EL_SP_EXIT_CLOSED)
8312 else if (element == EL_EXPANDABLE_WALL_GROWING)
8314 else if (element == EL_EXPANDABLE_WALL ||
8315 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8316 element == EL_EXPANDABLE_WALL_VERTICAL ||
8317 element == EL_EXPANDABLE_WALL_ANY)
8319 else if (element == EL_FLAMES)
8320 CheckForDragon(x, y);
8322 else if (IS_AUTO_CHANGING(element))
8323 ChangeElement(x, y);
8325 else if (element == EL_EXPLOSION)
8326 ; /* drawing of correct explosion animation is handled separately */
8327 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8328 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8331 /* this may take place after moving, so 'element' may have changed */
8332 if (IS_AUTO_CHANGING(Feld[x][y]))
8333 ChangeElement(x, y);
8336 if (IS_BELT_ACTIVE(element))
8337 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8339 if (game.magic_wall_active)
8341 int jx = local_player->jx, jy = local_player->jy;
8343 /* play the element sound at the position nearest to the player */
8344 if ((element == EL_MAGIC_WALL_FULL ||
8345 element == EL_MAGIC_WALL_ACTIVE ||
8346 element == EL_MAGIC_WALL_EMPTYING ||
8347 element == EL_BD_MAGIC_WALL_FULL ||
8348 element == EL_BD_MAGIC_WALL_ACTIVE ||
8349 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8350 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8358 #if USE_NEW_AMOEBA_CODE
8359 /* new experimental amoeba growth stuff */
8361 if (!(FrameCounter % 8))
8364 static unsigned long random = 1684108901;
8366 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8369 x = (random >> 10) % lev_fieldx;
8370 y = (random >> 20) % lev_fieldy;
8372 x = RND(lev_fieldx);
8373 y = RND(lev_fieldy);
8375 element = Feld[x][y];
8378 if (!IS_PLAYER(x,y) &&
8379 (element == EL_EMPTY ||
8380 CAN_GROW_INTO(element) ||
8381 element == EL_QUICKSAND_EMPTY ||
8382 element == EL_ACID_SPLASH_LEFT ||
8383 element == EL_ACID_SPLASH_RIGHT))
8385 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8386 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8387 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8388 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8389 Feld[x][y] = EL_AMOEBA_DROP;
8392 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8393 if (!IS_PLAYER(x,y) &&
8394 (element == EL_EMPTY ||
8395 element == EL_SAND ||
8396 element == EL_QUICKSAND_EMPTY ||
8397 element == EL_ACID_SPLASH_LEFT ||
8398 element == EL_ACID_SPLASH_RIGHT))
8400 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8401 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8402 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8403 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8404 Feld[x][y] = EL_AMOEBA_DROP;
8408 random = random * 129 + 1;
8414 if (game.explosions_delayed)
8417 game.explosions_delayed = FALSE;
8419 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8421 element = Feld[x][y];
8423 if (ExplodeField[x][y])
8424 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8425 else if (element == EL_EXPLOSION)
8426 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8428 ExplodeField[x][y] = EX_TYPE_NONE;
8431 game.explosions_delayed = TRUE;
8434 if (game.magic_wall_active)
8436 if (!(game.magic_wall_time_left % 4))
8438 int element = Feld[magic_wall_x][magic_wall_y];
8440 if (element == EL_BD_MAGIC_WALL_FULL ||
8441 element == EL_BD_MAGIC_WALL_ACTIVE ||
8442 element == EL_BD_MAGIC_WALL_EMPTYING)
8443 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8445 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8448 if (game.magic_wall_time_left > 0)
8450 game.magic_wall_time_left--;
8451 if (!game.magic_wall_time_left)
8453 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8455 element = Feld[x][y];
8457 if (element == EL_MAGIC_WALL_ACTIVE ||
8458 element == EL_MAGIC_WALL_FULL)
8460 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8461 DrawLevelField(x, y);
8463 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8464 element == EL_BD_MAGIC_WALL_FULL)
8466 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8467 DrawLevelField(x, y);
8471 game.magic_wall_active = FALSE;
8476 if (game.light_time_left > 0)
8478 game.light_time_left--;
8480 if (game.light_time_left == 0)
8481 RedrawAllLightSwitchesAndInvisibleElements();
8484 if (game.timegate_time_left > 0)
8486 game.timegate_time_left--;
8488 if (game.timegate_time_left == 0)
8489 CloseAllOpenTimegates();
8492 for (i = 0; i < MAX_PLAYERS; i++)
8494 struct PlayerInfo *player = &stored_player[i];
8496 if (SHIELD_ON(player))
8498 if (player->shield_deadly_time_left)
8499 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8500 else if (player->shield_normal_time_left)
8501 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8505 if (TimeFrames >= FRAMES_PER_SECOND)
8510 if (!level.use_step_counter)
8514 for (i = 0; i < MAX_PLAYERS; i++)
8516 struct PlayerInfo *player = &stored_player[i];
8518 if (SHIELD_ON(player))
8520 player->shield_normal_time_left--;
8522 if (player->shield_deadly_time_left > 0)
8523 player->shield_deadly_time_left--;
8531 if (TimeLeft <= 10 && setup.time_limit)
8532 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8534 DrawGameValue_Time(TimeLeft);
8536 if (!TimeLeft && setup.time_limit)
8537 for (i = 0; i < MAX_PLAYERS; i++)
8538 KillHero(&stored_player[i]);
8540 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8541 DrawGameValue_Time(TimePlayed);
8544 if (tape.recording || tape.playing)
8545 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8549 PlayAllPlayersSound();
8551 if (options.debug) /* calculate frames per second */
8553 static unsigned long fps_counter = 0;
8554 static int fps_frames = 0;
8555 unsigned long fps_delay_ms = Counter() - fps_counter;
8559 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8561 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8564 fps_counter = Counter();
8567 redraw_mask |= REDRAW_FPS;
8571 if (stored_player[0].jx != stored_player[0].last_jx ||
8572 stored_player[0].jy != stored_player[0].last_jy)
8573 printf("::: %d, %d, %d, %d, %d\n",
8574 stored_player[0].MovDir,
8575 stored_player[0].MovPos,
8576 stored_player[0].GfxPos,
8577 stored_player[0].Frame,
8578 stored_player[0].StepFrame);
8585 for (i = 0; i < MAX_PLAYERS; i++)
8588 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8590 stored_player[i].Frame += move_frames;
8592 if (stored_player[i].MovPos != 0)
8593 stored_player[i].StepFrame += move_frames;
8595 if (stored_player[i].drop_delay > 0)
8596 stored_player[i].drop_delay--;
8601 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8603 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8605 local_player->show_envelope = 0;
8610 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8612 int min_x = x, min_y = y, max_x = x, max_y = y;
8615 for (i = 0; i < MAX_PLAYERS; i++)
8617 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8619 if (!stored_player[i].active || &stored_player[i] == player)
8622 min_x = MIN(min_x, jx);
8623 min_y = MIN(min_y, jy);
8624 max_x = MAX(max_x, jx);
8625 max_y = MAX(max_y, jy);
8628 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8631 static boolean AllPlayersInVisibleScreen()
8635 for (i = 0; i < MAX_PLAYERS; i++)
8637 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8639 if (!stored_player[i].active)
8642 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8649 void ScrollLevel(int dx, int dy)
8651 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8654 BlitBitmap(drawto_field, drawto_field,
8655 FX + TILEX * (dx == -1) - softscroll_offset,
8656 FY + TILEY * (dy == -1) - softscroll_offset,
8657 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8658 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8659 FX + TILEX * (dx == 1) - softscroll_offset,
8660 FY + TILEY * (dy == 1) - softscroll_offset);
8664 x = (dx == 1 ? BX1 : BX2);
8665 for (y = BY1; y <= BY2; y++)
8666 DrawScreenField(x, y);
8671 y = (dy == 1 ? BY1 : BY2);
8672 for (x = BX1; x <= BX2; x++)
8673 DrawScreenField(x, y);
8676 redraw_mask |= REDRAW_FIELD;
8680 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8682 int nextx = x + dx, nexty = y + dy;
8683 int element = Feld[x][y];
8686 element != EL_SP_PORT_LEFT &&
8687 element != EL_SP_GRAVITY_PORT_LEFT &&
8688 element != EL_SP_PORT_HORIZONTAL &&
8689 element != EL_SP_PORT_ANY) ||
8691 element != EL_SP_PORT_RIGHT &&
8692 element != EL_SP_GRAVITY_PORT_RIGHT &&
8693 element != EL_SP_PORT_HORIZONTAL &&
8694 element != EL_SP_PORT_ANY) ||
8696 element != EL_SP_PORT_UP &&
8697 element != EL_SP_GRAVITY_PORT_UP &&
8698 element != EL_SP_PORT_VERTICAL &&
8699 element != EL_SP_PORT_ANY) ||
8701 element != EL_SP_PORT_DOWN &&
8702 element != EL_SP_GRAVITY_PORT_DOWN &&
8703 element != EL_SP_PORT_VERTICAL &&
8704 element != EL_SP_PORT_ANY) ||
8705 !IN_LEV_FIELD(nextx, nexty) ||
8706 !IS_FREE(nextx, nexty))
8713 static boolean canFallDown(struct PlayerInfo *player)
8715 int jx = player->jx, jy = player->jy;
8717 return (IN_LEV_FIELD(jx, jy + 1) &&
8718 (IS_FREE(jx, jy + 1) ||
8719 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8720 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8721 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8724 static boolean canPassField(int x, int y, int move_dir)
8726 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8727 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8728 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8731 int element = Feld[x][y];
8733 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8734 !CAN_MOVE(element) &&
8735 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8736 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8737 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8740 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8742 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8743 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8744 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8748 int nextx = newx + dx;
8749 int nexty = newy + dy;
8753 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8754 (IS_DIGGABLE(Feld[newx][newy]) ||
8755 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8756 canPassField(newx, newy, move_dir)));
8758 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8759 (IS_DIGGABLE(Feld[newx][newy]) ||
8760 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8761 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
8762 !CAN_MOVE(Feld[newx][newy]) &&
8763 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8764 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8765 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
8769 static void CheckGravityMovement(struct PlayerInfo *player)
8771 if (game.gravity && !player->programmed_action)
8774 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8775 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8777 int move_dir_horizontal = player->action & MV_HORIZONTAL;
8778 int move_dir_vertical = player->action & MV_VERTICAL;
8782 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8784 boolean player_is_snapping = player->action & JOY_BUTTON_1;
8787 int jx = player->jx, jy = player->jy;
8789 boolean player_is_moving_to_valid_field =
8790 (!player_is_snapping &&
8791 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8792 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8796 (player->last_move_dir & MV_HORIZONTAL ?
8797 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8798 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8802 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8803 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8804 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8805 int new_jx = jx + dx, new_jy = jy + dy;
8806 int nextx = new_jx + dx, nexty = new_jy + dy;
8812 boolean player_can_fall_down = canFallDown(player);
8814 boolean player_can_fall_down =
8815 (IN_LEV_FIELD(jx, jy + 1) &&
8816 (IS_FREE(jx, jy + 1) ||
8817 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
8821 boolean player_can_fall_down =
8822 (IN_LEV_FIELD(jx, jy + 1) &&
8823 (IS_FREE(jx, jy + 1)));
8827 boolean player_is_moving_to_valid_field =
8830 !player_is_snapping &&
8834 IN_LEV_FIELD(new_jx, new_jy) &&
8835 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
8836 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8837 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
8838 IN_LEV_FIELD(nextx, nexty) &&
8839 element_info[Feld[nextx][nexty]].access_direction & move_dir))
8841 IN_LEV_FIELD(new_jx, new_jy) &&
8842 (Feld[new_jx][new_jy] == EL_SP_BASE ||
8843 Feld[new_jx][new_jy] == EL_SAND ||
8844 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8845 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
8846 /* !!! extend EL_SAND to anything diggable !!! */
8852 boolean player_is_standing_on_valid_field =
8853 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8854 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
8858 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
8859 player_can_fall_down,
8860 player_is_standing_on_valid_field,
8861 player_is_moving_to_valid_field,
8862 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
8863 player->effective_action,
8864 player->can_fall_into_acid);
8867 if (player_can_fall_down &&
8869 !player_is_standing_on_valid_field &&
8871 !player_is_moving_to_valid_field)
8874 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
8875 jx, jy, FrameCounter);
8878 player->programmed_action = MV_DOWN;
8883 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8886 return CheckGravityMovement(player);
8889 if (game.gravity && !player->programmed_action)
8891 int jx = player->jx, jy = player->jy;
8892 boolean field_under_player_is_free =
8893 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8894 boolean player_is_standing_on_valid_field =
8895 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8896 (IS_WALKABLE(Feld[jx][jy]) &&
8897 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8899 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8900 player->programmed_action = MV_DOWN;
8906 -----------------------------------------------------------------------------
8907 dx, dy: direction (non-diagonal) to try to move the player to
8908 real_dx, real_dy: direction as read from input device (can be diagonal)
8911 boolean MovePlayerOneStep(struct PlayerInfo *player,
8912 int dx, int dy, int real_dx, int real_dy)
8915 static int trigger_sides[4][2] =
8917 /* enter side leave side */
8918 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8919 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8920 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8921 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8923 int move_direction = (dx == -1 ? MV_LEFT :
8924 dx == +1 ? MV_RIGHT :
8926 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8927 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8928 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8930 int jx = player->jx, jy = player->jy;
8931 int new_jx = jx + dx, new_jy = jy + dy;
8935 if (!player->active || (!dx && !dy))
8936 return MF_NO_ACTION;
8938 player->MovDir = (dx < 0 ? MV_LEFT :
8941 dy > 0 ? MV_DOWN : MV_NO_MOVING);
8943 if (!IN_LEV_FIELD(new_jx, new_jy))
8944 return MF_NO_ACTION;
8946 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8947 return MF_NO_ACTION;
8950 element = MovingOrBlocked2Element(new_jx, new_jy);
8952 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8955 if (DONT_RUN_INTO(element))
8957 if (element == EL_ACID && dx == 0 && dy == 1)
8959 SplashAcid(new_jx, new_jy);
8960 Feld[jx][jy] = EL_PLAYER_1;
8961 InitMovingField(jx, jy, MV_DOWN);
8962 Store[jx][jy] = EL_ACID;
8963 ContinueMoving(jx, jy);
8967 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8972 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8973 if (can_move != MF_MOVING)
8976 /* check if DigField() has caused relocation of the player */
8977 if (player->jx != jx || player->jy != jy)
8978 return MF_NO_ACTION;
8980 StorePlayer[jx][jy] = 0;
8981 player->last_jx = jx;
8982 player->last_jy = jy;
8983 player->jx = new_jx;
8984 player->jy = new_jy;
8985 StorePlayer[new_jx][new_jy] = player->element_nr;
8988 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8990 player->step_counter++;
8993 player->drop_delay = 0;
8996 PlayerVisit[jx][jy] = FrameCounter;
8998 ScrollPlayer(player, SCROLL_INIT);
9001 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9003 CheckTriggeredElementChangeSide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9005 CheckElementChangeSide(jx, jy, Feld[jx][jy], CE_LEFT_BY_PLAYER,leave_side);
9008 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9010 CheckTriggeredElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
9011 CE_OTHER_GETS_ENTERED, enter_side);
9012 CheckElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
9013 CE_ENTERED_BY_PLAYER, enter_side);
9020 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9022 int jx = player->jx, jy = player->jy;
9023 int old_jx = jx, old_jy = jy;
9024 int moved = MF_NO_ACTION;
9027 if (!player->active)
9032 if (player->MovPos == 0)
9034 player->is_moving = FALSE;
9035 player->is_digging = FALSE;
9036 player->is_collecting = FALSE;
9037 player->is_snapping = FALSE;
9038 player->is_pushing = FALSE;
9044 if (!player->active || (!dx && !dy))
9049 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9055 if (!FrameReached(&player->move_delay, player->move_delay_value))
9058 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9059 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9065 /* store if player is automatically moved to next field */
9066 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9068 /* remove the last programmed player action */
9069 player->programmed_action = 0;
9073 /* should only happen if pre-1.2 tape recordings are played */
9074 /* this is only for backward compatibility */
9076 int original_move_delay_value = player->move_delay_value;
9079 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9083 /* scroll remaining steps with finest movement resolution */
9084 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9086 while (player->MovPos)
9088 ScrollPlayer(player, SCROLL_GO_ON);
9089 ScrollScreen(NULL, SCROLL_GO_ON);
9095 player->move_delay_value = original_move_delay_value;
9098 if (player->last_move_dir & MV_HORIZONTAL)
9100 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9101 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9105 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9106 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9112 if (moved & MF_MOVING && !ScreenMovPos &&
9113 (player == local_player || !options.network))
9115 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9116 int offset = (setup.scroll_delay ? 3 : 0);
9118 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9120 /* actual player has left the screen -- scroll in that direction */
9121 if (jx != old_jx) /* player has moved horizontally */
9122 scroll_x += (jx - old_jx);
9123 else /* player has moved vertically */
9124 scroll_y += (jy - old_jy);
9128 if (jx != old_jx) /* player has moved horizontally */
9130 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9131 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9132 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9134 /* don't scroll over playfield boundaries */
9135 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9136 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9138 /* don't scroll more than one field at a time */
9139 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9141 /* don't scroll against the player's moving direction */
9142 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9143 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9144 scroll_x = old_scroll_x;
9146 else /* player has moved vertically */
9148 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9149 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9150 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9152 /* don't scroll over playfield boundaries */
9153 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9154 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9156 /* don't scroll more than one field at a time */
9157 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9159 /* don't scroll against the player's moving direction */
9160 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9161 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9162 scroll_y = old_scroll_y;
9166 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9168 if (!options.network && !AllPlayersInVisibleScreen())
9170 scroll_x = old_scroll_x;
9171 scroll_y = old_scroll_y;
9175 ScrollScreen(player, SCROLL_INIT);
9176 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9183 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9185 if (!(moved & MF_MOVING) && !player->is_pushing)
9190 player->StepFrame = 0;
9192 if (moved & MF_MOVING)
9194 if (old_jx != jx && old_jy == jy)
9195 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9196 else if (old_jx == jx && old_jy != jy)
9197 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9199 DrawLevelField(jx, jy); /* for "crumbled sand" */
9201 player->last_move_dir = player->MovDir;
9202 player->is_moving = TRUE;
9204 player->is_snapping = FALSE;
9208 player->is_switching = FALSE;
9211 player->is_dropping = FALSE;
9215 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9218 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9221 static int trigger_sides[4][2] =
9223 /* enter side leave side */
9224 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9225 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9226 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9227 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9229 int move_direction = player->MovDir;
9230 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9231 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9234 CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9236 player->index_bit, leave_side);
9238 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9239 CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9241 player->index_bit, leave_side);
9243 CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
9244 CE_OTHER_GETS_ENTERED,
9245 player->index_bit, enter_side);
9247 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9248 CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9249 player->index_bit, enter_side);
9259 CheckGravityMovementWhenNotMoving(player);
9262 player->last_move_dir = MV_NO_MOVING;
9264 player->is_moving = FALSE;
9267 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9269 TestIfHeroTouchesBadThing(jx, jy);
9270 TestIfPlayerTouchesCustomElement(jx, jy);
9273 if (!player->active)
9279 void ScrollPlayer(struct PlayerInfo *player, int mode)
9281 int jx = player->jx, jy = player->jy;
9282 int last_jx = player->last_jx, last_jy = player->last_jy;
9283 int move_stepsize = TILEX / player->move_delay_value;
9285 if (!player->active || !player->MovPos)
9288 if (mode == SCROLL_INIT)
9290 player->actual_frame_counter = FrameCounter;
9291 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9293 if (Feld[last_jx][last_jy] == EL_EMPTY)
9294 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9302 else if (!FrameReached(&player->actual_frame_counter, 1))
9305 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9306 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9308 if (!player->block_last_field &&
9309 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9310 Feld[last_jx][last_jy] = EL_EMPTY;
9312 /* before DrawPlayer() to draw correct player graphic for this case */
9313 if (player->MovPos == 0)
9314 CheckGravityMovement(player);
9317 DrawPlayer(player); /* needed here only to cleanup last field */
9320 if (player->MovPos == 0) /* player reached destination field */
9323 if (player->move_delay_reset_counter > 0)
9325 player->move_delay_reset_counter--;
9327 if (player->move_delay_reset_counter == 0)
9329 /* continue with normal speed after quickly moving through gate */
9330 HALVE_PLAYER_SPEED(player);
9332 /* be able to make the next move without delay */
9333 player->move_delay = 0;
9337 if (IS_PASSABLE(Feld[last_jx][last_jy]))
9339 /* continue with normal speed after quickly moving through gate */
9340 HALVE_PLAYER_SPEED(player);
9342 /* be able to make the next move without delay */
9343 player->move_delay = 0;
9347 if (player->block_last_field &&
9348 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9349 Feld[last_jx][last_jy] = EL_EMPTY;
9351 player->last_jx = jx;
9352 player->last_jy = jy;
9354 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9355 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9356 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9358 DrawPlayer(player); /* needed here only to cleanup last field */
9361 if (local_player->friends_still_needed == 0 ||
9362 IS_SP_ELEMENT(Feld[jx][jy]))
9363 player->LevelSolved = player->GameOver = TRUE;
9367 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9368 /* this breaks one level: "machine", level 000 */
9370 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9373 static int trigger_sides[4][2] =
9375 /* enter side leave side */
9376 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9377 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9378 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9379 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9381 int move_direction = player->MovDir;
9382 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9383 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9384 int old_jx = last_jx;
9385 int old_jy = last_jy;
9388 /* !!! TEST ONLY !!! */
9389 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9390 CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9392 player->index_bit, leave_side);
9394 CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9396 player->index_bit, leave_side);
9398 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9399 CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9400 player->index_bit, enter_side);
9402 CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
9403 CE_OTHER_GETS_ENTERED,
9404 player->index_bit, enter_side);
9410 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9412 TestIfHeroTouchesBadThing(jx, jy);
9413 TestIfPlayerTouchesCustomElement(jx, jy);
9415 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9418 if (!player->active)
9422 if (level.use_step_counter)
9428 for (i = 0; i < MAX_PLAYERS; i++)
9430 struct PlayerInfo *player = &stored_player[i];
9432 if (SHIELD_ON(player))
9434 player->shield_normal_time_left--;
9436 if (player->shield_deadly_time_left > 0)
9437 player->shield_deadly_time_left--;
9445 if (TimeLeft <= 10 && setup.time_limit)
9446 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9448 DrawGameValue_Time(TimeLeft);
9450 if (!TimeLeft && setup.time_limit)
9451 for (i = 0; i < MAX_PLAYERS; i++)
9452 KillHero(&stored_player[i]);
9454 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9455 DrawGameValue_Time(TimePlayed);
9458 if (tape.single_step && tape.recording && !tape.pausing &&
9459 !player->programmed_action)
9460 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9464 void ScrollScreen(struct PlayerInfo *player, int mode)
9466 static unsigned long screen_frame_counter = 0;
9468 if (mode == SCROLL_INIT)
9470 /* set scrolling step size according to actual player's moving speed */
9471 ScrollStepSize = TILEX / player->move_delay_value;
9473 screen_frame_counter = FrameCounter;
9474 ScreenMovDir = player->MovDir;
9475 ScreenMovPos = player->MovPos;
9476 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9479 else if (!FrameReached(&screen_frame_counter, 1))
9484 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9485 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9486 redraw_mask |= REDRAW_FIELD;
9489 ScreenMovDir = MV_NO_MOVING;
9492 void TestIfPlayerTouchesCustomElement(int x, int y)
9494 static int xy[4][2] =
9501 static int trigger_sides[4][2] =
9503 /* center side border side */
9504 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9505 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9506 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9507 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9509 static int touch_dir[4] =
9516 int center_element = Feld[x][y]; /* should always be non-moving! */
9519 for (i = 0; i < NUM_DIRECTIONS; i++)
9521 int xx = x + xy[i][0];
9522 int yy = y + xy[i][1];
9523 int center_side = trigger_sides[i][0];
9524 int border_side = trigger_sides[i][1];
9527 if (!IN_LEV_FIELD(xx, yy))
9530 if (IS_PLAYER(x, y))
9532 struct PlayerInfo *player = PLAYERINFO(x, y);
9534 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9535 border_element = Feld[xx][yy]; /* may be moving! */
9536 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9537 border_element = Feld[xx][yy];
9538 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9539 border_element = MovingOrBlocked2Element(xx, yy);
9541 continue; /* center and border element do not touch */
9544 /* !!! TEST ONLY !!! */
9545 CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9546 player->index_bit, border_side);
9547 CheckTriggeredElementChangePlayer(xx, yy, border_element,
9548 CE_OTHER_GETS_TOUCHED,
9549 player->index_bit, border_side);
9551 CheckTriggeredElementChangePlayer(xx, yy, border_element,
9552 CE_OTHER_GETS_TOUCHED,
9553 player->index_bit, border_side);
9554 CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9555 player->index_bit, border_side);
9558 else if (IS_PLAYER(xx, yy))
9560 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9562 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9564 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9565 continue; /* center and border element do not touch */
9569 /* !!! TEST ONLY !!! */
9570 CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9571 player->index_bit, center_side);
9572 CheckTriggeredElementChangePlayer(x, y, center_element,
9573 CE_OTHER_GETS_TOUCHED,
9574 player->index_bit, center_side);
9576 CheckTriggeredElementChangePlayer(x, y, center_element,
9577 CE_OTHER_GETS_TOUCHED,
9578 player->index_bit, center_side);
9579 CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9580 player->index_bit, center_side);
9588 void TestIfElementTouchesCustomElement(int x, int y)
9590 static int xy[4][2] =
9597 static int trigger_sides[4][2] =
9599 /* center side border side */
9600 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9601 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9602 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9603 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9605 static int touch_dir[4] =
9612 boolean change_center_element = FALSE;
9613 int center_element_change_page = 0;
9614 int center_element = Feld[x][y]; /* should always be non-moving! */
9615 int border_trigger_element;
9618 for (i = 0; i < NUM_DIRECTIONS; i++)
9620 int xx = x + xy[i][0];
9621 int yy = y + xy[i][1];
9622 int center_side = trigger_sides[i][0];
9623 int border_side = trigger_sides[i][1];
9626 if (!IN_LEV_FIELD(xx, yy))
9629 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9630 border_element = Feld[xx][yy]; /* may be moving! */
9631 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9632 border_element = Feld[xx][yy];
9633 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9634 border_element = MovingOrBlocked2Element(xx, yy);
9636 continue; /* center and border element do not touch */
9638 /* check for change of center element (but change it only once) */
9639 if (IS_CUSTOM_ELEMENT(center_element) &&
9640 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9641 !change_center_element)
9643 for (j = 0; j < element_info[center_element].num_change_pages; j++)
9645 struct ElementChangeInfo *change =
9646 &element_info[center_element].change_page[j];
9648 if (change->can_change &&
9649 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9650 change->trigger_side & border_side &&
9652 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9654 change->trigger_element == border_element
9658 change_center_element = TRUE;
9659 center_element_change_page = j;
9660 border_trigger_element = border_element;
9667 /* check for change of border element */
9668 if (IS_CUSTOM_ELEMENT(border_element) &&
9669 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9671 for (j = 0; j < element_info[border_element].num_change_pages; j++)
9673 struct ElementChangeInfo *change =
9674 &element_info[border_element].change_page[j];
9676 if (change->can_change &&
9677 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9678 change->trigger_side & center_side &&
9680 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9682 change->trigger_element == center_element
9687 printf("::: border_element %d, %d\n", x, y);
9690 CheckElementChangePage(xx, yy, border_element, center_element,
9691 CE_OTHER_IS_TOUCHING, j);
9698 if (change_center_element)
9701 printf("::: center_element %d, %d\n", x, y);
9704 CheckElementChangePage(x, y, center_element, border_trigger_element,
9705 CE_OTHER_IS_TOUCHING, center_element_change_page);
9709 void TestIfElementHitsCustomElement(int x, int y, int direction)
9711 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9712 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9713 int hitx = x + dx, hity = y + dy;
9714 int hitting_element = Feld[x][y];
9715 int touched_element;
9717 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9718 !IS_FREE(hitx, hity) &&
9719 (!IS_MOVING(hitx, hity) ||
9720 MovDir[hitx][hity] != direction ||
9721 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9724 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9728 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9732 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9733 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9735 CheckElementChangeSide(x, y, hitting_element, touched_element,
9736 CE_HITTING_SOMETHING, direction);
9738 if (IN_LEV_FIELD(hitx, hity))
9740 int opposite_direction = MV_DIR_OPPOSITE(direction);
9741 int hitting_side = direction;
9742 int touched_side = opposite_direction;
9744 int touched_element = MovingOrBlocked2Element(hitx, hity);
9747 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9748 MovDir[hitx][hity] != direction ||
9749 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9758 CheckElementChangeSide(hitx, hity, touched_element, hitting_element,
9759 CE_HIT_BY_SOMETHING, opposite_direction);
9761 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9762 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
9764 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9766 struct ElementChangeInfo *change =
9767 &element_info[hitting_element].change_page[i];
9769 if (change->can_change &&
9770 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
9771 change->trigger_side & touched_side &&
9774 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9776 change->trigger_element == touched_element
9780 CheckElementChangePage(x, y, hitting_element, touched_element,
9781 CE_OTHER_IS_HITTING, i);
9787 if (IS_CUSTOM_ELEMENT(touched_element) &&
9788 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
9790 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9792 struct ElementChangeInfo *change =
9793 &element_info[touched_element].change_page[i];
9795 if (change->can_change &&
9796 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
9797 change->trigger_side & hitting_side &&
9799 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9801 change->trigger_element == hitting_element
9805 CheckElementChangePage(hitx, hity, touched_element,
9806 hitting_element, CE_OTHER_GETS_HIT, i);
9816 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9818 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9819 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9820 int hitx = x + dx, hity = y + dy;
9821 int hitting_element = Feld[x][y];
9822 int touched_element;
9824 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9825 !IS_FREE(hitx, hity) &&
9826 (!IS_MOVING(hitx, hity) ||
9827 MovDir[hitx][hity] != direction ||
9828 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9831 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9835 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9839 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9840 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9842 CheckElementChangeSide(x, y, hitting_element, touched_element,
9843 EP_CAN_SMASH_EVERYTHING, direction);
9845 if (IN_LEV_FIELD(hitx, hity))
9847 int opposite_direction = MV_DIR_OPPOSITE(direction);
9848 int hitting_side = direction;
9849 int touched_side = opposite_direction;
9851 int touched_element = MovingOrBlocked2Element(hitx, hity);
9854 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9855 MovDir[hitx][hity] != direction ||
9856 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9865 CheckElementChangeSide(hitx, hity, touched_element, hitting_element,
9866 CE_SMASHED_BY_SOMETHING, opposite_direction);
9868 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9869 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
9871 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9873 struct ElementChangeInfo *change =
9874 &element_info[hitting_element].change_page[i];
9876 if (change->can_change &&
9877 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
9878 change->trigger_side & touched_side &&
9881 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9883 change->trigger_element == touched_element
9887 CheckElementChangePage(x, y, hitting_element, touched_element,
9888 CE_OTHER_IS_SMASHING, i);
9894 if (IS_CUSTOM_ELEMENT(touched_element) &&
9895 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
9897 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9899 struct ElementChangeInfo *change =
9900 &element_info[touched_element].change_page[i];
9902 if (change->can_change &&
9903 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
9904 change->trigger_side & hitting_side &&
9906 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9908 change->trigger_element == hitting_element
9912 CheckElementChangePage(hitx, hity, touched_element,
9913 hitting_element, CE_OTHER_GETS_SMASHED, i);
9923 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9925 int i, kill_x = -1, kill_y = -1;
9926 int bad_element = -1;
9927 static int test_xy[4][2] =
9934 static int test_dir[4] =
9942 for (i = 0; i < NUM_DIRECTIONS; i++)
9944 int test_x, test_y, test_move_dir, test_element;
9946 test_x = good_x + test_xy[i][0];
9947 test_y = good_y + test_xy[i][1];
9949 if (!IN_LEV_FIELD(test_x, test_y))
9953 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9956 test_element = Feld[test_x][test_y];
9958 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9961 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9962 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9964 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9965 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9969 bad_element = test_element;
9975 if (kill_x != -1 || kill_y != -1)
9977 if (IS_PLAYER(good_x, good_y))
9979 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9982 if (player->shield_deadly_time_left > 0 &&
9983 !IS_INDESTRUCTIBLE(bad_element))
9984 Bang(kill_x, kill_y);
9985 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9988 if (player->shield_deadly_time_left > 0)
9989 Bang(kill_x, kill_y);
9990 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9995 Bang(good_x, good_y);
9999 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10001 int i, kill_x = -1, kill_y = -1;
10002 int bad_element = Feld[bad_x][bad_y];
10003 static int test_xy[4][2] =
10010 static int touch_dir[4] =
10012 MV_LEFT | MV_RIGHT,
10017 static int test_dir[4] =
10025 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10028 for (i = 0; i < NUM_DIRECTIONS; i++)
10030 int test_x, test_y, test_move_dir, test_element;
10032 test_x = bad_x + test_xy[i][0];
10033 test_y = bad_y + test_xy[i][1];
10034 if (!IN_LEV_FIELD(test_x, test_y))
10038 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10040 test_element = Feld[test_x][test_y];
10042 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10043 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10045 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10046 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10048 /* good thing is player or penguin that does not move away */
10049 if (IS_PLAYER(test_x, test_y))
10051 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10053 if (bad_element == EL_ROBOT && player->is_moving)
10054 continue; /* robot does not kill player if he is moving */
10056 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10058 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10059 continue; /* center and border element do not touch */
10066 else if (test_element == EL_PENGUIN)
10075 if (kill_x != -1 || kill_y != -1)
10077 if (IS_PLAYER(kill_x, kill_y))
10079 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10082 if (player->shield_deadly_time_left > 0 &&
10083 !IS_INDESTRUCTIBLE(bad_element))
10084 Bang(bad_x, bad_y);
10085 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10088 if (player->shield_deadly_time_left > 0)
10089 Bang(bad_x, bad_y);
10090 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10095 Bang(kill_x, kill_y);
10099 void TestIfHeroTouchesBadThing(int x, int y)
10101 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10104 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10106 TestIfGoodThingHitsBadThing(x, y, move_dir);
10109 void TestIfBadThingTouchesHero(int x, int y)
10111 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10114 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10116 TestIfBadThingHitsGoodThing(x, y, move_dir);
10119 void TestIfFriendTouchesBadThing(int x, int y)
10121 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10124 void TestIfBadThingTouchesFriend(int x, int y)
10126 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10129 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10131 int i, kill_x = bad_x, kill_y = bad_y;
10132 static int xy[4][2] =
10140 for (i = 0; i < NUM_DIRECTIONS; i++)
10144 x = bad_x + xy[i][0];
10145 y = bad_y + xy[i][1];
10146 if (!IN_LEV_FIELD(x, y))
10149 element = Feld[x][y];
10150 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10151 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10159 if (kill_x != bad_x || kill_y != bad_y)
10160 Bang(bad_x, bad_y);
10163 void KillHero(struct PlayerInfo *player)
10165 int jx = player->jx, jy = player->jy;
10167 if (!player->active)
10170 /* remove accessible field at the player's position */
10171 Feld[jx][jy] = EL_EMPTY;
10173 /* deactivate shield (else Bang()/Explode() would not work right) */
10174 player->shield_normal_time_left = 0;
10175 player->shield_deadly_time_left = 0;
10181 static void KillHeroUnlessEnemyProtected(int x, int y)
10183 if (!PLAYER_ENEMY_PROTECTED(x, y))
10184 KillHero(PLAYERINFO(x, y));
10187 static void KillHeroUnlessExplosionProtected(int x, int y)
10189 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10190 KillHero(PLAYERINFO(x, y));
10193 void BuryHero(struct PlayerInfo *player)
10195 int jx = player->jx, jy = player->jy;
10197 if (!player->active)
10201 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10203 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10205 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10207 player->GameOver = TRUE;
10208 RemoveHero(player);
10211 void RemoveHero(struct PlayerInfo *player)
10213 int jx = player->jx, jy = player->jy;
10214 int i, found = FALSE;
10216 player->present = FALSE;
10217 player->active = FALSE;
10219 if (!ExplodeField[jx][jy])
10220 StorePlayer[jx][jy] = 0;
10222 for (i = 0; i < MAX_PLAYERS; i++)
10223 if (stored_player[i].active)
10227 AllPlayersGone = TRUE;
10234 =============================================================================
10235 checkDiagonalPushing()
10236 -----------------------------------------------------------------------------
10237 check if diagonal input device direction results in pushing of object
10238 (by checking if the alternative direction is walkable, diggable, ...)
10239 =============================================================================
10242 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10243 int x, int y, int real_dx, int real_dy)
10245 int jx, jy, dx, dy, xx, yy;
10247 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10250 /* diagonal direction: check alternative direction */
10255 xx = jx + (dx == 0 ? real_dx : 0);
10256 yy = jy + (dy == 0 ? real_dy : 0);
10258 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10262 =============================================================================
10264 -----------------------------------------------------------------------------
10265 x, y: field next to player (non-diagonal) to try to dig to
10266 real_dx, real_dy: direction as read from input device (can be diagonal)
10267 =============================================================================
10270 int DigField(struct PlayerInfo *player,
10271 int oldx, int oldy, int x, int y,
10272 int real_dx, int real_dy, int mode)
10274 static int trigger_sides[4] =
10276 CH_SIDE_RIGHT, /* moving left */
10277 CH_SIDE_LEFT, /* moving right */
10278 CH_SIDE_BOTTOM, /* moving up */
10279 CH_SIDE_TOP, /* moving down */
10282 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10284 int jx = oldx, jy = oldy;
10285 int dx = x - jx, dy = y - jy;
10286 int nextx = x + dx, nexty = y + dy;
10287 int move_direction = (dx == -1 ? MV_LEFT :
10288 dx == +1 ? MV_RIGHT :
10290 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10291 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10292 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10293 int old_element = Feld[jx][jy];
10296 if (player->MovPos == 0)
10298 player->is_digging = FALSE;
10299 player->is_collecting = FALSE;
10302 if (player->MovPos == 0) /* last pushing move finished */
10303 player->is_pushing = FALSE;
10305 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10307 player->is_switching = FALSE;
10308 player->push_delay = 0;
10310 return MF_NO_ACTION;
10313 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10314 return MF_NO_ACTION;
10319 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10321 if (IS_TUBE(Feld[jx][jy]) ||
10322 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10326 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10327 int tube_leave_directions[][2] =
10329 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10330 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10331 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10332 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
10333 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
10334 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
10335 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
10336 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
10337 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
10338 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
10339 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
10340 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10343 while (tube_leave_directions[i][0] != tube_element)
10346 if (tube_leave_directions[i][0] == -1) /* should not happen */
10350 if (!(tube_leave_directions[i][1] & move_direction))
10351 return MF_NO_ACTION; /* tube has no opening in this direction */
10356 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10357 old_element = Back[jx][jy];
10361 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10362 return MF_NO_ACTION; /* field has no opening in this direction */
10364 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10365 return MF_NO_ACTION; /* field has no opening in this direction */
10367 element = Feld[x][y];
10369 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10370 game.engine_version >= VERSION_IDENT(2,2,0,0))
10371 return MF_NO_ACTION;
10376 case EL_SP_PORT_LEFT:
10377 case EL_SP_PORT_RIGHT:
10378 case EL_SP_PORT_UP:
10379 case EL_SP_PORT_DOWN:
10380 case EL_SP_PORT_HORIZONTAL:
10381 case EL_SP_PORT_VERTICAL:
10382 case EL_SP_PORT_ANY:
10383 case EL_SP_GRAVITY_PORT_LEFT:
10384 case EL_SP_GRAVITY_PORT_RIGHT:
10385 case EL_SP_GRAVITY_PORT_UP:
10386 case EL_SP_GRAVITY_PORT_DOWN:
10388 if (!canEnterSupaplexPort(x, y, dx, dy))
10389 return MF_NO_ACTION;
10392 element != EL_SP_PORT_LEFT &&
10393 element != EL_SP_GRAVITY_PORT_LEFT &&
10394 element != EL_SP_PORT_HORIZONTAL &&
10395 element != EL_SP_PORT_ANY) ||
10397 element != EL_SP_PORT_RIGHT &&
10398 element != EL_SP_GRAVITY_PORT_RIGHT &&
10399 element != EL_SP_PORT_HORIZONTAL &&
10400 element != EL_SP_PORT_ANY) ||
10402 element != EL_SP_PORT_UP &&
10403 element != EL_SP_GRAVITY_PORT_UP &&
10404 element != EL_SP_PORT_VERTICAL &&
10405 element != EL_SP_PORT_ANY) ||
10407 element != EL_SP_PORT_DOWN &&
10408 element != EL_SP_GRAVITY_PORT_DOWN &&
10409 element != EL_SP_PORT_VERTICAL &&
10410 element != EL_SP_PORT_ANY) ||
10411 !IN_LEV_FIELD(nextx, nexty) ||
10412 !IS_FREE(nextx, nexty))
10413 return MF_NO_ACTION;
10416 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10417 element == EL_SP_GRAVITY_PORT_RIGHT ||
10418 element == EL_SP_GRAVITY_PORT_UP ||
10419 element == EL_SP_GRAVITY_PORT_DOWN)
10420 game.gravity = !game.gravity;
10422 /* automatically move to the next field with double speed */
10423 player->programmed_action = move_direction;
10425 if (player->move_delay_reset_counter == 0)
10427 player->move_delay_reset_counter = 2; /* two double speed steps */
10429 DOUBLE_PLAYER_SPEED(player);
10432 player->move_delay_reset_counter = 2;
10434 DOUBLE_PLAYER_SPEED(player);
10438 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10441 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10447 case EL_TUBE_VERTICAL:
10448 case EL_TUBE_HORIZONTAL:
10449 case EL_TUBE_VERTICAL_LEFT:
10450 case EL_TUBE_VERTICAL_RIGHT:
10451 case EL_TUBE_HORIZONTAL_UP:
10452 case EL_TUBE_HORIZONTAL_DOWN:
10453 case EL_TUBE_LEFT_UP:
10454 case EL_TUBE_LEFT_DOWN:
10455 case EL_TUBE_RIGHT_UP:
10456 case EL_TUBE_RIGHT_DOWN:
10459 int tube_enter_directions[][2] =
10461 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10462 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10463 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10464 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
10465 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
10466 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
10467 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
10468 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
10469 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
10470 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
10471 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
10472 { -1, MV_NO_MOVING }
10475 while (tube_enter_directions[i][0] != element)
10478 if (tube_enter_directions[i][0] == -1) /* should not happen */
10482 if (!(tube_enter_directions[i][1] & move_direction))
10483 return MF_NO_ACTION; /* tube has no opening in this direction */
10485 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10492 if (IS_WALKABLE(element))
10494 int sound_action = ACTION_WALKING;
10496 if (!ACCESS_FROM(element, opposite_direction))
10497 return MF_NO_ACTION; /* field not accessible from this direction */
10500 if (element == EL_EMPTY_SPACE &&
10501 game.gravity && !player->is_auto_moving &&
10502 canFallDown(player) && move_direction != MV_DOWN)
10503 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10506 if (IS_GATE(element))
10508 if (!player->key[element - EL_GATE_1])
10509 return MF_NO_ACTION;
10511 else if (IS_GATE_GRAY(element))
10513 if (!player->key[element - EL_GATE_1_GRAY])
10514 return MF_NO_ACTION;
10516 else if (element == EL_EXIT_OPEN ||
10517 element == EL_SP_EXIT_OPEN ||
10518 element == EL_SP_EXIT_OPENING)
10520 sound_action = ACTION_PASSING; /* player is passing exit */
10522 else if (element == EL_EMPTY)
10524 sound_action = ACTION_MOVING; /* nothing to walk on */
10527 /* play sound from background or player, whatever is available */
10528 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
10529 PlayLevelSoundElementAction(x, y, element, sound_action);
10531 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10535 else if (IS_PASSABLE(element))
10538 if (!canPassField(x, y, move_direction))
10539 return MF_NO_ACTION;
10543 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
10544 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
10545 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
10546 return MF_NO_ACTION;
10548 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10549 return MF_NO_ACTION;
10553 if (!ACCESS_FROM(element, opposite_direction))
10554 return MF_NO_ACTION; /* field not accessible from this direction */
10556 if (IS_CUSTOM_ELEMENT(element) &&
10557 !ACCESS_FROM(element, opposite_direction))
10558 return MF_NO_ACTION; /* field not accessible from this direction */
10562 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10563 return MF_NO_ACTION;
10568 if (IS_EM_GATE(element))
10570 if (!player->key[element - EL_EM_GATE_1])
10571 return MF_NO_ACTION;
10573 else if (IS_EM_GATE_GRAY(element))
10575 if (!player->key[element - EL_EM_GATE_1_GRAY])
10576 return MF_NO_ACTION;
10578 else if (IS_SP_PORT(element))
10580 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10581 element == EL_SP_GRAVITY_PORT_RIGHT ||
10582 element == EL_SP_GRAVITY_PORT_UP ||
10583 element == EL_SP_GRAVITY_PORT_DOWN)
10584 game.gravity = !game.gravity;
10587 /* automatically move to the next field with double speed */
10588 player->programmed_action = move_direction;
10590 if (player->move_delay_reset_counter == 0)
10592 player->move_delay_reset_counter = 2; /* two double speed steps */
10594 DOUBLE_PLAYER_SPEED(player);
10597 player->move_delay_reset_counter = 2;
10599 DOUBLE_PLAYER_SPEED(player);
10602 PlayLevelSoundAction(x, y, ACTION_PASSING);
10606 else if (IS_DIGGABLE(element))
10610 if (mode != DF_SNAP)
10613 GfxElement[x][y] = GFX_ELEMENT(element);
10616 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
10618 player->is_digging = TRUE;
10621 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10623 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED,
10624 player->index_bit, dig_side);
10627 if (mode == DF_SNAP)
10628 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10633 else if (IS_COLLECTIBLE(element))
10637 if (mode != DF_SNAP)
10639 GfxElement[x][y] = element;
10640 player->is_collecting = TRUE;
10643 if (element == EL_SPEED_PILL)
10644 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10645 else if (element == EL_EXTRA_TIME && level.time > 0)
10648 DrawGameValue_Time(TimeLeft);
10650 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10652 player->shield_normal_time_left += 10;
10653 if (element == EL_SHIELD_DEADLY)
10654 player->shield_deadly_time_left += 10;
10656 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10658 if (player->inventory_size < MAX_INVENTORY_SIZE)
10659 player->inventory_element[player->inventory_size++] = element;
10661 DrawGameValue_Dynamite(local_player->inventory_size);
10663 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10665 player->dynabomb_count++;
10666 player->dynabombs_left++;
10668 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10670 player->dynabomb_size++;
10672 else if (element == EL_DYNABOMB_INCREASE_POWER)
10674 player->dynabomb_xl = TRUE;
10676 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
10677 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
10679 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
10680 element - EL_KEY_1 : element - EL_EM_KEY_1);
10682 player->key[key_nr] = TRUE;
10684 DrawGameValue_Keys(player);
10686 redraw_mask |= REDRAW_DOOR_1;
10688 else if (IS_ENVELOPE(element))
10691 player->show_envelope = element;
10693 ShowEnvelope(element - EL_ENVELOPE_1);
10696 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
10700 if (element_info[element].collect_count == 0)
10701 player->inventory_infinite_element = element;
10703 for (i = 0; i < element_info[element].collect_count; i++)
10704 if (player->inventory_size < MAX_INVENTORY_SIZE)
10705 player->inventory_element[player->inventory_size++] = element;
10707 DrawGameValue_Dynamite(local_player->inventory_size);
10709 else if (element_info[element].collect_count > 0)
10711 local_player->gems_still_needed -=
10712 element_info[element].collect_count;
10713 if (local_player->gems_still_needed < 0)
10714 local_player->gems_still_needed = 0;
10716 DrawGameValue_Emeralds(local_player->gems_still_needed);
10719 RaiseScoreElement(element);
10720 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10722 CheckTriggeredElementChangePlayer(x, y, element,
10723 CE_OTHER_GETS_COLLECTED,
10724 player->index_bit, dig_side);
10727 if (mode == DF_SNAP)
10728 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10733 else if (IS_PUSHABLE(element))
10735 if (mode == DF_SNAP && element != EL_BD_ROCK)
10736 return MF_NO_ACTION;
10738 if (CAN_FALL(element) && dy)
10739 return MF_NO_ACTION;
10741 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10742 !(element == EL_SPRING && level.use_spring_bug))
10743 return MF_NO_ACTION;
10746 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10747 ((move_direction & MV_VERTICAL &&
10748 ((element_info[element].move_pattern & MV_LEFT &&
10749 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10750 (element_info[element].move_pattern & MV_RIGHT &&
10751 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10752 (move_direction & MV_HORIZONTAL &&
10753 ((element_info[element].move_pattern & MV_UP &&
10754 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10755 (element_info[element].move_pattern & MV_DOWN &&
10756 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10757 return MF_NO_ACTION;
10761 /* do not push elements already moving away faster than player */
10762 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10763 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10764 return MF_NO_ACTION;
10766 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
10767 return MF_NO_ACTION;
10771 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10773 if (player->push_delay_value == -1)
10774 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10776 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10778 if (!player->is_pushing)
10779 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10783 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
10784 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
10785 !player_is_pushing))
10786 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10789 if (!player->is_pushing &&
10790 game.engine_version >= VERSION_IDENT(2,2,0,7))
10791 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10795 printf("::: push delay: %ld [%d, %d] [%d]\n",
10796 player->push_delay_value, FrameCounter, game.engine_version,
10797 player->is_pushing);
10800 player->is_pushing = TRUE;
10802 if (!(IN_LEV_FIELD(nextx, nexty) &&
10803 (IS_FREE(nextx, nexty) ||
10804 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10805 IS_SB_ELEMENT(element)))))
10806 return MF_NO_ACTION;
10808 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10809 return MF_NO_ACTION;
10811 if (player->push_delay == 0) /* new pushing; restart delay */
10812 player->push_delay = FrameCounter;
10814 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
10815 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10816 element != EL_SPRING && element != EL_BALLOON)
10818 /* make sure that there is no move delay before next try to push */
10819 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10820 player->move_delay = INITIAL_MOVE_DELAY_OFF;
10822 return MF_NO_ACTION;
10826 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
10829 if (IS_SB_ELEMENT(element))
10831 if (element == EL_SOKOBAN_FIELD_FULL)
10833 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10834 local_player->sokobanfields_still_needed++;
10837 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10839 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10840 local_player->sokobanfields_still_needed--;
10843 Feld[x][y] = EL_SOKOBAN_OBJECT;
10845 if (Back[x][y] == Back[nextx][nexty])
10846 PlayLevelSoundAction(x, y, ACTION_PUSHING);
10847 else if (Back[x][y] != 0)
10848 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10851 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10854 if (local_player->sokobanfields_still_needed == 0 &&
10855 game.emulation == EMU_SOKOBAN)
10857 player->LevelSolved = player->GameOver = TRUE;
10858 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10862 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10864 InitMovingField(x, y, move_direction);
10865 GfxAction[x][y] = ACTION_PUSHING;
10867 if (mode == DF_SNAP)
10868 ContinueMoving(x, y);
10870 MovPos[x][y] = (dx != 0 ? dx : dy);
10872 Pushed[x][y] = TRUE;
10873 Pushed[nextx][nexty] = TRUE;
10875 if (game.engine_version < VERSION_IDENT(2,2,0,7))
10876 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10878 player->push_delay_value = -1; /* get new value later */
10881 /* !!! TEST ONLY !!! */
10882 CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10883 player->index_bit, dig_side);
10884 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED,
10885 player->index_bit, dig_side);
10887 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED,
10888 player->index_bit, dig_side);
10889 CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10890 player->index_bit, dig_side);
10895 else if (IS_SWITCHABLE(element))
10897 if (PLAYER_SWITCHING(player, x, y))
10900 player->is_switching = TRUE;
10901 player->switch_x = x;
10902 player->switch_y = y;
10904 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10906 if (element == EL_ROBOT_WHEEL)
10908 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10912 DrawLevelField(x, y);
10914 else if (element == EL_SP_TERMINAL)
10918 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
10920 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10922 else if (Feld[xx][yy] == EL_SP_TERMINAL)
10923 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10926 else if (IS_BELT_SWITCH(element))
10928 ToggleBeltSwitch(x, y);
10930 else if (element == EL_SWITCHGATE_SWITCH_UP ||
10931 element == EL_SWITCHGATE_SWITCH_DOWN)
10933 ToggleSwitchgateSwitch(x, y);
10935 else if (element == EL_LIGHT_SWITCH ||
10936 element == EL_LIGHT_SWITCH_ACTIVE)
10938 ToggleLightSwitch(x, y);
10941 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
10942 SND_LIGHT_SWITCH_ACTIVATING :
10943 SND_LIGHT_SWITCH_DEACTIVATING);
10946 else if (element == EL_TIMEGATE_SWITCH)
10948 ActivateTimegateSwitch(x, y);
10950 else if (element == EL_BALLOON_SWITCH_LEFT ||
10951 element == EL_BALLOON_SWITCH_RIGHT ||
10952 element == EL_BALLOON_SWITCH_UP ||
10953 element == EL_BALLOON_SWITCH_DOWN ||
10954 element == EL_BALLOON_SWITCH_ANY)
10956 if (element == EL_BALLOON_SWITCH_ANY)
10957 game.balloon_dir = move_direction;
10959 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10960 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10961 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10962 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10965 else if (element == EL_LAMP)
10967 Feld[x][y] = EL_LAMP_ACTIVE;
10968 local_player->lights_still_needed--;
10970 DrawLevelField(x, y);
10972 else if (element == EL_TIME_ORB_FULL)
10974 Feld[x][y] = EL_TIME_ORB_EMPTY;
10976 DrawGameValue_Time(TimeLeft);
10978 DrawLevelField(x, y);
10981 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
10989 if (!PLAYER_SWITCHING(player, x, y))
10991 player->is_switching = TRUE;
10992 player->switch_x = x;
10993 player->switch_y = y;
10996 /* !!! TEST ONLY !!! */
10997 CheckElementChangePlayer(x, y, element, CE_SWITCHED,
10998 player->index_bit, dig_side);
10999 CheckTriggeredElementChangePlayer(x, y, element,
11000 CE_OTHER_IS_SWITCHING,
11001 player->index_bit, dig_side);
11003 CheckTriggeredElementChangePlayer(x, y, element,
11004 CE_OTHER_IS_SWITCHING,
11005 player->index_bit, dig_side);
11006 CheckElementChangePlayer(x, y, element, CE_SWITCHED,
11007 player->index_bit, dig_side);
11012 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11013 CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11014 player->index_bit, dig_side);
11015 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED,
11016 player->index_bit, dig_side);
11018 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED,
11019 player->index_bit, dig_side);
11020 CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11021 player->index_bit, dig_side);
11025 return MF_NO_ACTION;
11028 player->push_delay = 0;
11030 if (Feld[x][y] != element) /* really digged/collected something */
11031 player->is_collecting = !player->is_digging;
11036 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11038 int jx = player->jx, jy = player->jy;
11039 int x = jx + dx, y = jy + dy;
11040 int snap_direction = (dx == -1 ? MV_LEFT :
11041 dx == +1 ? MV_RIGHT :
11043 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11046 if (player->MovPos)
11049 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
11053 if (!player->active || !IN_LEV_FIELD(x, y))
11061 if (player->MovPos == 0)
11062 player->is_pushing = FALSE;
11064 player->is_snapping = FALSE;
11066 if (player->MovPos == 0)
11068 player->is_moving = FALSE;
11069 player->is_digging = FALSE;
11070 player->is_collecting = FALSE;
11076 if (player->is_snapping)
11079 player->MovDir = snap_direction;
11082 if (player->MovPos == 0)
11085 player->is_moving = FALSE;
11086 player->is_digging = FALSE;
11087 player->is_collecting = FALSE;
11090 player->is_dropping = FALSE;
11092 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11095 player->is_snapping = TRUE;
11098 if (player->MovPos == 0)
11101 player->is_moving = FALSE;
11102 player->is_digging = FALSE;
11103 player->is_collecting = FALSE;
11106 DrawLevelField(x, y);
11112 boolean DropElement(struct PlayerInfo *player)
11114 static int trigger_sides[4] =
11116 CH_SIDE_LEFT, /* dropping left */
11117 CH_SIDE_RIGHT, /* dropping right */
11118 CH_SIDE_TOP, /* dropping up */
11119 CH_SIDE_BOTTOM, /* dropping down */
11121 int jx = player->jx, jy = player->jy;
11122 int drop_direction = player->MovDir;
11123 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11124 int old_element = Feld[jx][jy];
11125 int drop_element = (player->inventory_size > 0 ?
11126 player->inventory_element[player->inventory_size - 1] :
11127 player->inventory_infinite_element != EL_UNDEFINED ?
11128 player->inventory_infinite_element :
11129 player->dynabombs_left > 0 ?
11130 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11132 int new_element = drop_element; /* default: element does not change */
11134 /* check if player is active, not moving and ready to drop */
11135 if (!player->active || player->MovPos || player->drop_delay > 0)
11138 /* check if player has anything that can be dropped */
11140 if (new_element == EL_UNDEFINED)
11143 if (player->inventory_size == 0 &&
11144 player->inventory_infinite_element == EL_UNDEFINED &&
11145 player->dynabombs_left == 0)
11149 /* check if anything can be dropped at the current position */
11150 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11153 /* collected custom elements can only be dropped on empty fields */
11155 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11158 if (player->inventory_size > 0 &&
11159 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11160 && old_element != EL_EMPTY)
11164 if (old_element != EL_EMPTY)
11165 Back[jx][jy] = old_element; /* store old element on this field */
11167 ResetGfxAnimation(jx, jy);
11168 ResetRandomAnimationValue(jx, jy);
11170 if (player->inventory_size > 0 ||
11171 player->inventory_infinite_element != EL_UNDEFINED)
11173 if (player->inventory_size > 0)
11175 player->inventory_size--;
11178 new_element = player->inventory_element[player->inventory_size];
11181 DrawGameValue_Dynamite(local_player->inventory_size);
11183 if (new_element == EL_DYNAMITE)
11184 new_element = EL_DYNAMITE_ACTIVE;
11185 else if (new_element == EL_SP_DISK_RED)
11186 new_element = EL_SP_DISK_RED_ACTIVE;
11189 Feld[jx][jy] = new_element;
11191 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
11192 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
11194 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
11197 /* needed if previous element just changed to "empty" in the last frame */
11198 Changed[jx][jy] = 0; /* allow another change */
11202 /* !!! TEST ONLY !!! */
11203 CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
11204 player->index_bit, drop_side);
11205 CheckTriggeredElementChangePlayer(jx, jy, new_element,
11206 CE_OTHER_GETS_DROPPED,
11207 player->index_bit, drop_side);
11209 CheckTriggeredElementChangePlayer(jx, jy, new_element,
11210 CE_OTHER_GETS_DROPPED,
11211 player->index_bit, drop_side);
11212 CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
11213 player->index_bit, drop_side);
11216 TestIfElementTouchesCustomElement(jx, jy);
11218 else /* player is dropping a dyna bomb */
11220 player->dynabombs_left--;
11223 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
11226 Feld[jx][jy] = new_element;
11228 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
11229 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
11231 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
11238 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
11241 InitField_WithBug1(jx, jy, FALSE);
11243 InitField(jx, jy, FALSE);
11244 if (CAN_MOVE(Feld[jx][jy]))
11245 InitMovDir(jx, jy);
11249 new_element = Feld[jx][jy]; /* element might have changed */
11251 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11252 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11255 int move_stepsize = element_info[new_element].move_stepsize;
11257 int direction, dx, dy, nextx, nexty;
11259 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11260 MovDir[jx][jy] = player->MovDir;
11262 direction = MovDir[jx][jy];
11263 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11264 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11268 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
11271 WasJustMoving[jx][jy] = 3;
11273 InitMovingField(jx, jy, direction);
11274 ContinueMoving(jx, jy);
11279 Changed[jx][jy] = 0; /* allow another change */
11282 TestIfElementHitsCustomElement(jx, jy, direction);
11284 CheckElementChangeSide(jx, jy, new_element, touched_element,
11285 CE_HITTING_SOMETHING, direction);
11290 player->drop_delay = 2 * TILEX / move_stepsize + 1;
11295 player->drop_delay = 8 + 8 + 8;
11299 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11304 player->is_dropping = TRUE;
11310 /* ------------------------------------------------------------------------- */
11311 /* game sound playing functions */
11312 /* ------------------------------------------------------------------------- */
11314 static int *loop_sound_frame = NULL;
11315 static int *loop_sound_volume = NULL;
11317 void InitPlayLevelSound()
11319 int num_sounds = getSoundListSize();
11321 checked_free(loop_sound_frame);
11322 checked_free(loop_sound_volume);
11324 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11325 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11328 static void PlayLevelSound(int x, int y, int nr)
11330 int sx = SCREENX(x), sy = SCREENY(y);
11331 int volume, stereo_position;
11332 int max_distance = 8;
11333 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11335 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11336 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11339 if (!IN_LEV_FIELD(x, y) ||
11340 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11341 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11344 volume = SOUND_MAX_VOLUME;
11346 if (!IN_SCR_FIELD(sx, sy))
11348 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11349 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11351 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11354 stereo_position = (SOUND_MAX_LEFT +
11355 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11356 (SCR_FIELDX + 2 * max_distance));
11358 if (IS_LOOP_SOUND(nr))
11360 /* This assures that quieter loop sounds do not overwrite louder ones,
11361 while restarting sound volume comparison with each new game frame. */
11363 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11366 loop_sound_volume[nr] = volume;
11367 loop_sound_frame[nr] = FrameCounter;
11370 PlaySoundExt(nr, volume, stereo_position, type);
11373 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11375 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11376 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11377 y < LEVELY(BY1) ? LEVELY(BY1) :
11378 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11382 static void PlayLevelSoundAction(int x, int y, int action)
11384 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11387 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11389 int sound_effect = element_info[element].sound[action];
11391 if (sound_effect != SND_UNDEFINED)
11392 PlayLevelSound(x, y, sound_effect);
11395 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11398 int sound_effect = element_info[element].sound[action];
11400 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11401 PlayLevelSound(x, y, sound_effect);
11404 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11406 int sound_effect = element_info[Feld[x][y]].sound[action];
11408 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11409 PlayLevelSound(x, y, sound_effect);
11412 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11414 int sound_effect = element_info[Feld[x][y]].sound[action];
11416 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11417 StopSound(sound_effect);
11420 static void PlayLevelMusic()
11422 if (levelset.music[level_nr] != MUS_UNDEFINED)
11423 PlayMusic(levelset.music[level_nr]); /* from config file */
11425 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11428 void RaiseScore(int value)
11430 local_player->score += value;
11432 DrawGameValue_Score(local_player->score);
11435 void RaiseScoreElement(int element)
11440 case EL_BD_DIAMOND:
11441 case EL_EMERALD_YELLOW:
11442 case EL_EMERALD_RED:
11443 case EL_EMERALD_PURPLE:
11444 case EL_SP_INFOTRON:
11445 RaiseScore(level.score[SC_EMERALD]);
11448 RaiseScore(level.score[SC_DIAMOND]);
11451 RaiseScore(level.score[SC_CRYSTAL]);
11454 RaiseScore(level.score[SC_PEARL]);
11457 case EL_BD_BUTTERFLY:
11458 case EL_SP_ELECTRON:
11459 RaiseScore(level.score[SC_BUG]);
11462 case EL_BD_FIREFLY:
11463 case EL_SP_SNIKSNAK:
11464 RaiseScore(level.score[SC_SPACESHIP]);
11467 case EL_DARK_YAMYAM:
11468 RaiseScore(level.score[SC_YAMYAM]);
11471 RaiseScore(level.score[SC_ROBOT]);
11474 RaiseScore(level.score[SC_PACMAN]);
11477 RaiseScore(level.score[SC_NUT]);
11480 case EL_SP_DISK_RED:
11481 case EL_DYNABOMB_INCREASE_NUMBER:
11482 case EL_DYNABOMB_INCREASE_SIZE:
11483 case EL_DYNABOMB_INCREASE_POWER:
11484 RaiseScore(level.score[SC_DYNAMITE]);
11486 case EL_SHIELD_NORMAL:
11487 case EL_SHIELD_DEADLY:
11488 RaiseScore(level.score[SC_SHIELD]);
11490 case EL_EXTRA_TIME:
11491 RaiseScore(level.score[SC_TIME_BONUS]);
11497 RaiseScore(level.score[SC_KEY]);
11500 RaiseScore(element_info[element].collect_score);
11505 void RequestQuitGame(boolean ask_if_really_quit)
11507 if (AllPlayersGone ||
11508 !ask_if_really_quit ||
11509 level_editor_test_game ||
11510 Request("Do you really want to quit the game ?",
11511 REQ_ASK | REQ_STAY_CLOSED))
11513 #if defined(PLATFORM_UNIX)
11514 if (options.network)
11515 SendToServer_StopPlaying();
11519 game_status = GAME_MODE_MAIN;
11527 if (tape.playing && tape.deactivate_display)
11528 TapeDeactivateDisplayOff(TRUE);
11531 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11534 if (tape.playing && tape.deactivate_display)
11535 TapeDeactivateDisplayOn();
11542 /* ---------- new game button stuff ---------------------------------------- */
11544 /* graphic position values for game buttons */
11545 #define GAME_BUTTON_XSIZE 30
11546 #define GAME_BUTTON_YSIZE 30
11547 #define GAME_BUTTON_XPOS 5
11548 #define GAME_BUTTON_YPOS 215
11549 #define SOUND_BUTTON_XPOS 5
11550 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11552 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11553 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11554 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11555 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11556 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11557 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11564 } gamebutton_info[NUM_GAME_BUTTONS] =
11567 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11572 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11573 GAME_CTRL_ID_PAUSE,
11577 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11582 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11583 SOUND_CTRL_ID_MUSIC,
11584 "background music on/off"
11587 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11588 SOUND_CTRL_ID_LOOPS,
11589 "sound loops on/off"
11592 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11593 SOUND_CTRL_ID_SIMPLE,
11594 "normal sounds on/off"
11598 void CreateGameButtons()
11602 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11604 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11605 struct GadgetInfo *gi;
11608 unsigned long event_mask;
11609 int gd_xoffset, gd_yoffset;
11610 int gd_x1, gd_x2, gd_y1, gd_y2;
11613 gd_xoffset = gamebutton_info[i].x;
11614 gd_yoffset = gamebutton_info[i].y;
11615 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11616 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11618 if (id == GAME_CTRL_ID_STOP ||
11619 id == GAME_CTRL_ID_PAUSE ||
11620 id == GAME_CTRL_ID_PLAY)
11622 button_type = GD_TYPE_NORMAL_BUTTON;
11624 event_mask = GD_EVENT_RELEASED;
11625 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11626 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11630 button_type = GD_TYPE_CHECK_BUTTON;
11632 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11633 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11634 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11635 event_mask = GD_EVENT_PRESSED;
11636 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11637 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11640 gi = CreateGadget(GDI_CUSTOM_ID, id,
11641 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11642 GDI_X, DX + gd_xoffset,
11643 GDI_Y, DY + gd_yoffset,
11644 GDI_WIDTH, GAME_BUTTON_XSIZE,
11645 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11646 GDI_TYPE, button_type,
11647 GDI_STATE, GD_BUTTON_UNPRESSED,
11648 GDI_CHECKED, checked,
11649 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11650 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11651 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11652 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11653 GDI_EVENT_MASK, event_mask,
11654 GDI_CALLBACK_ACTION, HandleGameButtons,
11658 Error(ERR_EXIT, "cannot create gadget");
11660 game_gadget[id] = gi;
11664 void FreeGameButtons()
11668 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11669 FreeGadget(game_gadget[i]);
11672 static void MapGameButtons()
11676 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11677 MapGadget(game_gadget[i]);
11680 void UnmapGameButtons()
11684 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11685 UnmapGadget(game_gadget[i]);
11688 static void HandleGameButtons(struct GadgetInfo *gi)
11690 int id = gi->custom_id;
11692 if (game_status != GAME_MODE_PLAYING)
11697 case GAME_CTRL_ID_STOP:
11698 RequestQuitGame(TRUE);
11701 case GAME_CTRL_ID_PAUSE:
11702 if (options.network)
11704 #if defined(PLATFORM_UNIX)
11706 SendToServer_ContinuePlaying();
11708 SendToServer_PausePlaying();
11712 TapeTogglePause(TAPE_TOGGLE_MANUAL);
11715 case GAME_CTRL_ID_PLAY:
11718 #if defined(PLATFORM_UNIX)
11719 if (options.network)
11720 SendToServer_ContinuePlaying();
11724 tape.pausing = FALSE;
11725 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11730 case SOUND_CTRL_ID_MUSIC:
11731 if (setup.sound_music)
11733 setup.sound_music = FALSE;
11736 else if (audio.music_available)
11738 setup.sound = setup.sound_music = TRUE;
11740 SetAudioMode(setup.sound);
11746 case SOUND_CTRL_ID_LOOPS:
11747 if (setup.sound_loops)
11748 setup.sound_loops = FALSE;
11749 else if (audio.loops_available)
11751 setup.sound = setup.sound_loops = TRUE;
11752 SetAudioMode(setup.sound);
11756 case SOUND_CTRL_ID_SIMPLE:
11757 if (setup.sound_simple)
11758 setup.sound_simple = FALSE;
11759 else if (audio.sound_available)
11761 setup.sound = setup.sound_simple = TRUE;
11762 SetAudioMode(setup.sound);