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);
2552 if (player->GameOver) /* do not reanimate dead player */
2556 RemoveField(x, y); /* temporarily remove newly placed player */
2557 DrawLevelField(x, y);
2560 if (player->present)
2562 while (player->MovPos)
2564 ScrollPlayer(player, SCROLL_GO_ON);
2565 ScrollScreen(NULL, SCROLL_GO_ON);
2571 Delay(wait_delay_value);
2574 DrawPlayer(player); /* needed here only to cleanup last field */
2575 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2577 player->is_moving = FALSE;
2581 old_jx = player->jx;
2582 old_jy = player->jy;
2585 Feld[x][y] = element;
2586 InitPlayerField(x, y, element, TRUE);
2589 if (player == local_player)
2593 scroll_x += (local_player->jx - old_jx);
2594 scroll_y += (local_player->jy - old_jy);
2596 /* don't scroll over playfield boundaries */
2597 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2598 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2600 /* don't scroll over playfield boundaries */
2601 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2602 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2605 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2606 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2607 local_player->jx - MIDPOSX);
2609 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2610 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2611 local_player->jy - MIDPOSY);
2614 RedrawPlayfield(TRUE, 0,0,0,0);
2623 if (player == local_player)
2625 int scroll_xx = -999, scroll_yy = -999;
2627 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2630 int fx = FX, fy = FY;
2632 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2633 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2634 local_player->jx - MIDPOSX);
2636 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2637 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2638 local_player->jy - MIDPOSY);
2640 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2641 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2646 fx += dx * TILEX / 2;
2647 fy += dy * TILEY / 2;
2649 ScrollLevel(dx, dy);
2652 /* scroll in two steps of half tile size to make things smoother */
2653 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2655 Delay(wait_delay_value);
2657 /* scroll second step to align at full tile size */
2659 Delay(wait_delay_value);
2665 void Explode(int ex, int ey, int phase, int mode)
2672 /* !!! eliminate this variable !!! */
2673 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2678 int last_phase = num_phase * delay;
2679 int half_phase = (num_phase / 2) * delay;
2680 int first_phase_after_start = EX_PHASE_START + 1;
2684 if (game.explosions_delayed)
2686 ExplodeField[ex][ey] = mode;
2690 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2692 int center_element = Feld[ex][ey];
2695 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
2699 /* --- This is only really needed (and now handled) in "Impact()". --- */
2700 /* do not explode moving elements that left the explode field in time */
2701 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2702 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2706 if (mode == EX_NORMAL || mode == EX_CENTER)
2707 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2709 /* remove things displayed in background while burning dynamite */
2710 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2713 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2715 /* put moving element to center field (and let it explode there) */
2716 center_element = MovingOrBlocked2Element(ex, ey);
2717 RemoveMovingField(ex, ey);
2718 Feld[ex][ey] = center_element;
2722 last_phase = element_info[center_element].explosion_delay;
2725 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2727 int xx = x - ex + 1;
2728 int yy = y - ey + 1;
2732 if (!IN_LEV_FIELD(x, y) || (mode != EX_NORMAL && (x != ex || y != ey)))
2735 if (!IN_LEV_FIELD(x, y) ||
2736 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2737 (x != ex || y != ey)))
2741 element = Feld[x][y];
2743 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2745 element = MovingOrBlocked2Element(x, y);
2747 if (!IS_EXPLOSION_PROOF(element))
2748 RemoveMovingField(x, y);
2754 if (IS_EXPLOSION_PROOF(element))
2757 /* indestructible elements can only explode in center (but not flames) */
2758 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2759 element == EL_FLAMES)
2764 if ((IS_INDESTRUCTIBLE(element) &&
2765 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2766 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2767 element == EL_FLAMES)
2771 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2773 if (IS_ACTIVE_BOMB(element))
2775 /* re-activate things under the bomb like gate or penguin */
2776 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2783 /* save walkable background elements while explosion on same tile */
2785 if (IS_INDESTRUCTIBLE(element))
2786 Back[x][y] = element;
2788 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2789 Back[x][y] = element;
2792 /* ignite explodable elements reached by other explosion */
2793 if (element == EL_EXPLOSION)
2794 element = Store2[x][y];
2797 if (AmoebaNr[x][y] &&
2798 (element == EL_AMOEBA_FULL ||
2799 element == EL_BD_AMOEBA ||
2800 element == EL_AMOEBA_GROWING))
2802 AmoebaCnt[AmoebaNr[x][y]]--;
2803 AmoebaCnt2[AmoebaNr[x][y]]--;
2809 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2811 switch(StorePlayer[ex][ey])
2814 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2817 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2820 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2824 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2828 if (game.emulation == EMU_SUPAPLEX)
2829 Store[x][y] = EL_EMPTY;
2831 else if (center_element == EL_MOLE)
2832 Store[x][y] = EL_EMERALD_RED;
2833 else if (center_element == EL_PENGUIN)
2834 Store[x][y] = EL_EMERALD_PURPLE;
2835 else if (center_element == EL_BUG)
2836 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2837 else if (center_element == EL_BD_BUTTERFLY)
2838 Store[x][y] = EL_BD_DIAMOND;
2839 else if (center_element == EL_SP_ELECTRON)
2840 Store[x][y] = EL_SP_INFOTRON;
2841 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2842 Store[x][y] = level.amoeba_content;
2843 else if (center_element == EL_YAMYAM)
2844 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2845 else if (IS_CUSTOM_ELEMENT(center_element) &&
2846 element_info[center_element].content[xx][yy] != EL_EMPTY)
2847 Store[x][y] = element_info[center_element].content[xx][yy];
2848 else if (element == EL_WALL_EMERALD)
2849 Store[x][y] = EL_EMERALD;
2850 else if (element == EL_WALL_DIAMOND)
2851 Store[x][y] = EL_DIAMOND;
2852 else if (element == EL_WALL_BD_DIAMOND)
2853 Store[x][y] = EL_BD_DIAMOND;
2854 else if (element == EL_WALL_EMERALD_YELLOW)
2855 Store[x][y] = EL_EMERALD_YELLOW;
2856 else if (element == EL_WALL_EMERALD_RED)
2857 Store[x][y] = EL_EMERALD_RED;
2858 else if (element == EL_WALL_EMERALD_PURPLE)
2859 Store[x][y] = EL_EMERALD_PURPLE;
2860 else if (element == EL_WALL_PEARL)
2861 Store[x][y] = EL_PEARL;
2862 else if (element == EL_WALL_CRYSTAL)
2863 Store[x][y] = EL_CRYSTAL;
2864 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2865 Store[x][y] = element_info[element].content[1][1];
2867 Store[x][y] = EL_EMPTY;
2869 if (x != ex || y != ey ||
2870 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2871 Store2[x][y] = element;
2874 if (AmoebaNr[x][y] &&
2875 (element == EL_AMOEBA_FULL ||
2876 element == EL_BD_AMOEBA ||
2877 element == EL_AMOEBA_GROWING))
2879 AmoebaCnt[AmoebaNr[x][y]]--;
2880 AmoebaCnt2[AmoebaNr[x][y]]--;
2886 MovDir[x][y] = MovPos[x][y] = 0;
2887 GfxDir[x][y] = MovDir[x][y];
2892 Feld[x][y] = EL_EXPLOSION;
2894 GfxElement[x][y] = center_element;
2896 GfxElement[x][y] = EL_UNDEFINED;
2899 ExplodePhase[x][y] = 1;
2901 ExplodeDelay[x][y] = last_phase;
2906 if (center_element == EL_YAMYAM)
2907 game.yamyam_content_nr =
2908 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2920 last_phase = ExplodeDelay[x][y];
2923 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2927 /* activate this even in non-DEBUG version until cause for crash in
2928 getGraphicAnimationFrame() (see below) is found and eliminated */
2932 if (GfxElement[x][y] == EL_UNDEFINED)
2935 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2936 printf("Explode(): This should never happen!\n");
2939 GfxElement[x][y] = EL_EMPTY;
2945 border_element = Store2[x][y];
2946 if (IS_PLAYER(x, y))
2947 border_element = StorePlayer[x][y];
2949 if (phase == element_info[border_element].ignition_delay ||
2950 phase == last_phase)
2952 boolean border_explosion = FALSE;
2955 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
2957 if (IS_PLAYER(x, y))
2960 KillHeroUnlessExplosionProtected(x, y);
2961 border_explosion = TRUE;
2964 if (phase == last_phase)
2965 printf("::: IS_PLAYER\n");
2968 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
2970 Feld[x][y] = Store2[x][y];
2973 border_explosion = TRUE;
2976 if (phase == last_phase)
2977 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
2980 else if (border_element == EL_AMOEBA_TO_DIAMOND)
2982 AmoebeUmwandeln(x, y);
2984 border_explosion = TRUE;
2987 if (phase == last_phase)
2988 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
2989 element_info[border_element].explosion_delay,
2990 element_info[border_element].ignition_delay,
2996 /* if an element just explodes due to another explosion (chain-reaction),
2997 do not immediately end the new explosion when it was the last frame of
2998 the explosion (as it would be done in the following "if"-statement!) */
2999 if (border_explosion && phase == last_phase)
3006 if (phase == first_phase_after_start)
3008 int element = Store2[x][y];
3010 if (element == EL_BLACK_ORB)
3012 Feld[x][y] = Store2[x][y];
3017 else if (phase == half_phase)
3019 int element = Store2[x][y];
3021 if (IS_PLAYER(x, y))
3022 KillHeroUnlessExplosionProtected(x, y);
3023 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3025 Feld[x][y] = Store2[x][y];
3029 else if (element == EL_AMOEBA_TO_DIAMOND)
3030 AmoebeUmwandeln(x, y);
3034 if (phase == last_phase)
3039 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3042 element = Feld[x][y] = Store[x][y];
3043 Store[x][y] = Store2[x][y] = 0;
3044 GfxElement[x][y] = EL_UNDEFINED;
3046 /* player can escape from explosions and might therefore be still alive */
3047 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3048 element <= EL_PLAYER_IS_EXPLODING_4)
3049 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3051 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3052 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3053 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3056 /* restore probably existing indestructible background element */
3057 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3058 element = Feld[x][y] = Back[x][y];
3061 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3062 GfxDir[x][y] = MV_NO_MOVING;
3063 ChangeDelay[x][y] = 0;
3064 ChangePage[x][y] = -1;
3067 InitField_WithBug2(x, y, FALSE);
3069 InitField(x, y, FALSE);
3071 /* !!! not needed !!! */
3073 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
3074 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3077 if (CAN_MOVE(element))
3082 DrawLevelField(x, y);
3084 TestIfElementTouchesCustomElement(x, y);
3086 if (GFX_CRUMBLED(element))
3087 DrawLevelFieldCrumbledSandNeighbours(x, y);
3089 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3090 StorePlayer[x][y] = 0;
3092 if (ELEM_IS_PLAYER(element))
3093 RelocatePlayer(x, y, element);
3096 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3098 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3102 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3104 int stored = Store[x][y];
3105 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3106 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3109 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3112 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3113 element_info[GfxElement[x][y]].token_name,
3118 DrawLevelFieldCrumbledSand(x, y);
3120 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3122 DrawLevelElement(x, y, Back[x][y]);
3123 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3125 else if (IS_WALKABLE_UNDER(Back[x][y]))
3127 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3128 DrawLevelElementThruMask(x, y, Back[x][y]);
3130 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3131 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3135 void DynaExplode(int ex, int ey)
3138 int dynabomb_element = Feld[ex][ey];
3139 int dynabomb_size = 1;
3140 boolean dynabomb_xl = FALSE;
3141 struct PlayerInfo *player;
3142 static int xy[4][2] =
3150 if (IS_ACTIVE_BOMB(dynabomb_element))
3152 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3153 dynabomb_size = player->dynabomb_size;
3154 dynabomb_xl = player->dynabomb_xl;
3155 player->dynabombs_left++;
3158 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
3160 for (i = 0; i < NUM_DIRECTIONS; i++)
3162 for (j = 1; j <= dynabomb_size; j++)
3164 int x = ex + j * xy[i][0];
3165 int y = ey + j * xy[i][1];
3168 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3171 element = Feld[x][y];
3173 /* do not restart explosions of fields with active bombs */
3174 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3177 Explode(x, y, EX_PHASE_START, EX_BORDER);
3179 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3180 if (element != EL_EMPTY &&
3181 element != EL_SAND &&
3182 element != EL_EXPLOSION &&
3189 void Bang(int x, int y)
3192 int element = MovingOrBlocked2Element(x, y);
3194 int element = Feld[x][y];
3198 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3200 if (IS_PLAYER(x, y))
3203 struct PlayerInfo *player = PLAYERINFO(x, y);
3205 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3206 player->element_nr);
3211 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3213 if (game.emulation == EMU_SUPAPLEX)
3214 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3216 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3221 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3229 case EL_BD_BUTTERFLY:
3232 case EL_DARK_YAMYAM:
3236 RaiseScoreElement(element);
3237 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3239 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3240 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3241 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3242 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3243 case EL_DYNABOMB_INCREASE_NUMBER:
3244 case EL_DYNABOMB_INCREASE_SIZE:
3245 case EL_DYNABOMB_INCREASE_POWER:
3250 case EL_LAMP_ACTIVE:
3252 case EL_AMOEBA_TO_DIAMOND:
3254 if (IS_PLAYER(x, y))
3255 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3257 Explode(x, y, EX_PHASE_START, EX_CENTER);
3260 if (CAN_EXPLODE_DYNA(element))
3262 else if (CAN_EXPLODE_1X1(element))
3263 Explode(x, y, EX_PHASE_START, EX_CENTER);
3265 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3269 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3272 void SplashAcid(int x, int y)
3275 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3276 (!IN_LEV_FIELD(x - 1, y - 2) ||
3277 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3278 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3280 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3281 (!IN_LEV_FIELD(x + 1, y - 2) ||
3282 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3283 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3285 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3287 /* input: position of element entering acid (obsolete) */
3289 int element = Feld[x][y];
3291 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3294 if (element != EL_ACID_SPLASH_LEFT &&
3295 element != EL_ACID_SPLASH_RIGHT)
3297 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3299 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3300 (!IN_LEV_FIELD(x - 1, y - 1) ||
3301 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3302 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3304 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3305 (!IN_LEV_FIELD(x + 1, y - 1) ||
3306 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3307 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3312 static void InitBeltMovement()
3314 static int belt_base_element[4] =
3316 EL_CONVEYOR_BELT_1_LEFT,
3317 EL_CONVEYOR_BELT_2_LEFT,
3318 EL_CONVEYOR_BELT_3_LEFT,
3319 EL_CONVEYOR_BELT_4_LEFT
3321 static int belt_base_active_element[4] =
3323 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3324 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3325 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3326 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3331 /* set frame order for belt animation graphic according to belt direction */
3332 for (i = 0; i < NUM_BELTS; i++)
3336 for (j = 0; j < NUM_BELT_PARTS; j++)
3338 int element = belt_base_active_element[belt_nr] + j;
3339 int graphic = el2img(element);
3341 if (game.belt_dir[i] == MV_LEFT)
3342 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3344 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3348 for (y = 0; y < lev_fieldy; y++)
3350 for (x = 0; x < lev_fieldx; x++)
3352 int element = Feld[x][y];
3354 for (i = 0; i < NUM_BELTS; i++)
3356 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3358 int e_belt_nr = getBeltNrFromBeltElement(element);
3361 if (e_belt_nr == belt_nr)
3363 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3365 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3373 static void ToggleBeltSwitch(int x, int y)
3375 static int belt_base_element[4] =
3377 EL_CONVEYOR_BELT_1_LEFT,
3378 EL_CONVEYOR_BELT_2_LEFT,
3379 EL_CONVEYOR_BELT_3_LEFT,
3380 EL_CONVEYOR_BELT_4_LEFT
3382 static int belt_base_active_element[4] =
3384 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3385 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3386 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3387 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3389 static int belt_base_switch_element[4] =
3391 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3392 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3393 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3394 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3396 static int belt_move_dir[4] =
3404 int element = Feld[x][y];
3405 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3406 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3407 int belt_dir = belt_move_dir[belt_dir_nr];
3410 if (!IS_BELT_SWITCH(element))
3413 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3414 game.belt_dir[belt_nr] = belt_dir;
3416 if (belt_dir_nr == 3)
3419 /* set frame order for belt animation graphic according to belt direction */
3420 for (i = 0; i < NUM_BELT_PARTS; i++)
3422 int element = belt_base_active_element[belt_nr] + i;
3423 int graphic = el2img(element);
3425 if (belt_dir == MV_LEFT)
3426 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3428 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3431 for (yy = 0; yy < lev_fieldy; yy++)
3433 for (xx = 0; xx < lev_fieldx; xx++)
3435 int element = Feld[xx][yy];
3437 if (IS_BELT_SWITCH(element))
3439 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3441 if (e_belt_nr == belt_nr)
3443 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3444 DrawLevelField(xx, yy);
3447 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3449 int e_belt_nr = getBeltNrFromBeltElement(element);
3451 if (e_belt_nr == belt_nr)
3453 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3455 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3456 DrawLevelField(xx, yy);
3459 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3461 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3463 if (e_belt_nr == belt_nr)
3465 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3467 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3468 DrawLevelField(xx, yy);
3475 static void ToggleSwitchgateSwitch(int x, int y)
3479 game.switchgate_pos = !game.switchgate_pos;
3481 for (yy = 0; yy < lev_fieldy; yy++)
3483 for (xx = 0; xx < lev_fieldx; xx++)
3485 int element = Feld[xx][yy];
3487 if (element == EL_SWITCHGATE_SWITCH_UP ||
3488 element == EL_SWITCHGATE_SWITCH_DOWN)
3490 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3491 DrawLevelField(xx, yy);
3493 else if (element == EL_SWITCHGATE_OPEN ||
3494 element == EL_SWITCHGATE_OPENING)
3496 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3498 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3500 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3503 else if (element == EL_SWITCHGATE_CLOSED ||
3504 element == EL_SWITCHGATE_CLOSING)
3506 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3508 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3510 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3517 static int getInvisibleActiveFromInvisibleElement(int element)
3519 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3520 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3521 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3525 static int getInvisibleFromInvisibleActiveElement(int element)
3527 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3528 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3529 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3533 static void RedrawAllLightSwitchesAndInvisibleElements()
3537 for (y = 0; y < lev_fieldy; y++)
3539 for (x = 0; x < lev_fieldx; x++)
3541 int element = Feld[x][y];
3543 if (element == EL_LIGHT_SWITCH &&
3544 game.light_time_left > 0)
3546 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3547 DrawLevelField(x, y);
3549 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3550 game.light_time_left == 0)
3552 Feld[x][y] = EL_LIGHT_SWITCH;
3553 DrawLevelField(x, y);
3555 else if (element == EL_INVISIBLE_STEELWALL ||
3556 element == EL_INVISIBLE_WALL ||
3557 element == EL_INVISIBLE_SAND)
3559 if (game.light_time_left > 0)
3560 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3562 DrawLevelField(x, y);
3564 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3565 element == EL_INVISIBLE_WALL_ACTIVE ||
3566 element == EL_INVISIBLE_SAND_ACTIVE)
3568 if (game.light_time_left == 0)
3569 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3571 DrawLevelField(x, y);
3577 static void ToggleLightSwitch(int x, int y)
3579 int element = Feld[x][y];
3581 game.light_time_left =
3582 (element == EL_LIGHT_SWITCH ?
3583 level.time_light * FRAMES_PER_SECOND : 0);
3585 RedrawAllLightSwitchesAndInvisibleElements();
3588 static void ActivateTimegateSwitch(int x, int y)
3592 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3594 for (yy = 0; yy < lev_fieldy; yy++)
3596 for (xx = 0; xx < lev_fieldx; xx++)
3598 int element = Feld[xx][yy];
3600 if (element == EL_TIMEGATE_CLOSED ||
3601 element == EL_TIMEGATE_CLOSING)
3603 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3604 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3608 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3610 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3611 DrawLevelField(xx, yy);
3618 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3621 inline static int getElementMoveStepsize(int x, int y)
3623 int element = Feld[x][y];
3624 int direction = MovDir[x][y];
3625 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3626 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3627 int horiz_move = (dx != 0);
3628 int sign = (horiz_move ? dx : dy);
3629 int step = sign * element_info[element].move_stepsize;
3631 /* special values for move stepsize for spring and things on conveyor belt */
3635 if (element == EL_SPRING)
3636 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3637 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3638 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3639 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3641 if (CAN_FALL(element) &&
3642 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3643 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3644 else if (element == EL_SPRING)
3645 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3652 void Impact(int x, int y)
3654 boolean lastline = (y == lev_fieldy-1);
3655 boolean object_hit = FALSE;
3656 boolean impact = (lastline || object_hit);
3657 int element = Feld[x][y];
3658 int smashed = EL_UNDEFINED;
3660 if (!lastline) /* check if element below was hit */
3662 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3665 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3666 MovDir[x][y + 1] != MV_DOWN ||
3667 MovPos[x][y + 1] <= TILEY / 2));
3670 object_hit = !IS_FREE(x, y + 1);
3673 /* do not smash moving elements that left the smashed field in time */
3674 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3675 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3679 smashed = MovingOrBlocked2Element(x, y + 1);
3681 impact = (lastline || object_hit);
3684 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3686 SplashAcid(x, y + 1);
3690 /* only reset graphic animation if graphic really changes after impact */
3692 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3694 ResetGfxAnimation(x, y);
3695 DrawLevelField(x, y);
3698 if (impact && CAN_EXPLODE_IMPACT(element))
3703 else if (impact && element == EL_PEARL)
3705 Feld[x][y] = EL_PEARL_BREAKING;
3706 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3709 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3711 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3716 if (impact && element == EL_AMOEBA_DROP)
3718 if (object_hit && IS_PLAYER(x, y + 1))
3719 KillHeroUnlessEnemyProtected(x, y + 1);
3720 else if (object_hit && smashed == EL_PENGUIN)
3724 Feld[x][y] = EL_AMOEBA_GROWING;
3725 Store[x][y] = EL_AMOEBA_WET;
3727 ResetRandomAnimationValue(x, y);
3732 if (object_hit) /* check which object was hit */
3734 if (CAN_PASS_MAGIC_WALL(element) &&
3735 (smashed == EL_MAGIC_WALL ||
3736 smashed == EL_BD_MAGIC_WALL))
3739 int activated_magic_wall =
3740 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3741 EL_BD_MAGIC_WALL_ACTIVE);
3743 /* activate magic wall / mill */
3744 for (yy = 0; yy < lev_fieldy; yy++)
3745 for (xx = 0; xx < lev_fieldx; xx++)
3746 if (Feld[xx][yy] == smashed)
3747 Feld[xx][yy] = activated_magic_wall;
3749 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3750 game.magic_wall_active = TRUE;
3752 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3753 SND_MAGIC_WALL_ACTIVATING :
3754 SND_BD_MAGIC_WALL_ACTIVATING));
3757 if (IS_PLAYER(x, y + 1))
3759 if (CAN_SMASH_PLAYER(element))
3761 KillHeroUnlessEnemyProtected(x, y + 1);
3765 else if (smashed == EL_PENGUIN)
3767 if (CAN_SMASH_PLAYER(element))
3773 else if (element == EL_BD_DIAMOND)
3775 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3781 else if (((element == EL_SP_INFOTRON ||
3782 element == EL_SP_ZONK) &&
3783 (smashed == EL_SP_SNIKSNAK ||
3784 smashed == EL_SP_ELECTRON ||
3785 smashed == EL_SP_DISK_ORANGE)) ||
3786 (element == EL_SP_INFOTRON &&
3787 smashed == EL_SP_DISK_YELLOW))
3793 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3799 else if (CAN_SMASH_EVERYTHING(element))
3801 if (IS_CLASSIC_ENEMY(smashed) ||
3802 CAN_EXPLODE_SMASHED(smashed))
3807 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3809 if (smashed == EL_LAMP ||
3810 smashed == EL_LAMP_ACTIVE)
3815 else if (smashed == EL_NUT)
3817 Feld[x][y + 1] = EL_NUT_BREAKING;
3818 PlayLevelSound(x, y, SND_NUT_BREAKING);
3819 RaiseScoreElement(EL_NUT);
3822 else if (smashed == EL_PEARL)
3824 Feld[x][y + 1] = EL_PEARL_BREAKING;
3825 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3828 else if (smashed == EL_DIAMOND)
3830 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3831 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3834 else if (IS_BELT_SWITCH(smashed))
3836 ToggleBeltSwitch(x, y + 1);
3838 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3839 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3841 ToggleSwitchgateSwitch(x, y + 1);
3843 else if (smashed == EL_LIGHT_SWITCH ||
3844 smashed == EL_LIGHT_SWITCH_ACTIVE)
3846 ToggleLightSwitch(x, y + 1);
3851 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
3854 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3856 CheckTriggeredElementChangeSide(x, y + 1, smashed,
3857 CE_OTHER_IS_SWITCHING, CH_SIDE_TOP);
3858 CheckElementChangeSide(x, y + 1, smashed, CE_SWITCHED, CH_SIDE_TOP);
3863 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3868 /* play sound of magic wall / mill */
3870 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3871 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3873 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3874 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3875 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3876 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3881 /* play sound of object that hits the ground */
3882 if (lastline || object_hit)
3883 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3886 inline static void TurnRoundExt(int x, int y)
3898 { 0, 0 }, { 0, 0 }, { 0, 0 },
3903 int left, right, back;
3907 { MV_DOWN, MV_UP, MV_RIGHT },
3908 { MV_UP, MV_DOWN, MV_LEFT },
3910 { MV_LEFT, MV_RIGHT, MV_DOWN },
3914 { MV_RIGHT, MV_LEFT, MV_UP }
3917 int element = Feld[x][y];
3918 int move_pattern = element_info[element].move_pattern;
3920 int old_move_dir = MovDir[x][y];
3921 int left_dir = turn[old_move_dir].left;
3922 int right_dir = turn[old_move_dir].right;
3923 int back_dir = turn[old_move_dir].back;
3925 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3926 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3927 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3928 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3930 int left_x = x + left_dx, left_y = y + left_dy;
3931 int right_x = x + right_dx, right_y = y + right_dy;
3932 int move_x = x + move_dx, move_y = y + move_dy;
3936 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3938 TestIfBadThingTouchesOtherBadThing(x, y);
3940 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
3941 MovDir[x][y] = right_dir;
3942 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3943 MovDir[x][y] = left_dir;
3945 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3947 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3950 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3951 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3953 TestIfBadThingTouchesOtherBadThing(x, y);
3955 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
3956 MovDir[x][y] = left_dir;
3957 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3958 MovDir[x][y] = right_dir;
3960 if ((element == EL_SPACESHIP ||
3961 element == EL_SP_SNIKSNAK ||
3962 element == EL_SP_ELECTRON)
3963 && MovDir[x][y] != old_move_dir)
3965 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3968 else if (element == EL_YAMYAM)
3970 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3971 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3973 if (can_turn_left && can_turn_right)
3974 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3975 else if (can_turn_left)
3976 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3977 else if (can_turn_right)
3978 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3980 MovDir[x][y] = back_dir;
3982 MovDelay[x][y] = 16 + 16 * RND(3);
3984 else if (element == EL_DARK_YAMYAM)
3986 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3987 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3989 if (can_turn_left && can_turn_right)
3990 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3991 else if (can_turn_left)
3992 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3993 else if (can_turn_right)
3994 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3996 MovDir[x][y] = back_dir;
3998 MovDelay[x][y] = 16 + 16 * RND(3);
4000 else if (element == EL_PACMAN)
4002 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
4003 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
4005 if (can_turn_left && can_turn_right)
4006 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4007 else if (can_turn_left)
4008 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4009 else if (can_turn_right)
4010 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4012 MovDir[x][y] = back_dir;
4014 MovDelay[x][y] = 6 + RND(40);
4016 else if (element == EL_PIG)
4018 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
4019 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
4020 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
4021 boolean should_turn_left, should_turn_right, should_move_on;
4023 int rnd = RND(rnd_value);
4025 should_turn_left = (can_turn_left &&
4027 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4028 y + back_dy + left_dy)));
4029 should_turn_right = (can_turn_right &&
4031 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4032 y + back_dy + right_dy)));
4033 should_move_on = (can_move_on &&
4036 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4037 y + move_dy + left_dy) ||
4038 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4039 y + move_dy + right_dy)));
4041 if (should_turn_left || should_turn_right || should_move_on)
4043 if (should_turn_left && should_turn_right && should_move_on)
4044 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4045 rnd < 2 * rnd_value / 3 ? right_dir :
4047 else if (should_turn_left && should_turn_right)
4048 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4049 else if (should_turn_left && should_move_on)
4050 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4051 else if (should_turn_right && should_move_on)
4052 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4053 else if (should_turn_left)
4054 MovDir[x][y] = left_dir;
4055 else if (should_turn_right)
4056 MovDir[x][y] = right_dir;
4057 else if (should_move_on)
4058 MovDir[x][y] = old_move_dir;
4060 else if (can_move_on && rnd > rnd_value / 8)
4061 MovDir[x][y] = old_move_dir;
4062 else if (can_turn_left && can_turn_right)
4063 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4064 else if (can_turn_left && rnd > rnd_value / 8)
4065 MovDir[x][y] = left_dir;
4066 else if (can_turn_right && rnd > rnd_value/8)
4067 MovDir[x][y] = right_dir;
4069 MovDir[x][y] = back_dir;
4071 xx = x + move_xy[MovDir[x][y]].x;
4072 yy = y + move_xy[MovDir[x][y]].y;
4074 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4075 MovDir[x][y] = old_move_dir;
4079 else if (element == EL_DRAGON)
4081 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(left_x, left_y);
4082 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(right_x, right_y);
4083 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(move_x, move_y);
4085 int rnd = RND(rnd_value);
4088 if (FrameCounter < 1 && x == 0 && y == 29)
4089 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4092 if (can_move_on && rnd > rnd_value / 8)
4093 MovDir[x][y] = old_move_dir;
4094 else if (can_turn_left && can_turn_right)
4095 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4096 else if (can_turn_left && rnd > rnd_value / 8)
4097 MovDir[x][y] = left_dir;
4098 else if (can_turn_right && rnd > rnd_value / 8)
4099 MovDir[x][y] = right_dir;
4101 MovDir[x][y] = back_dir;
4103 xx = x + move_xy[MovDir[x][y]].x;
4104 yy = y + move_xy[MovDir[x][y]].y;
4107 if (FrameCounter < 1 && x == 0 && y == 29)
4108 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4109 xx, yy, Feld[xx][yy],
4114 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4115 MovDir[x][y] = old_move_dir;
4117 if (!IS_FREE(xx, yy))
4118 MovDir[x][y] = old_move_dir;
4122 if (FrameCounter < 1 && x == 0 && y == 29)
4123 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4128 else if (element == EL_MOLE)
4130 boolean can_move_on =
4131 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
4132 IS_AMOEBOID(Feld[move_x][move_y]) ||
4133 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4136 boolean can_turn_left =
4137 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
4138 IS_AMOEBOID(Feld[left_x][left_y])));
4140 boolean can_turn_right =
4141 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
4142 IS_AMOEBOID(Feld[right_x][right_y])));
4144 if (can_turn_left && can_turn_right)
4145 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4146 else if (can_turn_left)
4147 MovDir[x][y] = left_dir;
4149 MovDir[x][y] = right_dir;
4152 if (MovDir[x][y] != old_move_dir)
4155 else if (element == EL_BALLOON)
4157 MovDir[x][y] = game.balloon_dir;
4160 else if (element == EL_SPRING)
4163 if (MovDir[x][y] & MV_HORIZONTAL &&
4164 !SPRING_CAN_ENTER_FIELD(move_x, move_y))
4165 MovDir[x][y] = MV_NO_MOVING;
4167 if (MovDir[x][y] & MV_HORIZONTAL &&
4168 (!SPRING_CAN_ENTER_FIELD(move_x, move_y) ||
4169 SPRING_CAN_ENTER_FIELD(x, y + 1)))
4170 MovDir[x][y] = MV_NO_MOVING;
4175 else if (element == EL_ROBOT ||
4176 element == EL_SATELLITE ||
4177 element == EL_PENGUIN)
4179 int attr_x = -1, attr_y = -1;
4190 for (i = 0; i < MAX_PLAYERS; i++)
4192 struct PlayerInfo *player = &stored_player[i];
4193 int jx = player->jx, jy = player->jy;
4195 if (!player->active)
4199 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4207 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4213 if (element == EL_PENGUIN)
4216 static int xy[4][2] =
4224 for (i = 0; i < NUM_DIRECTIONS; i++)
4226 int ex = x + xy[i][0];
4227 int ey = y + xy[i][1];
4229 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4238 MovDir[x][y] = MV_NO_MOVING;
4240 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4241 else if (attr_x > x)
4242 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4244 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4245 else if (attr_y > y)
4246 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4248 if (element == EL_ROBOT)
4252 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4253 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4254 Moving2Blocked(x, y, &newx, &newy);
4256 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4257 MovDelay[x][y] = 8 + 8 * !RND(3);
4259 MovDelay[x][y] = 16;
4261 else if (element == EL_PENGUIN)
4267 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4269 boolean first_horiz = RND(2);
4270 int new_move_dir = MovDir[x][y];
4273 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4274 Moving2Blocked(x, y, &newx, &newy);
4276 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4280 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4281 Moving2Blocked(x, y, &newx, &newy);
4283 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4286 MovDir[x][y] = old_move_dir;
4290 else /* (element == EL_SATELLITE) */
4296 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4298 boolean first_horiz = RND(2);
4299 int new_move_dir = MovDir[x][y];
4302 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4303 Moving2Blocked(x, y, &newx, &newy);
4305 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4309 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4310 Moving2Blocked(x, y, &newx, &newy);
4312 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4315 MovDir[x][y] = old_move_dir;
4320 else if (move_pattern == MV_TURNING_LEFT ||
4321 move_pattern == MV_TURNING_RIGHT ||
4322 move_pattern == MV_TURNING_LEFT_RIGHT ||
4323 move_pattern == MV_TURNING_RIGHT_LEFT ||
4324 move_pattern == MV_TURNING_RANDOM ||
4325 move_pattern == MV_ALL_DIRECTIONS)
4327 boolean can_turn_left =
4328 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4329 boolean can_turn_right =
4330 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4332 if (move_pattern == MV_TURNING_LEFT)
4333 MovDir[x][y] = left_dir;
4334 else if (move_pattern == MV_TURNING_RIGHT)
4335 MovDir[x][y] = right_dir;
4336 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4337 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4338 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4339 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4340 else if (move_pattern == MV_TURNING_RANDOM)
4341 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4342 can_turn_right && !can_turn_left ? right_dir :
4343 RND(2) ? left_dir : right_dir);
4344 else if (can_turn_left && can_turn_right)
4345 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4346 else if (can_turn_left)
4347 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4348 else if (can_turn_right)
4349 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4351 MovDir[x][y] = back_dir;
4353 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4355 else if (move_pattern == MV_HORIZONTAL ||
4356 move_pattern == MV_VERTICAL)
4358 if (move_pattern & old_move_dir)
4359 MovDir[x][y] = back_dir;
4360 else if (move_pattern == MV_HORIZONTAL)
4361 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4362 else if (move_pattern == MV_VERTICAL)
4363 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4365 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4367 else if (move_pattern & MV_ANY_DIRECTION)
4369 MovDir[x][y] = move_pattern;
4370 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4372 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4374 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4375 MovDir[x][y] = left_dir;
4376 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4377 MovDir[x][y] = right_dir;
4379 if (MovDir[x][y] != old_move_dir)
4380 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4382 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4384 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4385 MovDir[x][y] = right_dir;
4386 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4387 MovDir[x][y] = left_dir;
4389 if (MovDir[x][y] != old_move_dir)
4390 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4392 else if (move_pattern == MV_TOWARDS_PLAYER ||
4393 move_pattern == MV_AWAY_FROM_PLAYER)
4395 int attr_x = -1, attr_y = -1;
4397 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4408 for (i = 0; i < MAX_PLAYERS; i++)
4410 struct PlayerInfo *player = &stored_player[i];
4411 int jx = player->jx, jy = player->jy;
4413 if (!player->active)
4417 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4425 MovDir[x][y] = MV_NO_MOVING;
4427 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4428 else if (attr_x > x)
4429 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4431 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4432 else if (attr_y > y)
4433 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4435 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4437 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4439 boolean first_horiz = RND(2);
4440 int new_move_dir = MovDir[x][y];
4443 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4444 Moving2Blocked(x, y, &newx, &newy);
4446 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4450 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4451 Moving2Blocked(x, y, &newx, &newy);
4453 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4456 MovDir[x][y] = old_move_dir;
4459 else if (move_pattern == MV_WHEN_PUSHED ||
4460 move_pattern == MV_WHEN_DROPPED)
4462 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4463 MovDir[x][y] = MV_NO_MOVING;
4467 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4469 static int test_xy[7][2] =
4479 static int test_dir[7] =
4489 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4490 int move_preference = -1000000; /* start with very low preference */
4491 int new_move_dir = MV_NO_MOVING;
4492 int start_test = RND(4);
4495 for (i = 0; i < NUM_DIRECTIONS; i++)
4497 int move_dir = test_dir[start_test + i];
4498 int move_dir_preference;
4500 xx = x + test_xy[start_test + i][0];
4501 yy = y + test_xy[start_test + i][1];
4503 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4504 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4506 new_move_dir = move_dir;
4511 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4514 move_dir_preference = -1 * RunnerVisit[xx][yy];
4515 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4516 move_dir_preference = PlayerVisit[xx][yy];
4518 if (move_dir_preference > move_preference)
4520 /* prefer field that has not been visited for the longest time */
4521 move_preference = move_dir_preference;
4522 new_move_dir = move_dir;
4524 else if (move_dir_preference == move_preference &&
4525 move_dir == old_move_dir)
4527 /* prefer last direction when all directions are preferred equally */
4528 move_preference = move_dir_preference;
4529 new_move_dir = move_dir;
4533 MovDir[x][y] = new_move_dir;
4534 if (old_move_dir != new_move_dir)
4539 static void TurnRound(int x, int y)
4541 int direction = MovDir[x][y];
4544 GfxDir[x][y] = MovDir[x][y];
4550 GfxDir[x][y] = MovDir[x][y];
4553 if (direction != MovDir[x][y])
4558 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4561 GfxAction[x][y] = ACTION_WAITING;
4565 static boolean JustBeingPushed(int x, int y)
4569 for (i = 0; i < MAX_PLAYERS; i++)
4571 struct PlayerInfo *player = &stored_player[i];
4573 if (player->active && player->is_pushing && player->MovPos)
4575 int next_jx = player->jx + (player->jx - player->last_jx);
4576 int next_jy = player->jy + (player->jy - player->last_jy);
4578 if (x == next_jx && y == next_jy)
4586 void StartMoving(int x, int y)
4589 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4591 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4592 int element = Feld[x][y];
4598 if (MovDelay[x][y] == 0)
4599 GfxAction[x][y] = ACTION_DEFAULT;
4601 /* !!! this should be handled more generic (not only for mole) !!! */
4602 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4603 GfxAction[x][y] = ACTION_DEFAULT;
4606 if (CAN_FALL(element) && y < lev_fieldy - 1)
4608 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4609 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4610 if (JustBeingPushed(x, y))
4613 if (element == EL_QUICKSAND_FULL)
4615 if (IS_FREE(x, y + 1))
4617 InitMovingField(x, y, MV_DOWN);
4618 started_moving = TRUE;
4620 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4621 Store[x][y] = EL_ROCK;
4623 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4625 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4628 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4630 if (!MovDelay[x][y])
4631 MovDelay[x][y] = TILEY + 1;
4640 Feld[x][y] = EL_QUICKSAND_EMPTY;
4641 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4642 Store[x][y + 1] = Store[x][y];
4645 PlayLevelSoundAction(x, y, ACTION_FILLING);
4647 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4651 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4652 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4654 InitMovingField(x, y, MV_DOWN);
4655 started_moving = TRUE;
4657 Feld[x][y] = EL_QUICKSAND_FILLING;
4658 Store[x][y] = element;
4660 PlayLevelSoundAction(x, y, ACTION_FILLING);
4662 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4665 else if (element == EL_MAGIC_WALL_FULL)
4667 if (IS_FREE(x, y + 1))
4669 InitMovingField(x, y, MV_DOWN);
4670 started_moving = TRUE;
4672 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4673 Store[x][y] = EL_CHANGED(Store[x][y]);
4675 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4677 if (!MovDelay[x][y])
4678 MovDelay[x][y] = TILEY/4 + 1;
4687 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4688 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4689 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4693 else if (element == EL_BD_MAGIC_WALL_FULL)
4695 if (IS_FREE(x, y + 1))
4697 InitMovingField(x, y, MV_DOWN);
4698 started_moving = TRUE;
4700 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4701 Store[x][y] = EL_CHANGED2(Store[x][y]);
4703 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4705 if (!MovDelay[x][y])
4706 MovDelay[x][y] = TILEY/4 + 1;
4715 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4716 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4717 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4721 else if (CAN_PASS_MAGIC_WALL(element) &&
4722 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4723 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4725 InitMovingField(x, y, MV_DOWN);
4726 started_moving = TRUE;
4729 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4730 EL_BD_MAGIC_WALL_FILLING);
4731 Store[x][y] = element;
4734 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4736 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4739 SplashAcid(x, y + 1);
4741 InitMovingField(x, y, MV_DOWN);
4742 started_moving = TRUE;
4744 Store[x][y] = EL_ACID;
4746 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4747 GfxAction[x][y + 1] = ACTION_ACTIVE;
4751 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4752 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4753 (Feld[x][y + 1] == EL_BLOCKED)) ||
4754 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4755 CAN_SMASH(element) && WasJustFalling[x][y] &&
4756 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4760 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4761 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4762 WasJustMoving[x][y] && !Pushed[x][y + 1])
4764 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4765 WasJustMoving[x][y])
4770 /* this is needed for a special case not covered by calling "Impact()"
4771 from "ContinueMoving()": if an element moves to a tile directly below
4772 another element which was just falling on that tile (which was empty
4773 in the previous frame), the falling element above would just stop
4774 instead of smashing the element below (in previous version, the above
4775 element was just checked for "moving" instead of "falling", resulting
4776 in incorrect smashes caused by horizontal movement of the above
4777 element; also, the case of the player being the element to smash was
4778 simply not covered here... :-/ ) */
4781 WasJustMoving[x][y] = 0;
4782 WasJustFalling[x][y] = 0;
4787 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4789 if (MovDir[x][y] == MV_NO_MOVING)
4791 InitMovingField(x, y, MV_DOWN);
4792 started_moving = TRUE;
4795 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4797 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4798 MovDir[x][y] = MV_DOWN;
4800 InitMovingField(x, y, MV_DOWN);
4801 started_moving = TRUE;
4803 else if (element == EL_AMOEBA_DROP)
4805 Feld[x][y] = EL_AMOEBA_GROWING;
4806 Store[x][y] = EL_AMOEBA_WET;
4808 /* Store[x][y + 1] must be zero, because:
4809 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4812 #if OLD_GAME_BEHAVIOUR
4813 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4815 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4816 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4817 element != EL_DX_SUPABOMB)
4820 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4821 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4822 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4823 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4826 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4827 (IS_FREE(x - 1, y + 1) ||
4828 Feld[x - 1][y + 1] == EL_ACID));
4829 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4830 (IS_FREE(x + 1, y + 1) ||
4831 Feld[x + 1][y + 1] == EL_ACID));
4832 boolean can_fall_any = (can_fall_left || can_fall_right);
4833 boolean can_fall_both = (can_fall_left && can_fall_right);
4835 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4837 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4839 if (slippery_type == SLIPPERY_ONLY_LEFT)
4840 can_fall_right = FALSE;
4841 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4842 can_fall_left = FALSE;
4843 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4844 can_fall_right = FALSE;
4845 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4846 can_fall_left = FALSE;
4848 can_fall_any = (can_fall_left || can_fall_right);
4849 can_fall_both = (can_fall_left && can_fall_right);
4854 if (can_fall_both &&
4855 (game.emulation != EMU_BOULDERDASH &&
4856 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4857 can_fall_left = !(can_fall_right = RND(2));
4859 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4860 started_moving = TRUE;
4864 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4866 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4869 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4870 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4871 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4872 int belt_dir = game.belt_dir[belt_nr];
4874 if ((belt_dir == MV_LEFT && left_is_free) ||
4875 (belt_dir == MV_RIGHT && right_is_free))
4878 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4881 InitMovingField(x, y, belt_dir);
4882 started_moving = TRUE;
4885 Pushed[x][y] = TRUE;
4886 Pushed[nextx][y] = TRUE;
4889 GfxAction[x][y] = ACTION_DEFAULT;
4893 MovDir[x][y] = 0; /* if element was moving, stop it */
4898 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4899 if (CAN_MOVE(element) && !started_moving)
4901 int move_pattern = element_info[element].move_pattern;
4904 Moving2Blocked(x, y, &newx, &newy);
4907 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4910 if ((element == EL_SATELLITE ||
4911 element == EL_BALLOON ||
4912 element == EL_SPRING)
4913 && JustBeingPushed(x, y))
4918 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4919 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4920 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4923 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4924 element, element_info[element].token_name,
4925 WasJustMoving[x][y],
4926 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4927 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4928 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4929 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4933 WasJustMoving[x][y] = 0;
4936 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4939 if (Feld[x][y] != element) /* element has changed */
4941 element = Feld[x][y];
4942 move_pattern = element_info[element].move_pattern;
4944 if (!CAN_MOVE(element))
4948 if (Feld[x][y] != element) /* element has changed */
4956 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4957 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4959 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4961 Moving2Blocked(x, y, &newx, &newy);
4962 if (Feld[newx][newy] == EL_BLOCKED)
4963 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4969 if (FrameCounter < 1 && x == 0 && y == 29)
4970 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4973 if (!MovDelay[x][y]) /* start new movement phase */
4975 /* all objects that can change their move direction after each step
4976 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4978 if (element != EL_YAMYAM &&
4979 element != EL_DARK_YAMYAM &&
4980 element != EL_PACMAN &&
4981 !(move_pattern & MV_ANY_DIRECTION) &&
4982 move_pattern != MV_TURNING_LEFT &&
4983 move_pattern != MV_TURNING_RIGHT &&
4984 move_pattern != MV_TURNING_LEFT_RIGHT &&
4985 move_pattern != MV_TURNING_RIGHT_LEFT &&
4986 move_pattern != MV_TURNING_RANDOM)
4991 if (FrameCounter < 1 && x == 0 && y == 29)
4992 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4995 if (MovDelay[x][y] && (element == EL_BUG ||
4996 element == EL_SPACESHIP ||
4997 element == EL_SP_SNIKSNAK ||
4998 element == EL_SP_ELECTRON ||
4999 element == EL_MOLE))
5000 DrawLevelField(x, y);
5004 if (MovDelay[x][y]) /* wait some time before next movement */
5009 if (element == EL_YAMYAM)
5012 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5013 DrawLevelElementAnimation(x, y, element);
5017 if (MovDelay[x][y]) /* element still has to wait some time */
5020 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5021 ResetGfxAnimation(x, y);
5025 if (GfxAction[x][y] != ACTION_WAITING)
5026 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5028 GfxAction[x][y] = ACTION_WAITING;
5032 if (element == EL_ROBOT ||
5034 element == EL_PACMAN ||
5036 element == EL_YAMYAM ||
5037 element == EL_DARK_YAMYAM)
5040 DrawLevelElementAnimation(x, y, element);
5042 DrawLevelElementAnimationIfNeeded(x, y, element);
5044 PlayLevelSoundAction(x, y, ACTION_WAITING);
5046 else if (element == EL_SP_ELECTRON)
5047 DrawLevelElementAnimationIfNeeded(x, y, element);
5048 else if (element == EL_DRAGON)
5051 int dir = MovDir[x][y];
5052 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5053 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5054 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5055 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5056 dir == MV_UP ? IMG_FLAMES_1_UP :
5057 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5058 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5061 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5064 GfxAction[x][y] = ACTION_ATTACKING;
5066 if (IS_PLAYER(x, y))
5067 DrawPlayerField(x, y);
5069 DrawLevelField(x, y);
5071 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5073 for (i = 1; i <= 3; i++)
5075 int xx = x + i * dx;
5076 int yy = y + i * dy;
5077 int sx = SCREENX(xx);
5078 int sy = SCREENY(yy);
5079 int flame_graphic = graphic + (i - 1);
5081 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5086 int flamed = MovingOrBlocked2Element(xx, yy);
5088 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5091 RemoveMovingField(xx, yy);
5093 Feld[xx][yy] = EL_FLAMES;
5094 if (IN_SCR_FIELD(sx, sy))
5096 DrawLevelFieldCrumbledSand(xx, yy);
5097 DrawGraphic(sx, sy, flame_graphic, frame);
5102 if (Feld[xx][yy] == EL_FLAMES)
5103 Feld[xx][yy] = EL_EMPTY;
5104 DrawLevelField(xx, yy);
5109 if (MovDelay[x][y]) /* element still has to wait some time */
5111 PlayLevelSoundAction(x, y, ACTION_WAITING);
5117 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5118 for all other elements GfxAction will be set by InitMovingField() */
5119 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5120 GfxAction[x][y] = ACTION_MOVING;
5124 /* now make next step */
5126 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5128 if (DONT_COLLIDE_WITH(element) &&
5129 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5130 !PLAYER_ENEMY_PROTECTED(newx, newy))
5133 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5137 /* player killed by element which is deadly when colliding with */
5139 KillHero(PLAYERINFO(newx, newy));
5146 else if (CAN_MOVE_INTO_ACID(element) &&
5147 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5148 (MovDir[x][y] == MV_DOWN ||
5149 game.engine_version > VERSION_IDENT(3,0,8,0)))
5151 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5152 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5156 else if ((element == EL_PENGUIN ||
5157 element == EL_ROBOT ||
5158 element == EL_SATELLITE ||
5159 element == EL_BALLOON ||
5160 IS_CUSTOM_ELEMENT(element)) &&
5161 IN_LEV_FIELD(newx, newy) &&
5162 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5165 SplashAcid(newx, newy);
5166 Store[x][y] = EL_ACID;
5168 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5170 if (Feld[newx][newy] == EL_EXIT_OPEN)
5174 DrawLevelField(x, y);
5176 Feld[x][y] = EL_EMPTY;
5177 DrawLevelField(x, y);
5180 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5181 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5182 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5184 local_player->friends_still_needed--;
5185 if (!local_player->friends_still_needed &&
5186 !local_player->GameOver && AllPlayersGone)
5187 local_player->LevelSolved = local_player->GameOver = TRUE;
5191 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5193 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5194 DrawLevelField(newx, newy);
5196 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5198 else if (!IS_FREE(newx, newy))
5200 GfxAction[x][y] = ACTION_WAITING;
5202 if (IS_PLAYER(x, y))
5203 DrawPlayerField(x, y);
5205 DrawLevelField(x, y);
5210 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5212 if (IS_FOOD_PIG(Feld[newx][newy]))
5214 if (IS_MOVING(newx, newy))
5215 RemoveMovingField(newx, newy);
5218 Feld[newx][newy] = EL_EMPTY;
5219 DrawLevelField(newx, newy);
5222 PlayLevelSound(x, y, SND_PIG_DIGGING);
5224 else if (!IS_FREE(newx, newy))
5226 if (IS_PLAYER(x, y))
5227 DrawPlayerField(x, y);
5229 DrawLevelField(x, y);
5238 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5241 else if (IS_CUSTOM_ELEMENT(element) &&
5242 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5246 !IS_FREE(newx, newy)
5251 int new_element = Feld[newx][newy];
5254 printf("::: '%s' digs '%s' [%d]\n",
5255 element_info[element].token_name,
5256 element_info[Feld[newx][newy]].token_name,
5257 StorePlayer[newx][newy]);
5260 if (!IS_FREE(newx, newy))
5262 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5263 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5266 /* no element can dig solid indestructible elements */
5267 if (IS_INDESTRUCTIBLE(new_element) &&
5268 !IS_DIGGABLE(new_element) &&
5269 !IS_COLLECTIBLE(new_element))
5272 if (AmoebaNr[newx][newy] &&
5273 (new_element == EL_AMOEBA_FULL ||
5274 new_element == EL_BD_AMOEBA ||
5275 new_element == EL_AMOEBA_GROWING))
5277 AmoebaCnt[AmoebaNr[newx][newy]]--;
5278 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5281 if (IS_MOVING(newx, newy))
5282 RemoveMovingField(newx, newy);
5285 RemoveField(newx, newy);
5286 DrawLevelField(newx, newy);
5289 PlayLevelSoundAction(x, y, action);
5292 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5293 element_info[element].can_leave_element = TRUE;
5295 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5297 RunnerVisit[x][y] = FrameCounter;
5298 PlayerVisit[x][y] /= 8; /* expire player visit path */
5304 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5306 if (!IS_FREE(newx, newy))
5308 if (IS_PLAYER(x, y))
5309 DrawPlayerField(x, y);
5311 DrawLevelField(x, y);
5317 boolean wanna_flame = !RND(10);
5318 int dx = newx - x, dy = newy - y;
5319 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5320 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5321 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5322 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5323 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5324 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5327 IS_CLASSIC_ENEMY(element1) ||
5328 IS_CLASSIC_ENEMY(element2)) &&
5329 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5330 element1 != EL_FLAMES && element2 != EL_FLAMES)
5333 ResetGfxAnimation(x, y);
5334 GfxAction[x][y] = ACTION_ATTACKING;
5337 if (IS_PLAYER(x, y))
5338 DrawPlayerField(x, y);
5340 DrawLevelField(x, y);
5342 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5344 MovDelay[x][y] = 50;
5346 Feld[newx][newy] = EL_FLAMES;
5347 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5348 Feld[newx1][newy1] = EL_FLAMES;
5349 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5350 Feld[newx2][newy2] = EL_FLAMES;
5356 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5357 Feld[newx][newy] == EL_DIAMOND)
5359 if (IS_MOVING(newx, newy))
5360 RemoveMovingField(newx, newy);
5363 Feld[newx][newy] = EL_EMPTY;
5364 DrawLevelField(newx, newy);
5367 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5369 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5370 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5372 if (AmoebaNr[newx][newy])
5374 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5375 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5376 Feld[newx][newy] == EL_BD_AMOEBA)
5377 AmoebaCnt[AmoebaNr[newx][newy]]--;
5382 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5384 if (IS_MOVING(newx, newy))
5387 RemoveMovingField(newx, newy);
5391 Feld[newx][newy] = EL_EMPTY;
5392 DrawLevelField(newx, newy);
5395 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5397 else if ((element == EL_PACMAN || element == EL_MOLE)
5398 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5400 if (AmoebaNr[newx][newy])
5402 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5403 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5404 Feld[newx][newy] == EL_BD_AMOEBA)
5405 AmoebaCnt[AmoebaNr[newx][newy]]--;
5408 if (element == EL_MOLE)
5410 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5411 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5413 ResetGfxAnimation(x, y);
5414 GfxAction[x][y] = ACTION_DIGGING;
5415 DrawLevelField(x, y);
5417 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5419 return; /* wait for shrinking amoeba */
5421 else /* element == EL_PACMAN */
5423 Feld[newx][newy] = EL_EMPTY;
5424 DrawLevelField(newx, newy);
5425 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5428 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5429 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5430 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5432 /* wait for shrinking amoeba to completely disappear */
5435 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5437 /* object was running against a wall */
5442 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5443 DrawLevelElementAnimation(x, y, element);
5445 if (element == EL_BUG ||
5446 element == EL_SPACESHIP ||
5447 element == EL_SP_SNIKSNAK)
5448 DrawLevelField(x, y);
5449 else if (element == EL_MOLE)
5450 DrawLevelField(x, y);
5451 else if (element == EL_BD_BUTTERFLY ||
5452 element == EL_BD_FIREFLY)
5453 DrawLevelElementAnimationIfNeeded(x, y, element);
5454 else if (element == EL_SATELLITE)
5455 DrawLevelElementAnimationIfNeeded(x, y, element);
5456 else if (element == EL_SP_ELECTRON)
5457 DrawLevelElementAnimationIfNeeded(x, y, element);
5460 if (DONT_TOUCH(element))
5461 TestIfBadThingTouchesHero(x, y);
5464 PlayLevelSoundAction(x, y, ACTION_WAITING);
5470 InitMovingField(x, y, MovDir[x][y]);
5472 PlayLevelSoundAction(x, y, ACTION_MOVING);
5476 ContinueMoving(x, y);
5479 void ContinueMoving(int x, int y)
5481 int element = Feld[x][y];
5482 struct ElementInfo *ei = &element_info[element];
5483 int direction = MovDir[x][y];
5484 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5485 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5486 int newx = x + dx, newy = y + dy;
5488 int nextx = newx + dx, nexty = newy + dy;
5491 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5492 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5494 boolean pushed_by_player = Pushed[x][y];
5497 MovPos[x][y] += getElementMoveStepsize(x, y);
5500 if (pushed_by_player && IS_PLAYER(x, y))
5502 /* special case: moving object pushed by player */
5503 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5506 if (pushed_by_player) /* special case: moving object pushed by player */
5507 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5510 if (ABS(MovPos[x][y]) < TILEX)
5512 DrawLevelField(x, y);
5514 return; /* element is still moving */
5517 /* element reached destination field */
5519 Feld[x][y] = EL_EMPTY;
5520 Feld[newx][newy] = element;
5521 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5523 if (element == EL_MOLE)
5525 Feld[x][y] = EL_SAND;
5527 DrawLevelFieldCrumbledSandNeighbours(x, y);
5529 else if (element == EL_QUICKSAND_FILLING)
5531 element = Feld[newx][newy] = get_next_element(element);
5532 Store[newx][newy] = Store[x][y];
5534 else if (element == EL_QUICKSAND_EMPTYING)
5536 Feld[x][y] = get_next_element(element);
5537 element = Feld[newx][newy] = Store[x][y];
5539 else if (element == EL_MAGIC_WALL_FILLING)
5541 element = Feld[newx][newy] = get_next_element(element);
5542 if (!game.magic_wall_active)
5543 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5544 Store[newx][newy] = Store[x][y];
5546 else if (element == EL_MAGIC_WALL_EMPTYING)
5548 Feld[x][y] = get_next_element(element);
5549 if (!game.magic_wall_active)
5550 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5551 element = Feld[newx][newy] = Store[x][y];
5553 else if (element == EL_BD_MAGIC_WALL_FILLING)
5555 element = Feld[newx][newy] = get_next_element(element);
5556 if (!game.magic_wall_active)
5557 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5558 Store[newx][newy] = Store[x][y];
5560 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5562 Feld[x][y] = get_next_element(element);
5563 if (!game.magic_wall_active)
5564 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5565 element = Feld[newx][newy] = Store[x][y];
5567 else if (element == EL_AMOEBA_DROPPING)
5569 Feld[x][y] = get_next_element(element);
5570 element = Feld[newx][newy] = Store[x][y];
5572 else if (element == EL_SOKOBAN_OBJECT)
5575 Feld[x][y] = Back[x][y];
5577 if (Back[newx][newy])
5578 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5580 Back[x][y] = Back[newx][newy] = 0;
5582 else if (Store[x][y] == EL_ACID)
5584 element = Feld[newx][newy] = EL_ACID;
5588 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5589 MovDelay[newx][newy] = 0;
5591 /* copy element change control values to new field */
5592 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5593 ChangePage[newx][newy] = ChangePage[x][y];
5594 Changed[newx][newy] = Changed[x][y];
5595 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5597 ChangeDelay[x][y] = 0;
5598 ChangePage[x][y] = -1;
5599 Changed[x][y] = CE_BITMASK_DEFAULT;
5600 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5602 /* copy animation control values to new field */
5603 GfxFrame[newx][newy] = GfxFrame[x][y];
5604 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5605 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5606 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5608 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5610 ResetGfxAnimation(x, y); /* reset animation values for old field */
5613 /* some elements can leave other elements behind after moving */
5614 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5615 ei->move_leave_element != EL_EMPTY &&
5616 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5617 ei->can_leave_element_last))
5619 Feld[x][y] = ei->move_leave_element;
5620 InitField(x, y, FALSE);
5622 if (GFX_CRUMBLED(Feld[x][y]))
5623 DrawLevelFieldCrumbledSandNeighbours(x, y);
5626 ei->can_leave_element_last = ei->can_leave_element;
5627 ei->can_leave_element = FALSE;
5631 /* 2.1.1 (does not work correctly for spring) */
5632 if (!CAN_MOVE(element))
5633 MovDir[newx][newy] = 0;
5637 /* (does not work for falling objects that slide horizontally) */
5638 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5639 MovDir[newx][newy] = 0;
5642 if (!CAN_MOVE(element) ||
5643 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5644 MovDir[newx][newy] = 0;
5647 if (!CAN_MOVE(element) ||
5648 (CAN_FALL(element) && direction == MV_DOWN))
5649 GfxDir[x][y] = MovDir[newx][newy] = 0;
5654 DrawLevelField(x, y);
5655 DrawLevelField(newx, newy);
5657 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5659 /* prevent pushed element from moving on in pushed direction */
5660 if (pushed_by_player && CAN_MOVE(element) &&
5661 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5662 !(element_info[element].move_pattern & direction))
5663 TurnRound(newx, newy);
5666 /* prevent elements on conveyor belt from moving on in last direction */
5667 if (pushed_by_conveyor && CAN_FALL(element) &&
5668 direction & MV_HORIZONTAL)
5669 MovDir[newx][newy] = 0;
5672 if (!pushed_by_player)
5674 WasJustMoving[newx][newy] = 3;
5676 if (CAN_FALL(element) && direction == MV_DOWN)
5677 WasJustFalling[newx][newy] = 3;
5680 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5682 TestIfBadThingTouchesHero(newx, newy);
5683 TestIfBadThingTouchesFriend(newx, newy);
5685 if (!IS_CUSTOM_ELEMENT(element))
5686 TestIfBadThingTouchesOtherBadThing(newx, newy);
5688 else if (element == EL_PENGUIN)
5689 TestIfFriendTouchesBadThing(newx, newy);
5691 if (CAN_FALL(element) && direction == MV_DOWN &&
5692 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5696 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5700 if (ChangePage[newx][newy] != -1) /* delayed change */
5701 ChangeElement(newx, newy, ChangePage[newx][newy]);
5706 TestIfElementHitsCustomElement(newx, newy, direction);
5710 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5712 int hitting_element = Feld[newx][newy];
5714 /* !!! fix side (direction) orientation here and elsewhere !!! */
5715 CheckElementChangeSide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
5719 if (IN_LEV_FIELD(nextx, nexty))
5721 int opposite_direction = MV_DIR_OPPOSITE(direction);
5722 int hitting_side = direction;
5723 int touched_side = opposite_direction;
5724 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5725 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5726 MovDir[nextx][nexty] != direction ||
5727 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5733 CheckElementChangeSide(nextx, nexty, touched_element,
5734 CE_HIT_BY_SOMETHING, opposite_direction);
5736 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5737 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5739 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5741 struct ElementChangeInfo *change =
5742 &element_info[hitting_element].change_page[i];
5744 if (change->can_change &&
5745 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5746 change->trigger_side & touched_side &&
5747 change->trigger_element == touched_element)
5749 CheckElementChangePage(newx, newy, hitting_element,
5750 CE_OTHER_IS_HITTING, i);
5756 if (IS_CUSTOM_ELEMENT(touched_element) &&
5757 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5759 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5761 struct ElementChangeInfo *change =
5762 &element_info[touched_element].change_page[i];
5764 if (change->can_change &&
5765 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5766 change->trigger_side & hitting_side &&
5767 change->trigger_element == hitting_element)
5769 CheckElementChangePage(nextx, nexty, touched_element,
5770 CE_OTHER_GETS_HIT, i);
5781 TestIfPlayerTouchesCustomElement(newx, newy);
5782 TestIfElementTouchesCustomElement(newx, newy);
5785 int AmoebeNachbarNr(int ax, int ay)
5788 int element = Feld[ax][ay];
5790 static int xy[4][2] =
5798 for (i = 0; i < NUM_DIRECTIONS; i++)
5800 int x = ax + xy[i][0];
5801 int y = ay + xy[i][1];
5803 if (!IN_LEV_FIELD(x, y))
5806 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5807 group_nr = AmoebaNr[x][y];
5813 void AmoebenVereinigen(int ax, int ay)
5815 int i, x, y, xx, yy;
5816 int new_group_nr = AmoebaNr[ax][ay];
5817 static int xy[4][2] =
5825 if (new_group_nr == 0)
5828 for (i = 0; i < NUM_DIRECTIONS; i++)
5833 if (!IN_LEV_FIELD(x, y))
5836 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5837 Feld[x][y] == EL_BD_AMOEBA ||
5838 Feld[x][y] == EL_AMOEBA_DEAD) &&
5839 AmoebaNr[x][y] != new_group_nr)
5841 int old_group_nr = AmoebaNr[x][y];
5843 if (old_group_nr == 0)
5846 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5847 AmoebaCnt[old_group_nr] = 0;
5848 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5849 AmoebaCnt2[old_group_nr] = 0;
5851 for (yy = 0; yy < lev_fieldy; yy++)
5853 for (xx = 0; xx < lev_fieldx; xx++)
5855 if (AmoebaNr[xx][yy] == old_group_nr)
5856 AmoebaNr[xx][yy] = new_group_nr;
5863 void AmoebeUmwandeln(int ax, int ay)
5867 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5869 int group_nr = AmoebaNr[ax][ay];
5874 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5875 printf("AmoebeUmwandeln(): This should never happen!\n");
5880 for (y = 0; y < lev_fieldy; y++)
5882 for (x = 0; x < lev_fieldx; x++)
5884 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5887 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5891 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5892 SND_AMOEBA_TURNING_TO_GEM :
5893 SND_AMOEBA_TURNING_TO_ROCK));
5898 static int xy[4][2] =
5906 for (i = 0; i < NUM_DIRECTIONS; i++)
5911 if (!IN_LEV_FIELD(x, y))
5914 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5916 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5917 SND_AMOEBA_TURNING_TO_GEM :
5918 SND_AMOEBA_TURNING_TO_ROCK));
5925 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5928 int group_nr = AmoebaNr[ax][ay];
5929 boolean done = FALSE;
5934 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5935 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5940 for (y = 0; y < lev_fieldy; y++)
5942 for (x = 0; x < lev_fieldx; x++)
5944 if (AmoebaNr[x][y] == group_nr &&
5945 (Feld[x][y] == EL_AMOEBA_DEAD ||
5946 Feld[x][y] == EL_BD_AMOEBA ||
5947 Feld[x][y] == EL_AMOEBA_GROWING))
5950 Feld[x][y] = new_element;
5951 InitField(x, y, FALSE);
5952 DrawLevelField(x, y);
5959 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5960 SND_BD_AMOEBA_TURNING_TO_ROCK :
5961 SND_BD_AMOEBA_TURNING_TO_GEM));
5964 void AmoebeWaechst(int x, int y)
5966 static unsigned long sound_delay = 0;
5967 static unsigned long sound_delay_value = 0;
5969 if (!MovDelay[x][y]) /* start new growing cycle */
5973 if (DelayReached(&sound_delay, sound_delay_value))
5976 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5978 if (Store[x][y] == EL_BD_AMOEBA)
5979 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5981 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5983 sound_delay_value = 30;
5987 if (MovDelay[x][y]) /* wait some time before growing bigger */
5990 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5992 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5993 6 - MovDelay[x][y]);
5995 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5998 if (!MovDelay[x][y])
6000 Feld[x][y] = Store[x][y];
6002 DrawLevelField(x, y);
6007 void AmoebaDisappearing(int x, int y)
6009 static unsigned long sound_delay = 0;
6010 static unsigned long sound_delay_value = 0;
6012 if (!MovDelay[x][y]) /* start new shrinking cycle */
6016 if (DelayReached(&sound_delay, sound_delay_value))
6017 sound_delay_value = 30;
6020 if (MovDelay[x][y]) /* wait some time before shrinking */
6023 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6025 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6026 6 - MovDelay[x][y]);
6028 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6031 if (!MovDelay[x][y])
6033 Feld[x][y] = EL_EMPTY;
6034 DrawLevelField(x, y);
6036 /* don't let mole enter this field in this cycle;
6037 (give priority to objects falling to this field from above) */
6043 void AmoebeAbleger(int ax, int ay)
6046 int element = Feld[ax][ay];
6047 int graphic = el2img(element);
6048 int newax = ax, neway = ay;
6049 static int xy[4][2] =
6057 if (!level.amoeba_speed)
6059 Feld[ax][ay] = EL_AMOEBA_DEAD;
6060 DrawLevelField(ax, ay);
6064 if (IS_ANIMATED(graphic))
6065 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6067 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6068 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6070 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6073 if (MovDelay[ax][ay])
6077 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6080 int x = ax + xy[start][0];
6081 int y = ay + xy[start][1];
6083 if (!IN_LEV_FIELD(x, y))
6086 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6087 if (IS_FREE(x, y) ||
6088 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6094 if (newax == ax && neway == ay)
6097 else /* normal or "filled" (BD style) amoeba */
6100 boolean waiting_for_player = FALSE;
6102 for (i = 0; i < NUM_DIRECTIONS; i++)
6104 int j = (start + i) % 4;
6105 int x = ax + xy[j][0];
6106 int y = ay + xy[j][1];
6108 if (!IN_LEV_FIELD(x, y))
6111 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6112 if (IS_FREE(x, y) ||
6113 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6119 else if (IS_PLAYER(x, y))
6120 waiting_for_player = TRUE;
6123 if (newax == ax && neway == ay) /* amoeba cannot grow */
6125 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6127 Feld[ax][ay] = EL_AMOEBA_DEAD;
6128 DrawLevelField(ax, ay);
6129 AmoebaCnt[AmoebaNr[ax][ay]]--;
6131 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6133 if (element == EL_AMOEBA_FULL)
6134 AmoebeUmwandeln(ax, ay);
6135 else if (element == EL_BD_AMOEBA)
6136 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6141 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6143 /* amoeba gets larger by growing in some direction */
6145 int new_group_nr = AmoebaNr[ax][ay];
6148 if (new_group_nr == 0)
6150 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6151 printf("AmoebeAbleger(): This should never happen!\n");
6156 AmoebaNr[newax][neway] = new_group_nr;
6157 AmoebaCnt[new_group_nr]++;
6158 AmoebaCnt2[new_group_nr]++;
6160 /* if amoeba touches other amoeba(s) after growing, unify them */
6161 AmoebenVereinigen(newax, neway);
6163 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6165 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6171 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6172 (neway == lev_fieldy - 1 && newax != ax))
6174 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6175 Store[newax][neway] = element;
6177 else if (neway == ay)
6179 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6181 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6183 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6188 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6189 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6190 Store[ax][ay] = EL_AMOEBA_DROP;
6191 ContinueMoving(ax, ay);
6195 DrawLevelField(newax, neway);
6198 void Life(int ax, int ay)
6201 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6203 int element = Feld[ax][ay];
6204 int graphic = el2img(element);
6205 boolean changed = FALSE;
6207 if (IS_ANIMATED(graphic))
6208 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6213 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6214 MovDelay[ax][ay] = life_time;
6216 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6219 if (MovDelay[ax][ay])
6223 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6225 int xx = ax+x1, yy = ay+y1;
6228 if (!IN_LEV_FIELD(xx, yy))
6231 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6233 int x = xx+x2, y = yy+y2;
6235 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6238 if (((Feld[x][y] == element ||
6239 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6241 (IS_FREE(x, y) && Stop[x][y]))
6245 if (xx == ax && yy == ay) /* field in the middle */
6247 if (nachbarn < life[0] || nachbarn > life[1])
6249 Feld[xx][yy] = EL_EMPTY;
6251 DrawLevelField(xx, yy);
6252 Stop[xx][yy] = TRUE;
6256 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6257 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6258 { /* free border field */
6259 if (nachbarn >= life[2] && nachbarn <= life[3])
6261 Feld[xx][yy] = element;
6262 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6264 DrawLevelField(xx, yy);
6265 Stop[xx][yy] = TRUE;
6272 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6273 SND_GAME_OF_LIFE_GROWING);
6276 static void InitRobotWheel(int x, int y)
6278 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6281 static void RunRobotWheel(int x, int y)
6283 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6286 static void StopRobotWheel(int x, int y)
6288 if (ZX == x && ZY == y)
6292 static void InitTimegateWheel(int x, int y)
6294 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6297 static void RunTimegateWheel(int x, int y)
6299 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6302 void CheckExit(int x, int y)
6304 if (local_player->gems_still_needed > 0 ||
6305 local_player->sokobanfields_still_needed > 0 ||
6306 local_player->lights_still_needed > 0)
6308 int element = Feld[x][y];
6309 int graphic = el2img(element);
6311 if (IS_ANIMATED(graphic))
6312 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6317 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6320 Feld[x][y] = EL_EXIT_OPENING;
6322 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6325 void CheckExitSP(int x, int y)
6327 if (local_player->gems_still_needed > 0)
6329 int element = Feld[x][y];
6330 int graphic = el2img(element);
6332 if (IS_ANIMATED(graphic))
6333 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6338 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6341 Feld[x][y] = EL_SP_EXIT_OPENING;
6343 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6346 static void CloseAllOpenTimegates()
6350 for (y = 0; y < lev_fieldy; y++)
6352 for (x = 0; x < lev_fieldx; x++)
6354 int element = Feld[x][y];
6356 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6358 Feld[x][y] = EL_TIMEGATE_CLOSING;
6360 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6362 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6369 void EdelsteinFunkeln(int x, int y)
6371 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6374 if (Feld[x][y] == EL_BD_DIAMOND)
6377 if (MovDelay[x][y] == 0) /* next animation frame */
6378 MovDelay[x][y] = 11 * !SimpleRND(500);
6380 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6384 if (setup.direct_draw && MovDelay[x][y])
6385 SetDrawtoField(DRAW_BUFFERED);
6387 DrawLevelElementAnimation(x, y, Feld[x][y]);
6389 if (MovDelay[x][y] != 0)
6391 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6392 10 - MovDelay[x][y]);
6394 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6396 if (setup.direct_draw)
6400 dest_x = FX + SCREENX(x) * TILEX;
6401 dest_y = FY + SCREENY(y) * TILEY;
6403 BlitBitmap(drawto_field, window,
6404 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6405 SetDrawtoField(DRAW_DIRECT);
6411 void MauerWaechst(int x, int y)
6415 if (!MovDelay[x][y]) /* next animation frame */
6416 MovDelay[x][y] = 3 * delay;
6418 if (MovDelay[x][y]) /* wait some time before next frame */
6422 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6424 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6425 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6427 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6430 if (!MovDelay[x][y])
6432 if (MovDir[x][y] == MV_LEFT)
6434 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6435 DrawLevelField(x - 1, y);
6437 else if (MovDir[x][y] == MV_RIGHT)
6439 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6440 DrawLevelField(x + 1, y);
6442 else if (MovDir[x][y] == MV_UP)
6444 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6445 DrawLevelField(x, y - 1);
6449 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6450 DrawLevelField(x, y + 1);
6453 Feld[x][y] = Store[x][y];
6455 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6456 DrawLevelField(x, y);
6461 void MauerAbleger(int ax, int ay)
6463 int element = Feld[ax][ay];
6464 int graphic = el2img(element);
6465 boolean oben_frei = FALSE, unten_frei = FALSE;
6466 boolean links_frei = FALSE, rechts_frei = FALSE;
6467 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6468 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6469 boolean new_wall = FALSE;
6471 if (IS_ANIMATED(graphic))
6472 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6474 if (!MovDelay[ax][ay]) /* start building new wall */
6475 MovDelay[ax][ay] = 6;
6477 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6480 if (MovDelay[ax][ay])
6484 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6486 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6488 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6490 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6493 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6494 element == EL_EXPANDABLE_WALL_ANY)
6498 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6499 Store[ax][ay-1] = element;
6500 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6501 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6502 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6503 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6508 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6509 Store[ax][ay+1] = element;
6510 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6511 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6512 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6513 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6518 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6519 element == EL_EXPANDABLE_WALL_ANY ||
6520 element == EL_EXPANDABLE_WALL)
6524 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6525 Store[ax-1][ay] = element;
6526 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6527 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6528 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6529 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6535 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6536 Store[ax+1][ay] = element;
6537 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6538 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6539 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6540 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6545 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6546 DrawLevelField(ax, ay);
6548 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6550 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6551 unten_massiv = TRUE;
6552 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6553 links_massiv = TRUE;
6554 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6555 rechts_massiv = TRUE;
6557 if (((oben_massiv && unten_massiv) ||
6558 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6559 element == EL_EXPANDABLE_WALL) &&
6560 ((links_massiv && rechts_massiv) ||
6561 element == EL_EXPANDABLE_WALL_VERTICAL))
6562 Feld[ax][ay] = EL_WALL;
6566 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6568 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6572 void CheckForDragon(int x, int y)
6575 boolean dragon_found = FALSE;
6576 static int xy[4][2] =
6584 for (i = 0; i < NUM_DIRECTIONS; i++)
6586 for (j = 0; j < 4; j++)
6588 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6590 if (IN_LEV_FIELD(xx, yy) &&
6591 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6593 if (Feld[xx][yy] == EL_DRAGON)
6594 dragon_found = TRUE;
6603 for (i = 0; i < NUM_DIRECTIONS; i++)
6605 for (j = 0; j < 3; j++)
6607 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6609 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6611 Feld[xx][yy] = EL_EMPTY;
6612 DrawLevelField(xx, yy);
6621 static void InitBuggyBase(int x, int y)
6623 int element = Feld[x][y];
6624 int activating_delay = FRAMES_PER_SECOND / 4;
6627 (element == EL_SP_BUGGY_BASE ?
6628 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6629 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6631 element == EL_SP_BUGGY_BASE_ACTIVE ?
6632 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6635 static void WarnBuggyBase(int x, int y)
6638 static int xy[4][2] =
6646 for (i = 0; i < NUM_DIRECTIONS; i++)
6648 int xx = x + xy[i][0], yy = y + xy[i][1];
6650 if (IS_PLAYER(xx, yy))
6652 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6659 static void InitTrap(int x, int y)
6661 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6664 static void ActivateTrap(int x, int y)
6666 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6669 static void ChangeActiveTrap(int x, int y)
6671 int graphic = IMG_TRAP_ACTIVE;
6673 /* if new animation frame was drawn, correct crumbled sand border */
6674 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6675 DrawLevelFieldCrumbledSand(x, y);
6678 static void ChangeElementNowExt(int x, int y, int target_element)
6680 int previous_move_direction = MovDir[x][y];
6682 /* check if element under player changes from accessible to unaccessible
6683 (needed for special case of dropping element which then changes) */
6684 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6685 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6692 Feld[x][y] = target_element;
6694 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6696 ResetGfxAnimation(x, y);
6697 ResetRandomAnimationValue(x, y);
6699 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6700 MovDir[x][y] = previous_move_direction;
6703 InitField_WithBug1(x, y, FALSE);
6705 InitField(x, y, FALSE);
6706 if (CAN_MOVE(Feld[x][y]))
6710 DrawLevelField(x, y);
6712 if (GFX_CRUMBLED(Feld[x][y]))
6713 DrawLevelFieldCrumbledSandNeighbours(x, y);
6715 TestIfBadThingTouchesHero(x, y);
6716 TestIfPlayerTouchesCustomElement(x, y);
6717 TestIfElementTouchesCustomElement(x, y);
6719 if (ELEM_IS_PLAYER(target_element))
6720 RelocatePlayer(x, y, target_element);
6723 static boolean ChangeElementNow(int x, int y, int element, int page)
6725 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6727 /* always use default change event to prevent running into a loop */
6728 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6729 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6731 /* do not change already changed elements with same change event */
6733 if (Changed[x][y] & ChangeEvent[x][y])
6740 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6742 CheckTriggeredElementChangePage(x,y, Feld[x][y], CE_OTHER_IS_CHANGING, page);
6744 if (change->explode)
6751 if (change->use_content)
6753 boolean complete_change = TRUE;
6754 boolean can_change[3][3];
6757 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6759 boolean half_destructible;
6760 int ex = x + xx - 1;
6761 int ey = y + yy - 1;
6764 can_change[xx][yy] = TRUE;
6766 if (ex == x && ey == y) /* do not check changing element itself */
6769 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6771 can_change[xx][yy] = FALSE; /* do not change empty borders */
6776 if (!IN_LEV_FIELD(ex, ey))
6778 can_change[xx][yy] = FALSE;
6779 complete_change = FALSE;
6786 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6787 e = MovingOrBlocked2Element(ex, ey);
6789 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6791 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6792 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6793 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6795 can_change[xx][yy] = FALSE;
6796 complete_change = FALSE;
6800 if (!change->only_complete || complete_change)
6802 boolean something_has_changed = FALSE;
6804 if (change->only_complete && change->use_random_change &&
6805 RND(100) < change->random)
6808 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6810 int ex = x + xx - 1;
6811 int ey = y + yy - 1;
6813 if (can_change[xx][yy] && (!change->use_random_change ||
6814 RND(100) < change->random))
6816 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6817 RemoveMovingField(ex, ey);
6819 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6821 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6823 something_has_changed = TRUE;
6825 /* for symmetry reasons, freeze newly created border elements */
6826 if (ex != x || ey != y)
6827 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6831 if (something_has_changed)
6832 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6837 ChangeElementNowExt(x, y, change->target_element);
6839 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6845 static void ChangeElement(int x, int y, int page)
6847 int element = MovingOrBlocked2Element(x, y);
6848 struct ElementInfo *ei = &element_info[element];
6849 struct ElementChangeInfo *change = &ei->change_page[page];
6853 if (!CAN_CHANGE(element))
6856 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6857 x, y, element, element_info[element].token_name);
6858 printf("ChangeElement(): This should never happen!\n");
6864 if (ChangeDelay[x][y] == 0) /* initialize element change */
6866 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6867 RND(change->delay_random * change->delay_frames)) + 1;
6869 ResetGfxAnimation(x, y);
6870 ResetRandomAnimationValue(x, y);
6872 if (change->pre_change_function)
6873 change->pre_change_function(x, y);
6876 ChangeDelay[x][y]--;
6878 if (ChangeDelay[x][y] != 0) /* continue element change */
6880 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6882 if (IS_ANIMATED(graphic))
6883 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6885 if (change->change_function)
6886 change->change_function(x, y);
6888 else /* finish element change */
6890 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6892 page = ChangePage[x][y];
6893 ChangePage[x][y] = -1;
6897 if (IS_MOVING(x, y) && !change->explode)
6899 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6902 ChangeDelay[x][y] = 1; /* try change after next move step */
6903 ChangePage[x][y] = page; /* remember page to use for change */
6908 if (ChangeElementNow(x, y, element, page))
6910 if (change->post_change_function)
6911 change->post_change_function(x, y);
6916 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
6917 int trigger_element,
6925 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6928 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6930 int element = EL_CUSTOM_START + i;
6932 boolean change_element = FALSE;
6935 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6938 for (j = 0; j < element_info[element].num_change_pages; j++)
6940 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6942 if (change->can_change &&
6943 change->events & CH_EVENT_BIT(trigger_event) &&
6944 change->trigger_side & trigger_side &&
6945 change->trigger_player & trigger_player &&
6946 change->trigger_page & (1 << trigger_page) &&
6947 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
6950 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6951 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6952 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6955 change_element = TRUE;
6962 if (!change_element)
6965 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6968 if (x == lx && y == ly) /* do not change trigger element itself */
6972 if (Feld[x][y] == element)
6974 ChangeDelay[x][y] = 1;
6975 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6976 ChangeElement(x, y, page);
6984 static boolean CheckElementChangeExt(int x, int y,
6991 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6994 if (Feld[x][y] == EL_BLOCKED)
6996 Blocked2Moving(x, y, &x, &y);
6997 element = Feld[x][y];
7001 if (trigger_page < 0)
7003 boolean change_element = FALSE;
7006 for (i = 0; i < element_info[element].num_change_pages; i++)
7008 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7010 if (change->can_change &&
7011 change->events & CH_EVENT_BIT(trigger_event) &&
7012 change->trigger_side & trigger_side &&
7013 change->trigger_player & trigger_player)
7015 change_element = TRUE;
7022 if (!change_element)
7028 /* !!! this check misses pages with same event, but different side !!! */
7030 if (trigger_page < 0)
7031 trigger_page = element_info[element].event_page_nr[trigger_event];
7033 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7037 ChangeDelay[x][y] = 1;
7038 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7039 ChangeElement(x, y, trigger_page);
7044 static void PlayPlayerSound(struct PlayerInfo *player)
7046 int jx = player->jx, jy = player->jy;
7047 int element = player->element_nr;
7048 int last_action = player->last_action_waiting;
7049 int action = player->action_waiting;
7051 if (player->is_waiting)
7053 if (action != last_action)
7054 PlayLevelSoundElementAction(jx, jy, element, action);
7056 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7060 if (action != last_action)
7061 StopSound(element_info[element].sound[last_action]);
7063 if (last_action == ACTION_SLEEPING)
7064 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7068 static void PlayAllPlayersSound()
7072 for (i = 0; i < MAX_PLAYERS; i++)
7073 if (stored_player[i].active)
7074 PlayPlayerSound(&stored_player[i]);
7077 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7079 boolean last_waiting = player->is_waiting;
7080 int move_dir = player->MovDir;
7082 player->last_action_waiting = player->action_waiting;
7086 if (!last_waiting) /* not waiting -> waiting */
7088 player->is_waiting = TRUE;
7090 player->frame_counter_bored =
7092 game.player_boring_delay_fixed +
7093 SimpleRND(game.player_boring_delay_random);
7094 player->frame_counter_sleeping =
7096 game.player_sleeping_delay_fixed +
7097 SimpleRND(game.player_sleeping_delay_random);
7099 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7102 if (game.player_sleeping_delay_fixed +
7103 game.player_sleeping_delay_random > 0 &&
7104 player->anim_delay_counter == 0 &&
7105 player->post_delay_counter == 0 &&
7106 FrameCounter >= player->frame_counter_sleeping)
7107 player->is_sleeping = TRUE;
7108 else if (game.player_boring_delay_fixed +
7109 game.player_boring_delay_random > 0 &&
7110 FrameCounter >= player->frame_counter_bored)
7111 player->is_bored = TRUE;
7113 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7114 player->is_bored ? ACTION_BORING :
7117 if (player->is_sleeping)
7119 if (player->num_special_action_sleeping > 0)
7121 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7123 int last_special_action = player->special_action_sleeping;
7124 int num_special_action = player->num_special_action_sleeping;
7125 int special_action =
7126 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7127 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7128 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7129 last_special_action + 1 : ACTION_SLEEPING);
7130 int special_graphic =
7131 el_act_dir2img(player->element_nr, special_action, move_dir);
7133 player->anim_delay_counter =
7134 graphic_info[special_graphic].anim_delay_fixed +
7135 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7136 player->post_delay_counter =
7137 graphic_info[special_graphic].post_delay_fixed +
7138 SimpleRND(graphic_info[special_graphic].post_delay_random);
7140 player->special_action_sleeping = special_action;
7143 if (player->anim_delay_counter > 0)
7145 player->action_waiting = player->special_action_sleeping;
7146 player->anim_delay_counter--;
7148 else if (player->post_delay_counter > 0)
7150 player->post_delay_counter--;
7154 else if (player->is_bored)
7156 if (player->num_special_action_bored > 0)
7158 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7160 int special_action =
7161 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7162 int special_graphic =
7163 el_act_dir2img(player->element_nr, special_action, move_dir);
7165 player->anim_delay_counter =
7166 graphic_info[special_graphic].anim_delay_fixed +
7167 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7168 player->post_delay_counter =
7169 graphic_info[special_graphic].post_delay_fixed +
7170 SimpleRND(graphic_info[special_graphic].post_delay_random);
7172 player->special_action_bored = special_action;
7175 if (player->anim_delay_counter > 0)
7177 player->action_waiting = player->special_action_bored;
7178 player->anim_delay_counter--;
7180 else if (player->post_delay_counter > 0)
7182 player->post_delay_counter--;
7187 else if (last_waiting) /* waiting -> not waiting */
7189 player->is_waiting = FALSE;
7190 player->is_bored = FALSE;
7191 player->is_sleeping = FALSE;
7193 player->frame_counter_bored = -1;
7194 player->frame_counter_sleeping = -1;
7196 player->anim_delay_counter = 0;
7197 player->post_delay_counter = 0;
7199 player->action_waiting = ACTION_DEFAULT;
7201 player->special_action_bored = ACTION_DEFAULT;
7202 player->special_action_sleeping = ACTION_DEFAULT;
7207 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7210 static byte stored_player_action[MAX_PLAYERS];
7211 static int num_stored_actions = 0;
7213 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7214 int left = player_action & JOY_LEFT;
7215 int right = player_action & JOY_RIGHT;
7216 int up = player_action & JOY_UP;
7217 int down = player_action & JOY_DOWN;
7218 int button1 = player_action & JOY_BUTTON_1;
7219 int button2 = player_action & JOY_BUTTON_2;
7220 int dx = (left ? -1 : right ? 1 : 0);
7221 int dy = (up ? -1 : down ? 1 : 0);
7224 stored_player_action[player->index_nr] = 0;
7225 num_stored_actions++;
7229 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7232 if (!player->active || tape.pausing)
7236 printf("::: [%d %d %d %d] [%d %d]\n",
7237 left, right, up, down, button1, button2);
7243 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7248 if (player->MovPos == 0)
7249 CheckGravityMovement(player);
7252 snapped = SnapField(player, dx, dy);
7256 dropped = DropElement(player);
7258 moved = MovePlayer(player, dx, dy);
7261 if (tape.single_step && tape.recording && !tape.pausing)
7263 if (button1 || (dropped && !moved))
7265 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7266 SnapField(player, 0, 0); /* stop snapping */
7270 SetPlayerWaiting(player, FALSE);
7273 return player_action;
7275 stored_player_action[player->index_nr] = player_action;
7281 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7284 /* no actions for this player (no input at player's configured device) */
7286 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7287 SnapField(player, 0, 0);
7288 CheckGravityMovementWhenNotMoving(player);
7290 if (player->MovPos == 0)
7291 SetPlayerWaiting(player, TRUE);
7293 if (player->MovPos == 0) /* needed for tape.playing */
7294 player->is_moving = FALSE;
7296 player->is_dropping = FALSE;
7302 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7304 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7306 TapeRecordAction(stored_player_action);
7307 num_stored_actions = 0;
7314 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7316 static byte stored_player_action[MAX_PLAYERS];
7317 static int num_stored_actions = 0;
7318 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7319 int left = player_action & JOY_LEFT;
7320 int right = player_action & JOY_RIGHT;
7321 int up = player_action & JOY_UP;
7322 int down = player_action & JOY_DOWN;
7323 int button1 = player_action & JOY_BUTTON_1;
7324 int button2 = player_action & JOY_BUTTON_2;
7325 int dx = (left ? -1 : right ? 1 : 0);
7326 int dy = (up ? -1 : down ? 1 : 0);
7328 stored_player_action[player->index_nr] = 0;
7329 num_stored_actions++;
7331 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7333 if (!player->active || tape.pausing)
7338 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7341 snapped = SnapField(player, dx, dy);
7345 dropped = DropElement(player);
7347 moved = MovePlayer(player, dx, dy);
7350 if (tape.single_step && tape.recording && !tape.pausing)
7352 if (button1 || (dropped && !moved))
7354 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7355 SnapField(player, 0, 0); /* stop snapping */
7359 stored_player_action[player->index_nr] = player_action;
7363 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7365 /* no actions for this player (no input at player's configured device) */
7367 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7368 SnapField(player, 0, 0);
7369 CheckGravityMovementWhenNotMoving(player);
7371 if (player->MovPos == 0)
7372 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7374 if (player->MovPos == 0) /* needed for tape.playing */
7375 player->is_moving = FALSE;
7378 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7380 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7382 TapeRecordAction(stored_player_action);
7383 num_stored_actions = 0;
7390 static unsigned long action_delay = 0;
7391 unsigned long action_delay_value;
7392 int magic_wall_x = 0, magic_wall_y = 0;
7393 int i, x, y, element, graphic;
7394 byte *recorded_player_action;
7395 byte summarized_player_action = 0;
7397 byte tape_action[MAX_PLAYERS];
7400 if (game_status != GAME_MODE_PLAYING)
7403 action_delay_value =
7404 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7406 if (tape.playing && tape.index_search && !tape.pausing)
7407 action_delay_value = 0;
7409 /* ---------- main game synchronization point ---------- */
7411 WaitUntilDelayReached(&action_delay, action_delay_value);
7413 if (network_playing && !network_player_action_received)
7417 printf("DEBUG: try to get network player actions in time\n");
7421 #if defined(PLATFORM_UNIX)
7422 /* last chance to get network player actions without main loop delay */
7426 if (game_status != GAME_MODE_PLAYING)
7429 if (!network_player_action_received)
7433 printf("DEBUG: failed to get network player actions in time\n");
7444 printf("::: getting new tape action [%d]\n", FrameCounter);
7447 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7450 if (recorded_player_action != NULL)
7451 for (i = 0; i < MAX_PLAYERS; i++)
7452 stored_player[i].action = recorded_player_action[i];
7455 for (i = 0; i < MAX_PLAYERS; i++)
7457 summarized_player_action |= stored_player[i].action;
7459 if (!network_playing)
7460 stored_player[i].effective_action = stored_player[i].action;
7463 #if defined(PLATFORM_UNIX)
7464 if (network_playing)
7465 SendToServer_MovePlayer(summarized_player_action);
7468 if (!options.network && !setup.team_mode)
7469 local_player->effective_action = summarized_player_action;
7471 for (i = 0; i < MAX_PLAYERS; i++)
7473 int actual_player_action = stored_player[i].effective_action;
7477 if (stored_player[i].MovPos == 0)
7478 CheckGravityMovement(&stored_player[i]);
7482 /* overwrite programmed action with tape action */
7483 if (stored_player[i].programmed_action)
7484 actual_player_action = stored_player[i].programmed_action;
7487 if (recorded_player_action)
7490 if (stored_player[i].programmed_action &&
7491 stored_player[i].programmed_action != recorded_player_action[i])
7492 printf("::: %d: %d <-> %d\n", i,
7493 stored_player[i].programmed_action, recorded_player_action[i]);
7497 actual_player_action = recorded_player_action[i];
7502 /* overwrite tape action with programmed action */
7503 if (stored_player[i].programmed_action)
7504 actual_player_action = stored_player[i].programmed_action;
7509 printf("::: action: %d: %x [%d]\n",
7510 stored_player[i].MovPos, actual_player_action, FrameCounter);
7513 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7515 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7516 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7518 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7523 TapeRecordAction(tape_action);
7526 network_player_action_received = FALSE;
7528 ScrollScreen(NULL, SCROLL_GO_ON);
7534 for (i = 0; i < MAX_PLAYERS; i++)
7535 stored_player[i].Frame++;
7539 /* for downwards compatibility, the following code emulates a fixed bug that
7540 occured when pushing elements (causing elements that just made their last
7541 pushing step to already (if possible) make their first falling step in the
7542 same game frame, which is bad); this code is also needed to use the famous
7543 "spring push bug" which is used in older levels and might be wanted to be
7544 used also in newer levels, but in this case the buggy pushing code is only
7545 affecting the "spring" element and no other elements */
7548 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7550 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7553 for (i = 0; i < MAX_PLAYERS; i++)
7555 struct PlayerInfo *player = &stored_player[i];
7560 if (player->active && player->is_pushing && player->is_moving &&
7562 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7563 Feld[x][y] == EL_SPRING))
7565 if (player->active && player->is_pushing && player->is_moving &&
7569 ContinueMoving(x, y);
7571 /* continue moving after pushing (this is actually a bug) */
7572 if (!IS_MOVING(x, y))
7581 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7583 Changed[x][y] = CE_BITMASK_DEFAULT;
7584 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7587 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7589 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7590 printf("GameActions(): This should never happen!\n");
7592 ChangePage[x][y] = -1;
7597 if (WasJustMoving[x][y] > 0)
7598 WasJustMoving[x][y]--;
7599 if (WasJustFalling[x][y] > 0)
7600 WasJustFalling[x][y]--;
7605 /* reset finished pushing action (not done in ContinueMoving() to allow
7606 continous pushing animation for elements with zero push delay) */
7607 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7609 ResetGfxAnimation(x, y);
7610 DrawLevelField(x, y);
7615 if (IS_BLOCKED(x, y))
7619 Blocked2Moving(x, y, &oldx, &oldy);
7620 if (!IS_MOVING(oldx, oldy))
7622 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7623 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7624 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7625 printf("GameActions(): This should never happen!\n");
7631 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7633 element = Feld[x][y];
7635 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7637 graphic = el2img(element);
7643 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7645 element = graphic = 0;
7649 if (graphic_info[graphic].anim_global_sync)
7650 GfxFrame[x][y] = FrameCounter;
7652 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7653 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7654 ResetRandomAnimationValue(x, y);
7656 SetRandomAnimationValue(x, y);
7659 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7662 if (IS_INACTIVE(element))
7664 if (IS_ANIMATED(graphic))
7665 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7671 /* this may take place after moving, so 'element' may have changed */
7673 if (IS_CHANGING(x, y))
7675 if (IS_CHANGING(x, y) &&
7676 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7680 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7681 element_info[element].event_page_nr[CE_DELAY]);
7683 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7686 element = Feld[x][y];
7687 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7691 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7696 element = Feld[x][y];
7697 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7699 if (element == EL_MOLE)
7700 printf("::: %d, %d, %d [%d]\n",
7701 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7705 if (element == EL_YAMYAM)
7706 printf("::: %d, %d, %d\n",
7707 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7711 if (IS_ANIMATED(graphic) &&
7715 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7718 if (element == EL_BUG)
7719 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7723 if (element == EL_MOLE)
7724 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7728 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7729 EdelsteinFunkeln(x, y);
7731 else if ((element == EL_ACID ||
7732 element == EL_EXIT_OPEN ||
7733 element == EL_SP_EXIT_OPEN ||
7734 element == EL_SP_TERMINAL ||
7735 element == EL_SP_TERMINAL_ACTIVE ||
7736 element == EL_EXTRA_TIME ||
7737 element == EL_SHIELD_NORMAL ||
7738 element == EL_SHIELD_DEADLY) &&
7739 IS_ANIMATED(graphic))
7740 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7741 else if (IS_MOVING(x, y))
7742 ContinueMoving(x, y);
7743 else if (IS_ACTIVE_BOMB(element))
7744 CheckDynamite(x, y);
7746 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7747 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7749 else if (element == EL_AMOEBA_GROWING)
7750 AmoebeWaechst(x, y);
7751 else if (element == EL_AMOEBA_SHRINKING)
7752 AmoebaDisappearing(x, y);
7754 #if !USE_NEW_AMOEBA_CODE
7755 else if (IS_AMOEBALIVE(element))
7756 AmoebeAbleger(x, y);
7759 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7761 else if (element == EL_EXIT_CLOSED)
7763 else if (element == EL_SP_EXIT_CLOSED)
7765 else if (element == EL_EXPANDABLE_WALL_GROWING)
7767 else if (element == EL_EXPANDABLE_WALL ||
7768 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7769 element == EL_EXPANDABLE_WALL_VERTICAL ||
7770 element == EL_EXPANDABLE_WALL_ANY)
7772 else if (element == EL_FLAMES)
7773 CheckForDragon(x, y);
7775 else if (IS_AUTO_CHANGING(element))
7776 ChangeElement(x, y);
7778 else if (element == EL_EXPLOSION)
7779 ; /* drawing of correct explosion animation is handled separately */
7780 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7781 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7784 /* this may take place after moving, so 'element' may have changed */
7785 if (IS_AUTO_CHANGING(Feld[x][y]))
7786 ChangeElement(x, y);
7789 if (IS_BELT_ACTIVE(element))
7790 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7792 if (game.magic_wall_active)
7794 int jx = local_player->jx, jy = local_player->jy;
7796 /* play the element sound at the position nearest to the player */
7797 if ((element == EL_MAGIC_WALL_FULL ||
7798 element == EL_MAGIC_WALL_ACTIVE ||
7799 element == EL_MAGIC_WALL_EMPTYING ||
7800 element == EL_BD_MAGIC_WALL_FULL ||
7801 element == EL_BD_MAGIC_WALL_ACTIVE ||
7802 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7803 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7811 #if USE_NEW_AMOEBA_CODE
7812 /* new experimental amoeba growth stuff */
7814 if (!(FrameCounter % 8))
7817 static unsigned long random = 1684108901;
7819 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7822 x = (random >> 10) % lev_fieldx;
7823 y = (random >> 20) % lev_fieldy;
7825 x = RND(lev_fieldx);
7826 y = RND(lev_fieldy);
7828 element = Feld[x][y];
7830 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7831 if (!IS_PLAYER(x,y) &&
7832 (element == EL_EMPTY ||
7833 element == EL_SAND ||
7834 element == EL_QUICKSAND_EMPTY ||
7835 element == EL_ACID_SPLASH_LEFT ||
7836 element == EL_ACID_SPLASH_RIGHT))
7838 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7839 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7840 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7841 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7842 Feld[x][y] = EL_AMOEBA_DROP;
7845 random = random * 129 + 1;
7851 if (game.explosions_delayed)
7854 game.explosions_delayed = FALSE;
7856 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7858 element = Feld[x][y];
7860 if (ExplodeField[x][y])
7861 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7862 else if (element == EL_EXPLOSION)
7863 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7865 ExplodeField[x][y] = EX_NO_EXPLOSION;
7868 game.explosions_delayed = TRUE;
7871 if (game.magic_wall_active)
7873 if (!(game.magic_wall_time_left % 4))
7875 int element = Feld[magic_wall_x][magic_wall_y];
7877 if (element == EL_BD_MAGIC_WALL_FULL ||
7878 element == EL_BD_MAGIC_WALL_ACTIVE ||
7879 element == EL_BD_MAGIC_WALL_EMPTYING)
7880 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7882 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7885 if (game.magic_wall_time_left > 0)
7887 game.magic_wall_time_left--;
7888 if (!game.magic_wall_time_left)
7890 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7892 element = Feld[x][y];
7894 if (element == EL_MAGIC_WALL_ACTIVE ||
7895 element == EL_MAGIC_WALL_FULL)
7897 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7898 DrawLevelField(x, y);
7900 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7901 element == EL_BD_MAGIC_WALL_FULL)
7903 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7904 DrawLevelField(x, y);
7908 game.magic_wall_active = FALSE;
7913 if (game.light_time_left > 0)
7915 game.light_time_left--;
7917 if (game.light_time_left == 0)
7918 RedrawAllLightSwitchesAndInvisibleElements();
7921 if (game.timegate_time_left > 0)
7923 game.timegate_time_left--;
7925 if (game.timegate_time_left == 0)
7926 CloseAllOpenTimegates();
7929 for (i = 0; i < MAX_PLAYERS; i++)
7931 struct PlayerInfo *player = &stored_player[i];
7933 if (SHIELD_ON(player))
7935 if (player->shield_deadly_time_left)
7936 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7937 else if (player->shield_normal_time_left)
7938 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7942 if (TimeFrames >= FRAMES_PER_SECOND)
7947 if (!level.use_step_counter)
7951 for (i = 0; i < MAX_PLAYERS; i++)
7953 struct PlayerInfo *player = &stored_player[i];
7955 if (SHIELD_ON(player))
7957 player->shield_normal_time_left--;
7959 if (player->shield_deadly_time_left > 0)
7960 player->shield_deadly_time_left--;
7968 if (TimeLeft <= 10 && setup.time_limit)
7969 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7971 DrawGameValue_Time(TimeLeft);
7973 if (!TimeLeft && setup.time_limit)
7974 for (i = 0; i < MAX_PLAYERS; i++)
7975 KillHero(&stored_player[i]);
7977 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
7978 DrawGameValue_Time(TimePlayed);
7981 if (tape.recording || tape.playing)
7982 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
7986 PlayAllPlayersSound();
7988 if (options.debug) /* calculate frames per second */
7990 static unsigned long fps_counter = 0;
7991 static int fps_frames = 0;
7992 unsigned long fps_delay_ms = Counter() - fps_counter;
7996 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7998 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8001 fps_counter = Counter();
8004 redraw_mask |= REDRAW_FPS;
8008 if (stored_player[0].jx != stored_player[0].last_jx ||
8009 stored_player[0].jy != stored_player[0].last_jy)
8010 printf("::: %d, %d, %d, %d, %d\n",
8011 stored_player[0].MovDir,
8012 stored_player[0].MovPos,
8013 stored_player[0].GfxPos,
8014 stored_player[0].Frame,
8015 stored_player[0].StepFrame);
8022 for (i = 0; i < MAX_PLAYERS; i++)
8025 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8027 stored_player[i].Frame += move_frames;
8029 if (stored_player[i].MovPos != 0)
8030 stored_player[i].StepFrame += move_frames;
8032 if (stored_player[i].drop_delay > 0)
8033 stored_player[i].drop_delay--;
8038 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8040 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8042 local_player->show_envelope = 0;
8047 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8049 int min_x = x, min_y = y, max_x = x, max_y = y;
8052 for (i = 0; i < MAX_PLAYERS; i++)
8054 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8056 if (!stored_player[i].active || &stored_player[i] == player)
8059 min_x = MIN(min_x, jx);
8060 min_y = MIN(min_y, jy);
8061 max_x = MAX(max_x, jx);
8062 max_y = MAX(max_y, jy);
8065 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8068 static boolean AllPlayersInVisibleScreen()
8072 for (i = 0; i < MAX_PLAYERS; i++)
8074 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8076 if (!stored_player[i].active)
8079 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8086 void ScrollLevel(int dx, int dy)
8088 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8091 BlitBitmap(drawto_field, drawto_field,
8092 FX + TILEX * (dx == -1) - softscroll_offset,
8093 FY + TILEY * (dy == -1) - softscroll_offset,
8094 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8095 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8096 FX + TILEX * (dx == 1) - softscroll_offset,
8097 FY + TILEY * (dy == 1) - softscroll_offset);
8101 x = (dx == 1 ? BX1 : BX2);
8102 for (y = BY1; y <= BY2; y++)
8103 DrawScreenField(x, y);
8108 y = (dy == 1 ? BY1 : BY2);
8109 for (x = BX1; x <= BX2; x++)
8110 DrawScreenField(x, y);
8113 redraw_mask |= REDRAW_FIELD;
8116 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8118 int nextx = x + dx, nexty = y + dy;
8119 int element = Feld[x][y];
8122 element != EL_SP_PORT_LEFT &&
8123 element != EL_SP_GRAVITY_PORT_LEFT &&
8124 element != EL_SP_PORT_HORIZONTAL &&
8125 element != EL_SP_PORT_ANY) ||
8127 element != EL_SP_PORT_RIGHT &&
8128 element != EL_SP_GRAVITY_PORT_RIGHT &&
8129 element != EL_SP_PORT_HORIZONTAL &&
8130 element != EL_SP_PORT_ANY) ||
8132 element != EL_SP_PORT_UP &&
8133 element != EL_SP_GRAVITY_PORT_UP &&
8134 element != EL_SP_PORT_VERTICAL &&
8135 element != EL_SP_PORT_ANY) ||
8137 element != EL_SP_PORT_DOWN &&
8138 element != EL_SP_GRAVITY_PORT_DOWN &&
8139 element != EL_SP_PORT_VERTICAL &&
8140 element != EL_SP_PORT_ANY) ||
8141 !IN_LEV_FIELD(nextx, nexty) ||
8142 !IS_FREE(nextx, nexty))
8148 static void CheckGravityMovement(struct PlayerInfo *player)
8150 if (game.gravity && !player->programmed_action)
8152 int move_dir_horizontal = player->action & MV_HORIZONTAL;
8153 int move_dir_vertical = player->action & MV_VERTICAL;
8155 (player->last_move_dir & MV_HORIZONTAL ?
8156 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8157 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8158 int jx = player->jx, jy = player->jy;
8159 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8160 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8161 int new_jx = jx + dx, new_jy = jy + dy;
8162 boolean player_is_snapping = player->action & JOY_BUTTON_1;
8164 boolean player_can_fall_down =
8165 (IN_LEV_FIELD(jx, jy + 1) &&
8166 (IS_FREE(jx, jy + 1) ||
8167 (Feld[jx][jy + 1] == EL_ACID && level.player_can_fall_into_acid)));
8169 boolean player_can_fall_down =
8170 (IN_LEV_FIELD(jx, jy + 1) &&
8171 (IS_FREE(jx, jy + 1)));
8173 boolean player_is_moving_to_valid_field =
8176 !player_is_snapping &&
8178 IN_LEV_FIELD(new_jx, new_jy) &&
8179 (Feld[new_jx][new_jy] == EL_SP_BASE ||
8180 Feld[new_jx][new_jy] == EL_SAND ||
8181 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8182 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
8183 /* !!! extend EL_SAND to anything diggable !!! */
8185 boolean player_is_standing_on_valid_field =
8186 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8187 (IS_WALKABLE(Feld[jx][jy]) &&
8188 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8191 printf("::: checking gravity NOW [%d, %d, %d] [%d] ...\n",
8192 player_can_fall_down,
8193 player_is_standing_on_valid_field,
8194 player_is_moving_to_valid_field,
8195 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1));
8198 if (player_can_fall_down &&
8199 !player_is_standing_on_valid_field &&
8200 !player_is_moving_to_valid_field)
8203 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
8204 jx, jy, FrameCounter);
8207 player->programmed_action = MV_DOWN;
8212 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8215 return CheckGravityMovement(player);
8218 if (game.gravity && !player->programmed_action)
8220 int jx = player->jx, jy = player->jy;
8221 boolean field_under_player_is_free =
8222 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8223 boolean player_is_standing_on_valid_field =
8224 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8225 (IS_WALKABLE(Feld[jx][jy]) &&
8226 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8228 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8229 player->programmed_action = MV_DOWN;
8235 -----------------------------------------------------------------------------
8236 dx, dy: direction (non-diagonal) to try to move the player to
8237 real_dx, real_dy: direction as read from input device (can be diagonal)
8240 boolean MovePlayerOneStep(struct PlayerInfo *player,
8241 int dx, int dy, int real_dx, int real_dy)
8244 static int trigger_sides[4][2] =
8246 /* enter side leave side */
8247 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8248 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8249 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8250 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8252 int move_direction = (dx == -1 ? MV_LEFT :
8253 dx == +1 ? MV_RIGHT :
8255 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8256 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8257 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8259 int jx = player->jx, jy = player->jy;
8260 int new_jx = jx + dx, new_jy = jy + dy;
8264 if (!player->active || (!dx && !dy))
8265 return MF_NO_ACTION;
8267 player->MovDir = (dx < 0 ? MV_LEFT :
8270 dy > 0 ? MV_DOWN : MV_NO_MOVING);
8272 if (!IN_LEV_FIELD(new_jx, new_jy))
8273 return MF_NO_ACTION;
8275 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8276 return MF_NO_ACTION;
8279 element = MovingOrBlocked2Element(new_jx, new_jy);
8281 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8284 if (DONT_RUN_INTO(element))
8286 if (element == EL_ACID && dx == 0 && dy == 1)
8288 SplashAcid(new_jx, new_jy);
8289 Feld[jx][jy] = EL_PLAYER_1;
8290 InitMovingField(jx, jy, MV_DOWN);
8291 Store[jx][jy] = EL_ACID;
8292 ContinueMoving(jx, jy);
8296 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8301 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8302 if (can_move != MF_MOVING)
8305 /* check if DigField() has caused relocation of the player */
8306 if (player->jx != jx || player->jy != jy)
8307 return MF_NO_ACTION;
8309 StorePlayer[jx][jy] = 0;
8310 player->last_jx = jx;
8311 player->last_jy = jy;
8312 player->jx = new_jx;
8313 player->jy = new_jy;
8314 StorePlayer[new_jx][new_jy] = player->element_nr;
8317 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8319 player->step_counter++;
8321 player->drop_delay = 0;
8323 PlayerVisit[jx][jy] = FrameCounter;
8325 ScrollPlayer(player, SCROLL_INIT);
8328 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8330 CheckTriggeredElementChangeSide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
8332 CheckElementChangeSide(jx, jy, Feld[jx][jy], CE_LEFT_BY_PLAYER,leave_side);
8335 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
8337 CheckTriggeredElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8338 CE_OTHER_GETS_ENTERED, enter_side);
8339 CheckElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8340 CE_ENTERED_BY_PLAYER, enter_side);
8347 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8349 int jx = player->jx, jy = player->jy;
8350 int old_jx = jx, old_jy = jy;
8351 int moved = MF_NO_ACTION;
8354 if (!player->active)
8359 if (player->MovPos == 0)
8361 player->is_moving = FALSE;
8362 player->is_digging = FALSE;
8363 player->is_collecting = FALSE;
8364 player->is_snapping = FALSE;
8365 player->is_pushing = FALSE;
8371 if (!player->active || (!dx && !dy))
8376 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8382 if (!FrameReached(&player->move_delay, player->move_delay_value))
8385 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8386 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
8392 /* remove the last programmed player action */
8393 player->programmed_action = 0;
8397 /* should only happen if pre-1.2 tape recordings are played */
8398 /* this is only for backward compatibility */
8400 int original_move_delay_value = player->move_delay_value;
8403 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8407 /* scroll remaining steps with finest movement resolution */
8408 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8410 while (player->MovPos)
8412 ScrollPlayer(player, SCROLL_GO_ON);
8413 ScrollScreen(NULL, SCROLL_GO_ON);
8419 player->move_delay_value = original_move_delay_value;
8422 if (player->last_move_dir & MV_HORIZONTAL)
8424 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8425 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8429 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8430 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8436 if (moved & MF_MOVING && !ScreenMovPos &&
8437 (player == local_player || !options.network))
8439 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8440 int offset = (setup.scroll_delay ? 3 : 0);
8442 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8444 /* actual player has left the screen -- scroll in that direction */
8445 if (jx != old_jx) /* player has moved horizontally */
8446 scroll_x += (jx - old_jx);
8447 else /* player has moved vertically */
8448 scroll_y += (jy - old_jy);
8452 if (jx != old_jx) /* player has moved horizontally */
8454 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8455 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8456 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8458 /* don't scroll over playfield boundaries */
8459 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8460 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8462 /* don't scroll more than one field at a time */
8463 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8465 /* don't scroll against the player's moving direction */
8466 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8467 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8468 scroll_x = old_scroll_x;
8470 else /* player has moved vertically */
8472 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8473 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8474 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8476 /* don't scroll over playfield boundaries */
8477 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8478 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8480 /* don't scroll more than one field at a time */
8481 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8483 /* don't scroll against the player's moving direction */
8484 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8485 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8486 scroll_y = old_scroll_y;
8490 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8492 if (!options.network && !AllPlayersInVisibleScreen())
8494 scroll_x = old_scroll_x;
8495 scroll_y = old_scroll_y;
8499 ScrollScreen(player, SCROLL_INIT);
8500 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8507 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8509 if (!(moved & MF_MOVING) && !player->is_pushing)
8514 player->StepFrame = 0;
8516 if (moved & MF_MOVING)
8518 if (old_jx != jx && old_jy == jy)
8519 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8520 else if (old_jx == jx && old_jy != jy)
8521 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8523 DrawLevelField(jx, jy); /* for "crumbled sand" */
8525 player->last_move_dir = player->MovDir;
8526 player->is_moving = TRUE;
8528 player->is_snapping = FALSE;
8532 player->is_switching = FALSE;
8535 player->is_dropping = FALSE;
8540 static int trigger_sides[4][2] =
8542 /* enter side leave side */
8543 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8544 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8545 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8546 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8548 int move_direction = player->MovDir;
8549 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8550 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8553 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8555 CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8557 player->index_bit, leave_side);
8558 CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8560 player->index_bit, leave_side);
8563 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8565 CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
8566 CE_OTHER_GETS_ENTERED,
8567 player->index_bit, enter_side);
8568 CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
8569 player->index_bit, enter_side);
8580 CheckGravityMovementWhenNotMoving(player);
8583 player->last_move_dir = MV_NO_MOVING;
8585 player->is_moving = FALSE;
8588 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8590 TestIfHeroTouchesBadThing(jx, jy);
8591 TestIfPlayerTouchesCustomElement(jx, jy);
8594 if (!player->active)
8600 void ScrollPlayer(struct PlayerInfo *player, int mode)
8602 int jx = player->jx, jy = player->jy;
8603 int last_jx = player->last_jx, last_jy = player->last_jy;
8604 int move_stepsize = TILEX / player->move_delay_value;
8606 if (!player->active || !player->MovPos)
8609 if (mode == SCROLL_INIT)
8611 player->actual_frame_counter = FrameCounter;
8612 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8614 if (Feld[last_jx][last_jy] == EL_EMPTY)
8615 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8623 else if (!FrameReached(&player->actual_frame_counter, 1))
8626 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8627 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8629 if (!player->block_last_field &&
8630 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8631 Feld[last_jx][last_jy] = EL_EMPTY;
8633 /* before DrawPlayer() to draw correct player graphic for this case */
8634 if (player->MovPos == 0)
8635 CheckGravityMovement(player);
8638 DrawPlayer(player); /* needed here only to cleanup last field */
8641 if (player->MovPos == 0) /* player reached destination field */
8644 if (player->move_delay_reset_counter > 0)
8646 player->move_delay_reset_counter--;
8648 if (player->move_delay_reset_counter == 0)
8650 /* continue with normal speed after quickly moving through gate */
8651 HALVE_PLAYER_SPEED(player);
8653 /* be able to make the next move without delay */
8654 player->move_delay = 0;
8658 if (IS_PASSABLE(Feld[last_jx][last_jy]))
8660 /* continue with normal speed after quickly moving through gate */
8661 HALVE_PLAYER_SPEED(player);
8663 /* be able to make the next move without delay */
8664 player->move_delay = 0;
8668 if (player->block_last_field &&
8669 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8670 Feld[last_jx][last_jy] = EL_EMPTY;
8672 player->last_jx = jx;
8673 player->last_jy = jy;
8675 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8676 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8677 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8679 DrawPlayer(player); /* needed here only to cleanup last field */
8682 if (local_player->friends_still_needed == 0 ||
8683 IS_SP_ELEMENT(Feld[jx][jy]))
8684 player->LevelSolved = player->GameOver = TRUE;
8688 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
8690 static int trigger_sides[4][2] =
8692 /* enter side leave side */
8693 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8694 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8695 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8696 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8698 int move_direction = player->MovDir;
8699 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8700 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8701 int old_jx = last_jx;
8702 int old_jy = last_jy;
8705 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8707 CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8709 player->index_bit, leave_side);
8710 CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8712 player->index_bit, leave_side);
8715 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8717 CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
8718 CE_OTHER_GETS_ENTERED,
8719 player->index_bit, enter_side);
8720 CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
8721 player->index_bit, enter_side);
8728 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8730 TestIfHeroTouchesBadThing(jx, jy);
8731 TestIfPlayerTouchesCustomElement(jx, jy);
8733 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8736 if (!player->active)
8740 if (level.use_step_counter)
8746 for (i = 0; i < MAX_PLAYERS; i++)
8748 struct PlayerInfo *player = &stored_player[i];
8750 if (SHIELD_ON(player))
8752 player->shield_normal_time_left--;
8754 if (player->shield_deadly_time_left > 0)
8755 player->shield_deadly_time_left--;
8763 if (TimeLeft <= 10 && setup.time_limit)
8764 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8766 DrawGameValue_Time(TimeLeft);
8768 if (!TimeLeft && setup.time_limit)
8769 for (i = 0; i < MAX_PLAYERS; i++)
8770 KillHero(&stored_player[i]);
8772 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8773 DrawGameValue_Time(TimePlayed);
8776 if (tape.single_step && tape.recording && !tape.pausing &&
8777 !player->programmed_action)
8778 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8782 void ScrollScreen(struct PlayerInfo *player, int mode)
8784 static unsigned long screen_frame_counter = 0;
8786 if (mode == SCROLL_INIT)
8788 /* set scrolling step size according to actual player's moving speed */
8789 ScrollStepSize = TILEX / player->move_delay_value;
8791 screen_frame_counter = FrameCounter;
8792 ScreenMovDir = player->MovDir;
8793 ScreenMovPos = player->MovPos;
8794 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8797 else if (!FrameReached(&screen_frame_counter, 1))
8802 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8803 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8804 redraw_mask |= REDRAW_FIELD;
8807 ScreenMovDir = MV_NO_MOVING;
8810 void TestIfPlayerTouchesCustomElement(int x, int y)
8812 static int xy[4][2] =
8819 static int trigger_sides[4][2] =
8821 /* center side border side */
8822 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8823 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8824 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8825 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8827 static int touch_dir[4] =
8834 int center_element = Feld[x][y]; /* should always be non-moving! */
8837 for (i = 0; i < NUM_DIRECTIONS; i++)
8839 int xx = x + xy[i][0];
8840 int yy = y + xy[i][1];
8841 int center_side = trigger_sides[i][0];
8842 int border_side = trigger_sides[i][1];
8845 if (!IN_LEV_FIELD(xx, yy))
8848 if (IS_PLAYER(x, y))
8850 struct PlayerInfo *player = PLAYERINFO(x, y);
8852 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8853 border_element = Feld[xx][yy]; /* may be moving! */
8854 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8855 border_element = Feld[xx][yy];
8856 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8857 border_element = MovingOrBlocked2Element(xx, yy);
8859 continue; /* center and border element do not touch */
8861 CheckTriggeredElementChangePlayer(xx, yy, border_element,
8862 CE_OTHER_GETS_TOUCHED,
8863 player->index_bit, border_side);
8864 CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
8865 player->index_bit, border_side);
8867 else if (IS_PLAYER(xx, yy))
8869 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8871 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8873 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8874 continue; /* center and border element do not touch */
8877 CheckTriggeredElementChangePlayer(x, y, center_element,
8878 CE_OTHER_GETS_TOUCHED,
8879 player->index_bit, center_side);
8880 CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
8881 player->index_bit, center_side);
8888 void TestIfElementTouchesCustomElement(int x, int y)
8890 static int xy[4][2] =
8897 static int trigger_sides[4][2] =
8899 /* center side border side */
8900 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8901 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8902 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8903 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8905 static int touch_dir[4] =
8912 boolean change_center_element = FALSE;
8913 int center_element_change_page = 0;
8914 int center_element = Feld[x][y]; /* should always be non-moving! */
8917 for (i = 0; i < NUM_DIRECTIONS; i++)
8919 int xx = x + xy[i][0];
8920 int yy = y + xy[i][1];
8921 int center_side = trigger_sides[i][0];
8922 int border_side = trigger_sides[i][1];
8925 if (!IN_LEV_FIELD(xx, yy))
8928 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8929 border_element = Feld[xx][yy]; /* may be moving! */
8930 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8931 border_element = Feld[xx][yy];
8932 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8933 border_element = MovingOrBlocked2Element(xx, yy);
8935 continue; /* center and border element do not touch */
8937 /* check for change of center element (but change it only once) */
8938 if (IS_CUSTOM_ELEMENT(center_element) &&
8939 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8940 !change_center_element)
8942 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8944 struct ElementChangeInfo *change =
8945 &element_info[center_element].change_page[j];
8947 if (change->can_change &&
8948 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8949 change->trigger_side & border_side &&
8951 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8953 change->trigger_element == border_element
8957 change_center_element = TRUE;
8958 center_element_change_page = j;
8965 /* check for change of border element */
8966 if (IS_CUSTOM_ELEMENT(border_element) &&
8967 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8969 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8971 struct ElementChangeInfo *change =
8972 &element_info[border_element].change_page[j];
8974 if (change->can_change &&
8975 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8976 change->trigger_side & center_side &&
8978 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8980 change->trigger_element == center_element
8984 CheckElementChangePage(xx, yy, border_element, CE_OTHER_IS_TOUCHING,
8992 if (change_center_element)
8993 CheckElementChangePage(x, y, center_element, CE_OTHER_IS_TOUCHING,
8994 center_element_change_page);
8997 void TestIfElementHitsCustomElement(int x, int y, int direction)
8999 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9000 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9001 int hitx = x + dx, hity = y + dy;
9002 int hitting_element = Feld[x][y];
9004 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9005 !IS_FREE(hitx, hity) &&
9006 (!IS_MOVING(hitx, hity) ||
9007 MovDir[hitx][hity] != direction ||
9008 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9011 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9015 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9019 CheckElementChangeSide(x, y, hitting_element, CE_HITTING_SOMETHING,
9022 if (IN_LEV_FIELD(hitx, hity))
9024 int opposite_direction = MV_DIR_OPPOSITE(direction);
9025 int hitting_side = direction;
9026 int touched_side = opposite_direction;
9027 int touched_element = MovingOrBlocked2Element(hitx, hity);
9029 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9030 MovDir[hitx][hity] != direction ||
9031 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9040 CheckElementChangeSide(hitx, hity, touched_element, CE_HIT_BY_SOMETHING,
9041 opposite_direction);
9043 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9044 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
9046 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9048 struct ElementChangeInfo *change =
9049 &element_info[hitting_element].change_page[i];
9051 if (change->can_change &&
9052 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
9053 change->trigger_side & touched_side &&
9056 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9058 change->trigger_element == touched_element
9062 CheckElementChangePage(x, y, hitting_element, CE_OTHER_IS_HITTING,
9069 if (IS_CUSTOM_ELEMENT(touched_element) &&
9070 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
9072 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9074 struct ElementChangeInfo *change =
9075 &element_info[touched_element].change_page[i];
9077 if (change->can_change &&
9078 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
9079 change->trigger_side & hitting_side &&
9081 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9083 change->trigger_element == hitting_element
9087 CheckElementChangePage(hitx, hity, touched_element,
9088 CE_OTHER_GETS_HIT, i);
9098 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9100 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9101 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9102 int hitx = x + dx, hity = y + dy;
9103 int hitting_element = Feld[x][y];
9105 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9106 !IS_FREE(hitx, hity) &&
9107 (!IS_MOVING(hitx, hity) ||
9108 MovDir[hitx][hity] != direction ||
9109 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9112 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9116 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9120 CheckElementChangeSide(x, y, hitting_element, EP_CAN_SMASH_EVERYTHING,
9123 if (IN_LEV_FIELD(hitx, hity))
9125 int opposite_direction = MV_DIR_OPPOSITE(direction);
9126 int hitting_side = direction;
9127 int touched_side = opposite_direction;
9128 int touched_element = MovingOrBlocked2Element(hitx, hity);
9130 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9131 MovDir[hitx][hity] != direction ||
9132 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9141 CheckElementChangeSide(hitx, hity, touched_element,
9142 CE_SMASHED_BY_SOMETHING, opposite_direction);
9144 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9145 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
9147 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9149 struct ElementChangeInfo *change =
9150 &element_info[hitting_element].change_page[i];
9152 if (change->can_change &&
9153 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
9154 change->trigger_side & touched_side &&
9157 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9159 change->trigger_element == touched_element
9163 CheckElementChangePage(x, y, hitting_element, CE_OTHER_IS_SMASHING,
9170 if (IS_CUSTOM_ELEMENT(touched_element) &&
9171 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
9173 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9175 struct ElementChangeInfo *change =
9176 &element_info[touched_element].change_page[i];
9178 if (change->can_change &&
9179 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
9180 change->trigger_side & hitting_side &&
9182 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9184 change->trigger_element == hitting_element
9188 CheckElementChangePage(hitx, hity, touched_element,
9189 CE_OTHER_GETS_SMASHED, i);
9199 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9201 int i, kill_x = -1, kill_y = -1;
9202 static int test_xy[4][2] =
9209 static int test_dir[4] =
9217 for (i = 0; i < NUM_DIRECTIONS; i++)
9219 int test_x, test_y, test_move_dir, test_element;
9221 test_x = good_x + test_xy[i][0];
9222 test_y = good_y + test_xy[i][1];
9223 if (!IN_LEV_FIELD(test_x, test_y))
9227 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9230 test_element = Feld[test_x][test_y];
9232 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9235 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9236 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9238 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9239 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9247 if (kill_x != -1 || kill_y != -1)
9249 if (IS_PLAYER(good_x, good_y))
9251 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9253 if (player->shield_deadly_time_left > 0)
9254 Bang(kill_x, kill_y);
9255 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9259 Bang(good_x, good_y);
9263 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9265 int i, kill_x = -1, kill_y = -1;
9266 int bad_element = Feld[bad_x][bad_y];
9267 static int test_xy[4][2] =
9274 static int touch_dir[4] =
9281 static int test_dir[4] =
9289 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9292 for (i = 0; i < NUM_DIRECTIONS; i++)
9294 int test_x, test_y, test_move_dir, test_element;
9296 test_x = bad_x + test_xy[i][0];
9297 test_y = bad_y + test_xy[i][1];
9298 if (!IN_LEV_FIELD(test_x, test_y))
9302 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9304 test_element = Feld[test_x][test_y];
9306 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9307 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9309 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9310 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9312 /* good thing is player or penguin that does not move away */
9313 if (IS_PLAYER(test_x, test_y))
9315 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9317 if (bad_element == EL_ROBOT && player->is_moving)
9318 continue; /* robot does not kill player if he is moving */
9320 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9322 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9323 continue; /* center and border element do not touch */
9330 else if (test_element == EL_PENGUIN)
9339 if (kill_x != -1 || kill_y != -1)
9341 if (IS_PLAYER(kill_x, kill_y))
9343 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9345 if (player->shield_deadly_time_left > 0)
9347 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9351 Bang(kill_x, kill_y);
9355 void TestIfHeroTouchesBadThing(int x, int y)
9357 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9360 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
9362 TestIfGoodThingHitsBadThing(x, y, move_dir);
9365 void TestIfBadThingTouchesHero(int x, int y)
9367 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9370 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
9372 TestIfBadThingHitsGoodThing(x, y, move_dir);
9375 void TestIfFriendTouchesBadThing(int x, int y)
9377 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9380 void TestIfBadThingTouchesFriend(int x, int y)
9382 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9385 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9387 int i, kill_x = bad_x, kill_y = bad_y;
9388 static int xy[4][2] =
9396 for (i = 0; i < NUM_DIRECTIONS; i++)
9400 x = bad_x + xy[i][0];
9401 y = bad_y + xy[i][1];
9402 if (!IN_LEV_FIELD(x, y))
9405 element = Feld[x][y];
9406 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9407 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9415 if (kill_x != bad_x || kill_y != bad_y)
9419 void KillHero(struct PlayerInfo *player)
9421 int jx = player->jx, jy = player->jy;
9423 if (!player->active)
9426 /* remove accessible field at the player's position */
9427 Feld[jx][jy] = EL_EMPTY;
9429 /* deactivate shield (else Bang()/Explode() would not work right) */
9430 player->shield_normal_time_left = 0;
9431 player->shield_deadly_time_left = 0;
9437 static void KillHeroUnlessEnemyProtected(int x, int y)
9439 if (!PLAYER_ENEMY_PROTECTED(x, y))
9440 KillHero(PLAYERINFO(x, y));
9443 static void KillHeroUnlessExplosionProtected(int x, int y)
9445 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9446 KillHero(PLAYERINFO(x, y));
9449 void BuryHero(struct PlayerInfo *player)
9451 int jx = player->jx, jy = player->jy;
9453 if (!player->active)
9457 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9459 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
9461 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9463 player->GameOver = TRUE;
9467 void RemoveHero(struct PlayerInfo *player)
9469 int jx = player->jx, jy = player->jy;
9470 int i, found = FALSE;
9472 player->present = FALSE;
9473 player->active = FALSE;
9475 if (!ExplodeField[jx][jy])
9476 StorePlayer[jx][jy] = 0;
9478 for (i = 0; i < MAX_PLAYERS; i++)
9479 if (stored_player[i].active)
9483 AllPlayersGone = TRUE;
9490 =============================================================================
9491 checkDiagonalPushing()
9492 -----------------------------------------------------------------------------
9493 check if diagonal input device direction results in pushing of object
9494 (by checking if the alternative direction is walkable, diggable, ...)
9495 =============================================================================
9498 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9499 int x, int y, int real_dx, int real_dy)
9501 int jx, jy, dx, dy, xx, yy;
9503 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9506 /* diagonal direction: check alternative direction */
9511 xx = jx + (dx == 0 ? real_dx : 0);
9512 yy = jy + (dy == 0 ? real_dy : 0);
9514 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9518 =============================================================================
9520 -----------------------------------------------------------------------------
9521 x, y: field next to player (non-diagonal) to try to dig to
9522 real_dx, real_dy: direction as read from input device (can be diagonal)
9523 =============================================================================
9526 int DigField(struct PlayerInfo *player,
9527 int oldx, int oldy, int x, int y,
9528 int real_dx, int real_dy, int mode)
9530 static int trigger_sides[4] =
9532 CH_SIDE_RIGHT, /* moving left */
9533 CH_SIDE_LEFT, /* moving right */
9534 CH_SIDE_BOTTOM, /* moving up */
9535 CH_SIDE_TOP, /* moving down */
9538 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
9540 int jx = oldx, jy = oldy;
9541 int dx = x - jx, dy = y - jy;
9542 int nextx = x + dx, nexty = y + dy;
9543 int move_direction = (dx == -1 ? MV_LEFT :
9544 dx == +1 ? MV_RIGHT :
9546 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9547 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9548 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
9549 int old_element = Feld[jx][jy];
9552 if (player->MovPos == 0)
9554 player->is_digging = FALSE;
9555 player->is_collecting = FALSE;
9558 if (player->MovPos == 0) /* last pushing move finished */
9559 player->is_pushing = FALSE;
9561 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9563 player->is_switching = FALSE;
9564 player->push_delay = 0;
9566 return MF_NO_ACTION;
9569 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9570 return MF_NO_ACTION;
9575 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
9577 if (IS_TUBE(Feld[jx][jy]) ||
9578 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
9582 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
9583 int tube_leave_directions[][2] =
9585 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9586 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9587 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9588 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
9589 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
9590 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
9591 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
9592 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
9593 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
9594 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
9595 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
9596 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
9599 while (tube_leave_directions[i][0] != tube_element)
9602 if (tube_leave_directions[i][0] == -1) /* should not happen */
9606 if (!(tube_leave_directions[i][1] & move_direction))
9607 return MF_NO_ACTION; /* tube has no opening in this direction */
9612 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9613 old_element = Back[jx][jy];
9617 if (IS_WALKABLE(old_element) &&
9618 !(element_info[old_element].access_direction & move_direction))
9619 return MF_NO_ACTION; /* field has no opening in this direction */
9621 element = Feld[x][y];
9623 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9624 game.engine_version >= VERSION_IDENT(2,2,0,0))
9625 return MF_NO_ACTION;
9629 case EL_SP_PORT_LEFT:
9630 case EL_SP_PORT_RIGHT:
9632 case EL_SP_PORT_DOWN:
9633 case EL_SP_PORT_HORIZONTAL:
9634 case EL_SP_PORT_VERTICAL:
9635 case EL_SP_PORT_ANY:
9636 case EL_SP_GRAVITY_PORT_LEFT:
9637 case EL_SP_GRAVITY_PORT_RIGHT:
9638 case EL_SP_GRAVITY_PORT_UP:
9639 case EL_SP_GRAVITY_PORT_DOWN:
9641 if (!canEnterSupaplexPort(x, y, dx, dy))
9642 return MF_NO_ACTION;
9645 element != EL_SP_PORT_LEFT &&
9646 element != EL_SP_GRAVITY_PORT_LEFT &&
9647 element != EL_SP_PORT_HORIZONTAL &&
9648 element != EL_SP_PORT_ANY) ||
9650 element != EL_SP_PORT_RIGHT &&
9651 element != EL_SP_GRAVITY_PORT_RIGHT &&
9652 element != EL_SP_PORT_HORIZONTAL &&
9653 element != EL_SP_PORT_ANY) ||
9655 element != EL_SP_PORT_UP &&
9656 element != EL_SP_GRAVITY_PORT_UP &&
9657 element != EL_SP_PORT_VERTICAL &&
9658 element != EL_SP_PORT_ANY) ||
9660 element != EL_SP_PORT_DOWN &&
9661 element != EL_SP_GRAVITY_PORT_DOWN &&
9662 element != EL_SP_PORT_VERTICAL &&
9663 element != EL_SP_PORT_ANY) ||
9664 !IN_LEV_FIELD(nextx, nexty) ||
9665 !IS_FREE(nextx, nexty))
9666 return MF_NO_ACTION;
9669 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9670 element == EL_SP_GRAVITY_PORT_RIGHT ||
9671 element == EL_SP_GRAVITY_PORT_UP ||
9672 element == EL_SP_GRAVITY_PORT_DOWN)
9673 game.gravity = !game.gravity;
9675 /* automatically move to the next field with double speed */
9676 player->programmed_action = move_direction;
9678 if (player->move_delay_reset_counter == 0)
9680 player->move_delay_reset_counter = 2; /* two double speed steps */
9682 DOUBLE_PLAYER_SPEED(player);
9685 player->move_delay_reset_counter = 2;
9687 DOUBLE_PLAYER_SPEED(player);
9691 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
9694 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9699 case EL_TUBE_VERTICAL:
9700 case EL_TUBE_HORIZONTAL:
9701 case EL_TUBE_VERTICAL_LEFT:
9702 case EL_TUBE_VERTICAL_RIGHT:
9703 case EL_TUBE_HORIZONTAL_UP:
9704 case EL_TUBE_HORIZONTAL_DOWN:
9705 case EL_TUBE_LEFT_UP:
9706 case EL_TUBE_LEFT_DOWN:
9707 case EL_TUBE_RIGHT_UP:
9708 case EL_TUBE_RIGHT_DOWN:
9711 int tube_enter_directions[][2] =
9713 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9714 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9715 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9716 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
9717 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
9718 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
9719 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
9720 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
9721 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
9722 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
9723 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
9724 { -1, MV_NO_MOVING }
9727 while (tube_enter_directions[i][0] != element)
9730 if (tube_enter_directions[i][0] == -1) /* should not happen */
9734 if (!(tube_enter_directions[i][1] & move_direction))
9735 return MF_NO_ACTION; /* tube has no opening in this direction */
9737 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9744 if (IS_WALKABLE(element))
9746 int sound_action = ACTION_WALKING;
9748 if (!(element_info[element].access_direction & opposite_direction))
9749 return MF_NO_ACTION; /* field not accessible from this direction */
9751 if (element >= EL_GATE_1 && element <= EL_GATE_4)
9753 if (!player->key[element - EL_GATE_1])
9754 return MF_NO_ACTION;
9756 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9758 if (!player->key[element - EL_GATE_1_GRAY])
9759 return MF_NO_ACTION;
9761 else if (element == EL_EXIT_OPEN ||
9762 element == EL_SP_EXIT_OPEN ||
9763 element == EL_SP_EXIT_OPENING)
9765 sound_action = ACTION_PASSING; /* player is passing exit */
9767 else if (element == EL_EMPTY)
9769 sound_action = ACTION_MOVING; /* nothing to walk on */
9772 /* play sound from background or player, whatever is available */
9773 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9774 PlayLevelSoundElementAction(x, y, element, sound_action);
9776 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9780 else if (IS_PASSABLE(element))
9782 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9783 return MF_NO_ACTION;
9785 if (IS_CUSTOM_ELEMENT(element) &&
9786 !(element_info[element].access_direction & opposite_direction))
9787 return MF_NO_ACTION; /* field not accessible from this direction */
9790 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9791 return MF_NO_ACTION;
9794 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9796 if (!player->key[element - EL_EM_GATE_1])
9797 return MF_NO_ACTION;
9799 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9801 if (!player->key[element - EL_EM_GATE_1_GRAY])
9802 return MF_NO_ACTION;
9805 /* automatically move to the next field with double speed */
9806 player->programmed_action = move_direction;
9808 if (player->move_delay_reset_counter == 0)
9810 player->move_delay_reset_counter = 2; /* two double speed steps */
9812 DOUBLE_PLAYER_SPEED(player);
9815 player->move_delay_reset_counter = 2;
9817 DOUBLE_PLAYER_SPEED(player);
9820 PlayLevelSoundAction(x, y, ACTION_PASSING);
9824 else if (IS_DIGGABLE(element))
9828 if (mode != DF_SNAP)
9831 GfxElement[x][y] = GFX_ELEMENT(element);
9834 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9836 player->is_digging = TRUE;
9839 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9841 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED,
9842 player->index_bit, CH_SIDE_ANY);
9845 if (mode == DF_SNAP)
9846 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9851 else if (IS_COLLECTIBLE(element))
9855 if (mode != DF_SNAP)
9857 GfxElement[x][y] = element;
9858 player->is_collecting = TRUE;
9861 if (element == EL_SPEED_PILL)
9862 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9863 else if (element == EL_EXTRA_TIME && level.time > 0)
9866 DrawGameValue_Time(TimeLeft);
9868 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9870 player->shield_normal_time_left += 10;
9871 if (element == EL_SHIELD_DEADLY)
9872 player->shield_deadly_time_left += 10;
9874 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9876 if (player->inventory_size < MAX_INVENTORY_SIZE)
9877 player->inventory_element[player->inventory_size++] = element;
9879 DrawGameValue_Dynamite(local_player->inventory_size);
9881 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9883 player->dynabomb_count++;
9884 player->dynabombs_left++;
9886 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9888 player->dynabomb_size++;
9890 else if (element == EL_DYNABOMB_INCREASE_POWER)
9892 player->dynabomb_xl = TRUE;
9894 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9895 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9897 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9898 element - EL_KEY_1 : element - EL_EM_KEY_1);
9900 player->key[key_nr] = TRUE;
9902 DrawGameValue_Keys(player);
9904 redraw_mask |= REDRAW_DOOR_1;
9906 else if (IS_ENVELOPE(element))
9909 player->show_envelope = element;
9911 ShowEnvelope(element - EL_ENVELOPE_1);
9914 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9918 if (element_info[element].collect_count == 0)
9919 player->inventory_infinite_element = element;
9921 for (i = 0; i < element_info[element].collect_count; i++)
9922 if (player->inventory_size < MAX_INVENTORY_SIZE)
9923 player->inventory_element[player->inventory_size++] = element;
9925 DrawGameValue_Dynamite(local_player->inventory_size);
9927 else if (element_info[element].collect_count > 0)
9929 local_player->gems_still_needed -=
9930 element_info[element].collect_count;
9931 if (local_player->gems_still_needed < 0)
9932 local_player->gems_still_needed = 0;
9934 DrawGameValue_Emeralds(local_player->gems_still_needed);
9937 RaiseScoreElement(element);
9938 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9940 CheckTriggeredElementChangePlayer(x, y, element,
9941 CE_OTHER_GETS_COLLECTED,
9942 player->index_bit, CH_SIDE_ANY);
9945 if (mode == DF_SNAP)
9946 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9951 else if (IS_PUSHABLE(element))
9953 if (mode == DF_SNAP && element != EL_BD_ROCK)
9954 return MF_NO_ACTION;
9956 if (CAN_FALL(element) && dy)
9957 return MF_NO_ACTION;
9959 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9960 !(element == EL_SPRING && level.use_spring_bug))
9961 return MF_NO_ACTION;
9964 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9965 ((move_direction & MV_VERTICAL &&
9966 ((element_info[element].move_pattern & MV_LEFT &&
9967 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9968 (element_info[element].move_pattern & MV_RIGHT &&
9969 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9970 (move_direction & MV_HORIZONTAL &&
9971 ((element_info[element].move_pattern & MV_UP &&
9972 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9973 (element_info[element].move_pattern & MV_DOWN &&
9974 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9975 return MF_NO_ACTION;
9979 /* do not push elements already moving away faster than player */
9980 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9981 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9982 return MF_NO_ACTION;
9984 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9985 return MF_NO_ACTION;
9989 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9991 if (player->push_delay_value == -1)
9992 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9994 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9996 if (!player->is_pushing)
9997 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10001 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
10002 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
10003 !player_is_pushing))
10004 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10007 if (!player->is_pushing &&
10008 game.engine_version >= VERSION_IDENT(2,2,0,7))
10009 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10013 printf("::: push delay: %ld [%d, %d] [%d]\n",
10014 player->push_delay_value, FrameCounter, game.engine_version,
10015 player->is_pushing);
10018 player->is_pushing = TRUE;
10020 if (!(IN_LEV_FIELD(nextx, nexty) &&
10021 (IS_FREE(nextx, nexty) ||
10022 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10023 IS_SB_ELEMENT(element)))))
10024 return MF_NO_ACTION;
10026 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10027 return MF_NO_ACTION;
10029 if (player->push_delay == 0) /* new pushing; restart delay */
10030 player->push_delay = FrameCounter;
10032 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
10033 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10034 element != EL_SPRING && element != EL_BALLOON)
10036 /* make sure that there is no move delay before next try to push */
10037 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10038 player->move_delay = INITIAL_MOVE_DELAY_OFF;
10040 return MF_NO_ACTION;
10044 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
10047 if (IS_SB_ELEMENT(element))
10049 if (element == EL_SOKOBAN_FIELD_FULL)
10051 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10052 local_player->sokobanfields_still_needed++;
10055 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10057 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10058 local_player->sokobanfields_still_needed--;
10061 Feld[x][y] = EL_SOKOBAN_OBJECT;
10063 if (Back[x][y] == Back[nextx][nexty])
10064 PlayLevelSoundAction(x, y, ACTION_PUSHING);
10065 else if (Back[x][y] != 0)
10066 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10069 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10072 if (local_player->sokobanfields_still_needed == 0 &&
10073 game.emulation == EMU_SOKOBAN)
10075 player->LevelSolved = player->GameOver = TRUE;
10076 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10080 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10082 InitMovingField(x, y, move_direction);
10083 GfxAction[x][y] = ACTION_PUSHING;
10085 if (mode == DF_SNAP)
10086 ContinueMoving(x, y);
10088 MovPos[x][y] = (dx != 0 ? dx : dy);
10090 Pushed[x][y] = TRUE;
10091 Pushed[nextx][nexty] = TRUE;
10093 if (game.engine_version < VERSION_IDENT(2,2,0,7))
10094 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10096 player->push_delay_value = -1; /* get new value later */
10098 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED,
10099 player->index_bit, dig_side);
10100 CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10101 player->index_bit, dig_side);
10105 else if (IS_SWITCHABLE(element))
10107 if (PLAYER_SWITCHING(player, x, y))
10110 player->is_switching = TRUE;
10111 player->switch_x = x;
10112 player->switch_y = y;
10114 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10116 if (element == EL_ROBOT_WHEEL)
10118 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10122 DrawLevelField(x, y);
10124 else if (element == EL_SP_TERMINAL)
10128 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
10130 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10132 else if (Feld[xx][yy] == EL_SP_TERMINAL)
10133 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10136 else if (IS_BELT_SWITCH(element))
10138 ToggleBeltSwitch(x, y);
10140 else if (element == EL_SWITCHGATE_SWITCH_UP ||
10141 element == EL_SWITCHGATE_SWITCH_DOWN)
10143 ToggleSwitchgateSwitch(x, y);
10145 else if (element == EL_LIGHT_SWITCH ||
10146 element == EL_LIGHT_SWITCH_ACTIVE)
10148 ToggleLightSwitch(x, y);
10151 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
10152 SND_LIGHT_SWITCH_ACTIVATING :
10153 SND_LIGHT_SWITCH_DEACTIVATING);
10156 else if (element == EL_TIMEGATE_SWITCH)
10158 ActivateTimegateSwitch(x, y);
10160 else if (element == EL_BALLOON_SWITCH_LEFT ||
10161 element == EL_BALLOON_SWITCH_RIGHT ||
10162 element == EL_BALLOON_SWITCH_UP ||
10163 element == EL_BALLOON_SWITCH_DOWN ||
10164 element == EL_BALLOON_SWITCH_ANY)
10166 if (element == EL_BALLOON_SWITCH_ANY)
10167 game.balloon_dir = move_direction;
10169 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10170 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10171 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10172 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10175 else if (element == EL_LAMP)
10177 Feld[x][y] = EL_LAMP_ACTIVE;
10178 local_player->lights_still_needed--;
10180 DrawLevelField(x, y);
10182 else if (element == EL_TIME_ORB_FULL)
10184 Feld[x][y] = EL_TIME_ORB_EMPTY;
10186 DrawGameValue_Time(TimeLeft);
10188 DrawLevelField(x, y);
10191 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
10199 if (!PLAYER_SWITCHING(player, x, y))
10201 player->is_switching = TRUE;
10202 player->switch_x = x;
10203 player->switch_y = y;
10205 CheckTriggeredElementChangePlayer(x, y, element,
10206 CE_OTHER_IS_SWITCHING,
10207 player->index_bit, dig_side);
10208 CheckElementChangePlayer(x, y, element, CE_SWITCHED,
10209 player->index_bit, dig_side);
10212 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED,
10213 player->index_bit, dig_side);
10214 CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10215 player->index_bit, dig_side);
10218 return MF_NO_ACTION;
10221 player->push_delay = 0;
10223 if (Feld[x][y] != element) /* really digged/collected something */
10224 player->is_collecting = !player->is_digging;
10229 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10231 int jx = player->jx, jy = player->jy;
10232 int x = jx + dx, y = jy + dy;
10233 int snap_direction = (dx == -1 ? MV_LEFT :
10234 dx == +1 ? MV_RIGHT :
10236 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10239 if (player->MovPos)
10242 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
10246 if (!player->active || !IN_LEV_FIELD(x, y))
10254 if (player->MovPos == 0)
10255 player->is_pushing = FALSE;
10257 player->is_snapping = FALSE;
10259 if (player->MovPos == 0)
10261 player->is_moving = FALSE;
10262 player->is_digging = FALSE;
10263 player->is_collecting = FALSE;
10269 if (player->is_snapping)
10272 player->MovDir = snap_direction;
10275 if (player->MovPos == 0)
10278 player->is_moving = FALSE;
10279 player->is_digging = FALSE;
10280 player->is_collecting = FALSE;
10283 player->is_dropping = FALSE;
10285 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10288 player->is_snapping = TRUE;
10291 if (player->MovPos == 0)
10294 player->is_moving = FALSE;
10295 player->is_digging = FALSE;
10296 player->is_collecting = FALSE;
10299 DrawLevelField(x, y);
10305 boolean DropElement(struct PlayerInfo *player)
10307 int jx = player->jx, jy = player->jy;
10308 int old_element = Feld[jx][jy];
10309 int new_element = (player->inventory_size > 0 ?
10310 player->inventory_element[player->inventory_size - 1] :
10311 player->inventory_infinite_element != EL_UNDEFINED ?
10312 player->inventory_infinite_element :
10313 player->dynabombs_left > 0 ?
10314 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10317 /* check if player is active, not moving and ready to drop */
10318 if (!player->active || player->MovPos || player->drop_delay > 0)
10321 /* check if player has anything that can be dropped */
10323 if (new_element == EL_UNDEFINED)
10326 if (player->inventory_size == 0 &&
10327 player->inventory_infinite_element == EL_UNDEFINED &&
10328 player->dynabombs_left == 0)
10332 /* check if anything can be dropped at the current position */
10333 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10336 /* collected custom elements can only be dropped on empty fields */
10338 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10341 if (player->inventory_size > 0 &&
10342 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
10343 && old_element != EL_EMPTY)
10347 if (old_element != EL_EMPTY)
10348 Back[jx][jy] = old_element; /* store old element on this field */
10350 ResetGfxAnimation(jx, jy);
10351 ResetRandomAnimationValue(jx, jy);
10353 if (player->inventory_size > 0 ||
10354 player->inventory_infinite_element != EL_UNDEFINED)
10356 if (player->inventory_size > 0)
10358 player->inventory_size--;
10361 new_element = player->inventory_element[player->inventory_size];
10364 DrawGameValue_Dynamite(local_player->inventory_size);
10366 if (new_element == EL_DYNAMITE)
10367 new_element = EL_DYNAMITE_ACTIVE;
10368 else if (new_element == EL_SP_DISK_RED)
10369 new_element = EL_SP_DISK_RED_ACTIVE;
10372 Feld[jx][jy] = new_element;
10374 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10375 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10377 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10380 /* needed if previous element just changed to "empty" in the last frame */
10381 Changed[jx][jy] = 0; /* allow another change */
10384 CheckTriggeredElementChangePlayer(jx, jy, new_element,
10385 CE_OTHER_GETS_DROPPED,
10386 player->index_bit, CH_SIDE_ANY);
10387 CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
10388 player->index_bit, CH_SIDE_ANY);
10390 TestIfElementTouchesCustomElement(jx, jy);
10392 else /* player is dropping a dyna bomb */
10394 player->dynabombs_left--;
10397 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
10400 Feld[jx][jy] = new_element;
10402 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10403 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10405 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10412 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
10415 InitField_WithBug1(jx, jy, FALSE);
10417 InitField(jx, jy, FALSE);
10418 if (CAN_MOVE(Feld[jx][jy]))
10419 InitMovDir(jx, jy);
10423 new_element = Feld[jx][jy];
10425 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10426 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10428 int move_stepsize = element_info[new_element].move_stepsize;
10429 int direction, dx, dy, nextx, nexty;
10431 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10432 MovDir[jx][jy] = player->MovDir;
10434 direction = MovDir[jx][jy];
10435 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10436 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10440 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
10443 WasJustMoving[jx][jy] = 3;
10445 InitMovingField(jx, jy, direction);
10446 ContinueMoving(jx, jy);
10451 Changed[jx][jy] = 0; /* allow another change */
10454 TestIfElementHitsCustomElement(jx, jy, direction);
10456 CheckElementChangeSide(jx, jy, new_element, CE_HITTING_SOMETHING,
10461 player->drop_delay = 2 * TILEX / move_stepsize + 1;
10465 player->drop_delay = 8 + 8 + 8;
10470 player->is_dropping = TRUE;
10476 /* ------------------------------------------------------------------------- */
10477 /* game sound playing functions */
10478 /* ------------------------------------------------------------------------- */
10480 static int *loop_sound_frame = NULL;
10481 static int *loop_sound_volume = NULL;
10483 void InitPlayLevelSound()
10485 int num_sounds = getSoundListSize();
10487 checked_free(loop_sound_frame);
10488 checked_free(loop_sound_volume);
10490 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10491 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10494 static void PlayLevelSound(int x, int y, int nr)
10496 int sx = SCREENX(x), sy = SCREENY(y);
10497 int volume, stereo_position;
10498 int max_distance = 8;
10499 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10501 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10502 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10505 if (!IN_LEV_FIELD(x, y) ||
10506 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10507 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10510 volume = SOUND_MAX_VOLUME;
10512 if (!IN_SCR_FIELD(sx, sy))
10514 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10515 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10517 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10520 stereo_position = (SOUND_MAX_LEFT +
10521 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10522 (SCR_FIELDX + 2 * max_distance));
10524 if (IS_LOOP_SOUND(nr))
10526 /* This assures that quieter loop sounds do not overwrite louder ones,
10527 while restarting sound volume comparison with each new game frame. */
10529 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10532 loop_sound_volume[nr] = volume;
10533 loop_sound_frame[nr] = FrameCounter;
10536 PlaySoundExt(nr, volume, stereo_position, type);
10539 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10541 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10542 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10543 y < LEVELY(BY1) ? LEVELY(BY1) :
10544 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10548 static void PlayLevelSoundAction(int x, int y, int action)
10550 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10553 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10555 int sound_effect = element_info[element].sound[action];
10557 if (sound_effect != SND_UNDEFINED)
10558 PlayLevelSound(x, y, sound_effect);
10561 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10564 int sound_effect = element_info[element].sound[action];
10566 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10567 PlayLevelSound(x, y, sound_effect);
10570 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10572 int sound_effect = element_info[Feld[x][y]].sound[action];
10574 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10575 PlayLevelSound(x, y, sound_effect);
10578 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10580 int sound_effect = element_info[Feld[x][y]].sound[action];
10582 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10583 StopSound(sound_effect);
10586 static void PlayLevelMusic()
10588 if (levelset.music[level_nr] != MUS_UNDEFINED)
10589 PlayMusic(levelset.music[level_nr]); /* from config file */
10591 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10594 void RaiseScore(int value)
10596 local_player->score += value;
10598 DrawGameValue_Score(local_player->score);
10601 void RaiseScoreElement(int element)
10606 case EL_BD_DIAMOND:
10607 case EL_EMERALD_YELLOW:
10608 case EL_EMERALD_RED:
10609 case EL_EMERALD_PURPLE:
10610 case EL_SP_INFOTRON:
10611 RaiseScore(level.score[SC_EMERALD]);
10614 RaiseScore(level.score[SC_DIAMOND]);
10617 RaiseScore(level.score[SC_CRYSTAL]);
10620 RaiseScore(level.score[SC_PEARL]);
10623 case EL_BD_BUTTERFLY:
10624 case EL_SP_ELECTRON:
10625 RaiseScore(level.score[SC_BUG]);
10628 case EL_BD_FIREFLY:
10629 case EL_SP_SNIKSNAK:
10630 RaiseScore(level.score[SC_SPACESHIP]);
10633 case EL_DARK_YAMYAM:
10634 RaiseScore(level.score[SC_YAMYAM]);
10637 RaiseScore(level.score[SC_ROBOT]);
10640 RaiseScore(level.score[SC_PACMAN]);
10643 RaiseScore(level.score[SC_NUT]);
10646 case EL_SP_DISK_RED:
10647 case EL_DYNABOMB_INCREASE_NUMBER:
10648 case EL_DYNABOMB_INCREASE_SIZE:
10649 case EL_DYNABOMB_INCREASE_POWER:
10650 RaiseScore(level.score[SC_DYNAMITE]);
10652 case EL_SHIELD_NORMAL:
10653 case EL_SHIELD_DEADLY:
10654 RaiseScore(level.score[SC_SHIELD]);
10656 case EL_EXTRA_TIME:
10657 RaiseScore(level.score[SC_TIME_BONUS]);
10663 RaiseScore(level.score[SC_KEY]);
10666 RaiseScore(element_info[element].collect_score);
10671 void RequestQuitGame(boolean ask_if_really_quit)
10673 if (AllPlayersGone ||
10674 !ask_if_really_quit ||
10675 level_editor_test_game ||
10676 Request("Do you really want to quit the game ?",
10677 REQ_ASK | REQ_STAY_CLOSED))
10679 #if defined(PLATFORM_UNIX)
10680 if (options.network)
10681 SendToServer_StopPlaying();
10685 game_status = GAME_MODE_MAIN;
10693 if (tape.playing && tape.index_search)
10695 SetDrawDeactivationMask(REDRAW_NONE);
10696 audio.sound_deactivated = FALSE;
10700 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10703 if (tape.playing && tape.index_search)
10705 SetDrawDeactivationMask(REDRAW_FIELD);
10706 audio.sound_deactivated = TRUE;
10714 /* ---------- new game button stuff ---------------------------------------- */
10716 /* graphic position values for game buttons */
10717 #define GAME_BUTTON_XSIZE 30
10718 #define GAME_BUTTON_YSIZE 30
10719 #define GAME_BUTTON_XPOS 5
10720 #define GAME_BUTTON_YPOS 215
10721 #define SOUND_BUTTON_XPOS 5
10722 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10724 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10725 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10726 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10727 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10728 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10729 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10736 } gamebutton_info[NUM_GAME_BUTTONS] =
10739 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10744 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10745 GAME_CTRL_ID_PAUSE,
10749 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10754 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10755 SOUND_CTRL_ID_MUSIC,
10756 "background music on/off"
10759 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10760 SOUND_CTRL_ID_LOOPS,
10761 "sound loops on/off"
10764 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10765 SOUND_CTRL_ID_SIMPLE,
10766 "normal sounds on/off"
10770 void CreateGameButtons()
10774 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10776 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10777 struct GadgetInfo *gi;
10780 unsigned long event_mask;
10781 int gd_xoffset, gd_yoffset;
10782 int gd_x1, gd_x2, gd_y1, gd_y2;
10785 gd_xoffset = gamebutton_info[i].x;
10786 gd_yoffset = gamebutton_info[i].y;
10787 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10788 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10790 if (id == GAME_CTRL_ID_STOP ||
10791 id == GAME_CTRL_ID_PAUSE ||
10792 id == GAME_CTRL_ID_PLAY)
10794 button_type = GD_TYPE_NORMAL_BUTTON;
10796 event_mask = GD_EVENT_RELEASED;
10797 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10798 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10802 button_type = GD_TYPE_CHECK_BUTTON;
10804 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10805 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10806 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10807 event_mask = GD_EVENT_PRESSED;
10808 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10809 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10812 gi = CreateGadget(GDI_CUSTOM_ID, id,
10813 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10814 GDI_X, DX + gd_xoffset,
10815 GDI_Y, DY + gd_yoffset,
10816 GDI_WIDTH, GAME_BUTTON_XSIZE,
10817 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10818 GDI_TYPE, button_type,
10819 GDI_STATE, GD_BUTTON_UNPRESSED,
10820 GDI_CHECKED, checked,
10821 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10822 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10823 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10824 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10825 GDI_EVENT_MASK, event_mask,
10826 GDI_CALLBACK_ACTION, HandleGameButtons,
10830 Error(ERR_EXIT, "cannot create gadget");
10832 game_gadget[id] = gi;
10836 void FreeGameButtons()
10840 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10841 FreeGadget(game_gadget[i]);
10844 static void MapGameButtons()
10848 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10849 MapGadget(game_gadget[i]);
10852 void UnmapGameButtons()
10856 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10857 UnmapGadget(game_gadget[i]);
10860 static void HandleGameButtons(struct GadgetInfo *gi)
10862 int id = gi->custom_id;
10864 if (game_status != GAME_MODE_PLAYING)
10869 case GAME_CTRL_ID_STOP:
10870 RequestQuitGame(TRUE);
10873 case GAME_CTRL_ID_PAUSE:
10874 if (options.network)
10876 #if defined(PLATFORM_UNIX)
10878 SendToServer_ContinuePlaying();
10880 SendToServer_PausePlaying();
10884 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10887 case GAME_CTRL_ID_PLAY:
10890 #if defined(PLATFORM_UNIX)
10891 if (options.network)
10892 SendToServer_ContinuePlaying();
10896 tape.pausing = FALSE;
10897 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10902 case SOUND_CTRL_ID_MUSIC:
10903 if (setup.sound_music)
10905 setup.sound_music = FALSE;
10908 else if (audio.music_available)
10910 setup.sound = setup.sound_music = TRUE;
10912 SetAudioMode(setup.sound);
10918 case SOUND_CTRL_ID_LOOPS:
10919 if (setup.sound_loops)
10920 setup.sound_loops = FALSE;
10921 else if (audio.loops_available)
10923 setup.sound = setup.sound_loops = TRUE;
10924 SetAudioMode(setup.sound);
10928 case SOUND_CTRL_ID_SIMPLE:
10929 if (setup.sound_simple)
10930 setup.sound_simple = FALSE;
10931 else if (audio.sound_available)
10933 setup.sound = setup.sound_simple = TRUE;
10934 SetAudioMode(setup.sound);