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) */
3951 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3952 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3954 TestIfBadThingTouchesOtherBadThing(x, y);
3956 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
3957 MovDir[x][y] = left_dir;
3958 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3959 MovDir[x][y] = right_dir;
3961 if ((element == EL_SPACESHIP ||
3962 element == EL_SP_SNIKSNAK ||
3963 element == EL_SP_ELECTRON)
3964 && MovDir[x][y] != old_move_dir)
3966 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3970 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
3972 TestIfBadThingTouchesOtherBadThing(x, y);
3974 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
3975 MovDir[x][y] = left_dir;
3976 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3977 MovDir[x][y] = right_dir;
3979 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
3981 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3984 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3986 TestIfBadThingTouchesOtherBadThing(x, y);
3988 if (ELEMENT_CAN_ENTER_FIELD_GENERIC(element, left_x, left_y, 0))
3989 MovDir[x][y] = left_dir;
3990 else if (!ELEMENT_CAN_ENTER_FIELD_GENERIC(element, move_x, move_y, 0))
3991 MovDir[x][y] = right_dir;
3993 if (MovDir[x][y] != old_move_dir)
3997 else if (element == EL_YAMYAM)
3999 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
4000 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
4002 if (can_turn_left && can_turn_right)
4003 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4004 else if (can_turn_left)
4005 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4006 else if (can_turn_right)
4007 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4009 MovDir[x][y] = back_dir;
4011 MovDelay[x][y] = 16 + 16 * RND(3);
4013 else if (element == EL_DARK_YAMYAM)
4015 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
4016 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
4018 if (can_turn_left && can_turn_right)
4019 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4020 else if (can_turn_left)
4021 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4022 else if (can_turn_right)
4023 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4025 MovDir[x][y] = back_dir;
4027 MovDelay[x][y] = 16 + 16 * RND(3);
4029 else if (element == EL_PACMAN)
4031 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
4032 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
4034 if (can_turn_left && can_turn_right)
4035 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4036 else if (can_turn_left)
4037 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4038 else if (can_turn_right)
4039 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4041 MovDir[x][y] = back_dir;
4043 MovDelay[x][y] = 6 + RND(40);
4045 else if (element == EL_PIG)
4047 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
4048 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
4049 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
4050 boolean should_turn_left, should_turn_right, should_move_on;
4052 int rnd = RND(rnd_value);
4054 should_turn_left = (can_turn_left &&
4056 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4057 y + back_dy + left_dy)));
4058 should_turn_right = (can_turn_right &&
4060 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4061 y + back_dy + right_dy)));
4062 should_move_on = (can_move_on &&
4065 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4066 y + move_dy + left_dy) ||
4067 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4068 y + move_dy + right_dy)));
4070 if (should_turn_left || should_turn_right || should_move_on)
4072 if (should_turn_left && should_turn_right && should_move_on)
4073 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4074 rnd < 2 * rnd_value / 3 ? right_dir :
4076 else if (should_turn_left && should_turn_right)
4077 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4078 else if (should_turn_left && should_move_on)
4079 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4080 else if (should_turn_right && should_move_on)
4081 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4082 else if (should_turn_left)
4083 MovDir[x][y] = left_dir;
4084 else if (should_turn_right)
4085 MovDir[x][y] = right_dir;
4086 else if (should_move_on)
4087 MovDir[x][y] = old_move_dir;
4089 else if (can_move_on && rnd > rnd_value / 8)
4090 MovDir[x][y] = old_move_dir;
4091 else if (can_turn_left && can_turn_right)
4092 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4093 else if (can_turn_left && rnd > rnd_value / 8)
4094 MovDir[x][y] = left_dir;
4095 else if (can_turn_right && rnd > rnd_value/8)
4096 MovDir[x][y] = right_dir;
4098 MovDir[x][y] = back_dir;
4100 xx = x + move_xy[MovDir[x][y]].x;
4101 yy = y + move_xy[MovDir[x][y]].y;
4103 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4104 MovDir[x][y] = old_move_dir;
4108 else if (element == EL_DRAGON)
4110 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(left_x, left_y);
4111 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(right_x, right_y);
4112 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(move_x, move_y);
4114 int rnd = RND(rnd_value);
4117 if (FrameCounter < 1 && x == 0 && y == 29)
4118 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4121 if (can_move_on && rnd > rnd_value / 8)
4122 MovDir[x][y] = old_move_dir;
4123 else if (can_turn_left && can_turn_right)
4124 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4125 else if (can_turn_left && rnd > rnd_value / 8)
4126 MovDir[x][y] = left_dir;
4127 else if (can_turn_right && rnd > rnd_value / 8)
4128 MovDir[x][y] = right_dir;
4130 MovDir[x][y] = back_dir;
4132 xx = x + move_xy[MovDir[x][y]].x;
4133 yy = y + move_xy[MovDir[x][y]].y;
4136 if (FrameCounter < 1 && x == 0 && y == 29)
4137 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4138 xx, yy, Feld[xx][yy],
4143 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4144 MovDir[x][y] = old_move_dir;
4146 if (!IS_FREE(xx, yy))
4147 MovDir[x][y] = old_move_dir;
4151 if (FrameCounter < 1 && x == 0 && y == 29)
4152 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4157 else if (element == EL_MOLE)
4159 boolean can_move_on =
4160 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
4161 IS_AMOEBOID(Feld[move_x][move_y]) ||
4162 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4165 boolean can_turn_left =
4166 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
4167 IS_AMOEBOID(Feld[left_x][left_y])));
4169 boolean can_turn_right =
4170 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
4171 IS_AMOEBOID(Feld[right_x][right_y])));
4173 if (can_turn_left && can_turn_right)
4174 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4175 else if (can_turn_left)
4176 MovDir[x][y] = left_dir;
4178 MovDir[x][y] = right_dir;
4181 if (MovDir[x][y] != old_move_dir)
4184 else if (element == EL_BALLOON)
4186 MovDir[x][y] = game.balloon_dir;
4189 else if (element == EL_SPRING)
4192 if (MovDir[x][y] & MV_HORIZONTAL &&
4193 !SPRING_CAN_ENTER_FIELD(move_x, move_y))
4194 MovDir[x][y] = MV_NO_MOVING;
4196 if (MovDir[x][y] & MV_HORIZONTAL &&
4197 (!SPRING_CAN_ENTER_FIELD(move_x, move_y) ||
4198 SPRING_CAN_ENTER_FIELD(x, y + 1)))
4199 MovDir[x][y] = MV_NO_MOVING;
4204 else if (element == EL_ROBOT ||
4205 element == EL_SATELLITE ||
4206 element == EL_PENGUIN)
4208 int attr_x = -1, attr_y = -1;
4219 for (i = 0; i < MAX_PLAYERS; i++)
4221 struct PlayerInfo *player = &stored_player[i];
4222 int jx = player->jx, jy = player->jy;
4224 if (!player->active)
4228 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4236 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4242 if (element == EL_PENGUIN)
4245 static int xy[4][2] =
4253 for (i = 0; i < NUM_DIRECTIONS; i++)
4255 int ex = x + xy[i][0];
4256 int ey = y + xy[i][1];
4258 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4267 MovDir[x][y] = MV_NO_MOVING;
4269 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4270 else if (attr_x > x)
4271 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4273 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4274 else if (attr_y > y)
4275 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4277 if (element == EL_ROBOT)
4281 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4282 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4283 Moving2Blocked(x, y, &newx, &newy);
4285 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4286 MovDelay[x][y] = 8 + 8 * !RND(3);
4288 MovDelay[x][y] = 16;
4290 else if (element == EL_PENGUIN)
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 (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4309 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4310 Moving2Blocked(x, y, &newx, &newy);
4312 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4315 MovDir[x][y] = old_move_dir;
4319 else /* (element == EL_SATELLITE) */
4325 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4327 boolean first_horiz = RND(2);
4328 int new_move_dir = MovDir[x][y];
4331 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4332 Moving2Blocked(x, y, &newx, &newy);
4334 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4338 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4339 Moving2Blocked(x, y, &newx, &newy);
4341 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4344 MovDir[x][y] = old_move_dir;
4349 else if (move_pattern == MV_TURNING_LEFT ||
4350 move_pattern == MV_TURNING_RIGHT ||
4351 move_pattern == MV_TURNING_LEFT_RIGHT ||
4352 move_pattern == MV_TURNING_RIGHT_LEFT ||
4353 move_pattern == MV_TURNING_RANDOM ||
4354 move_pattern == MV_ALL_DIRECTIONS)
4356 boolean can_turn_left =
4357 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4358 boolean can_turn_right =
4359 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4361 if (move_pattern == MV_TURNING_LEFT)
4362 MovDir[x][y] = left_dir;
4363 else if (move_pattern == MV_TURNING_RIGHT)
4364 MovDir[x][y] = right_dir;
4365 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4366 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4367 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4368 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4369 else if (move_pattern == MV_TURNING_RANDOM)
4370 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4371 can_turn_right && !can_turn_left ? right_dir :
4372 RND(2) ? left_dir : right_dir);
4373 else if (can_turn_left && can_turn_right)
4374 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4375 else if (can_turn_left)
4376 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4377 else if (can_turn_right)
4378 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4380 MovDir[x][y] = back_dir;
4382 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4384 else if (move_pattern == MV_HORIZONTAL ||
4385 move_pattern == MV_VERTICAL)
4387 if (move_pattern & old_move_dir)
4388 MovDir[x][y] = back_dir;
4389 else if (move_pattern == MV_HORIZONTAL)
4390 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4391 else if (move_pattern == MV_VERTICAL)
4392 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4394 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4396 else if (move_pattern & MV_ANY_DIRECTION)
4398 MovDir[x][y] = move_pattern;
4399 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4401 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4403 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4404 MovDir[x][y] = left_dir;
4405 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4406 MovDir[x][y] = right_dir;
4408 if (MovDir[x][y] != old_move_dir)
4409 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4411 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4413 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4414 MovDir[x][y] = right_dir;
4415 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4416 MovDir[x][y] = left_dir;
4418 if (MovDir[x][y] != old_move_dir)
4419 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4421 else if (move_pattern == MV_TOWARDS_PLAYER ||
4422 move_pattern == MV_AWAY_FROM_PLAYER)
4424 int attr_x = -1, attr_y = -1;
4426 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4437 for (i = 0; i < MAX_PLAYERS; i++)
4439 struct PlayerInfo *player = &stored_player[i];
4440 int jx = player->jx, jy = player->jy;
4442 if (!player->active)
4446 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4454 MovDir[x][y] = MV_NO_MOVING;
4456 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4457 else if (attr_x > x)
4458 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4460 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4461 else if (attr_y > y)
4462 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4464 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4466 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4468 boolean first_horiz = RND(2);
4469 int new_move_dir = MovDir[x][y];
4472 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4473 Moving2Blocked(x, y, &newx, &newy);
4475 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4479 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4480 Moving2Blocked(x, y, &newx, &newy);
4482 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4485 MovDir[x][y] = old_move_dir;
4488 else if (move_pattern == MV_WHEN_PUSHED ||
4489 move_pattern == MV_WHEN_DROPPED)
4491 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4492 MovDir[x][y] = MV_NO_MOVING;
4496 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4498 static int test_xy[7][2] =
4508 static int test_dir[7] =
4518 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4519 int move_preference = -1000000; /* start with very low preference */
4520 int new_move_dir = MV_NO_MOVING;
4521 int start_test = RND(4);
4524 for (i = 0; i < NUM_DIRECTIONS; i++)
4526 int move_dir = test_dir[start_test + i];
4527 int move_dir_preference;
4529 xx = x + test_xy[start_test + i][0];
4530 yy = y + test_xy[start_test + i][1];
4532 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4533 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4535 new_move_dir = move_dir;
4540 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4543 move_dir_preference = -1 * RunnerVisit[xx][yy];
4544 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4545 move_dir_preference = PlayerVisit[xx][yy];
4547 if (move_dir_preference > move_preference)
4549 /* prefer field that has not been visited for the longest time */
4550 move_preference = move_dir_preference;
4551 new_move_dir = move_dir;
4553 else if (move_dir_preference == move_preference &&
4554 move_dir == old_move_dir)
4556 /* prefer last direction when all directions are preferred equally */
4557 move_preference = move_dir_preference;
4558 new_move_dir = move_dir;
4562 MovDir[x][y] = new_move_dir;
4563 if (old_move_dir != new_move_dir)
4568 static void TurnRound(int x, int y)
4570 int direction = MovDir[x][y];
4573 GfxDir[x][y] = MovDir[x][y];
4579 GfxDir[x][y] = MovDir[x][y];
4582 if (direction != MovDir[x][y])
4587 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4590 GfxAction[x][y] = ACTION_WAITING;
4594 static boolean JustBeingPushed(int x, int y)
4598 for (i = 0; i < MAX_PLAYERS; i++)
4600 struct PlayerInfo *player = &stored_player[i];
4602 if (player->active && player->is_pushing && player->MovPos)
4604 int next_jx = player->jx + (player->jx - player->last_jx);
4605 int next_jy = player->jy + (player->jy - player->last_jy);
4607 if (x == next_jx && y == next_jy)
4615 void StartMoving(int x, int y)
4618 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4620 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4621 int element = Feld[x][y];
4627 if (MovDelay[x][y] == 0)
4628 GfxAction[x][y] = ACTION_DEFAULT;
4630 /* !!! this should be handled more generic (not only for mole) !!! */
4631 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4632 GfxAction[x][y] = ACTION_DEFAULT;
4635 if (CAN_FALL(element) && y < lev_fieldy - 1)
4637 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4638 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4639 if (JustBeingPushed(x, y))
4642 if (element == EL_QUICKSAND_FULL)
4644 if (IS_FREE(x, y + 1))
4646 InitMovingField(x, y, MV_DOWN);
4647 started_moving = TRUE;
4649 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4650 Store[x][y] = EL_ROCK;
4652 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4654 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4657 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4659 if (!MovDelay[x][y])
4660 MovDelay[x][y] = TILEY + 1;
4669 Feld[x][y] = EL_QUICKSAND_EMPTY;
4670 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4671 Store[x][y + 1] = Store[x][y];
4674 PlayLevelSoundAction(x, y, ACTION_FILLING);
4676 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4680 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4681 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4683 InitMovingField(x, y, MV_DOWN);
4684 started_moving = TRUE;
4686 Feld[x][y] = EL_QUICKSAND_FILLING;
4687 Store[x][y] = element;
4689 PlayLevelSoundAction(x, y, ACTION_FILLING);
4691 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4694 else if (element == EL_MAGIC_WALL_FULL)
4696 if (IS_FREE(x, y + 1))
4698 InitMovingField(x, y, MV_DOWN);
4699 started_moving = TRUE;
4701 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4702 Store[x][y] = EL_CHANGED(Store[x][y]);
4704 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4706 if (!MovDelay[x][y])
4707 MovDelay[x][y] = TILEY/4 + 1;
4716 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4717 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4718 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4722 else if (element == EL_BD_MAGIC_WALL_FULL)
4724 if (IS_FREE(x, y + 1))
4726 InitMovingField(x, y, MV_DOWN);
4727 started_moving = TRUE;
4729 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4730 Store[x][y] = EL_CHANGED2(Store[x][y]);
4732 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4734 if (!MovDelay[x][y])
4735 MovDelay[x][y] = TILEY/4 + 1;
4744 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4745 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4746 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4750 else if (CAN_PASS_MAGIC_WALL(element) &&
4751 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4752 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4754 InitMovingField(x, y, MV_DOWN);
4755 started_moving = TRUE;
4758 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4759 EL_BD_MAGIC_WALL_FILLING);
4760 Store[x][y] = element;
4763 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4765 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4768 SplashAcid(x, y + 1);
4770 InitMovingField(x, y, MV_DOWN);
4771 started_moving = TRUE;
4773 Store[x][y] = EL_ACID;
4775 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4776 GfxAction[x][y + 1] = ACTION_ACTIVE;
4780 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4781 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4782 (Feld[x][y + 1] == EL_BLOCKED)) ||
4783 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4784 CAN_SMASH(element) && WasJustFalling[x][y] &&
4785 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4789 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4790 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4791 WasJustMoving[x][y] && !Pushed[x][y + 1])
4793 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4794 WasJustMoving[x][y])
4799 /* this is needed for a special case not covered by calling "Impact()"
4800 from "ContinueMoving()": if an element moves to a tile directly below
4801 another element which was just falling on that tile (which was empty
4802 in the previous frame), the falling element above would just stop
4803 instead of smashing the element below (in previous version, the above
4804 element was just checked for "moving" instead of "falling", resulting
4805 in incorrect smashes caused by horizontal movement of the above
4806 element; also, the case of the player being the element to smash was
4807 simply not covered here... :-/ ) */
4810 WasJustMoving[x][y] = 0;
4811 WasJustFalling[x][y] = 0;
4816 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4818 if (MovDir[x][y] == MV_NO_MOVING)
4820 InitMovingField(x, y, MV_DOWN);
4821 started_moving = TRUE;
4824 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4826 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4827 MovDir[x][y] = MV_DOWN;
4829 InitMovingField(x, y, MV_DOWN);
4830 started_moving = TRUE;
4832 else if (element == EL_AMOEBA_DROP)
4834 Feld[x][y] = EL_AMOEBA_GROWING;
4835 Store[x][y] = EL_AMOEBA_WET;
4837 /* Store[x][y + 1] must be zero, because:
4838 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4841 #if OLD_GAME_BEHAVIOUR
4842 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4844 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4845 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4846 element != EL_DX_SUPABOMB)
4849 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4850 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4851 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4852 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4855 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4856 (IS_FREE(x - 1, y + 1) ||
4857 Feld[x - 1][y + 1] == EL_ACID));
4858 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4859 (IS_FREE(x + 1, y + 1) ||
4860 Feld[x + 1][y + 1] == EL_ACID));
4861 boolean can_fall_any = (can_fall_left || can_fall_right);
4862 boolean can_fall_both = (can_fall_left && can_fall_right);
4864 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4866 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4868 if (slippery_type == SLIPPERY_ONLY_LEFT)
4869 can_fall_right = FALSE;
4870 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4871 can_fall_left = FALSE;
4872 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4873 can_fall_right = FALSE;
4874 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4875 can_fall_left = FALSE;
4877 can_fall_any = (can_fall_left || can_fall_right);
4878 can_fall_both = (can_fall_left && can_fall_right);
4883 if (can_fall_both &&
4884 (game.emulation != EMU_BOULDERDASH &&
4885 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4886 can_fall_left = !(can_fall_right = RND(2));
4888 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4889 started_moving = TRUE;
4893 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4895 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4898 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4899 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4900 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4901 int belt_dir = game.belt_dir[belt_nr];
4903 if ((belt_dir == MV_LEFT && left_is_free) ||
4904 (belt_dir == MV_RIGHT && right_is_free))
4907 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4910 InitMovingField(x, y, belt_dir);
4911 started_moving = TRUE;
4914 Pushed[x][y] = TRUE;
4915 Pushed[nextx][y] = TRUE;
4918 GfxAction[x][y] = ACTION_DEFAULT;
4922 MovDir[x][y] = 0; /* if element was moving, stop it */
4927 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4928 if (CAN_MOVE(element) && !started_moving)
4930 int move_pattern = element_info[element].move_pattern;
4933 Moving2Blocked(x, y, &newx, &newy);
4936 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4939 if ((element == EL_SATELLITE ||
4940 element == EL_BALLOON ||
4941 element == EL_SPRING)
4942 && JustBeingPushed(x, y))
4947 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4948 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4949 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4952 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4953 element, element_info[element].token_name,
4954 WasJustMoving[x][y],
4955 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4956 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4957 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4958 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4962 WasJustMoving[x][y] = 0;
4965 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4968 if (Feld[x][y] != element) /* element has changed */
4970 element = Feld[x][y];
4971 move_pattern = element_info[element].move_pattern;
4973 if (!CAN_MOVE(element))
4977 if (Feld[x][y] != element) /* element has changed */
4985 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4986 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4988 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4990 Moving2Blocked(x, y, &newx, &newy);
4991 if (Feld[newx][newy] == EL_BLOCKED)
4992 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4998 if (FrameCounter < 1 && x == 0 && y == 29)
4999 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5002 if (!MovDelay[x][y]) /* start new movement phase */
5004 /* all objects that can change their move direction after each step
5005 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5007 if (element != EL_YAMYAM &&
5008 element != EL_DARK_YAMYAM &&
5009 element != EL_PACMAN &&
5010 !(move_pattern & MV_ANY_DIRECTION) &&
5011 move_pattern != MV_TURNING_LEFT &&
5012 move_pattern != MV_TURNING_RIGHT &&
5013 move_pattern != MV_TURNING_LEFT_RIGHT &&
5014 move_pattern != MV_TURNING_RIGHT_LEFT &&
5015 move_pattern != MV_TURNING_RANDOM)
5020 if (FrameCounter < 1 && x == 0 && y == 29)
5021 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5024 if (MovDelay[x][y] && (element == EL_BUG ||
5025 element == EL_SPACESHIP ||
5026 element == EL_SP_SNIKSNAK ||
5027 element == EL_SP_ELECTRON ||
5028 element == EL_MOLE))
5029 DrawLevelField(x, y);
5033 if (MovDelay[x][y]) /* wait some time before next movement */
5038 if (element == EL_YAMYAM)
5041 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5042 DrawLevelElementAnimation(x, y, element);
5046 if (MovDelay[x][y]) /* element still has to wait some time */
5049 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5050 ResetGfxAnimation(x, y);
5054 if (GfxAction[x][y] != ACTION_WAITING)
5055 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5057 GfxAction[x][y] = ACTION_WAITING;
5061 if (element == EL_ROBOT ||
5063 element == EL_PACMAN ||
5065 element == EL_YAMYAM ||
5066 element == EL_DARK_YAMYAM)
5069 DrawLevelElementAnimation(x, y, element);
5071 DrawLevelElementAnimationIfNeeded(x, y, element);
5073 PlayLevelSoundAction(x, y, ACTION_WAITING);
5075 else if (element == EL_SP_ELECTRON)
5076 DrawLevelElementAnimationIfNeeded(x, y, element);
5077 else if (element == EL_DRAGON)
5080 int dir = MovDir[x][y];
5081 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5082 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5083 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5084 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5085 dir == MV_UP ? IMG_FLAMES_1_UP :
5086 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5087 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5090 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5093 GfxAction[x][y] = ACTION_ATTACKING;
5095 if (IS_PLAYER(x, y))
5096 DrawPlayerField(x, y);
5098 DrawLevelField(x, y);
5100 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5102 for (i = 1; i <= 3; i++)
5104 int xx = x + i * dx;
5105 int yy = y + i * dy;
5106 int sx = SCREENX(xx);
5107 int sy = SCREENY(yy);
5108 int flame_graphic = graphic + (i - 1);
5110 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5115 int flamed = MovingOrBlocked2Element(xx, yy);
5117 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5120 RemoveMovingField(xx, yy);
5122 Feld[xx][yy] = EL_FLAMES;
5123 if (IN_SCR_FIELD(sx, sy))
5125 DrawLevelFieldCrumbledSand(xx, yy);
5126 DrawGraphic(sx, sy, flame_graphic, frame);
5131 if (Feld[xx][yy] == EL_FLAMES)
5132 Feld[xx][yy] = EL_EMPTY;
5133 DrawLevelField(xx, yy);
5138 if (MovDelay[x][y]) /* element still has to wait some time */
5140 PlayLevelSoundAction(x, y, ACTION_WAITING);
5146 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5147 for all other elements GfxAction will be set by InitMovingField() */
5148 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5149 GfxAction[x][y] = ACTION_MOVING;
5153 /* now make next step */
5155 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5157 if (DONT_COLLIDE_WITH(element) &&
5158 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5159 !PLAYER_ENEMY_PROTECTED(newx, newy))
5162 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5166 /* player killed by element which is deadly when colliding with */
5168 KillHero(PLAYERINFO(newx, newy));
5175 else if (CAN_MOVE_INTO_ACID(element) &&
5176 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5177 (MovDir[x][y] == MV_DOWN ||
5178 game.engine_version > VERSION_IDENT(3,0,8,0)))
5180 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5181 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5185 else if ((element == EL_PENGUIN ||
5186 element == EL_ROBOT ||
5187 element == EL_SATELLITE ||
5188 element == EL_BALLOON ||
5189 IS_CUSTOM_ELEMENT(element)) &&
5190 IN_LEV_FIELD(newx, newy) &&
5191 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5194 SplashAcid(newx, newy);
5195 Store[x][y] = EL_ACID;
5197 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5199 if (Feld[newx][newy] == EL_EXIT_OPEN)
5203 DrawLevelField(x, y);
5205 Feld[x][y] = EL_EMPTY;
5206 DrawLevelField(x, y);
5209 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5210 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5211 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5213 local_player->friends_still_needed--;
5214 if (!local_player->friends_still_needed &&
5215 !local_player->GameOver && AllPlayersGone)
5216 local_player->LevelSolved = local_player->GameOver = TRUE;
5220 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5222 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5223 DrawLevelField(newx, newy);
5225 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5227 else if (!IS_FREE(newx, newy))
5229 GfxAction[x][y] = ACTION_WAITING;
5231 if (IS_PLAYER(x, y))
5232 DrawPlayerField(x, y);
5234 DrawLevelField(x, y);
5239 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5241 if (IS_FOOD_PIG(Feld[newx][newy]))
5243 if (IS_MOVING(newx, newy))
5244 RemoveMovingField(newx, newy);
5247 Feld[newx][newy] = EL_EMPTY;
5248 DrawLevelField(newx, newy);
5251 PlayLevelSound(x, y, SND_PIG_DIGGING);
5253 else if (!IS_FREE(newx, newy))
5255 if (IS_PLAYER(x, y))
5256 DrawPlayerField(x, y);
5258 DrawLevelField(x, y);
5267 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5270 else if (IS_CUSTOM_ELEMENT(element) &&
5271 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5275 !IS_FREE(newx, newy)
5280 int new_element = Feld[newx][newy];
5283 printf("::: '%s' digs '%s' [%d]\n",
5284 element_info[element].token_name,
5285 element_info[Feld[newx][newy]].token_name,
5286 StorePlayer[newx][newy]);
5289 if (!IS_FREE(newx, newy))
5291 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5292 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5295 /* no element can dig solid indestructible elements */
5296 if (IS_INDESTRUCTIBLE(new_element) &&
5297 !IS_DIGGABLE(new_element) &&
5298 !IS_COLLECTIBLE(new_element))
5301 if (AmoebaNr[newx][newy] &&
5302 (new_element == EL_AMOEBA_FULL ||
5303 new_element == EL_BD_AMOEBA ||
5304 new_element == EL_AMOEBA_GROWING))
5306 AmoebaCnt[AmoebaNr[newx][newy]]--;
5307 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5310 if (IS_MOVING(newx, newy))
5311 RemoveMovingField(newx, newy);
5314 RemoveField(newx, newy);
5315 DrawLevelField(newx, newy);
5318 PlayLevelSoundAction(x, y, action);
5321 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5322 element_info[element].can_leave_element = TRUE;
5324 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5326 RunnerVisit[x][y] = FrameCounter;
5327 PlayerVisit[x][y] /= 8; /* expire player visit path */
5333 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5335 if (!IS_FREE(newx, newy))
5337 if (IS_PLAYER(x, y))
5338 DrawPlayerField(x, y);
5340 DrawLevelField(x, y);
5346 boolean wanna_flame = !RND(10);
5347 int dx = newx - x, dy = newy - y;
5348 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5349 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5350 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5351 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5352 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5353 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5356 IS_CLASSIC_ENEMY(element1) ||
5357 IS_CLASSIC_ENEMY(element2)) &&
5358 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5359 element1 != EL_FLAMES && element2 != EL_FLAMES)
5362 ResetGfxAnimation(x, y);
5363 GfxAction[x][y] = ACTION_ATTACKING;
5366 if (IS_PLAYER(x, y))
5367 DrawPlayerField(x, y);
5369 DrawLevelField(x, y);
5371 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5373 MovDelay[x][y] = 50;
5375 Feld[newx][newy] = EL_FLAMES;
5376 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5377 Feld[newx1][newy1] = EL_FLAMES;
5378 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5379 Feld[newx2][newy2] = EL_FLAMES;
5385 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5386 Feld[newx][newy] == EL_DIAMOND)
5388 if (IS_MOVING(newx, newy))
5389 RemoveMovingField(newx, newy);
5392 Feld[newx][newy] = EL_EMPTY;
5393 DrawLevelField(newx, newy);
5396 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5398 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5399 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5401 if (AmoebaNr[newx][newy])
5403 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5404 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5405 Feld[newx][newy] == EL_BD_AMOEBA)
5406 AmoebaCnt[AmoebaNr[newx][newy]]--;
5411 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5413 if (IS_MOVING(newx, newy))
5416 RemoveMovingField(newx, newy);
5420 Feld[newx][newy] = EL_EMPTY;
5421 DrawLevelField(newx, newy);
5424 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5426 else if ((element == EL_PACMAN || element == EL_MOLE)
5427 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5429 if (AmoebaNr[newx][newy])
5431 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5432 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5433 Feld[newx][newy] == EL_BD_AMOEBA)
5434 AmoebaCnt[AmoebaNr[newx][newy]]--;
5437 if (element == EL_MOLE)
5439 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5440 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5442 ResetGfxAnimation(x, y);
5443 GfxAction[x][y] = ACTION_DIGGING;
5444 DrawLevelField(x, y);
5446 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5448 return; /* wait for shrinking amoeba */
5450 else /* element == EL_PACMAN */
5452 Feld[newx][newy] = EL_EMPTY;
5453 DrawLevelField(newx, newy);
5454 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5457 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5458 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5459 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5461 /* wait for shrinking amoeba to completely disappear */
5464 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5466 /* object was running against a wall */
5471 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5472 DrawLevelElementAnimation(x, y, element);
5474 if (element == EL_BUG ||
5475 element == EL_SPACESHIP ||
5476 element == EL_SP_SNIKSNAK)
5477 DrawLevelField(x, y);
5478 else if (element == EL_MOLE)
5479 DrawLevelField(x, y);
5480 else if (element == EL_BD_BUTTERFLY ||
5481 element == EL_BD_FIREFLY)
5482 DrawLevelElementAnimationIfNeeded(x, y, element);
5483 else if (element == EL_SATELLITE)
5484 DrawLevelElementAnimationIfNeeded(x, y, element);
5485 else if (element == EL_SP_ELECTRON)
5486 DrawLevelElementAnimationIfNeeded(x, y, element);
5489 if (DONT_TOUCH(element))
5490 TestIfBadThingTouchesHero(x, y);
5493 PlayLevelSoundAction(x, y, ACTION_WAITING);
5499 InitMovingField(x, y, MovDir[x][y]);
5501 PlayLevelSoundAction(x, y, ACTION_MOVING);
5505 ContinueMoving(x, y);
5508 void ContinueMoving(int x, int y)
5510 int element = Feld[x][y];
5511 struct ElementInfo *ei = &element_info[element];
5512 int direction = MovDir[x][y];
5513 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5514 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5515 int newx = x + dx, newy = y + dy;
5517 int nextx = newx + dx, nexty = newy + dy;
5520 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5521 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5523 boolean pushed_by_player = Pushed[x][y];
5526 MovPos[x][y] += getElementMoveStepsize(x, y);
5529 if (pushed_by_player && IS_PLAYER(x, y))
5531 /* special case: moving object pushed by player */
5532 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5535 if (pushed_by_player) /* special case: moving object pushed by player */
5536 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5539 if (ABS(MovPos[x][y]) < TILEX)
5541 DrawLevelField(x, y);
5543 return; /* element is still moving */
5546 /* element reached destination field */
5548 Feld[x][y] = EL_EMPTY;
5549 Feld[newx][newy] = element;
5550 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5552 if (element == EL_MOLE)
5554 Feld[x][y] = EL_SAND;
5556 DrawLevelFieldCrumbledSandNeighbours(x, y);
5558 else if (element == EL_QUICKSAND_FILLING)
5560 element = Feld[newx][newy] = get_next_element(element);
5561 Store[newx][newy] = Store[x][y];
5563 else if (element == EL_QUICKSAND_EMPTYING)
5565 Feld[x][y] = get_next_element(element);
5566 element = Feld[newx][newy] = Store[x][y];
5568 else if (element == EL_MAGIC_WALL_FILLING)
5570 element = Feld[newx][newy] = get_next_element(element);
5571 if (!game.magic_wall_active)
5572 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5573 Store[newx][newy] = Store[x][y];
5575 else if (element == EL_MAGIC_WALL_EMPTYING)
5577 Feld[x][y] = get_next_element(element);
5578 if (!game.magic_wall_active)
5579 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5580 element = Feld[newx][newy] = Store[x][y];
5582 else if (element == EL_BD_MAGIC_WALL_FILLING)
5584 element = Feld[newx][newy] = get_next_element(element);
5585 if (!game.magic_wall_active)
5586 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5587 Store[newx][newy] = Store[x][y];
5589 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5591 Feld[x][y] = get_next_element(element);
5592 if (!game.magic_wall_active)
5593 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5594 element = Feld[newx][newy] = Store[x][y];
5596 else if (element == EL_AMOEBA_DROPPING)
5598 Feld[x][y] = get_next_element(element);
5599 element = Feld[newx][newy] = Store[x][y];
5601 else if (element == EL_SOKOBAN_OBJECT)
5604 Feld[x][y] = Back[x][y];
5606 if (Back[newx][newy])
5607 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5609 Back[x][y] = Back[newx][newy] = 0;
5611 else if (Store[x][y] == EL_ACID)
5613 element = Feld[newx][newy] = EL_ACID;
5617 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5618 MovDelay[newx][newy] = 0;
5620 /* copy element change control values to new field */
5621 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5622 ChangePage[newx][newy] = ChangePage[x][y];
5623 Changed[newx][newy] = Changed[x][y];
5624 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5626 ChangeDelay[x][y] = 0;
5627 ChangePage[x][y] = -1;
5628 Changed[x][y] = CE_BITMASK_DEFAULT;
5629 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5631 /* copy animation control values to new field */
5632 GfxFrame[newx][newy] = GfxFrame[x][y];
5633 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5634 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5635 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5637 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5639 ResetGfxAnimation(x, y); /* reset animation values for old field */
5642 /* some elements can leave other elements behind after moving */
5643 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5644 ei->move_leave_element != EL_EMPTY &&
5645 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5646 ei->can_leave_element_last))
5648 Feld[x][y] = ei->move_leave_element;
5649 InitField(x, y, FALSE);
5651 if (GFX_CRUMBLED(Feld[x][y]))
5652 DrawLevelFieldCrumbledSandNeighbours(x, y);
5655 ei->can_leave_element_last = ei->can_leave_element;
5656 ei->can_leave_element = FALSE;
5660 /* 2.1.1 (does not work correctly for spring) */
5661 if (!CAN_MOVE(element))
5662 MovDir[newx][newy] = 0;
5666 /* (does not work for falling objects that slide horizontally) */
5667 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5668 MovDir[newx][newy] = 0;
5671 if (!CAN_MOVE(element) ||
5672 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5673 MovDir[newx][newy] = 0;
5676 if (!CAN_MOVE(element) ||
5677 (CAN_FALL(element) && direction == MV_DOWN))
5678 GfxDir[x][y] = MovDir[newx][newy] = 0;
5683 DrawLevelField(x, y);
5684 DrawLevelField(newx, newy);
5686 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5688 /* prevent pushed element from moving on in pushed direction */
5689 if (pushed_by_player && CAN_MOVE(element) &&
5690 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5691 !(element_info[element].move_pattern & direction))
5692 TurnRound(newx, newy);
5695 /* prevent elements on conveyor belt from moving on in last direction */
5696 if (pushed_by_conveyor && CAN_FALL(element) &&
5697 direction & MV_HORIZONTAL)
5698 MovDir[newx][newy] = 0;
5701 if (!pushed_by_player)
5703 WasJustMoving[newx][newy] = 3;
5705 if (CAN_FALL(element) && direction == MV_DOWN)
5706 WasJustFalling[newx][newy] = 3;
5709 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5711 TestIfBadThingTouchesHero(newx, newy);
5712 TestIfBadThingTouchesFriend(newx, newy);
5714 if (!IS_CUSTOM_ELEMENT(element))
5715 TestIfBadThingTouchesOtherBadThing(newx, newy);
5717 else if (element == EL_PENGUIN)
5718 TestIfFriendTouchesBadThing(newx, newy);
5720 if (CAN_FALL(element) && direction == MV_DOWN &&
5721 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5725 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5729 if (ChangePage[newx][newy] != -1) /* delayed change */
5730 ChangeElement(newx, newy, ChangePage[newx][newy]);
5735 TestIfElementHitsCustomElement(newx, newy, direction);
5739 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5741 int hitting_element = Feld[newx][newy];
5743 /* !!! fix side (direction) orientation here and elsewhere !!! */
5744 CheckElementChangeSide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
5748 if (IN_LEV_FIELD(nextx, nexty))
5750 int opposite_direction = MV_DIR_OPPOSITE(direction);
5751 int hitting_side = direction;
5752 int touched_side = opposite_direction;
5753 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5754 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5755 MovDir[nextx][nexty] != direction ||
5756 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5762 CheckElementChangeSide(nextx, nexty, touched_element,
5763 CE_HIT_BY_SOMETHING, opposite_direction);
5765 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5766 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5768 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5770 struct ElementChangeInfo *change =
5771 &element_info[hitting_element].change_page[i];
5773 if (change->can_change &&
5774 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5775 change->trigger_side & touched_side &&
5776 change->trigger_element == touched_element)
5778 CheckElementChangePage(newx, newy, hitting_element,
5779 CE_OTHER_IS_HITTING, i);
5785 if (IS_CUSTOM_ELEMENT(touched_element) &&
5786 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5788 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5790 struct ElementChangeInfo *change =
5791 &element_info[touched_element].change_page[i];
5793 if (change->can_change &&
5794 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5795 change->trigger_side & hitting_side &&
5796 change->trigger_element == hitting_element)
5798 CheckElementChangePage(nextx, nexty, touched_element,
5799 CE_OTHER_GETS_HIT, i);
5810 TestIfPlayerTouchesCustomElement(newx, newy);
5811 TestIfElementTouchesCustomElement(newx, newy);
5814 int AmoebeNachbarNr(int ax, int ay)
5817 int element = Feld[ax][ay];
5819 static int xy[4][2] =
5827 for (i = 0; i < NUM_DIRECTIONS; i++)
5829 int x = ax + xy[i][0];
5830 int y = ay + xy[i][1];
5832 if (!IN_LEV_FIELD(x, y))
5835 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5836 group_nr = AmoebaNr[x][y];
5842 void AmoebenVereinigen(int ax, int ay)
5844 int i, x, y, xx, yy;
5845 int new_group_nr = AmoebaNr[ax][ay];
5846 static int xy[4][2] =
5854 if (new_group_nr == 0)
5857 for (i = 0; i < NUM_DIRECTIONS; i++)
5862 if (!IN_LEV_FIELD(x, y))
5865 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5866 Feld[x][y] == EL_BD_AMOEBA ||
5867 Feld[x][y] == EL_AMOEBA_DEAD) &&
5868 AmoebaNr[x][y] != new_group_nr)
5870 int old_group_nr = AmoebaNr[x][y];
5872 if (old_group_nr == 0)
5875 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5876 AmoebaCnt[old_group_nr] = 0;
5877 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5878 AmoebaCnt2[old_group_nr] = 0;
5880 for (yy = 0; yy < lev_fieldy; yy++)
5882 for (xx = 0; xx < lev_fieldx; xx++)
5884 if (AmoebaNr[xx][yy] == old_group_nr)
5885 AmoebaNr[xx][yy] = new_group_nr;
5892 void AmoebeUmwandeln(int ax, int ay)
5896 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5898 int group_nr = AmoebaNr[ax][ay];
5903 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5904 printf("AmoebeUmwandeln(): This should never happen!\n");
5909 for (y = 0; y < lev_fieldy; y++)
5911 for (x = 0; x < lev_fieldx; x++)
5913 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5916 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5920 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5921 SND_AMOEBA_TURNING_TO_GEM :
5922 SND_AMOEBA_TURNING_TO_ROCK));
5927 static int xy[4][2] =
5935 for (i = 0; i < NUM_DIRECTIONS; i++)
5940 if (!IN_LEV_FIELD(x, y))
5943 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5945 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5946 SND_AMOEBA_TURNING_TO_GEM :
5947 SND_AMOEBA_TURNING_TO_ROCK));
5954 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5957 int group_nr = AmoebaNr[ax][ay];
5958 boolean done = FALSE;
5963 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5964 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5969 for (y = 0; y < lev_fieldy; y++)
5971 for (x = 0; x < lev_fieldx; x++)
5973 if (AmoebaNr[x][y] == group_nr &&
5974 (Feld[x][y] == EL_AMOEBA_DEAD ||
5975 Feld[x][y] == EL_BD_AMOEBA ||
5976 Feld[x][y] == EL_AMOEBA_GROWING))
5979 Feld[x][y] = new_element;
5980 InitField(x, y, FALSE);
5981 DrawLevelField(x, y);
5988 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5989 SND_BD_AMOEBA_TURNING_TO_ROCK :
5990 SND_BD_AMOEBA_TURNING_TO_GEM));
5993 void AmoebeWaechst(int x, int y)
5995 static unsigned long sound_delay = 0;
5996 static unsigned long sound_delay_value = 0;
5998 if (!MovDelay[x][y]) /* start new growing cycle */
6002 if (DelayReached(&sound_delay, sound_delay_value))
6005 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6007 if (Store[x][y] == EL_BD_AMOEBA)
6008 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6010 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6012 sound_delay_value = 30;
6016 if (MovDelay[x][y]) /* wait some time before growing bigger */
6019 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6021 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6022 6 - MovDelay[x][y]);
6024 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6027 if (!MovDelay[x][y])
6029 Feld[x][y] = Store[x][y];
6031 DrawLevelField(x, y);
6036 void AmoebaDisappearing(int x, int y)
6038 static unsigned long sound_delay = 0;
6039 static unsigned long sound_delay_value = 0;
6041 if (!MovDelay[x][y]) /* start new shrinking cycle */
6045 if (DelayReached(&sound_delay, sound_delay_value))
6046 sound_delay_value = 30;
6049 if (MovDelay[x][y]) /* wait some time before shrinking */
6052 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6054 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6055 6 - MovDelay[x][y]);
6057 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6060 if (!MovDelay[x][y])
6062 Feld[x][y] = EL_EMPTY;
6063 DrawLevelField(x, y);
6065 /* don't let mole enter this field in this cycle;
6066 (give priority to objects falling to this field from above) */
6072 void AmoebeAbleger(int ax, int ay)
6075 int element = Feld[ax][ay];
6076 int graphic = el2img(element);
6077 int newax = ax, neway = ay;
6078 static int xy[4][2] =
6086 if (!level.amoeba_speed)
6088 Feld[ax][ay] = EL_AMOEBA_DEAD;
6089 DrawLevelField(ax, ay);
6093 if (IS_ANIMATED(graphic))
6094 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6096 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6097 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6099 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6102 if (MovDelay[ax][ay])
6106 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6109 int x = ax + xy[start][0];
6110 int y = ay + xy[start][1];
6112 if (!IN_LEV_FIELD(x, y))
6115 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6116 if (IS_FREE(x, y) ||
6117 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6123 if (newax == ax && neway == ay)
6126 else /* normal or "filled" (BD style) amoeba */
6129 boolean waiting_for_player = FALSE;
6131 for (i = 0; i < NUM_DIRECTIONS; i++)
6133 int j = (start + i) % 4;
6134 int x = ax + xy[j][0];
6135 int y = ay + xy[j][1];
6137 if (!IN_LEV_FIELD(x, y))
6140 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6141 if (IS_FREE(x, y) ||
6142 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6148 else if (IS_PLAYER(x, y))
6149 waiting_for_player = TRUE;
6152 if (newax == ax && neway == ay) /* amoeba cannot grow */
6154 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6156 Feld[ax][ay] = EL_AMOEBA_DEAD;
6157 DrawLevelField(ax, ay);
6158 AmoebaCnt[AmoebaNr[ax][ay]]--;
6160 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6162 if (element == EL_AMOEBA_FULL)
6163 AmoebeUmwandeln(ax, ay);
6164 else if (element == EL_BD_AMOEBA)
6165 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6170 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6172 /* amoeba gets larger by growing in some direction */
6174 int new_group_nr = AmoebaNr[ax][ay];
6177 if (new_group_nr == 0)
6179 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6180 printf("AmoebeAbleger(): This should never happen!\n");
6185 AmoebaNr[newax][neway] = new_group_nr;
6186 AmoebaCnt[new_group_nr]++;
6187 AmoebaCnt2[new_group_nr]++;
6189 /* if amoeba touches other amoeba(s) after growing, unify them */
6190 AmoebenVereinigen(newax, neway);
6192 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6194 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6200 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6201 (neway == lev_fieldy - 1 && newax != ax))
6203 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6204 Store[newax][neway] = element;
6206 else if (neway == ay)
6208 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6210 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6212 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6217 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6218 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6219 Store[ax][ay] = EL_AMOEBA_DROP;
6220 ContinueMoving(ax, ay);
6224 DrawLevelField(newax, neway);
6227 void Life(int ax, int ay)
6230 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6232 int element = Feld[ax][ay];
6233 int graphic = el2img(element);
6234 boolean changed = FALSE;
6236 if (IS_ANIMATED(graphic))
6237 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6242 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6243 MovDelay[ax][ay] = life_time;
6245 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6248 if (MovDelay[ax][ay])
6252 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6254 int xx = ax+x1, yy = ay+y1;
6257 if (!IN_LEV_FIELD(xx, yy))
6260 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6262 int x = xx+x2, y = yy+y2;
6264 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6267 if (((Feld[x][y] == element ||
6268 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6270 (IS_FREE(x, y) && Stop[x][y]))
6274 if (xx == ax && yy == ay) /* field in the middle */
6276 if (nachbarn < life[0] || nachbarn > life[1])
6278 Feld[xx][yy] = EL_EMPTY;
6280 DrawLevelField(xx, yy);
6281 Stop[xx][yy] = TRUE;
6285 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6286 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6287 { /* free border field */
6288 if (nachbarn >= life[2] && nachbarn <= life[3])
6290 Feld[xx][yy] = element;
6291 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6293 DrawLevelField(xx, yy);
6294 Stop[xx][yy] = TRUE;
6301 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6302 SND_GAME_OF_LIFE_GROWING);
6305 static void InitRobotWheel(int x, int y)
6307 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6310 static void RunRobotWheel(int x, int y)
6312 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6315 static void StopRobotWheel(int x, int y)
6317 if (ZX == x && ZY == y)
6321 static void InitTimegateWheel(int x, int y)
6323 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6326 static void RunTimegateWheel(int x, int y)
6328 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6331 void CheckExit(int x, int y)
6333 if (local_player->gems_still_needed > 0 ||
6334 local_player->sokobanfields_still_needed > 0 ||
6335 local_player->lights_still_needed > 0)
6337 int element = Feld[x][y];
6338 int graphic = el2img(element);
6340 if (IS_ANIMATED(graphic))
6341 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6346 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6349 Feld[x][y] = EL_EXIT_OPENING;
6351 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6354 void CheckExitSP(int x, int y)
6356 if (local_player->gems_still_needed > 0)
6358 int element = Feld[x][y];
6359 int graphic = el2img(element);
6361 if (IS_ANIMATED(graphic))
6362 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6367 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6370 Feld[x][y] = EL_SP_EXIT_OPENING;
6372 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6375 static void CloseAllOpenTimegates()
6379 for (y = 0; y < lev_fieldy; y++)
6381 for (x = 0; x < lev_fieldx; x++)
6383 int element = Feld[x][y];
6385 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6387 Feld[x][y] = EL_TIMEGATE_CLOSING;
6389 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6391 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6398 void EdelsteinFunkeln(int x, int y)
6400 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6403 if (Feld[x][y] == EL_BD_DIAMOND)
6406 if (MovDelay[x][y] == 0) /* next animation frame */
6407 MovDelay[x][y] = 11 * !SimpleRND(500);
6409 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6413 if (setup.direct_draw && MovDelay[x][y])
6414 SetDrawtoField(DRAW_BUFFERED);
6416 DrawLevelElementAnimation(x, y, Feld[x][y]);
6418 if (MovDelay[x][y] != 0)
6420 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6421 10 - MovDelay[x][y]);
6423 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6425 if (setup.direct_draw)
6429 dest_x = FX + SCREENX(x) * TILEX;
6430 dest_y = FY + SCREENY(y) * TILEY;
6432 BlitBitmap(drawto_field, window,
6433 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6434 SetDrawtoField(DRAW_DIRECT);
6440 void MauerWaechst(int x, int y)
6444 if (!MovDelay[x][y]) /* next animation frame */
6445 MovDelay[x][y] = 3 * delay;
6447 if (MovDelay[x][y]) /* wait some time before next frame */
6451 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6453 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6454 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6456 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6459 if (!MovDelay[x][y])
6461 if (MovDir[x][y] == MV_LEFT)
6463 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6464 DrawLevelField(x - 1, y);
6466 else if (MovDir[x][y] == MV_RIGHT)
6468 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6469 DrawLevelField(x + 1, y);
6471 else if (MovDir[x][y] == MV_UP)
6473 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6474 DrawLevelField(x, y - 1);
6478 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6479 DrawLevelField(x, y + 1);
6482 Feld[x][y] = Store[x][y];
6484 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6485 DrawLevelField(x, y);
6490 void MauerAbleger(int ax, int ay)
6492 int element = Feld[ax][ay];
6493 int graphic = el2img(element);
6494 boolean oben_frei = FALSE, unten_frei = FALSE;
6495 boolean links_frei = FALSE, rechts_frei = FALSE;
6496 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6497 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6498 boolean new_wall = FALSE;
6500 if (IS_ANIMATED(graphic))
6501 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6503 if (!MovDelay[ax][ay]) /* start building new wall */
6504 MovDelay[ax][ay] = 6;
6506 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6509 if (MovDelay[ax][ay])
6513 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6515 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6517 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6519 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6522 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6523 element == EL_EXPANDABLE_WALL_ANY)
6527 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6528 Store[ax][ay-1] = element;
6529 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6530 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6531 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6532 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6537 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6538 Store[ax][ay+1] = element;
6539 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6540 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6541 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6542 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6547 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6548 element == EL_EXPANDABLE_WALL_ANY ||
6549 element == EL_EXPANDABLE_WALL)
6553 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6554 Store[ax-1][ay] = element;
6555 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6556 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6557 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6558 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6564 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6565 Store[ax+1][ay] = element;
6566 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6567 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6568 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6569 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6574 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6575 DrawLevelField(ax, ay);
6577 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6579 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6580 unten_massiv = TRUE;
6581 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6582 links_massiv = TRUE;
6583 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6584 rechts_massiv = TRUE;
6586 if (((oben_massiv && unten_massiv) ||
6587 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6588 element == EL_EXPANDABLE_WALL) &&
6589 ((links_massiv && rechts_massiv) ||
6590 element == EL_EXPANDABLE_WALL_VERTICAL))
6591 Feld[ax][ay] = EL_WALL;
6595 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6597 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6601 void CheckForDragon(int x, int y)
6604 boolean dragon_found = FALSE;
6605 static int xy[4][2] =
6613 for (i = 0; i < NUM_DIRECTIONS; i++)
6615 for (j = 0; j < 4; j++)
6617 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6619 if (IN_LEV_FIELD(xx, yy) &&
6620 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6622 if (Feld[xx][yy] == EL_DRAGON)
6623 dragon_found = TRUE;
6632 for (i = 0; i < NUM_DIRECTIONS; i++)
6634 for (j = 0; j < 3; j++)
6636 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6638 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6640 Feld[xx][yy] = EL_EMPTY;
6641 DrawLevelField(xx, yy);
6650 static void InitBuggyBase(int x, int y)
6652 int element = Feld[x][y];
6653 int activating_delay = FRAMES_PER_SECOND / 4;
6656 (element == EL_SP_BUGGY_BASE ?
6657 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6658 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6660 element == EL_SP_BUGGY_BASE_ACTIVE ?
6661 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6664 static void WarnBuggyBase(int x, int y)
6667 static int xy[4][2] =
6675 for (i = 0; i < NUM_DIRECTIONS; i++)
6677 int xx = x + xy[i][0], yy = y + xy[i][1];
6679 if (IS_PLAYER(xx, yy))
6681 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6688 static void InitTrap(int x, int y)
6690 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6693 static void ActivateTrap(int x, int y)
6695 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6698 static void ChangeActiveTrap(int x, int y)
6700 int graphic = IMG_TRAP_ACTIVE;
6702 /* if new animation frame was drawn, correct crumbled sand border */
6703 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6704 DrawLevelFieldCrumbledSand(x, y);
6707 static void ChangeElementNowExt(int x, int y, int target_element)
6709 int previous_move_direction = MovDir[x][y];
6711 /* check if element under player changes from accessible to unaccessible
6712 (needed for special case of dropping element which then changes) */
6713 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6714 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6721 Feld[x][y] = target_element;
6723 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6725 ResetGfxAnimation(x, y);
6726 ResetRandomAnimationValue(x, y);
6728 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6729 MovDir[x][y] = previous_move_direction;
6732 InitField_WithBug1(x, y, FALSE);
6734 InitField(x, y, FALSE);
6735 if (CAN_MOVE(Feld[x][y]))
6739 DrawLevelField(x, y);
6741 if (GFX_CRUMBLED(Feld[x][y]))
6742 DrawLevelFieldCrumbledSandNeighbours(x, y);
6744 TestIfBadThingTouchesHero(x, y);
6745 TestIfPlayerTouchesCustomElement(x, y);
6746 TestIfElementTouchesCustomElement(x, y);
6748 if (ELEM_IS_PLAYER(target_element))
6749 RelocatePlayer(x, y, target_element);
6752 static boolean ChangeElementNow(int x, int y, int element, int page)
6754 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6756 /* always use default change event to prevent running into a loop */
6757 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6758 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6760 /* do not change already changed elements with same change event */
6762 if (Changed[x][y] & ChangeEvent[x][y])
6769 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6771 CheckTriggeredElementChangePage(x,y, Feld[x][y], CE_OTHER_IS_CHANGING, page);
6773 if (change->explode)
6780 if (change->use_content)
6782 boolean complete_change = TRUE;
6783 boolean can_change[3][3];
6786 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6788 boolean half_destructible;
6789 int ex = x + xx - 1;
6790 int ey = y + yy - 1;
6793 can_change[xx][yy] = TRUE;
6795 if (ex == x && ey == y) /* do not check changing element itself */
6798 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6800 can_change[xx][yy] = FALSE; /* do not change empty borders */
6805 if (!IN_LEV_FIELD(ex, ey))
6807 can_change[xx][yy] = FALSE;
6808 complete_change = FALSE;
6815 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6816 e = MovingOrBlocked2Element(ex, ey);
6818 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6820 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6821 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6822 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6824 can_change[xx][yy] = FALSE;
6825 complete_change = FALSE;
6829 if (!change->only_complete || complete_change)
6831 boolean something_has_changed = FALSE;
6833 if (change->only_complete && change->use_random_change &&
6834 RND(100) < change->random)
6837 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6839 int ex = x + xx - 1;
6840 int ey = y + yy - 1;
6842 if (can_change[xx][yy] && (!change->use_random_change ||
6843 RND(100) < change->random))
6845 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6846 RemoveMovingField(ex, ey);
6848 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6850 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6852 something_has_changed = TRUE;
6854 /* for symmetry reasons, freeze newly created border elements */
6855 if (ex != x || ey != y)
6856 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6860 if (something_has_changed)
6861 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6866 ChangeElementNowExt(x, y, change->target_element);
6868 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6874 static void ChangeElement(int x, int y, int page)
6876 int element = MovingOrBlocked2Element(x, y);
6877 struct ElementInfo *ei = &element_info[element];
6878 struct ElementChangeInfo *change = &ei->change_page[page];
6882 if (!CAN_CHANGE(element))
6885 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6886 x, y, element, element_info[element].token_name);
6887 printf("ChangeElement(): This should never happen!\n");
6893 if (ChangeDelay[x][y] == 0) /* initialize element change */
6895 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6896 RND(change->delay_random * change->delay_frames)) + 1;
6898 ResetGfxAnimation(x, y);
6899 ResetRandomAnimationValue(x, y);
6901 if (change->pre_change_function)
6902 change->pre_change_function(x, y);
6905 ChangeDelay[x][y]--;
6907 if (ChangeDelay[x][y] != 0) /* continue element change */
6909 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6911 if (IS_ANIMATED(graphic))
6912 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6914 if (change->change_function)
6915 change->change_function(x, y);
6917 else /* finish element change */
6919 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6921 page = ChangePage[x][y];
6922 ChangePage[x][y] = -1;
6926 if (IS_MOVING(x, y) && !change->explode)
6928 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6931 ChangeDelay[x][y] = 1; /* try change after next move step */
6932 ChangePage[x][y] = page; /* remember page to use for change */
6937 if (ChangeElementNow(x, y, element, page))
6939 if (change->post_change_function)
6940 change->post_change_function(x, y);
6945 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
6946 int trigger_element,
6954 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6957 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6959 int element = EL_CUSTOM_START + i;
6961 boolean change_element = FALSE;
6964 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6967 for (j = 0; j < element_info[element].num_change_pages; j++)
6969 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6971 if (change->can_change &&
6972 change->events & CH_EVENT_BIT(trigger_event) &&
6973 change->trigger_side & trigger_side &&
6974 change->trigger_player & trigger_player &&
6975 change->trigger_page & (1 << trigger_page) &&
6976 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
6979 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6980 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6981 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6984 change_element = TRUE;
6991 if (!change_element)
6994 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6997 if (x == lx && y == ly) /* do not change trigger element itself */
7001 if (Feld[x][y] == element)
7003 ChangeDelay[x][y] = 1;
7004 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7005 ChangeElement(x, y, page);
7013 static boolean CheckElementChangeExt(int x, int y,
7020 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7023 if (Feld[x][y] == EL_BLOCKED)
7025 Blocked2Moving(x, y, &x, &y);
7026 element = Feld[x][y];
7030 if (trigger_page < 0)
7032 boolean change_element = FALSE;
7035 for (i = 0; i < element_info[element].num_change_pages; i++)
7037 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7039 if (change->can_change &&
7040 change->events & CH_EVENT_BIT(trigger_event) &&
7041 change->trigger_side & trigger_side &&
7042 change->trigger_player & trigger_player)
7044 change_element = TRUE;
7051 if (!change_element)
7057 /* !!! this check misses pages with same event, but different side !!! */
7059 if (trigger_page < 0)
7060 trigger_page = element_info[element].event_page_nr[trigger_event];
7062 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7066 ChangeDelay[x][y] = 1;
7067 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7068 ChangeElement(x, y, trigger_page);
7073 static void PlayPlayerSound(struct PlayerInfo *player)
7075 int jx = player->jx, jy = player->jy;
7076 int element = player->element_nr;
7077 int last_action = player->last_action_waiting;
7078 int action = player->action_waiting;
7080 if (player->is_waiting)
7082 if (action != last_action)
7083 PlayLevelSoundElementAction(jx, jy, element, action);
7085 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7089 if (action != last_action)
7090 StopSound(element_info[element].sound[last_action]);
7092 if (last_action == ACTION_SLEEPING)
7093 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7097 static void PlayAllPlayersSound()
7101 for (i = 0; i < MAX_PLAYERS; i++)
7102 if (stored_player[i].active)
7103 PlayPlayerSound(&stored_player[i]);
7106 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7108 boolean last_waiting = player->is_waiting;
7109 int move_dir = player->MovDir;
7111 player->last_action_waiting = player->action_waiting;
7115 if (!last_waiting) /* not waiting -> waiting */
7117 player->is_waiting = TRUE;
7119 player->frame_counter_bored =
7121 game.player_boring_delay_fixed +
7122 SimpleRND(game.player_boring_delay_random);
7123 player->frame_counter_sleeping =
7125 game.player_sleeping_delay_fixed +
7126 SimpleRND(game.player_sleeping_delay_random);
7128 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7131 if (game.player_sleeping_delay_fixed +
7132 game.player_sleeping_delay_random > 0 &&
7133 player->anim_delay_counter == 0 &&
7134 player->post_delay_counter == 0 &&
7135 FrameCounter >= player->frame_counter_sleeping)
7136 player->is_sleeping = TRUE;
7137 else if (game.player_boring_delay_fixed +
7138 game.player_boring_delay_random > 0 &&
7139 FrameCounter >= player->frame_counter_bored)
7140 player->is_bored = TRUE;
7142 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7143 player->is_bored ? ACTION_BORING :
7146 if (player->is_sleeping)
7148 if (player->num_special_action_sleeping > 0)
7150 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7152 int last_special_action = player->special_action_sleeping;
7153 int num_special_action = player->num_special_action_sleeping;
7154 int special_action =
7155 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7156 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7157 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7158 last_special_action + 1 : ACTION_SLEEPING);
7159 int special_graphic =
7160 el_act_dir2img(player->element_nr, special_action, move_dir);
7162 player->anim_delay_counter =
7163 graphic_info[special_graphic].anim_delay_fixed +
7164 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7165 player->post_delay_counter =
7166 graphic_info[special_graphic].post_delay_fixed +
7167 SimpleRND(graphic_info[special_graphic].post_delay_random);
7169 player->special_action_sleeping = special_action;
7172 if (player->anim_delay_counter > 0)
7174 player->action_waiting = player->special_action_sleeping;
7175 player->anim_delay_counter--;
7177 else if (player->post_delay_counter > 0)
7179 player->post_delay_counter--;
7183 else if (player->is_bored)
7185 if (player->num_special_action_bored > 0)
7187 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7189 int special_action =
7190 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7191 int special_graphic =
7192 el_act_dir2img(player->element_nr, special_action, move_dir);
7194 player->anim_delay_counter =
7195 graphic_info[special_graphic].anim_delay_fixed +
7196 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7197 player->post_delay_counter =
7198 graphic_info[special_graphic].post_delay_fixed +
7199 SimpleRND(graphic_info[special_graphic].post_delay_random);
7201 player->special_action_bored = special_action;
7204 if (player->anim_delay_counter > 0)
7206 player->action_waiting = player->special_action_bored;
7207 player->anim_delay_counter--;
7209 else if (player->post_delay_counter > 0)
7211 player->post_delay_counter--;
7216 else if (last_waiting) /* waiting -> not waiting */
7218 player->is_waiting = FALSE;
7219 player->is_bored = FALSE;
7220 player->is_sleeping = FALSE;
7222 player->frame_counter_bored = -1;
7223 player->frame_counter_sleeping = -1;
7225 player->anim_delay_counter = 0;
7226 player->post_delay_counter = 0;
7228 player->action_waiting = ACTION_DEFAULT;
7230 player->special_action_bored = ACTION_DEFAULT;
7231 player->special_action_sleeping = ACTION_DEFAULT;
7236 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7239 static byte stored_player_action[MAX_PLAYERS];
7240 static int num_stored_actions = 0;
7242 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7243 int left = player_action & JOY_LEFT;
7244 int right = player_action & JOY_RIGHT;
7245 int up = player_action & JOY_UP;
7246 int down = player_action & JOY_DOWN;
7247 int button1 = player_action & JOY_BUTTON_1;
7248 int button2 = player_action & JOY_BUTTON_2;
7249 int dx = (left ? -1 : right ? 1 : 0);
7250 int dy = (up ? -1 : down ? 1 : 0);
7253 stored_player_action[player->index_nr] = 0;
7254 num_stored_actions++;
7258 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7261 if (!player->active || tape.pausing)
7265 printf("::: [%d %d %d %d] [%d %d]\n",
7266 left, right, up, down, button1, button2);
7272 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7277 if (player->MovPos == 0)
7278 CheckGravityMovement(player);
7281 snapped = SnapField(player, dx, dy);
7285 dropped = DropElement(player);
7287 moved = MovePlayer(player, dx, dy);
7290 if (tape.single_step && tape.recording && !tape.pausing)
7292 if (button1 || (dropped && !moved))
7294 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7295 SnapField(player, 0, 0); /* stop snapping */
7299 SetPlayerWaiting(player, FALSE);
7302 return player_action;
7304 stored_player_action[player->index_nr] = player_action;
7310 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7313 /* no actions for this player (no input at player's configured device) */
7315 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7316 SnapField(player, 0, 0);
7317 CheckGravityMovementWhenNotMoving(player);
7319 if (player->MovPos == 0)
7320 SetPlayerWaiting(player, TRUE);
7322 if (player->MovPos == 0) /* needed for tape.playing */
7323 player->is_moving = FALSE;
7325 player->is_dropping = FALSE;
7331 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7333 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7335 TapeRecordAction(stored_player_action);
7336 num_stored_actions = 0;
7343 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7345 static byte stored_player_action[MAX_PLAYERS];
7346 static int num_stored_actions = 0;
7347 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7348 int left = player_action & JOY_LEFT;
7349 int right = player_action & JOY_RIGHT;
7350 int up = player_action & JOY_UP;
7351 int down = player_action & JOY_DOWN;
7352 int button1 = player_action & JOY_BUTTON_1;
7353 int button2 = player_action & JOY_BUTTON_2;
7354 int dx = (left ? -1 : right ? 1 : 0);
7355 int dy = (up ? -1 : down ? 1 : 0);
7357 stored_player_action[player->index_nr] = 0;
7358 num_stored_actions++;
7360 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7362 if (!player->active || tape.pausing)
7367 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7370 snapped = SnapField(player, dx, dy);
7374 dropped = DropElement(player);
7376 moved = MovePlayer(player, dx, dy);
7379 if (tape.single_step && tape.recording && !tape.pausing)
7381 if (button1 || (dropped && !moved))
7383 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7384 SnapField(player, 0, 0); /* stop snapping */
7388 stored_player_action[player->index_nr] = player_action;
7392 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7394 /* no actions for this player (no input at player's configured device) */
7396 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7397 SnapField(player, 0, 0);
7398 CheckGravityMovementWhenNotMoving(player);
7400 if (player->MovPos == 0)
7401 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7403 if (player->MovPos == 0) /* needed for tape.playing */
7404 player->is_moving = FALSE;
7407 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7409 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7411 TapeRecordAction(stored_player_action);
7412 num_stored_actions = 0;
7419 static unsigned long action_delay = 0;
7420 unsigned long action_delay_value;
7421 int magic_wall_x = 0, magic_wall_y = 0;
7422 int i, x, y, element, graphic;
7423 byte *recorded_player_action;
7424 byte summarized_player_action = 0;
7426 byte tape_action[MAX_PLAYERS];
7429 if (game_status != GAME_MODE_PLAYING)
7432 action_delay_value =
7433 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7435 if (tape.playing && tape.index_search && !tape.pausing)
7436 action_delay_value = 0;
7438 /* ---------- main game synchronization point ---------- */
7440 WaitUntilDelayReached(&action_delay, action_delay_value);
7442 if (network_playing && !network_player_action_received)
7446 printf("DEBUG: try to get network player actions in time\n");
7450 #if defined(PLATFORM_UNIX)
7451 /* last chance to get network player actions without main loop delay */
7455 if (game_status != GAME_MODE_PLAYING)
7458 if (!network_player_action_received)
7462 printf("DEBUG: failed to get network player actions in time\n");
7473 printf("::: getting new tape action [%d]\n", FrameCounter);
7476 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7479 if (recorded_player_action != NULL)
7480 for (i = 0; i < MAX_PLAYERS; i++)
7481 stored_player[i].action = recorded_player_action[i];
7484 for (i = 0; i < MAX_PLAYERS; i++)
7486 summarized_player_action |= stored_player[i].action;
7488 if (!network_playing)
7489 stored_player[i].effective_action = stored_player[i].action;
7492 #if defined(PLATFORM_UNIX)
7493 if (network_playing)
7494 SendToServer_MovePlayer(summarized_player_action);
7497 if (!options.network && !setup.team_mode)
7498 local_player->effective_action = summarized_player_action;
7501 for (i = 0; i < MAX_PLAYERS; i++)
7503 tape_action[i] = stored_player[i].effective_action;
7505 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7506 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7509 /* only save actions from input devices, but not programmed actions */
7511 TapeRecordAction(tape_action);
7514 for (i = 0; i < MAX_PLAYERS; i++)
7516 int actual_player_action = stored_player[i].effective_action;
7519 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
7520 - rnd_equinox_tetrachloride 048
7521 - rnd_equinox_tetrachloride_ii 096
7522 - rnd_emanuel_schmieg 002
7524 if (stored_player[i].MovPos == 0)
7525 CheckGravityMovement(&stored_player[i]);
7529 /* overwrite programmed action with tape action */
7530 if (stored_player[i].programmed_action)
7531 actual_player_action = stored_player[i].programmed_action;
7534 if (recorded_player_action)
7537 if (stored_player[i].programmed_action &&
7538 stored_player[i].programmed_action != recorded_player_action[i])
7539 printf("::: %d: %d <-> %d\n", i,
7540 stored_player[i].programmed_action, recorded_player_action[i]);
7544 actual_player_action = recorded_player_action[i];
7549 /* overwrite tape action with programmed action */
7550 if (stored_player[i].programmed_action)
7551 actual_player_action = stored_player[i].programmed_action;
7556 printf("::: action: %d: %x [%d]\n",
7557 stored_player[i].MovPos, actual_player_action, FrameCounter);
7561 PlayerActions(&stored_player[i], actual_player_action);
7563 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7565 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7566 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7569 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7574 TapeRecordAction(tape_action);
7577 network_player_action_received = FALSE;
7579 ScrollScreen(NULL, SCROLL_GO_ON);
7585 for (i = 0; i < MAX_PLAYERS; i++)
7586 stored_player[i].Frame++;
7590 /* for downwards compatibility, the following code emulates a fixed bug that
7591 occured when pushing elements (causing elements that just made their last
7592 pushing step to already (if possible) make their first falling step in the
7593 same game frame, which is bad); this code is also needed to use the famous
7594 "spring push bug" which is used in older levels and might be wanted to be
7595 used also in newer levels, but in this case the buggy pushing code is only
7596 affecting the "spring" element and no other elements */
7599 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7601 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7604 for (i = 0; i < MAX_PLAYERS; i++)
7606 struct PlayerInfo *player = &stored_player[i];
7611 if (player->active && player->is_pushing && player->is_moving &&
7613 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7614 Feld[x][y] == EL_SPRING))
7616 if (player->active && player->is_pushing && player->is_moving &&
7620 ContinueMoving(x, y);
7622 /* continue moving after pushing (this is actually a bug) */
7623 if (!IS_MOVING(x, y))
7632 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7634 Changed[x][y] = CE_BITMASK_DEFAULT;
7635 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7638 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7640 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7641 printf("GameActions(): This should never happen!\n");
7643 ChangePage[x][y] = -1;
7648 if (WasJustMoving[x][y] > 0)
7649 WasJustMoving[x][y]--;
7650 if (WasJustFalling[x][y] > 0)
7651 WasJustFalling[x][y]--;
7656 /* reset finished pushing action (not done in ContinueMoving() to allow
7657 continous pushing animation for elements with zero push delay) */
7658 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7660 ResetGfxAnimation(x, y);
7661 DrawLevelField(x, y);
7666 if (IS_BLOCKED(x, y))
7670 Blocked2Moving(x, y, &oldx, &oldy);
7671 if (!IS_MOVING(oldx, oldy))
7673 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7674 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7675 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7676 printf("GameActions(): This should never happen!\n");
7682 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7684 element = Feld[x][y];
7686 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7688 graphic = el2img(element);
7694 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7696 element = graphic = 0;
7700 if (graphic_info[graphic].anim_global_sync)
7701 GfxFrame[x][y] = FrameCounter;
7703 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7704 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7705 ResetRandomAnimationValue(x, y);
7707 SetRandomAnimationValue(x, y);
7710 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7713 if (IS_INACTIVE(element))
7715 if (IS_ANIMATED(graphic))
7716 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7722 /* this may take place after moving, so 'element' may have changed */
7724 if (IS_CHANGING(x, y))
7726 if (IS_CHANGING(x, y) &&
7727 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7731 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7732 element_info[element].event_page_nr[CE_DELAY]);
7734 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7737 element = Feld[x][y];
7738 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7742 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7747 element = Feld[x][y];
7748 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7750 if (element == EL_MOLE)
7751 printf("::: %d, %d, %d [%d]\n",
7752 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7756 if (element == EL_YAMYAM)
7757 printf("::: %d, %d, %d\n",
7758 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7762 if (IS_ANIMATED(graphic) &&
7766 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7769 if (element == EL_BUG)
7770 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7774 if (element == EL_MOLE)
7775 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7779 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7780 EdelsteinFunkeln(x, y);
7782 else if ((element == EL_ACID ||
7783 element == EL_EXIT_OPEN ||
7784 element == EL_SP_EXIT_OPEN ||
7785 element == EL_SP_TERMINAL ||
7786 element == EL_SP_TERMINAL_ACTIVE ||
7787 element == EL_EXTRA_TIME ||
7788 element == EL_SHIELD_NORMAL ||
7789 element == EL_SHIELD_DEADLY) &&
7790 IS_ANIMATED(graphic))
7791 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7792 else if (IS_MOVING(x, y))
7793 ContinueMoving(x, y);
7794 else if (IS_ACTIVE_BOMB(element))
7795 CheckDynamite(x, y);
7797 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7798 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7800 else if (element == EL_AMOEBA_GROWING)
7801 AmoebeWaechst(x, y);
7802 else if (element == EL_AMOEBA_SHRINKING)
7803 AmoebaDisappearing(x, y);
7805 #if !USE_NEW_AMOEBA_CODE
7806 else if (IS_AMOEBALIVE(element))
7807 AmoebeAbleger(x, y);
7810 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7812 else if (element == EL_EXIT_CLOSED)
7814 else if (element == EL_SP_EXIT_CLOSED)
7816 else if (element == EL_EXPANDABLE_WALL_GROWING)
7818 else if (element == EL_EXPANDABLE_WALL ||
7819 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7820 element == EL_EXPANDABLE_WALL_VERTICAL ||
7821 element == EL_EXPANDABLE_WALL_ANY)
7823 else if (element == EL_FLAMES)
7824 CheckForDragon(x, y);
7826 else if (IS_AUTO_CHANGING(element))
7827 ChangeElement(x, y);
7829 else if (element == EL_EXPLOSION)
7830 ; /* drawing of correct explosion animation is handled separately */
7831 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7832 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7835 /* this may take place after moving, so 'element' may have changed */
7836 if (IS_AUTO_CHANGING(Feld[x][y]))
7837 ChangeElement(x, y);
7840 if (IS_BELT_ACTIVE(element))
7841 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7843 if (game.magic_wall_active)
7845 int jx = local_player->jx, jy = local_player->jy;
7847 /* play the element sound at the position nearest to the player */
7848 if ((element == EL_MAGIC_WALL_FULL ||
7849 element == EL_MAGIC_WALL_ACTIVE ||
7850 element == EL_MAGIC_WALL_EMPTYING ||
7851 element == EL_BD_MAGIC_WALL_FULL ||
7852 element == EL_BD_MAGIC_WALL_ACTIVE ||
7853 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7854 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7862 #if USE_NEW_AMOEBA_CODE
7863 /* new experimental amoeba growth stuff */
7865 if (!(FrameCounter % 8))
7868 static unsigned long random = 1684108901;
7870 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7873 x = (random >> 10) % lev_fieldx;
7874 y = (random >> 20) % lev_fieldy;
7876 x = RND(lev_fieldx);
7877 y = RND(lev_fieldy);
7879 element = Feld[x][y];
7881 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7882 if (!IS_PLAYER(x,y) &&
7883 (element == EL_EMPTY ||
7884 element == EL_SAND ||
7885 element == EL_QUICKSAND_EMPTY ||
7886 element == EL_ACID_SPLASH_LEFT ||
7887 element == EL_ACID_SPLASH_RIGHT))
7889 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7890 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7891 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7892 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7893 Feld[x][y] = EL_AMOEBA_DROP;
7896 random = random * 129 + 1;
7902 if (game.explosions_delayed)
7905 game.explosions_delayed = FALSE;
7907 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7909 element = Feld[x][y];
7911 if (ExplodeField[x][y])
7912 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7913 else if (element == EL_EXPLOSION)
7914 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7916 ExplodeField[x][y] = EX_NO_EXPLOSION;
7919 game.explosions_delayed = TRUE;
7922 if (game.magic_wall_active)
7924 if (!(game.magic_wall_time_left % 4))
7926 int element = Feld[magic_wall_x][magic_wall_y];
7928 if (element == EL_BD_MAGIC_WALL_FULL ||
7929 element == EL_BD_MAGIC_WALL_ACTIVE ||
7930 element == EL_BD_MAGIC_WALL_EMPTYING)
7931 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7933 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7936 if (game.magic_wall_time_left > 0)
7938 game.magic_wall_time_left--;
7939 if (!game.magic_wall_time_left)
7941 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7943 element = Feld[x][y];
7945 if (element == EL_MAGIC_WALL_ACTIVE ||
7946 element == EL_MAGIC_WALL_FULL)
7948 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7949 DrawLevelField(x, y);
7951 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7952 element == EL_BD_MAGIC_WALL_FULL)
7954 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7955 DrawLevelField(x, y);
7959 game.magic_wall_active = FALSE;
7964 if (game.light_time_left > 0)
7966 game.light_time_left--;
7968 if (game.light_time_left == 0)
7969 RedrawAllLightSwitchesAndInvisibleElements();
7972 if (game.timegate_time_left > 0)
7974 game.timegate_time_left--;
7976 if (game.timegate_time_left == 0)
7977 CloseAllOpenTimegates();
7980 for (i = 0; i < MAX_PLAYERS; i++)
7982 struct PlayerInfo *player = &stored_player[i];
7984 if (SHIELD_ON(player))
7986 if (player->shield_deadly_time_left)
7987 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7988 else if (player->shield_normal_time_left)
7989 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7993 if (TimeFrames >= FRAMES_PER_SECOND)
7998 if (!level.use_step_counter)
8002 for (i = 0; i < MAX_PLAYERS; i++)
8004 struct PlayerInfo *player = &stored_player[i];
8006 if (SHIELD_ON(player))
8008 player->shield_normal_time_left--;
8010 if (player->shield_deadly_time_left > 0)
8011 player->shield_deadly_time_left--;
8019 if (TimeLeft <= 10 && setup.time_limit)
8020 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8022 DrawGameValue_Time(TimeLeft);
8024 if (!TimeLeft && setup.time_limit)
8025 for (i = 0; i < MAX_PLAYERS; i++)
8026 KillHero(&stored_player[i]);
8028 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8029 DrawGameValue_Time(TimePlayed);
8032 if (tape.recording || tape.playing)
8033 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8037 PlayAllPlayersSound();
8039 if (options.debug) /* calculate frames per second */
8041 static unsigned long fps_counter = 0;
8042 static int fps_frames = 0;
8043 unsigned long fps_delay_ms = Counter() - fps_counter;
8047 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8049 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8052 fps_counter = Counter();
8055 redraw_mask |= REDRAW_FPS;
8059 if (stored_player[0].jx != stored_player[0].last_jx ||
8060 stored_player[0].jy != stored_player[0].last_jy)
8061 printf("::: %d, %d, %d, %d, %d\n",
8062 stored_player[0].MovDir,
8063 stored_player[0].MovPos,
8064 stored_player[0].GfxPos,
8065 stored_player[0].Frame,
8066 stored_player[0].StepFrame);
8073 for (i = 0; i < MAX_PLAYERS; i++)
8076 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8078 stored_player[i].Frame += move_frames;
8080 if (stored_player[i].MovPos != 0)
8081 stored_player[i].StepFrame += move_frames;
8083 if (stored_player[i].drop_delay > 0)
8084 stored_player[i].drop_delay--;
8089 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8091 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8093 local_player->show_envelope = 0;
8098 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8100 int min_x = x, min_y = y, max_x = x, max_y = y;
8103 for (i = 0; i < MAX_PLAYERS; i++)
8105 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8107 if (!stored_player[i].active || &stored_player[i] == player)
8110 min_x = MIN(min_x, jx);
8111 min_y = MIN(min_y, jy);
8112 max_x = MAX(max_x, jx);
8113 max_y = MAX(max_y, jy);
8116 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8119 static boolean AllPlayersInVisibleScreen()
8123 for (i = 0; i < MAX_PLAYERS; i++)
8125 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8127 if (!stored_player[i].active)
8130 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8137 void ScrollLevel(int dx, int dy)
8139 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8142 BlitBitmap(drawto_field, drawto_field,
8143 FX + TILEX * (dx == -1) - softscroll_offset,
8144 FY + TILEY * (dy == -1) - softscroll_offset,
8145 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8146 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8147 FX + TILEX * (dx == 1) - softscroll_offset,
8148 FY + TILEY * (dy == 1) - softscroll_offset);
8152 x = (dx == 1 ? BX1 : BX2);
8153 for (y = BY1; y <= BY2; y++)
8154 DrawScreenField(x, y);
8159 y = (dy == 1 ? BY1 : BY2);
8160 for (x = BX1; x <= BX2; x++)
8161 DrawScreenField(x, y);
8164 redraw_mask |= REDRAW_FIELD;
8167 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8169 int nextx = x + dx, nexty = y + dy;
8170 int element = Feld[x][y];
8173 element != EL_SP_PORT_LEFT &&
8174 element != EL_SP_GRAVITY_PORT_LEFT &&
8175 element != EL_SP_PORT_HORIZONTAL &&
8176 element != EL_SP_PORT_ANY) ||
8178 element != EL_SP_PORT_RIGHT &&
8179 element != EL_SP_GRAVITY_PORT_RIGHT &&
8180 element != EL_SP_PORT_HORIZONTAL &&
8181 element != EL_SP_PORT_ANY) ||
8183 element != EL_SP_PORT_UP &&
8184 element != EL_SP_GRAVITY_PORT_UP &&
8185 element != EL_SP_PORT_VERTICAL &&
8186 element != EL_SP_PORT_ANY) ||
8188 element != EL_SP_PORT_DOWN &&
8189 element != EL_SP_GRAVITY_PORT_DOWN &&
8190 element != EL_SP_PORT_VERTICAL &&
8191 element != EL_SP_PORT_ANY) ||
8192 !IN_LEV_FIELD(nextx, nexty) ||
8193 !IS_FREE(nextx, nexty))
8199 static void CheckGravityMovement(struct PlayerInfo *player)
8201 if (game.gravity && !player->programmed_action)
8203 int move_dir_horizontal = player->action & MV_HORIZONTAL;
8204 int move_dir_vertical = player->action & MV_VERTICAL;
8206 (player->last_move_dir & MV_HORIZONTAL ?
8207 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8208 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8209 int jx = player->jx, jy = player->jy;
8210 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8211 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8212 int new_jx = jx + dx, new_jy = jy + dy;
8213 boolean player_is_snapping = player->action & JOY_BUTTON_1;
8215 boolean player_can_fall_down =
8216 (IN_LEV_FIELD(jx, jy + 1) &&
8217 (IS_FREE(jx, jy + 1) ||
8218 (Feld[jx][jy + 1] == EL_ACID && level.player_can_fall_into_acid)));
8220 boolean player_can_fall_down =
8221 (IN_LEV_FIELD(jx, jy + 1) &&
8222 (IS_FREE(jx, jy + 1)));
8224 boolean player_is_moving_to_valid_field =
8227 !player_is_snapping &&
8229 IN_LEV_FIELD(new_jx, new_jy) &&
8230 (Feld[new_jx][new_jy] == EL_SP_BASE ||
8231 Feld[new_jx][new_jy] == EL_SAND ||
8232 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8233 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
8234 /* !!! extend EL_SAND to anything diggable !!! */
8236 boolean player_is_standing_on_valid_field =
8237 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8238 (IS_WALKABLE(Feld[jx][jy]) &&
8239 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8242 printf("::: checking gravity NOW [%d, %d, %d] [%d] ...\n",
8243 player_can_fall_down,
8244 player_is_standing_on_valid_field,
8245 player_is_moving_to_valid_field,
8246 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1));
8249 if (player_can_fall_down &&
8250 !player_is_standing_on_valid_field &&
8251 !player_is_moving_to_valid_field)
8254 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
8255 jx, jy, FrameCounter);
8258 player->programmed_action = MV_DOWN;
8263 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8266 return CheckGravityMovement(player);
8269 if (game.gravity && !player->programmed_action)
8271 int jx = player->jx, jy = player->jy;
8272 boolean field_under_player_is_free =
8273 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8274 boolean player_is_standing_on_valid_field =
8275 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8276 (IS_WALKABLE(Feld[jx][jy]) &&
8277 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8279 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8280 player->programmed_action = MV_DOWN;
8286 -----------------------------------------------------------------------------
8287 dx, dy: direction (non-diagonal) to try to move the player to
8288 real_dx, real_dy: direction as read from input device (can be diagonal)
8291 boolean MovePlayerOneStep(struct PlayerInfo *player,
8292 int dx, int dy, int real_dx, int real_dy)
8295 static int trigger_sides[4][2] =
8297 /* enter side leave side */
8298 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8299 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8300 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8301 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8303 int move_direction = (dx == -1 ? MV_LEFT :
8304 dx == +1 ? MV_RIGHT :
8306 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8307 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8308 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8310 int jx = player->jx, jy = player->jy;
8311 int new_jx = jx + dx, new_jy = jy + dy;
8315 if (!player->active || (!dx && !dy))
8316 return MF_NO_ACTION;
8318 player->MovDir = (dx < 0 ? MV_LEFT :
8321 dy > 0 ? MV_DOWN : MV_NO_MOVING);
8323 if (!IN_LEV_FIELD(new_jx, new_jy))
8324 return MF_NO_ACTION;
8326 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8327 return MF_NO_ACTION;
8330 element = MovingOrBlocked2Element(new_jx, new_jy);
8332 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8335 if (DONT_RUN_INTO(element))
8337 if (element == EL_ACID && dx == 0 && dy == 1)
8339 SplashAcid(new_jx, new_jy);
8340 Feld[jx][jy] = EL_PLAYER_1;
8341 InitMovingField(jx, jy, MV_DOWN);
8342 Store[jx][jy] = EL_ACID;
8343 ContinueMoving(jx, jy);
8347 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8352 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8353 if (can_move != MF_MOVING)
8356 /* check if DigField() has caused relocation of the player */
8357 if (player->jx != jx || player->jy != jy)
8358 return MF_NO_ACTION;
8360 StorePlayer[jx][jy] = 0;
8361 player->last_jx = jx;
8362 player->last_jy = jy;
8363 player->jx = new_jx;
8364 player->jy = new_jy;
8365 StorePlayer[new_jx][new_jy] = player->element_nr;
8368 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8370 player->step_counter++;
8372 player->drop_delay = 0;
8374 PlayerVisit[jx][jy] = FrameCounter;
8376 ScrollPlayer(player, SCROLL_INIT);
8379 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8381 CheckTriggeredElementChangeSide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
8383 CheckElementChangeSide(jx, jy, Feld[jx][jy], CE_LEFT_BY_PLAYER,leave_side);
8386 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
8388 CheckTriggeredElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8389 CE_OTHER_GETS_ENTERED, enter_side);
8390 CheckElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8391 CE_ENTERED_BY_PLAYER, enter_side);
8398 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8400 int jx = player->jx, jy = player->jy;
8401 int old_jx = jx, old_jy = jy;
8402 int moved = MF_NO_ACTION;
8405 if (!player->active)
8410 if (player->MovPos == 0)
8412 player->is_moving = FALSE;
8413 player->is_digging = FALSE;
8414 player->is_collecting = FALSE;
8415 player->is_snapping = FALSE;
8416 player->is_pushing = FALSE;
8422 if (!player->active || (!dx && !dy))
8427 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8433 if (!FrameReached(&player->move_delay, player->move_delay_value))
8436 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8437 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
8443 /* remove the last programmed player action */
8444 player->programmed_action = 0;
8448 /* should only happen if pre-1.2 tape recordings are played */
8449 /* this is only for backward compatibility */
8451 int original_move_delay_value = player->move_delay_value;
8454 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8458 /* scroll remaining steps with finest movement resolution */
8459 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8461 while (player->MovPos)
8463 ScrollPlayer(player, SCROLL_GO_ON);
8464 ScrollScreen(NULL, SCROLL_GO_ON);
8470 player->move_delay_value = original_move_delay_value;
8473 if (player->last_move_dir & MV_HORIZONTAL)
8475 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8476 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8480 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8481 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8487 if (moved & MF_MOVING && !ScreenMovPos &&
8488 (player == local_player || !options.network))
8490 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8491 int offset = (setup.scroll_delay ? 3 : 0);
8493 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8495 /* actual player has left the screen -- scroll in that direction */
8496 if (jx != old_jx) /* player has moved horizontally */
8497 scroll_x += (jx - old_jx);
8498 else /* player has moved vertically */
8499 scroll_y += (jy - old_jy);
8503 if (jx != old_jx) /* player has moved horizontally */
8505 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8506 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8507 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8509 /* don't scroll over playfield boundaries */
8510 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8511 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8513 /* don't scroll more than one field at a time */
8514 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8516 /* don't scroll against the player's moving direction */
8517 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8518 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8519 scroll_x = old_scroll_x;
8521 else /* player has moved vertically */
8523 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8524 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8525 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8527 /* don't scroll over playfield boundaries */
8528 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8529 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8531 /* don't scroll more than one field at a time */
8532 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8534 /* don't scroll against the player's moving direction */
8535 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8536 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8537 scroll_y = old_scroll_y;
8541 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8543 if (!options.network && !AllPlayersInVisibleScreen())
8545 scroll_x = old_scroll_x;
8546 scroll_y = old_scroll_y;
8550 ScrollScreen(player, SCROLL_INIT);
8551 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8558 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8560 if (!(moved & MF_MOVING) && !player->is_pushing)
8565 player->StepFrame = 0;
8567 if (moved & MF_MOVING)
8569 if (old_jx != jx && old_jy == jy)
8570 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8571 else if (old_jx == jx && old_jy != jy)
8572 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8574 DrawLevelField(jx, jy); /* for "crumbled sand" */
8576 player->last_move_dir = player->MovDir;
8577 player->is_moving = TRUE;
8579 player->is_snapping = FALSE;
8583 player->is_switching = FALSE;
8586 player->is_dropping = FALSE;
8591 static int trigger_sides[4][2] =
8593 /* enter side leave side */
8594 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8595 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8596 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8597 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8599 int move_direction = player->MovDir;
8600 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8601 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8604 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8606 CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8608 player->index_bit, leave_side);
8609 CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8611 player->index_bit, leave_side);
8614 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8616 CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
8617 CE_OTHER_GETS_ENTERED,
8618 player->index_bit, enter_side);
8619 CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
8620 player->index_bit, enter_side);
8631 CheckGravityMovementWhenNotMoving(player);
8634 player->last_move_dir = MV_NO_MOVING;
8636 player->is_moving = FALSE;
8639 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8641 TestIfHeroTouchesBadThing(jx, jy);
8642 TestIfPlayerTouchesCustomElement(jx, jy);
8645 if (!player->active)
8651 void ScrollPlayer(struct PlayerInfo *player, int mode)
8653 int jx = player->jx, jy = player->jy;
8654 int last_jx = player->last_jx, last_jy = player->last_jy;
8655 int move_stepsize = TILEX / player->move_delay_value;
8657 if (!player->active || !player->MovPos)
8660 if (mode == SCROLL_INIT)
8662 player->actual_frame_counter = FrameCounter;
8663 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8665 if (Feld[last_jx][last_jy] == EL_EMPTY)
8666 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8674 else if (!FrameReached(&player->actual_frame_counter, 1))
8677 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8678 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8680 if (!player->block_last_field &&
8681 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8682 Feld[last_jx][last_jy] = EL_EMPTY;
8684 /* before DrawPlayer() to draw correct player graphic for this case */
8685 if (player->MovPos == 0)
8686 CheckGravityMovement(player);
8689 DrawPlayer(player); /* needed here only to cleanup last field */
8692 if (player->MovPos == 0) /* player reached destination field */
8695 if (player->move_delay_reset_counter > 0)
8697 player->move_delay_reset_counter--;
8699 if (player->move_delay_reset_counter == 0)
8701 /* continue with normal speed after quickly moving through gate */
8702 HALVE_PLAYER_SPEED(player);
8704 /* be able to make the next move without delay */
8705 player->move_delay = 0;
8709 if (IS_PASSABLE(Feld[last_jx][last_jy]))
8711 /* continue with normal speed after quickly moving through gate */
8712 HALVE_PLAYER_SPEED(player);
8714 /* be able to make the next move without delay */
8715 player->move_delay = 0;
8719 if (player->block_last_field &&
8720 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8721 Feld[last_jx][last_jy] = EL_EMPTY;
8723 player->last_jx = jx;
8724 player->last_jy = jy;
8726 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8727 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8728 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8730 DrawPlayer(player); /* needed here only to cleanup last field */
8733 if (local_player->friends_still_needed == 0 ||
8734 IS_SP_ELEMENT(Feld[jx][jy]))
8735 player->LevelSolved = player->GameOver = TRUE;
8739 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
8741 static int trigger_sides[4][2] =
8743 /* enter side leave side */
8744 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8745 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8746 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8747 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8749 int move_direction = player->MovDir;
8750 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8751 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8752 int old_jx = last_jx;
8753 int old_jy = last_jy;
8756 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8758 CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8760 player->index_bit, leave_side);
8761 CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8763 player->index_bit, leave_side);
8766 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8768 CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
8769 CE_OTHER_GETS_ENTERED,
8770 player->index_bit, enter_side);
8771 CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
8772 player->index_bit, enter_side);
8779 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8781 TestIfHeroTouchesBadThing(jx, jy);
8782 TestIfPlayerTouchesCustomElement(jx, jy);
8784 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8787 if (!player->active)
8791 if (level.use_step_counter)
8797 for (i = 0; i < MAX_PLAYERS; i++)
8799 struct PlayerInfo *player = &stored_player[i];
8801 if (SHIELD_ON(player))
8803 player->shield_normal_time_left--;
8805 if (player->shield_deadly_time_left > 0)
8806 player->shield_deadly_time_left--;
8814 if (TimeLeft <= 10 && setup.time_limit)
8815 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8817 DrawGameValue_Time(TimeLeft);
8819 if (!TimeLeft && setup.time_limit)
8820 for (i = 0; i < MAX_PLAYERS; i++)
8821 KillHero(&stored_player[i]);
8823 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8824 DrawGameValue_Time(TimePlayed);
8827 if (tape.single_step && tape.recording && !tape.pausing &&
8828 !player->programmed_action)
8829 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8833 void ScrollScreen(struct PlayerInfo *player, int mode)
8835 static unsigned long screen_frame_counter = 0;
8837 if (mode == SCROLL_INIT)
8839 /* set scrolling step size according to actual player's moving speed */
8840 ScrollStepSize = TILEX / player->move_delay_value;
8842 screen_frame_counter = FrameCounter;
8843 ScreenMovDir = player->MovDir;
8844 ScreenMovPos = player->MovPos;
8845 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8848 else if (!FrameReached(&screen_frame_counter, 1))
8853 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8854 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8855 redraw_mask |= REDRAW_FIELD;
8858 ScreenMovDir = MV_NO_MOVING;
8861 void TestIfPlayerTouchesCustomElement(int x, int y)
8863 static int xy[4][2] =
8870 static int trigger_sides[4][2] =
8872 /* center side border side */
8873 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8874 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8875 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8876 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8878 static int touch_dir[4] =
8885 int center_element = Feld[x][y]; /* should always be non-moving! */
8888 for (i = 0; i < NUM_DIRECTIONS; i++)
8890 int xx = x + xy[i][0];
8891 int yy = y + xy[i][1];
8892 int center_side = trigger_sides[i][0];
8893 int border_side = trigger_sides[i][1];
8896 if (!IN_LEV_FIELD(xx, yy))
8899 if (IS_PLAYER(x, y))
8901 struct PlayerInfo *player = PLAYERINFO(x, y);
8903 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8904 border_element = Feld[xx][yy]; /* may be moving! */
8905 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8906 border_element = Feld[xx][yy];
8907 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8908 border_element = MovingOrBlocked2Element(xx, yy);
8910 continue; /* center and border element do not touch */
8912 CheckTriggeredElementChangePlayer(xx, yy, border_element,
8913 CE_OTHER_GETS_TOUCHED,
8914 player->index_bit, border_side);
8915 CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
8916 player->index_bit, border_side);
8918 else if (IS_PLAYER(xx, yy))
8920 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8922 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8924 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8925 continue; /* center and border element do not touch */
8928 CheckTriggeredElementChangePlayer(x, y, center_element,
8929 CE_OTHER_GETS_TOUCHED,
8930 player->index_bit, center_side);
8931 CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
8932 player->index_bit, center_side);
8939 void TestIfElementTouchesCustomElement(int x, int y)
8941 static int xy[4][2] =
8948 static int trigger_sides[4][2] =
8950 /* center side border side */
8951 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8952 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8953 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8954 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8956 static int touch_dir[4] =
8963 boolean change_center_element = FALSE;
8964 int center_element_change_page = 0;
8965 int center_element = Feld[x][y]; /* should always be non-moving! */
8968 for (i = 0; i < NUM_DIRECTIONS; i++)
8970 int xx = x + xy[i][0];
8971 int yy = y + xy[i][1];
8972 int center_side = trigger_sides[i][0];
8973 int border_side = trigger_sides[i][1];
8976 if (!IN_LEV_FIELD(xx, yy))
8979 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8980 border_element = Feld[xx][yy]; /* may be moving! */
8981 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8982 border_element = Feld[xx][yy];
8983 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8984 border_element = MovingOrBlocked2Element(xx, yy);
8986 continue; /* center and border element do not touch */
8988 /* check for change of center element (but change it only once) */
8989 if (IS_CUSTOM_ELEMENT(center_element) &&
8990 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8991 !change_center_element)
8993 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8995 struct ElementChangeInfo *change =
8996 &element_info[center_element].change_page[j];
8998 if (change->can_change &&
8999 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9000 change->trigger_side & border_side &&
9002 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9004 change->trigger_element == border_element
9008 change_center_element = TRUE;
9009 center_element_change_page = j;
9016 /* check for change of border element */
9017 if (IS_CUSTOM_ELEMENT(border_element) &&
9018 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9020 for (j = 0; j < element_info[border_element].num_change_pages; j++)
9022 struct ElementChangeInfo *change =
9023 &element_info[border_element].change_page[j];
9025 if (change->can_change &&
9026 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9027 change->trigger_side & center_side &&
9029 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9031 change->trigger_element == center_element
9035 CheckElementChangePage(xx, yy, border_element, CE_OTHER_IS_TOUCHING,
9043 if (change_center_element)
9044 CheckElementChangePage(x, y, center_element, CE_OTHER_IS_TOUCHING,
9045 center_element_change_page);
9048 void TestIfElementHitsCustomElement(int x, int y, int direction)
9050 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9051 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9052 int hitx = x + dx, hity = y + dy;
9053 int hitting_element = Feld[x][y];
9055 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9056 !IS_FREE(hitx, hity) &&
9057 (!IS_MOVING(hitx, hity) ||
9058 MovDir[hitx][hity] != direction ||
9059 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9062 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9066 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9070 CheckElementChangeSide(x, y, hitting_element, CE_HITTING_SOMETHING,
9073 if (IN_LEV_FIELD(hitx, hity))
9075 int opposite_direction = MV_DIR_OPPOSITE(direction);
9076 int hitting_side = direction;
9077 int touched_side = opposite_direction;
9078 int touched_element = MovingOrBlocked2Element(hitx, hity);
9080 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9081 MovDir[hitx][hity] != direction ||
9082 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9091 CheckElementChangeSide(hitx, hity, touched_element, CE_HIT_BY_SOMETHING,
9092 opposite_direction);
9094 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9095 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
9097 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9099 struct ElementChangeInfo *change =
9100 &element_info[hitting_element].change_page[i];
9102 if (change->can_change &&
9103 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
9104 change->trigger_side & touched_side &&
9107 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9109 change->trigger_element == touched_element
9113 CheckElementChangePage(x, y, hitting_element, CE_OTHER_IS_HITTING,
9120 if (IS_CUSTOM_ELEMENT(touched_element) &&
9121 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
9123 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9125 struct ElementChangeInfo *change =
9126 &element_info[touched_element].change_page[i];
9128 if (change->can_change &&
9129 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
9130 change->trigger_side & hitting_side &&
9132 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9134 change->trigger_element == hitting_element
9138 CheckElementChangePage(hitx, hity, touched_element,
9139 CE_OTHER_GETS_HIT, i);
9149 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9151 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9152 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9153 int hitx = x + dx, hity = y + dy;
9154 int hitting_element = Feld[x][y];
9156 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9157 !IS_FREE(hitx, hity) &&
9158 (!IS_MOVING(hitx, hity) ||
9159 MovDir[hitx][hity] != direction ||
9160 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9163 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9167 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9171 CheckElementChangeSide(x, y, hitting_element, EP_CAN_SMASH_EVERYTHING,
9174 if (IN_LEV_FIELD(hitx, hity))
9176 int opposite_direction = MV_DIR_OPPOSITE(direction);
9177 int hitting_side = direction;
9178 int touched_side = opposite_direction;
9179 int touched_element = MovingOrBlocked2Element(hitx, hity);
9181 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9182 MovDir[hitx][hity] != direction ||
9183 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9192 CheckElementChangeSide(hitx, hity, touched_element,
9193 CE_SMASHED_BY_SOMETHING, opposite_direction);
9195 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9196 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
9198 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9200 struct ElementChangeInfo *change =
9201 &element_info[hitting_element].change_page[i];
9203 if (change->can_change &&
9204 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
9205 change->trigger_side & touched_side &&
9208 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9210 change->trigger_element == touched_element
9214 CheckElementChangePage(x, y, hitting_element, CE_OTHER_IS_SMASHING,
9221 if (IS_CUSTOM_ELEMENT(touched_element) &&
9222 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
9224 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9226 struct ElementChangeInfo *change =
9227 &element_info[touched_element].change_page[i];
9229 if (change->can_change &&
9230 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
9231 change->trigger_side & hitting_side &&
9233 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9235 change->trigger_element == hitting_element
9239 CheckElementChangePage(hitx, hity, touched_element,
9240 CE_OTHER_GETS_SMASHED, i);
9250 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9252 int i, kill_x = -1, kill_y = -1;
9253 static int test_xy[4][2] =
9260 static int test_dir[4] =
9268 for (i = 0; i < NUM_DIRECTIONS; i++)
9270 int test_x, test_y, test_move_dir, test_element;
9272 test_x = good_x + test_xy[i][0];
9273 test_y = good_y + test_xy[i][1];
9274 if (!IN_LEV_FIELD(test_x, test_y))
9278 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9281 test_element = Feld[test_x][test_y];
9283 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9286 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9287 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9289 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9290 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9298 if (kill_x != -1 || kill_y != -1)
9300 if (IS_PLAYER(good_x, good_y))
9302 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9304 if (player->shield_deadly_time_left > 0)
9305 Bang(kill_x, kill_y);
9306 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9310 Bang(good_x, good_y);
9314 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9316 int i, kill_x = -1, kill_y = -1;
9317 int bad_element = Feld[bad_x][bad_y];
9318 static int test_xy[4][2] =
9325 static int touch_dir[4] =
9332 static int test_dir[4] =
9340 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9343 for (i = 0; i < NUM_DIRECTIONS; i++)
9345 int test_x, test_y, test_move_dir, test_element;
9347 test_x = bad_x + test_xy[i][0];
9348 test_y = bad_y + test_xy[i][1];
9349 if (!IN_LEV_FIELD(test_x, test_y))
9353 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9355 test_element = Feld[test_x][test_y];
9357 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9358 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9360 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9361 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9363 /* good thing is player or penguin that does not move away */
9364 if (IS_PLAYER(test_x, test_y))
9366 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9368 if (bad_element == EL_ROBOT && player->is_moving)
9369 continue; /* robot does not kill player if he is moving */
9371 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9373 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9374 continue; /* center and border element do not touch */
9381 else if (test_element == EL_PENGUIN)
9390 if (kill_x != -1 || kill_y != -1)
9392 if (IS_PLAYER(kill_x, kill_y))
9394 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9396 if (player->shield_deadly_time_left > 0)
9398 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9402 Bang(kill_x, kill_y);
9406 void TestIfHeroTouchesBadThing(int x, int y)
9408 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9411 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
9413 TestIfGoodThingHitsBadThing(x, y, move_dir);
9416 void TestIfBadThingTouchesHero(int x, int y)
9418 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9421 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
9423 TestIfBadThingHitsGoodThing(x, y, move_dir);
9426 void TestIfFriendTouchesBadThing(int x, int y)
9428 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9431 void TestIfBadThingTouchesFriend(int x, int y)
9433 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9436 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9438 int i, kill_x = bad_x, kill_y = bad_y;
9439 static int xy[4][2] =
9447 for (i = 0; i < NUM_DIRECTIONS; i++)
9451 x = bad_x + xy[i][0];
9452 y = bad_y + xy[i][1];
9453 if (!IN_LEV_FIELD(x, y))
9456 element = Feld[x][y];
9457 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9458 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9466 if (kill_x != bad_x || kill_y != bad_y)
9470 void KillHero(struct PlayerInfo *player)
9472 int jx = player->jx, jy = player->jy;
9474 if (!player->active)
9477 /* remove accessible field at the player's position */
9478 Feld[jx][jy] = EL_EMPTY;
9480 /* deactivate shield (else Bang()/Explode() would not work right) */
9481 player->shield_normal_time_left = 0;
9482 player->shield_deadly_time_left = 0;
9488 static void KillHeroUnlessEnemyProtected(int x, int y)
9490 if (!PLAYER_ENEMY_PROTECTED(x, y))
9491 KillHero(PLAYERINFO(x, y));
9494 static void KillHeroUnlessExplosionProtected(int x, int y)
9496 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9497 KillHero(PLAYERINFO(x, y));
9500 void BuryHero(struct PlayerInfo *player)
9502 int jx = player->jx, jy = player->jy;
9504 if (!player->active)
9508 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9510 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
9512 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9514 player->GameOver = TRUE;
9518 void RemoveHero(struct PlayerInfo *player)
9520 int jx = player->jx, jy = player->jy;
9521 int i, found = FALSE;
9523 player->present = FALSE;
9524 player->active = FALSE;
9526 if (!ExplodeField[jx][jy])
9527 StorePlayer[jx][jy] = 0;
9529 for (i = 0; i < MAX_PLAYERS; i++)
9530 if (stored_player[i].active)
9534 AllPlayersGone = TRUE;
9541 =============================================================================
9542 checkDiagonalPushing()
9543 -----------------------------------------------------------------------------
9544 check if diagonal input device direction results in pushing of object
9545 (by checking if the alternative direction is walkable, diggable, ...)
9546 =============================================================================
9549 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9550 int x, int y, int real_dx, int real_dy)
9552 int jx, jy, dx, dy, xx, yy;
9554 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9557 /* diagonal direction: check alternative direction */
9562 xx = jx + (dx == 0 ? real_dx : 0);
9563 yy = jy + (dy == 0 ? real_dy : 0);
9565 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9569 =============================================================================
9571 -----------------------------------------------------------------------------
9572 x, y: field next to player (non-diagonal) to try to dig to
9573 real_dx, real_dy: direction as read from input device (can be diagonal)
9574 =============================================================================
9577 int DigField(struct PlayerInfo *player,
9578 int oldx, int oldy, int x, int y,
9579 int real_dx, int real_dy, int mode)
9581 static int trigger_sides[4] =
9583 CH_SIDE_RIGHT, /* moving left */
9584 CH_SIDE_LEFT, /* moving right */
9585 CH_SIDE_BOTTOM, /* moving up */
9586 CH_SIDE_TOP, /* moving down */
9589 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
9591 int jx = oldx, jy = oldy;
9592 int dx = x - jx, dy = y - jy;
9593 int nextx = x + dx, nexty = y + dy;
9594 int move_direction = (dx == -1 ? MV_LEFT :
9595 dx == +1 ? MV_RIGHT :
9597 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9598 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9599 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
9600 int old_element = Feld[jx][jy];
9603 if (player->MovPos == 0)
9605 player->is_digging = FALSE;
9606 player->is_collecting = FALSE;
9609 if (player->MovPos == 0) /* last pushing move finished */
9610 player->is_pushing = FALSE;
9612 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9614 player->is_switching = FALSE;
9615 player->push_delay = 0;
9617 return MF_NO_ACTION;
9620 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9621 return MF_NO_ACTION;
9626 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
9628 if (IS_TUBE(Feld[jx][jy]) ||
9629 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
9633 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
9634 int tube_leave_directions[][2] =
9636 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9637 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9638 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9639 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
9640 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
9641 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
9642 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
9643 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
9644 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
9645 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
9646 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
9647 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
9650 while (tube_leave_directions[i][0] != tube_element)
9653 if (tube_leave_directions[i][0] == -1) /* should not happen */
9657 if (!(tube_leave_directions[i][1] & move_direction))
9658 return MF_NO_ACTION; /* tube has no opening in this direction */
9663 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9664 old_element = Back[jx][jy];
9668 if (IS_WALKABLE(old_element) &&
9669 !(element_info[old_element].access_direction & move_direction))
9670 return MF_NO_ACTION; /* field has no opening in this direction */
9672 element = Feld[x][y];
9674 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9675 game.engine_version >= VERSION_IDENT(2,2,0,0))
9676 return MF_NO_ACTION;
9680 case EL_SP_PORT_LEFT:
9681 case EL_SP_PORT_RIGHT:
9683 case EL_SP_PORT_DOWN:
9684 case EL_SP_PORT_HORIZONTAL:
9685 case EL_SP_PORT_VERTICAL:
9686 case EL_SP_PORT_ANY:
9687 case EL_SP_GRAVITY_PORT_LEFT:
9688 case EL_SP_GRAVITY_PORT_RIGHT:
9689 case EL_SP_GRAVITY_PORT_UP:
9690 case EL_SP_GRAVITY_PORT_DOWN:
9692 if (!canEnterSupaplexPort(x, y, dx, dy))
9693 return MF_NO_ACTION;
9696 element != EL_SP_PORT_LEFT &&
9697 element != EL_SP_GRAVITY_PORT_LEFT &&
9698 element != EL_SP_PORT_HORIZONTAL &&
9699 element != EL_SP_PORT_ANY) ||
9701 element != EL_SP_PORT_RIGHT &&
9702 element != EL_SP_GRAVITY_PORT_RIGHT &&
9703 element != EL_SP_PORT_HORIZONTAL &&
9704 element != EL_SP_PORT_ANY) ||
9706 element != EL_SP_PORT_UP &&
9707 element != EL_SP_GRAVITY_PORT_UP &&
9708 element != EL_SP_PORT_VERTICAL &&
9709 element != EL_SP_PORT_ANY) ||
9711 element != EL_SP_PORT_DOWN &&
9712 element != EL_SP_GRAVITY_PORT_DOWN &&
9713 element != EL_SP_PORT_VERTICAL &&
9714 element != EL_SP_PORT_ANY) ||
9715 !IN_LEV_FIELD(nextx, nexty) ||
9716 !IS_FREE(nextx, nexty))
9717 return MF_NO_ACTION;
9720 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9721 element == EL_SP_GRAVITY_PORT_RIGHT ||
9722 element == EL_SP_GRAVITY_PORT_UP ||
9723 element == EL_SP_GRAVITY_PORT_DOWN)
9724 game.gravity = !game.gravity;
9726 /* automatically move to the next field with double speed */
9727 player->programmed_action = move_direction;
9729 if (player->move_delay_reset_counter == 0)
9731 player->move_delay_reset_counter = 2; /* two double speed steps */
9733 DOUBLE_PLAYER_SPEED(player);
9736 player->move_delay_reset_counter = 2;
9738 DOUBLE_PLAYER_SPEED(player);
9742 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
9745 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9750 case EL_TUBE_VERTICAL:
9751 case EL_TUBE_HORIZONTAL:
9752 case EL_TUBE_VERTICAL_LEFT:
9753 case EL_TUBE_VERTICAL_RIGHT:
9754 case EL_TUBE_HORIZONTAL_UP:
9755 case EL_TUBE_HORIZONTAL_DOWN:
9756 case EL_TUBE_LEFT_UP:
9757 case EL_TUBE_LEFT_DOWN:
9758 case EL_TUBE_RIGHT_UP:
9759 case EL_TUBE_RIGHT_DOWN:
9762 int tube_enter_directions[][2] =
9764 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9765 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9766 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9767 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
9768 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
9769 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
9770 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
9771 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
9772 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
9773 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
9774 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
9775 { -1, MV_NO_MOVING }
9778 while (tube_enter_directions[i][0] != element)
9781 if (tube_enter_directions[i][0] == -1) /* should not happen */
9785 if (!(tube_enter_directions[i][1] & move_direction))
9786 return MF_NO_ACTION; /* tube has no opening in this direction */
9788 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9795 if (IS_WALKABLE(element))
9797 int sound_action = ACTION_WALKING;
9799 if (!(element_info[element].access_direction & opposite_direction))
9800 return MF_NO_ACTION; /* field not accessible from this direction */
9802 if (element >= EL_GATE_1 && element <= EL_GATE_4)
9804 if (!player->key[element - EL_GATE_1])
9805 return MF_NO_ACTION;
9807 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9809 if (!player->key[element - EL_GATE_1_GRAY])
9810 return MF_NO_ACTION;
9812 else if (element == EL_EXIT_OPEN ||
9813 element == EL_SP_EXIT_OPEN ||
9814 element == EL_SP_EXIT_OPENING)
9816 sound_action = ACTION_PASSING; /* player is passing exit */
9818 else if (element == EL_EMPTY)
9820 sound_action = ACTION_MOVING; /* nothing to walk on */
9823 /* play sound from background or player, whatever is available */
9824 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9825 PlayLevelSoundElementAction(x, y, element, sound_action);
9827 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9831 else if (IS_PASSABLE(element))
9833 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9834 return MF_NO_ACTION;
9836 if (IS_CUSTOM_ELEMENT(element) &&
9837 !(element_info[element].access_direction & opposite_direction))
9838 return MF_NO_ACTION; /* field not accessible from this direction */
9841 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9842 return MF_NO_ACTION;
9845 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9847 if (!player->key[element - EL_EM_GATE_1])
9848 return MF_NO_ACTION;
9850 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9852 if (!player->key[element - EL_EM_GATE_1_GRAY])
9853 return MF_NO_ACTION;
9856 /* automatically move to the next field with double speed */
9857 player->programmed_action = move_direction;
9859 if (player->move_delay_reset_counter == 0)
9861 player->move_delay_reset_counter = 2; /* two double speed steps */
9863 DOUBLE_PLAYER_SPEED(player);
9866 player->move_delay_reset_counter = 2;
9868 DOUBLE_PLAYER_SPEED(player);
9871 PlayLevelSoundAction(x, y, ACTION_PASSING);
9875 else if (IS_DIGGABLE(element))
9879 if (mode != DF_SNAP)
9882 GfxElement[x][y] = GFX_ELEMENT(element);
9885 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9887 player->is_digging = TRUE;
9890 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9892 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED,
9893 player->index_bit, CH_SIDE_ANY);
9896 if (mode == DF_SNAP)
9897 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9902 else if (IS_COLLECTIBLE(element))
9906 if (mode != DF_SNAP)
9908 GfxElement[x][y] = element;
9909 player->is_collecting = TRUE;
9912 if (element == EL_SPEED_PILL)
9913 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9914 else if (element == EL_EXTRA_TIME && level.time > 0)
9917 DrawGameValue_Time(TimeLeft);
9919 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9921 player->shield_normal_time_left += 10;
9922 if (element == EL_SHIELD_DEADLY)
9923 player->shield_deadly_time_left += 10;
9925 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9927 if (player->inventory_size < MAX_INVENTORY_SIZE)
9928 player->inventory_element[player->inventory_size++] = element;
9930 DrawGameValue_Dynamite(local_player->inventory_size);
9932 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9934 player->dynabomb_count++;
9935 player->dynabombs_left++;
9937 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9939 player->dynabomb_size++;
9941 else if (element == EL_DYNABOMB_INCREASE_POWER)
9943 player->dynabomb_xl = TRUE;
9945 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9946 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9948 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9949 element - EL_KEY_1 : element - EL_EM_KEY_1);
9951 player->key[key_nr] = TRUE;
9953 DrawGameValue_Keys(player);
9955 redraw_mask |= REDRAW_DOOR_1;
9957 else if (IS_ENVELOPE(element))
9960 player->show_envelope = element;
9962 ShowEnvelope(element - EL_ENVELOPE_1);
9965 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9969 if (element_info[element].collect_count == 0)
9970 player->inventory_infinite_element = element;
9972 for (i = 0; i < element_info[element].collect_count; i++)
9973 if (player->inventory_size < MAX_INVENTORY_SIZE)
9974 player->inventory_element[player->inventory_size++] = element;
9976 DrawGameValue_Dynamite(local_player->inventory_size);
9978 else if (element_info[element].collect_count > 0)
9980 local_player->gems_still_needed -=
9981 element_info[element].collect_count;
9982 if (local_player->gems_still_needed < 0)
9983 local_player->gems_still_needed = 0;
9985 DrawGameValue_Emeralds(local_player->gems_still_needed);
9988 RaiseScoreElement(element);
9989 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9991 CheckTriggeredElementChangePlayer(x, y, element,
9992 CE_OTHER_GETS_COLLECTED,
9993 player->index_bit, CH_SIDE_ANY);
9996 if (mode == DF_SNAP)
9997 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10002 else if (IS_PUSHABLE(element))
10004 if (mode == DF_SNAP && element != EL_BD_ROCK)
10005 return MF_NO_ACTION;
10007 if (CAN_FALL(element) && dy)
10008 return MF_NO_ACTION;
10010 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10011 !(element == EL_SPRING && level.use_spring_bug))
10012 return MF_NO_ACTION;
10015 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10016 ((move_direction & MV_VERTICAL &&
10017 ((element_info[element].move_pattern & MV_LEFT &&
10018 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10019 (element_info[element].move_pattern & MV_RIGHT &&
10020 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10021 (move_direction & MV_HORIZONTAL &&
10022 ((element_info[element].move_pattern & MV_UP &&
10023 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10024 (element_info[element].move_pattern & MV_DOWN &&
10025 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10026 return MF_NO_ACTION;
10030 /* do not push elements already moving away faster than player */
10031 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10032 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10033 return MF_NO_ACTION;
10035 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
10036 return MF_NO_ACTION;
10040 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10042 if (player->push_delay_value == -1)
10043 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10045 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10047 if (!player->is_pushing)
10048 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10052 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
10053 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
10054 !player_is_pushing))
10055 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10058 if (!player->is_pushing &&
10059 game.engine_version >= VERSION_IDENT(2,2,0,7))
10060 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10064 printf("::: push delay: %ld [%d, %d] [%d]\n",
10065 player->push_delay_value, FrameCounter, game.engine_version,
10066 player->is_pushing);
10069 player->is_pushing = TRUE;
10071 if (!(IN_LEV_FIELD(nextx, nexty) &&
10072 (IS_FREE(nextx, nexty) ||
10073 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10074 IS_SB_ELEMENT(element)))))
10075 return MF_NO_ACTION;
10077 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10078 return MF_NO_ACTION;
10080 if (player->push_delay == 0) /* new pushing; restart delay */
10081 player->push_delay = FrameCounter;
10083 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
10084 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10085 element != EL_SPRING && element != EL_BALLOON)
10087 /* make sure that there is no move delay before next try to push */
10088 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10089 player->move_delay = INITIAL_MOVE_DELAY_OFF;
10091 return MF_NO_ACTION;
10095 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
10098 if (IS_SB_ELEMENT(element))
10100 if (element == EL_SOKOBAN_FIELD_FULL)
10102 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10103 local_player->sokobanfields_still_needed++;
10106 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10108 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10109 local_player->sokobanfields_still_needed--;
10112 Feld[x][y] = EL_SOKOBAN_OBJECT;
10114 if (Back[x][y] == Back[nextx][nexty])
10115 PlayLevelSoundAction(x, y, ACTION_PUSHING);
10116 else if (Back[x][y] != 0)
10117 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10120 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10123 if (local_player->sokobanfields_still_needed == 0 &&
10124 game.emulation == EMU_SOKOBAN)
10126 player->LevelSolved = player->GameOver = TRUE;
10127 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10131 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10133 InitMovingField(x, y, move_direction);
10134 GfxAction[x][y] = ACTION_PUSHING;
10136 if (mode == DF_SNAP)
10137 ContinueMoving(x, y);
10139 MovPos[x][y] = (dx != 0 ? dx : dy);
10141 Pushed[x][y] = TRUE;
10142 Pushed[nextx][nexty] = TRUE;
10144 if (game.engine_version < VERSION_IDENT(2,2,0,7))
10145 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10147 player->push_delay_value = -1; /* get new value later */
10149 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED,
10150 player->index_bit, dig_side);
10151 CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10152 player->index_bit, dig_side);
10156 else if (IS_SWITCHABLE(element))
10158 if (PLAYER_SWITCHING(player, x, y))
10161 player->is_switching = TRUE;
10162 player->switch_x = x;
10163 player->switch_y = y;
10165 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10167 if (element == EL_ROBOT_WHEEL)
10169 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10173 DrawLevelField(x, y);
10175 else if (element == EL_SP_TERMINAL)
10179 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
10181 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10183 else if (Feld[xx][yy] == EL_SP_TERMINAL)
10184 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10187 else if (IS_BELT_SWITCH(element))
10189 ToggleBeltSwitch(x, y);
10191 else if (element == EL_SWITCHGATE_SWITCH_UP ||
10192 element == EL_SWITCHGATE_SWITCH_DOWN)
10194 ToggleSwitchgateSwitch(x, y);
10196 else if (element == EL_LIGHT_SWITCH ||
10197 element == EL_LIGHT_SWITCH_ACTIVE)
10199 ToggleLightSwitch(x, y);
10202 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
10203 SND_LIGHT_SWITCH_ACTIVATING :
10204 SND_LIGHT_SWITCH_DEACTIVATING);
10207 else if (element == EL_TIMEGATE_SWITCH)
10209 ActivateTimegateSwitch(x, y);
10211 else if (element == EL_BALLOON_SWITCH_LEFT ||
10212 element == EL_BALLOON_SWITCH_RIGHT ||
10213 element == EL_BALLOON_SWITCH_UP ||
10214 element == EL_BALLOON_SWITCH_DOWN ||
10215 element == EL_BALLOON_SWITCH_ANY)
10217 if (element == EL_BALLOON_SWITCH_ANY)
10218 game.balloon_dir = move_direction;
10220 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10221 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10222 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10223 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10226 else if (element == EL_LAMP)
10228 Feld[x][y] = EL_LAMP_ACTIVE;
10229 local_player->lights_still_needed--;
10231 DrawLevelField(x, y);
10233 else if (element == EL_TIME_ORB_FULL)
10235 Feld[x][y] = EL_TIME_ORB_EMPTY;
10237 DrawGameValue_Time(TimeLeft);
10239 DrawLevelField(x, y);
10242 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
10250 if (!PLAYER_SWITCHING(player, x, y))
10252 player->is_switching = TRUE;
10253 player->switch_x = x;
10254 player->switch_y = y;
10256 CheckTriggeredElementChangePlayer(x, y, element,
10257 CE_OTHER_IS_SWITCHING,
10258 player->index_bit, dig_side);
10259 CheckElementChangePlayer(x, y, element, CE_SWITCHED,
10260 player->index_bit, dig_side);
10263 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED,
10264 player->index_bit, dig_side);
10265 CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10266 player->index_bit, dig_side);
10269 return MF_NO_ACTION;
10272 player->push_delay = 0;
10274 if (Feld[x][y] != element) /* really digged/collected something */
10275 player->is_collecting = !player->is_digging;
10280 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10282 int jx = player->jx, jy = player->jy;
10283 int x = jx + dx, y = jy + dy;
10284 int snap_direction = (dx == -1 ? MV_LEFT :
10285 dx == +1 ? MV_RIGHT :
10287 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10290 if (player->MovPos)
10293 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
10297 if (!player->active || !IN_LEV_FIELD(x, y))
10305 if (player->MovPos == 0)
10306 player->is_pushing = FALSE;
10308 player->is_snapping = FALSE;
10310 if (player->MovPos == 0)
10312 player->is_moving = FALSE;
10313 player->is_digging = FALSE;
10314 player->is_collecting = FALSE;
10320 if (player->is_snapping)
10323 player->MovDir = snap_direction;
10326 if (player->MovPos == 0)
10329 player->is_moving = FALSE;
10330 player->is_digging = FALSE;
10331 player->is_collecting = FALSE;
10334 player->is_dropping = FALSE;
10336 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10339 player->is_snapping = TRUE;
10342 if (player->MovPos == 0)
10345 player->is_moving = FALSE;
10346 player->is_digging = FALSE;
10347 player->is_collecting = FALSE;
10350 DrawLevelField(x, y);
10356 boolean DropElement(struct PlayerInfo *player)
10358 int jx = player->jx, jy = player->jy;
10359 int old_element = Feld[jx][jy];
10360 int new_element = (player->inventory_size > 0 ?
10361 player->inventory_element[player->inventory_size - 1] :
10362 player->inventory_infinite_element != EL_UNDEFINED ?
10363 player->inventory_infinite_element :
10364 player->dynabombs_left > 0 ?
10365 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10368 /* check if player is active, not moving and ready to drop */
10369 if (!player->active || player->MovPos || player->drop_delay > 0)
10372 /* check if player has anything that can be dropped */
10374 if (new_element == EL_UNDEFINED)
10377 if (player->inventory_size == 0 &&
10378 player->inventory_infinite_element == EL_UNDEFINED &&
10379 player->dynabombs_left == 0)
10383 /* check if anything can be dropped at the current position */
10384 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10387 /* collected custom elements can only be dropped on empty fields */
10389 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10392 if (player->inventory_size > 0 &&
10393 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
10394 && old_element != EL_EMPTY)
10398 if (old_element != EL_EMPTY)
10399 Back[jx][jy] = old_element; /* store old element on this field */
10401 ResetGfxAnimation(jx, jy);
10402 ResetRandomAnimationValue(jx, jy);
10404 if (player->inventory_size > 0 ||
10405 player->inventory_infinite_element != EL_UNDEFINED)
10407 if (player->inventory_size > 0)
10409 player->inventory_size--;
10412 new_element = player->inventory_element[player->inventory_size];
10415 DrawGameValue_Dynamite(local_player->inventory_size);
10417 if (new_element == EL_DYNAMITE)
10418 new_element = EL_DYNAMITE_ACTIVE;
10419 else if (new_element == EL_SP_DISK_RED)
10420 new_element = EL_SP_DISK_RED_ACTIVE;
10423 Feld[jx][jy] = new_element;
10425 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10426 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10428 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10431 /* needed if previous element just changed to "empty" in the last frame */
10432 Changed[jx][jy] = 0; /* allow another change */
10435 CheckTriggeredElementChangePlayer(jx, jy, new_element,
10436 CE_OTHER_GETS_DROPPED,
10437 player->index_bit, CH_SIDE_ANY);
10438 CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
10439 player->index_bit, CH_SIDE_ANY);
10441 TestIfElementTouchesCustomElement(jx, jy);
10443 else /* player is dropping a dyna bomb */
10445 player->dynabombs_left--;
10448 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
10451 Feld[jx][jy] = new_element;
10453 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10454 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10456 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10463 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
10466 InitField_WithBug1(jx, jy, FALSE);
10468 InitField(jx, jy, FALSE);
10469 if (CAN_MOVE(Feld[jx][jy]))
10470 InitMovDir(jx, jy);
10474 new_element = Feld[jx][jy];
10476 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10477 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10479 int move_stepsize = element_info[new_element].move_stepsize;
10480 int direction, dx, dy, nextx, nexty;
10482 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10483 MovDir[jx][jy] = player->MovDir;
10485 direction = MovDir[jx][jy];
10486 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10487 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10491 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
10494 WasJustMoving[jx][jy] = 3;
10496 InitMovingField(jx, jy, direction);
10497 ContinueMoving(jx, jy);
10502 Changed[jx][jy] = 0; /* allow another change */
10505 TestIfElementHitsCustomElement(jx, jy, direction);
10507 CheckElementChangeSide(jx, jy, new_element, CE_HITTING_SOMETHING,
10512 player->drop_delay = 2 * TILEX / move_stepsize + 1;
10516 player->drop_delay = 8 + 8 + 8;
10521 player->is_dropping = TRUE;
10527 /* ------------------------------------------------------------------------- */
10528 /* game sound playing functions */
10529 /* ------------------------------------------------------------------------- */
10531 static int *loop_sound_frame = NULL;
10532 static int *loop_sound_volume = NULL;
10534 void InitPlayLevelSound()
10536 int num_sounds = getSoundListSize();
10538 checked_free(loop_sound_frame);
10539 checked_free(loop_sound_volume);
10541 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10542 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10545 static void PlayLevelSound(int x, int y, int nr)
10547 int sx = SCREENX(x), sy = SCREENY(y);
10548 int volume, stereo_position;
10549 int max_distance = 8;
10550 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10552 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10553 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10556 if (!IN_LEV_FIELD(x, y) ||
10557 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10558 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10561 volume = SOUND_MAX_VOLUME;
10563 if (!IN_SCR_FIELD(sx, sy))
10565 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10566 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10568 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10571 stereo_position = (SOUND_MAX_LEFT +
10572 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10573 (SCR_FIELDX + 2 * max_distance));
10575 if (IS_LOOP_SOUND(nr))
10577 /* This assures that quieter loop sounds do not overwrite louder ones,
10578 while restarting sound volume comparison with each new game frame. */
10580 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10583 loop_sound_volume[nr] = volume;
10584 loop_sound_frame[nr] = FrameCounter;
10587 PlaySoundExt(nr, volume, stereo_position, type);
10590 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10592 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10593 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10594 y < LEVELY(BY1) ? LEVELY(BY1) :
10595 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10599 static void PlayLevelSoundAction(int x, int y, int action)
10601 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10604 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10606 int sound_effect = element_info[element].sound[action];
10608 if (sound_effect != SND_UNDEFINED)
10609 PlayLevelSound(x, y, sound_effect);
10612 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10615 int sound_effect = element_info[element].sound[action];
10617 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10618 PlayLevelSound(x, y, sound_effect);
10621 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10623 int sound_effect = element_info[Feld[x][y]].sound[action];
10625 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10626 PlayLevelSound(x, y, sound_effect);
10629 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10631 int sound_effect = element_info[Feld[x][y]].sound[action];
10633 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10634 StopSound(sound_effect);
10637 static void PlayLevelMusic()
10639 if (levelset.music[level_nr] != MUS_UNDEFINED)
10640 PlayMusic(levelset.music[level_nr]); /* from config file */
10642 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10645 void RaiseScore(int value)
10647 local_player->score += value;
10649 DrawGameValue_Score(local_player->score);
10652 void RaiseScoreElement(int element)
10657 case EL_BD_DIAMOND:
10658 case EL_EMERALD_YELLOW:
10659 case EL_EMERALD_RED:
10660 case EL_EMERALD_PURPLE:
10661 case EL_SP_INFOTRON:
10662 RaiseScore(level.score[SC_EMERALD]);
10665 RaiseScore(level.score[SC_DIAMOND]);
10668 RaiseScore(level.score[SC_CRYSTAL]);
10671 RaiseScore(level.score[SC_PEARL]);
10674 case EL_BD_BUTTERFLY:
10675 case EL_SP_ELECTRON:
10676 RaiseScore(level.score[SC_BUG]);
10679 case EL_BD_FIREFLY:
10680 case EL_SP_SNIKSNAK:
10681 RaiseScore(level.score[SC_SPACESHIP]);
10684 case EL_DARK_YAMYAM:
10685 RaiseScore(level.score[SC_YAMYAM]);
10688 RaiseScore(level.score[SC_ROBOT]);
10691 RaiseScore(level.score[SC_PACMAN]);
10694 RaiseScore(level.score[SC_NUT]);
10697 case EL_SP_DISK_RED:
10698 case EL_DYNABOMB_INCREASE_NUMBER:
10699 case EL_DYNABOMB_INCREASE_SIZE:
10700 case EL_DYNABOMB_INCREASE_POWER:
10701 RaiseScore(level.score[SC_DYNAMITE]);
10703 case EL_SHIELD_NORMAL:
10704 case EL_SHIELD_DEADLY:
10705 RaiseScore(level.score[SC_SHIELD]);
10707 case EL_EXTRA_TIME:
10708 RaiseScore(level.score[SC_TIME_BONUS]);
10714 RaiseScore(level.score[SC_KEY]);
10717 RaiseScore(element_info[element].collect_score);
10722 void RequestQuitGame(boolean ask_if_really_quit)
10724 if (AllPlayersGone ||
10725 !ask_if_really_quit ||
10726 level_editor_test_game ||
10727 Request("Do you really want to quit the game ?",
10728 REQ_ASK | REQ_STAY_CLOSED))
10730 #if defined(PLATFORM_UNIX)
10731 if (options.network)
10732 SendToServer_StopPlaying();
10736 game_status = GAME_MODE_MAIN;
10744 if (tape.playing && tape.index_search)
10746 SetDrawDeactivationMask(REDRAW_NONE);
10747 audio.sound_deactivated = FALSE;
10751 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10754 if (tape.playing && tape.index_search)
10756 SetDrawDeactivationMask(REDRAW_FIELD);
10757 audio.sound_deactivated = TRUE;
10765 /* ---------- new game button stuff ---------------------------------------- */
10767 /* graphic position values for game buttons */
10768 #define GAME_BUTTON_XSIZE 30
10769 #define GAME_BUTTON_YSIZE 30
10770 #define GAME_BUTTON_XPOS 5
10771 #define GAME_BUTTON_YPOS 215
10772 #define SOUND_BUTTON_XPOS 5
10773 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10775 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10776 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10777 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10778 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10779 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10780 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10787 } gamebutton_info[NUM_GAME_BUTTONS] =
10790 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10795 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10796 GAME_CTRL_ID_PAUSE,
10800 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10805 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10806 SOUND_CTRL_ID_MUSIC,
10807 "background music on/off"
10810 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10811 SOUND_CTRL_ID_LOOPS,
10812 "sound loops on/off"
10815 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10816 SOUND_CTRL_ID_SIMPLE,
10817 "normal sounds on/off"
10821 void CreateGameButtons()
10825 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10827 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10828 struct GadgetInfo *gi;
10831 unsigned long event_mask;
10832 int gd_xoffset, gd_yoffset;
10833 int gd_x1, gd_x2, gd_y1, gd_y2;
10836 gd_xoffset = gamebutton_info[i].x;
10837 gd_yoffset = gamebutton_info[i].y;
10838 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10839 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10841 if (id == GAME_CTRL_ID_STOP ||
10842 id == GAME_CTRL_ID_PAUSE ||
10843 id == GAME_CTRL_ID_PLAY)
10845 button_type = GD_TYPE_NORMAL_BUTTON;
10847 event_mask = GD_EVENT_RELEASED;
10848 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10849 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10853 button_type = GD_TYPE_CHECK_BUTTON;
10855 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10856 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10857 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10858 event_mask = GD_EVENT_PRESSED;
10859 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10860 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10863 gi = CreateGadget(GDI_CUSTOM_ID, id,
10864 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10865 GDI_X, DX + gd_xoffset,
10866 GDI_Y, DY + gd_yoffset,
10867 GDI_WIDTH, GAME_BUTTON_XSIZE,
10868 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10869 GDI_TYPE, button_type,
10870 GDI_STATE, GD_BUTTON_UNPRESSED,
10871 GDI_CHECKED, checked,
10872 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10873 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10874 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10875 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10876 GDI_EVENT_MASK, event_mask,
10877 GDI_CALLBACK_ACTION, HandleGameButtons,
10881 Error(ERR_EXIT, "cannot create gadget");
10883 game_gadget[id] = gi;
10887 void FreeGameButtons()
10891 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10892 FreeGadget(game_gadget[i]);
10895 static void MapGameButtons()
10899 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10900 MapGadget(game_gadget[i]);
10903 void UnmapGameButtons()
10907 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10908 UnmapGadget(game_gadget[i]);
10911 static void HandleGameButtons(struct GadgetInfo *gi)
10913 int id = gi->custom_id;
10915 if (game_status != GAME_MODE_PLAYING)
10920 case GAME_CTRL_ID_STOP:
10921 RequestQuitGame(TRUE);
10924 case GAME_CTRL_ID_PAUSE:
10925 if (options.network)
10927 #if defined(PLATFORM_UNIX)
10929 SendToServer_ContinuePlaying();
10931 SendToServer_PausePlaying();
10935 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10938 case GAME_CTRL_ID_PLAY:
10941 #if defined(PLATFORM_UNIX)
10942 if (options.network)
10943 SendToServer_ContinuePlaying();
10947 tape.pausing = FALSE;
10948 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10953 case SOUND_CTRL_ID_MUSIC:
10954 if (setup.sound_music)
10956 setup.sound_music = FALSE;
10959 else if (audio.music_available)
10961 setup.sound = setup.sound_music = TRUE;
10963 SetAudioMode(setup.sound);
10969 case SOUND_CTRL_ID_LOOPS:
10970 if (setup.sound_loops)
10971 setup.sound_loops = FALSE;
10972 else if (audio.loops_available)
10974 setup.sound = setup.sound_loops = TRUE;
10975 SetAudioMode(setup.sound);
10979 case SOUND_CTRL_ID_SIMPLE:
10980 if (setup.sound_simple)
10981 setup.sound_simple = FALSE;
10982 else if (audio.sound_available)
10984 setup.sound = setup.sound_simple = TRUE;
10985 SetAudioMode(setup.sound);