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_NO_EXPLOSION 0
51 /* special positions in the game control window (relative to control window) */
54 #define XX_EMERALDS 29
55 #define YY_EMERALDS 54
56 #define XX_DYNAMITE 29
57 #define YY_DYNAMITE 89
66 /* special positions in the game control window (relative to main window) */
67 #define DX_LEVEL (DX + XX_LEVEL)
68 #define DY_LEVEL (DY + YY_LEVEL)
69 #define DX_EMERALDS (DX + XX_EMERALDS)
70 #define DY_EMERALDS (DY + YY_EMERALDS)
71 #define DX_DYNAMITE (DX + XX_DYNAMITE)
72 #define DY_DYNAMITE (DY + YY_DYNAMITE)
73 #define DX_KEYS (DX + XX_KEYS)
74 #define DY_KEYS (DY + YY_KEYS)
75 #define DX_SCORE (DX + XX_SCORE)
76 #define DY_SCORE (DY + YY_SCORE)
77 #define DX_TIME1 (DX + XX_TIME1)
78 #define DX_TIME2 (DX + XX_TIME2)
79 #define DY_TIME (DY + YY_TIME)
81 /* values for initial player move delay (initial delay counter value) */
82 #define INITIAL_MOVE_DELAY_OFF -1
83 #define INITIAL_MOVE_DELAY_ON 0
85 /* values for player movement speed (which is in fact a delay value) */
86 #define MOVE_DELAY_NORMAL_SPEED 8
87 #define MOVE_DELAY_HIGH_SPEED 4
89 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
90 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
91 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
92 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
94 /* values for other actions */
95 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
97 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
99 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
100 RND(element_info[e].push_delay_random))
101 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
102 RND(element_info[e].move_delay_random))
103 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
104 (element_info[e].move_delay_random))
106 #define ELEMENT_CAN_ENTER_FIELD_BASE(e, x, y, condition) \
107 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
108 (CAN_MOVE_INTO_ACID(e) && \
109 Feld[x][y] == EL_ACID) || \
113 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
114 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
116 (DONT_COLLIDE_WITH(e) && \
118 !PLAYER_ENEMY_PROTECTED(x, y))))
120 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
121 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
123 (CAN_MOVE_INTO_ACID(e) && \
124 Feld[x][y] == EL_ACID) || \
125 (DONT_COLLIDE_WITH(e) && \
127 !PLAYER_ENEMY_PROTECTED(x, y))))
130 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
131 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
134 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
135 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
137 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
138 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
140 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \
141 ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
144 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
146 #define ENEMY_CAN_ENTER_FIELD(e, x, y) ELEMENT_CAN_ENTER_FIELD_BASE(e, x, y, 0)
149 #define YAMYAM_CAN_ENTER_FIELD(x, y) \
150 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
151 (CAN_MOVE_INTO_ACID(EL_YAMYAM) && \
152 Feld[x][y] == EL_ACID) || \
153 Feld[x][y] == EL_DIAMOND))
155 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \
156 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
157 (CAN_MOVE_INTO_ACID(EL_DARK_YAMYAM) &&\
158 Feld[x][y] == EL_ACID) || \
159 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
161 #define PACMAN_CAN_ENTER_FIELD(x, y) \
162 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
163 (CAN_MOVE_INTO_ACID(EL_PACMAN) && \
164 Feld[x][y] == EL_ACID) || \
165 IS_AMOEBOID(Feld[x][y])))
167 #define PIG_CAN_ENTER_FIELD(x, y) \
168 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
169 (CAN_MOVE_INTO_ACID(EL_PIG) && \
170 Feld[x][y] == EL_ACID) || \
171 IS_FOOD_PIG(Feld[x][y])))
173 #define PENGUIN_CAN_ENTER_FIELD(x, y) \
174 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
175 (CAN_MOVE_INTO_ACID(EL_PENGUIN) && \
176 Feld[x][y] == EL_ACID) || \
177 IS_FOOD_PENGUIN(Feld[x][y]) || \
178 Feld[x][y] == EL_EXIT_OPEN))
180 #define DRAGON_CAN_ENTER_FIELD(x, y) \
181 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
182 (CAN_MOVE_INTO_ACID(EL_DRAGON) && \
183 Feld[x][y] == EL_ACID)))
185 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
186 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
187 (CAN_MOVE_INTO_ACID(EL_MOLE) && \
188 Feld[x][y] == EL_ACID) || \
191 #define SPRING_CAN_ENTER_FIELD(x, y) \
192 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
193 (CAN_MOVE_INTO_ACID(EL_SPRING) && \
194 Feld[x][y] == EL_ACID)))
196 #define GROUP_NR(e) ((e) - EL_GROUP_START)
197 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
198 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
199 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
201 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
202 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
205 #define CE_ENTER_FIELD_COND(e, x, y) \
206 (!IS_PLAYER(x, y) && \
207 (Feld[x][y] == EL_ACID || \
208 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
210 #define CE_ENTER_FIELD_COND(e, x, y) \
211 (!IS_PLAYER(x, y) && \
212 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
215 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
216 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
218 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
219 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
221 /* game button identifiers */
222 #define GAME_CTRL_ID_STOP 0
223 #define GAME_CTRL_ID_PAUSE 1
224 #define GAME_CTRL_ID_PLAY 2
225 #define SOUND_CTRL_ID_MUSIC 3
226 #define SOUND_CTRL_ID_LOOPS 4
227 #define SOUND_CTRL_ID_SIMPLE 5
229 #define NUM_GAME_BUTTONS 6
232 /* forward declaration for internal use */
234 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
235 static boolean MovePlayer(struct PlayerInfo *, int, int);
236 static void ScrollPlayer(struct PlayerInfo *, int);
237 static void ScrollScreen(struct PlayerInfo *, int);
239 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
241 static void InitBeltMovement(void);
242 static void CloseAllOpenTimegates(void);
243 static void CheckGravityMovement(struct PlayerInfo *);
244 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
245 static void KillHeroUnlessEnemyProtected(int, int);
246 static void KillHeroUnlessExplosionProtected(int, int);
248 static void TestIfPlayerTouchesCustomElement(int, int);
249 static void TestIfElementTouchesCustomElement(int, int);
250 static void TestIfElementHitsCustomElement(int, int, int);
252 static void TestIfElementSmashesCustomElement(int, int, int);
255 static void ChangeElement(int, int, int);
257 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
258 #define CheckTriggeredElementChange(x, y, e, ev) \
259 CheckTriggeredElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, -1)
260 #define CheckTriggeredElementChangePlayer(x, y, e, ev, p, s) \
261 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
262 #define CheckTriggeredElementChangeSide(x, y, e, ev, s) \
263 CheckTriggeredElementChangeExt(x, y, e, ev, -1, s, -1)
264 #define CheckTriggeredElementChangePage(x, y, e, ev, p) \
265 CheckTriggeredElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, p)
267 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
268 #define CheckElementChange(x, y, e, ev) \
269 CheckElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, -1)
270 #define CheckElementChangePlayer(x, y, e, ev, p, s) \
271 CheckElementChangeExt(x, y, e, ev, p, s, -1)
272 #define CheckElementChangeSide(x, y, e, ev, s) \
273 CheckElementChangeExt(x, y, e, ev, -1, s, -1)
274 #define CheckElementChangePage(x, y, e, ev, p) \
275 CheckElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, p)
277 static void PlayLevelSound(int, int, int);
278 static void PlayLevelSoundNearest(int, int, int);
279 static void PlayLevelSoundAction(int, int, int);
280 static void PlayLevelSoundElementAction(int, int, int, int);
281 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
282 static void PlayLevelSoundActionIfLoop(int, int, int);
283 static void StopLevelSoundActionIfLoop(int, int, int);
284 static void PlayLevelMusic();
286 static void MapGameButtons();
287 static void HandleGameButtons(struct GadgetInfo *);
289 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
292 /* ------------------------------------------------------------------------- */
293 /* definition of elements that automatically change to other elements after */
294 /* a specified time, eventually calling a function when changing */
295 /* ------------------------------------------------------------------------- */
297 /* forward declaration for changer functions */
298 static void InitBuggyBase(int x, int y);
299 static void WarnBuggyBase(int x, int y);
301 static void InitTrap(int x, int y);
302 static void ActivateTrap(int x, int y);
303 static void ChangeActiveTrap(int x, int y);
305 static void InitRobotWheel(int x, int y);
306 static void RunRobotWheel(int x, int y);
307 static void StopRobotWheel(int x, int y);
309 static void InitTimegateWheel(int x, int y);
310 static void RunTimegateWheel(int x, int y);
312 struct ChangingElementInfo
317 void (*pre_change_function)(int x, int y);
318 void (*change_function)(int x, int y);
319 void (*post_change_function)(int x, int y);
322 static struct ChangingElementInfo change_delay_list[] =
373 EL_SWITCHGATE_OPENING,
381 EL_SWITCHGATE_CLOSING,
382 EL_SWITCHGATE_CLOSED,
414 EL_ACID_SPLASH_RIGHT,
423 EL_SP_BUGGY_BASE_ACTIVATING,
430 EL_SP_BUGGY_BASE_ACTIVATING,
431 EL_SP_BUGGY_BASE_ACTIVE,
438 EL_SP_BUGGY_BASE_ACTIVE,
462 EL_ROBOT_WHEEL_ACTIVE,
470 EL_TIMEGATE_SWITCH_ACTIVE,
491 int push_delay_fixed, push_delay_random;
496 { EL_BALLOON, 0, 0 },
498 { EL_SOKOBAN_OBJECT, 2, 0 },
499 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
500 { EL_SATELLITE, 2, 0 },
501 { EL_SP_DISK_YELLOW, 2, 0 },
503 { EL_UNDEFINED, 0, 0 },
511 move_stepsize_list[] =
513 { EL_AMOEBA_DROP, 2 },
514 { EL_AMOEBA_DROPPING, 2 },
515 { EL_QUICKSAND_FILLING, 1 },
516 { EL_QUICKSAND_EMPTYING, 1 },
517 { EL_MAGIC_WALL_FILLING, 2 },
518 { EL_BD_MAGIC_WALL_FILLING, 2 },
519 { EL_MAGIC_WALL_EMPTYING, 2 },
520 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
530 collect_count_list[] =
533 { EL_BD_DIAMOND, 1 },
534 { EL_EMERALD_YELLOW, 1 },
535 { EL_EMERALD_RED, 1 },
536 { EL_EMERALD_PURPLE, 1 },
538 { EL_SP_INFOTRON, 1 },
552 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
553 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
554 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
555 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
556 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
557 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
558 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
559 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
560 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
561 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
562 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
567 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
569 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
570 CH_EVENT_BIT(CE_DELAY))
571 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
572 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
573 IS_JUST_CHANGING(x, y))
575 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
578 void GetPlayerConfig()
580 if (!audio.sound_available)
581 setup.sound_simple = FALSE;
583 if (!audio.loops_available)
584 setup.sound_loops = FALSE;
586 if (!audio.music_available)
587 setup.sound_music = FALSE;
589 if (!video.fullscreen_available)
590 setup.fullscreen = FALSE;
592 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
594 SetAudioMode(setup.sound);
598 static int getBeltNrFromBeltElement(int element)
600 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
601 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
602 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
605 static int getBeltNrFromBeltActiveElement(int element)
607 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
608 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
609 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
612 static int getBeltNrFromBeltSwitchElement(int element)
614 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
615 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
616 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
619 static int getBeltDirNrFromBeltSwitchElement(int element)
621 static int belt_base_element[4] =
623 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
624 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
625 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
626 EL_CONVEYOR_BELT_4_SWITCH_LEFT
629 int belt_nr = getBeltNrFromBeltSwitchElement(element);
630 int belt_dir_nr = element - belt_base_element[belt_nr];
632 return (belt_dir_nr % 3);
635 static int getBeltDirFromBeltSwitchElement(int element)
637 static int belt_move_dir[3] =
644 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
646 return belt_move_dir[belt_dir_nr];
649 static void InitPlayerField(int x, int y, int element, boolean init_game)
651 if (element == EL_SP_MURPHY)
655 if (stored_player[0].present)
657 Feld[x][y] = EL_SP_MURPHY_CLONE;
663 stored_player[0].use_murphy_graphic = TRUE;
666 Feld[x][y] = EL_PLAYER_1;
672 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
673 int jx = player->jx, jy = player->jy;
675 player->present = TRUE;
677 player->block_last_field = (element == EL_SP_MURPHY ?
678 level.sp_block_last_field :
679 level.block_last_field);
681 if (!options.network || player->connected)
683 player->active = TRUE;
685 /* remove potentially duplicate players */
686 if (StorePlayer[jx][jy] == Feld[x][y])
687 StorePlayer[jx][jy] = 0;
689 StorePlayer[x][y] = Feld[x][y];
693 printf("Player %d activated.\n", player->element_nr);
694 printf("[Local player is %d and currently %s.]\n",
695 local_player->element_nr,
696 local_player->active ? "active" : "not active");
700 Feld[x][y] = EL_EMPTY;
701 player->jx = player->last_jx = x;
702 player->jy = player->last_jy = y;
706 static void InitField(int x, int y, boolean init_game)
708 int element = Feld[x][y];
717 InitPlayerField(x, y, element, init_game);
720 case EL_SOKOBAN_FIELD_PLAYER:
721 element = Feld[x][y] = EL_PLAYER_1;
722 InitField(x, y, init_game);
724 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
725 InitField(x, y, init_game);
728 case EL_SOKOBAN_FIELD_EMPTY:
729 local_player->sokobanfields_still_needed++;
733 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
734 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
735 else if (x > 0 && Feld[x-1][y] == EL_ACID)
736 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
737 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
738 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
739 else if (y > 0 && Feld[x][y-1] == EL_ACID)
740 Feld[x][y] = EL_ACID_POOL_BOTTOM;
741 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
742 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
750 case EL_SPACESHIP_RIGHT:
751 case EL_SPACESHIP_UP:
752 case EL_SPACESHIP_LEFT:
753 case EL_SPACESHIP_DOWN:
755 case EL_BD_BUTTERFLY_RIGHT:
756 case EL_BD_BUTTERFLY_UP:
757 case EL_BD_BUTTERFLY_LEFT:
758 case EL_BD_BUTTERFLY_DOWN:
759 case EL_BD_BUTTERFLY:
760 case EL_BD_FIREFLY_RIGHT:
761 case EL_BD_FIREFLY_UP:
762 case EL_BD_FIREFLY_LEFT:
763 case EL_BD_FIREFLY_DOWN:
765 case EL_PACMAN_RIGHT:
789 if (y == lev_fieldy - 1)
791 Feld[x][y] = EL_AMOEBA_GROWING;
792 Store[x][y] = EL_AMOEBA_WET;
796 case EL_DYNAMITE_ACTIVE:
797 case EL_SP_DISK_RED_ACTIVE:
798 case EL_DYNABOMB_PLAYER_1_ACTIVE:
799 case EL_DYNABOMB_PLAYER_2_ACTIVE:
800 case EL_DYNABOMB_PLAYER_3_ACTIVE:
801 case EL_DYNABOMB_PLAYER_4_ACTIVE:
806 local_player->lights_still_needed++;
810 local_player->friends_still_needed++;
815 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
820 Feld[x][y] = EL_EMPTY;
825 case EL_EM_KEY_1_FILE:
826 Feld[x][y] = EL_EM_KEY_1;
828 case EL_EM_KEY_2_FILE:
829 Feld[x][y] = EL_EM_KEY_2;
831 case EL_EM_KEY_3_FILE:
832 Feld[x][y] = EL_EM_KEY_3;
834 case EL_EM_KEY_4_FILE:
835 Feld[x][y] = EL_EM_KEY_4;
839 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
840 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
841 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
842 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
843 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
844 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
845 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
846 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
847 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
848 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
849 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
850 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
853 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
854 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
855 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
857 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
859 game.belt_dir[belt_nr] = belt_dir;
860 game.belt_dir_nr[belt_nr] = belt_dir_nr;
862 else /* more than one switch -- set it like the first switch */
864 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
869 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
871 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
874 case EL_LIGHT_SWITCH_ACTIVE:
876 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
880 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
882 else if (IS_GROUP_ELEMENT(element))
884 struct ElementGroupInfo *group = element_info[element].group;
885 int last_anim_random_frame = gfx.anim_random_frame;
888 if (group->choice_mode == ANIM_RANDOM)
889 gfx.anim_random_frame = RND(group->num_elements_resolved);
891 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
892 group->choice_mode, 0,
895 if (group->choice_mode == ANIM_RANDOM)
896 gfx.anim_random_frame = last_anim_random_frame;
900 Feld[x][y] = group->element_resolved[element_pos];
902 InitField(x, y, init_game);
908 static inline void InitField_WithBug1(int x, int y, boolean init_game)
910 InitField(x, y, init_game);
912 /* not needed to call InitMovDir() -- already done by InitField()! */
913 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
914 CAN_MOVE(Feld[x][y]))
918 static inline void InitField_WithBug2(int x, int y, boolean init_game)
920 int old_element = Feld[x][y];
922 InitField(x, y, init_game);
924 /* not needed to call InitMovDir() -- already done by InitField()! */
925 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
926 CAN_MOVE(old_element) &&
927 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
930 /* this case is in fact a combination of not less than three bugs:
931 first, it calls InitMovDir() for elements that can move, although this is
932 already done by InitField(); then, it checks the element that was at this
933 field _before_ the call to InitField() (which can change it)
938 inline void DrawGameValue_Emeralds(int value)
940 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
943 inline void DrawGameValue_Dynamite(int value)
945 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
948 inline void DrawGameValue_Keys(struct PlayerInfo *player)
952 for (i = 0; i < MAX_KEYS; i++)
954 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
955 el2edimg(EL_KEY_1 + i));
958 inline void DrawGameValue_Score(int value)
960 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
963 inline void DrawGameValue_Time(int value)
966 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
968 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
971 inline void DrawGameValue_Level(int value)
974 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
977 /* misuse area for displaying emeralds to draw bigger level number */
978 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
979 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
981 /* now copy it to the area for displaying level number */
982 BlitBitmap(drawto, drawto,
983 DX_EMERALDS, DY_EMERALDS + 1,
984 getFontWidth(FONT_LEVEL_NUMBER) * 3,
985 getFontHeight(FONT_LEVEL_NUMBER) - 1,
986 DX_LEVEL - 1, DY_LEVEL + 1);
988 /* restore the area for displaying emeralds */
989 DrawGameValue_Emeralds(local_player->gems_still_needed);
991 /* yes, this is all really ugly :-) */
995 void DrawGameDoorValues()
999 DrawGameValue_Level(level_nr);
1001 for (i = 0; i < MAX_PLAYERS; i++)
1002 DrawGameValue_Keys(&stored_player[i]);
1004 DrawGameValue_Emeralds(local_player->gems_still_needed);
1005 DrawGameValue_Dynamite(local_player->inventory_size);
1006 DrawGameValue_Score(local_player->score);
1007 DrawGameValue_Time(TimeLeft);
1010 static void resolve_group_element(int group_element, int recursion_depth)
1012 static int group_nr;
1013 static struct ElementGroupInfo *group;
1014 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1017 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1019 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1020 group_element - EL_GROUP_START + 1);
1022 /* replace element which caused too deep recursion by question mark */
1023 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1028 if (recursion_depth == 0) /* initialization */
1030 group = element_info[group_element].group;
1031 group_nr = group_element - EL_GROUP_START;
1033 group->num_elements_resolved = 0;
1034 group->choice_pos = 0;
1037 for (i = 0; i < actual_group->num_elements; i++)
1039 int element = actual_group->element[i];
1041 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1044 if (IS_GROUP_ELEMENT(element))
1045 resolve_group_element(element, recursion_depth + 1);
1048 group->element_resolved[group->num_elements_resolved++] = element;
1049 element_info[element].in_group[group_nr] = TRUE;
1054 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1056 printf("::: group %d: %d resolved elements\n",
1057 group_element - EL_GROUP_START, group->num_elements_resolved);
1058 for (i = 0; i < group->num_elements_resolved; i++)
1059 printf("::: - %d ['%s']\n", group->element_resolved[i],
1060 element_info[group->element_resolved[i]].token_name);
1067 =============================================================================
1069 -----------------------------------------------------------------------------
1070 initialize game engine due to level / tape version number
1071 =============================================================================
1074 static void InitGameEngine()
1078 /* set game engine from tape file when re-playing, else from level file */
1079 game.engine_version = (tape.playing ? tape.engine_version :
1080 level.game_version);
1082 /* dynamically adjust element properties according to game engine version */
1083 InitElementPropertiesEngine(game.engine_version);
1086 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1087 printf(" tape version == %06d [%s] [file: %06d]\n",
1088 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1090 printf(" => game.engine_version == %06d\n", game.engine_version);
1093 /* ---------- recursively resolve group elements ------------------------- */
1095 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1096 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1097 element_info[i].in_group[j] = FALSE;
1099 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1100 resolve_group_element(EL_GROUP_START + i, 0);
1102 /* ---------- initialize player's initial move delay --------------------- */
1104 /* dynamically adjust player properties according to game engine version */
1105 game.initial_move_delay =
1106 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1107 INITIAL_MOVE_DELAY_OFF);
1109 /* dynamically adjust player properties according to level information */
1110 game.initial_move_delay_value =
1111 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1113 /* ---------- initialize player's initial push delay --------------------- */
1115 /* dynamically adjust player properties according to game engine version */
1116 game.initial_push_delay_value =
1117 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1119 /* ---------- initialize changing elements ------------------------------- */
1121 /* initialize changing elements information */
1122 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1124 struct ElementInfo *ei = &element_info[i];
1126 /* this pointer might have been changed in the level editor */
1127 ei->change = &ei->change_page[0];
1129 if (!IS_CUSTOM_ELEMENT(i))
1131 ei->change->target_element = EL_EMPTY_SPACE;
1132 ei->change->delay_fixed = 0;
1133 ei->change->delay_random = 0;
1134 ei->change->delay_frames = 1;
1137 ei->change_events = CE_BITMASK_DEFAULT;
1138 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1140 ei->event_page_nr[j] = 0;
1141 ei->event_page[j] = &ei->change_page[0];
1145 /* add changing elements from pre-defined list */
1146 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1148 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1149 struct ElementInfo *ei = &element_info[ch_delay->element];
1151 ei->change->target_element = ch_delay->target_element;
1152 ei->change->delay_fixed = ch_delay->change_delay;
1154 ei->change->pre_change_function = ch_delay->pre_change_function;
1155 ei->change->change_function = ch_delay->change_function;
1156 ei->change->post_change_function = ch_delay->post_change_function;
1158 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1162 /* add change events from custom element configuration */
1163 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1165 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1167 for (j = 0; j < ei->num_change_pages; j++)
1169 if (!ei->change_page[j].can_change)
1172 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1174 /* only add event page for the first page found with this event */
1175 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1176 !(ei->change_events & CH_EVENT_BIT(k)))
1178 ei->change_events |= CH_EVENT_BIT(k);
1179 ei->event_page_nr[k] = j;
1180 ei->event_page[k] = &ei->change_page[j];
1188 /* add change events from custom element configuration */
1189 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1191 int element = EL_CUSTOM_START + i;
1193 /* only add custom elements that change after fixed/random frame delay */
1194 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1195 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1199 /* ---------- initialize trigger events ---------------------------------- */
1201 /* initialize trigger events information */
1202 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1203 trigger_events[i] = EP_BITMASK_DEFAULT;
1206 /* add trigger events from element change event properties */
1207 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1209 struct ElementInfo *ei = &element_info[i];
1211 for (j = 0; j < ei->num_change_pages; j++)
1213 if (!ei->change_page[j].can_change)
1216 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1218 int trigger_element = ei->change_page[j].trigger_element;
1220 if (IS_GROUP_ELEMENT(trigger_element))
1222 struct ElementGroupInfo *group = element_info[trigger_element].group;
1224 for (k = 0; k < group->num_elements_resolved; k++)
1225 trigger_events[group->element_resolved[k]]
1226 |= ei->change_page[j].events;
1229 trigger_events[trigger_element] |= ei->change_page[j].events;
1234 /* add trigger events from element change event properties */
1235 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1236 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1237 trigger_events[element_info[i].change->trigger_element] |=
1238 element_info[i].change->events;
1241 /* ---------- initialize push delay -------------------------------------- */
1243 /* initialize push delay values to default */
1244 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1246 if (!IS_CUSTOM_ELEMENT(i))
1248 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1249 element_info[i].push_delay_random = game.default_push_delay_random;
1253 /* set push delay value for certain elements from pre-defined list */
1254 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1256 int e = push_delay_list[i].element;
1258 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1259 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1262 /* set push delay value for Supaplex elements for newer engine versions */
1263 if (game.engine_version >= VERSION_IDENT(3,0,9,0))
1265 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1267 if (IS_SP_ELEMENT(i))
1269 element_info[i].push_delay_fixed = 6;
1270 element_info[i].push_delay_random = 0;
1275 /* ---------- initialize move stepsize ----------------------------------- */
1277 /* initialize move stepsize values to default */
1278 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1279 if (!IS_CUSTOM_ELEMENT(i))
1280 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1282 /* set move stepsize value for certain elements from pre-defined list */
1283 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1285 int e = move_stepsize_list[i].element;
1287 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1290 /* ---------- initialize move dig/leave ---------------------------------- */
1292 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1294 element_info[i].can_leave_element = FALSE;
1295 element_info[i].can_leave_element_last = FALSE;
1298 /* ---------- initialize gem count --------------------------------------- */
1300 /* initialize gem count values for each element */
1301 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1302 if (!IS_CUSTOM_ELEMENT(i))
1303 element_info[i].collect_count = 0;
1305 /* add gem count values for all elements from pre-defined list */
1306 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1307 element_info[collect_count_list[i].element].collect_count =
1308 collect_count_list[i].count;
1310 /* ---------- initialize access direction -------------------------------- */
1312 /* initialize access direction values to default */
1313 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1314 if (!IS_CUSTOM_ELEMENT(i))
1315 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1317 /* set access direction value for certain elements from pre-defined list */
1318 for (i = 0; tube_access[i].element != EL_UNDEFINED; i++)
1319 element_info[tube_access[i].element].access_direction =
1320 tube_access[i].direction;
1325 =============================================================================
1327 -----------------------------------------------------------------------------
1328 initialize and start new game
1329 =============================================================================
1334 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1335 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1336 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1343 #if USE_NEW_AMOEBA_CODE
1344 printf("Using new amoeba code.\n");
1346 printf("Using old amoeba code.\n");
1351 /* don't play tapes over network */
1352 network_playing = (options.network && !tape.playing);
1354 for (i = 0; i < MAX_PLAYERS; i++)
1356 struct PlayerInfo *player = &stored_player[i];
1358 player->index_nr = i;
1359 player->index_bit = (1 << i);
1360 player->element_nr = EL_PLAYER_1 + i;
1362 player->present = FALSE;
1363 player->active = FALSE;
1366 player->effective_action = 0;
1367 player->programmed_action = 0;
1370 player->gems_still_needed = level.gems_needed;
1371 player->sokobanfields_still_needed = 0;
1372 player->lights_still_needed = 0;
1373 player->friends_still_needed = 0;
1375 for (j = 0; j < MAX_KEYS; j++)
1376 player->key[j] = FALSE;
1378 player->dynabomb_count = 0;
1379 player->dynabomb_size = 1;
1380 player->dynabombs_left = 0;
1381 player->dynabomb_xl = FALSE;
1383 player->MovDir = MV_NO_MOVING;
1386 player->GfxDir = MV_NO_MOVING;
1387 player->GfxAction = ACTION_DEFAULT;
1389 player->StepFrame = 0;
1391 player->use_murphy_graphic = FALSE;
1393 player->block_last_field = FALSE;
1395 player->actual_frame_counter = 0;
1397 player->step_counter = 0;
1399 player->last_move_dir = MV_NO_MOVING;
1401 player->is_waiting = FALSE;
1402 player->is_moving = FALSE;
1403 player->is_digging = FALSE;
1404 player->is_snapping = FALSE;
1405 player->is_collecting = FALSE;
1406 player->is_pushing = FALSE;
1407 player->is_switching = FALSE;
1408 player->is_dropping = FALSE;
1410 player->is_bored = FALSE;
1411 player->is_sleeping = FALSE;
1413 player->frame_counter_bored = -1;
1414 player->frame_counter_sleeping = -1;
1416 player->anim_delay_counter = 0;
1417 player->post_delay_counter = 0;
1419 player->action_waiting = ACTION_DEFAULT;
1420 player->last_action_waiting = ACTION_DEFAULT;
1421 player->special_action_bored = ACTION_DEFAULT;
1422 player->special_action_sleeping = ACTION_DEFAULT;
1424 player->num_special_action_bored = 0;
1425 player->num_special_action_sleeping = 0;
1427 /* determine number of special actions for bored and sleeping animation */
1428 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1430 boolean found = FALSE;
1432 for (k = 0; k < NUM_DIRECTIONS; k++)
1433 if (el_act_dir2img(player->element_nr, j, k) !=
1434 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1438 player->num_special_action_bored++;
1442 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1444 boolean found = FALSE;
1446 for (k = 0; k < NUM_DIRECTIONS; k++)
1447 if (el_act_dir2img(player->element_nr, j, k) !=
1448 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1452 player->num_special_action_sleeping++;
1457 player->switch_x = -1;
1458 player->switch_y = -1;
1460 player->show_envelope = 0;
1462 player->move_delay = game.initial_move_delay;
1463 player->move_delay_value = game.initial_move_delay_value;
1465 player->move_delay_reset_counter = 0;
1467 player->push_delay = 0;
1468 player->push_delay_value = game.initial_push_delay_value;
1470 player->drop_delay = 0;
1472 player->last_jx = player->last_jy = 0;
1473 player->jx = player->jy = 0;
1475 player->shield_normal_time_left = 0;
1476 player->shield_deadly_time_left = 0;
1478 player->inventory_infinite_element = EL_UNDEFINED;
1479 player->inventory_size = 0;
1481 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1482 SnapField(player, 0, 0);
1484 player->LevelSolved = FALSE;
1485 player->GameOver = FALSE;
1488 network_player_action_received = FALSE;
1490 #if defined(PLATFORM_UNIX)
1491 /* initial null action */
1492 if (network_playing)
1493 SendToServer_MovePlayer(MV_NO_MOVING);
1501 TimeLeft = level.time;
1504 ScreenMovDir = MV_NO_MOVING;
1508 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1510 AllPlayersGone = FALSE;
1512 game.yamyam_content_nr = 0;
1513 game.magic_wall_active = FALSE;
1514 game.magic_wall_time_left = 0;
1515 game.light_time_left = 0;
1516 game.timegate_time_left = 0;
1517 game.switchgate_pos = 0;
1518 game.balloon_dir = MV_NO_MOVING;
1519 game.gravity = level.initial_gravity;
1520 game.explosions_delayed = TRUE;
1522 game.envelope_active = FALSE;
1524 for (i = 0; i < NUM_BELTS; i++)
1526 game.belt_dir[i] = MV_NO_MOVING;
1527 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1530 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1531 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1533 for (x = 0; x < lev_fieldx; x++)
1535 for (y = 0; y < lev_fieldy; y++)
1537 Feld[x][y] = level.field[x][y];
1538 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1539 ChangeDelay[x][y] = 0;
1540 ChangePage[x][y] = -1;
1541 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1543 WasJustMoving[x][y] = 0;
1544 WasJustFalling[x][y] = 0;
1546 Pushed[x][y] = FALSE;
1548 Changed[x][y] = CE_BITMASK_DEFAULT;
1549 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1551 ExplodePhase[x][y] = 0;
1552 ExplodeDelay[x][y] = 0;
1553 ExplodeField[x][y] = EX_NO_EXPLOSION;
1555 RunnerVisit[x][y] = 0;
1556 PlayerVisit[x][y] = 0;
1559 GfxRandom[x][y] = INIT_GFX_RANDOM();
1560 GfxElement[x][y] = EL_UNDEFINED;
1561 GfxAction[x][y] = ACTION_DEFAULT;
1562 GfxDir[x][y] = MV_NO_MOVING;
1566 for (y = 0; y < lev_fieldy; y++)
1568 for (x = 0; x < lev_fieldx; x++)
1570 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1572 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1574 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1577 InitField(x, y, TRUE);
1583 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1584 emulate_sb ? EMU_SOKOBAN :
1585 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1587 /* initialize explosion and ignition delay */
1588 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1590 if (!IS_CUSTOM_ELEMENT(i))
1593 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
1594 int last_phase = num_phase * delay;
1595 int half_phase = (num_phase / 2) * delay;
1597 element_info[i].explosion_delay = last_phase;
1598 element_info[i].ignition_delay = half_phase;
1600 if (i == EL_BLACK_ORB)
1601 element_info[i].ignition_delay = 1;
1604 if (element_info[i].explosion_delay < 2) /* !!! check again !!! */
1605 element_info[i].explosion_delay = 2;
1607 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1608 element_info[i].ignition_delay = 1;
1611 /* correct non-moving belts to start moving left */
1612 for (i = 0; i < NUM_BELTS; i++)
1613 if (game.belt_dir[i] == MV_NO_MOVING)
1614 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1616 /* check if any connected player was not found in playfield */
1617 for (i = 0; i < MAX_PLAYERS; i++)
1619 struct PlayerInfo *player = &stored_player[i];
1621 if (player->connected && !player->present)
1623 for (j = 0; j < MAX_PLAYERS; j++)
1625 struct PlayerInfo *some_player = &stored_player[j];
1626 int jx = some_player->jx, jy = some_player->jy;
1628 /* assign first free player found that is present in the playfield */
1629 if (some_player->present && !some_player->connected)
1631 player->present = TRUE;
1632 player->active = TRUE;
1634 some_player->present = FALSE;
1635 some_player->active = FALSE;
1638 player->element_nr = some_player->element_nr;
1641 StorePlayer[jx][jy] = player->element_nr;
1642 player->jx = player->last_jx = jx;
1643 player->jy = player->last_jy = jy;
1653 /* when playing a tape, eliminate all players which do not participate */
1655 for (i = 0; i < MAX_PLAYERS; i++)
1657 if (stored_player[i].active && !tape.player_participates[i])
1659 struct PlayerInfo *player = &stored_player[i];
1660 int jx = player->jx, jy = player->jy;
1662 player->active = FALSE;
1663 StorePlayer[jx][jy] = 0;
1664 Feld[jx][jy] = EL_EMPTY;
1668 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1670 /* when in single player mode, eliminate all but the first active player */
1672 for (i = 0; i < MAX_PLAYERS; i++)
1674 if (stored_player[i].active)
1676 for (j = i + 1; j < MAX_PLAYERS; j++)
1678 if (stored_player[j].active)
1680 struct PlayerInfo *player = &stored_player[j];
1681 int jx = player->jx, jy = player->jy;
1683 player->active = FALSE;
1684 player->present = FALSE;
1686 StorePlayer[jx][jy] = 0;
1687 Feld[jx][jy] = EL_EMPTY;
1694 /* when recording the game, store which players take part in the game */
1697 for (i = 0; i < MAX_PLAYERS; i++)
1698 if (stored_player[i].active)
1699 tape.player_participates[i] = TRUE;
1704 for (i = 0; i < MAX_PLAYERS; i++)
1706 struct PlayerInfo *player = &stored_player[i];
1708 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1713 if (local_player == player)
1714 printf("Player %d is local player.\n", i+1);
1718 if (BorderElement == EL_EMPTY)
1721 SBX_Right = lev_fieldx - SCR_FIELDX;
1723 SBY_Lower = lev_fieldy - SCR_FIELDY;
1728 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1730 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1733 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1734 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1736 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1737 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1739 /* if local player not found, look for custom element that might create
1740 the player (make some assumptions about the right custom element) */
1741 if (!local_player->present)
1743 int start_x = 0, start_y = 0;
1744 int found_rating = 0;
1745 int found_element = EL_UNDEFINED;
1747 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1749 int element = Feld[x][y];
1754 if (!IS_CUSTOM_ELEMENT(element))
1757 if (CAN_CHANGE(element))
1759 for (i = 0; i < element_info[element].num_change_pages; i++)
1761 content = element_info[element].change_page[i].target_element;
1762 is_player = ELEM_IS_PLAYER(content);
1764 if (is_player && (found_rating < 3 || element < found_element))
1770 found_element = element;
1775 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1777 content = element_info[element].content[xx][yy];
1778 is_player = ELEM_IS_PLAYER(content);
1780 if (is_player && (found_rating < 2 || element < found_element))
1782 start_x = x + xx - 1;
1783 start_y = y + yy - 1;
1786 found_element = element;
1789 if (!CAN_CHANGE(element))
1792 for (i = 0; i < element_info[element].num_change_pages; i++)
1794 content = element_info[element].change_page[i].content[xx][yy];
1795 is_player = ELEM_IS_PLAYER(content);
1797 if (is_player && (found_rating < 1 || element < found_element))
1799 start_x = x + xx - 1;
1800 start_y = y + yy - 1;
1803 found_element = element;
1809 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1810 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1813 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1814 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1820 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1821 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1822 local_player->jx - MIDPOSX);
1824 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1825 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1826 local_player->jy - MIDPOSY);
1828 scroll_x = SBX_Left;
1829 scroll_y = SBY_Upper;
1830 if (local_player->jx >= SBX_Left + MIDPOSX)
1831 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1832 local_player->jx - MIDPOSX :
1834 if (local_player->jy >= SBY_Upper + MIDPOSY)
1835 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1836 local_player->jy - MIDPOSY :
1841 CloseDoor(DOOR_CLOSE_1);
1846 /* after drawing the level, correct some elements */
1847 if (game.timegate_time_left == 0)
1848 CloseAllOpenTimegates();
1850 if (setup.soft_scrolling)
1851 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1853 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1856 /* copy default game door content to main double buffer */
1857 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1858 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1860 DrawGameDoorValues();
1864 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1865 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1866 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1870 /* copy actual game door content to door double buffer for OpenDoor() */
1871 BlitBitmap(drawto, bitmap_db_door,
1872 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1874 OpenDoor(DOOR_OPEN_ALL);
1876 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1878 if (setup.sound_music)
1881 KeyboardAutoRepeatOffUnlessAutoplay();
1885 for (i = 0; i < MAX_PLAYERS; i++)
1886 printf("Player %d %sactive.\n",
1887 i + 1, (stored_player[i].active ? "" : "not "));
1891 printf("::: starting game [%d]\n", FrameCounter);
1895 void InitMovDir(int x, int y)
1897 int i, element = Feld[x][y];
1898 static int xy[4][2] =
1905 static int direction[3][4] =
1907 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1908 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1909 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1918 Feld[x][y] = EL_BUG;
1919 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1922 case EL_SPACESHIP_RIGHT:
1923 case EL_SPACESHIP_UP:
1924 case EL_SPACESHIP_LEFT:
1925 case EL_SPACESHIP_DOWN:
1926 Feld[x][y] = EL_SPACESHIP;
1927 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1930 case EL_BD_BUTTERFLY_RIGHT:
1931 case EL_BD_BUTTERFLY_UP:
1932 case EL_BD_BUTTERFLY_LEFT:
1933 case EL_BD_BUTTERFLY_DOWN:
1934 Feld[x][y] = EL_BD_BUTTERFLY;
1935 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1938 case EL_BD_FIREFLY_RIGHT:
1939 case EL_BD_FIREFLY_UP:
1940 case EL_BD_FIREFLY_LEFT:
1941 case EL_BD_FIREFLY_DOWN:
1942 Feld[x][y] = EL_BD_FIREFLY;
1943 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1946 case EL_PACMAN_RIGHT:
1948 case EL_PACMAN_LEFT:
1949 case EL_PACMAN_DOWN:
1950 Feld[x][y] = EL_PACMAN;
1951 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1954 case EL_SP_SNIKSNAK:
1955 MovDir[x][y] = MV_UP;
1958 case EL_SP_ELECTRON:
1959 MovDir[x][y] = MV_LEFT;
1966 Feld[x][y] = EL_MOLE;
1967 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1971 if (IS_CUSTOM_ELEMENT(element))
1973 struct ElementInfo *ei = &element_info[element];
1974 int move_direction_initial = ei->move_direction_initial;
1975 int move_pattern = ei->move_pattern;
1977 if (move_direction_initial == MV_START_PREVIOUS)
1979 if (MovDir[x][y] != MV_NO_MOVING)
1982 move_direction_initial = MV_START_AUTOMATIC;
1985 if (move_direction_initial == MV_START_RANDOM)
1986 MovDir[x][y] = 1 << RND(4);
1987 else if (move_direction_initial & MV_ANY_DIRECTION)
1988 MovDir[x][y] = move_direction_initial;
1989 else if (move_pattern == MV_ALL_DIRECTIONS ||
1990 move_pattern == MV_TURNING_LEFT ||
1991 move_pattern == MV_TURNING_RIGHT ||
1992 move_pattern == MV_TURNING_LEFT_RIGHT ||
1993 move_pattern == MV_TURNING_RIGHT_LEFT ||
1994 move_pattern == MV_TURNING_RANDOM)
1995 MovDir[x][y] = 1 << RND(4);
1996 else if (move_pattern == MV_HORIZONTAL)
1997 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1998 else if (move_pattern == MV_VERTICAL)
1999 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2000 else if (move_pattern & MV_ANY_DIRECTION)
2001 MovDir[x][y] = element_info[element].move_pattern;
2002 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2003 move_pattern == MV_ALONG_RIGHT_SIDE)
2005 for (i = 0; i < NUM_DIRECTIONS; i++)
2007 int x1 = x + xy[i][0];
2008 int y1 = y + xy[i][1];
2010 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2012 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2013 MovDir[x][y] = direction[0][i];
2015 MovDir[x][y] = direction[1][i];
2024 MovDir[x][y] = 1 << RND(4);
2026 if (element != EL_BUG &&
2027 element != EL_SPACESHIP &&
2028 element != EL_BD_BUTTERFLY &&
2029 element != EL_BD_FIREFLY)
2032 for (i = 0; i < NUM_DIRECTIONS; i++)
2034 int x1 = x + xy[i][0];
2035 int y1 = y + xy[i][1];
2037 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2039 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2041 MovDir[x][y] = direction[0][i];
2044 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2045 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2047 MovDir[x][y] = direction[1][i];
2056 GfxDir[x][y] = MovDir[x][y];
2059 void InitAmoebaNr(int x, int y)
2062 int group_nr = AmoebeNachbarNr(x, y);
2066 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2068 if (AmoebaCnt[i] == 0)
2076 AmoebaNr[x][y] = group_nr;
2077 AmoebaCnt[group_nr]++;
2078 AmoebaCnt2[group_nr]++;
2084 boolean raise_level = FALSE;
2086 if (local_player->MovPos)
2090 if (tape.auto_play) /* tape might already be stopped here */
2091 tape.auto_play_level_solved = TRUE;
2093 if (tape.playing && tape.auto_play)
2094 tape.auto_play_level_solved = TRUE;
2097 local_player->LevelSolved = FALSE;
2099 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2103 if (!tape.playing && setup.sound_loops)
2104 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2105 SND_CTRL_PLAY_LOOP);
2107 while (TimeLeft > 0)
2109 if (!tape.playing && !setup.sound_loops)
2110 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2111 if (TimeLeft > 0 && !(TimeLeft % 10))
2112 RaiseScore(level.score[SC_TIME_BONUS]);
2113 if (TimeLeft > 100 && !(TimeLeft % 10))
2118 DrawGameValue_Time(TimeLeft);
2126 if (!tape.playing && setup.sound_loops)
2127 StopSound(SND_GAME_LEVELTIME_BONUS);
2129 else if (level.time == 0) /* level without time limit */
2131 if (!tape.playing && setup.sound_loops)
2132 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2133 SND_CTRL_PLAY_LOOP);
2135 while (TimePlayed < 999)
2137 if (!tape.playing && !setup.sound_loops)
2138 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2139 if (TimePlayed < 999 && !(TimePlayed % 10))
2140 RaiseScore(level.score[SC_TIME_BONUS]);
2141 if (TimePlayed < 900 && !(TimePlayed % 10))
2146 DrawGameValue_Time(TimePlayed);
2154 if (!tape.playing && setup.sound_loops)
2155 StopSound(SND_GAME_LEVELTIME_BONUS);
2158 /* close exit door after last player */
2159 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2160 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2162 int element = Feld[ExitX][ExitY];
2164 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2165 EL_SP_EXIT_CLOSING);
2167 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2170 /* Hero disappears */
2171 DrawLevelField(ExitX, ExitY);
2177 CloseDoor(DOOR_CLOSE_1);
2182 SaveTape(tape.level_nr); /* Ask to save tape */
2185 if (level_nr == leveldir_current->handicap_level)
2187 leveldir_current->handicap_level++;
2188 SaveLevelSetup_SeriesInfo();
2191 if (level_editor_test_game)
2192 local_player->score = -1; /* no highscore when playing from editor */
2193 else if (level_nr < leveldir_current->last_level)
2194 raise_level = TRUE; /* advance to next level */
2196 if ((hi_pos = NewHiScore()) >= 0)
2198 game_status = GAME_MODE_SCORES;
2199 DrawHallOfFame(hi_pos);
2208 game_status = GAME_MODE_MAIN;
2225 LoadScore(level_nr);
2227 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2228 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2231 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2233 if (local_player->score > highscore[k].Score)
2235 /* player has made it to the hall of fame */
2237 if (k < MAX_SCORE_ENTRIES - 1)
2239 int m = MAX_SCORE_ENTRIES - 1;
2242 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2243 if (!strcmp(setup.player_name, highscore[l].Name))
2245 if (m == k) /* player's new highscore overwrites his old one */
2249 for (l = m; l > k; l--)
2251 strcpy(highscore[l].Name, highscore[l - 1].Name);
2252 highscore[l].Score = highscore[l - 1].Score;
2259 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2260 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2261 highscore[k].Score = local_player->score;
2267 else if (!strncmp(setup.player_name, highscore[k].Name,
2268 MAX_PLAYER_NAME_LEN))
2269 break; /* player already there with a higher score */
2275 SaveScore(level_nr);
2280 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2282 if (player->GfxAction != action || player->GfxDir != dir)
2285 printf("Player frame reset! (%d => %d, %d => %d)\n",
2286 player->GfxAction, action, player->GfxDir, dir);
2289 player->GfxAction = action;
2290 player->GfxDir = dir;
2292 player->StepFrame = 0;
2296 static void ResetRandomAnimationValue(int x, int y)
2298 GfxRandom[x][y] = INIT_GFX_RANDOM();
2301 static void ResetGfxAnimation(int x, int y)
2304 GfxAction[x][y] = ACTION_DEFAULT;
2305 GfxDir[x][y] = MovDir[x][y];
2308 void InitMovingField(int x, int y, int direction)
2310 int element = Feld[x][y];
2311 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2312 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2316 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2317 ResetGfxAnimation(x, y);
2319 MovDir[newx][newy] = MovDir[x][y] = direction;
2320 GfxDir[x][y] = direction;
2322 if (Feld[newx][newy] == EL_EMPTY)
2323 Feld[newx][newy] = EL_BLOCKED;
2325 if (direction == MV_DOWN && CAN_FALL(element))
2326 GfxAction[x][y] = ACTION_FALLING;
2328 GfxAction[x][y] = ACTION_MOVING;
2330 GfxFrame[newx][newy] = GfxFrame[x][y];
2331 GfxRandom[newx][newy] = GfxRandom[x][y];
2332 GfxAction[newx][newy] = GfxAction[x][y];
2333 GfxDir[newx][newy] = GfxDir[x][y];
2336 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2338 int direction = MovDir[x][y];
2339 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2340 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2346 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2348 int oldx = x, oldy = y;
2349 int direction = MovDir[x][y];
2351 if (direction == MV_LEFT)
2353 else if (direction == MV_RIGHT)
2355 else if (direction == MV_UP)
2357 else if (direction == MV_DOWN)
2360 *comes_from_x = oldx;
2361 *comes_from_y = oldy;
2364 int MovingOrBlocked2Element(int x, int y)
2366 int element = Feld[x][y];
2368 if (element == EL_BLOCKED)
2372 Blocked2Moving(x, y, &oldx, &oldy);
2373 return Feld[oldx][oldy];
2379 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2381 /* like MovingOrBlocked2Element(), but if element is moving
2382 and (x,y) is the field the moving element is just leaving,
2383 return EL_BLOCKED instead of the element value */
2384 int element = Feld[x][y];
2386 if (IS_MOVING(x, y))
2388 if (element == EL_BLOCKED)
2392 Blocked2Moving(x, y, &oldx, &oldy);
2393 return Feld[oldx][oldy];
2402 static void RemoveField(int x, int y)
2404 Feld[x][y] = EL_EMPTY;
2411 ChangeDelay[x][y] = 0;
2412 ChangePage[x][y] = -1;
2413 Pushed[x][y] = FALSE;
2415 GfxElement[x][y] = EL_UNDEFINED;
2416 GfxAction[x][y] = ACTION_DEFAULT;
2417 GfxDir[x][y] = MV_NO_MOVING;
2420 void RemoveMovingField(int x, int y)
2422 int oldx = x, oldy = y, newx = x, newy = y;
2423 int element = Feld[x][y];
2424 int next_element = EL_UNDEFINED;
2426 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2429 if (IS_MOVING(x, y))
2431 Moving2Blocked(x, y, &newx, &newy);
2433 if (Feld[newx][newy] != EL_BLOCKED)
2436 if (Feld[newx][newy] != EL_BLOCKED)
2438 /* element is moving, but target field is not free (blocked), but
2439 already occupied by something different (example: acid pool);
2440 in this case, only remove the moving field, but not the target */
2442 RemoveField(oldx, oldy);
2444 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2446 DrawLevelField(oldx, oldy);
2452 else if (element == EL_BLOCKED)
2454 Blocked2Moving(x, y, &oldx, &oldy);
2455 if (!IS_MOVING(oldx, oldy))
2459 if (element == EL_BLOCKED &&
2460 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2461 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2462 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2463 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2464 next_element = get_next_element(Feld[oldx][oldy]);
2466 RemoveField(oldx, oldy);
2467 RemoveField(newx, newy);
2469 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2471 if (next_element != EL_UNDEFINED)
2472 Feld[oldx][oldy] = next_element;
2474 DrawLevelField(oldx, oldy);
2475 DrawLevelField(newx, newy);
2478 void DrawDynamite(int x, int y)
2480 int sx = SCREENX(x), sy = SCREENY(y);
2481 int graphic = el2img(Feld[x][y]);
2484 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2487 if (IS_WALKABLE_INSIDE(Back[x][y]))
2491 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2492 else if (Store[x][y])
2493 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2495 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2498 if (Back[x][y] || Store[x][y])
2499 DrawGraphicThruMask(sx, sy, graphic, frame);
2501 DrawGraphic(sx, sy, graphic, frame);
2503 if (game.emulation == EMU_SUPAPLEX)
2504 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2505 else if (Store[x][y])
2506 DrawGraphicThruMask(sx, sy, graphic, frame);
2508 DrawGraphic(sx, sy, graphic, frame);
2512 void CheckDynamite(int x, int y)
2514 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2518 if (MovDelay[x][y] != 0)
2521 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2528 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2530 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2531 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2532 StopSound(SND_DYNAMITE_ACTIVE);
2534 StopSound(SND_DYNABOMB_ACTIVE);
2540 void RelocatePlayer(int x, int y, int element_raw)
2542 int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
2543 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2544 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2545 boolean no_delay = (tape.index_search);
2546 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2547 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2549 if (player->GameOver) /* do not reanimate dead player */
2553 RemoveField(x, y); /* temporarily remove newly placed player */
2554 DrawLevelField(x, y);
2557 if (player->present)
2559 while (player->MovPos)
2561 ScrollPlayer(player, SCROLL_GO_ON);
2562 ScrollScreen(NULL, SCROLL_GO_ON);
2568 Delay(wait_delay_value);
2571 DrawPlayer(player); /* needed here only to cleanup last field */
2572 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2574 player->is_moving = FALSE;
2577 Feld[x][y] = element;
2578 InitPlayerField(x, y, element, TRUE);
2580 if (player == local_player)
2582 int scroll_xx = -999, scroll_yy = -999;
2584 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2587 int fx = FX, fy = FY;
2589 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2590 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2591 local_player->jx - MIDPOSX);
2593 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2594 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2595 local_player->jy - MIDPOSY);
2597 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2598 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2603 fx += dx * TILEX / 2;
2604 fy += dy * TILEY / 2;
2606 ScrollLevel(dx, dy);
2609 /* scroll in two steps of half tile size to make things smoother */
2610 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2612 Delay(wait_delay_value);
2614 /* scroll second step to align at full tile size */
2616 Delay(wait_delay_value);
2621 void Explode(int ex, int ey, int phase, int mode)
2628 /* !!! eliminate this variable !!! */
2629 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2634 int last_phase = num_phase * delay;
2635 int half_phase = (num_phase / 2) * delay;
2636 int first_phase_after_start = EX_PHASE_START + 1;
2640 if (game.explosions_delayed)
2642 ExplodeField[ex][ey] = mode;
2646 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2648 int center_element = Feld[ex][ey];
2651 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
2655 /* --- This is only really needed (and now handled) in "Impact()". --- */
2656 /* do not explode moving elements that left the explode field in time */
2657 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2658 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2662 if (mode == EX_NORMAL || mode == EX_CENTER)
2663 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2665 /* remove things displayed in background while burning dynamite */
2666 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2669 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2671 /* put moving element to center field (and let it explode there) */
2672 center_element = MovingOrBlocked2Element(ex, ey);
2673 RemoveMovingField(ex, ey);
2674 Feld[ex][ey] = center_element;
2678 last_phase = element_info[center_element].explosion_delay;
2681 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2683 int xx = x - ex + 1;
2684 int yy = y - ey + 1;
2688 if (!IN_LEV_FIELD(x, y) || (mode != EX_NORMAL && (x != ex || y != ey)))
2691 if (!IN_LEV_FIELD(x, y) ||
2692 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2693 (x != ex || y != ey)))
2697 element = Feld[x][y];
2699 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2701 element = MovingOrBlocked2Element(x, y);
2703 if (!IS_EXPLOSION_PROOF(element))
2704 RemoveMovingField(x, y);
2710 if (IS_EXPLOSION_PROOF(element))
2713 /* indestructible elements can only explode in center (but not flames) */
2714 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2715 element == EL_FLAMES)
2720 if ((IS_INDESTRUCTIBLE(element) &&
2721 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2722 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2723 element == EL_FLAMES)
2727 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2729 if (IS_ACTIVE_BOMB(element))
2731 /* re-activate things under the bomb like gate or penguin */
2732 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2739 /* save walkable background elements while explosion on same tile */
2741 if (IS_INDESTRUCTIBLE(element))
2742 Back[x][y] = element;
2744 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2745 Back[x][y] = element;
2748 /* ignite explodable elements reached by other explosion */
2749 if (element == EL_EXPLOSION)
2750 element = Store2[x][y];
2753 if (AmoebaNr[x][y] &&
2754 (element == EL_AMOEBA_FULL ||
2755 element == EL_BD_AMOEBA ||
2756 element == EL_AMOEBA_GROWING))
2758 AmoebaCnt[AmoebaNr[x][y]]--;
2759 AmoebaCnt2[AmoebaNr[x][y]]--;
2765 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2767 switch(StorePlayer[ex][ey])
2770 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2773 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2776 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2780 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2784 if (game.emulation == EMU_SUPAPLEX)
2785 Store[x][y] = EL_EMPTY;
2787 else if (center_element == EL_MOLE)
2788 Store[x][y] = EL_EMERALD_RED;
2789 else if (center_element == EL_PENGUIN)
2790 Store[x][y] = EL_EMERALD_PURPLE;
2791 else if (center_element == EL_BUG)
2792 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2793 else if (center_element == EL_BD_BUTTERFLY)
2794 Store[x][y] = EL_BD_DIAMOND;
2795 else if (center_element == EL_SP_ELECTRON)
2796 Store[x][y] = EL_SP_INFOTRON;
2797 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2798 Store[x][y] = level.amoeba_content;
2799 else if (center_element == EL_YAMYAM)
2800 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2801 else if (IS_CUSTOM_ELEMENT(center_element) &&
2802 element_info[center_element].content[xx][yy] != EL_EMPTY)
2803 Store[x][y] = element_info[center_element].content[xx][yy];
2804 else if (element == EL_WALL_EMERALD)
2805 Store[x][y] = EL_EMERALD;
2806 else if (element == EL_WALL_DIAMOND)
2807 Store[x][y] = EL_DIAMOND;
2808 else if (element == EL_WALL_BD_DIAMOND)
2809 Store[x][y] = EL_BD_DIAMOND;
2810 else if (element == EL_WALL_EMERALD_YELLOW)
2811 Store[x][y] = EL_EMERALD_YELLOW;
2812 else if (element == EL_WALL_EMERALD_RED)
2813 Store[x][y] = EL_EMERALD_RED;
2814 else if (element == EL_WALL_EMERALD_PURPLE)
2815 Store[x][y] = EL_EMERALD_PURPLE;
2816 else if (element == EL_WALL_PEARL)
2817 Store[x][y] = EL_PEARL;
2818 else if (element == EL_WALL_CRYSTAL)
2819 Store[x][y] = EL_CRYSTAL;
2820 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2821 Store[x][y] = element_info[element].content[1][1];
2823 Store[x][y] = EL_EMPTY;
2825 if (x != ex || y != ey ||
2826 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2827 Store2[x][y] = element;
2830 if (AmoebaNr[x][y] &&
2831 (element == EL_AMOEBA_FULL ||
2832 element == EL_BD_AMOEBA ||
2833 element == EL_AMOEBA_GROWING))
2835 AmoebaCnt[AmoebaNr[x][y]]--;
2836 AmoebaCnt2[AmoebaNr[x][y]]--;
2842 MovDir[x][y] = MovPos[x][y] = 0;
2843 GfxDir[x][y] = MovDir[x][y];
2848 Feld[x][y] = EL_EXPLOSION;
2850 GfxElement[x][y] = center_element;
2852 GfxElement[x][y] = EL_UNDEFINED;
2855 ExplodePhase[x][y] = 1;
2857 ExplodeDelay[x][y] = last_phase;
2862 if (center_element == EL_YAMYAM)
2863 game.yamyam_content_nr =
2864 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2876 last_phase = ExplodeDelay[x][y];
2879 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2883 /* activate this even in non-DEBUG version until cause for crash in
2884 getGraphicAnimationFrame() (see below) is found and eliminated */
2888 if (GfxElement[x][y] == EL_UNDEFINED)
2891 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2892 printf("Explode(): This should never happen!\n");
2895 GfxElement[x][y] = EL_EMPTY;
2901 border_element = Store2[x][y];
2902 if (IS_PLAYER(x, y))
2903 border_element = StorePlayer[x][y];
2905 if (phase == element_info[border_element].ignition_delay ||
2906 phase == last_phase)
2908 boolean border_explosion = FALSE;
2911 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
2913 if (IS_PLAYER(x, y))
2916 KillHeroUnlessExplosionProtected(x, y);
2917 border_explosion = TRUE;
2920 if (phase == last_phase)
2921 printf("::: IS_PLAYER\n");
2924 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
2926 Feld[x][y] = Store2[x][y];
2929 border_explosion = TRUE;
2932 if (phase == last_phase)
2933 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
2936 else if (border_element == EL_AMOEBA_TO_DIAMOND)
2938 AmoebeUmwandeln(x, y);
2940 border_explosion = TRUE;
2943 if (phase == last_phase)
2944 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
2945 element_info[border_element].explosion_delay,
2946 element_info[border_element].ignition_delay,
2952 /* if an element just explodes due to another explosion (chain-reaction),
2953 do not immediately end the new explosion when it was the last frame of
2954 the explosion (as it would be done in the following "if"-statement!) */
2955 if (border_explosion && phase == last_phase)
2962 if (phase == first_phase_after_start)
2964 int element = Store2[x][y];
2966 if (element == EL_BLACK_ORB)
2968 Feld[x][y] = Store2[x][y];
2973 else if (phase == half_phase)
2975 int element = Store2[x][y];
2977 if (IS_PLAYER(x, y))
2978 KillHeroUnlessExplosionProtected(x, y);
2979 else if (CAN_EXPLODE_BY_EXPLOSION(element))
2981 Feld[x][y] = Store2[x][y];
2985 else if (element == EL_AMOEBA_TO_DIAMOND)
2986 AmoebeUmwandeln(x, y);
2990 if (phase == last_phase)
2995 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
2998 element = Feld[x][y] = Store[x][y];
2999 Store[x][y] = Store2[x][y] = 0;
3000 GfxElement[x][y] = EL_UNDEFINED;
3002 /* player can escape from explosions and might therefore be still alive */
3003 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3004 element <= EL_PLAYER_IS_EXPLODING_4)
3005 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3007 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3008 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3009 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3012 /* restore probably existing indestructible background element */
3013 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3014 element = Feld[x][y] = Back[x][y];
3017 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3018 GfxDir[x][y] = MV_NO_MOVING;
3019 ChangeDelay[x][y] = 0;
3020 ChangePage[x][y] = -1;
3023 InitField_WithBug2(x, y, FALSE);
3025 InitField(x, y, FALSE);
3027 /* !!! not needed !!! */
3029 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
3030 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3033 if (CAN_MOVE(element))
3038 DrawLevelField(x, y);
3040 TestIfElementTouchesCustomElement(x, y);
3042 if (GFX_CRUMBLED(element))
3043 DrawLevelFieldCrumbledSandNeighbours(x, y);
3045 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3046 StorePlayer[x][y] = 0;
3048 if (ELEM_IS_PLAYER(element))
3049 RelocatePlayer(x, y, element);
3052 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3054 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3058 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3060 int stored = Store[x][y];
3061 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3062 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3065 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3068 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3069 element_info[GfxElement[x][y]].token_name,
3074 DrawLevelFieldCrumbledSand(x, y);
3076 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3078 DrawLevelElement(x, y, Back[x][y]);
3079 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3081 else if (IS_WALKABLE_UNDER(Back[x][y]))
3083 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3084 DrawLevelElementThruMask(x, y, Back[x][y]);
3086 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3087 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3091 void DynaExplode(int ex, int ey)
3094 int dynabomb_element = Feld[ex][ey];
3095 int dynabomb_size = 1;
3096 boolean dynabomb_xl = FALSE;
3097 struct PlayerInfo *player;
3098 static int xy[4][2] =
3106 if (IS_ACTIVE_BOMB(dynabomb_element))
3108 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3109 dynabomb_size = player->dynabomb_size;
3110 dynabomb_xl = player->dynabomb_xl;
3111 player->dynabombs_left++;
3114 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
3116 for (i = 0; i < NUM_DIRECTIONS; i++)
3118 for (j = 1; j <= dynabomb_size; j++)
3120 int x = ex + j * xy[i][0];
3121 int y = ey + j * xy[i][1];
3124 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3127 element = Feld[x][y];
3129 /* do not restart explosions of fields with active bombs */
3130 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3133 Explode(x, y, EX_PHASE_START, EX_BORDER);
3135 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3136 if (element != EL_EMPTY &&
3137 element != EL_SAND &&
3138 element != EL_EXPLOSION &&
3145 void Bang(int x, int y)
3148 int element = MovingOrBlocked2Element(x, y);
3150 int element = Feld[x][y];
3154 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3156 if (IS_PLAYER(x, y))
3159 struct PlayerInfo *player = PLAYERINFO(x, y);
3161 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3162 player->element_nr);
3167 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3169 if (game.emulation == EMU_SUPAPLEX)
3170 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3172 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3177 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3185 case EL_BD_BUTTERFLY:
3188 case EL_DARK_YAMYAM:
3192 RaiseScoreElement(element);
3193 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3195 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3196 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3197 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3198 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3199 case EL_DYNABOMB_INCREASE_NUMBER:
3200 case EL_DYNABOMB_INCREASE_SIZE:
3201 case EL_DYNABOMB_INCREASE_POWER:
3206 case EL_LAMP_ACTIVE:
3208 case EL_AMOEBA_TO_DIAMOND:
3210 if (IS_PLAYER(x, y))
3211 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3213 Explode(x, y, EX_PHASE_START, EX_CENTER);
3216 if (CAN_EXPLODE_DYNA(element))
3218 else if (CAN_EXPLODE_1X1(element))
3219 Explode(x, y, EX_PHASE_START, EX_CENTER);
3221 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3225 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3228 void SplashAcid(int x, int y)
3231 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3232 (!IN_LEV_FIELD(x - 1, y - 2) ||
3233 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3234 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3236 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3237 (!IN_LEV_FIELD(x + 1, y - 2) ||
3238 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3239 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3241 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3243 /* input: position of element entering acid (obsolete) */
3245 int element = Feld[x][y];
3247 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3250 if (element != EL_ACID_SPLASH_LEFT &&
3251 element != EL_ACID_SPLASH_RIGHT)
3253 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3255 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3256 (!IN_LEV_FIELD(x - 1, y - 1) ||
3257 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3258 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3260 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3261 (!IN_LEV_FIELD(x + 1, y - 1) ||
3262 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3263 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3268 static void InitBeltMovement()
3270 static int belt_base_element[4] =
3272 EL_CONVEYOR_BELT_1_LEFT,
3273 EL_CONVEYOR_BELT_2_LEFT,
3274 EL_CONVEYOR_BELT_3_LEFT,
3275 EL_CONVEYOR_BELT_4_LEFT
3277 static int belt_base_active_element[4] =
3279 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3280 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3281 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3282 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3287 /* set frame order for belt animation graphic according to belt direction */
3288 for (i = 0; i < NUM_BELTS; i++)
3292 for (j = 0; j < NUM_BELT_PARTS; j++)
3294 int element = belt_base_active_element[belt_nr] + j;
3295 int graphic = el2img(element);
3297 if (game.belt_dir[i] == MV_LEFT)
3298 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3300 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3304 for (y = 0; y < lev_fieldy; y++)
3306 for (x = 0; x < lev_fieldx; x++)
3308 int element = Feld[x][y];
3310 for (i = 0; i < NUM_BELTS; i++)
3312 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3314 int e_belt_nr = getBeltNrFromBeltElement(element);
3317 if (e_belt_nr == belt_nr)
3319 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3321 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3329 static void ToggleBeltSwitch(int x, int y)
3331 static int belt_base_element[4] =
3333 EL_CONVEYOR_BELT_1_LEFT,
3334 EL_CONVEYOR_BELT_2_LEFT,
3335 EL_CONVEYOR_BELT_3_LEFT,
3336 EL_CONVEYOR_BELT_4_LEFT
3338 static int belt_base_active_element[4] =
3340 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3341 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3342 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3343 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3345 static int belt_base_switch_element[4] =
3347 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3348 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3349 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3350 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3352 static int belt_move_dir[4] =
3360 int element = Feld[x][y];
3361 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3362 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3363 int belt_dir = belt_move_dir[belt_dir_nr];
3366 if (!IS_BELT_SWITCH(element))
3369 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3370 game.belt_dir[belt_nr] = belt_dir;
3372 if (belt_dir_nr == 3)
3375 /* set frame order for belt animation graphic according to belt direction */
3376 for (i = 0; i < NUM_BELT_PARTS; i++)
3378 int element = belt_base_active_element[belt_nr] + i;
3379 int graphic = el2img(element);
3381 if (belt_dir == MV_LEFT)
3382 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3384 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3387 for (yy = 0; yy < lev_fieldy; yy++)
3389 for (xx = 0; xx < lev_fieldx; xx++)
3391 int element = Feld[xx][yy];
3393 if (IS_BELT_SWITCH(element))
3395 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3397 if (e_belt_nr == belt_nr)
3399 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3400 DrawLevelField(xx, yy);
3403 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3405 int e_belt_nr = getBeltNrFromBeltElement(element);
3407 if (e_belt_nr == belt_nr)
3409 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3411 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3412 DrawLevelField(xx, yy);
3415 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3417 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3419 if (e_belt_nr == belt_nr)
3421 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3423 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3424 DrawLevelField(xx, yy);
3431 static void ToggleSwitchgateSwitch(int x, int y)
3435 game.switchgate_pos = !game.switchgate_pos;
3437 for (yy = 0; yy < lev_fieldy; yy++)
3439 for (xx = 0; xx < lev_fieldx; xx++)
3441 int element = Feld[xx][yy];
3443 if (element == EL_SWITCHGATE_SWITCH_UP ||
3444 element == EL_SWITCHGATE_SWITCH_DOWN)
3446 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3447 DrawLevelField(xx, yy);
3449 else if (element == EL_SWITCHGATE_OPEN ||
3450 element == EL_SWITCHGATE_OPENING)
3452 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3454 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3456 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3459 else if (element == EL_SWITCHGATE_CLOSED ||
3460 element == EL_SWITCHGATE_CLOSING)
3462 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3464 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3466 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3473 static int getInvisibleActiveFromInvisibleElement(int element)
3475 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3476 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3477 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3481 static int getInvisibleFromInvisibleActiveElement(int element)
3483 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3484 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3485 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3489 static void RedrawAllLightSwitchesAndInvisibleElements()
3493 for (y = 0; y < lev_fieldy; y++)
3495 for (x = 0; x < lev_fieldx; x++)
3497 int element = Feld[x][y];
3499 if (element == EL_LIGHT_SWITCH &&
3500 game.light_time_left > 0)
3502 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3503 DrawLevelField(x, y);
3505 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3506 game.light_time_left == 0)
3508 Feld[x][y] = EL_LIGHT_SWITCH;
3509 DrawLevelField(x, y);
3511 else if (element == EL_INVISIBLE_STEELWALL ||
3512 element == EL_INVISIBLE_WALL ||
3513 element == EL_INVISIBLE_SAND)
3515 if (game.light_time_left > 0)
3516 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3518 DrawLevelField(x, y);
3520 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3521 element == EL_INVISIBLE_WALL_ACTIVE ||
3522 element == EL_INVISIBLE_SAND_ACTIVE)
3524 if (game.light_time_left == 0)
3525 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3527 DrawLevelField(x, y);
3533 static void ToggleLightSwitch(int x, int y)
3535 int element = Feld[x][y];
3537 game.light_time_left =
3538 (element == EL_LIGHT_SWITCH ?
3539 level.time_light * FRAMES_PER_SECOND : 0);
3541 RedrawAllLightSwitchesAndInvisibleElements();
3544 static void ActivateTimegateSwitch(int x, int y)
3548 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3550 for (yy = 0; yy < lev_fieldy; yy++)
3552 for (xx = 0; xx < lev_fieldx; xx++)
3554 int element = Feld[xx][yy];
3556 if (element == EL_TIMEGATE_CLOSED ||
3557 element == EL_TIMEGATE_CLOSING)
3559 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3560 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3564 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3566 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3567 DrawLevelField(xx, yy);
3574 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3577 inline static int getElementMoveStepsize(int x, int y)
3579 int element = Feld[x][y];
3580 int direction = MovDir[x][y];
3581 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3582 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3583 int horiz_move = (dx != 0);
3584 int sign = (horiz_move ? dx : dy);
3585 int step = sign * element_info[element].move_stepsize;
3587 /* special values for move stepsize for spring and things on conveyor belt */
3591 if (element == EL_SPRING)
3592 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3593 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3594 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3595 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3597 if (CAN_FALL(element) &&
3598 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3599 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3600 else if (element == EL_SPRING)
3601 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3608 void Impact(int x, int y)
3610 boolean lastline = (y == lev_fieldy-1);
3611 boolean object_hit = FALSE;
3612 boolean impact = (lastline || object_hit);
3613 int element = Feld[x][y];
3614 int smashed = EL_UNDEFINED;
3616 if (!lastline) /* check if element below was hit */
3618 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3621 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3622 MovDir[x][y + 1] != MV_DOWN ||
3623 MovPos[x][y + 1] <= TILEY / 2));
3626 object_hit = !IS_FREE(x, y + 1);
3629 /* do not smash moving elements that left the smashed field in time */
3630 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3631 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3635 smashed = MovingOrBlocked2Element(x, y + 1);
3637 impact = (lastline || object_hit);
3640 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3642 SplashAcid(x, y + 1);
3646 /* only reset graphic animation if graphic really changes after impact */
3648 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3650 ResetGfxAnimation(x, y);
3651 DrawLevelField(x, y);
3654 if (impact && CAN_EXPLODE_IMPACT(element))
3659 else if (impact && element == EL_PEARL)
3661 Feld[x][y] = EL_PEARL_BREAKING;
3662 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3665 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3667 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3672 if (impact && element == EL_AMOEBA_DROP)
3674 if (object_hit && IS_PLAYER(x, y + 1))
3675 KillHeroUnlessEnemyProtected(x, y + 1);
3676 else if (object_hit && smashed == EL_PENGUIN)
3680 Feld[x][y] = EL_AMOEBA_GROWING;
3681 Store[x][y] = EL_AMOEBA_WET;
3683 ResetRandomAnimationValue(x, y);
3688 if (object_hit) /* check which object was hit */
3690 if (CAN_PASS_MAGIC_WALL(element) &&
3691 (smashed == EL_MAGIC_WALL ||
3692 smashed == EL_BD_MAGIC_WALL))
3695 int activated_magic_wall =
3696 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3697 EL_BD_MAGIC_WALL_ACTIVE);
3699 /* activate magic wall / mill */
3700 for (yy = 0; yy < lev_fieldy; yy++)
3701 for (xx = 0; xx < lev_fieldx; xx++)
3702 if (Feld[xx][yy] == smashed)
3703 Feld[xx][yy] = activated_magic_wall;
3705 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3706 game.magic_wall_active = TRUE;
3708 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3709 SND_MAGIC_WALL_ACTIVATING :
3710 SND_BD_MAGIC_WALL_ACTIVATING));
3713 if (IS_PLAYER(x, y + 1))
3715 if (CAN_SMASH_PLAYER(element))
3717 KillHeroUnlessEnemyProtected(x, y + 1);
3721 else if (smashed == EL_PENGUIN)
3723 if (CAN_SMASH_PLAYER(element))
3729 else if (element == EL_BD_DIAMOND)
3731 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3737 else if (((element == EL_SP_INFOTRON ||
3738 element == EL_SP_ZONK) &&
3739 (smashed == EL_SP_SNIKSNAK ||
3740 smashed == EL_SP_ELECTRON ||
3741 smashed == EL_SP_DISK_ORANGE)) ||
3742 (element == EL_SP_INFOTRON &&
3743 smashed == EL_SP_DISK_YELLOW))
3749 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3755 else if (CAN_SMASH_EVERYTHING(element))
3757 if (IS_CLASSIC_ENEMY(smashed) ||
3758 CAN_EXPLODE_SMASHED(smashed))
3763 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3765 if (smashed == EL_LAMP ||
3766 smashed == EL_LAMP_ACTIVE)
3771 else if (smashed == EL_NUT)
3773 Feld[x][y + 1] = EL_NUT_BREAKING;
3774 PlayLevelSound(x, y, SND_NUT_BREAKING);
3775 RaiseScoreElement(EL_NUT);
3778 else if (smashed == EL_PEARL)
3780 Feld[x][y + 1] = EL_PEARL_BREAKING;
3781 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3784 else if (smashed == EL_DIAMOND)
3786 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3787 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3790 else if (IS_BELT_SWITCH(smashed))
3792 ToggleBeltSwitch(x, y + 1);
3794 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3795 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3797 ToggleSwitchgateSwitch(x, y + 1);
3799 else if (smashed == EL_LIGHT_SWITCH ||
3800 smashed == EL_LIGHT_SWITCH_ACTIVE)
3802 ToggleLightSwitch(x, y + 1);
3807 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
3810 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3812 CheckTriggeredElementChangeSide(x, y + 1, smashed,
3813 CE_OTHER_IS_SWITCHING, CH_SIDE_TOP);
3814 CheckElementChangeSide(x, y + 1, smashed, CE_SWITCHED, CH_SIDE_TOP);
3819 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3824 /* play sound of magic wall / mill */
3826 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3827 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3829 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3830 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3831 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3832 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3837 /* play sound of object that hits the ground */
3838 if (lastline || object_hit)
3839 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3842 inline static void TurnRoundExt(int x, int y)
3854 { 0, 0 }, { 0, 0 }, { 0, 0 },
3859 int left, right, back;
3863 { MV_DOWN, MV_UP, MV_RIGHT },
3864 { MV_UP, MV_DOWN, MV_LEFT },
3866 { MV_LEFT, MV_RIGHT, MV_DOWN },
3870 { MV_RIGHT, MV_LEFT, MV_UP }
3873 int element = Feld[x][y];
3874 int move_pattern = element_info[element].move_pattern;
3876 int old_move_dir = MovDir[x][y];
3877 int left_dir = turn[old_move_dir].left;
3878 int right_dir = turn[old_move_dir].right;
3879 int back_dir = turn[old_move_dir].back;
3881 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3882 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3883 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3884 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3886 int left_x = x + left_dx, left_y = y + left_dy;
3887 int right_x = x + right_dx, right_y = y + right_dy;
3888 int move_x = x + move_dx, move_y = y + move_dy;
3892 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3894 TestIfBadThingTouchesOtherBadThing(x, y);
3896 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
3897 MovDir[x][y] = right_dir;
3898 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3899 MovDir[x][y] = left_dir;
3901 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3903 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3906 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3907 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3909 TestIfBadThingTouchesOtherBadThing(x, y);
3911 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
3912 MovDir[x][y] = left_dir;
3913 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3914 MovDir[x][y] = right_dir;
3916 if ((element == EL_SPACESHIP ||
3917 element == EL_SP_SNIKSNAK ||
3918 element == EL_SP_ELECTRON)
3919 && MovDir[x][y] != old_move_dir)
3921 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3924 else if (element == EL_YAMYAM)
3926 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3927 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3929 if (can_turn_left && can_turn_right)
3930 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3931 else if (can_turn_left)
3932 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3933 else if (can_turn_right)
3934 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3936 MovDir[x][y] = back_dir;
3938 MovDelay[x][y] = 16 + 16 * RND(3);
3940 else if (element == EL_DARK_YAMYAM)
3942 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3943 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3945 if (can_turn_left && can_turn_right)
3946 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3947 else if (can_turn_left)
3948 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3949 else if (can_turn_right)
3950 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3952 MovDir[x][y] = back_dir;
3954 MovDelay[x][y] = 16 + 16 * RND(3);
3956 else if (element == EL_PACMAN)
3958 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3959 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3961 if (can_turn_left && can_turn_right)
3962 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3963 else if (can_turn_left)
3964 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3965 else if (can_turn_right)
3966 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3968 MovDir[x][y] = back_dir;
3970 MovDelay[x][y] = 6 + RND(40);
3972 else if (element == EL_PIG)
3974 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3975 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3976 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3977 boolean should_turn_left, should_turn_right, should_move_on;
3979 int rnd = RND(rnd_value);
3981 should_turn_left = (can_turn_left &&
3983 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3984 y + back_dy + left_dy)));
3985 should_turn_right = (can_turn_right &&
3987 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3988 y + back_dy + right_dy)));
3989 should_move_on = (can_move_on &&
3992 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3993 y + move_dy + left_dy) ||
3994 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3995 y + move_dy + right_dy)));
3997 if (should_turn_left || should_turn_right || should_move_on)
3999 if (should_turn_left && should_turn_right && should_move_on)
4000 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4001 rnd < 2 * rnd_value / 3 ? right_dir :
4003 else if (should_turn_left && should_turn_right)
4004 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4005 else if (should_turn_left && should_move_on)
4006 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4007 else if (should_turn_right && should_move_on)
4008 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4009 else if (should_turn_left)
4010 MovDir[x][y] = left_dir;
4011 else if (should_turn_right)
4012 MovDir[x][y] = right_dir;
4013 else if (should_move_on)
4014 MovDir[x][y] = old_move_dir;
4016 else if (can_move_on && rnd > rnd_value / 8)
4017 MovDir[x][y] = old_move_dir;
4018 else if (can_turn_left && can_turn_right)
4019 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4020 else if (can_turn_left && rnd > rnd_value / 8)
4021 MovDir[x][y] = left_dir;
4022 else if (can_turn_right && rnd > rnd_value/8)
4023 MovDir[x][y] = right_dir;
4025 MovDir[x][y] = back_dir;
4027 xx = x + move_xy[MovDir[x][y]].x;
4028 yy = y + move_xy[MovDir[x][y]].y;
4030 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4031 MovDir[x][y] = old_move_dir;
4035 else if (element == EL_DRAGON)
4037 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(left_x, left_y);
4038 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(right_x, right_y);
4039 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(move_x, move_y);
4041 int rnd = RND(rnd_value);
4044 if (FrameCounter < 1 && x == 0 && y == 29)
4045 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4048 if (can_move_on && rnd > rnd_value / 8)
4049 MovDir[x][y] = old_move_dir;
4050 else if (can_turn_left && can_turn_right)
4051 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4052 else if (can_turn_left && rnd > rnd_value / 8)
4053 MovDir[x][y] = left_dir;
4054 else if (can_turn_right && rnd > rnd_value / 8)
4055 MovDir[x][y] = right_dir;
4057 MovDir[x][y] = back_dir;
4059 xx = x + move_xy[MovDir[x][y]].x;
4060 yy = y + move_xy[MovDir[x][y]].y;
4063 if (FrameCounter < 1 && x == 0 && y == 29)
4064 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4065 xx, yy, Feld[xx][yy],
4070 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4071 MovDir[x][y] = old_move_dir;
4073 if (!IS_FREE(xx, yy))
4074 MovDir[x][y] = old_move_dir;
4078 if (FrameCounter < 1 && x == 0 && y == 29)
4079 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4084 else if (element == EL_MOLE)
4086 boolean can_move_on =
4087 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
4088 IS_AMOEBOID(Feld[move_x][move_y]) ||
4089 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4092 boolean can_turn_left =
4093 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
4094 IS_AMOEBOID(Feld[left_x][left_y])));
4096 boolean can_turn_right =
4097 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
4098 IS_AMOEBOID(Feld[right_x][right_y])));
4100 if (can_turn_left && can_turn_right)
4101 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4102 else if (can_turn_left)
4103 MovDir[x][y] = left_dir;
4105 MovDir[x][y] = right_dir;
4108 if (MovDir[x][y] != old_move_dir)
4111 else if (element == EL_BALLOON)
4113 MovDir[x][y] = game.balloon_dir;
4116 else if (element == EL_SPRING)
4119 if (MovDir[x][y] & MV_HORIZONTAL &&
4120 !SPRING_CAN_ENTER_FIELD(move_x, move_y))
4121 MovDir[x][y] = MV_NO_MOVING;
4123 if (MovDir[x][y] & MV_HORIZONTAL &&
4124 (!SPRING_CAN_ENTER_FIELD(move_x, move_y) ||
4125 SPRING_CAN_ENTER_FIELD(x, y + 1)))
4126 MovDir[x][y] = MV_NO_MOVING;
4131 else if (element == EL_ROBOT ||
4132 element == EL_SATELLITE ||
4133 element == EL_PENGUIN)
4135 int attr_x = -1, attr_y = -1;
4146 for (i = 0; i < MAX_PLAYERS; i++)
4148 struct PlayerInfo *player = &stored_player[i];
4149 int jx = player->jx, jy = player->jy;
4151 if (!player->active)
4155 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4163 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4169 if (element == EL_PENGUIN)
4172 static int xy[4][2] =
4180 for (i = 0; i < NUM_DIRECTIONS; i++)
4182 int ex = x + xy[i][0];
4183 int ey = y + xy[i][1];
4185 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4194 MovDir[x][y] = MV_NO_MOVING;
4196 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4197 else if (attr_x > x)
4198 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4200 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4201 else if (attr_y > y)
4202 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4204 if (element == EL_ROBOT)
4208 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4209 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4210 Moving2Blocked(x, y, &newx, &newy);
4212 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4213 MovDelay[x][y] = 8 + 8 * !RND(3);
4215 MovDelay[x][y] = 16;
4217 else if (element == EL_PENGUIN)
4223 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4225 boolean first_horiz = RND(2);
4226 int new_move_dir = MovDir[x][y];
4229 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4230 Moving2Blocked(x, y, &newx, &newy);
4232 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4236 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4237 Moving2Blocked(x, y, &newx, &newy);
4239 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4242 MovDir[x][y] = old_move_dir;
4246 else /* (element == EL_SATELLITE) */
4252 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4254 boolean first_horiz = RND(2);
4255 int new_move_dir = MovDir[x][y];
4258 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4259 Moving2Blocked(x, y, &newx, &newy);
4261 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4265 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4266 Moving2Blocked(x, y, &newx, &newy);
4268 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4271 MovDir[x][y] = old_move_dir;
4276 else if (move_pattern == MV_TURNING_LEFT ||
4277 move_pattern == MV_TURNING_RIGHT ||
4278 move_pattern == MV_TURNING_LEFT_RIGHT ||
4279 move_pattern == MV_TURNING_RIGHT_LEFT ||
4280 move_pattern == MV_TURNING_RANDOM ||
4281 move_pattern == MV_ALL_DIRECTIONS)
4283 boolean can_turn_left =
4284 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4285 boolean can_turn_right =
4286 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4288 if (move_pattern == MV_TURNING_LEFT)
4289 MovDir[x][y] = left_dir;
4290 else if (move_pattern == MV_TURNING_RIGHT)
4291 MovDir[x][y] = right_dir;
4292 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4293 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4294 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4295 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4296 else if (move_pattern == MV_TURNING_RANDOM)
4297 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4298 can_turn_right && !can_turn_left ? right_dir :
4299 RND(2) ? left_dir : right_dir);
4300 else if (can_turn_left && can_turn_right)
4301 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4302 else if (can_turn_left)
4303 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4304 else if (can_turn_right)
4305 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4307 MovDir[x][y] = back_dir;
4309 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4311 else if (move_pattern == MV_HORIZONTAL ||
4312 move_pattern == MV_VERTICAL)
4314 if (move_pattern & old_move_dir)
4315 MovDir[x][y] = back_dir;
4316 else if (move_pattern == MV_HORIZONTAL)
4317 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4318 else if (move_pattern == MV_VERTICAL)
4319 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4321 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4323 else if (move_pattern & MV_ANY_DIRECTION)
4325 MovDir[x][y] = move_pattern;
4326 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4328 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4330 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4331 MovDir[x][y] = left_dir;
4332 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4333 MovDir[x][y] = right_dir;
4335 if (MovDir[x][y] != old_move_dir)
4336 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4338 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4340 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4341 MovDir[x][y] = right_dir;
4342 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4343 MovDir[x][y] = left_dir;
4345 if (MovDir[x][y] != old_move_dir)
4346 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4348 else if (move_pattern == MV_TOWARDS_PLAYER ||
4349 move_pattern == MV_AWAY_FROM_PLAYER)
4351 int attr_x = -1, attr_y = -1;
4353 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4364 for (i = 0; i < MAX_PLAYERS; i++)
4366 struct PlayerInfo *player = &stored_player[i];
4367 int jx = player->jx, jy = player->jy;
4369 if (!player->active)
4373 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4381 MovDir[x][y] = MV_NO_MOVING;
4383 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4384 else if (attr_x > x)
4385 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4387 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4388 else if (attr_y > y)
4389 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4391 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4393 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4395 boolean first_horiz = RND(2);
4396 int new_move_dir = MovDir[x][y];
4399 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4400 Moving2Blocked(x, y, &newx, &newy);
4402 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4406 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4407 Moving2Blocked(x, y, &newx, &newy);
4409 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4412 MovDir[x][y] = old_move_dir;
4415 else if (move_pattern == MV_WHEN_PUSHED ||
4416 move_pattern == MV_WHEN_DROPPED)
4418 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4419 MovDir[x][y] = MV_NO_MOVING;
4423 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4425 static int test_xy[7][2] =
4435 static int test_dir[7] =
4445 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4446 int move_preference = -1000000; /* start with very low preference */
4447 int new_move_dir = MV_NO_MOVING;
4448 int start_test = RND(4);
4451 for (i = 0; i < NUM_DIRECTIONS; i++)
4453 int move_dir = test_dir[start_test + i];
4454 int move_dir_preference;
4456 xx = x + test_xy[start_test + i][0];
4457 yy = y + test_xy[start_test + i][1];
4459 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4460 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4462 new_move_dir = move_dir;
4467 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4470 move_dir_preference = -1 * RunnerVisit[xx][yy];
4471 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4472 move_dir_preference = PlayerVisit[xx][yy];
4474 if (move_dir_preference > move_preference)
4476 /* prefer field that has not been visited for the longest time */
4477 move_preference = move_dir_preference;
4478 new_move_dir = move_dir;
4480 else if (move_dir_preference == move_preference &&
4481 move_dir == old_move_dir)
4483 /* prefer last direction when all directions are preferred equally */
4484 move_preference = move_dir_preference;
4485 new_move_dir = move_dir;
4489 MovDir[x][y] = new_move_dir;
4490 if (old_move_dir != new_move_dir)
4495 static void TurnRound(int x, int y)
4497 int direction = MovDir[x][y];
4500 GfxDir[x][y] = MovDir[x][y];
4506 GfxDir[x][y] = MovDir[x][y];
4509 if (direction != MovDir[x][y])
4514 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4517 GfxAction[x][y] = ACTION_WAITING;
4521 static boolean JustBeingPushed(int x, int y)
4525 for (i = 0; i < MAX_PLAYERS; i++)
4527 struct PlayerInfo *player = &stored_player[i];
4529 if (player->active && player->is_pushing && player->MovPos)
4531 int next_jx = player->jx + (player->jx - player->last_jx);
4532 int next_jy = player->jy + (player->jy - player->last_jy);
4534 if (x == next_jx && y == next_jy)
4542 void StartMoving(int x, int y)
4545 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4547 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4548 int element = Feld[x][y];
4554 if (MovDelay[x][y] == 0)
4555 GfxAction[x][y] = ACTION_DEFAULT;
4557 /* !!! this should be handled more generic (not only for mole) !!! */
4558 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4559 GfxAction[x][y] = ACTION_DEFAULT;
4562 if (CAN_FALL(element) && y < lev_fieldy - 1)
4564 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4565 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4566 if (JustBeingPushed(x, y))
4569 if (element == EL_QUICKSAND_FULL)
4571 if (IS_FREE(x, y + 1))
4573 InitMovingField(x, y, MV_DOWN);
4574 started_moving = TRUE;
4576 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4577 Store[x][y] = EL_ROCK;
4579 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4581 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4584 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4586 if (!MovDelay[x][y])
4587 MovDelay[x][y] = TILEY + 1;
4596 Feld[x][y] = EL_QUICKSAND_EMPTY;
4597 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4598 Store[x][y + 1] = Store[x][y];
4601 PlayLevelSoundAction(x, y, ACTION_FILLING);
4603 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4607 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4608 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4610 InitMovingField(x, y, MV_DOWN);
4611 started_moving = TRUE;
4613 Feld[x][y] = EL_QUICKSAND_FILLING;
4614 Store[x][y] = element;
4616 PlayLevelSoundAction(x, y, ACTION_FILLING);
4618 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4621 else if (element == EL_MAGIC_WALL_FULL)
4623 if (IS_FREE(x, y + 1))
4625 InitMovingField(x, y, MV_DOWN);
4626 started_moving = TRUE;
4628 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4629 Store[x][y] = EL_CHANGED(Store[x][y]);
4631 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4633 if (!MovDelay[x][y])
4634 MovDelay[x][y] = TILEY/4 + 1;
4643 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4644 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4645 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4649 else if (element == EL_BD_MAGIC_WALL_FULL)
4651 if (IS_FREE(x, y + 1))
4653 InitMovingField(x, y, MV_DOWN);
4654 started_moving = TRUE;
4656 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4657 Store[x][y] = EL_CHANGED2(Store[x][y]);
4659 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4661 if (!MovDelay[x][y])
4662 MovDelay[x][y] = TILEY/4 + 1;
4671 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4672 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4673 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4677 else if (CAN_PASS_MAGIC_WALL(element) &&
4678 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4679 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4681 InitMovingField(x, y, MV_DOWN);
4682 started_moving = TRUE;
4685 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4686 EL_BD_MAGIC_WALL_FILLING);
4687 Store[x][y] = element;
4690 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4692 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4695 SplashAcid(x, y + 1);
4697 InitMovingField(x, y, MV_DOWN);
4698 started_moving = TRUE;
4700 Store[x][y] = EL_ACID;
4702 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4703 GfxAction[x][y + 1] = ACTION_ACTIVE;
4707 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4708 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4709 (Feld[x][y + 1] == EL_BLOCKED)) ||
4710 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4711 CAN_SMASH(element) && WasJustFalling[x][y] &&
4712 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4716 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4717 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4718 WasJustMoving[x][y] && !Pushed[x][y + 1])
4720 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4721 WasJustMoving[x][y])
4726 /* this is needed for a special case not covered by calling "Impact()"
4727 from "ContinueMoving()": if an element moves to a tile directly below
4728 another element which was just falling on that tile (which was empty
4729 in the previous frame), the falling element above would just stop
4730 instead of smashing the element below (in previous version, the above
4731 element was just checked for "moving" instead of "falling", resulting
4732 in incorrect smashes caused by horizontal movement of the above
4733 element; also, the case of the player being the element to smash was
4734 simply not covered here... :-/ ) */
4737 WasJustMoving[x][y] = 0;
4738 WasJustFalling[x][y] = 0;
4743 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4745 if (MovDir[x][y] == MV_NO_MOVING)
4747 InitMovingField(x, y, MV_DOWN);
4748 started_moving = TRUE;
4751 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4753 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4754 MovDir[x][y] = MV_DOWN;
4756 InitMovingField(x, y, MV_DOWN);
4757 started_moving = TRUE;
4759 else if (element == EL_AMOEBA_DROP)
4761 Feld[x][y] = EL_AMOEBA_GROWING;
4762 Store[x][y] = EL_AMOEBA_WET;
4764 /* Store[x][y + 1] must be zero, because:
4765 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4768 #if OLD_GAME_BEHAVIOUR
4769 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4771 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4772 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4773 element != EL_DX_SUPABOMB)
4776 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4777 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4778 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4779 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4782 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4783 (IS_FREE(x - 1, y + 1) ||
4784 Feld[x - 1][y + 1] == EL_ACID));
4785 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4786 (IS_FREE(x + 1, y + 1) ||
4787 Feld[x + 1][y + 1] == EL_ACID));
4788 boolean can_fall_any = (can_fall_left || can_fall_right);
4789 boolean can_fall_both = (can_fall_left && can_fall_right);
4791 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4793 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4795 if (slippery_type == SLIPPERY_ONLY_LEFT)
4796 can_fall_right = FALSE;
4797 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4798 can_fall_left = FALSE;
4799 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4800 can_fall_right = FALSE;
4801 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4802 can_fall_left = FALSE;
4804 can_fall_any = (can_fall_left || can_fall_right);
4805 can_fall_both = (can_fall_left && can_fall_right);
4810 if (can_fall_both &&
4811 (game.emulation != EMU_BOULDERDASH &&
4812 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4813 can_fall_left = !(can_fall_right = RND(2));
4815 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4816 started_moving = TRUE;
4820 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4822 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4825 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4826 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4827 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4828 int belt_dir = game.belt_dir[belt_nr];
4830 if ((belt_dir == MV_LEFT && left_is_free) ||
4831 (belt_dir == MV_RIGHT && right_is_free))
4834 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4837 InitMovingField(x, y, belt_dir);
4838 started_moving = TRUE;
4841 Pushed[x][y] = TRUE;
4842 Pushed[nextx][y] = TRUE;
4845 GfxAction[x][y] = ACTION_DEFAULT;
4849 MovDir[x][y] = 0; /* if element was moving, stop it */
4854 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4855 if (CAN_MOVE(element) && !started_moving)
4857 int move_pattern = element_info[element].move_pattern;
4860 Moving2Blocked(x, y, &newx, &newy);
4863 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4866 if ((element == EL_SATELLITE ||
4867 element == EL_BALLOON ||
4868 element == EL_SPRING)
4869 && JustBeingPushed(x, y))
4874 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4875 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4876 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4879 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4880 element, element_info[element].token_name,
4881 WasJustMoving[x][y],
4882 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4883 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4884 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4885 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4889 WasJustMoving[x][y] = 0;
4892 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4895 if (Feld[x][y] != element) /* element has changed */
4897 element = Feld[x][y];
4898 move_pattern = element_info[element].move_pattern;
4900 if (!CAN_MOVE(element))
4904 if (Feld[x][y] != element) /* element has changed */
4912 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4913 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4915 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4917 Moving2Blocked(x, y, &newx, &newy);
4918 if (Feld[newx][newy] == EL_BLOCKED)
4919 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4925 if (FrameCounter < 1 && x == 0 && y == 29)
4926 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4929 if (!MovDelay[x][y]) /* start new movement phase */
4931 /* all objects that can change their move direction after each step
4932 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4934 if (element != EL_YAMYAM &&
4935 element != EL_DARK_YAMYAM &&
4936 element != EL_PACMAN &&
4937 !(move_pattern & MV_ANY_DIRECTION) &&
4938 move_pattern != MV_TURNING_LEFT &&
4939 move_pattern != MV_TURNING_RIGHT &&
4940 move_pattern != MV_TURNING_LEFT_RIGHT &&
4941 move_pattern != MV_TURNING_RIGHT_LEFT &&
4942 move_pattern != MV_TURNING_RANDOM)
4947 if (FrameCounter < 1 && x == 0 && y == 29)
4948 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4951 if (MovDelay[x][y] && (element == EL_BUG ||
4952 element == EL_SPACESHIP ||
4953 element == EL_SP_SNIKSNAK ||
4954 element == EL_SP_ELECTRON ||
4955 element == EL_MOLE))
4956 DrawLevelField(x, y);
4960 if (MovDelay[x][y]) /* wait some time before next movement */
4965 if (element == EL_YAMYAM)
4968 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4969 DrawLevelElementAnimation(x, y, element);
4973 if (MovDelay[x][y]) /* element still has to wait some time */
4976 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4977 ResetGfxAnimation(x, y);
4981 if (GfxAction[x][y] != ACTION_WAITING)
4982 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4984 GfxAction[x][y] = ACTION_WAITING;
4988 if (element == EL_ROBOT ||
4990 element == EL_PACMAN ||
4992 element == EL_YAMYAM ||
4993 element == EL_DARK_YAMYAM)
4996 DrawLevelElementAnimation(x, y, element);
4998 DrawLevelElementAnimationIfNeeded(x, y, element);
5000 PlayLevelSoundAction(x, y, ACTION_WAITING);
5002 else if (element == EL_SP_ELECTRON)
5003 DrawLevelElementAnimationIfNeeded(x, y, element);
5004 else if (element == EL_DRAGON)
5007 int dir = MovDir[x][y];
5008 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5009 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5010 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5011 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5012 dir == MV_UP ? IMG_FLAMES_1_UP :
5013 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5014 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5017 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5020 GfxAction[x][y] = ACTION_ATTACKING;
5022 if (IS_PLAYER(x, y))
5023 DrawPlayerField(x, y);
5025 DrawLevelField(x, y);
5027 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5029 for (i = 1; i <= 3; i++)
5031 int xx = x + i * dx;
5032 int yy = y + i * dy;
5033 int sx = SCREENX(xx);
5034 int sy = SCREENY(yy);
5035 int flame_graphic = graphic + (i - 1);
5037 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5042 int flamed = MovingOrBlocked2Element(xx, yy);
5044 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5047 RemoveMovingField(xx, yy);
5049 Feld[xx][yy] = EL_FLAMES;
5050 if (IN_SCR_FIELD(sx, sy))
5052 DrawLevelFieldCrumbledSand(xx, yy);
5053 DrawGraphic(sx, sy, flame_graphic, frame);
5058 if (Feld[xx][yy] == EL_FLAMES)
5059 Feld[xx][yy] = EL_EMPTY;
5060 DrawLevelField(xx, yy);
5065 if (MovDelay[x][y]) /* element still has to wait some time */
5067 PlayLevelSoundAction(x, y, ACTION_WAITING);
5073 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5074 for all other elements GfxAction will be set by InitMovingField() */
5075 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5076 GfxAction[x][y] = ACTION_MOVING;
5080 /* now make next step */
5082 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5084 if (DONT_COLLIDE_WITH(element) &&
5085 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5086 !PLAYER_ENEMY_PROTECTED(newx, newy))
5089 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5093 /* player killed by element which is deadly when colliding with */
5095 KillHero(PLAYERINFO(newx, newy));
5102 else if (CAN_MOVE_INTO_ACID(element) &&
5103 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5104 (MovDir[x][y] == MV_DOWN ||
5105 game.engine_version > VERSION_IDENT(3,0,8,0)))
5107 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5108 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5112 else if ((element == EL_PENGUIN ||
5113 element == EL_ROBOT ||
5114 element == EL_SATELLITE ||
5115 element == EL_BALLOON ||
5116 IS_CUSTOM_ELEMENT(element)) &&
5117 IN_LEV_FIELD(newx, newy) &&
5118 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5121 SplashAcid(newx, newy);
5122 Store[x][y] = EL_ACID;
5124 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5126 if (Feld[newx][newy] == EL_EXIT_OPEN)
5130 DrawLevelField(x, y);
5132 Feld[x][y] = EL_EMPTY;
5133 DrawLevelField(x, y);
5136 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5137 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5138 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5140 local_player->friends_still_needed--;
5141 if (!local_player->friends_still_needed &&
5142 !local_player->GameOver && AllPlayersGone)
5143 local_player->LevelSolved = local_player->GameOver = TRUE;
5147 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5149 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5150 DrawLevelField(newx, newy);
5152 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5154 else if (!IS_FREE(newx, newy))
5156 GfxAction[x][y] = ACTION_WAITING;
5158 if (IS_PLAYER(x, y))
5159 DrawPlayerField(x, y);
5161 DrawLevelField(x, y);
5166 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5168 if (IS_FOOD_PIG(Feld[newx][newy]))
5170 if (IS_MOVING(newx, newy))
5171 RemoveMovingField(newx, newy);
5174 Feld[newx][newy] = EL_EMPTY;
5175 DrawLevelField(newx, newy);
5178 PlayLevelSound(x, y, SND_PIG_DIGGING);
5180 else if (!IS_FREE(newx, newy))
5182 if (IS_PLAYER(x, y))
5183 DrawPlayerField(x, y);
5185 DrawLevelField(x, y);
5194 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5197 else if (IS_CUSTOM_ELEMENT(element) &&
5198 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5202 !IS_FREE(newx, newy)
5207 int new_element = Feld[newx][newy];
5210 printf("::: '%s' digs '%s' [%d]\n",
5211 element_info[element].token_name,
5212 element_info[Feld[newx][newy]].token_name,
5213 StorePlayer[newx][newy]);
5216 if (!IS_FREE(newx, newy))
5218 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5219 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5222 /* no element can dig solid indestructible elements */
5223 if (IS_INDESTRUCTIBLE(new_element) &&
5224 !IS_DIGGABLE(new_element) &&
5225 !IS_COLLECTIBLE(new_element))
5228 if (AmoebaNr[newx][newy] &&
5229 (new_element == EL_AMOEBA_FULL ||
5230 new_element == EL_BD_AMOEBA ||
5231 new_element == EL_AMOEBA_GROWING))
5233 AmoebaCnt[AmoebaNr[newx][newy]]--;
5234 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5237 if (IS_MOVING(newx, newy))
5238 RemoveMovingField(newx, newy);
5241 RemoveField(newx, newy);
5242 DrawLevelField(newx, newy);
5245 PlayLevelSoundAction(x, y, action);
5248 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5249 element_info[element].can_leave_element = TRUE;
5251 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5253 RunnerVisit[x][y] = FrameCounter;
5254 PlayerVisit[x][y] /= 8; /* expire player visit path */
5260 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5262 if (!IS_FREE(newx, newy))
5264 if (IS_PLAYER(x, y))
5265 DrawPlayerField(x, y);
5267 DrawLevelField(x, y);
5273 boolean wanna_flame = !RND(10);
5274 int dx = newx - x, dy = newy - y;
5275 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5276 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5277 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5278 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5279 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5280 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5283 IS_CLASSIC_ENEMY(element1) ||
5284 IS_CLASSIC_ENEMY(element2)) &&
5285 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5286 element1 != EL_FLAMES && element2 != EL_FLAMES)
5289 ResetGfxAnimation(x, y);
5290 GfxAction[x][y] = ACTION_ATTACKING;
5293 if (IS_PLAYER(x, y))
5294 DrawPlayerField(x, y);
5296 DrawLevelField(x, y);
5298 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5300 MovDelay[x][y] = 50;
5302 Feld[newx][newy] = EL_FLAMES;
5303 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5304 Feld[newx1][newy1] = EL_FLAMES;
5305 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5306 Feld[newx2][newy2] = EL_FLAMES;
5312 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5313 Feld[newx][newy] == EL_DIAMOND)
5315 if (IS_MOVING(newx, newy))
5316 RemoveMovingField(newx, newy);
5319 Feld[newx][newy] = EL_EMPTY;
5320 DrawLevelField(newx, newy);
5323 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5325 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5326 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5328 if (AmoebaNr[newx][newy])
5330 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5331 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5332 Feld[newx][newy] == EL_BD_AMOEBA)
5333 AmoebaCnt[AmoebaNr[newx][newy]]--;
5338 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5340 if (IS_MOVING(newx, newy))
5343 RemoveMovingField(newx, newy);
5347 Feld[newx][newy] = EL_EMPTY;
5348 DrawLevelField(newx, newy);
5351 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5353 else if ((element == EL_PACMAN || element == EL_MOLE)
5354 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5356 if (AmoebaNr[newx][newy])
5358 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5359 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5360 Feld[newx][newy] == EL_BD_AMOEBA)
5361 AmoebaCnt[AmoebaNr[newx][newy]]--;
5364 if (element == EL_MOLE)
5366 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5367 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5369 ResetGfxAnimation(x, y);
5370 GfxAction[x][y] = ACTION_DIGGING;
5371 DrawLevelField(x, y);
5373 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5375 return; /* wait for shrinking amoeba */
5377 else /* element == EL_PACMAN */
5379 Feld[newx][newy] = EL_EMPTY;
5380 DrawLevelField(newx, newy);
5381 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5384 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5385 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5386 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5388 /* wait for shrinking amoeba to completely disappear */
5391 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5393 /* object was running against a wall */
5398 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5399 DrawLevelElementAnimation(x, y, element);
5401 if (element == EL_BUG ||
5402 element == EL_SPACESHIP ||
5403 element == EL_SP_SNIKSNAK)
5404 DrawLevelField(x, y);
5405 else if (element == EL_MOLE)
5406 DrawLevelField(x, y);
5407 else if (element == EL_BD_BUTTERFLY ||
5408 element == EL_BD_FIREFLY)
5409 DrawLevelElementAnimationIfNeeded(x, y, element);
5410 else if (element == EL_SATELLITE)
5411 DrawLevelElementAnimationIfNeeded(x, y, element);
5412 else if (element == EL_SP_ELECTRON)
5413 DrawLevelElementAnimationIfNeeded(x, y, element);
5416 if (DONT_TOUCH(element))
5417 TestIfBadThingTouchesHero(x, y);
5420 PlayLevelSoundAction(x, y, ACTION_WAITING);
5426 InitMovingField(x, y, MovDir[x][y]);
5428 PlayLevelSoundAction(x, y, ACTION_MOVING);
5432 ContinueMoving(x, y);
5435 void ContinueMoving(int x, int y)
5437 int element = Feld[x][y];
5438 struct ElementInfo *ei = &element_info[element];
5439 int direction = MovDir[x][y];
5440 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5441 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5442 int newx = x + dx, newy = y + dy;
5444 int nextx = newx + dx, nexty = newy + dy;
5447 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5448 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5450 boolean pushed_by_player = Pushed[x][y];
5453 MovPos[x][y] += getElementMoveStepsize(x, y);
5456 if (pushed_by_player && IS_PLAYER(x, y))
5458 /* special case: moving object pushed by player */
5459 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5462 if (pushed_by_player) /* special case: moving object pushed by player */
5463 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5466 if (ABS(MovPos[x][y]) < TILEX)
5468 DrawLevelField(x, y);
5470 return; /* element is still moving */
5473 /* element reached destination field */
5475 Feld[x][y] = EL_EMPTY;
5476 Feld[newx][newy] = element;
5477 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5479 if (element == EL_MOLE)
5481 Feld[x][y] = EL_SAND;
5483 DrawLevelFieldCrumbledSandNeighbours(x, y);
5485 else if (element == EL_QUICKSAND_FILLING)
5487 element = Feld[newx][newy] = get_next_element(element);
5488 Store[newx][newy] = Store[x][y];
5490 else if (element == EL_QUICKSAND_EMPTYING)
5492 Feld[x][y] = get_next_element(element);
5493 element = Feld[newx][newy] = Store[x][y];
5495 else if (element == EL_MAGIC_WALL_FILLING)
5497 element = Feld[newx][newy] = get_next_element(element);
5498 if (!game.magic_wall_active)
5499 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5500 Store[newx][newy] = Store[x][y];
5502 else if (element == EL_MAGIC_WALL_EMPTYING)
5504 Feld[x][y] = get_next_element(element);
5505 if (!game.magic_wall_active)
5506 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5507 element = Feld[newx][newy] = Store[x][y];
5509 else if (element == EL_BD_MAGIC_WALL_FILLING)
5511 element = Feld[newx][newy] = get_next_element(element);
5512 if (!game.magic_wall_active)
5513 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5514 Store[newx][newy] = Store[x][y];
5516 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5518 Feld[x][y] = get_next_element(element);
5519 if (!game.magic_wall_active)
5520 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5521 element = Feld[newx][newy] = Store[x][y];
5523 else if (element == EL_AMOEBA_DROPPING)
5525 Feld[x][y] = get_next_element(element);
5526 element = Feld[newx][newy] = Store[x][y];
5528 else if (element == EL_SOKOBAN_OBJECT)
5531 Feld[x][y] = Back[x][y];
5533 if (Back[newx][newy])
5534 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5536 Back[x][y] = Back[newx][newy] = 0;
5538 else if (Store[x][y] == EL_ACID)
5540 element = Feld[newx][newy] = EL_ACID;
5544 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5545 MovDelay[newx][newy] = 0;
5547 /* copy element change control values to new field */
5548 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5549 ChangePage[newx][newy] = ChangePage[x][y];
5550 Changed[newx][newy] = Changed[x][y];
5551 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5553 ChangeDelay[x][y] = 0;
5554 ChangePage[x][y] = -1;
5555 Changed[x][y] = CE_BITMASK_DEFAULT;
5556 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5558 /* copy animation control values to new field */
5559 GfxFrame[newx][newy] = GfxFrame[x][y];
5560 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5561 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5562 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5564 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5566 ResetGfxAnimation(x, y); /* reset animation values for old field */
5569 /* some elements can leave other elements behind after moving */
5570 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5571 ei->move_leave_element != EL_EMPTY &&
5572 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5573 ei->can_leave_element_last))
5575 Feld[x][y] = ei->move_leave_element;
5576 InitField(x, y, FALSE);
5578 if (GFX_CRUMBLED(Feld[x][y]))
5579 DrawLevelFieldCrumbledSandNeighbours(x, y);
5582 ei->can_leave_element_last = ei->can_leave_element;
5583 ei->can_leave_element = FALSE;
5587 /* 2.1.1 (does not work correctly for spring) */
5588 if (!CAN_MOVE(element))
5589 MovDir[newx][newy] = 0;
5593 /* (does not work for falling objects that slide horizontally) */
5594 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5595 MovDir[newx][newy] = 0;
5598 if (!CAN_MOVE(element) ||
5599 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5600 MovDir[newx][newy] = 0;
5603 if (!CAN_MOVE(element) ||
5604 (CAN_FALL(element) && direction == MV_DOWN))
5605 GfxDir[x][y] = MovDir[newx][newy] = 0;
5610 DrawLevelField(x, y);
5611 DrawLevelField(newx, newy);
5613 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5615 /* prevent pushed element from moving on in pushed direction */
5616 if (pushed_by_player && CAN_MOVE(element) &&
5617 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5618 !(element_info[element].move_pattern & direction))
5619 TurnRound(newx, newy);
5622 /* prevent elements on conveyor belt from moving on in last direction */
5623 if (pushed_by_conveyor && CAN_FALL(element) &&
5624 direction & MV_HORIZONTAL)
5625 MovDir[newx][newy] = 0;
5628 if (!pushed_by_player)
5630 WasJustMoving[newx][newy] = 3;
5632 if (CAN_FALL(element) && direction == MV_DOWN)
5633 WasJustFalling[newx][newy] = 3;
5636 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5638 TestIfBadThingTouchesHero(newx, newy);
5639 TestIfBadThingTouchesFriend(newx, newy);
5641 if (!IS_CUSTOM_ELEMENT(element))
5642 TestIfBadThingTouchesOtherBadThing(newx, newy);
5644 else if (element == EL_PENGUIN)
5645 TestIfFriendTouchesBadThing(newx, newy);
5647 if (CAN_FALL(element) && direction == MV_DOWN &&
5648 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5652 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5656 if (ChangePage[newx][newy] != -1) /* delayed change */
5657 ChangeElement(newx, newy, ChangePage[newx][newy]);
5662 TestIfElementHitsCustomElement(newx, newy, direction);
5666 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5668 int hitting_element = Feld[newx][newy];
5670 /* !!! fix side (direction) orientation here and elsewhere !!! */
5671 CheckElementChangeSide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
5675 if (IN_LEV_FIELD(nextx, nexty))
5677 int opposite_direction = MV_DIR_OPPOSITE(direction);
5678 int hitting_side = direction;
5679 int touched_side = opposite_direction;
5680 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5681 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5682 MovDir[nextx][nexty] != direction ||
5683 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5689 CheckElementChangeSide(nextx, nexty, touched_element,
5690 CE_HIT_BY_SOMETHING, opposite_direction);
5692 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5693 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5695 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5697 struct ElementChangeInfo *change =
5698 &element_info[hitting_element].change_page[i];
5700 if (change->can_change &&
5701 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5702 change->trigger_side & touched_side &&
5703 change->trigger_element == touched_element)
5705 CheckElementChangePage(newx, newy, hitting_element,
5706 CE_OTHER_IS_HITTING, i);
5712 if (IS_CUSTOM_ELEMENT(touched_element) &&
5713 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5715 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5717 struct ElementChangeInfo *change =
5718 &element_info[touched_element].change_page[i];
5720 if (change->can_change &&
5721 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5722 change->trigger_side & hitting_side &&
5723 change->trigger_element == hitting_element)
5725 CheckElementChangePage(nextx, nexty, touched_element,
5726 CE_OTHER_GETS_HIT, i);
5737 TestIfPlayerTouchesCustomElement(newx, newy);
5738 TestIfElementTouchesCustomElement(newx, newy);
5741 int AmoebeNachbarNr(int ax, int ay)
5744 int element = Feld[ax][ay];
5746 static int xy[4][2] =
5754 for (i = 0; i < NUM_DIRECTIONS; i++)
5756 int x = ax + xy[i][0];
5757 int y = ay + xy[i][1];
5759 if (!IN_LEV_FIELD(x, y))
5762 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5763 group_nr = AmoebaNr[x][y];
5769 void AmoebenVereinigen(int ax, int ay)
5771 int i, x, y, xx, yy;
5772 int new_group_nr = AmoebaNr[ax][ay];
5773 static int xy[4][2] =
5781 if (new_group_nr == 0)
5784 for (i = 0; i < NUM_DIRECTIONS; i++)
5789 if (!IN_LEV_FIELD(x, y))
5792 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5793 Feld[x][y] == EL_BD_AMOEBA ||
5794 Feld[x][y] == EL_AMOEBA_DEAD) &&
5795 AmoebaNr[x][y] != new_group_nr)
5797 int old_group_nr = AmoebaNr[x][y];
5799 if (old_group_nr == 0)
5802 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5803 AmoebaCnt[old_group_nr] = 0;
5804 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5805 AmoebaCnt2[old_group_nr] = 0;
5807 for (yy = 0; yy < lev_fieldy; yy++)
5809 for (xx = 0; xx < lev_fieldx; xx++)
5811 if (AmoebaNr[xx][yy] == old_group_nr)
5812 AmoebaNr[xx][yy] = new_group_nr;
5819 void AmoebeUmwandeln(int ax, int ay)
5823 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5825 int group_nr = AmoebaNr[ax][ay];
5830 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5831 printf("AmoebeUmwandeln(): This should never happen!\n");
5836 for (y = 0; y < lev_fieldy; y++)
5838 for (x = 0; x < lev_fieldx; x++)
5840 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5843 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5847 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5848 SND_AMOEBA_TURNING_TO_GEM :
5849 SND_AMOEBA_TURNING_TO_ROCK));
5854 static int xy[4][2] =
5862 for (i = 0; i < NUM_DIRECTIONS; i++)
5867 if (!IN_LEV_FIELD(x, y))
5870 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5872 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5873 SND_AMOEBA_TURNING_TO_GEM :
5874 SND_AMOEBA_TURNING_TO_ROCK));
5881 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5884 int group_nr = AmoebaNr[ax][ay];
5885 boolean done = FALSE;
5890 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5891 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5896 for (y = 0; y < lev_fieldy; y++)
5898 for (x = 0; x < lev_fieldx; x++)
5900 if (AmoebaNr[x][y] == group_nr &&
5901 (Feld[x][y] == EL_AMOEBA_DEAD ||
5902 Feld[x][y] == EL_BD_AMOEBA ||
5903 Feld[x][y] == EL_AMOEBA_GROWING))
5906 Feld[x][y] = new_element;
5907 InitField(x, y, FALSE);
5908 DrawLevelField(x, y);
5915 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5916 SND_BD_AMOEBA_TURNING_TO_ROCK :
5917 SND_BD_AMOEBA_TURNING_TO_GEM));
5920 void AmoebeWaechst(int x, int y)
5922 static unsigned long sound_delay = 0;
5923 static unsigned long sound_delay_value = 0;
5925 if (!MovDelay[x][y]) /* start new growing cycle */
5929 if (DelayReached(&sound_delay, sound_delay_value))
5932 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5934 if (Store[x][y] == EL_BD_AMOEBA)
5935 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5937 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5939 sound_delay_value = 30;
5943 if (MovDelay[x][y]) /* wait some time before growing bigger */
5946 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5948 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5949 6 - MovDelay[x][y]);
5951 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5954 if (!MovDelay[x][y])
5956 Feld[x][y] = Store[x][y];
5958 DrawLevelField(x, y);
5963 void AmoebaDisappearing(int x, int y)
5965 static unsigned long sound_delay = 0;
5966 static unsigned long sound_delay_value = 0;
5968 if (!MovDelay[x][y]) /* start new shrinking cycle */
5972 if (DelayReached(&sound_delay, sound_delay_value))
5973 sound_delay_value = 30;
5976 if (MovDelay[x][y]) /* wait some time before shrinking */
5979 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5981 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5982 6 - MovDelay[x][y]);
5984 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5987 if (!MovDelay[x][y])
5989 Feld[x][y] = EL_EMPTY;
5990 DrawLevelField(x, y);
5992 /* don't let mole enter this field in this cycle;
5993 (give priority to objects falling to this field from above) */
5999 void AmoebeAbleger(int ax, int ay)
6002 int element = Feld[ax][ay];
6003 int graphic = el2img(element);
6004 int newax = ax, neway = ay;
6005 static int xy[4][2] =
6013 if (!level.amoeba_speed)
6015 Feld[ax][ay] = EL_AMOEBA_DEAD;
6016 DrawLevelField(ax, ay);
6020 if (IS_ANIMATED(graphic))
6021 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6023 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6024 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6026 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6029 if (MovDelay[ax][ay])
6033 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6036 int x = ax + xy[start][0];
6037 int y = ay + xy[start][1];
6039 if (!IN_LEV_FIELD(x, y))
6042 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6043 if (IS_FREE(x, y) ||
6044 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6050 if (newax == ax && neway == ay)
6053 else /* normal or "filled" (BD style) amoeba */
6056 boolean waiting_for_player = FALSE;
6058 for (i = 0; i < NUM_DIRECTIONS; i++)
6060 int j = (start + i) % 4;
6061 int x = ax + xy[j][0];
6062 int y = ay + xy[j][1];
6064 if (!IN_LEV_FIELD(x, y))
6067 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6068 if (IS_FREE(x, y) ||
6069 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6075 else if (IS_PLAYER(x, y))
6076 waiting_for_player = TRUE;
6079 if (newax == ax && neway == ay) /* amoeba cannot grow */
6081 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6083 Feld[ax][ay] = EL_AMOEBA_DEAD;
6084 DrawLevelField(ax, ay);
6085 AmoebaCnt[AmoebaNr[ax][ay]]--;
6087 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6089 if (element == EL_AMOEBA_FULL)
6090 AmoebeUmwandeln(ax, ay);
6091 else if (element == EL_BD_AMOEBA)
6092 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6097 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6099 /* amoeba gets larger by growing in some direction */
6101 int new_group_nr = AmoebaNr[ax][ay];
6104 if (new_group_nr == 0)
6106 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6107 printf("AmoebeAbleger(): This should never happen!\n");
6112 AmoebaNr[newax][neway] = new_group_nr;
6113 AmoebaCnt[new_group_nr]++;
6114 AmoebaCnt2[new_group_nr]++;
6116 /* if amoeba touches other amoeba(s) after growing, unify them */
6117 AmoebenVereinigen(newax, neway);
6119 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6121 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6127 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6128 (neway == lev_fieldy - 1 && newax != ax))
6130 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6131 Store[newax][neway] = element;
6133 else if (neway == ay)
6135 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6137 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6139 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6144 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6145 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6146 Store[ax][ay] = EL_AMOEBA_DROP;
6147 ContinueMoving(ax, ay);
6151 DrawLevelField(newax, neway);
6154 void Life(int ax, int ay)
6157 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6159 int element = Feld[ax][ay];
6160 int graphic = el2img(element);
6161 boolean changed = FALSE;
6163 if (IS_ANIMATED(graphic))
6164 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6169 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6170 MovDelay[ax][ay] = life_time;
6172 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6175 if (MovDelay[ax][ay])
6179 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6181 int xx = ax+x1, yy = ay+y1;
6184 if (!IN_LEV_FIELD(xx, yy))
6187 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6189 int x = xx+x2, y = yy+y2;
6191 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6194 if (((Feld[x][y] == element ||
6195 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6197 (IS_FREE(x, y) && Stop[x][y]))
6201 if (xx == ax && yy == ay) /* field in the middle */
6203 if (nachbarn < life[0] || nachbarn > life[1])
6205 Feld[xx][yy] = EL_EMPTY;
6207 DrawLevelField(xx, yy);
6208 Stop[xx][yy] = TRUE;
6212 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6213 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6214 { /* free border field */
6215 if (nachbarn >= life[2] && nachbarn <= life[3])
6217 Feld[xx][yy] = element;
6218 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6220 DrawLevelField(xx, yy);
6221 Stop[xx][yy] = TRUE;
6228 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6229 SND_GAME_OF_LIFE_GROWING);
6232 static void InitRobotWheel(int x, int y)
6234 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6237 static void RunRobotWheel(int x, int y)
6239 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6242 static void StopRobotWheel(int x, int y)
6244 if (ZX == x && ZY == y)
6248 static void InitTimegateWheel(int x, int y)
6250 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6253 static void RunTimegateWheel(int x, int y)
6255 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6258 void CheckExit(int x, int y)
6260 if (local_player->gems_still_needed > 0 ||
6261 local_player->sokobanfields_still_needed > 0 ||
6262 local_player->lights_still_needed > 0)
6264 int element = Feld[x][y];
6265 int graphic = el2img(element);
6267 if (IS_ANIMATED(graphic))
6268 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6273 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6276 Feld[x][y] = EL_EXIT_OPENING;
6278 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6281 void CheckExitSP(int x, int y)
6283 if (local_player->gems_still_needed > 0)
6285 int element = Feld[x][y];
6286 int graphic = el2img(element);
6288 if (IS_ANIMATED(graphic))
6289 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6294 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6297 Feld[x][y] = EL_SP_EXIT_OPENING;
6299 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6302 static void CloseAllOpenTimegates()
6306 for (y = 0; y < lev_fieldy; y++)
6308 for (x = 0; x < lev_fieldx; x++)
6310 int element = Feld[x][y];
6312 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6314 Feld[x][y] = EL_TIMEGATE_CLOSING;
6316 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6318 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6325 void EdelsteinFunkeln(int x, int y)
6327 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6330 if (Feld[x][y] == EL_BD_DIAMOND)
6333 if (MovDelay[x][y] == 0) /* next animation frame */
6334 MovDelay[x][y] = 11 * !SimpleRND(500);
6336 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6340 if (setup.direct_draw && MovDelay[x][y])
6341 SetDrawtoField(DRAW_BUFFERED);
6343 DrawLevelElementAnimation(x, y, Feld[x][y]);
6345 if (MovDelay[x][y] != 0)
6347 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6348 10 - MovDelay[x][y]);
6350 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6352 if (setup.direct_draw)
6356 dest_x = FX + SCREENX(x) * TILEX;
6357 dest_y = FY + SCREENY(y) * TILEY;
6359 BlitBitmap(drawto_field, window,
6360 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6361 SetDrawtoField(DRAW_DIRECT);
6367 void MauerWaechst(int x, int y)
6371 if (!MovDelay[x][y]) /* next animation frame */
6372 MovDelay[x][y] = 3 * delay;
6374 if (MovDelay[x][y]) /* wait some time before next frame */
6378 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6380 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6381 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6383 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6386 if (!MovDelay[x][y])
6388 if (MovDir[x][y] == MV_LEFT)
6390 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6391 DrawLevelField(x - 1, y);
6393 else if (MovDir[x][y] == MV_RIGHT)
6395 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6396 DrawLevelField(x + 1, y);
6398 else if (MovDir[x][y] == MV_UP)
6400 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6401 DrawLevelField(x, y - 1);
6405 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6406 DrawLevelField(x, y + 1);
6409 Feld[x][y] = Store[x][y];
6411 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6412 DrawLevelField(x, y);
6417 void MauerAbleger(int ax, int ay)
6419 int element = Feld[ax][ay];
6420 int graphic = el2img(element);
6421 boolean oben_frei = FALSE, unten_frei = FALSE;
6422 boolean links_frei = FALSE, rechts_frei = FALSE;
6423 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6424 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6425 boolean new_wall = FALSE;
6427 if (IS_ANIMATED(graphic))
6428 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6430 if (!MovDelay[ax][ay]) /* start building new wall */
6431 MovDelay[ax][ay] = 6;
6433 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6436 if (MovDelay[ax][ay])
6440 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6442 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6444 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6446 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6449 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6450 element == EL_EXPANDABLE_WALL_ANY)
6454 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6455 Store[ax][ay-1] = element;
6456 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6457 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6458 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6459 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6464 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6465 Store[ax][ay+1] = element;
6466 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6467 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6468 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6469 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6474 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6475 element == EL_EXPANDABLE_WALL_ANY ||
6476 element == EL_EXPANDABLE_WALL)
6480 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6481 Store[ax-1][ay] = element;
6482 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6483 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6484 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6485 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6491 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6492 Store[ax+1][ay] = element;
6493 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6494 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6495 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6496 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6501 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6502 DrawLevelField(ax, ay);
6504 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6506 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6507 unten_massiv = TRUE;
6508 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6509 links_massiv = TRUE;
6510 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6511 rechts_massiv = TRUE;
6513 if (((oben_massiv && unten_massiv) ||
6514 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6515 element == EL_EXPANDABLE_WALL) &&
6516 ((links_massiv && rechts_massiv) ||
6517 element == EL_EXPANDABLE_WALL_VERTICAL))
6518 Feld[ax][ay] = EL_WALL;
6522 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6524 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6528 void CheckForDragon(int x, int y)
6531 boolean dragon_found = FALSE;
6532 static int xy[4][2] =
6540 for (i = 0; i < NUM_DIRECTIONS; i++)
6542 for (j = 0; j < 4; j++)
6544 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6546 if (IN_LEV_FIELD(xx, yy) &&
6547 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6549 if (Feld[xx][yy] == EL_DRAGON)
6550 dragon_found = TRUE;
6559 for (i = 0; i < NUM_DIRECTIONS; i++)
6561 for (j = 0; j < 3; j++)
6563 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6565 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6567 Feld[xx][yy] = EL_EMPTY;
6568 DrawLevelField(xx, yy);
6577 static void InitBuggyBase(int x, int y)
6579 int element = Feld[x][y];
6580 int activating_delay = FRAMES_PER_SECOND / 4;
6583 (element == EL_SP_BUGGY_BASE ?
6584 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6585 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6587 element == EL_SP_BUGGY_BASE_ACTIVE ?
6588 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6591 static void WarnBuggyBase(int x, int y)
6594 static int xy[4][2] =
6602 for (i = 0; i < NUM_DIRECTIONS; i++)
6604 int xx = x + xy[i][0], yy = y + xy[i][1];
6606 if (IS_PLAYER(xx, yy))
6608 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6615 static void InitTrap(int x, int y)
6617 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6620 static void ActivateTrap(int x, int y)
6622 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6625 static void ChangeActiveTrap(int x, int y)
6627 int graphic = IMG_TRAP_ACTIVE;
6629 /* if new animation frame was drawn, correct crumbled sand border */
6630 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6631 DrawLevelFieldCrumbledSand(x, y);
6634 static void ChangeElementNowExt(int x, int y, int target_element)
6636 int previous_move_direction = MovDir[x][y];
6638 /* check if element under player changes from accessible to unaccessible
6639 (needed for special case of dropping element which then changes) */
6640 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6641 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6648 Feld[x][y] = target_element;
6650 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6652 ResetGfxAnimation(x, y);
6653 ResetRandomAnimationValue(x, y);
6655 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6656 MovDir[x][y] = previous_move_direction;
6659 InitField_WithBug1(x, y, FALSE);
6661 InitField(x, y, FALSE);
6662 if (CAN_MOVE(Feld[x][y]))
6666 DrawLevelField(x, y);
6668 if (GFX_CRUMBLED(Feld[x][y]))
6669 DrawLevelFieldCrumbledSandNeighbours(x, y);
6671 TestIfBadThingTouchesHero(x, y);
6672 TestIfPlayerTouchesCustomElement(x, y);
6673 TestIfElementTouchesCustomElement(x, y);
6675 if (ELEM_IS_PLAYER(target_element))
6676 RelocatePlayer(x, y, target_element);
6679 static boolean ChangeElementNow(int x, int y, int element, int page)
6681 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6683 /* always use default change event to prevent running into a loop */
6684 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6685 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6687 /* do not change already changed elements with same change event */
6689 if (Changed[x][y] & ChangeEvent[x][y])
6696 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6698 CheckTriggeredElementChangePage(x,y, Feld[x][y], CE_OTHER_IS_CHANGING, page);
6700 if (change->explode)
6707 if (change->use_content)
6709 boolean complete_change = TRUE;
6710 boolean can_change[3][3];
6713 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6715 boolean half_destructible;
6716 int ex = x + xx - 1;
6717 int ey = y + yy - 1;
6720 can_change[xx][yy] = TRUE;
6722 if (ex == x && ey == y) /* do not check changing element itself */
6725 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6727 can_change[xx][yy] = FALSE; /* do not change empty borders */
6732 if (!IN_LEV_FIELD(ex, ey))
6734 can_change[xx][yy] = FALSE;
6735 complete_change = FALSE;
6742 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6743 e = MovingOrBlocked2Element(ex, ey);
6745 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6747 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6748 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6749 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6751 can_change[xx][yy] = FALSE;
6752 complete_change = FALSE;
6756 if (!change->only_complete || complete_change)
6758 boolean something_has_changed = FALSE;
6760 if (change->only_complete && change->use_random_change &&
6761 RND(100) < change->random)
6764 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6766 int ex = x + xx - 1;
6767 int ey = y + yy - 1;
6769 if (can_change[xx][yy] && (!change->use_random_change ||
6770 RND(100) < change->random))
6772 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6773 RemoveMovingField(ex, ey);
6775 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6777 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6779 something_has_changed = TRUE;
6781 /* for symmetry reasons, freeze newly created border elements */
6782 if (ex != x || ey != y)
6783 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6787 if (something_has_changed)
6788 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6793 ChangeElementNowExt(x, y, change->target_element);
6795 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6801 static void ChangeElement(int x, int y, int page)
6803 int element = MovingOrBlocked2Element(x, y);
6804 struct ElementInfo *ei = &element_info[element];
6805 struct ElementChangeInfo *change = &ei->change_page[page];
6809 if (!CAN_CHANGE(element))
6812 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6813 x, y, element, element_info[element].token_name);
6814 printf("ChangeElement(): This should never happen!\n");
6820 if (ChangeDelay[x][y] == 0) /* initialize element change */
6822 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6823 RND(change->delay_random * change->delay_frames)) + 1;
6825 ResetGfxAnimation(x, y);
6826 ResetRandomAnimationValue(x, y);
6828 if (change->pre_change_function)
6829 change->pre_change_function(x, y);
6832 ChangeDelay[x][y]--;
6834 if (ChangeDelay[x][y] != 0) /* continue element change */
6836 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6838 if (IS_ANIMATED(graphic))
6839 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6841 if (change->change_function)
6842 change->change_function(x, y);
6844 else /* finish element change */
6846 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6848 page = ChangePage[x][y];
6849 ChangePage[x][y] = -1;
6853 if (IS_MOVING(x, y) && !change->explode)
6855 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6858 ChangeDelay[x][y] = 1; /* try change after next move step */
6859 ChangePage[x][y] = page; /* remember page to use for change */
6864 if (ChangeElementNow(x, y, element, page))
6866 if (change->post_change_function)
6867 change->post_change_function(x, y);
6872 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
6873 int trigger_element,
6881 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6884 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6886 int element = EL_CUSTOM_START + i;
6888 boolean change_element = FALSE;
6891 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6894 for (j = 0; j < element_info[element].num_change_pages; j++)
6896 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6898 if (change->can_change &&
6899 change->events & CH_EVENT_BIT(trigger_event) &&
6900 change->trigger_side & trigger_side &&
6901 change->trigger_player & trigger_player &&
6902 change->trigger_page & (1 << trigger_page) &&
6903 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
6906 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6907 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6908 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6911 change_element = TRUE;
6918 if (!change_element)
6921 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6924 if (x == lx && y == ly) /* do not change trigger element itself */
6928 if (Feld[x][y] == element)
6930 ChangeDelay[x][y] = 1;
6931 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6932 ChangeElement(x, y, page);
6940 static boolean CheckElementChangeExt(int x, int y,
6947 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6950 if (Feld[x][y] == EL_BLOCKED)
6952 Blocked2Moving(x, y, &x, &y);
6953 element = Feld[x][y];
6957 if (trigger_page < 0)
6959 boolean change_element = FALSE;
6962 for (i = 0; i < element_info[element].num_change_pages; i++)
6964 struct ElementChangeInfo *change = &element_info[element].change_page[i];
6966 if (change->can_change &&
6967 change->events & CH_EVENT_BIT(trigger_event) &&
6968 change->trigger_side & trigger_side &&
6969 change->trigger_player & trigger_player)
6971 change_element = TRUE;
6978 if (!change_element)
6984 /* !!! this check misses pages with same event, but different side !!! */
6986 if (trigger_page < 0)
6987 trigger_page = element_info[element].event_page_nr[trigger_event];
6989 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
6993 ChangeDelay[x][y] = 1;
6994 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6995 ChangeElement(x, y, trigger_page);
7000 static void PlayPlayerSound(struct PlayerInfo *player)
7002 int jx = player->jx, jy = player->jy;
7003 int element = player->element_nr;
7004 int last_action = player->last_action_waiting;
7005 int action = player->action_waiting;
7007 if (player->is_waiting)
7009 if (action != last_action)
7010 PlayLevelSoundElementAction(jx, jy, element, action);
7012 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7016 if (action != last_action)
7017 StopSound(element_info[element].sound[last_action]);
7019 if (last_action == ACTION_SLEEPING)
7020 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7024 static void PlayAllPlayersSound()
7028 for (i = 0; i < MAX_PLAYERS; i++)
7029 if (stored_player[i].active)
7030 PlayPlayerSound(&stored_player[i]);
7033 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7035 boolean last_waiting = player->is_waiting;
7036 int move_dir = player->MovDir;
7038 player->last_action_waiting = player->action_waiting;
7042 if (!last_waiting) /* not waiting -> waiting */
7044 player->is_waiting = TRUE;
7046 player->frame_counter_bored =
7048 game.player_boring_delay_fixed +
7049 SimpleRND(game.player_boring_delay_random);
7050 player->frame_counter_sleeping =
7052 game.player_sleeping_delay_fixed +
7053 SimpleRND(game.player_sleeping_delay_random);
7055 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7058 if (game.player_sleeping_delay_fixed +
7059 game.player_sleeping_delay_random > 0 &&
7060 player->anim_delay_counter == 0 &&
7061 player->post_delay_counter == 0 &&
7062 FrameCounter >= player->frame_counter_sleeping)
7063 player->is_sleeping = TRUE;
7064 else if (game.player_boring_delay_fixed +
7065 game.player_boring_delay_random > 0 &&
7066 FrameCounter >= player->frame_counter_bored)
7067 player->is_bored = TRUE;
7069 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7070 player->is_bored ? ACTION_BORING :
7073 if (player->is_sleeping)
7075 if (player->num_special_action_sleeping > 0)
7077 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7079 int last_special_action = player->special_action_sleeping;
7080 int num_special_action = player->num_special_action_sleeping;
7081 int special_action =
7082 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7083 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7084 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7085 last_special_action + 1 : ACTION_SLEEPING);
7086 int special_graphic =
7087 el_act_dir2img(player->element_nr, special_action, move_dir);
7089 player->anim_delay_counter =
7090 graphic_info[special_graphic].anim_delay_fixed +
7091 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7092 player->post_delay_counter =
7093 graphic_info[special_graphic].post_delay_fixed +
7094 SimpleRND(graphic_info[special_graphic].post_delay_random);
7096 player->special_action_sleeping = special_action;
7099 if (player->anim_delay_counter > 0)
7101 player->action_waiting = player->special_action_sleeping;
7102 player->anim_delay_counter--;
7104 else if (player->post_delay_counter > 0)
7106 player->post_delay_counter--;
7110 else if (player->is_bored)
7112 if (player->num_special_action_bored > 0)
7114 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7116 int special_action =
7117 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7118 int special_graphic =
7119 el_act_dir2img(player->element_nr, special_action, move_dir);
7121 player->anim_delay_counter =
7122 graphic_info[special_graphic].anim_delay_fixed +
7123 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7124 player->post_delay_counter =
7125 graphic_info[special_graphic].post_delay_fixed +
7126 SimpleRND(graphic_info[special_graphic].post_delay_random);
7128 player->special_action_bored = special_action;
7131 if (player->anim_delay_counter > 0)
7133 player->action_waiting = player->special_action_bored;
7134 player->anim_delay_counter--;
7136 else if (player->post_delay_counter > 0)
7138 player->post_delay_counter--;
7143 else if (last_waiting) /* waiting -> not waiting */
7145 player->is_waiting = FALSE;
7146 player->is_bored = FALSE;
7147 player->is_sleeping = FALSE;
7149 player->frame_counter_bored = -1;
7150 player->frame_counter_sleeping = -1;
7152 player->anim_delay_counter = 0;
7153 player->post_delay_counter = 0;
7155 player->action_waiting = ACTION_DEFAULT;
7157 player->special_action_bored = ACTION_DEFAULT;
7158 player->special_action_sleeping = ACTION_DEFAULT;
7163 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7166 static byte stored_player_action[MAX_PLAYERS];
7167 static int num_stored_actions = 0;
7169 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7170 int left = player_action & JOY_LEFT;
7171 int right = player_action & JOY_RIGHT;
7172 int up = player_action & JOY_UP;
7173 int down = player_action & JOY_DOWN;
7174 int button1 = player_action & JOY_BUTTON_1;
7175 int button2 = player_action & JOY_BUTTON_2;
7176 int dx = (left ? -1 : right ? 1 : 0);
7177 int dy = (up ? -1 : down ? 1 : 0);
7180 stored_player_action[player->index_nr] = 0;
7181 num_stored_actions++;
7185 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7188 if (!player->active || tape.pausing)
7192 printf("::: [%d %d %d %d] [%d %d]\n",
7193 left, right, up, down, button1, button2);
7199 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7204 CheckGravityMovement(player);
7207 snapped = SnapField(player, dx, dy);
7211 dropped = DropElement(player);
7213 moved = MovePlayer(player, dx, dy);
7216 if (tape.single_step && tape.recording && !tape.pausing)
7218 if (button1 || (dropped && !moved))
7220 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7221 SnapField(player, 0, 0); /* stop snapping */
7225 SetPlayerWaiting(player, FALSE);
7228 return player_action;
7230 stored_player_action[player->index_nr] = player_action;
7236 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7239 /* no actions for this player (no input at player's configured device) */
7241 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7242 SnapField(player, 0, 0);
7243 CheckGravityMovementWhenNotMoving(player);
7245 if (player->MovPos == 0)
7246 SetPlayerWaiting(player, TRUE);
7248 if (player->MovPos == 0) /* needed for tape.playing */
7249 player->is_moving = FALSE;
7251 player->is_dropping = FALSE;
7257 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7259 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7261 TapeRecordAction(stored_player_action);
7262 num_stored_actions = 0;
7269 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7271 static byte stored_player_action[MAX_PLAYERS];
7272 static int num_stored_actions = 0;
7273 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7274 int left = player_action & JOY_LEFT;
7275 int right = player_action & JOY_RIGHT;
7276 int up = player_action & JOY_UP;
7277 int down = player_action & JOY_DOWN;
7278 int button1 = player_action & JOY_BUTTON_1;
7279 int button2 = player_action & JOY_BUTTON_2;
7280 int dx = (left ? -1 : right ? 1 : 0);
7281 int dy = (up ? -1 : down ? 1 : 0);
7283 stored_player_action[player->index_nr] = 0;
7284 num_stored_actions++;
7286 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7288 if (!player->active || tape.pausing)
7293 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7296 snapped = SnapField(player, dx, dy);
7300 dropped = DropElement(player);
7302 moved = MovePlayer(player, dx, dy);
7305 if (tape.single_step && tape.recording && !tape.pausing)
7307 if (button1 || (dropped && !moved))
7309 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7310 SnapField(player, 0, 0); /* stop snapping */
7314 stored_player_action[player->index_nr] = player_action;
7318 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7320 /* no actions for this player (no input at player's configured device) */
7322 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7323 SnapField(player, 0, 0);
7324 CheckGravityMovementWhenNotMoving(player);
7326 if (player->MovPos == 0)
7327 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7329 if (player->MovPos == 0) /* needed for tape.playing */
7330 player->is_moving = FALSE;
7333 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7335 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7337 TapeRecordAction(stored_player_action);
7338 num_stored_actions = 0;
7345 static unsigned long action_delay = 0;
7346 unsigned long action_delay_value;
7347 int magic_wall_x = 0, magic_wall_y = 0;
7348 int i, x, y, element, graphic;
7349 byte *recorded_player_action;
7350 byte summarized_player_action = 0;
7352 byte tape_action[MAX_PLAYERS];
7355 if (game_status != GAME_MODE_PLAYING)
7358 action_delay_value =
7359 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7361 if (tape.playing && tape.index_search && !tape.pausing)
7362 action_delay_value = 0;
7364 /* ---------- main game synchronization point ---------- */
7366 WaitUntilDelayReached(&action_delay, action_delay_value);
7368 if (network_playing && !network_player_action_received)
7372 printf("DEBUG: try to get network player actions in time\n");
7376 #if defined(PLATFORM_UNIX)
7377 /* last chance to get network player actions without main loop delay */
7381 if (game_status != GAME_MODE_PLAYING)
7384 if (!network_player_action_received)
7388 printf("DEBUG: failed to get network player actions in time\n");
7399 printf("::: getting new tape action [%d]\n", FrameCounter);
7402 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7405 if (recorded_player_action != NULL)
7406 for (i = 0; i < MAX_PLAYERS; i++)
7407 stored_player[i].action = recorded_player_action[i];
7410 for (i = 0; i < MAX_PLAYERS; i++)
7412 summarized_player_action |= stored_player[i].action;
7414 if (!network_playing)
7415 stored_player[i].effective_action = stored_player[i].action;
7418 #if defined(PLATFORM_UNIX)
7419 if (network_playing)
7420 SendToServer_MovePlayer(summarized_player_action);
7423 if (!options.network && !setup.team_mode)
7424 local_player->effective_action = summarized_player_action;
7426 for (i = 0; i < MAX_PLAYERS; i++)
7428 int actual_player_action = stored_player[i].effective_action;
7431 /* OLD: overwrite programmed action with tape action (BAD!!!) */
7432 if (stored_player[i].programmed_action)
7433 actual_player_action = stored_player[i].programmed_action;
7436 if (recorded_player_action)
7439 if (stored_player[i].programmed_action &&
7440 stored_player[i].programmed_action != recorded_player_action[i])
7441 printf("::: %d: %d <-> %d\n", i,
7442 stored_player[i].programmed_action, recorded_player_action[i]);
7446 actual_player_action = recorded_player_action[i];
7451 /* NEW: overwrite tape action with programmed action */
7452 if (stored_player[i].programmed_action)
7453 actual_player_action = stored_player[i].programmed_action;
7456 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7458 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7459 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7461 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7466 TapeRecordAction(tape_action);
7469 network_player_action_received = FALSE;
7471 ScrollScreen(NULL, SCROLL_GO_ON);
7477 for (i = 0; i < MAX_PLAYERS; i++)
7478 stored_player[i].Frame++;
7482 /* for downwards compatibility, the following code emulates a fixed bug that
7483 occured when pushing elements (causing elements that just made their last
7484 pushing step to already (if possible) make their first falling step in the
7485 same game frame, which is bad); this code is also needed to use the famous
7486 "spring push bug" which is used in older levels and might be wanted to be
7487 used also in newer levels, but in this case the buggy pushing code is only
7488 affecting the "spring" element and no other elements */
7491 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7493 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7496 for (i = 0; i < MAX_PLAYERS; i++)
7498 struct PlayerInfo *player = &stored_player[i];
7503 if (player->active && player->is_pushing && player->is_moving &&
7505 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7506 Feld[x][y] == EL_SPRING))
7508 if (player->active && player->is_pushing && player->is_moving &&
7512 ContinueMoving(x, y);
7514 /* continue moving after pushing (this is actually a bug) */
7515 if (!IS_MOVING(x, y))
7524 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7526 Changed[x][y] = CE_BITMASK_DEFAULT;
7527 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7530 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7532 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7533 printf("GameActions(): This should never happen!\n");
7535 ChangePage[x][y] = -1;
7540 if (WasJustMoving[x][y] > 0)
7541 WasJustMoving[x][y]--;
7542 if (WasJustFalling[x][y] > 0)
7543 WasJustFalling[x][y]--;
7548 /* reset finished pushing action (not done in ContinueMoving() to allow
7549 continous pushing animation for elements with zero push delay) */
7550 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7552 ResetGfxAnimation(x, y);
7553 DrawLevelField(x, y);
7558 if (IS_BLOCKED(x, y))
7562 Blocked2Moving(x, y, &oldx, &oldy);
7563 if (!IS_MOVING(oldx, oldy))
7565 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7566 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7567 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7568 printf("GameActions(): This should never happen!\n");
7574 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7576 element = Feld[x][y];
7578 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7580 graphic = el2img(element);
7586 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7588 element = graphic = 0;
7592 if (graphic_info[graphic].anim_global_sync)
7593 GfxFrame[x][y] = FrameCounter;
7595 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7596 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7597 ResetRandomAnimationValue(x, y);
7599 SetRandomAnimationValue(x, y);
7602 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7605 if (IS_INACTIVE(element))
7607 if (IS_ANIMATED(graphic))
7608 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7614 /* this may take place after moving, so 'element' may have changed */
7616 if (IS_CHANGING(x, y))
7618 if (IS_CHANGING(x, y) &&
7619 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7623 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7624 element_info[element].event_page_nr[CE_DELAY]);
7626 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7629 element = Feld[x][y];
7630 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7634 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7639 element = Feld[x][y];
7640 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7642 if (element == EL_MOLE)
7643 printf("::: %d, %d, %d [%d]\n",
7644 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7648 if (element == EL_YAMYAM)
7649 printf("::: %d, %d, %d\n",
7650 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7654 if (IS_ANIMATED(graphic) &&
7658 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7661 if (element == EL_BUG)
7662 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7666 if (element == EL_MOLE)
7667 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7671 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7672 EdelsteinFunkeln(x, y);
7674 else if ((element == EL_ACID ||
7675 element == EL_EXIT_OPEN ||
7676 element == EL_SP_EXIT_OPEN ||
7677 element == EL_SP_TERMINAL ||
7678 element == EL_SP_TERMINAL_ACTIVE ||
7679 element == EL_EXTRA_TIME ||
7680 element == EL_SHIELD_NORMAL ||
7681 element == EL_SHIELD_DEADLY) &&
7682 IS_ANIMATED(graphic))
7683 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7684 else if (IS_MOVING(x, y))
7685 ContinueMoving(x, y);
7686 else if (IS_ACTIVE_BOMB(element))
7687 CheckDynamite(x, y);
7689 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7690 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7692 else if (element == EL_AMOEBA_GROWING)
7693 AmoebeWaechst(x, y);
7694 else if (element == EL_AMOEBA_SHRINKING)
7695 AmoebaDisappearing(x, y);
7697 #if !USE_NEW_AMOEBA_CODE
7698 else if (IS_AMOEBALIVE(element))
7699 AmoebeAbleger(x, y);
7702 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7704 else if (element == EL_EXIT_CLOSED)
7706 else if (element == EL_SP_EXIT_CLOSED)
7708 else if (element == EL_EXPANDABLE_WALL_GROWING)
7710 else if (element == EL_EXPANDABLE_WALL ||
7711 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7712 element == EL_EXPANDABLE_WALL_VERTICAL ||
7713 element == EL_EXPANDABLE_WALL_ANY)
7715 else if (element == EL_FLAMES)
7716 CheckForDragon(x, y);
7718 else if (IS_AUTO_CHANGING(element))
7719 ChangeElement(x, y);
7721 else if (element == EL_EXPLOSION)
7722 ; /* drawing of correct explosion animation is handled separately */
7723 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7724 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7727 /* this may take place after moving, so 'element' may have changed */
7728 if (IS_AUTO_CHANGING(Feld[x][y]))
7729 ChangeElement(x, y);
7732 if (IS_BELT_ACTIVE(element))
7733 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7735 if (game.magic_wall_active)
7737 int jx = local_player->jx, jy = local_player->jy;
7739 /* play the element sound at the position nearest to the player */
7740 if ((element == EL_MAGIC_WALL_FULL ||
7741 element == EL_MAGIC_WALL_ACTIVE ||
7742 element == EL_MAGIC_WALL_EMPTYING ||
7743 element == EL_BD_MAGIC_WALL_FULL ||
7744 element == EL_BD_MAGIC_WALL_ACTIVE ||
7745 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7746 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7754 #if USE_NEW_AMOEBA_CODE
7755 /* new experimental amoeba growth stuff */
7757 if (!(FrameCounter % 8))
7760 static unsigned long random = 1684108901;
7762 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7765 x = (random >> 10) % lev_fieldx;
7766 y = (random >> 20) % lev_fieldy;
7768 x = RND(lev_fieldx);
7769 y = RND(lev_fieldy);
7771 element = Feld[x][y];
7773 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7774 if (!IS_PLAYER(x,y) &&
7775 (element == EL_EMPTY ||
7776 element == EL_SAND ||
7777 element == EL_QUICKSAND_EMPTY ||
7778 element == EL_ACID_SPLASH_LEFT ||
7779 element == EL_ACID_SPLASH_RIGHT))
7781 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7782 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7783 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7784 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7785 Feld[x][y] = EL_AMOEBA_DROP;
7788 random = random * 129 + 1;
7794 if (game.explosions_delayed)
7797 game.explosions_delayed = FALSE;
7799 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7801 element = Feld[x][y];
7803 if (ExplodeField[x][y])
7804 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7805 else if (element == EL_EXPLOSION)
7806 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7808 ExplodeField[x][y] = EX_NO_EXPLOSION;
7811 game.explosions_delayed = TRUE;
7814 if (game.magic_wall_active)
7816 if (!(game.magic_wall_time_left % 4))
7818 int element = Feld[magic_wall_x][magic_wall_y];
7820 if (element == EL_BD_MAGIC_WALL_FULL ||
7821 element == EL_BD_MAGIC_WALL_ACTIVE ||
7822 element == EL_BD_MAGIC_WALL_EMPTYING)
7823 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7825 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7828 if (game.magic_wall_time_left > 0)
7830 game.magic_wall_time_left--;
7831 if (!game.magic_wall_time_left)
7833 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7835 element = Feld[x][y];
7837 if (element == EL_MAGIC_WALL_ACTIVE ||
7838 element == EL_MAGIC_WALL_FULL)
7840 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7841 DrawLevelField(x, y);
7843 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7844 element == EL_BD_MAGIC_WALL_FULL)
7846 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7847 DrawLevelField(x, y);
7851 game.magic_wall_active = FALSE;
7856 if (game.light_time_left > 0)
7858 game.light_time_left--;
7860 if (game.light_time_left == 0)
7861 RedrawAllLightSwitchesAndInvisibleElements();
7864 if (game.timegate_time_left > 0)
7866 game.timegate_time_left--;
7868 if (game.timegate_time_left == 0)
7869 CloseAllOpenTimegates();
7872 for (i = 0; i < MAX_PLAYERS; i++)
7874 struct PlayerInfo *player = &stored_player[i];
7876 if (SHIELD_ON(player))
7878 if (player->shield_deadly_time_left)
7879 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7880 else if (player->shield_normal_time_left)
7881 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7885 if (TimeFrames >= FRAMES_PER_SECOND)
7890 if (!level.use_step_counter)
7894 for (i = 0; i < MAX_PLAYERS; i++)
7896 struct PlayerInfo *player = &stored_player[i];
7898 if (SHIELD_ON(player))
7900 player->shield_normal_time_left--;
7902 if (player->shield_deadly_time_left > 0)
7903 player->shield_deadly_time_left--;
7911 if (TimeLeft <= 10 && setup.time_limit)
7912 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7914 DrawGameValue_Time(TimeLeft);
7916 if (!TimeLeft && setup.time_limit)
7917 for (i = 0; i < MAX_PLAYERS; i++)
7918 KillHero(&stored_player[i]);
7920 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
7921 DrawGameValue_Time(TimePlayed);
7924 if (tape.recording || tape.playing)
7925 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
7929 PlayAllPlayersSound();
7931 if (options.debug) /* calculate frames per second */
7933 static unsigned long fps_counter = 0;
7934 static int fps_frames = 0;
7935 unsigned long fps_delay_ms = Counter() - fps_counter;
7939 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7941 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7944 fps_counter = Counter();
7947 redraw_mask |= REDRAW_FPS;
7951 if (stored_player[0].jx != stored_player[0].last_jx ||
7952 stored_player[0].jy != stored_player[0].last_jy)
7953 printf("::: %d, %d, %d, %d, %d\n",
7954 stored_player[0].MovDir,
7955 stored_player[0].MovPos,
7956 stored_player[0].GfxPos,
7957 stored_player[0].Frame,
7958 stored_player[0].StepFrame);
7965 for (i = 0; i < MAX_PLAYERS; i++)
7968 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7970 stored_player[i].Frame += move_frames;
7972 if (stored_player[i].MovPos != 0)
7973 stored_player[i].StepFrame += move_frames;
7975 if (stored_player[i].drop_delay > 0)
7976 stored_player[i].drop_delay--;
7981 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7983 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7985 local_player->show_envelope = 0;
7990 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7992 int min_x = x, min_y = y, max_x = x, max_y = y;
7995 for (i = 0; i < MAX_PLAYERS; i++)
7997 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7999 if (!stored_player[i].active || &stored_player[i] == player)
8002 min_x = MIN(min_x, jx);
8003 min_y = MIN(min_y, jy);
8004 max_x = MAX(max_x, jx);
8005 max_y = MAX(max_y, jy);
8008 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8011 static boolean AllPlayersInVisibleScreen()
8015 for (i = 0; i < MAX_PLAYERS; i++)
8017 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8019 if (!stored_player[i].active)
8022 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8029 void ScrollLevel(int dx, int dy)
8031 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8034 BlitBitmap(drawto_field, drawto_field,
8035 FX + TILEX * (dx == -1) - softscroll_offset,
8036 FY + TILEY * (dy == -1) - softscroll_offset,
8037 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8038 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8039 FX + TILEX * (dx == 1) - softscroll_offset,
8040 FY + TILEY * (dy == 1) - softscroll_offset);
8044 x = (dx == 1 ? BX1 : BX2);
8045 for (y = BY1; y <= BY2; y++)
8046 DrawScreenField(x, y);
8051 y = (dy == 1 ? BY1 : BY2);
8052 for (x = BX1; x <= BX2; x++)
8053 DrawScreenField(x, y);
8056 redraw_mask |= REDRAW_FIELD;
8059 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8061 int nextx = x + dx, nexty = y + dy;
8062 int element = Feld[x][y];
8065 element != EL_SP_PORT_LEFT &&
8066 element != EL_SP_GRAVITY_PORT_LEFT &&
8067 element != EL_SP_PORT_HORIZONTAL &&
8068 element != EL_SP_PORT_ANY) ||
8070 element != EL_SP_PORT_RIGHT &&
8071 element != EL_SP_GRAVITY_PORT_RIGHT &&
8072 element != EL_SP_PORT_HORIZONTAL &&
8073 element != EL_SP_PORT_ANY) ||
8075 element != EL_SP_PORT_UP &&
8076 element != EL_SP_GRAVITY_PORT_UP &&
8077 element != EL_SP_PORT_VERTICAL &&
8078 element != EL_SP_PORT_ANY) ||
8080 element != EL_SP_PORT_DOWN &&
8081 element != EL_SP_GRAVITY_PORT_DOWN &&
8082 element != EL_SP_PORT_VERTICAL &&
8083 element != EL_SP_PORT_ANY) ||
8084 !IN_LEV_FIELD(nextx, nexty) ||
8085 !IS_FREE(nextx, nexty))
8091 static void CheckGravityMovement(struct PlayerInfo *player)
8093 if (game.gravity && !player->programmed_action)
8095 int move_dir_horizontal = player->action & MV_HORIZONTAL;
8096 int move_dir_vertical = player->action & MV_VERTICAL;
8098 (player->last_move_dir & MV_HORIZONTAL ?
8099 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8100 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8101 int jx = player->jx, jy = player->jy;
8102 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8103 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8104 int new_jx = jx + dx, new_jy = jy + dy;
8105 boolean player_is_snapping = player->action & JOY_BUTTON_1;
8107 /* !!! MAKE THIS CUSTOMIZABLE !!! */
8108 boolean field_under_player_is_free_or_acid =
8109 (IN_LEV_FIELD(jx, jy + 1) &&
8110 (IS_FREE(jx, jy + 1) || Feld[jx][jy + 1] == EL_ACID));
8112 boolean field_under_player_is_free_or_acid =
8113 (IN_LEV_FIELD(jx, jy + 1) &&
8114 (IS_FREE(jx, jy + 1)));
8116 boolean player_is_moving_to_valid_field =
8119 !player_is_snapping &&
8121 IN_LEV_FIELD(new_jx, new_jy) &&
8122 (Feld[new_jx][new_jy] == EL_SP_BASE ||
8123 Feld[new_jx][new_jy] == EL_SAND ||
8124 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8125 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
8126 /* !!! extend EL_SAND to anything diggable !!! */
8128 boolean player_is_standing_on_valid_field =
8129 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8130 (IS_WALKABLE(Feld[jx][jy]) &&
8131 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8134 printf("::: checking gravity NOW [%d, %d, %d] [%d] ...\n",
8135 field_under_player_is_free_or_acid,
8136 player_is_standing_on_valid_field,
8137 player_is_moving_to_valid_field,
8138 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1));
8141 if (field_under_player_is_free_or_acid &&
8142 !player_is_standing_on_valid_field &&
8143 !player_is_moving_to_valid_field)
8146 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
8147 jx, jy, FrameCounter);
8150 player->programmed_action = MV_DOWN;
8155 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8158 return CheckGravityMovement(player);
8161 if (game.gravity && !player->programmed_action)
8163 int jx = player->jx, jy = player->jy;
8164 boolean field_under_player_is_free =
8165 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8166 boolean player_is_standing_on_valid_field =
8167 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8168 (IS_WALKABLE(Feld[jx][jy]) &&
8169 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8171 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8172 player->programmed_action = MV_DOWN;
8178 -----------------------------------------------------------------------------
8179 dx, dy: direction (non-diagonal) to try to move the player to
8180 real_dx, real_dy: direction as read from input device (can be diagonal)
8183 boolean MovePlayerOneStep(struct PlayerInfo *player,
8184 int dx, int dy, int real_dx, int real_dy)
8187 static int trigger_sides[4][2] =
8189 /* enter side leave side */
8190 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8191 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8192 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8193 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8195 int move_direction = (dx == -1 ? MV_LEFT :
8196 dx == +1 ? MV_RIGHT :
8198 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8199 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8200 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8202 int jx = player->jx, jy = player->jy;
8203 int new_jx = jx + dx, new_jy = jy + dy;
8207 if (!player->active || (!dx && !dy))
8208 return MF_NO_ACTION;
8210 player->MovDir = (dx < 0 ? MV_LEFT :
8213 dy > 0 ? MV_DOWN : MV_NO_MOVING);
8215 if (!IN_LEV_FIELD(new_jx, new_jy))
8216 return MF_NO_ACTION;
8218 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8219 return MF_NO_ACTION;
8222 element = MovingOrBlocked2Element(new_jx, new_jy);
8224 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8227 if (DONT_RUN_INTO(element))
8229 if (element == EL_ACID && dx == 0 && dy == 1)
8231 SplashAcid(new_jx, new_jy);
8232 Feld[jx][jy] = EL_PLAYER_1;
8233 InitMovingField(jx, jy, MV_DOWN);
8234 Store[jx][jy] = EL_ACID;
8235 ContinueMoving(jx, jy);
8239 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8244 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8245 if (can_move != MF_MOVING)
8248 /* check if DigField() has caused relocation of the player */
8249 if (player->jx != jx || player->jy != jy)
8250 return MF_NO_ACTION;
8252 StorePlayer[jx][jy] = 0;
8253 player->last_jx = jx;
8254 player->last_jy = jy;
8255 player->jx = new_jx;
8256 player->jy = new_jy;
8257 StorePlayer[new_jx][new_jy] = player->element_nr;
8260 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8262 player->step_counter++;
8264 player->drop_delay = 0;
8266 PlayerVisit[jx][jy] = FrameCounter;
8268 ScrollPlayer(player, SCROLL_INIT);
8271 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8273 CheckTriggeredElementChangeSide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
8275 CheckElementChangeSide(jx, jy, Feld[jx][jy], CE_LEFT_BY_PLAYER,leave_side);
8278 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
8280 CheckTriggeredElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8281 CE_OTHER_GETS_ENTERED, enter_side);
8282 CheckElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8283 CE_ENTERED_BY_PLAYER, enter_side);
8290 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8292 int jx = player->jx, jy = player->jy;
8293 int old_jx = jx, old_jy = jy;
8294 int moved = MF_NO_ACTION;
8297 if (!player->active)
8302 if (player->MovPos == 0)
8304 player->is_moving = FALSE;
8305 player->is_digging = FALSE;
8306 player->is_collecting = FALSE;
8307 player->is_snapping = FALSE;
8308 player->is_pushing = FALSE;
8314 if (!player->active || (!dx && !dy))
8319 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8325 if (!FrameReached(&player->move_delay, player->move_delay_value))
8328 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8329 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
8335 /* remove the last programmed player action */
8336 player->programmed_action = 0;
8340 /* should only happen if pre-1.2 tape recordings are played */
8341 /* this is only for backward compatibility */
8343 int original_move_delay_value = player->move_delay_value;
8346 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8350 /* scroll remaining steps with finest movement resolution */
8351 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8353 while (player->MovPos)
8355 ScrollPlayer(player, SCROLL_GO_ON);
8356 ScrollScreen(NULL, SCROLL_GO_ON);
8362 player->move_delay_value = original_move_delay_value;
8365 if (player->last_move_dir & MV_HORIZONTAL)
8367 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8368 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8372 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8373 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8379 if (moved & MF_MOVING && !ScreenMovPos &&
8380 (player == local_player || !options.network))
8382 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8383 int offset = (setup.scroll_delay ? 3 : 0);
8385 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8387 /* actual player has left the screen -- scroll in that direction */
8388 if (jx != old_jx) /* player has moved horizontally */
8389 scroll_x += (jx - old_jx);
8390 else /* player has moved vertically */
8391 scroll_y += (jy - old_jy);
8395 if (jx != old_jx) /* player has moved horizontally */
8397 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8398 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8399 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8401 /* don't scroll over playfield boundaries */
8402 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8403 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8405 /* don't scroll more than one field at a time */
8406 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8408 /* don't scroll against the player's moving direction */
8409 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8410 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8411 scroll_x = old_scroll_x;
8413 else /* player has moved vertically */
8415 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8416 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8417 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8419 /* don't scroll over playfield boundaries */
8420 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8421 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8423 /* don't scroll more than one field at a time */
8424 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8426 /* don't scroll against the player's moving direction */
8427 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8428 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8429 scroll_y = old_scroll_y;
8433 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8435 if (!options.network && !AllPlayersInVisibleScreen())
8437 scroll_x = old_scroll_x;
8438 scroll_y = old_scroll_y;
8442 ScrollScreen(player, SCROLL_INIT);
8443 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8450 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8452 if (!(moved & MF_MOVING) && !player->is_pushing)
8457 player->StepFrame = 0;
8459 if (moved & MF_MOVING)
8461 if (old_jx != jx && old_jy == jy)
8462 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8463 else if (old_jx == jx && old_jy != jy)
8464 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8466 DrawLevelField(jx, jy); /* for "crumbled sand" */
8468 player->last_move_dir = player->MovDir;
8469 player->is_moving = TRUE;
8471 player->is_snapping = FALSE;
8475 player->is_switching = FALSE;
8478 player->is_dropping = FALSE;
8483 static int trigger_sides[4][2] =
8485 /* enter side leave side */
8486 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8487 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8488 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8489 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8491 int move_direction = player->MovDir;
8492 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8493 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8496 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8498 CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8500 player->index_bit, leave_side);
8501 CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8503 player->index_bit, leave_side);
8506 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8508 CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
8509 CE_OTHER_GETS_ENTERED,
8510 player->index_bit, enter_side);
8511 CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
8512 player->index_bit, enter_side);
8523 CheckGravityMovementWhenNotMoving(player);
8526 player->last_move_dir = MV_NO_MOVING;
8528 player->is_moving = FALSE;
8531 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8533 TestIfHeroTouchesBadThing(jx, jy);
8534 TestIfPlayerTouchesCustomElement(jx, jy);
8537 if (!player->active)
8543 void ScrollPlayer(struct PlayerInfo *player, int mode)
8545 int jx = player->jx, jy = player->jy;
8546 int last_jx = player->last_jx, last_jy = player->last_jy;
8547 int move_stepsize = TILEX / player->move_delay_value;
8549 if (!player->active || !player->MovPos)
8552 if (mode == SCROLL_INIT)
8554 player->actual_frame_counter = FrameCounter;
8555 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8557 if (Feld[last_jx][last_jy] == EL_EMPTY)
8558 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8566 else if (!FrameReached(&player->actual_frame_counter, 1))
8569 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8570 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8572 if (!player->block_last_field &&
8573 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8574 Feld[last_jx][last_jy] = EL_EMPTY;
8576 /* before DrawPlayer() to draw correct player graphic for this case */
8577 if (player->MovPos == 0)
8578 CheckGravityMovement(player);
8581 DrawPlayer(player); /* needed here only to cleanup last field */
8584 if (player->MovPos == 0) /* player reached destination field */
8587 if (player->move_delay_reset_counter > 0)
8589 player->move_delay_reset_counter--;
8591 if (player->move_delay_reset_counter == 0)
8593 /* continue with normal speed after quickly moving through gate */
8594 HALVE_PLAYER_SPEED(player);
8596 /* be able to make the next move without delay */
8597 player->move_delay = 0;
8601 if (IS_PASSABLE(Feld[last_jx][last_jy]))
8603 /* continue with normal speed after quickly moving through gate */
8604 HALVE_PLAYER_SPEED(player);
8606 /* be able to make the next move without delay */
8607 player->move_delay = 0;
8611 if (player->block_last_field &&
8612 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8613 Feld[last_jx][last_jy] = EL_EMPTY;
8615 player->last_jx = jx;
8616 player->last_jy = jy;
8618 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8619 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8620 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8622 DrawPlayer(player); /* needed here only to cleanup last field */
8625 if (local_player->friends_still_needed == 0 ||
8626 IS_SP_ELEMENT(Feld[jx][jy]))
8627 player->LevelSolved = player->GameOver = TRUE;
8630 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8632 TestIfHeroTouchesBadThing(jx, jy);
8633 TestIfPlayerTouchesCustomElement(jx, jy);
8635 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8638 if (!player->active)
8642 if (level.use_step_counter)
8648 for (i = 0; i < MAX_PLAYERS; i++)
8650 struct PlayerInfo *player = &stored_player[i];
8652 if (SHIELD_ON(player))
8654 player->shield_normal_time_left--;
8656 if (player->shield_deadly_time_left > 0)
8657 player->shield_deadly_time_left--;
8665 if (TimeLeft <= 10 && setup.time_limit)
8666 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8668 DrawGameValue_Time(TimeLeft);
8670 if (!TimeLeft && setup.time_limit)
8671 for (i = 0; i < MAX_PLAYERS; i++)
8672 KillHero(&stored_player[i]);
8674 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8675 DrawGameValue_Time(TimePlayed);
8678 if (tape.single_step && tape.recording && !tape.pausing &&
8679 !player->programmed_action)
8680 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8684 void ScrollScreen(struct PlayerInfo *player, int mode)
8686 static unsigned long screen_frame_counter = 0;
8688 if (mode == SCROLL_INIT)
8690 /* set scrolling step size according to actual player's moving speed */
8691 ScrollStepSize = TILEX / player->move_delay_value;
8693 screen_frame_counter = FrameCounter;
8694 ScreenMovDir = player->MovDir;
8695 ScreenMovPos = player->MovPos;
8696 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8699 else if (!FrameReached(&screen_frame_counter, 1))
8704 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8705 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8706 redraw_mask |= REDRAW_FIELD;
8709 ScreenMovDir = MV_NO_MOVING;
8712 void TestIfPlayerTouchesCustomElement(int x, int y)
8714 static int xy[4][2] =
8721 static int trigger_sides[4][2] =
8723 /* center side border side */
8724 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8725 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8726 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8727 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8729 static int touch_dir[4] =
8736 int center_element = Feld[x][y]; /* should always be non-moving! */
8739 for (i = 0; i < NUM_DIRECTIONS; i++)
8741 int xx = x + xy[i][0];
8742 int yy = y + xy[i][1];
8743 int center_side = trigger_sides[i][0];
8744 int border_side = trigger_sides[i][1];
8747 if (!IN_LEV_FIELD(xx, yy))
8750 if (IS_PLAYER(x, y))
8752 struct PlayerInfo *player = PLAYERINFO(x, y);
8754 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8755 border_element = Feld[xx][yy]; /* may be moving! */
8756 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8757 border_element = Feld[xx][yy];
8758 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8759 border_element = MovingOrBlocked2Element(xx, yy);
8761 continue; /* center and border element do not touch */
8763 CheckTriggeredElementChangePlayer(xx, yy, border_element,
8764 CE_OTHER_GETS_TOUCHED,
8765 player->index_bit, border_side);
8766 CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
8767 player->index_bit, border_side);
8769 else if (IS_PLAYER(xx, yy))
8771 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8773 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8775 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8776 continue; /* center and border element do not touch */
8779 CheckTriggeredElementChangePlayer(x, y, center_element,
8780 CE_OTHER_GETS_TOUCHED,
8781 player->index_bit, center_side);
8782 CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
8783 player->index_bit, center_side);
8790 void TestIfElementTouchesCustomElement(int x, int y)
8792 static int xy[4][2] =
8799 static int trigger_sides[4][2] =
8801 /* center side border side */
8802 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8803 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8804 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8805 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8807 static int touch_dir[4] =
8814 boolean change_center_element = FALSE;
8815 int center_element_change_page = 0;
8816 int center_element = Feld[x][y]; /* should always be non-moving! */
8819 for (i = 0; i < NUM_DIRECTIONS; i++)
8821 int xx = x + xy[i][0];
8822 int yy = y + xy[i][1];
8823 int center_side = trigger_sides[i][0];
8824 int border_side = trigger_sides[i][1];
8827 if (!IN_LEV_FIELD(xx, yy))
8830 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8831 border_element = Feld[xx][yy]; /* may be moving! */
8832 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8833 border_element = Feld[xx][yy];
8834 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8835 border_element = MovingOrBlocked2Element(xx, yy);
8837 continue; /* center and border element do not touch */
8839 /* check for change of center element (but change it only once) */
8840 if (IS_CUSTOM_ELEMENT(center_element) &&
8841 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8842 !change_center_element)
8844 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8846 struct ElementChangeInfo *change =
8847 &element_info[center_element].change_page[j];
8849 if (change->can_change &&
8850 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8851 change->trigger_side & border_side &&
8853 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8855 change->trigger_element == border_element
8859 change_center_element = TRUE;
8860 center_element_change_page = j;
8867 /* check for change of border element */
8868 if (IS_CUSTOM_ELEMENT(border_element) &&
8869 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8871 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8873 struct ElementChangeInfo *change =
8874 &element_info[border_element].change_page[j];
8876 if (change->can_change &&
8877 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8878 change->trigger_side & center_side &&
8880 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8882 change->trigger_element == center_element
8886 CheckElementChangePage(xx, yy, border_element, CE_OTHER_IS_TOUCHING,
8894 if (change_center_element)
8895 CheckElementChangePage(x, y, center_element, CE_OTHER_IS_TOUCHING,
8896 center_element_change_page);
8899 void TestIfElementHitsCustomElement(int x, int y, int direction)
8901 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8902 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8903 int hitx = x + dx, hity = y + dy;
8904 int hitting_element = Feld[x][y];
8906 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8907 !IS_FREE(hitx, hity) &&
8908 (!IS_MOVING(hitx, hity) ||
8909 MovDir[hitx][hity] != direction ||
8910 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8913 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8917 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8921 CheckElementChangeSide(x, y, hitting_element, CE_HITTING_SOMETHING,
8924 if (IN_LEV_FIELD(hitx, hity))
8926 int opposite_direction = MV_DIR_OPPOSITE(direction);
8927 int hitting_side = direction;
8928 int touched_side = opposite_direction;
8929 int touched_element = MovingOrBlocked2Element(hitx, hity);
8931 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8932 MovDir[hitx][hity] != direction ||
8933 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8942 CheckElementChangeSide(hitx, hity, touched_element, CE_HIT_BY_SOMETHING,
8943 opposite_direction);
8945 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8946 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8948 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8950 struct ElementChangeInfo *change =
8951 &element_info[hitting_element].change_page[i];
8953 if (change->can_change &&
8954 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8955 change->trigger_side & touched_side &&
8958 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8960 change->trigger_element == touched_element
8964 CheckElementChangePage(x, y, hitting_element, CE_OTHER_IS_HITTING,
8971 if (IS_CUSTOM_ELEMENT(touched_element) &&
8972 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8974 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8976 struct ElementChangeInfo *change =
8977 &element_info[touched_element].change_page[i];
8979 if (change->can_change &&
8980 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8981 change->trigger_side & hitting_side &&
8983 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8985 change->trigger_element == hitting_element
8989 CheckElementChangePage(hitx, hity, touched_element,
8990 CE_OTHER_GETS_HIT, i);
9000 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9002 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9003 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9004 int hitx = x + dx, hity = y + dy;
9005 int hitting_element = Feld[x][y];
9007 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9008 !IS_FREE(hitx, hity) &&
9009 (!IS_MOVING(hitx, hity) ||
9010 MovDir[hitx][hity] != direction ||
9011 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9014 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9018 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9022 CheckElementChangeSide(x, y, hitting_element, EP_CAN_SMASH_EVERYTHING,
9025 if (IN_LEV_FIELD(hitx, hity))
9027 int opposite_direction = MV_DIR_OPPOSITE(direction);
9028 int hitting_side = direction;
9029 int touched_side = opposite_direction;
9030 int touched_element = MovingOrBlocked2Element(hitx, hity);
9032 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9033 MovDir[hitx][hity] != direction ||
9034 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9043 CheckElementChangeSide(hitx, hity, touched_element,
9044 CE_SMASHED_BY_SOMETHING, opposite_direction);
9046 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9047 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
9049 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9051 struct ElementChangeInfo *change =
9052 &element_info[hitting_element].change_page[i];
9054 if (change->can_change &&
9055 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
9056 change->trigger_side & touched_side &&
9059 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9061 change->trigger_element == touched_element
9065 CheckElementChangePage(x, y, hitting_element, CE_OTHER_IS_SMASHING,
9072 if (IS_CUSTOM_ELEMENT(touched_element) &&
9073 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
9075 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9077 struct ElementChangeInfo *change =
9078 &element_info[touched_element].change_page[i];
9080 if (change->can_change &&
9081 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
9082 change->trigger_side & hitting_side &&
9084 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9086 change->trigger_element == hitting_element
9090 CheckElementChangePage(hitx, hity, touched_element,
9091 CE_OTHER_GETS_SMASHED, i);
9101 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9103 int i, kill_x = -1, kill_y = -1;
9104 static int test_xy[4][2] =
9111 static int test_dir[4] =
9119 for (i = 0; i < NUM_DIRECTIONS; i++)
9121 int test_x, test_y, test_move_dir, test_element;
9123 test_x = good_x + test_xy[i][0];
9124 test_y = good_y + test_xy[i][1];
9125 if (!IN_LEV_FIELD(test_x, test_y))
9129 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9132 test_element = Feld[test_x][test_y];
9134 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9137 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9138 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9140 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9141 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9149 if (kill_x != -1 || kill_y != -1)
9151 if (IS_PLAYER(good_x, good_y))
9153 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9155 if (player->shield_deadly_time_left > 0)
9156 Bang(kill_x, kill_y);
9157 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9161 Bang(good_x, good_y);
9165 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9167 int i, kill_x = -1, kill_y = -1;
9168 int bad_element = Feld[bad_x][bad_y];
9169 static int test_xy[4][2] =
9176 static int touch_dir[4] =
9183 static int test_dir[4] =
9191 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9194 for (i = 0; i < NUM_DIRECTIONS; i++)
9196 int test_x, test_y, test_move_dir, test_element;
9198 test_x = bad_x + test_xy[i][0];
9199 test_y = bad_y + test_xy[i][1];
9200 if (!IN_LEV_FIELD(test_x, test_y))
9204 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9206 test_element = Feld[test_x][test_y];
9208 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9209 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9211 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9212 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9214 /* good thing is player or penguin that does not move away */
9215 if (IS_PLAYER(test_x, test_y))
9217 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9219 if (bad_element == EL_ROBOT && player->is_moving)
9220 continue; /* robot does not kill player if he is moving */
9222 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9224 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9225 continue; /* center and border element do not touch */
9232 else if (test_element == EL_PENGUIN)
9241 if (kill_x != -1 || kill_y != -1)
9243 if (IS_PLAYER(kill_x, kill_y))
9245 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9247 if (player->shield_deadly_time_left > 0)
9249 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9253 Bang(kill_x, kill_y);
9257 void TestIfHeroTouchesBadThing(int x, int y)
9259 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9262 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
9264 TestIfGoodThingHitsBadThing(x, y, move_dir);
9267 void TestIfBadThingTouchesHero(int x, int y)
9269 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9272 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
9274 TestIfBadThingHitsGoodThing(x, y, move_dir);
9277 void TestIfFriendTouchesBadThing(int x, int y)
9279 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9282 void TestIfBadThingTouchesFriend(int x, int y)
9284 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9287 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9289 int i, kill_x = bad_x, kill_y = bad_y;
9290 static int xy[4][2] =
9298 for (i = 0; i < NUM_DIRECTIONS; i++)
9302 x = bad_x + xy[i][0];
9303 y = bad_y + xy[i][1];
9304 if (!IN_LEV_FIELD(x, y))
9307 element = Feld[x][y];
9308 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9309 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9317 if (kill_x != bad_x || kill_y != bad_y)
9321 void KillHero(struct PlayerInfo *player)
9323 int jx = player->jx, jy = player->jy;
9325 if (!player->active)
9328 /* remove accessible field at the player's position */
9329 Feld[jx][jy] = EL_EMPTY;
9331 /* deactivate shield (else Bang()/Explode() would not work right) */
9332 player->shield_normal_time_left = 0;
9333 player->shield_deadly_time_left = 0;
9339 static void KillHeroUnlessEnemyProtected(int x, int y)
9341 if (!PLAYER_ENEMY_PROTECTED(x, y))
9342 KillHero(PLAYERINFO(x, y));
9345 static void KillHeroUnlessExplosionProtected(int x, int y)
9347 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9348 KillHero(PLAYERINFO(x, y));
9351 void BuryHero(struct PlayerInfo *player)
9353 int jx = player->jx, jy = player->jy;
9355 if (!player->active)
9359 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9361 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
9363 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9365 player->GameOver = TRUE;
9369 void RemoveHero(struct PlayerInfo *player)
9371 int jx = player->jx, jy = player->jy;
9372 int i, found = FALSE;
9374 player->present = FALSE;
9375 player->active = FALSE;
9377 if (!ExplodeField[jx][jy])
9378 StorePlayer[jx][jy] = 0;
9380 for (i = 0; i < MAX_PLAYERS; i++)
9381 if (stored_player[i].active)
9385 AllPlayersGone = TRUE;
9392 =============================================================================
9393 checkDiagonalPushing()
9394 -----------------------------------------------------------------------------
9395 check if diagonal input device direction results in pushing of object
9396 (by checking if the alternative direction is walkable, diggable, ...)
9397 =============================================================================
9400 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9401 int x, int y, int real_dx, int real_dy)
9403 int jx, jy, dx, dy, xx, yy;
9405 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9408 /* diagonal direction: check alternative direction */
9413 xx = jx + (dx == 0 ? real_dx : 0);
9414 yy = jy + (dy == 0 ? real_dy : 0);
9416 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9420 =============================================================================
9422 -----------------------------------------------------------------------------
9423 x, y: field next to player (non-diagonal) to try to dig to
9424 real_dx, real_dy: direction as read from input device (can be diagonal)
9425 =============================================================================
9428 int DigField(struct PlayerInfo *player,
9429 int oldx, int oldy, int x, int y,
9430 int real_dx, int real_dy, int mode)
9432 static int trigger_sides[4] =
9434 CH_SIDE_RIGHT, /* moving left */
9435 CH_SIDE_LEFT, /* moving right */
9436 CH_SIDE_BOTTOM, /* moving up */
9437 CH_SIDE_TOP, /* moving down */
9440 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
9442 int jx = oldx, jy = oldy;
9443 int dx = x - jx, dy = y - jy;
9444 int nextx = x + dx, nexty = y + dy;
9445 int move_direction = (dx == -1 ? MV_LEFT :
9446 dx == +1 ? MV_RIGHT :
9448 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9449 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9450 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
9451 int old_element = Feld[jx][jy];
9454 if (player->MovPos == 0)
9456 player->is_digging = FALSE;
9457 player->is_collecting = FALSE;
9460 if (player->MovPos == 0) /* last pushing move finished */
9461 player->is_pushing = FALSE;
9463 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9465 player->is_switching = FALSE;
9466 player->push_delay = 0;
9468 return MF_NO_ACTION;
9471 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9472 return MF_NO_ACTION;
9477 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
9479 if (IS_TUBE(Feld[jx][jy]) ||
9480 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
9484 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
9485 int tube_leave_directions[][2] =
9487 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9488 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9489 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9490 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
9491 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
9492 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
9493 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
9494 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
9495 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
9496 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
9497 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
9498 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
9501 while (tube_leave_directions[i][0] != tube_element)
9504 if (tube_leave_directions[i][0] == -1) /* should not happen */
9508 if (!(tube_leave_directions[i][1] & move_direction))
9509 return MF_NO_ACTION; /* tube has no opening in this direction */
9514 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9515 old_element = Back[jx][jy];
9519 if (IS_WALKABLE(old_element) &&
9520 !(element_info[old_element].access_direction & move_direction))
9521 return MF_NO_ACTION; /* field has no opening in this direction */
9523 element = Feld[x][y];
9525 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9526 game.engine_version >= VERSION_IDENT(2,2,0,0))
9527 return MF_NO_ACTION;
9531 case EL_SP_PORT_LEFT:
9532 case EL_SP_PORT_RIGHT:
9534 case EL_SP_PORT_DOWN:
9535 case EL_SP_PORT_HORIZONTAL:
9536 case EL_SP_PORT_VERTICAL:
9537 case EL_SP_PORT_ANY:
9538 case EL_SP_GRAVITY_PORT_LEFT:
9539 case EL_SP_GRAVITY_PORT_RIGHT:
9540 case EL_SP_GRAVITY_PORT_UP:
9541 case EL_SP_GRAVITY_PORT_DOWN:
9543 if (!canEnterSupaplexPort(x, y, dx, dy))
9544 return MF_NO_ACTION;
9547 element != EL_SP_PORT_LEFT &&
9548 element != EL_SP_GRAVITY_PORT_LEFT &&
9549 element != EL_SP_PORT_HORIZONTAL &&
9550 element != EL_SP_PORT_ANY) ||
9552 element != EL_SP_PORT_RIGHT &&
9553 element != EL_SP_GRAVITY_PORT_RIGHT &&
9554 element != EL_SP_PORT_HORIZONTAL &&
9555 element != EL_SP_PORT_ANY) ||
9557 element != EL_SP_PORT_UP &&
9558 element != EL_SP_GRAVITY_PORT_UP &&
9559 element != EL_SP_PORT_VERTICAL &&
9560 element != EL_SP_PORT_ANY) ||
9562 element != EL_SP_PORT_DOWN &&
9563 element != EL_SP_GRAVITY_PORT_DOWN &&
9564 element != EL_SP_PORT_VERTICAL &&
9565 element != EL_SP_PORT_ANY) ||
9566 !IN_LEV_FIELD(nextx, nexty) ||
9567 !IS_FREE(nextx, nexty))
9568 return MF_NO_ACTION;
9571 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9572 element == EL_SP_GRAVITY_PORT_RIGHT ||
9573 element == EL_SP_GRAVITY_PORT_UP ||
9574 element == EL_SP_GRAVITY_PORT_DOWN)
9575 game.gravity = !game.gravity;
9577 /* automatically move to the next field with double speed */
9578 player->programmed_action = move_direction;
9580 if (player->move_delay_reset_counter == 0)
9582 player->move_delay_reset_counter = 2; /* two double speed steps */
9584 DOUBLE_PLAYER_SPEED(player);
9587 player->move_delay_reset_counter = 2;
9589 DOUBLE_PLAYER_SPEED(player);
9593 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
9596 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9601 case EL_TUBE_VERTICAL:
9602 case EL_TUBE_HORIZONTAL:
9603 case EL_TUBE_VERTICAL_LEFT:
9604 case EL_TUBE_VERTICAL_RIGHT:
9605 case EL_TUBE_HORIZONTAL_UP:
9606 case EL_TUBE_HORIZONTAL_DOWN:
9607 case EL_TUBE_LEFT_UP:
9608 case EL_TUBE_LEFT_DOWN:
9609 case EL_TUBE_RIGHT_UP:
9610 case EL_TUBE_RIGHT_DOWN:
9613 int tube_enter_directions[][2] =
9615 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9616 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9617 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9618 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
9619 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
9620 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
9621 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
9622 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
9623 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
9624 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
9625 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
9626 { -1, MV_NO_MOVING }
9629 while (tube_enter_directions[i][0] != element)
9632 if (tube_enter_directions[i][0] == -1) /* should not happen */
9636 if (!(tube_enter_directions[i][1] & move_direction))
9637 return MF_NO_ACTION; /* tube has no opening in this direction */
9639 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9646 if (IS_WALKABLE(element))
9648 int sound_action = ACTION_WALKING;
9650 if (!(element_info[element].access_direction & opposite_direction))
9651 return MF_NO_ACTION; /* field not accessible from this direction */
9653 if (element >= EL_GATE_1 && element <= EL_GATE_4)
9655 if (!player->key[element - EL_GATE_1])
9656 return MF_NO_ACTION;
9658 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9660 if (!player->key[element - EL_GATE_1_GRAY])
9661 return MF_NO_ACTION;
9663 else if (element == EL_EXIT_OPEN ||
9664 element == EL_SP_EXIT_OPEN ||
9665 element == EL_SP_EXIT_OPENING)
9667 sound_action = ACTION_PASSING; /* player is passing exit */
9669 else if (element == EL_EMPTY)
9671 sound_action = ACTION_MOVING; /* nothing to walk on */
9674 /* play sound from background or player, whatever is available */
9675 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9676 PlayLevelSoundElementAction(x, y, element, sound_action);
9678 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9682 else if (IS_PASSABLE(element))
9684 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9685 return MF_NO_ACTION;
9687 if (IS_CUSTOM_ELEMENT(element) &&
9688 !(element_info[element].access_direction & opposite_direction))
9689 return MF_NO_ACTION; /* field not accessible from this direction */
9692 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9693 return MF_NO_ACTION;
9696 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9698 if (!player->key[element - EL_EM_GATE_1])
9699 return MF_NO_ACTION;
9701 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9703 if (!player->key[element - EL_EM_GATE_1_GRAY])
9704 return MF_NO_ACTION;
9707 /* automatically move to the next field with double speed */
9708 player->programmed_action = move_direction;
9710 if (player->move_delay_reset_counter == 0)
9712 player->move_delay_reset_counter = 2; /* two double speed steps */
9714 DOUBLE_PLAYER_SPEED(player);
9717 player->move_delay_reset_counter = 2;
9719 DOUBLE_PLAYER_SPEED(player);
9722 PlayLevelSoundAction(x, y, ACTION_PASSING);
9726 else if (IS_DIGGABLE(element))
9730 if (mode != DF_SNAP)
9733 GfxElement[x][y] = GFX_ELEMENT(element);
9736 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9738 player->is_digging = TRUE;
9741 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9743 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED,
9744 player->index_bit, CH_SIDE_ANY);
9747 if (mode == DF_SNAP)
9748 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9753 else if (IS_COLLECTIBLE(element))
9757 if (mode != DF_SNAP)
9759 GfxElement[x][y] = element;
9760 player->is_collecting = TRUE;
9763 if (element == EL_SPEED_PILL)
9764 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9765 else if (element == EL_EXTRA_TIME && level.time > 0)
9768 DrawGameValue_Time(TimeLeft);
9770 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9772 player->shield_normal_time_left += 10;
9773 if (element == EL_SHIELD_DEADLY)
9774 player->shield_deadly_time_left += 10;
9776 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9778 if (player->inventory_size < MAX_INVENTORY_SIZE)
9779 player->inventory_element[player->inventory_size++] = element;
9781 DrawGameValue_Dynamite(local_player->inventory_size);
9783 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9785 player->dynabomb_count++;
9786 player->dynabombs_left++;
9788 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9790 player->dynabomb_size++;
9792 else if (element == EL_DYNABOMB_INCREASE_POWER)
9794 player->dynabomb_xl = TRUE;
9796 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9797 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9799 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9800 element - EL_KEY_1 : element - EL_EM_KEY_1);
9802 player->key[key_nr] = TRUE;
9804 DrawGameValue_Keys(player);
9806 redraw_mask |= REDRAW_DOOR_1;
9808 else if (IS_ENVELOPE(element))
9811 player->show_envelope = element;
9813 ShowEnvelope(element - EL_ENVELOPE_1);
9816 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9820 if (element_info[element].collect_count == 0)
9821 player->inventory_infinite_element = element;
9823 for (i = 0; i < element_info[element].collect_count; i++)
9824 if (player->inventory_size < MAX_INVENTORY_SIZE)
9825 player->inventory_element[player->inventory_size++] = element;
9827 DrawGameValue_Dynamite(local_player->inventory_size);
9829 else if (element_info[element].collect_count > 0)
9831 local_player->gems_still_needed -=
9832 element_info[element].collect_count;
9833 if (local_player->gems_still_needed < 0)
9834 local_player->gems_still_needed = 0;
9836 DrawGameValue_Emeralds(local_player->gems_still_needed);
9839 RaiseScoreElement(element);
9840 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9842 CheckTriggeredElementChangePlayer(x, y, element,
9843 CE_OTHER_GETS_COLLECTED,
9844 player->index_bit, CH_SIDE_ANY);
9847 if (mode == DF_SNAP)
9848 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9853 else if (IS_PUSHABLE(element))
9855 if (mode == DF_SNAP && element != EL_BD_ROCK)
9856 return MF_NO_ACTION;
9858 if (CAN_FALL(element) && dy)
9859 return MF_NO_ACTION;
9861 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9862 !(element == EL_SPRING && level.use_spring_bug))
9863 return MF_NO_ACTION;
9866 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9867 ((move_direction & MV_VERTICAL &&
9868 ((element_info[element].move_pattern & MV_LEFT &&
9869 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9870 (element_info[element].move_pattern & MV_RIGHT &&
9871 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9872 (move_direction & MV_HORIZONTAL &&
9873 ((element_info[element].move_pattern & MV_UP &&
9874 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9875 (element_info[element].move_pattern & MV_DOWN &&
9876 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9877 return MF_NO_ACTION;
9881 /* do not push elements already moving away faster than player */
9882 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9883 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9884 return MF_NO_ACTION;
9886 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9887 return MF_NO_ACTION;
9891 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9893 if (player->push_delay_value == -1)
9894 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9896 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9898 if (!player->is_pushing)
9899 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9903 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9904 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9905 !player_is_pushing))
9906 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9909 if (!player->is_pushing &&
9910 game.engine_version >= VERSION_IDENT(2,2,0,7))
9911 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9915 printf("::: push delay: %ld [%d, %d] [%d]\n",
9916 player->push_delay_value, FrameCounter, game.engine_version,
9917 player->is_pushing);
9920 player->is_pushing = TRUE;
9922 if (!(IN_LEV_FIELD(nextx, nexty) &&
9923 (IS_FREE(nextx, nexty) ||
9924 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9925 IS_SB_ELEMENT(element)))))
9926 return MF_NO_ACTION;
9928 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9929 return MF_NO_ACTION;
9931 if (player->push_delay == 0) /* new pushing; restart delay */
9932 player->push_delay = FrameCounter;
9934 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9935 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9936 element != EL_SPRING && element != EL_BALLOON)
9938 /* make sure that there is no move delay before next try to push */
9939 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9940 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9942 return MF_NO_ACTION;
9946 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9949 if (IS_SB_ELEMENT(element))
9951 if (element == EL_SOKOBAN_FIELD_FULL)
9953 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9954 local_player->sokobanfields_still_needed++;
9957 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9959 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9960 local_player->sokobanfields_still_needed--;
9963 Feld[x][y] = EL_SOKOBAN_OBJECT;
9965 if (Back[x][y] == Back[nextx][nexty])
9966 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9967 else if (Back[x][y] != 0)
9968 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9971 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9974 if (local_player->sokobanfields_still_needed == 0 &&
9975 game.emulation == EMU_SOKOBAN)
9977 player->LevelSolved = player->GameOver = TRUE;
9978 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9982 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9984 InitMovingField(x, y, move_direction);
9985 GfxAction[x][y] = ACTION_PUSHING;
9987 if (mode == DF_SNAP)
9988 ContinueMoving(x, y);
9990 MovPos[x][y] = (dx != 0 ? dx : dy);
9992 Pushed[x][y] = TRUE;
9993 Pushed[nextx][nexty] = TRUE;
9995 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9996 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9998 player->push_delay_value = -1; /* get new value later */
10000 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED,
10001 player->index_bit, dig_side);
10002 CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10003 player->index_bit, dig_side);
10007 else if (IS_SWITCHABLE(element))
10009 if (PLAYER_SWITCHING(player, x, y))
10012 player->is_switching = TRUE;
10013 player->switch_x = x;
10014 player->switch_y = y;
10016 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10018 if (element == EL_ROBOT_WHEEL)
10020 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10024 DrawLevelField(x, y);
10026 else if (element == EL_SP_TERMINAL)
10030 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
10032 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10034 else if (Feld[xx][yy] == EL_SP_TERMINAL)
10035 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10038 else if (IS_BELT_SWITCH(element))
10040 ToggleBeltSwitch(x, y);
10042 else if (element == EL_SWITCHGATE_SWITCH_UP ||
10043 element == EL_SWITCHGATE_SWITCH_DOWN)
10045 ToggleSwitchgateSwitch(x, y);
10047 else if (element == EL_LIGHT_SWITCH ||
10048 element == EL_LIGHT_SWITCH_ACTIVE)
10050 ToggleLightSwitch(x, y);
10053 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
10054 SND_LIGHT_SWITCH_ACTIVATING :
10055 SND_LIGHT_SWITCH_DEACTIVATING);
10058 else if (element == EL_TIMEGATE_SWITCH)
10060 ActivateTimegateSwitch(x, y);
10062 else if (element == EL_BALLOON_SWITCH_LEFT ||
10063 element == EL_BALLOON_SWITCH_RIGHT ||
10064 element == EL_BALLOON_SWITCH_UP ||
10065 element == EL_BALLOON_SWITCH_DOWN ||
10066 element == EL_BALLOON_SWITCH_ANY)
10068 if (element == EL_BALLOON_SWITCH_ANY)
10069 game.balloon_dir = move_direction;
10071 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10072 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10073 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10074 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10077 else if (element == EL_LAMP)
10079 Feld[x][y] = EL_LAMP_ACTIVE;
10080 local_player->lights_still_needed--;
10082 DrawLevelField(x, y);
10084 else if (element == EL_TIME_ORB_FULL)
10086 Feld[x][y] = EL_TIME_ORB_EMPTY;
10088 DrawGameValue_Time(TimeLeft);
10090 DrawLevelField(x, y);
10093 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
10101 if (!PLAYER_SWITCHING(player, x, y))
10103 player->is_switching = TRUE;
10104 player->switch_x = x;
10105 player->switch_y = y;
10107 CheckTriggeredElementChangePlayer(x, y, element,
10108 CE_OTHER_IS_SWITCHING,
10109 player->index_bit, dig_side);
10110 CheckElementChangePlayer(x, y, element, CE_SWITCHED,
10111 player->index_bit, dig_side);
10114 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED,
10115 player->index_bit, dig_side);
10116 CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10117 player->index_bit, dig_side);
10120 return MF_NO_ACTION;
10123 player->push_delay = 0;
10125 if (Feld[x][y] != element) /* really digged/collected something */
10126 player->is_collecting = !player->is_digging;
10131 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10133 int jx = player->jx, jy = player->jy;
10134 int x = jx + dx, y = jy + dy;
10135 int snap_direction = (dx == -1 ? MV_LEFT :
10136 dx == +1 ? MV_RIGHT :
10138 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10140 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
10143 if (!player->active || !IN_LEV_FIELD(x, y))
10151 if (player->MovPos == 0)
10152 player->is_pushing = FALSE;
10154 player->is_snapping = FALSE;
10156 if (player->MovPos == 0)
10158 player->is_moving = FALSE;
10159 player->is_digging = FALSE;
10160 player->is_collecting = FALSE;
10166 if (player->is_snapping)
10169 player->MovDir = snap_direction;
10172 if (player->MovPos == 0)
10175 player->is_moving = FALSE;
10176 player->is_digging = FALSE;
10177 player->is_collecting = FALSE;
10180 player->is_dropping = FALSE;
10182 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10185 player->is_snapping = TRUE;
10188 if (player->MovPos == 0)
10191 player->is_moving = FALSE;
10192 player->is_digging = FALSE;
10193 player->is_collecting = FALSE;
10196 DrawLevelField(x, y);
10202 boolean DropElement(struct PlayerInfo *player)
10204 int jx = player->jx, jy = player->jy;
10205 int old_element = Feld[jx][jy];
10206 int new_element = (player->inventory_size > 0 ?
10207 player->inventory_element[player->inventory_size - 1] :
10208 player->inventory_infinite_element != EL_UNDEFINED ?
10209 player->inventory_infinite_element :
10210 player->dynabombs_left > 0 ?
10211 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10214 /* check if player is active, not moving and ready to drop */
10215 if (!player->active || player->MovPos || player->drop_delay > 0)
10218 /* check if player has anything that can be dropped */
10220 if (new_element == EL_UNDEFINED)
10223 if (player->inventory_size == 0 &&
10224 player->inventory_infinite_element == EL_UNDEFINED &&
10225 player->dynabombs_left == 0)
10229 /* check if anything can be dropped at the current position */
10230 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10233 /* collected custom elements can only be dropped on empty fields */
10235 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10238 if (player->inventory_size > 0 &&
10239 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
10240 && old_element != EL_EMPTY)
10244 if (old_element != EL_EMPTY)
10245 Back[jx][jy] = old_element; /* store old element on this field */
10247 ResetGfxAnimation(jx, jy);
10248 ResetRandomAnimationValue(jx, jy);
10250 if (player->inventory_size > 0 ||
10251 player->inventory_infinite_element != EL_UNDEFINED)
10253 if (player->inventory_size > 0)
10255 player->inventory_size--;
10258 new_element = player->inventory_element[player->inventory_size];
10261 DrawGameValue_Dynamite(local_player->inventory_size);
10263 if (new_element == EL_DYNAMITE)
10264 new_element = EL_DYNAMITE_ACTIVE;
10265 else if (new_element == EL_SP_DISK_RED)
10266 new_element = EL_SP_DISK_RED_ACTIVE;
10269 Feld[jx][jy] = new_element;
10271 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10272 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10274 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10277 /* needed if previous element just changed to "empty" in the last frame */
10278 Changed[jx][jy] = 0; /* allow another change */
10281 CheckTriggeredElementChangePlayer(jx, jy, new_element,
10282 CE_OTHER_GETS_DROPPED,
10283 player->index_bit, CH_SIDE_ANY);
10284 CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
10285 player->index_bit, CH_SIDE_ANY);
10287 TestIfElementTouchesCustomElement(jx, jy);
10289 else /* player is dropping a dyna bomb */
10291 player->dynabombs_left--;
10294 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
10297 Feld[jx][jy] = new_element;
10299 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10300 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10302 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10309 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
10312 InitField_WithBug1(jx, jy, FALSE);
10314 InitField(jx, jy, FALSE);
10315 if (CAN_MOVE(Feld[jx][jy]))
10316 InitMovDir(jx, jy);
10320 new_element = Feld[jx][jy];
10322 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10323 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10325 int move_stepsize = element_info[new_element].move_stepsize;
10326 int direction, dx, dy, nextx, nexty;
10328 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10329 MovDir[jx][jy] = player->MovDir;
10331 direction = MovDir[jx][jy];
10332 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10333 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10337 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
10340 WasJustMoving[jx][jy] = 3;
10342 InitMovingField(jx, jy, direction);
10343 ContinueMoving(jx, jy);
10348 Changed[jx][jy] = 0; /* allow another change */
10351 TestIfElementHitsCustomElement(jx, jy, direction);
10353 CheckElementChangeSide(jx, jy, new_element, CE_HITTING_SOMETHING,
10358 player->drop_delay = 2 * TILEX / move_stepsize + 1;
10362 player->drop_delay = 8 + 8 + 8;
10367 player->is_dropping = TRUE;
10373 /* ------------------------------------------------------------------------- */
10374 /* game sound playing functions */
10375 /* ------------------------------------------------------------------------- */
10377 static int *loop_sound_frame = NULL;
10378 static int *loop_sound_volume = NULL;
10380 void InitPlayLevelSound()
10382 int num_sounds = getSoundListSize();
10384 checked_free(loop_sound_frame);
10385 checked_free(loop_sound_volume);
10387 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10388 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10391 static void PlayLevelSound(int x, int y, int nr)
10393 int sx = SCREENX(x), sy = SCREENY(y);
10394 int volume, stereo_position;
10395 int max_distance = 8;
10396 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10398 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10399 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10402 if (!IN_LEV_FIELD(x, y) ||
10403 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10404 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10407 volume = SOUND_MAX_VOLUME;
10409 if (!IN_SCR_FIELD(sx, sy))
10411 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10412 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10414 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10417 stereo_position = (SOUND_MAX_LEFT +
10418 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10419 (SCR_FIELDX + 2 * max_distance));
10421 if (IS_LOOP_SOUND(nr))
10423 /* This assures that quieter loop sounds do not overwrite louder ones,
10424 while restarting sound volume comparison with each new game frame. */
10426 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10429 loop_sound_volume[nr] = volume;
10430 loop_sound_frame[nr] = FrameCounter;
10433 PlaySoundExt(nr, volume, stereo_position, type);
10436 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10438 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10439 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10440 y < LEVELY(BY1) ? LEVELY(BY1) :
10441 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10445 static void PlayLevelSoundAction(int x, int y, int action)
10447 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10450 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10452 int sound_effect = element_info[element].sound[action];
10454 if (sound_effect != SND_UNDEFINED)
10455 PlayLevelSound(x, y, sound_effect);
10458 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10461 int sound_effect = element_info[element].sound[action];
10463 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10464 PlayLevelSound(x, y, sound_effect);
10467 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10469 int sound_effect = element_info[Feld[x][y]].sound[action];
10471 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10472 PlayLevelSound(x, y, sound_effect);
10475 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10477 int sound_effect = element_info[Feld[x][y]].sound[action];
10479 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10480 StopSound(sound_effect);
10483 static void PlayLevelMusic()
10485 if (levelset.music[level_nr] != MUS_UNDEFINED)
10486 PlayMusic(levelset.music[level_nr]); /* from config file */
10488 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10491 void RaiseScore(int value)
10493 local_player->score += value;
10495 DrawGameValue_Score(local_player->score);
10498 void RaiseScoreElement(int element)
10503 case EL_BD_DIAMOND:
10504 case EL_EMERALD_YELLOW:
10505 case EL_EMERALD_RED:
10506 case EL_EMERALD_PURPLE:
10507 case EL_SP_INFOTRON:
10508 RaiseScore(level.score[SC_EMERALD]);
10511 RaiseScore(level.score[SC_DIAMOND]);
10514 RaiseScore(level.score[SC_CRYSTAL]);
10517 RaiseScore(level.score[SC_PEARL]);
10520 case EL_BD_BUTTERFLY:
10521 case EL_SP_ELECTRON:
10522 RaiseScore(level.score[SC_BUG]);
10525 case EL_BD_FIREFLY:
10526 case EL_SP_SNIKSNAK:
10527 RaiseScore(level.score[SC_SPACESHIP]);
10530 case EL_DARK_YAMYAM:
10531 RaiseScore(level.score[SC_YAMYAM]);
10534 RaiseScore(level.score[SC_ROBOT]);
10537 RaiseScore(level.score[SC_PACMAN]);
10540 RaiseScore(level.score[SC_NUT]);
10543 case EL_SP_DISK_RED:
10544 case EL_DYNABOMB_INCREASE_NUMBER:
10545 case EL_DYNABOMB_INCREASE_SIZE:
10546 case EL_DYNABOMB_INCREASE_POWER:
10547 RaiseScore(level.score[SC_DYNAMITE]);
10549 case EL_SHIELD_NORMAL:
10550 case EL_SHIELD_DEADLY:
10551 RaiseScore(level.score[SC_SHIELD]);
10553 case EL_EXTRA_TIME:
10554 RaiseScore(level.score[SC_TIME_BONUS]);
10560 RaiseScore(level.score[SC_KEY]);
10563 RaiseScore(element_info[element].collect_score);
10568 void RequestQuitGame(boolean ask_if_really_quit)
10570 if (AllPlayersGone ||
10571 !ask_if_really_quit ||
10572 level_editor_test_game ||
10573 Request("Do you really want to quit the game ?",
10574 REQ_ASK | REQ_STAY_CLOSED))
10576 #if defined(PLATFORM_UNIX)
10577 if (options.network)
10578 SendToServer_StopPlaying();
10582 game_status = GAME_MODE_MAIN;
10588 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10593 /* ---------- new game button stuff ---------------------------------------- */
10595 /* graphic position values for game buttons */
10596 #define GAME_BUTTON_XSIZE 30
10597 #define GAME_BUTTON_YSIZE 30
10598 #define GAME_BUTTON_XPOS 5
10599 #define GAME_BUTTON_YPOS 215
10600 #define SOUND_BUTTON_XPOS 5
10601 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10603 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10604 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10605 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10606 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10607 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10608 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10615 } gamebutton_info[NUM_GAME_BUTTONS] =
10618 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10623 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10624 GAME_CTRL_ID_PAUSE,
10628 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10633 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10634 SOUND_CTRL_ID_MUSIC,
10635 "background music on/off"
10638 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10639 SOUND_CTRL_ID_LOOPS,
10640 "sound loops on/off"
10643 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10644 SOUND_CTRL_ID_SIMPLE,
10645 "normal sounds on/off"
10649 void CreateGameButtons()
10653 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10655 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10656 struct GadgetInfo *gi;
10659 unsigned long event_mask;
10660 int gd_xoffset, gd_yoffset;
10661 int gd_x1, gd_x2, gd_y1, gd_y2;
10664 gd_xoffset = gamebutton_info[i].x;
10665 gd_yoffset = gamebutton_info[i].y;
10666 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10667 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10669 if (id == GAME_CTRL_ID_STOP ||
10670 id == GAME_CTRL_ID_PAUSE ||
10671 id == GAME_CTRL_ID_PLAY)
10673 button_type = GD_TYPE_NORMAL_BUTTON;
10675 event_mask = GD_EVENT_RELEASED;
10676 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10677 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10681 button_type = GD_TYPE_CHECK_BUTTON;
10683 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10684 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10685 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10686 event_mask = GD_EVENT_PRESSED;
10687 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10688 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10691 gi = CreateGadget(GDI_CUSTOM_ID, id,
10692 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10693 GDI_X, DX + gd_xoffset,
10694 GDI_Y, DY + gd_yoffset,
10695 GDI_WIDTH, GAME_BUTTON_XSIZE,
10696 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10697 GDI_TYPE, button_type,
10698 GDI_STATE, GD_BUTTON_UNPRESSED,
10699 GDI_CHECKED, checked,
10700 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10701 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10702 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10703 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10704 GDI_EVENT_MASK, event_mask,
10705 GDI_CALLBACK_ACTION, HandleGameButtons,
10709 Error(ERR_EXIT, "cannot create gadget");
10711 game_gadget[id] = gi;
10715 void FreeGameButtons()
10719 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10720 FreeGadget(game_gadget[i]);
10723 static void MapGameButtons()
10727 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10728 MapGadget(game_gadget[i]);
10731 void UnmapGameButtons()
10735 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10736 UnmapGadget(game_gadget[i]);
10739 static void HandleGameButtons(struct GadgetInfo *gi)
10741 int id = gi->custom_id;
10743 if (game_status != GAME_MODE_PLAYING)
10748 case GAME_CTRL_ID_STOP:
10749 RequestQuitGame(TRUE);
10752 case GAME_CTRL_ID_PAUSE:
10753 if (options.network)
10755 #if defined(PLATFORM_UNIX)
10757 SendToServer_ContinuePlaying();
10759 SendToServer_PausePlaying();
10763 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10766 case GAME_CTRL_ID_PLAY:
10769 #if defined(PLATFORM_UNIX)
10770 if (options.network)
10771 SendToServer_ContinuePlaying();
10775 tape.pausing = FALSE;
10776 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10781 case SOUND_CTRL_ID_MUSIC:
10782 if (setup.sound_music)
10784 setup.sound_music = FALSE;
10787 else if (audio.music_available)
10789 setup.sound = setup.sound_music = TRUE;
10791 SetAudioMode(setup.sound);
10797 case SOUND_CTRL_ID_LOOPS:
10798 if (setup.sound_loops)
10799 setup.sound_loops = FALSE;
10800 else if (audio.loops_available)
10802 setup.sound = setup.sound_loops = TRUE;
10803 SetAudioMode(setup.sound);
10807 case SOUND_CTRL_ID_SIMPLE:
10808 if (setup.sound_simple)
10809 setup.sound_simple = FALSE;
10810 else if (audio.sound_available)
10812 setup.sound = setup.sound_simple = TRUE;
10813 SetAudioMode(setup.sound);