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 ChangeElement(int, int, int);
254 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
255 #define CheckTriggeredElementChange(x, y, e, ev) \
256 CheckTriggeredElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, -1)
257 #define CheckTriggeredElementChangePlayer(x, y, e, ev, p, s) \
258 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
259 #define CheckTriggeredElementChangeSide(x, y, e, ev, s) \
260 CheckTriggeredElementChangeExt(x, y, e, ev, -1, s, -1)
261 #define CheckTriggeredElementChangePage(x, y, e, ev, p) \
262 CheckTriggeredElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, p)
264 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
265 #define CheckElementChange(x, y, e, ev) \
266 CheckElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, -1)
267 #define CheckElementChangePlayer(x, y, e, ev, p, s) \
268 CheckElementChangeExt(x, y, e, ev, p, s, -1)
269 #define CheckElementChangeSide(x, y, e, ev, s) \
270 CheckElementChangeExt(x, y, e, ev, -1, s, -1)
271 #define CheckElementChangePage(x, y, e, ev, p) \
272 CheckElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, p)
274 static void PlayLevelSound(int, int, int);
275 static void PlayLevelSoundNearest(int, int, int);
276 static void PlayLevelSoundAction(int, int, int);
277 static void PlayLevelSoundElementAction(int, int, int, int);
278 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
279 static void PlayLevelSoundActionIfLoop(int, int, int);
280 static void StopLevelSoundActionIfLoop(int, int, int);
281 static void PlayLevelMusic();
283 static void MapGameButtons();
284 static void HandleGameButtons(struct GadgetInfo *);
286 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
289 /* ------------------------------------------------------------------------- */
290 /* definition of elements that automatically change to other elements after */
291 /* a specified time, eventually calling a function when changing */
292 /* ------------------------------------------------------------------------- */
294 /* forward declaration for changer functions */
295 static void InitBuggyBase(int x, int y);
296 static void WarnBuggyBase(int x, int y);
298 static void InitTrap(int x, int y);
299 static void ActivateTrap(int x, int y);
300 static void ChangeActiveTrap(int x, int y);
302 static void InitRobotWheel(int x, int y);
303 static void RunRobotWheel(int x, int y);
304 static void StopRobotWheel(int x, int y);
306 static void InitTimegateWheel(int x, int y);
307 static void RunTimegateWheel(int x, int y);
309 struct ChangingElementInfo
314 void (*pre_change_function)(int x, int y);
315 void (*change_function)(int x, int y);
316 void (*post_change_function)(int x, int y);
319 static struct ChangingElementInfo change_delay_list[] =
370 EL_SWITCHGATE_OPENING,
378 EL_SWITCHGATE_CLOSING,
379 EL_SWITCHGATE_CLOSED,
411 EL_ACID_SPLASH_RIGHT,
420 EL_SP_BUGGY_BASE_ACTIVATING,
427 EL_SP_BUGGY_BASE_ACTIVATING,
428 EL_SP_BUGGY_BASE_ACTIVE,
435 EL_SP_BUGGY_BASE_ACTIVE,
459 EL_ROBOT_WHEEL_ACTIVE,
467 EL_TIMEGATE_SWITCH_ACTIVE,
488 int push_delay_fixed, push_delay_random;
493 { EL_BALLOON, 0, 0 },
495 { EL_SOKOBAN_OBJECT, 2, 0 },
496 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
497 { EL_SATELLITE, 2, 0 },
498 { EL_SP_DISK_YELLOW, 2, 0 },
500 { EL_UNDEFINED, 0, 0 },
508 move_stepsize_list[] =
510 { EL_AMOEBA_DROP, 2 },
511 { EL_AMOEBA_DROPPING, 2 },
512 { EL_QUICKSAND_FILLING, 1 },
513 { EL_QUICKSAND_EMPTYING, 1 },
514 { EL_MAGIC_WALL_FILLING, 2 },
515 { EL_BD_MAGIC_WALL_FILLING, 2 },
516 { EL_MAGIC_WALL_EMPTYING, 2 },
517 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
527 collect_count_list[] =
530 { EL_BD_DIAMOND, 1 },
531 { EL_EMERALD_YELLOW, 1 },
532 { EL_EMERALD_RED, 1 },
533 { EL_EMERALD_PURPLE, 1 },
535 { EL_SP_INFOTRON, 1 },
549 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
550 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
551 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
552 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
553 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
554 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
555 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
556 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
557 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
558 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
559 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
564 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
566 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
567 CH_EVENT_BIT(CE_DELAY))
568 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
569 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
570 IS_JUST_CHANGING(x, y))
572 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
575 void GetPlayerConfig()
577 if (!audio.sound_available)
578 setup.sound_simple = FALSE;
580 if (!audio.loops_available)
581 setup.sound_loops = FALSE;
583 if (!audio.music_available)
584 setup.sound_music = FALSE;
586 if (!video.fullscreen_available)
587 setup.fullscreen = FALSE;
589 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
591 SetAudioMode(setup.sound);
595 static int getBeltNrFromBeltElement(int element)
597 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
598 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
599 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
602 static int getBeltNrFromBeltActiveElement(int element)
604 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
605 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
606 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
609 static int getBeltNrFromBeltSwitchElement(int element)
611 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
612 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
613 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
616 static int getBeltDirNrFromBeltSwitchElement(int element)
618 static int belt_base_element[4] =
620 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
621 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
622 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
623 EL_CONVEYOR_BELT_4_SWITCH_LEFT
626 int belt_nr = getBeltNrFromBeltSwitchElement(element);
627 int belt_dir_nr = element - belt_base_element[belt_nr];
629 return (belt_dir_nr % 3);
632 static int getBeltDirFromBeltSwitchElement(int element)
634 static int belt_move_dir[3] =
641 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
643 return belt_move_dir[belt_dir_nr];
646 static void InitPlayerField(int x, int y, int element, boolean init_game)
648 if (element == EL_SP_MURPHY)
652 if (stored_player[0].present)
654 Feld[x][y] = EL_SP_MURPHY_CLONE;
660 stored_player[0].use_murphy_graphic = TRUE;
663 Feld[x][y] = EL_PLAYER_1;
669 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
670 int jx = player->jx, jy = player->jy;
672 player->present = TRUE;
674 player->block_last_field = (element == EL_SP_MURPHY ?
675 level.sp_block_last_field :
676 level.block_last_field);
678 if (!options.network || player->connected)
680 player->active = TRUE;
682 /* remove potentially duplicate players */
683 if (StorePlayer[jx][jy] == Feld[x][y])
684 StorePlayer[jx][jy] = 0;
686 StorePlayer[x][y] = Feld[x][y];
690 printf("Player %d activated.\n", player->element_nr);
691 printf("[Local player is %d and currently %s.]\n",
692 local_player->element_nr,
693 local_player->active ? "active" : "not active");
697 Feld[x][y] = EL_EMPTY;
698 player->jx = player->last_jx = x;
699 player->jy = player->last_jy = y;
703 static void InitField(int x, int y, boolean init_game)
705 int element = Feld[x][y];
714 InitPlayerField(x, y, element, init_game);
717 case EL_SOKOBAN_FIELD_PLAYER:
718 element = Feld[x][y] = EL_PLAYER_1;
719 InitField(x, y, init_game);
721 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
722 InitField(x, y, init_game);
725 case EL_SOKOBAN_FIELD_EMPTY:
726 local_player->sokobanfields_still_needed++;
730 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
731 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
732 else if (x > 0 && Feld[x-1][y] == EL_ACID)
733 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
734 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
735 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
736 else if (y > 0 && Feld[x][y-1] == EL_ACID)
737 Feld[x][y] = EL_ACID_POOL_BOTTOM;
738 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
739 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
747 case EL_SPACESHIP_RIGHT:
748 case EL_SPACESHIP_UP:
749 case EL_SPACESHIP_LEFT:
750 case EL_SPACESHIP_DOWN:
752 case EL_BD_BUTTERFLY_RIGHT:
753 case EL_BD_BUTTERFLY_UP:
754 case EL_BD_BUTTERFLY_LEFT:
755 case EL_BD_BUTTERFLY_DOWN:
756 case EL_BD_BUTTERFLY:
757 case EL_BD_FIREFLY_RIGHT:
758 case EL_BD_FIREFLY_UP:
759 case EL_BD_FIREFLY_LEFT:
760 case EL_BD_FIREFLY_DOWN:
762 case EL_PACMAN_RIGHT:
786 if (y == lev_fieldy - 1)
788 Feld[x][y] = EL_AMOEBA_GROWING;
789 Store[x][y] = EL_AMOEBA_WET;
793 case EL_DYNAMITE_ACTIVE:
794 case EL_SP_DISK_RED_ACTIVE:
795 case EL_DYNABOMB_PLAYER_1_ACTIVE:
796 case EL_DYNABOMB_PLAYER_2_ACTIVE:
797 case EL_DYNABOMB_PLAYER_3_ACTIVE:
798 case EL_DYNABOMB_PLAYER_4_ACTIVE:
803 local_player->lights_still_needed++;
807 local_player->friends_still_needed++;
812 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
817 Feld[x][y] = EL_EMPTY;
822 case EL_EM_KEY_1_FILE:
823 Feld[x][y] = EL_EM_KEY_1;
825 case EL_EM_KEY_2_FILE:
826 Feld[x][y] = EL_EM_KEY_2;
828 case EL_EM_KEY_3_FILE:
829 Feld[x][y] = EL_EM_KEY_3;
831 case EL_EM_KEY_4_FILE:
832 Feld[x][y] = EL_EM_KEY_4;
836 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
837 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
838 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
839 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
840 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
841 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
842 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
843 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
844 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
845 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
846 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
847 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
850 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
851 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
852 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
854 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
856 game.belt_dir[belt_nr] = belt_dir;
857 game.belt_dir_nr[belt_nr] = belt_dir_nr;
859 else /* more than one switch -- set it like the first switch */
861 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
866 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
868 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
871 case EL_LIGHT_SWITCH_ACTIVE:
873 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
877 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
879 else if (IS_GROUP_ELEMENT(element))
881 struct ElementGroupInfo *group = element_info[element].group;
882 int last_anim_random_frame = gfx.anim_random_frame;
885 if (group->choice_mode == ANIM_RANDOM)
886 gfx.anim_random_frame = RND(group->num_elements_resolved);
888 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
889 group->choice_mode, 0,
892 if (group->choice_mode == ANIM_RANDOM)
893 gfx.anim_random_frame = last_anim_random_frame;
897 Feld[x][y] = group->element_resolved[element_pos];
899 InitField(x, y, init_game);
905 static inline void InitField_WithBug1(int x, int y, boolean init_game)
907 InitField(x, y, init_game);
909 /* not needed to call InitMovDir() -- already done by InitField()! */
910 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
911 CAN_MOVE(Feld[x][y]))
915 static inline void InitField_WithBug2(int x, int y, boolean init_game)
917 int old_element = Feld[x][y];
919 InitField(x, y, init_game);
921 /* not needed to call InitMovDir() -- already done by InitField()! */
922 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
923 CAN_MOVE(old_element) &&
924 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
927 /* this case is in fact a combination of not less than three bugs:
928 first, it calls InitMovDir() for elements that can move, although this is
929 already done by InitField(); then, it checks the element that was at this
930 field _before_ the call to InitField() (which can change it)
935 inline void DrawGameValue_Emeralds(int value)
937 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
940 inline void DrawGameValue_Dynamite(int value)
942 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
945 inline void DrawGameValue_Keys(struct PlayerInfo *player)
949 for (i = 0; i < MAX_KEYS; i++)
951 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
952 el2edimg(EL_KEY_1 + i));
955 inline void DrawGameValue_Score(int value)
957 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
960 inline void DrawGameValue_Time(int value)
963 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
965 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
968 inline void DrawGameValue_Level(int value)
971 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
974 /* misuse area for displaying emeralds to draw bigger level number */
975 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
976 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
978 /* now copy it to the area for displaying level number */
979 BlitBitmap(drawto, drawto,
980 DX_EMERALDS, DY_EMERALDS + 1,
981 getFontWidth(FONT_LEVEL_NUMBER) * 3,
982 getFontHeight(FONT_LEVEL_NUMBER) - 1,
983 DX_LEVEL - 1, DY_LEVEL + 1);
985 /* restore the area for displaying emeralds */
986 DrawGameValue_Emeralds(local_player->gems_still_needed);
988 /* yes, this is all really ugly :-) */
992 void DrawGameDoorValues()
996 DrawGameValue_Level(level_nr);
998 for (i = 0; i < MAX_PLAYERS; i++)
999 DrawGameValue_Keys(&stored_player[i]);
1001 DrawGameValue_Emeralds(local_player->gems_still_needed);
1002 DrawGameValue_Dynamite(local_player->inventory_size);
1003 DrawGameValue_Score(local_player->score);
1004 DrawGameValue_Time(TimeLeft);
1007 static void resolve_group_element(int group_element, int recursion_depth)
1009 static int group_nr;
1010 static struct ElementGroupInfo *group;
1011 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1014 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1016 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1017 group_element - EL_GROUP_START + 1);
1019 /* replace element which caused too deep recursion by question mark */
1020 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1025 if (recursion_depth == 0) /* initialization */
1027 group = element_info[group_element].group;
1028 group_nr = group_element - EL_GROUP_START;
1030 group->num_elements_resolved = 0;
1031 group->choice_pos = 0;
1034 for (i = 0; i < actual_group->num_elements; i++)
1036 int element = actual_group->element[i];
1038 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1041 if (IS_GROUP_ELEMENT(element))
1042 resolve_group_element(element, recursion_depth + 1);
1045 group->element_resolved[group->num_elements_resolved++] = element;
1046 element_info[element].in_group[group_nr] = TRUE;
1051 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1053 printf("::: group %d: %d resolved elements\n",
1054 group_element - EL_GROUP_START, group->num_elements_resolved);
1055 for (i = 0; i < group->num_elements_resolved; i++)
1056 printf("::: - %d ['%s']\n", group->element_resolved[i],
1057 element_info[group->element_resolved[i]].token_name);
1064 =============================================================================
1066 -----------------------------------------------------------------------------
1067 initialize game engine due to level / tape version number
1068 =============================================================================
1071 static void InitGameEngine()
1075 /* set game engine from tape file when re-playing, else from level file */
1076 game.engine_version = (tape.playing ? tape.engine_version :
1077 level.game_version);
1079 /* dynamically adjust element properties according to game engine version */
1080 InitElementPropertiesEngine(game.engine_version);
1083 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1084 printf(" tape version == %06d [%s] [file: %06d]\n",
1085 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1087 printf(" => game.engine_version == %06d\n", game.engine_version);
1090 /* ---------- recursively resolve group elements ------------------------- */
1092 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1093 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1094 element_info[i].in_group[j] = FALSE;
1096 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1097 resolve_group_element(EL_GROUP_START + i, 0);
1099 /* ---------- initialize player's initial move delay --------------------- */
1101 /* dynamically adjust player properties according to game engine version */
1102 game.initial_move_delay =
1103 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1104 INITIAL_MOVE_DELAY_OFF);
1106 /* dynamically adjust player properties according to level information */
1107 game.initial_move_delay_value =
1108 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1110 /* ---------- initialize player's initial push delay --------------------- */
1112 /* dynamically adjust player properties according to game engine version */
1113 game.initial_push_delay_value =
1114 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1116 /* ---------- initialize changing elements ------------------------------- */
1118 /* initialize changing elements information */
1119 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1121 struct ElementInfo *ei = &element_info[i];
1123 /* this pointer might have been changed in the level editor */
1124 ei->change = &ei->change_page[0];
1126 if (!IS_CUSTOM_ELEMENT(i))
1128 ei->change->target_element = EL_EMPTY_SPACE;
1129 ei->change->delay_fixed = 0;
1130 ei->change->delay_random = 0;
1131 ei->change->delay_frames = 1;
1134 ei->change_events = CE_BITMASK_DEFAULT;
1135 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1137 ei->event_page_nr[j] = 0;
1138 ei->event_page[j] = &ei->change_page[0];
1142 /* add changing elements from pre-defined list */
1143 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1145 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1146 struct ElementInfo *ei = &element_info[ch_delay->element];
1148 ei->change->target_element = ch_delay->target_element;
1149 ei->change->delay_fixed = ch_delay->change_delay;
1151 ei->change->pre_change_function = ch_delay->pre_change_function;
1152 ei->change->change_function = ch_delay->change_function;
1153 ei->change->post_change_function = ch_delay->post_change_function;
1155 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1159 /* add change events from custom element configuration */
1160 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1162 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1164 for (j = 0; j < ei->num_change_pages; j++)
1166 if (!ei->change_page[j].can_change)
1169 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1171 /* only add event page for the first page found with this event */
1172 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1173 !(ei->change_events & CH_EVENT_BIT(k)))
1175 ei->change_events |= CH_EVENT_BIT(k);
1176 ei->event_page_nr[k] = j;
1177 ei->event_page[k] = &ei->change_page[j];
1185 /* add change events from custom element configuration */
1186 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1188 int element = EL_CUSTOM_START + i;
1190 /* only add custom elements that change after fixed/random frame delay */
1191 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1192 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1196 /* ---------- initialize trigger events ---------------------------------- */
1198 /* initialize trigger events information */
1199 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1200 trigger_events[i] = EP_BITMASK_DEFAULT;
1203 /* add trigger events from element change event properties */
1204 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1206 struct ElementInfo *ei = &element_info[i];
1208 for (j = 0; j < ei->num_change_pages; j++)
1210 if (!ei->change_page[j].can_change)
1213 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1215 int trigger_element = ei->change_page[j].trigger_element;
1217 if (IS_GROUP_ELEMENT(trigger_element))
1219 struct ElementGroupInfo *group = element_info[trigger_element].group;
1221 for (k = 0; k < group->num_elements_resolved; k++)
1222 trigger_events[group->element_resolved[k]]
1223 |= ei->change_page[j].events;
1226 trigger_events[trigger_element] |= ei->change_page[j].events;
1231 /* add trigger events from element change event properties */
1232 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1233 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1234 trigger_events[element_info[i].change->trigger_element] |=
1235 element_info[i].change->events;
1238 /* ---------- initialize push delay -------------------------------------- */
1240 /* initialize push delay values to default */
1241 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1243 if (!IS_CUSTOM_ELEMENT(i))
1245 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1246 element_info[i].push_delay_random = game.default_push_delay_random;
1250 /* set push delay value for certain elements from pre-defined list */
1251 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1253 int e = push_delay_list[i].element;
1255 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1256 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1259 /* set push delay value for Supaplex elements for newer engine versions */
1260 if (game.engine_version >= VERSION_IDENT(3,0,9,0))
1262 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1264 if (IS_SP_ELEMENT(i))
1266 element_info[i].push_delay_fixed = 6;
1267 element_info[i].push_delay_random = 0;
1272 /* ---------- initialize move stepsize ----------------------------------- */
1274 /* initialize move stepsize values to default */
1275 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1276 if (!IS_CUSTOM_ELEMENT(i))
1277 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1279 /* set move stepsize value for certain elements from pre-defined list */
1280 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1282 int e = move_stepsize_list[i].element;
1284 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1287 /* ---------- initialize move dig/leave ---------------------------------- */
1289 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1291 element_info[i].can_leave_element = FALSE;
1292 element_info[i].can_leave_element_last = FALSE;
1295 /* ---------- initialize gem count --------------------------------------- */
1297 /* initialize gem count values for each element */
1298 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1299 if (!IS_CUSTOM_ELEMENT(i))
1300 element_info[i].collect_count = 0;
1302 /* add gem count values for all elements from pre-defined list */
1303 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1304 element_info[collect_count_list[i].element].collect_count =
1305 collect_count_list[i].count;
1307 /* ---------- initialize access direction -------------------------------- */
1309 /* initialize access direction values to default */
1310 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1311 if (!IS_CUSTOM_ELEMENT(i))
1312 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1314 /* set access direction value for certain elements from pre-defined list */
1315 for (i = 0; tube_access[i].element != EL_UNDEFINED; i++)
1316 element_info[tube_access[i].element].access_direction =
1317 tube_access[i].direction;
1322 =============================================================================
1324 -----------------------------------------------------------------------------
1325 initialize and start new game
1326 =============================================================================
1331 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1332 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1333 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1340 #if USE_NEW_AMOEBA_CODE
1341 printf("Using new amoeba code.\n");
1343 printf("Using old amoeba code.\n");
1348 /* don't play tapes over network */
1349 network_playing = (options.network && !tape.playing);
1351 for (i = 0; i < MAX_PLAYERS; i++)
1353 struct PlayerInfo *player = &stored_player[i];
1355 player->index_nr = i;
1356 player->index_bit = (1 << i);
1357 player->element_nr = EL_PLAYER_1 + i;
1359 player->present = FALSE;
1360 player->active = FALSE;
1363 player->effective_action = 0;
1364 player->programmed_action = 0;
1367 player->gems_still_needed = level.gems_needed;
1368 player->sokobanfields_still_needed = 0;
1369 player->lights_still_needed = 0;
1370 player->friends_still_needed = 0;
1372 for (j = 0; j < MAX_KEYS; j++)
1373 player->key[j] = FALSE;
1375 player->dynabomb_count = 0;
1376 player->dynabomb_size = 1;
1377 player->dynabombs_left = 0;
1378 player->dynabomb_xl = FALSE;
1380 player->MovDir = MV_NO_MOVING;
1383 player->GfxDir = MV_NO_MOVING;
1384 player->GfxAction = ACTION_DEFAULT;
1386 player->StepFrame = 0;
1388 player->use_murphy_graphic = FALSE;
1390 player->block_last_field = FALSE;
1392 player->actual_frame_counter = 0;
1394 player->step_counter = 0;
1396 player->last_move_dir = MV_NO_MOVING;
1398 player->is_waiting = FALSE;
1399 player->is_moving = FALSE;
1400 player->is_digging = FALSE;
1401 player->is_snapping = FALSE;
1402 player->is_collecting = FALSE;
1403 player->is_pushing = FALSE;
1404 player->is_switching = FALSE;
1405 player->is_dropping = FALSE;
1407 player->is_bored = FALSE;
1408 player->is_sleeping = FALSE;
1410 player->frame_counter_bored = -1;
1411 player->frame_counter_sleeping = -1;
1413 player->anim_delay_counter = 0;
1414 player->post_delay_counter = 0;
1416 player->action_waiting = ACTION_DEFAULT;
1417 player->last_action_waiting = ACTION_DEFAULT;
1418 player->special_action_bored = ACTION_DEFAULT;
1419 player->special_action_sleeping = ACTION_DEFAULT;
1421 player->num_special_action_bored = 0;
1422 player->num_special_action_sleeping = 0;
1424 /* determine number of special actions for bored and sleeping animation */
1425 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1427 boolean found = FALSE;
1429 for (k = 0; k < NUM_DIRECTIONS; k++)
1430 if (el_act_dir2img(player->element_nr, j, k) !=
1431 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1435 player->num_special_action_bored++;
1439 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1441 boolean found = FALSE;
1443 for (k = 0; k < NUM_DIRECTIONS; k++)
1444 if (el_act_dir2img(player->element_nr, j, k) !=
1445 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1449 player->num_special_action_sleeping++;
1454 player->switch_x = -1;
1455 player->switch_y = -1;
1457 player->show_envelope = 0;
1459 player->move_delay = game.initial_move_delay;
1460 player->move_delay_value = game.initial_move_delay_value;
1462 player->move_delay_reset_counter = 0;
1464 player->push_delay = 0;
1465 player->push_delay_value = game.initial_push_delay_value;
1467 player->drop_delay = 0;
1469 player->last_jx = player->last_jy = 0;
1470 player->jx = player->jy = 0;
1472 player->shield_normal_time_left = 0;
1473 player->shield_deadly_time_left = 0;
1475 player->inventory_infinite_element = EL_UNDEFINED;
1476 player->inventory_size = 0;
1478 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1479 SnapField(player, 0, 0);
1481 player->LevelSolved = FALSE;
1482 player->GameOver = FALSE;
1485 network_player_action_received = FALSE;
1487 #if defined(PLATFORM_UNIX)
1488 /* initial null action */
1489 if (network_playing)
1490 SendToServer_MovePlayer(MV_NO_MOVING);
1498 TimeLeft = level.time;
1501 ScreenMovDir = MV_NO_MOVING;
1505 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1507 AllPlayersGone = FALSE;
1509 game.yamyam_content_nr = 0;
1510 game.magic_wall_active = FALSE;
1511 game.magic_wall_time_left = 0;
1512 game.light_time_left = 0;
1513 game.timegate_time_left = 0;
1514 game.switchgate_pos = 0;
1515 game.balloon_dir = MV_NO_MOVING;
1516 game.gravity = level.initial_gravity;
1517 game.explosions_delayed = TRUE;
1519 game.envelope_active = FALSE;
1521 for (i = 0; i < NUM_BELTS; i++)
1523 game.belt_dir[i] = MV_NO_MOVING;
1524 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1527 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1528 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1530 for (x = 0; x < lev_fieldx; x++)
1532 for (y = 0; y < lev_fieldy; y++)
1534 Feld[x][y] = level.field[x][y];
1535 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1536 ChangeDelay[x][y] = 0;
1537 ChangePage[x][y] = -1;
1538 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1540 WasJustMoving[x][y] = 0;
1541 WasJustFalling[x][y] = 0;
1543 Pushed[x][y] = FALSE;
1545 Changed[x][y] = CE_BITMASK_DEFAULT;
1546 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1548 ExplodePhase[x][y] = 0;
1549 ExplodeDelay[x][y] = 0;
1550 ExplodeField[x][y] = EX_NO_EXPLOSION;
1552 RunnerVisit[x][y] = 0;
1553 PlayerVisit[x][y] = 0;
1556 GfxRandom[x][y] = INIT_GFX_RANDOM();
1557 GfxElement[x][y] = EL_UNDEFINED;
1558 GfxAction[x][y] = ACTION_DEFAULT;
1559 GfxDir[x][y] = MV_NO_MOVING;
1563 for (y = 0; y < lev_fieldy; y++)
1565 for (x = 0; x < lev_fieldx; x++)
1567 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1569 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1571 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1574 InitField(x, y, TRUE);
1580 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1581 emulate_sb ? EMU_SOKOBAN :
1582 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1584 /* initialize explosion and ignition delay */
1585 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1587 if (!IS_CUSTOM_ELEMENT(i))
1590 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
1591 int last_phase = num_phase * delay;
1592 int half_phase = (num_phase / 2) * delay;
1594 element_info[i].explosion_delay = last_phase;
1595 element_info[i].ignition_delay = half_phase;
1597 if (i == EL_BLACK_ORB)
1598 element_info[i].ignition_delay = 1;
1601 if (element_info[i].explosion_delay < 2) /* !!! check again !!! */
1602 element_info[i].explosion_delay = 2;
1604 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1605 element_info[i].ignition_delay = 1;
1608 /* correct non-moving belts to start moving left */
1609 for (i = 0; i < NUM_BELTS; i++)
1610 if (game.belt_dir[i] == MV_NO_MOVING)
1611 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1613 /* check if any connected player was not found in playfield */
1614 for (i = 0; i < MAX_PLAYERS; i++)
1616 struct PlayerInfo *player = &stored_player[i];
1618 if (player->connected && !player->present)
1620 for (j = 0; j < MAX_PLAYERS; j++)
1622 struct PlayerInfo *some_player = &stored_player[j];
1623 int jx = some_player->jx, jy = some_player->jy;
1625 /* assign first free player found that is present in the playfield */
1626 if (some_player->present && !some_player->connected)
1628 player->present = TRUE;
1629 player->active = TRUE;
1631 some_player->present = FALSE;
1632 some_player->active = FALSE;
1634 StorePlayer[jx][jy] = player->element_nr;
1635 player->jx = player->last_jx = jx;
1636 player->jy = player->last_jy = jy;
1646 /* when playing a tape, eliminate all players which do not participate */
1648 for (i = 0; i < MAX_PLAYERS; i++)
1650 if (stored_player[i].active && !tape.player_participates[i])
1652 struct PlayerInfo *player = &stored_player[i];
1653 int jx = player->jx, jy = player->jy;
1655 player->active = FALSE;
1656 StorePlayer[jx][jy] = 0;
1657 Feld[jx][jy] = EL_EMPTY;
1661 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1663 /* when in single player mode, eliminate all but the first active player */
1665 for (i = 0; i < MAX_PLAYERS; i++)
1667 if (stored_player[i].active)
1669 for (j = i + 1; j < MAX_PLAYERS; j++)
1671 if (stored_player[j].active)
1673 struct PlayerInfo *player = &stored_player[j];
1674 int jx = player->jx, jy = player->jy;
1676 player->active = FALSE;
1677 player->present = FALSE;
1679 StorePlayer[jx][jy] = 0;
1680 Feld[jx][jy] = EL_EMPTY;
1687 /* when recording the game, store which players take part in the game */
1690 for (i = 0; i < MAX_PLAYERS; i++)
1691 if (stored_player[i].active)
1692 tape.player_participates[i] = TRUE;
1697 for (i = 0; i < MAX_PLAYERS; i++)
1699 struct PlayerInfo *player = &stored_player[i];
1701 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1706 if (local_player == player)
1707 printf("Player %d is local player.\n", i+1);
1711 if (BorderElement == EL_EMPTY)
1714 SBX_Right = lev_fieldx - SCR_FIELDX;
1716 SBY_Lower = lev_fieldy - SCR_FIELDY;
1721 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1723 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1726 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1727 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1729 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1730 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1732 /* if local player not found, look for custom element that might create
1733 the player (make some assumptions about the right custom element) */
1734 if (!local_player->present)
1736 int start_x = 0, start_y = 0;
1737 int found_rating = 0;
1738 int found_element = EL_UNDEFINED;
1740 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1742 int element = Feld[x][y];
1747 if (!IS_CUSTOM_ELEMENT(element))
1750 if (CAN_CHANGE(element))
1752 for (i = 0; i < element_info[element].num_change_pages; i++)
1754 content = element_info[element].change_page[i].target_element;
1755 is_player = ELEM_IS_PLAYER(content);
1757 if (is_player && (found_rating < 3 || element < found_element))
1763 found_element = element;
1768 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1770 content = element_info[element].content[xx][yy];
1771 is_player = ELEM_IS_PLAYER(content);
1773 if (is_player && (found_rating < 2 || element < found_element))
1775 start_x = x + xx - 1;
1776 start_y = y + yy - 1;
1779 found_element = element;
1782 if (!CAN_CHANGE(element))
1785 for (i = 0; i < element_info[element].num_change_pages; i++)
1787 content = element_info[element].change_page[i].content[xx][yy];
1788 is_player = ELEM_IS_PLAYER(content);
1790 if (is_player && (found_rating < 1 || element < found_element))
1792 start_x = x + xx - 1;
1793 start_y = y + yy - 1;
1796 found_element = element;
1802 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1803 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1806 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1807 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1813 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1814 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1815 local_player->jx - MIDPOSX);
1817 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1818 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1819 local_player->jy - MIDPOSY);
1821 scroll_x = SBX_Left;
1822 scroll_y = SBY_Upper;
1823 if (local_player->jx >= SBX_Left + MIDPOSX)
1824 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1825 local_player->jx - MIDPOSX :
1827 if (local_player->jy >= SBY_Upper + MIDPOSY)
1828 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1829 local_player->jy - MIDPOSY :
1834 CloseDoor(DOOR_CLOSE_1);
1839 /* after drawing the level, correct some elements */
1840 if (game.timegate_time_left == 0)
1841 CloseAllOpenTimegates();
1843 if (setup.soft_scrolling)
1844 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1846 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1849 /* copy default game door content to main double buffer */
1850 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1851 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1853 DrawGameDoorValues();
1857 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1858 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1859 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1863 /* copy actual game door content to door double buffer for OpenDoor() */
1864 BlitBitmap(drawto, bitmap_db_door,
1865 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1867 OpenDoor(DOOR_OPEN_ALL);
1869 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1871 if (setup.sound_music)
1874 KeyboardAutoRepeatOffUnlessAutoplay();
1878 for (i = 0; i < MAX_PLAYERS; i++)
1879 printf("Player %d %sactive.\n",
1880 i + 1, (stored_player[i].active ? "" : "not "));
1884 printf("::: starting game [%d]\n", FrameCounter);
1888 void InitMovDir(int x, int y)
1890 int i, element = Feld[x][y];
1891 static int xy[4][2] =
1898 static int direction[3][4] =
1900 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1901 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1902 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1911 Feld[x][y] = EL_BUG;
1912 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1915 case EL_SPACESHIP_RIGHT:
1916 case EL_SPACESHIP_UP:
1917 case EL_SPACESHIP_LEFT:
1918 case EL_SPACESHIP_DOWN:
1919 Feld[x][y] = EL_SPACESHIP;
1920 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1923 case EL_BD_BUTTERFLY_RIGHT:
1924 case EL_BD_BUTTERFLY_UP:
1925 case EL_BD_BUTTERFLY_LEFT:
1926 case EL_BD_BUTTERFLY_DOWN:
1927 Feld[x][y] = EL_BD_BUTTERFLY;
1928 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1931 case EL_BD_FIREFLY_RIGHT:
1932 case EL_BD_FIREFLY_UP:
1933 case EL_BD_FIREFLY_LEFT:
1934 case EL_BD_FIREFLY_DOWN:
1935 Feld[x][y] = EL_BD_FIREFLY;
1936 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1939 case EL_PACMAN_RIGHT:
1941 case EL_PACMAN_LEFT:
1942 case EL_PACMAN_DOWN:
1943 Feld[x][y] = EL_PACMAN;
1944 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1947 case EL_SP_SNIKSNAK:
1948 MovDir[x][y] = MV_UP;
1951 case EL_SP_ELECTRON:
1952 MovDir[x][y] = MV_LEFT;
1959 Feld[x][y] = EL_MOLE;
1960 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1964 if (IS_CUSTOM_ELEMENT(element))
1966 struct ElementInfo *ei = &element_info[element];
1967 int move_direction_initial = ei->move_direction_initial;
1968 int move_pattern = ei->move_pattern;
1970 if (move_direction_initial == MV_START_PREVIOUS)
1972 if (MovDir[x][y] != MV_NO_MOVING)
1975 move_direction_initial = MV_START_AUTOMATIC;
1978 if (move_direction_initial == MV_START_RANDOM)
1979 MovDir[x][y] = 1 << RND(4);
1980 else if (move_direction_initial & MV_ANY_DIRECTION)
1981 MovDir[x][y] = move_direction_initial;
1982 else if (move_pattern == MV_ALL_DIRECTIONS ||
1983 move_pattern == MV_TURNING_LEFT ||
1984 move_pattern == MV_TURNING_RIGHT ||
1985 move_pattern == MV_TURNING_LEFT_RIGHT ||
1986 move_pattern == MV_TURNING_RIGHT_LEFT ||
1987 move_pattern == MV_TURNING_RANDOM)
1988 MovDir[x][y] = 1 << RND(4);
1989 else if (move_pattern == MV_HORIZONTAL)
1990 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1991 else if (move_pattern == MV_VERTICAL)
1992 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1993 else if (move_pattern & MV_ANY_DIRECTION)
1994 MovDir[x][y] = element_info[element].move_pattern;
1995 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
1996 move_pattern == MV_ALONG_RIGHT_SIDE)
1998 for (i = 0; i < NUM_DIRECTIONS; i++)
2000 int x1 = x + xy[i][0];
2001 int y1 = y + xy[i][1];
2003 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2005 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2006 MovDir[x][y] = direction[0][i];
2008 MovDir[x][y] = direction[1][i];
2017 MovDir[x][y] = 1 << RND(4);
2019 if (element != EL_BUG &&
2020 element != EL_SPACESHIP &&
2021 element != EL_BD_BUTTERFLY &&
2022 element != EL_BD_FIREFLY)
2025 for (i = 0; i < NUM_DIRECTIONS; i++)
2027 int x1 = x + xy[i][0];
2028 int y1 = y + xy[i][1];
2030 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2032 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2034 MovDir[x][y] = direction[0][i];
2037 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2038 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2040 MovDir[x][y] = direction[1][i];
2049 GfxDir[x][y] = MovDir[x][y];
2052 void InitAmoebaNr(int x, int y)
2055 int group_nr = AmoebeNachbarNr(x, y);
2059 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2061 if (AmoebaCnt[i] == 0)
2069 AmoebaNr[x][y] = group_nr;
2070 AmoebaCnt[group_nr]++;
2071 AmoebaCnt2[group_nr]++;
2077 boolean raise_level = FALSE;
2079 if (local_player->MovPos)
2083 if (tape.auto_play) /* tape might already be stopped here */
2084 tape.auto_play_level_solved = TRUE;
2086 if (tape.playing && tape.auto_play)
2087 tape.auto_play_level_solved = TRUE;
2090 local_player->LevelSolved = FALSE;
2092 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2096 if (!tape.playing && setup.sound_loops)
2097 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2098 SND_CTRL_PLAY_LOOP);
2100 while (TimeLeft > 0)
2102 if (!tape.playing && !setup.sound_loops)
2103 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2104 if (TimeLeft > 0 && !(TimeLeft % 10))
2105 RaiseScore(level.score[SC_TIME_BONUS]);
2106 if (TimeLeft > 100 && !(TimeLeft % 10))
2111 DrawGameValue_Time(TimeLeft);
2119 if (!tape.playing && setup.sound_loops)
2120 StopSound(SND_GAME_LEVELTIME_BONUS);
2122 else if (level.time == 0) /* level without time limit */
2124 if (!tape.playing && setup.sound_loops)
2125 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2126 SND_CTRL_PLAY_LOOP);
2128 while (TimePlayed < 999)
2130 if (!tape.playing && !setup.sound_loops)
2131 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2132 if (TimePlayed < 999 && !(TimePlayed % 10))
2133 RaiseScore(level.score[SC_TIME_BONUS]);
2134 if (TimePlayed < 900 && !(TimePlayed % 10))
2139 DrawGameValue_Time(TimePlayed);
2147 if (!tape.playing && setup.sound_loops)
2148 StopSound(SND_GAME_LEVELTIME_BONUS);
2151 /* close exit door after last player */
2152 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2153 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2155 int element = Feld[ExitX][ExitY];
2157 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2158 EL_SP_EXIT_CLOSING);
2160 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2163 /* Hero disappears */
2164 DrawLevelField(ExitX, ExitY);
2170 CloseDoor(DOOR_CLOSE_1);
2175 SaveTape(tape.level_nr); /* Ask to save tape */
2178 if (level_nr == leveldir_current->handicap_level)
2180 leveldir_current->handicap_level++;
2181 SaveLevelSetup_SeriesInfo();
2184 if (level_editor_test_game)
2185 local_player->score = -1; /* no highscore when playing from editor */
2186 else if (level_nr < leveldir_current->last_level)
2187 raise_level = TRUE; /* advance to next level */
2189 if ((hi_pos = NewHiScore()) >= 0)
2191 game_status = GAME_MODE_SCORES;
2192 DrawHallOfFame(hi_pos);
2201 game_status = GAME_MODE_MAIN;
2218 LoadScore(level_nr);
2220 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2221 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2224 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2226 if (local_player->score > highscore[k].Score)
2228 /* player has made it to the hall of fame */
2230 if (k < MAX_SCORE_ENTRIES - 1)
2232 int m = MAX_SCORE_ENTRIES - 1;
2235 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2236 if (!strcmp(setup.player_name, highscore[l].Name))
2238 if (m == k) /* player's new highscore overwrites his old one */
2242 for (l = m; l > k; l--)
2244 strcpy(highscore[l].Name, highscore[l - 1].Name);
2245 highscore[l].Score = highscore[l - 1].Score;
2252 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2253 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2254 highscore[k].Score = local_player->score;
2260 else if (!strncmp(setup.player_name, highscore[k].Name,
2261 MAX_PLAYER_NAME_LEN))
2262 break; /* player already there with a higher score */
2268 SaveScore(level_nr);
2273 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2275 if (player->GfxAction != action || player->GfxDir != dir)
2278 printf("Player frame reset! (%d => %d, %d => %d)\n",
2279 player->GfxAction, action, player->GfxDir, dir);
2282 player->GfxAction = action;
2283 player->GfxDir = dir;
2285 player->StepFrame = 0;
2289 static void ResetRandomAnimationValue(int x, int y)
2291 GfxRandom[x][y] = INIT_GFX_RANDOM();
2294 static void ResetGfxAnimation(int x, int y)
2297 GfxAction[x][y] = ACTION_DEFAULT;
2298 GfxDir[x][y] = MovDir[x][y];
2301 void InitMovingField(int x, int y, int direction)
2303 int element = Feld[x][y];
2304 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2305 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2309 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2310 ResetGfxAnimation(x, y);
2312 MovDir[newx][newy] = MovDir[x][y] = direction;
2313 GfxDir[x][y] = direction;
2315 if (Feld[newx][newy] == EL_EMPTY)
2316 Feld[newx][newy] = EL_BLOCKED;
2318 if (direction == MV_DOWN && CAN_FALL(element))
2319 GfxAction[x][y] = ACTION_FALLING;
2321 GfxAction[x][y] = ACTION_MOVING;
2323 GfxFrame[newx][newy] = GfxFrame[x][y];
2324 GfxRandom[newx][newy] = GfxRandom[x][y];
2325 GfxAction[newx][newy] = GfxAction[x][y];
2326 GfxDir[newx][newy] = GfxDir[x][y];
2329 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2331 int direction = MovDir[x][y];
2332 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2333 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2339 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2341 int oldx = x, oldy = y;
2342 int direction = MovDir[x][y];
2344 if (direction == MV_LEFT)
2346 else if (direction == MV_RIGHT)
2348 else if (direction == MV_UP)
2350 else if (direction == MV_DOWN)
2353 *comes_from_x = oldx;
2354 *comes_from_y = oldy;
2357 int MovingOrBlocked2Element(int x, int y)
2359 int element = Feld[x][y];
2361 if (element == EL_BLOCKED)
2365 Blocked2Moving(x, y, &oldx, &oldy);
2366 return Feld[oldx][oldy];
2372 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2374 /* like MovingOrBlocked2Element(), but if element is moving
2375 and (x,y) is the field the moving element is just leaving,
2376 return EL_BLOCKED instead of the element value */
2377 int element = Feld[x][y];
2379 if (IS_MOVING(x, y))
2381 if (element == EL_BLOCKED)
2385 Blocked2Moving(x, y, &oldx, &oldy);
2386 return Feld[oldx][oldy];
2395 static void RemoveField(int x, int y)
2397 Feld[x][y] = EL_EMPTY;
2404 ChangeDelay[x][y] = 0;
2405 ChangePage[x][y] = -1;
2406 Pushed[x][y] = FALSE;
2408 GfxElement[x][y] = EL_UNDEFINED;
2409 GfxAction[x][y] = ACTION_DEFAULT;
2410 GfxDir[x][y] = MV_NO_MOVING;
2413 void RemoveMovingField(int x, int y)
2415 int oldx = x, oldy = y, newx = x, newy = y;
2416 int element = Feld[x][y];
2417 int next_element = EL_UNDEFINED;
2419 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2422 if (IS_MOVING(x, y))
2424 Moving2Blocked(x, y, &newx, &newy);
2426 if (Feld[newx][newy] != EL_BLOCKED)
2429 if (Feld[newx][newy] != EL_BLOCKED)
2431 /* element is moving, but target field is not free (blocked), but
2432 already occupied by something different (example: acid pool);
2433 in this case, only remove the moving field, but not the target */
2435 RemoveField(oldx, oldy);
2437 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2439 DrawLevelField(oldx, oldy);
2445 else if (element == EL_BLOCKED)
2447 Blocked2Moving(x, y, &oldx, &oldy);
2448 if (!IS_MOVING(oldx, oldy))
2452 if (element == EL_BLOCKED &&
2453 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2454 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2455 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2456 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2457 next_element = get_next_element(Feld[oldx][oldy]);
2459 RemoveField(oldx, oldy);
2460 RemoveField(newx, newy);
2462 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2464 if (next_element != EL_UNDEFINED)
2465 Feld[oldx][oldy] = next_element;
2467 DrawLevelField(oldx, oldy);
2468 DrawLevelField(newx, newy);
2471 void DrawDynamite(int x, int y)
2473 int sx = SCREENX(x), sy = SCREENY(y);
2474 int graphic = el2img(Feld[x][y]);
2477 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2480 if (IS_WALKABLE_INSIDE(Back[x][y]))
2484 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2485 else if (Store[x][y])
2486 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2488 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2491 if (Back[x][y] || Store[x][y])
2492 DrawGraphicThruMask(sx, sy, graphic, frame);
2494 DrawGraphic(sx, sy, graphic, frame);
2496 if (game.emulation == EMU_SUPAPLEX)
2497 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2498 else if (Store[x][y])
2499 DrawGraphicThruMask(sx, sy, graphic, frame);
2501 DrawGraphic(sx, sy, graphic, frame);
2505 void CheckDynamite(int x, int y)
2507 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2511 if (MovDelay[x][y] != 0)
2514 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2521 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2523 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2524 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2525 StopSound(SND_DYNAMITE_ACTIVE);
2527 StopSound(SND_DYNABOMB_ACTIVE);
2533 void RelocatePlayer(int x, int y, int element_raw)
2535 int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
2536 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2537 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2538 boolean no_delay = (tape.index_search);
2539 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2540 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2542 if (player->GameOver) /* do not reanimate dead player */
2546 RemoveField(x, y); /* temporarily remove newly placed player */
2547 DrawLevelField(x, y);
2550 if (player->present)
2552 while (player->MovPos)
2554 ScrollPlayer(player, SCROLL_GO_ON);
2555 ScrollScreen(NULL, SCROLL_GO_ON);
2561 Delay(wait_delay_value);
2564 DrawPlayer(player); /* needed here only to cleanup last field */
2565 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2567 player->is_moving = FALSE;
2570 Feld[x][y] = element;
2571 InitPlayerField(x, y, element, TRUE);
2573 if (player == local_player)
2575 int scroll_xx = -999, scroll_yy = -999;
2577 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2580 int fx = FX, fy = FY;
2582 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2583 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2584 local_player->jx - MIDPOSX);
2586 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2587 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2588 local_player->jy - MIDPOSY);
2590 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2591 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2596 fx += dx * TILEX / 2;
2597 fy += dy * TILEY / 2;
2599 ScrollLevel(dx, dy);
2602 /* scroll in two steps of half tile size to make things smoother */
2603 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2605 Delay(wait_delay_value);
2607 /* scroll second step to align at full tile size */
2609 Delay(wait_delay_value);
2614 void Explode(int ex, int ey, int phase, int mode)
2621 /* !!! eliminate this variable !!! */
2622 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2627 int last_phase = num_phase * delay;
2628 int half_phase = (num_phase / 2) * delay;
2629 int first_phase_after_start = EX_PHASE_START + 1;
2633 if (game.explosions_delayed)
2635 ExplodeField[ex][ey] = mode;
2639 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2641 int center_element = Feld[ex][ey];
2644 /* --- This is only really needed (and now handled) in "Impact()". --- */
2645 /* do not explode moving elements that left the explode field in time */
2646 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2647 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2651 if (mode == EX_NORMAL || mode == EX_CENTER)
2652 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2654 /* remove things displayed in background while burning dynamite */
2655 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2658 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2660 /* put moving element to center field (and let it explode there) */
2661 center_element = MovingOrBlocked2Element(ex, ey);
2662 RemoveMovingField(ex, ey);
2663 Feld[ex][ey] = center_element;
2667 last_phase = element_info[center_element].explosion_delay;
2670 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2672 int xx = x - ex + 1;
2673 int yy = y - ey + 1;
2677 if (!IN_LEV_FIELD(x, y) || (mode != EX_NORMAL && (x != ex || y != ey)))
2680 if (!IN_LEV_FIELD(x, y) ||
2681 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2682 (x != ex || y != ey)))
2686 element = Feld[x][y];
2688 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2690 element = MovingOrBlocked2Element(x, y);
2692 if (!IS_EXPLOSION_PROOF(element))
2693 RemoveMovingField(x, y);
2699 if (IS_EXPLOSION_PROOF(element))
2702 /* indestructible elements can only explode in center (but not flames) */
2703 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2704 element == EL_FLAMES)
2709 if ((IS_INDESTRUCTIBLE(element) &&
2710 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2711 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2712 element == EL_FLAMES)
2716 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2718 if (IS_ACTIVE_BOMB(element))
2720 /* re-activate things under the bomb like gate or penguin */
2721 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2728 /* save walkable background elements while explosion on same tile */
2730 if (IS_INDESTRUCTIBLE(element))
2731 Back[x][y] = element;
2733 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2734 Back[x][y] = element;
2737 /* ignite explodable elements reached by other explosion */
2738 if (element == EL_EXPLOSION)
2739 element = Store2[x][y];
2742 if (AmoebaNr[x][y] &&
2743 (element == EL_AMOEBA_FULL ||
2744 element == EL_BD_AMOEBA ||
2745 element == EL_AMOEBA_GROWING))
2747 AmoebaCnt[AmoebaNr[x][y]]--;
2748 AmoebaCnt2[AmoebaNr[x][y]]--;
2754 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2756 switch(StorePlayer[ex][ey])
2759 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2762 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2765 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2769 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2773 if (game.emulation == EMU_SUPAPLEX)
2774 Store[x][y] = EL_EMPTY;
2776 else if (center_element == EL_MOLE)
2777 Store[x][y] = EL_EMERALD_RED;
2778 else if (center_element == EL_PENGUIN)
2779 Store[x][y] = EL_EMERALD_PURPLE;
2780 else if (center_element == EL_BUG)
2781 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2782 else if (center_element == EL_BD_BUTTERFLY)
2783 Store[x][y] = EL_BD_DIAMOND;
2784 else if (center_element == EL_SP_ELECTRON)
2785 Store[x][y] = EL_SP_INFOTRON;
2786 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2787 Store[x][y] = level.amoeba_content;
2788 else if (center_element == EL_YAMYAM)
2789 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2790 else if (IS_CUSTOM_ELEMENT(center_element) &&
2791 element_info[center_element].content[xx][yy] != EL_EMPTY)
2792 Store[x][y] = element_info[center_element].content[xx][yy];
2793 else if (element == EL_WALL_EMERALD)
2794 Store[x][y] = EL_EMERALD;
2795 else if (element == EL_WALL_DIAMOND)
2796 Store[x][y] = EL_DIAMOND;
2797 else if (element == EL_WALL_BD_DIAMOND)
2798 Store[x][y] = EL_BD_DIAMOND;
2799 else if (element == EL_WALL_EMERALD_YELLOW)
2800 Store[x][y] = EL_EMERALD_YELLOW;
2801 else if (element == EL_WALL_EMERALD_RED)
2802 Store[x][y] = EL_EMERALD_RED;
2803 else if (element == EL_WALL_EMERALD_PURPLE)
2804 Store[x][y] = EL_EMERALD_PURPLE;
2805 else if (element == EL_WALL_PEARL)
2806 Store[x][y] = EL_PEARL;
2807 else if (element == EL_WALL_CRYSTAL)
2808 Store[x][y] = EL_CRYSTAL;
2809 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2810 Store[x][y] = element_info[element].content[1][1];
2812 Store[x][y] = EL_EMPTY;
2814 if (x != ex || y != ey ||
2815 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2816 Store2[x][y] = element;
2819 if (AmoebaNr[x][y] &&
2820 (element == EL_AMOEBA_FULL ||
2821 element == EL_BD_AMOEBA ||
2822 element == EL_AMOEBA_GROWING))
2824 AmoebaCnt[AmoebaNr[x][y]]--;
2825 AmoebaCnt2[AmoebaNr[x][y]]--;
2831 MovDir[x][y] = MovPos[x][y] = 0;
2832 GfxDir[x][y] = MovDir[x][y];
2837 Feld[x][y] = EL_EXPLOSION;
2839 GfxElement[x][y] = center_element;
2841 GfxElement[x][y] = EL_UNDEFINED;
2844 ExplodePhase[x][y] = 1;
2846 ExplodeDelay[x][y] = last_phase;
2851 if (center_element == EL_YAMYAM)
2852 game.yamyam_content_nr =
2853 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2865 last_phase = ExplodeDelay[x][y];
2868 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2872 /* activate this even in non-DEBUG version until cause for crash in
2873 getGraphicAnimationFrame() (see below) is found and eliminated */
2877 if (GfxElement[x][y] == EL_UNDEFINED)
2880 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2881 printf("Explode(): This should never happen!\n");
2884 GfxElement[x][y] = EL_EMPTY;
2890 border_element = Store2[x][y];
2891 if (IS_PLAYER(x, y))
2892 border_element = StorePlayer[x][y];
2894 if (phase == element_info[border_element].ignition_delay ||
2895 phase == last_phase)
2897 boolean border_explosion = FALSE;
2900 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
2902 if (IS_PLAYER(x, y))
2905 KillHeroUnlessExplosionProtected(x, y);
2906 border_explosion = TRUE;
2909 if (phase == last_phase)
2910 printf("::: IS_PLAYER\n");
2913 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
2915 Feld[x][y] = Store2[x][y];
2918 border_explosion = TRUE;
2921 if (phase == last_phase)
2922 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
2925 else if (border_element == EL_AMOEBA_TO_DIAMOND)
2927 AmoebeUmwandeln(x, y);
2929 border_explosion = TRUE;
2932 if (phase == last_phase)
2933 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
2934 element_info[border_element].explosion_delay,
2935 element_info[border_element].ignition_delay,
2941 /* if an element just explodes due to another explosion (chain-reaction),
2942 do not immediately end the new explosion when it was the last frame of
2943 the explosion (as it would be done in the following "if"-statement!) */
2944 if (border_explosion && phase == last_phase)
2951 if (phase == first_phase_after_start)
2953 int element = Store2[x][y];
2955 if (element == EL_BLACK_ORB)
2957 Feld[x][y] = Store2[x][y];
2962 else if (phase == half_phase)
2964 int element = Store2[x][y];
2966 if (IS_PLAYER(x, y))
2967 KillHeroUnlessExplosionProtected(x, y);
2968 else if (CAN_EXPLODE_BY_EXPLOSION(element))
2970 Feld[x][y] = Store2[x][y];
2974 else if (element == EL_AMOEBA_TO_DIAMOND)
2975 AmoebeUmwandeln(x, y);
2979 if (phase == last_phase)
2983 element = Feld[x][y] = Store[x][y];
2984 Store[x][y] = Store2[x][y] = 0;
2985 GfxElement[x][y] = EL_UNDEFINED;
2987 /* player can escape from explosions and might therefore be still alive */
2988 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2989 element <= EL_PLAYER_IS_EXPLODING_4)
2990 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2992 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2993 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2994 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2997 /* restore probably existing indestructible background element */
2998 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2999 element = Feld[x][y] = Back[x][y];
3002 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3003 GfxDir[x][y] = MV_NO_MOVING;
3004 ChangeDelay[x][y] = 0;
3005 ChangePage[x][y] = -1;
3008 InitField_WithBug2(x, y, FALSE);
3010 InitField(x, y, FALSE);
3012 /* !!! not needed !!! */
3014 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
3015 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3018 if (CAN_MOVE(element))
3023 DrawLevelField(x, y);
3025 TestIfElementTouchesCustomElement(x, y);
3027 if (GFX_CRUMBLED(element))
3028 DrawLevelFieldCrumbledSandNeighbours(x, y);
3030 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3031 StorePlayer[x][y] = 0;
3033 if (ELEM_IS_PLAYER(element))
3034 RelocatePlayer(x, y, element);
3037 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3039 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3043 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3045 int stored = Store[x][y];
3046 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3047 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3050 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3053 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3054 element_info[GfxElement[x][y]].token_name,
3059 DrawLevelFieldCrumbledSand(x, y);
3061 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3063 DrawLevelElement(x, y, Back[x][y]);
3064 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3066 else if (IS_WALKABLE_UNDER(Back[x][y]))
3068 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3069 DrawLevelElementThruMask(x, y, Back[x][y]);
3071 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3072 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3076 void DynaExplode(int ex, int ey)
3079 int dynabomb_element = Feld[ex][ey];
3080 int dynabomb_size = 1;
3081 boolean dynabomb_xl = FALSE;
3082 struct PlayerInfo *player;
3083 static int xy[4][2] =
3091 if (IS_ACTIVE_BOMB(dynabomb_element))
3093 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3094 dynabomb_size = player->dynabomb_size;
3095 dynabomb_xl = player->dynabomb_xl;
3096 player->dynabombs_left++;
3099 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
3101 for (i = 0; i < NUM_DIRECTIONS; i++)
3103 for (j = 1; j <= dynabomb_size; j++)
3105 int x = ex + j * xy[i][0];
3106 int y = ey + j * xy[i][1];
3109 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3112 element = Feld[x][y];
3114 /* do not restart explosions of fields with active bombs */
3115 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3118 Explode(x, y, EX_PHASE_START, EX_BORDER);
3120 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3121 if (element != EL_EMPTY &&
3122 element != EL_SAND &&
3123 element != EL_EXPLOSION &&
3130 void Bang(int x, int y)
3133 int element = MovingOrBlocked2Element(x, y);
3135 int element = Feld[x][y];
3139 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3141 if (IS_PLAYER(x, y))
3144 struct PlayerInfo *player = PLAYERINFO(x, y);
3146 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3147 player->element_nr);
3152 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3154 if (game.emulation == EMU_SUPAPLEX)
3155 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3157 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3162 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3170 case EL_BD_BUTTERFLY:
3173 case EL_DARK_YAMYAM:
3177 RaiseScoreElement(element);
3178 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3180 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3181 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3182 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3183 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3184 case EL_DYNABOMB_INCREASE_NUMBER:
3185 case EL_DYNABOMB_INCREASE_SIZE:
3186 case EL_DYNABOMB_INCREASE_POWER:
3191 case EL_LAMP_ACTIVE:
3193 case EL_AMOEBA_TO_DIAMOND:
3195 if (IS_PLAYER(x, y))
3196 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3198 Explode(x, y, EX_PHASE_START, EX_CENTER);
3201 if (CAN_EXPLODE_DYNA(element))
3203 else if (CAN_EXPLODE_1X1(element))
3204 Explode(x, y, EX_PHASE_START, EX_CENTER);
3206 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3210 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3213 void SplashAcid(int x, int y)
3216 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3217 (!IN_LEV_FIELD(x - 1, y - 2) ||
3218 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3219 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3221 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3222 (!IN_LEV_FIELD(x + 1, y - 2) ||
3223 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3224 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3226 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3228 /* input: position of element entering acid (obsolete) */
3230 int element = Feld[x][y];
3232 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3235 if (element != EL_ACID_SPLASH_LEFT &&
3236 element != EL_ACID_SPLASH_RIGHT)
3238 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3240 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3241 (!IN_LEV_FIELD(x - 1, y - 1) ||
3242 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3243 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3245 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3246 (!IN_LEV_FIELD(x + 1, y - 1) ||
3247 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3248 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3253 static void InitBeltMovement()
3255 static int belt_base_element[4] =
3257 EL_CONVEYOR_BELT_1_LEFT,
3258 EL_CONVEYOR_BELT_2_LEFT,
3259 EL_CONVEYOR_BELT_3_LEFT,
3260 EL_CONVEYOR_BELT_4_LEFT
3262 static int belt_base_active_element[4] =
3264 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3265 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3266 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3267 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3272 /* set frame order for belt animation graphic according to belt direction */
3273 for (i = 0; i < NUM_BELTS; i++)
3277 for (j = 0; j < NUM_BELT_PARTS; j++)
3279 int element = belt_base_active_element[belt_nr] + j;
3280 int graphic = el2img(element);
3282 if (game.belt_dir[i] == MV_LEFT)
3283 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3285 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3289 for (y = 0; y < lev_fieldy; y++)
3291 for (x = 0; x < lev_fieldx; x++)
3293 int element = Feld[x][y];
3295 for (i = 0; i < NUM_BELTS; i++)
3297 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3299 int e_belt_nr = getBeltNrFromBeltElement(element);
3302 if (e_belt_nr == belt_nr)
3304 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3306 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3314 static void ToggleBeltSwitch(int x, int y)
3316 static int belt_base_element[4] =
3318 EL_CONVEYOR_BELT_1_LEFT,
3319 EL_CONVEYOR_BELT_2_LEFT,
3320 EL_CONVEYOR_BELT_3_LEFT,
3321 EL_CONVEYOR_BELT_4_LEFT
3323 static int belt_base_active_element[4] =
3325 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3326 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3327 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3328 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3330 static int belt_base_switch_element[4] =
3332 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3333 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3334 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3335 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3337 static int belt_move_dir[4] =
3345 int element = Feld[x][y];
3346 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3347 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3348 int belt_dir = belt_move_dir[belt_dir_nr];
3351 if (!IS_BELT_SWITCH(element))
3354 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3355 game.belt_dir[belt_nr] = belt_dir;
3357 if (belt_dir_nr == 3)
3360 /* set frame order for belt animation graphic according to belt direction */
3361 for (i = 0; i < NUM_BELT_PARTS; i++)
3363 int element = belt_base_active_element[belt_nr] + i;
3364 int graphic = el2img(element);
3366 if (belt_dir == MV_LEFT)
3367 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3369 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3372 for (yy = 0; yy < lev_fieldy; yy++)
3374 for (xx = 0; xx < lev_fieldx; xx++)
3376 int element = Feld[xx][yy];
3378 if (IS_BELT_SWITCH(element))
3380 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3382 if (e_belt_nr == belt_nr)
3384 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3385 DrawLevelField(xx, yy);
3388 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3390 int e_belt_nr = getBeltNrFromBeltElement(element);
3392 if (e_belt_nr == belt_nr)
3394 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3396 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3397 DrawLevelField(xx, yy);
3400 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3402 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3404 if (e_belt_nr == belt_nr)
3406 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3408 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3409 DrawLevelField(xx, yy);
3416 static void ToggleSwitchgateSwitch(int x, int y)
3420 game.switchgate_pos = !game.switchgate_pos;
3422 for (yy = 0; yy < lev_fieldy; yy++)
3424 for (xx = 0; xx < lev_fieldx; xx++)
3426 int element = Feld[xx][yy];
3428 if (element == EL_SWITCHGATE_SWITCH_UP ||
3429 element == EL_SWITCHGATE_SWITCH_DOWN)
3431 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3432 DrawLevelField(xx, yy);
3434 else if (element == EL_SWITCHGATE_OPEN ||
3435 element == EL_SWITCHGATE_OPENING)
3437 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3439 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3441 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3444 else if (element == EL_SWITCHGATE_CLOSED ||
3445 element == EL_SWITCHGATE_CLOSING)
3447 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3449 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3451 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3458 static int getInvisibleActiveFromInvisibleElement(int element)
3460 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3461 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3462 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3466 static int getInvisibleFromInvisibleActiveElement(int element)
3468 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3469 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3470 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3474 static void RedrawAllLightSwitchesAndInvisibleElements()
3478 for (y = 0; y < lev_fieldy; y++)
3480 for (x = 0; x < lev_fieldx; x++)
3482 int element = Feld[x][y];
3484 if (element == EL_LIGHT_SWITCH &&
3485 game.light_time_left > 0)
3487 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3488 DrawLevelField(x, y);
3490 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3491 game.light_time_left == 0)
3493 Feld[x][y] = EL_LIGHT_SWITCH;
3494 DrawLevelField(x, y);
3496 else if (element == EL_INVISIBLE_STEELWALL ||
3497 element == EL_INVISIBLE_WALL ||
3498 element == EL_INVISIBLE_SAND)
3500 if (game.light_time_left > 0)
3501 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3503 DrawLevelField(x, y);
3505 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3506 element == EL_INVISIBLE_WALL_ACTIVE ||
3507 element == EL_INVISIBLE_SAND_ACTIVE)
3509 if (game.light_time_left == 0)
3510 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3512 DrawLevelField(x, y);
3518 static void ToggleLightSwitch(int x, int y)
3520 int element = Feld[x][y];
3522 game.light_time_left =
3523 (element == EL_LIGHT_SWITCH ?
3524 level.time_light * FRAMES_PER_SECOND : 0);
3526 RedrawAllLightSwitchesAndInvisibleElements();
3529 static void ActivateTimegateSwitch(int x, int y)
3533 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3535 for (yy = 0; yy < lev_fieldy; yy++)
3537 for (xx = 0; xx < lev_fieldx; xx++)
3539 int element = Feld[xx][yy];
3541 if (element == EL_TIMEGATE_CLOSED ||
3542 element == EL_TIMEGATE_CLOSING)
3544 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3545 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3549 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3551 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3552 DrawLevelField(xx, yy);
3559 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3562 inline static int getElementMoveStepsize(int x, int y)
3564 int element = Feld[x][y];
3565 int direction = MovDir[x][y];
3566 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3567 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3568 int horiz_move = (dx != 0);
3569 int sign = (horiz_move ? dx : dy);
3570 int step = sign * element_info[element].move_stepsize;
3572 /* special values for move stepsize for spring and things on conveyor belt */
3576 if (element == EL_SPRING)
3577 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3578 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3579 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3580 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3582 if (CAN_FALL(element) &&
3583 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3584 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3585 else if (element == EL_SPRING)
3586 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3593 void Impact(int x, int y)
3595 boolean lastline = (y == lev_fieldy-1);
3596 boolean object_hit = FALSE;
3597 boolean impact = (lastline || object_hit);
3598 int element = Feld[x][y];
3599 int smashed = EL_UNDEFINED;
3601 if (!lastline) /* check if element below was hit */
3603 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3606 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3607 MovDir[x][y + 1] != MV_DOWN ||
3608 MovPos[x][y + 1] <= TILEY / 2));
3611 object_hit = !IS_FREE(x, y + 1);
3614 /* do not smash moving elements that left the smashed field in time */
3615 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3616 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3620 smashed = MovingOrBlocked2Element(x, y + 1);
3622 impact = (lastline || object_hit);
3625 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3627 SplashAcid(x, y + 1);
3631 /* only reset graphic animation if graphic really changes after impact */
3633 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3635 ResetGfxAnimation(x, y);
3636 DrawLevelField(x, y);
3639 if (impact && CAN_EXPLODE_IMPACT(element))
3644 else if (impact && element == EL_PEARL)
3646 Feld[x][y] = EL_PEARL_BREAKING;
3647 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3650 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3652 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3657 if (impact && element == EL_AMOEBA_DROP)
3659 if (object_hit && IS_PLAYER(x, y + 1))
3660 KillHeroUnlessEnemyProtected(x, y + 1);
3661 else if (object_hit && smashed == EL_PENGUIN)
3665 Feld[x][y] = EL_AMOEBA_GROWING;
3666 Store[x][y] = EL_AMOEBA_WET;
3668 ResetRandomAnimationValue(x, y);
3673 if (object_hit) /* check which object was hit */
3675 if (CAN_PASS_MAGIC_WALL(element) &&
3676 (smashed == EL_MAGIC_WALL ||
3677 smashed == EL_BD_MAGIC_WALL))
3680 int activated_magic_wall =
3681 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3682 EL_BD_MAGIC_WALL_ACTIVE);
3684 /* activate magic wall / mill */
3685 for (yy = 0; yy < lev_fieldy; yy++)
3686 for (xx = 0; xx < lev_fieldx; xx++)
3687 if (Feld[xx][yy] == smashed)
3688 Feld[xx][yy] = activated_magic_wall;
3690 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3691 game.magic_wall_active = TRUE;
3693 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3694 SND_MAGIC_WALL_ACTIVATING :
3695 SND_BD_MAGIC_WALL_ACTIVATING));
3698 if (IS_PLAYER(x, y + 1))
3700 if (CAN_SMASH_PLAYER(element))
3702 KillHeroUnlessEnemyProtected(x, y + 1);
3706 else if (smashed == EL_PENGUIN)
3708 if (CAN_SMASH_PLAYER(element))
3714 else if (element == EL_BD_DIAMOND)
3716 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3722 else if (((element == EL_SP_INFOTRON ||
3723 element == EL_SP_ZONK) &&
3724 (smashed == EL_SP_SNIKSNAK ||
3725 smashed == EL_SP_ELECTRON ||
3726 smashed == EL_SP_DISK_ORANGE)) ||
3727 (element == EL_SP_INFOTRON &&
3728 smashed == EL_SP_DISK_YELLOW))
3734 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3740 else if (CAN_SMASH_EVERYTHING(element))
3742 if (IS_CLASSIC_ENEMY(smashed) ||
3743 CAN_EXPLODE_SMASHED(smashed))
3748 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3750 if (smashed == EL_LAMP ||
3751 smashed == EL_LAMP_ACTIVE)
3756 else if (smashed == EL_NUT)
3758 Feld[x][y + 1] = EL_NUT_BREAKING;
3759 PlayLevelSound(x, y, SND_NUT_BREAKING);
3760 RaiseScoreElement(EL_NUT);
3763 else if (smashed == EL_PEARL)
3765 Feld[x][y + 1] = EL_PEARL_BREAKING;
3766 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3769 else if (smashed == EL_DIAMOND)
3771 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3772 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3775 else if (IS_BELT_SWITCH(smashed))
3777 ToggleBeltSwitch(x, y + 1);
3779 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3780 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3782 ToggleSwitchgateSwitch(x, y + 1);
3784 else if (smashed == EL_LIGHT_SWITCH ||
3785 smashed == EL_LIGHT_SWITCH_ACTIVE)
3787 ToggleLightSwitch(x, y + 1);
3791 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3793 CheckTriggeredElementChangeSide(x, y + 1, smashed,
3794 CE_OTHER_IS_SWITCHING, CH_SIDE_TOP);
3795 CheckElementChangeSide(x, y + 1, smashed, CE_SWITCHED, CH_SIDE_TOP);
3800 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3805 /* play sound of magic wall / mill */
3807 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3808 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3810 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3811 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3812 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3813 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3818 /* play sound of object that hits the ground */
3819 if (lastline || object_hit)
3820 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3823 inline static void TurnRoundExt(int x, int y)
3835 { 0, 0 }, { 0, 0 }, { 0, 0 },
3840 int left, right, back;
3844 { MV_DOWN, MV_UP, MV_RIGHT },
3845 { MV_UP, MV_DOWN, MV_LEFT },
3847 { MV_LEFT, MV_RIGHT, MV_DOWN },
3851 { MV_RIGHT, MV_LEFT, MV_UP }
3854 int element = Feld[x][y];
3855 int move_pattern = element_info[element].move_pattern;
3857 int old_move_dir = MovDir[x][y];
3858 int left_dir = turn[old_move_dir].left;
3859 int right_dir = turn[old_move_dir].right;
3860 int back_dir = turn[old_move_dir].back;
3862 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3863 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3864 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3865 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3867 int left_x = x + left_dx, left_y = y + left_dy;
3868 int right_x = x + right_dx, right_y = y + right_dy;
3869 int move_x = x + move_dx, move_y = y + move_dy;
3873 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3875 TestIfBadThingTouchesOtherBadThing(x, y);
3877 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
3878 MovDir[x][y] = right_dir;
3879 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3880 MovDir[x][y] = left_dir;
3882 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3884 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3887 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3888 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3890 TestIfBadThingTouchesOtherBadThing(x, y);
3892 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
3893 MovDir[x][y] = left_dir;
3894 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3895 MovDir[x][y] = right_dir;
3897 if ((element == EL_SPACESHIP ||
3898 element == EL_SP_SNIKSNAK ||
3899 element == EL_SP_ELECTRON)
3900 && MovDir[x][y] != old_move_dir)
3902 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3905 else if (element == EL_YAMYAM)
3907 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3908 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3910 if (can_turn_left && can_turn_right)
3911 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3912 else if (can_turn_left)
3913 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3914 else if (can_turn_right)
3915 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3917 MovDir[x][y] = back_dir;
3919 MovDelay[x][y] = 16 + 16 * RND(3);
3921 else if (element == EL_DARK_YAMYAM)
3923 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3924 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3926 if (can_turn_left && can_turn_right)
3927 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3928 else if (can_turn_left)
3929 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3930 else if (can_turn_right)
3931 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3933 MovDir[x][y] = back_dir;
3935 MovDelay[x][y] = 16 + 16 * RND(3);
3937 else if (element == EL_PACMAN)
3939 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3940 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3942 if (can_turn_left && can_turn_right)
3943 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3944 else if (can_turn_left)
3945 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3946 else if (can_turn_right)
3947 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3949 MovDir[x][y] = back_dir;
3951 MovDelay[x][y] = 6 + RND(40);
3953 else if (element == EL_PIG)
3955 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3956 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3957 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3958 boolean should_turn_left, should_turn_right, should_move_on;
3960 int rnd = RND(rnd_value);
3962 should_turn_left = (can_turn_left &&
3964 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3965 y + back_dy + left_dy)));
3966 should_turn_right = (can_turn_right &&
3968 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3969 y + back_dy + right_dy)));
3970 should_move_on = (can_move_on &&
3973 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3974 y + move_dy + left_dy) ||
3975 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3976 y + move_dy + right_dy)));
3978 if (should_turn_left || should_turn_right || should_move_on)
3980 if (should_turn_left && should_turn_right && should_move_on)
3981 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3982 rnd < 2 * rnd_value / 3 ? right_dir :
3984 else if (should_turn_left && should_turn_right)
3985 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3986 else if (should_turn_left && should_move_on)
3987 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3988 else if (should_turn_right && should_move_on)
3989 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3990 else if (should_turn_left)
3991 MovDir[x][y] = left_dir;
3992 else if (should_turn_right)
3993 MovDir[x][y] = right_dir;
3994 else if (should_move_on)
3995 MovDir[x][y] = old_move_dir;
3997 else if (can_move_on && rnd > rnd_value / 8)
3998 MovDir[x][y] = old_move_dir;
3999 else if (can_turn_left && can_turn_right)
4000 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4001 else if (can_turn_left && rnd > rnd_value / 8)
4002 MovDir[x][y] = left_dir;
4003 else if (can_turn_right && rnd > rnd_value/8)
4004 MovDir[x][y] = right_dir;
4006 MovDir[x][y] = back_dir;
4008 xx = x + move_xy[MovDir[x][y]].x;
4009 yy = y + move_xy[MovDir[x][y]].y;
4011 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4012 MovDir[x][y] = old_move_dir;
4016 else if (element == EL_DRAGON)
4018 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(left_x, left_y);
4019 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(right_x, right_y);
4020 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(move_x, move_y);
4022 int rnd = RND(rnd_value);
4025 if (FrameCounter < 1 && x == 0 && y == 29)
4026 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4029 if (can_move_on && rnd > rnd_value / 8)
4030 MovDir[x][y] = old_move_dir;
4031 else if (can_turn_left && can_turn_right)
4032 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4033 else if (can_turn_left && rnd > rnd_value / 8)
4034 MovDir[x][y] = left_dir;
4035 else if (can_turn_right && rnd > rnd_value / 8)
4036 MovDir[x][y] = right_dir;
4038 MovDir[x][y] = back_dir;
4040 xx = x + move_xy[MovDir[x][y]].x;
4041 yy = y + move_xy[MovDir[x][y]].y;
4044 if (FrameCounter < 1 && x == 0 && y == 29)
4045 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4046 xx, yy, Feld[xx][yy],
4051 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4052 MovDir[x][y] = old_move_dir;
4054 if (!IS_FREE(xx, yy))
4055 MovDir[x][y] = old_move_dir;
4059 if (FrameCounter < 1 && x == 0 && y == 29)
4060 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4065 else if (element == EL_MOLE)
4067 boolean can_move_on =
4068 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
4069 IS_AMOEBOID(Feld[move_x][move_y]) ||
4070 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4073 boolean can_turn_left =
4074 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
4075 IS_AMOEBOID(Feld[left_x][left_y])));
4077 boolean can_turn_right =
4078 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
4079 IS_AMOEBOID(Feld[right_x][right_y])));
4081 if (can_turn_left && can_turn_right)
4082 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4083 else if (can_turn_left)
4084 MovDir[x][y] = left_dir;
4086 MovDir[x][y] = right_dir;
4089 if (MovDir[x][y] != old_move_dir)
4092 else if (element == EL_BALLOON)
4094 MovDir[x][y] = game.balloon_dir;
4097 else if (element == EL_SPRING)
4100 if (MovDir[x][y] & MV_HORIZONTAL &&
4101 !SPRING_CAN_ENTER_FIELD(move_x, move_y))
4102 MovDir[x][y] = MV_NO_MOVING;
4104 if (MovDir[x][y] & MV_HORIZONTAL &&
4105 (!SPRING_CAN_ENTER_FIELD(move_x, move_y) ||
4106 SPRING_CAN_ENTER_FIELD(x, y + 1)))
4107 MovDir[x][y] = MV_NO_MOVING;
4112 else if (element == EL_ROBOT ||
4113 element == EL_SATELLITE ||
4114 element == EL_PENGUIN)
4116 int attr_x = -1, attr_y = -1;
4127 for (i = 0; i < MAX_PLAYERS; i++)
4129 struct PlayerInfo *player = &stored_player[i];
4130 int jx = player->jx, jy = player->jy;
4132 if (!player->active)
4136 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4144 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4150 if (element == EL_PENGUIN)
4153 static int xy[4][2] =
4161 for (i = 0; i < NUM_DIRECTIONS; i++)
4163 int ex = x + xy[i][0];
4164 int ey = y + xy[i][1];
4166 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4175 MovDir[x][y] = MV_NO_MOVING;
4177 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4178 else if (attr_x > x)
4179 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4181 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4182 else if (attr_y > y)
4183 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4185 if (element == EL_ROBOT)
4189 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4190 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4191 Moving2Blocked(x, y, &newx, &newy);
4193 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4194 MovDelay[x][y] = 8 + 8 * !RND(3);
4196 MovDelay[x][y] = 16;
4198 else if (element == EL_PENGUIN)
4204 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4206 boolean first_horiz = RND(2);
4207 int new_move_dir = MovDir[x][y];
4210 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4211 Moving2Blocked(x, y, &newx, &newy);
4213 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4217 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4218 Moving2Blocked(x, y, &newx, &newy);
4220 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4223 MovDir[x][y] = old_move_dir;
4227 else /* (element == EL_SATELLITE) */
4233 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4235 boolean first_horiz = RND(2);
4236 int new_move_dir = MovDir[x][y];
4239 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4240 Moving2Blocked(x, y, &newx, &newy);
4242 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4246 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4247 Moving2Blocked(x, y, &newx, &newy);
4249 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4252 MovDir[x][y] = old_move_dir;
4257 else if (move_pattern == MV_TURNING_LEFT ||
4258 move_pattern == MV_TURNING_RIGHT ||
4259 move_pattern == MV_TURNING_LEFT_RIGHT ||
4260 move_pattern == MV_TURNING_RIGHT_LEFT ||
4261 move_pattern == MV_TURNING_RANDOM ||
4262 move_pattern == MV_ALL_DIRECTIONS)
4264 boolean can_turn_left =
4265 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4266 boolean can_turn_right =
4267 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4269 if (move_pattern == MV_TURNING_LEFT)
4270 MovDir[x][y] = left_dir;
4271 else if (move_pattern == MV_TURNING_RIGHT)
4272 MovDir[x][y] = right_dir;
4273 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4274 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4275 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4276 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4277 else if (move_pattern == MV_TURNING_RANDOM)
4278 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4279 can_turn_right && !can_turn_left ? right_dir :
4280 RND(2) ? left_dir : right_dir);
4281 else if (can_turn_left && can_turn_right)
4282 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4283 else if (can_turn_left)
4284 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4285 else if (can_turn_right)
4286 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4288 MovDir[x][y] = back_dir;
4290 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4292 else if (move_pattern == MV_HORIZONTAL ||
4293 move_pattern == MV_VERTICAL)
4295 if (move_pattern & old_move_dir)
4296 MovDir[x][y] = back_dir;
4297 else if (move_pattern == MV_HORIZONTAL)
4298 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4299 else if (move_pattern == MV_VERTICAL)
4300 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4302 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4304 else if (move_pattern & MV_ANY_DIRECTION)
4306 MovDir[x][y] = move_pattern;
4307 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4309 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4311 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4312 MovDir[x][y] = left_dir;
4313 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4314 MovDir[x][y] = right_dir;
4316 if (MovDir[x][y] != old_move_dir)
4317 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4319 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4321 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4322 MovDir[x][y] = right_dir;
4323 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4324 MovDir[x][y] = left_dir;
4326 if (MovDir[x][y] != old_move_dir)
4327 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4329 else if (move_pattern == MV_TOWARDS_PLAYER ||
4330 move_pattern == MV_AWAY_FROM_PLAYER)
4332 int attr_x = -1, attr_y = -1;
4334 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4345 for (i = 0; i < MAX_PLAYERS; i++)
4347 struct PlayerInfo *player = &stored_player[i];
4348 int jx = player->jx, jy = player->jy;
4350 if (!player->active)
4354 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4362 MovDir[x][y] = MV_NO_MOVING;
4364 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4365 else if (attr_x > x)
4366 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4368 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4369 else if (attr_y > y)
4370 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4372 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4374 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4376 boolean first_horiz = RND(2);
4377 int new_move_dir = MovDir[x][y];
4380 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4381 Moving2Blocked(x, y, &newx, &newy);
4383 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4387 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4388 Moving2Blocked(x, y, &newx, &newy);
4390 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4393 MovDir[x][y] = old_move_dir;
4396 else if (move_pattern == MV_WHEN_PUSHED ||
4397 move_pattern == MV_WHEN_DROPPED)
4399 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4400 MovDir[x][y] = MV_NO_MOVING;
4404 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4406 static int test_xy[7][2] =
4416 static int test_dir[7] =
4426 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4427 int move_preference = -1000000; /* start with very low preference */
4428 int new_move_dir = MV_NO_MOVING;
4429 int start_test = RND(4);
4432 for (i = 0; i < NUM_DIRECTIONS; i++)
4434 int move_dir = test_dir[start_test + i];
4435 int move_dir_preference;
4437 xx = x + test_xy[start_test + i][0];
4438 yy = y + test_xy[start_test + i][1];
4440 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4441 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4443 new_move_dir = move_dir;
4448 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4451 move_dir_preference = -1 * RunnerVisit[xx][yy];
4452 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4453 move_dir_preference = PlayerVisit[xx][yy];
4455 if (move_dir_preference > move_preference)
4457 /* prefer field that has not been visited for the longest time */
4458 move_preference = move_dir_preference;
4459 new_move_dir = move_dir;
4461 else if (move_dir_preference == move_preference &&
4462 move_dir == old_move_dir)
4464 /* prefer last direction when all directions are preferred equally */
4465 move_preference = move_dir_preference;
4466 new_move_dir = move_dir;
4470 MovDir[x][y] = new_move_dir;
4471 if (old_move_dir != new_move_dir)
4476 static void TurnRound(int x, int y)
4478 int direction = MovDir[x][y];
4481 GfxDir[x][y] = MovDir[x][y];
4487 GfxDir[x][y] = MovDir[x][y];
4490 if (direction != MovDir[x][y])
4495 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4498 GfxAction[x][y] = ACTION_WAITING;
4502 static boolean JustBeingPushed(int x, int y)
4506 for (i = 0; i < MAX_PLAYERS; i++)
4508 struct PlayerInfo *player = &stored_player[i];
4510 if (player->active && player->is_pushing && player->MovPos)
4512 int next_jx = player->jx + (player->jx - player->last_jx);
4513 int next_jy = player->jy + (player->jy - player->last_jy);
4515 if (x == next_jx && y == next_jy)
4523 void StartMoving(int x, int y)
4526 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4528 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4529 int element = Feld[x][y];
4535 if (MovDelay[x][y] == 0)
4536 GfxAction[x][y] = ACTION_DEFAULT;
4538 /* !!! this should be handled more generic (not only for mole) !!! */
4539 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4540 GfxAction[x][y] = ACTION_DEFAULT;
4543 if (CAN_FALL(element) && y < lev_fieldy - 1)
4545 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4546 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4547 if (JustBeingPushed(x, y))
4550 if (element == EL_QUICKSAND_FULL)
4552 if (IS_FREE(x, y + 1))
4554 InitMovingField(x, y, MV_DOWN);
4555 started_moving = TRUE;
4557 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4558 Store[x][y] = EL_ROCK;
4560 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4562 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4565 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4567 if (!MovDelay[x][y])
4568 MovDelay[x][y] = TILEY + 1;
4577 Feld[x][y] = EL_QUICKSAND_EMPTY;
4578 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4579 Store[x][y + 1] = Store[x][y];
4582 PlayLevelSoundAction(x, y, ACTION_FILLING);
4584 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4588 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4589 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4591 InitMovingField(x, y, MV_DOWN);
4592 started_moving = TRUE;
4594 Feld[x][y] = EL_QUICKSAND_FILLING;
4595 Store[x][y] = element;
4597 PlayLevelSoundAction(x, y, ACTION_FILLING);
4599 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4602 else if (element == EL_MAGIC_WALL_FULL)
4604 if (IS_FREE(x, y + 1))
4606 InitMovingField(x, y, MV_DOWN);
4607 started_moving = TRUE;
4609 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4610 Store[x][y] = EL_CHANGED(Store[x][y]);
4612 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4614 if (!MovDelay[x][y])
4615 MovDelay[x][y] = TILEY/4 + 1;
4624 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4625 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4626 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4630 else if (element == EL_BD_MAGIC_WALL_FULL)
4632 if (IS_FREE(x, y + 1))
4634 InitMovingField(x, y, MV_DOWN);
4635 started_moving = TRUE;
4637 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4638 Store[x][y] = EL_CHANGED2(Store[x][y]);
4640 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4642 if (!MovDelay[x][y])
4643 MovDelay[x][y] = TILEY/4 + 1;
4652 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4653 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4654 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4658 else if (CAN_PASS_MAGIC_WALL(element) &&
4659 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4660 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4662 InitMovingField(x, y, MV_DOWN);
4663 started_moving = TRUE;
4666 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4667 EL_BD_MAGIC_WALL_FILLING);
4668 Store[x][y] = element;
4671 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4673 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4676 SplashAcid(x, y + 1);
4678 InitMovingField(x, y, MV_DOWN);
4679 started_moving = TRUE;
4681 Store[x][y] = EL_ACID;
4683 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4684 GfxAction[x][y + 1] = ACTION_ACTIVE;
4688 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4689 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4690 (Feld[x][y + 1] == EL_BLOCKED)) ||
4691 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4692 CAN_SMASH(element) && WasJustFalling[x][y] &&
4693 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4697 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4698 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4699 WasJustMoving[x][y] && !Pushed[x][y + 1])
4701 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4702 WasJustMoving[x][y])
4707 /* this is needed for a special case not covered by calling "Impact()"
4708 from "ContinueMoving()": if an element moves to a tile directly below
4709 another element which was just falling on that tile (which was empty
4710 in the previous frame), the falling element above would just stop
4711 instead of smashing the element below (in previous version, the above
4712 element was just checked for "moving" instead of "falling", resulting
4713 in incorrect smashes caused by horizontal movement of the above
4714 element; also, the case of the player being the element to smash was
4715 simply not covered here... :-/ ) */
4718 WasJustMoving[x][y] = 0;
4719 WasJustFalling[x][y] = 0;
4724 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4726 if (MovDir[x][y] == MV_NO_MOVING)
4728 InitMovingField(x, y, MV_DOWN);
4729 started_moving = TRUE;
4732 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4734 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4735 MovDir[x][y] = MV_DOWN;
4737 InitMovingField(x, y, MV_DOWN);
4738 started_moving = TRUE;
4740 else if (element == EL_AMOEBA_DROP)
4742 Feld[x][y] = EL_AMOEBA_GROWING;
4743 Store[x][y] = EL_AMOEBA_WET;
4745 /* Store[x][y + 1] must be zero, because:
4746 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4749 #if OLD_GAME_BEHAVIOUR
4750 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4752 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4753 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4754 element != EL_DX_SUPABOMB)
4757 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4758 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4759 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4760 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4763 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4764 (IS_FREE(x - 1, y + 1) ||
4765 Feld[x - 1][y + 1] == EL_ACID));
4766 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4767 (IS_FREE(x + 1, y + 1) ||
4768 Feld[x + 1][y + 1] == EL_ACID));
4769 boolean can_fall_any = (can_fall_left || can_fall_right);
4770 boolean can_fall_both = (can_fall_left && can_fall_right);
4772 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4774 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4776 if (slippery_type == SLIPPERY_ONLY_LEFT)
4777 can_fall_right = FALSE;
4778 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4779 can_fall_left = FALSE;
4780 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4781 can_fall_right = FALSE;
4782 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4783 can_fall_left = FALSE;
4785 can_fall_any = (can_fall_left || can_fall_right);
4786 can_fall_both = (can_fall_left && can_fall_right);
4791 if (can_fall_both &&
4792 (game.emulation != EMU_BOULDERDASH &&
4793 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4794 can_fall_left = !(can_fall_right = RND(2));
4796 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4797 started_moving = TRUE;
4801 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4803 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4806 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4807 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4808 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4809 int belt_dir = game.belt_dir[belt_nr];
4811 if ((belt_dir == MV_LEFT && left_is_free) ||
4812 (belt_dir == MV_RIGHT && right_is_free))
4815 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4818 InitMovingField(x, y, belt_dir);
4819 started_moving = TRUE;
4822 Pushed[x][y] = TRUE;
4823 Pushed[nextx][y] = TRUE;
4826 GfxAction[x][y] = ACTION_DEFAULT;
4830 MovDir[x][y] = 0; /* if element was moving, stop it */
4835 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4836 if (CAN_MOVE(element) && !started_moving)
4838 int move_pattern = element_info[element].move_pattern;
4841 Moving2Blocked(x, y, &newx, &newy);
4844 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4847 if ((element == EL_SATELLITE ||
4848 element == EL_BALLOON ||
4849 element == EL_SPRING)
4850 && JustBeingPushed(x, y))
4855 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4856 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4857 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4860 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4861 element, element_info[element].token_name,
4862 WasJustMoving[x][y],
4863 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4864 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4865 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4866 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4870 WasJustMoving[x][y] = 0;
4873 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4876 if (Feld[x][y] != element) /* element has changed */
4878 element = Feld[x][y];
4879 move_pattern = element_info[element].move_pattern;
4881 if (!CAN_MOVE(element))
4885 if (Feld[x][y] != element) /* element has changed */
4893 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4894 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4896 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4898 Moving2Blocked(x, y, &newx, &newy);
4899 if (Feld[newx][newy] == EL_BLOCKED)
4900 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4906 if (FrameCounter < 1 && x == 0 && y == 29)
4907 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4910 if (!MovDelay[x][y]) /* start new movement phase */
4912 /* all objects that can change their move direction after each step
4913 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4915 if (element != EL_YAMYAM &&
4916 element != EL_DARK_YAMYAM &&
4917 element != EL_PACMAN &&
4918 !(move_pattern & MV_ANY_DIRECTION) &&
4919 move_pattern != MV_TURNING_LEFT &&
4920 move_pattern != MV_TURNING_RIGHT &&
4921 move_pattern != MV_TURNING_LEFT_RIGHT &&
4922 move_pattern != MV_TURNING_RIGHT_LEFT &&
4923 move_pattern != MV_TURNING_RANDOM)
4928 if (FrameCounter < 1 && x == 0 && y == 29)
4929 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4932 if (MovDelay[x][y] && (element == EL_BUG ||
4933 element == EL_SPACESHIP ||
4934 element == EL_SP_SNIKSNAK ||
4935 element == EL_SP_ELECTRON ||
4936 element == EL_MOLE))
4937 DrawLevelField(x, y);
4941 if (MovDelay[x][y]) /* wait some time before next movement */
4946 if (element == EL_YAMYAM)
4949 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4950 DrawLevelElementAnimation(x, y, element);
4954 if (MovDelay[x][y]) /* element still has to wait some time */
4957 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4958 ResetGfxAnimation(x, y);
4962 if (GfxAction[x][y] != ACTION_WAITING)
4963 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4965 GfxAction[x][y] = ACTION_WAITING;
4969 if (element == EL_ROBOT ||
4971 element == EL_PACMAN ||
4973 element == EL_YAMYAM ||
4974 element == EL_DARK_YAMYAM)
4977 DrawLevelElementAnimation(x, y, element);
4979 DrawLevelElementAnimationIfNeeded(x, y, element);
4981 PlayLevelSoundAction(x, y, ACTION_WAITING);
4983 else if (element == EL_SP_ELECTRON)
4984 DrawLevelElementAnimationIfNeeded(x, y, element);
4985 else if (element == EL_DRAGON)
4988 int dir = MovDir[x][y];
4989 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4990 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4991 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4992 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4993 dir == MV_UP ? IMG_FLAMES_1_UP :
4994 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4995 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4998 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5001 GfxAction[x][y] = ACTION_ATTACKING;
5003 if (IS_PLAYER(x, y))
5004 DrawPlayerField(x, y);
5006 DrawLevelField(x, y);
5008 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5010 for (i = 1; i <= 3; i++)
5012 int xx = x + i * dx;
5013 int yy = y + i * dy;
5014 int sx = SCREENX(xx);
5015 int sy = SCREENY(yy);
5016 int flame_graphic = graphic + (i - 1);
5018 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5023 int flamed = MovingOrBlocked2Element(xx, yy);
5025 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5028 RemoveMovingField(xx, yy);
5030 Feld[xx][yy] = EL_FLAMES;
5031 if (IN_SCR_FIELD(sx, sy))
5033 DrawLevelFieldCrumbledSand(xx, yy);
5034 DrawGraphic(sx, sy, flame_graphic, frame);
5039 if (Feld[xx][yy] == EL_FLAMES)
5040 Feld[xx][yy] = EL_EMPTY;
5041 DrawLevelField(xx, yy);
5046 if (MovDelay[x][y]) /* element still has to wait some time */
5048 PlayLevelSoundAction(x, y, ACTION_WAITING);
5054 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5055 for all other elements GfxAction will be set by InitMovingField() */
5056 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5057 GfxAction[x][y] = ACTION_MOVING;
5061 /* now make next step */
5063 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5065 if (DONT_COLLIDE_WITH(element) &&
5066 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5067 !PLAYER_ENEMY_PROTECTED(newx, newy))
5070 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5074 /* player killed by element which is deadly when colliding with */
5076 KillHero(PLAYERINFO(newx, newy));
5083 else if (CAN_MOVE_INTO_ACID(element) &&
5084 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5085 (MovDir[x][y] == MV_DOWN ||
5086 game.engine_version > VERSION_IDENT(3,0,8,0)))
5088 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5089 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5093 else if ((element == EL_PENGUIN ||
5094 element == EL_ROBOT ||
5095 element == EL_SATELLITE ||
5096 element == EL_BALLOON ||
5097 IS_CUSTOM_ELEMENT(element)) &&
5098 IN_LEV_FIELD(newx, newy) &&
5099 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5102 SplashAcid(newx, newy);
5103 Store[x][y] = EL_ACID;
5105 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5107 if (Feld[newx][newy] == EL_EXIT_OPEN)
5111 DrawLevelField(x, y);
5113 Feld[x][y] = EL_EMPTY;
5114 DrawLevelField(x, y);
5117 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5118 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5119 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5121 local_player->friends_still_needed--;
5122 if (!local_player->friends_still_needed &&
5123 !local_player->GameOver && AllPlayersGone)
5124 local_player->LevelSolved = local_player->GameOver = TRUE;
5128 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5130 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5131 DrawLevelField(newx, newy);
5133 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5135 else if (!IS_FREE(newx, newy))
5137 GfxAction[x][y] = ACTION_WAITING;
5139 if (IS_PLAYER(x, y))
5140 DrawPlayerField(x, y);
5142 DrawLevelField(x, y);
5147 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5149 if (IS_FOOD_PIG(Feld[newx][newy]))
5151 if (IS_MOVING(newx, newy))
5152 RemoveMovingField(newx, newy);
5155 Feld[newx][newy] = EL_EMPTY;
5156 DrawLevelField(newx, newy);
5159 PlayLevelSound(x, y, SND_PIG_DIGGING);
5161 else if (!IS_FREE(newx, newy))
5163 if (IS_PLAYER(x, y))
5164 DrawPlayerField(x, y);
5166 DrawLevelField(x, y);
5175 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5178 else if (IS_CUSTOM_ELEMENT(element) &&
5179 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5183 !IS_FREE(newx, newy)
5188 int new_element = Feld[newx][newy];
5191 printf("::: '%s' digs '%s' [%d]\n",
5192 element_info[element].token_name,
5193 element_info[Feld[newx][newy]].token_name,
5194 StorePlayer[newx][newy]);
5197 if (!IS_FREE(newx, newy))
5199 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5200 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5203 /* no element can dig solid indestructible elements */
5204 if (IS_INDESTRUCTIBLE(new_element) &&
5205 !IS_DIGGABLE(new_element) &&
5206 !IS_COLLECTIBLE(new_element))
5209 if (AmoebaNr[newx][newy] &&
5210 (new_element == EL_AMOEBA_FULL ||
5211 new_element == EL_BD_AMOEBA ||
5212 new_element == EL_AMOEBA_GROWING))
5214 AmoebaCnt[AmoebaNr[newx][newy]]--;
5215 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5218 if (IS_MOVING(newx, newy))
5219 RemoveMovingField(newx, newy);
5222 RemoveField(newx, newy);
5223 DrawLevelField(newx, newy);
5226 PlayLevelSoundAction(x, y, action);
5229 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5230 element_info[element].can_leave_element = TRUE;
5232 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5234 RunnerVisit[x][y] = FrameCounter;
5235 PlayerVisit[x][y] /= 8; /* expire player visit path */
5241 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5243 if (!IS_FREE(newx, newy))
5245 if (IS_PLAYER(x, y))
5246 DrawPlayerField(x, y);
5248 DrawLevelField(x, y);
5254 boolean wanna_flame = !RND(10);
5255 int dx = newx - x, dy = newy - y;
5256 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5257 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5258 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5259 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5260 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5261 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5264 IS_CLASSIC_ENEMY(element1) ||
5265 IS_CLASSIC_ENEMY(element2)) &&
5266 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5267 element1 != EL_FLAMES && element2 != EL_FLAMES)
5270 ResetGfxAnimation(x, y);
5271 GfxAction[x][y] = ACTION_ATTACKING;
5274 if (IS_PLAYER(x, y))
5275 DrawPlayerField(x, y);
5277 DrawLevelField(x, y);
5279 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5281 MovDelay[x][y] = 50;
5283 Feld[newx][newy] = EL_FLAMES;
5284 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5285 Feld[newx1][newy1] = EL_FLAMES;
5286 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5287 Feld[newx2][newy2] = EL_FLAMES;
5293 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5294 Feld[newx][newy] == EL_DIAMOND)
5296 if (IS_MOVING(newx, newy))
5297 RemoveMovingField(newx, newy);
5300 Feld[newx][newy] = EL_EMPTY;
5301 DrawLevelField(newx, newy);
5304 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5306 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5307 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5309 if (AmoebaNr[newx][newy])
5311 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5312 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5313 Feld[newx][newy] == EL_BD_AMOEBA)
5314 AmoebaCnt[AmoebaNr[newx][newy]]--;
5319 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5321 if (IS_MOVING(newx, newy))
5324 RemoveMovingField(newx, newy);
5328 Feld[newx][newy] = EL_EMPTY;
5329 DrawLevelField(newx, newy);
5332 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5334 else if ((element == EL_PACMAN || element == EL_MOLE)
5335 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5337 if (AmoebaNr[newx][newy])
5339 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5340 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5341 Feld[newx][newy] == EL_BD_AMOEBA)
5342 AmoebaCnt[AmoebaNr[newx][newy]]--;
5345 if (element == EL_MOLE)
5347 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5348 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5350 ResetGfxAnimation(x, y);
5351 GfxAction[x][y] = ACTION_DIGGING;
5352 DrawLevelField(x, y);
5354 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5356 return; /* wait for shrinking amoeba */
5358 else /* element == EL_PACMAN */
5360 Feld[newx][newy] = EL_EMPTY;
5361 DrawLevelField(newx, newy);
5362 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5365 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5366 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5367 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5369 /* wait for shrinking amoeba to completely disappear */
5372 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5374 /* object was running against a wall */
5379 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5380 DrawLevelElementAnimation(x, y, element);
5382 if (element == EL_BUG ||
5383 element == EL_SPACESHIP ||
5384 element == EL_SP_SNIKSNAK)
5385 DrawLevelField(x, y);
5386 else if (element == EL_MOLE)
5387 DrawLevelField(x, y);
5388 else if (element == EL_BD_BUTTERFLY ||
5389 element == EL_BD_FIREFLY)
5390 DrawLevelElementAnimationIfNeeded(x, y, element);
5391 else if (element == EL_SATELLITE)
5392 DrawLevelElementAnimationIfNeeded(x, y, element);
5393 else if (element == EL_SP_ELECTRON)
5394 DrawLevelElementAnimationIfNeeded(x, y, element);
5397 if (DONT_TOUCH(element))
5398 TestIfBadThingTouchesHero(x, y);
5401 PlayLevelSoundAction(x, y, ACTION_WAITING);
5407 InitMovingField(x, y, MovDir[x][y]);
5409 PlayLevelSoundAction(x, y, ACTION_MOVING);
5413 ContinueMoving(x, y);
5416 void ContinueMoving(int x, int y)
5418 int element = Feld[x][y];
5419 struct ElementInfo *ei = &element_info[element];
5420 int direction = MovDir[x][y];
5421 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5422 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5423 int newx = x + dx, newy = y + dy;
5425 int nextx = newx + dx, nexty = newy + dy;
5428 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5429 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5431 boolean pushed_by_player = Pushed[x][y];
5434 MovPos[x][y] += getElementMoveStepsize(x, y);
5437 if (pushed_by_player && IS_PLAYER(x, y))
5439 /* special case: moving object pushed by player */
5440 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5443 if (pushed_by_player) /* special case: moving object pushed by player */
5444 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5447 if (ABS(MovPos[x][y]) < TILEX)
5449 DrawLevelField(x, y);
5451 return; /* element is still moving */
5454 /* element reached destination field */
5456 Feld[x][y] = EL_EMPTY;
5457 Feld[newx][newy] = element;
5458 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5460 if (element == EL_MOLE)
5462 Feld[x][y] = EL_SAND;
5464 DrawLevelFieldCrumbledSandNeighbours(x, y);
5466 else if (element == EL_QUICKSAND_FILLING)
5468 element = Feld[newx][newy] = get_next_element(element);
5469 Store[newx][newy] = Store[x][y];
5471 else if (element == EL_QUICKSAND_EMPTYING)
5473 Feld[x][y] = get_next_element(element);
5474 element = Feld[newx][newy] = Store[x][y];
5476 else if (element == EL_MAGIC_WALL_FILLING)
5478 element = Feld[newx][newy] = get_next_element(element);
5479 if (!game.magic_wall_active)
5480 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5481 Store[newx][newy] = Store[x][y];
5483 else if (element == EL_MAGIC_WALL_EMPTYING)
5485 Feld[x][y] = get_next_element(element);
5486 if (!game.magic_wall_active)
5487 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5488 element = Feld[newx][newy] = Store[x][y];
5490 else if (element == EL_BD_MAGIC_WALL_FILLING)
5492 element = Feld[newx][newy] = get_next_element(element);
5493 if (!game.magic_wall_active)
5494 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5495 Store[newx][newy] = Store[x][y];
5497 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5499 Feld[x][y] = get_next_element(element);
5500 if (!game.magic_wall_active)
5501 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5502 element = Feld[newx][newy] = Store[x][y];
5504 else if (element == EL_AMOEBA_DROPPING)
5506 Feld[x][y] = get_next_element(element);
5507 element = Feld[newx][newy] = Store[x][y];
5509 else if (element == EL_SOKOBAN_OBJECT)
5512 Feld[x][y] = Back[x][y];
5514 if (Back[newx][newy])
5515 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5517 Back[x][y] = Back[newx][newy] = 0;
5519 else if (Store[x][y] == EL_ACID)
5521 element = Feld[newx][newy] = EL_ACID;
5525 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5526 MovDelay[newx][newy] = 0;
5528 /* copy element change control values to new field */
5529 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5530 ChangePage[newx][newy] = ChangePage[x][y];
5531 Changed[newx][newy] = Changed[x][y];
5532 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5534 ChangeDelay[x][y] = 0;
5535 ChangePage[x][y] = -1;
5536 Changed[x][y] = CE_BITMASK_DEFAULT;
5537 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5539 /* copy animation control values to new field */
5540 GfxFrame[newx][newy] = GfxFrame[x][y];
5541 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5542 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5543 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5545 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5547 ResetGfxAnimation(x, y); /* reset animation values for old field */
5550 /* some elements can leave other elements behind after moving */
5551 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5552 ei->move_leave_element != EL_EMPTY &&
5553 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5554 ei->can_leave_element_last))
5556 Feld[x][y] = ei->move_leave_element;
5557 InitField(x, y, FALSE);
5559 if (GFX_CRUMBLED(Feld[x][y]))
5560 DrawLevelFieldCrumbledSandNeighbours(x, y);
5563 ei->can_leave_element_last = ei->can_leave_element;
5564 ei->can_leave_element = FALSE;
5568 /* 2.1.1 (does not work correctly for spring) */
5569 if (!CAN_MOVE(element))
5570 MovDir[newx][newy] = 0;
5574 /* (does not work for falling objects that slide horizontally) */
5575 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5576 MovDir[newx][newy] = 0;
5579 if (!CAN_MOVE(element) ||
5580 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5581 MovDir[newx][newy] = 0;
5584 if (!CAN_MOVE(element) ||
5585 (CAN_FALL(element) && direction == MV_DOWN))
5586 GfxDir[x][y] = MovDir[newx][newy] = 0;
5591 DrawLevelField(x, y);
5592 DrawLevelField(newx, newy);
5594 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5596 /* prevent pushed element from moving on in pushed direction */
5597 if (pushed_by_player && CAN_MOVE(element) &&
5598 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5599 !(element_info[element].move_pattern & direction))
5600 TurnRound(newx, newy);
5603 /* prevent elements on conveyor belt from moving on in last direction */
5604 if (pushed_by_conveyor && CAN_FALL(element) &&
5605 direction & MV_HORIZONTAL)
5606 MovDir[newx][newy] = 0;
5609 if (!pushed_by_player)
5611 WasJustMoving[newx][newy] = 3;
5613 if (CAN_FALL(element) && direction == MV_DOWN)
5614 WasJustFalling[newx][newy] = 3;
5617 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5619 TestIfBadThingTouchesHero(newx, newy);
5620 TestIfBadThingTouchesFriend(newx, newy);
5622 if (!IS_CUSTOM_ELEMENT(element))
5623 TestIfBadThingTouchesOtherBadThing(newx, newy);
5625 else if (element == EL_PENGUIN)
5626 TestIfFriendTouchesBadThing(newx, newy);
5628 if (CAN_FALL(element) && direction == MV_DOWN &&
5629 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5633 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5637 if (ChangePage[newx][newy] != -1) /* delayed change */
5638 ChangeElement(newx, newy, ChangePage[newx][newy]);
5643 TestIfElementHitsCustomElement(newx, newy, direction);
5647 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5649 int hitting_element = Feld[newx][newy];
5651 /* !!! fix side (direction) orientation here and elsewhere !!! */
5652 CheckElementChangeSide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
5656 if (IN_LEV_FIELD(nextx, nexty))
5658 int opposite_direction = MV_DIR_OPPOSITE(direction);
5659 int hitting_side = direction;
5660 int touched_side = opposite_direction;
5661 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5662 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5663 MovDir[nextx][nexty] != direction ||
5664 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5670 CheckElementChangeSide(nextx, nexty, touched_element,
5671 CE_HIT_BY_SOMETHING, opposite_direction);
5673 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5674 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5676 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5678 struct ElementChangeInfo *change =
5679 &element_info[hitting_element].change_page[i];
5681 if (change->can_change &&
5682 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5683 change->trigger_side & touched_side &&
5684 change->trigger_element == touched_element)
5686 CheckElementChangePage(newx, newy, hitting_element,
5687 CE_OTHER_IS_HITTING, i);
5693 if (IS_CUSTOM_ELEMENT(touched_element) &&
5694 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5696 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5698 struct ElementChangeInfo *change =
5699 &element_info[touched_element].change_page[i];
5701 if (change->can_change &&
5702 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5703 change->trigger_side & hitting_side &&
5704 change->trigger_element == hitting_element)
5706 CheckElementChangePage(nextx, nexty, touched_element,
5707 CE_OTHER_GETS_HIT, i);
5718 TestIfPlayerTouchesCustomElement(newx, newy);
5719 TestIfElementTouchesCustomElement(newx, newy);
5722 int AmoebeNachbarNr(int ax, int ay)
5725 int element = Feld[ax][ay];
5727 static int xy[4][2] =
5735 for (i = 0; i < NUM_DIRECTIONS; i++)
5737 int x = ax + xy[i][0];
5738 int y = ay + xy[i][1];
5740 if (!IN_LEV_FIELD(x, y))
5743 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5744 group_nr = AmoebaNr[x][y];
5750 void AmoebenVereinigen(int ax, int ay)
5752 int i, x, y, xx, yy;
5753 int new_group_nr = AmoebaNr[ax][ay];
5754 static int xy[4][2] =
5762 if (new_group_nr == 0)
5765 for (i = 0; i < NUM_DIRECTIONS; i++)
5770 if (!IN_LEV_FIELD(x, y))
5773 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5774 Feld[x][y] == EL_BD_AMOEBA ||
5775 Feld[x][y] == EL_AMOEBA_DEAD) &&
5776 AmoebaNr[x][y] != new_group_nr)
5778 int old_group_nr = AmoebaNr[x][y];
5780 if (old_group_nr == 0)
5783 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5784 AmoebaCnt[old_group_nr] = 0;
5785 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5786 AmoebaCnt2[old_group_nr] = 0;
5788 for (yy = 0; yy < lev_fieldy; yy++)
5790 for (xx = 0; xx < lev_fieldx; xx++)
5792 if (AmoebaNr[xx][yy] == old_group_nr)
5793 AmoebaNr[xx][yy] = new_group_nr;
5800 void AmoebeUmwandeln(int ax, int ay)
5804 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5806 int group_nr = AmoebaNr[ax][ay];
5811 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5812 printf("AmoebeUmwandeln(): This should never happen!\n");
5817 for (y = 0; y < lev_fieldy; y++)
5819 for (x = 0; x < lev_fieldx; x++)
5821 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5824 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5828 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5829 SND_AMOEBA_TURNING_TO_GEM :
5830 SND_AMOEBA_TURNING_TO_ROCK));
5835 static int xy[4][2] =
5843 for (i = 0; i < NUM_DIRECTIONS; i++)
5848 if (!IN_LEV_FIELD(x, y))
5851 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5853 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5854 SND_AMOEBA_TURNING_TO_GEM :
5855 SND_AMOEBA_TURNING_TO_ROCK));
5862 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5865 int group_nr = AmoebaNr[ax][ay];
5866 boolean done = FALSE;
5871 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5872 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5877 for (y = 0; y < lev_fieldy; y++)
5879 for (x = 0; x < lev_fieldx; x++)
5881 if (AmoebaNr[x][y] == group_nr &&
5882 (Feld[x][y] == EL_AMOEBA_DEAD ||
5883 Feld[x][y] == EL_BD_AMOEBA ||
5884 Feld[x][y] == EL_AMOEBA_GROWING))
5887 Feld[x][y] = new_element;
5888 InitField(x, y, FALSE);
5889 DrawLevelField(x, y);
5896 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5897 SND_BD_AMOEBA_TURNING_TO_ROCK :
5898 SND_BD_AMOEBA_TURNING_TO_GEM));
5901 void AmoebeWaechst(int x, int y)
5903 static unsigned long sound_delay = 0;
5904 static unsigned long sound_delay_value = 0;
5906 if (!MovDelay[x][y]) /* start new growing cycle */
5910 if (DelayReached(&sound_delay, sound_delay_value))
5913 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5915 if (Store[x][y] == EL_BD_AMOEBA)
5916 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5918 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5920 sound_delay_value = 30;
5924 if (MovDelay[x][y]) /* wait some time before growing bigger */
5927 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5929 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5930 6 - MovDelay[x][y]);
5932 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5935 if (!MovDelay[x][y])
5937 Feld[x][y] = Store[x][y];
5939 DrawLevelField(x, y);
5944 void AmoebaDisappearing(int x, int y)
5946 static unsigned long sound_delay = 0;
5947 static unsigned long sound_delay_value = 0;
5949 if (!MovDelay[x][y]) /* start new shrinking cycle */
5953 if (DelayReached(&sound_delay, sound_delay_value))
5954 sound_delay_value = 30;
5957 if (MovDelay[x][y]) /* wait some time before shrinking */
5960 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5962 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5963 6 - MovDelay[x][y]);
5965 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5968 if (!MovDelay[x][y])
5970 Feld[x][y] = EL_EMPTY;
5971 DrawLevelField(x, y);
5973 /* don't let mole enter this field in this cycle;
5974 (give priority to objects falling to this field from above) */
5980 void AmoebeAbleger(int ax, int ay)
5983 int element = Feld[ax][ay];
5984 int graphic = el2img(element);
5985 int newax = ax, neway = ay;
5986 static int xy[4][2] =
5994 if (!level.amoeba_speed)
5996 Feld[ax][ay] = EL_AMOEBA_DEAD;
5997 DrawLevelField(ax, ay);
6001 if (IS_ANIMATED(graphic))
6002 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6004 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6005 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6007 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6010 if (MovDelay[ax][ay])
6014 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6017 int x = ax + xy[start][0];
6018 int y = ay + xy[start][1];
6020 if (!IN_LEV_FIELD(x, y))
6023 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6024 if (IS_FREE(x, y) ||
6025 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6031 if (newax == ax && neway == ay)
6034 else /* normal or "filled" (BD style) amoeba */
6037 boolean waiting_for_player = FALSE;
6039 for (i = 0; i < NUM_DIRECTIONS; i++)
6041 int j = (start + i) % 4;
6042 int x = ax + xy[j][0];
6043 int y = ay + xy[j][1];
6045 if (!IN_LEV_FIELD(x, y))
6048 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6049 if (IS_FREE(x, y) ||
6050 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6056 else if (IS_PLAYER(x, y))
6057 waiting_for_player = TRUE;
6060 if (newax == ax && neway == ay) /* amoeba cannot grow */
6062 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6064 Feld[ax][ay] = EL_AMOEBA_DEAD;
6065 DrawLevelField(ax, ay);
6066 AmoebaCnt[AmoebaNr[ax][ay]]--;
6068 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6070 if (element == EL_AMOEBA_FULL)
6071 AmoebeUmwandeln(ax, ay);
6072 else if (element == EL_BD_AMOEBA)
6073 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6078 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6080 /* amoeba gets larger by growing in some direction */
6082 int new_group_nr = AmoebaNr[ax][ay];
6085 if (new_group_nr == 0)
6087 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6088 printf("AmoebeAbleger(): This should never happen!\n");
6093 AmoebaNr[newax][neway] = new_group_nr;
6094 AmoebaCnt[new_group_nr]++;
6095 AmoebaCnt2[new_group_nr]++;
6097 /* if amoeba touches other amoeba(s) after growing, unify them */
6098 AmoebenVereinigen(newax, neway);
6100 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6102 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6108 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6109 (neway == lev_fieldy - 1 && newax != ax))
6111 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6112 Store[newax][neway] = element;
6114 else if (neway == ay)
6116 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6118 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6120 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6125 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6126 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6127 Store[ax][ay] = EL_AMOEBA_DROP;
6128 ContinueMoving(ax, ay);
6132 DrawLevelField(newax, neway);
6135 void Life(int ax, int ay)
6138 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6140 int element = Feld[ax][ay];
6141 int graphic = el2img(element);
6142 boolean changed = FALSE;
6144 if (IS_ANIMATED(graphic))
6145 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6150 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6151 MovDelay[ax][ay] = life_time;
6153 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6156 if (MovDelay[ax][ay])
6160 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6162 int xx = ax+x1, yy = ay+y1;
6165 if (!IN_LEV_FIELD(xx, yy))
6168 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6170 int x = xx+x2, y = yy+y2;
6172 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6175 if (((Feld[x][y] == element ||
6176 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6178 (IS_FREE(x, y) && Stop[x][y]))
6182 if (xx == ax && yy == ay) /* field in the middle */
6184 if (nachbarn < life[0] || nachbarn > life[1])
6186 Feld[xx][yy] = EL_EMPTY;
6188 DrawLevelField(xx, yy);
6189 Stop[xx][yy] = TRUE;
6193 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6194 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6195 { /* free border field */
6196 if (nachbarn >= life[2] && nachbarn <= life[3])
6198 Feld[xx][yy] = element;
6199 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6201 DrawLevelField(xx, yy);
6202 Stop[xx][yy] = TRUE;
6209 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6210 SND_GAME_OF_LIFE_GROWING);
6213 static void InitRobotWheel(int x, int y)
6215 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6218 static void RunRobotWheel(int x, int y)
6220 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6223 static void StopRobotWheel(int x, int y)
6225 if (ZX == x && ZY == y)
6229 static void InitTimegateWheel(int x, int y)
6231 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6234 static void RunTimegateWheel(int x, int y)
6236 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6239 void CheckExit(int x, int y)
6241 if (local_player->gems_still_needed > 0 ||
6242 local_player->sokobanfields_still_needed > 0 ||
6243 local_player->lights_still_needed > 0)
6245 int element = Feld[x][y];
6246 int graphic = el2img(element);
6248 if (IS_ANIMATED(graphic))
6249 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6254 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6257 Feld[x][y] = EL_EXIT_OPENING;
6259 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6262 void CheckExitSP(int x, int y)
6264 if (local_player->gems_still_needed > 0)
6266 int element = Feld[x][y];
6267 int graphic = el2img(element);
6269 if (IS_ANIMATED(graphic))
6270 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6275 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6278 Feld[x][y] = EL_SP_EXIT_OPENING;
6280 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6283 static void CloseAllOpenTimegates()
6287 for (y = 0; y < lev_fieldy; y++)
6289 for (x = 0; x < lev_fieldx; x++)
6291 int element = Feld[x][y];
6293 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6295 Feld[x][y] = EL_TIMEGATE_CLOSING;
6297 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6299 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6306 void EdelsteinFunkeln(int x, int y)
6308 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6311 if (Feld[x][y] == EL_BD_DIAMOND)
6314 if (MovDelay[x][y] == 0) /* next animation frame */
6315 MovDelay[x][y] = 11 * !SimpleRND(500);
6317 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6321 if (setup.direct_draw && MovDelay[x][y])
6322 SetDrawtoField(DRAW_BUFFERED);
6324 DrawLevelElementAnimation(x, y, Feld[x][y]);
6326 if (MovDelay[x][y] != 0)
6328 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6329 10 - MovDelay[x][y]);
6331 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6333 if (setup.direct_draw)
6337 dest_x = FX + SCREENX(x) * TILEX;
6338 dest_y = FY + SCREENY(y) * TILEY;
6340 BlitBitmap(drawto_field, window,
6341 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6342 SetDrawtoField(DRAW_DIRECT);
6348 void MauerWaechst(int x, int y)
6352 if (!MovDelay[x][y]) /* next animation frame */
6353 MovDelay[x][y] = 3 * delay;
6355 if (MovDelay[x][y]) /* wait some time before next frame */
6359 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6361 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6362 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6364 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6367 if (!MovDelay[x][y])
6369 if (MovDir[x][y] == MV_LEFT)
6371 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6372 DrawLevelField(x - 1, y);
6374 else if (MovDir[x][y] == MV_RIGHT)
6376 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6377 DrawLevelField(x + 1, y);
6379 else if (MovDir[x][y] == MV_UP)
6381 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6382 DrawLevelField(x, y - 1);
6386 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6387 DrawLevelField(x, y + 1);
6390 Feld[x][y] = Store[x][y];
6392 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6393 DrawLevelField(x, y);
6398 void MauerAbleger(int ax, int ay)
6400 int element = Feld[ax][ay];
6401 int graphic = el2img(element);
6402 boolean oben_frei = FALSE, unten_frei = FALSE;
6403 boolean links_frei = FALSE, rechts_frei = FALSE;
6404 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6405 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6406 boolean new_wall = FALSE;
6408 if (IS_ANIMATED(graphic))
6409 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6411 if (!MovDelay[ax][ay]) /* start building new wall */
6412 MovDelay[ax][ay] = 6;
6414 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6417 if (MovDelay[ax][ay])
6421 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6423 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6425 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6427 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6430 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6431 element == EL_EXPANDABLE_WALL_ANY)
6435 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6436 Store[ax][ay-1] = element;
6437 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6438 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6439 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6440 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6445 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6446 Store[ax][ay+1] = element;
6447 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6448 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6449 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6450 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6455 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6456 element == EL_EXPANDABLE_WALL_ANY ||
6457 element == EL_EXPANDABLE_WALL)
6461 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6462 Store[ax-1][ay] = element;
6463 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6464 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6465 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6466 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6472 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6473 Store[ax+1][ay] = element;
6474 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6475 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6476 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6477 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6482 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6483 DrawLevelField(ax, ay);
6485 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6487 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6488 unten_massiv = TRUE;
6489 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6490 links_massiv = TRUE;
6491 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6492 rechts_massiv = TRUE;
6494 if (((oben_massiv && unten_massiv) ||
6495 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6496 element == EL_EXPANDABLE_WALL) &&
6497 ((links_massiv && rechts_massiv) ||
6498 element == EL_EXPANDABLE_WALL_VERTICAL))
6499 Feld[ax][ay] = EL_WALL;
6503 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6505 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6509 void CheckForDragon(int x, int y)
6512 boolean dragon_found = FALSE;
6513 static int xy[4][2] =
6521 for (i = 0; i < NUM_DIRECTIONS; i++)
6523 for (j = 0; j < 4; j++)
6525 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6527 if (IN_LEV_FIELD(xx, yy) &&
6528 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6530 if (Feld[xx][yy] == EL_DRAGON)
6531 dragon_found = TRUE;
6540 for (i = 0; i < NUM_DIRECTIONS; i++)
6542 for (j = 0; j < 3; j++)
6544 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6546 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6548 Feld[xx][yy] = EL_EMPTY;
6549 DrawLevelField(xx, yy);
6558 static void InitBuggyBase(int x, int y)
6560 int element = Feld[x][y];
6561 int activating_delay = FRAMES_PER_SECOND / 4;
6564 (element == EL_SP_BUGGY_BASE ?
6565 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6566 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6568 element == EL_SP_BUGGY_BASE_ACTIVE ?
6569 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6572 static void WarnBuggyBase(int x, int y)
6575 static int xy[4][2] =
6583 for (i = 0; i < NUM_DIRECTIONS; i++)
6585 int xx = x + xy[i][0], yy = y + xy[i][1];
6587 if (IS_PLAYER(xx, yy))
6589 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6596 static void InitTrap(int x, int y)
6598 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6601 static void ActivateTrap(int x, int y)
6603 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6606 static void ChangeActiveTrap(int x, int y)
6608 int graphic = IMG_TRAP_ACTIVE;
6610 /* if new animation frame was drawn, correct crumbled sand border */
6611 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6612 DrawLevelFieldCrumbledSand(x, y);
6615 static void ChangeElementNowExt(int x, int y, int target_element)
6617 int previous_move_direction = MovDir[x][y];
6619 /* check if element under player changes from accessible to unaccessible
6620 (needed for special case of dropping element which then changes) */
6621 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6622 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6629 Feld[x][y] = target_element;
6631 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6633 ResetGfxAnimation(x, y);
6634 ResetRandomAnimationValue(x, y);
6636 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6637 MovDir[x][y] = previous_move_direction;
6640 InitField_WithBug1(x, y, FALSE);
6642 InitField(x, y, FALSE);
6643 if (CAN_MOVE(Feld[x][y]))
6647 DrawLevelField(x, y);
6649 if (GFX_CRUMBLED(Feld[x][y]))
6650 DrawLevelFieldCrumbledSandNeighbours(x, y);
6652 TestIfBadThingTouchesHero(x, y);
6653 TestIfPlayerTouchesCustomElement(x, y);
6654 TestIfElementTouchesCustomElement(x, y);
6656 if (ELEM_IS_PLAYER(target_element))
6657 RelocatePlayer(x, y, target_element);
6660 static boolean ChangeElementNow(int x, int y, int element, int page)
6662 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6664 /* always use default change event to prevent running into a loop */
6665 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6666 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6668 /* do not change already changed elements with same change event */
6670 if (Changed[x][y] & ChangeEvent[x][y])
6677 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6679 CheckTriggeredElementChangePage(x,y, Feld[x][y], CE_OTHER_IS_CHANGING, page);
6681 if (change->explode)
6688 if (change->use_content)
6690 boolean complete_change = TRUE;
6691 boolean can_change[3][3];
6694 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6696 boolean half_destructible;
6697 int ex = x + xx - 1;
6698 int ey = y + yy - 1;
6701 can_change[xx][yy] = TRUE;
6703 if (ex == x && ey == y) /* do not check changing element itself */
6706 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6708 can_change[xx][yy] = FALSE; /* do not change empty borders */
6713 if (!IN_LEV_FIELD(ex, ey))
6715 can_change[xx][yy] = FALSE;
6716 complete_change = FALSE;
6723 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6724 e = MovingOrBlocked2Element(ex, ey);
6726 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6728 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6729 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6730 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6732 can_change[xx][yy] = FALSE;
6733 complete_change = FALSE;
6737 if (!change->only_complete || complete_change)
6739 boolean something_has_changed = FALSE;
6741 if (change->only_complete && change->use_random_change &&
6742 RND(100) < change->random)
6745 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6747 int ex = x + xx - 1;
6748 int ey = y + yy - 1;
6750 if (can_change[xx][yy] && (!change->use_random_change ||
6751 RND(100) < change->random))
6753 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6754 RemoveMovingField(ex, ey);
6756 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6758 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6760 something_has_changed = TRUE;
6762 /* for symmetry reasons, freeze newly created border elements */
6763 if (ex != x || ey != y)
6764 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6768 if (something_has_changed)
6769 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6774 ChangeElementNowExt(x, y, change->target_element);
6776 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6782 static void ChangeElement(int x, int y, int page)
6784 int element = MovingOrBlocked2Element(x, y);
6785 struct ElementInfo *ei = &element_info[element];
6786 struct ElementChangeInfo *change = &ei->change_page[page];
6790 if (!CAN_CHANGE(element))
6793 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6794 x, y, element, element_info[element].token_name);
6795 printf("ChangeElement(): This should never happen!\n");
6801 if (ChangeDelay[x][y] == 0) /* initialize element change */
6803 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6804 RND(change->delay_random * change->delay_frames)) + 1;
6806 ResetGfxAnimation(x, y);
6807 ResetRandomAnimationValue(x, y);
6809 if (change->pre_change_function)
6810 change->pre_change_function(x, y);
6813 ChangeDelay[x][y]--;
6815 if (ChangeDelay[x][y] != 0) /* continue element change */
6817 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6819 if (IS_ANIMATED(graphic))
6820 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6822 if (change->change_function)
6823 change->change_function(x, y);
6825 else /* finish element change */
6827 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6829 page = ChangePage[x][y];
6830 ChangePage[x][y] = -1;
6834 if (IS_MOVING(x, y) && !change->explode)
6836 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6839 ChangeDelay[x][y] = 1; /* try change after next move step */
6840 ChangePage[x][y] = page; /* remember page to use for change */
6845 if (ChangeElementNow(x, y, element, page))
6847 if (change->post_change_function)
6848 change->post_change_function(x, y);
6853 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
6854 int trigger_element,
6862 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6865 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6867 int element = EL_CUSTOM_START + i;
6869 boolean change_element = FALSE;
6872 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6875 for (j = 0; j < element_info[element].num_change_pages; j++)
6877 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6879 if (change->can_change &&
6880 change->events & CH_EVENT_BIT(trigger_event) &&
6881 change->trigger_side & trigger_side &&
6882 change->trigger_player & trigger_player &&
6883 change->trigger_page & (1 << trigger_page) &&
6884 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
6887 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6888 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6889 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6892 change_element = TRUE;
6899 if (!change_element)
6902 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6905 if (x == lx && y == ly) /* do not change trigger element itself */
6909 if (Feld[x][y] == element)
6911 ChangeDelay[x][y] = 1;
6912 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6913 ChangeElement(x, y, page);
6921 static boolean CheckElementChangeExt(int x, int y,
6928 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6931 if (Feld[x][y] == EL_BLOCKED)
6933 Blocked2Moving(x, y, &x, &y);
6934 element = Feld[x][y];
6938 if (trigger_page < 0)
6940 boolean change_element = FALSE;
6943 for (i = 0; i < element_info[element].num_change_pages; i++)
6945 struct ElementChangeInfo *change = &element_info[element].change_page[i];
6947 if (change->can_change &&
6948 change->events & CH_EVENT_BIT(trigger_event) &&
6949 change->trigger_side & trigger_side &&
6950 change->trigger_player & trigger_player)
6952 change_element = TRUE;
6959 if (!change_element)
6965 /* !!! this check misses pages with same event, but different side !!! */
6967 if (trigger_page < 0)
6968 trigger_page = element_info[element].event_page_nr[trigger_event];
6970 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
6974 ChangeDelay[x][y] = 1;
6975 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6976 ChangeElement(x, y, trigger_page);
6981 static void PlayPlayerSound(struct PlayerInfo *player)
6983 int jx = player->jx, jy = player->jy;
6984 int element = player->element_nr;
6985 int last_action = player->last_action_waiting;
6986 int action = player->action_waiting;
6988 if (player->is_waiting)
6990 if (action != last_action)
6991 PlayLevelSoundElementAction(jx, jy, element, action);
6993 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6997 if (action != last_action)
6998 StopSound(element_info[element].sound[last_action]);
7000 if (last_action == ACTION_SLEEPING)
7001 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7005 static void PlayAllPlayersSound()
7009 for (i = 0; i < MAX_PLAYERS; i++)
7010 if (stored_player[i].active)
7011 PlayPlayerSound(&stored_player[i]);
7014 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7016 boolean last_waiting = player->is_waiting;
7017 int move_dir = player->MovDir;
7019 player->last_action_waiting = player->action_waiting;
7023 if (!last_waiting) /* not waiting -> waiting */
7025 player->is_waiting = TRUE;
7027 player->frame_counter_bored =
7029 game.player_boring_delay_fixed +
7030 SimpleRND(game.player_boring_delay_random);
7031 player->frame_counter_sleeping =
7033 game.player_sleeping_delay_fixed +
7034 SimpleRND(game.player_sleeping_delay_random);
7036 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7039 if (game.player_sleeping_delay_fixed +
7040 game.player_sleeping_delay_random > 0 &&
7041 player->anim_delay_counter == 0 &&
7042 player->post_delay_counter == 0 &&
7043 FrameCounter >= player->frame_counter_sleeping)
7044 player->is_sleeping = TRUE;
7045 else if (game.player_boring_delay_fixed +
7046 game.player_boring_delay_random > 0 &&
7047 FrameCounter >= player->frame_counter_bored)
7048 player->is_bored = TRUE;
7050 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7051 player->is_bored ? ACTION_BORING :
7054 if (player->is_sleeping)
7056 if (player->num_special_action_sleeping > 0)
7058 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7060 int last_special_action = player->special_action_sleeping;
7061 int num_special_action = player->num_special_action_sleeping;
7062 int special_action =
7063 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7064 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7065 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7066 last_special_action + 1 : ACTION_SLEEPING);
7067 int special_graphic =
7068 el_act_dir2img(player->element_nr, special_action, move_dir);
7070 player->anim_delay_counter =
7071 graphic_info[special_graphic].anim_delay_fixed +
7072 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7073 player->post_delay_counter =
7074 graphic_info[special_graphic].post_delay_fixed +
7075 SimpleRND(graphic_info[special_graphic].post_delay_random);
7077 player->special_action_sleeping = special_action;
7080 if (player->anim_delay_counter > 0)
7082 player->action_waiting = player->special_action_sleeping;
7083 player->anim_delay_counter--;
7085 else if (player->post_delay_counter > 0)
7087 player->post_delay_counter--;
7091 else if (player->is_bored)
7093 if (player->num_special_action_bored > 0)
7095 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7097 int special_action =
7098 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7099 int special_graphic =
7100 el_act_dir2img(player->element_nr, special_action, move_dir);
7102 player->anim_delay_counter =
7103 graphic_info[special_graphic].anim_delay_fixed +
7104 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7105 player->post_delay_counter =
7106 graphic_info[special_graphic].post_delay_fixed +
7107 SimpleRND(graphic_info[special_graphic].post_delay_random);
7109 player->special_action_bored = special_action;
7112 if (player->anim_delay_counter > 0)
7114 player->action_waiting = player->special_action_bored;
7115 player->anim_delay_counter--;
7117 else if (player->post_delay_counter > 0)
7119 player->post_delay_counter--;
7124 else if (last_waiting) /* waiting -> not waiting */
7126 player->is_waiting = FALSE;
7127 player->is_bored = FALSE;
7128 player->is_sleeping = FALSE;
7130 player->frame_counter_bored = -1;
7131 player->frame_counter_sleeping = -1;
7133 player->anim_delay_counter = 0;
7134 player->post_delay_counter = 0;
7136 player->action_waiting = ACTION_DEFAULT;
7138 player->special_action_bored = ACTION_DEFAULT;
7139 player->special_action_sleeping = ACTION_DEFAULT;
7144 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7147 static byte stored_player_action[MAX_PLAYERS];
7148 static int num_stored_actions = 0;
7150 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7151 int left = player_action & JOY_LEFT;
7152 int right = player_action & JOY_RIGHT;
7153 int up = player_action & JOY_UP;
7154 int down = player_action & JOY_DOWN;
7155 int button1 = player_action & JOY_BUTTON_1;
7156 int button2 = player_action & JOY_BUTTON_2;
7157 int dx = (left ? -1 : right ? 1 : 0);
7158 int dy = (up ? -1 : down ? 1 : 0);
7161 stored_player_action[player->index_nr] = 0;
7162 num_stored_actions++;
7166 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7169 if (!player->active || tape.pausing)
7173 printf("::: [%d %d %d %d] [%d %d]\n",
7174 left, right, up, down, button1, button2);
7180 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7185 CheckGravityMovement(player);
7188 snapped = SnapField(player, dx, dy);
7192 dropped = DropElement(player);
7194 moved = MovePlayer(player, dx, dy);
7197 if (tape.single_step && tape.recording && !tape.pausing)
7199 if (button1 || (dropped && !moved))
7201 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7202 SnapField(player, 0, 0); /* stop snapping */
7206 SetPlayerWaiting(player, FALSE);
7209 return player_action;
7211 stored_player_action[player->index_nr] = player_action;
7217 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7220 /* no actions for this player (no input at player's configured device) */
7222 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7223 SnapField(player, 0, 0);
7224 CheckGravityMovementWhenNotMoving(player);
7226 if (player->MovPos == 0)
7227 SetPlayerWaiting(player, TRUE);
7229 if (player->MovPos == 0) /* needed for tape.playing */
7230 player->is_moving = FALSE;
7232 player->is_dropping = FALSE;
7238 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7240 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7242 TapeRecordAction(stored_player_action);
7243 num_stored_actions = 0;
7250 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7252 static byte stored_player_action[MAX_PLAYERS];
7253 static int num_stored_actions = 0;
7254 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7255 int left = player_action & JOY_LEFT;
7256 int right = player_action & JOY_RIGHT;
7257 int up = player_action & JOY_UP;
7258 int down = player_action & JOY_DOWN;
7259 int button1 = player_action & JOY_BUTTON_1;
7260 int button2 = player_action & JOY_BUTTON_2;
7261 int dx = (left ? -1 : right ? 1 : 0);
7262 int dy = (up ? -1 : down ? 1 : 0);
7264 stored_player_action[player->index_nr] = 0;
7265 num_stored_actions++;
7267 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7269 if (!player->active || tape.pausing)
7274 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7277 snapped = SnapField(player, dx, dy);
7281 dropped = DropElement(player);
7283 moved = MovePlayer(player, dx, dy);
7286 if (tape.single_step && tape.recording && !tape.pausing)
7288 if (button1 || (dropped && !moved))
7290 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7291 SnapField(player, 0, 0); /* stop snapping */
7295 stored_player_action[player->index_nr] = player_action;
7299 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7301 /* no actions for this player (no input at player's configured device) */
7303 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7304 SnapField(player, 0, 0);
7305 CheckGravityMovementWhenNotMoving(player);
7307 if (player->MovPos == 0)
7308 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7310 if (player->MovPos == 0) /* needed for tape.playing */
7311 player->is_moving = FALSE;
7314 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7316 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7318 TapeRecordAction(stored_player_action);
7319 num_stored_actions = 0;
7326 static unsigned long action_delay = 0;
7327 unsigned long action_delay_value;
7328 int magic_wall_x = 0, magic_wall_y = 0;
7329 int i, x, y, element, graphic;
7330 byte *recorded_player_action;
7331 byte summarized_player_action = 0;
7333 byte tape_action[MAX_PLAYERS];
7336 if (game_status != GAME_MODE_PLAYING)
7339 action_delay_value =
7340 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7342 if (tape.playing && tape.index_search && !tape.pausing)
7343 action_delay_value = 0;
7345 /* ---------- main game synchronization point ---------- */
7347 WaitUntilDelayReached(&action_delay, action_delay_value);
7349 if (network_playing && !network_player_action_received)
7353 printf("DEBUG: try to get network player actions in time\n");
7357 #if defined(PLATFORM_UNIX)
7358 /* last chance to get network player actions without main loop delay */
7362 if (game_status != GAME_MODE_PLAYING)
7365 if (!network_player_action_received)
7369 printf("DEBUG: failed to get network player actions in time\n");
7380 printf("::: getting new tape action [%d]\n", FrameCounter);
7383 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7386 if (recorded_player_action != NULL)
7387 for (i = 0; i < MAX_PLAYERS; i++)
7388 stored_player[i].action = recorded_player_action[i];
7391 for (i = 0; i < MAX_PLAYERS; i++)
7393 summarized_player_action |= stored_player[i].action;
7395 if (!network_playing)
7396 stored_player[i].effective_action = stored_player[i].action;
7399 #if defined(PLATFORM_UNIX)
7400 if (network_playing)
7401 SendToServer_MovePlayer(summarized_player_action);
7404 if (!options.network && !setup.team_mode)
7405 local_player->effective_action = summarized_player_action;
7407 for (i = 0; i < MAX_PLAYERS; i++)
7409 int actual_player_action = stored_player[i].effective_action;
7412 /* OLD: overwrite programmed action with tape action (BAD!!!) */
7413 if (stored_player[i].programmed_action)
7414 actual_player_action = stored_player[i].programmed_action;
7417 if (recorded_player_action)
7420 if (stored_player[i].programmed_action &&
7421 stored_player[i].programmed_action != recorded_player_action[i])
7422 printf("::: %d: %d <-> %d\n", i,
7423 stored_player[i].programmed_action, recorded_player_action[i]);
7427 actual_player_action = recorded_player_action[i];
7432 /* NEW: overwrite tape action with programmed action */
7433 if (stored_player[i].programmed_action)
7434 actual_player_action = stored_player[i].programmed_action;
7437 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7439 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7440 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7442 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7447 TapeRecordAction(tape_action);
7450 network_player_action_received = FALSE;
7452 ScrollScreen(NULL, SCROLL_GO_ON);
7458 for (i = 0; i < MAX_PLAYERS; i++)
7459 stored_player[i].Frame++;
7463 /* for downwards compatibility, the following code emulates a fixed bug that
7464 occured when pushing elements (causing elements that just made their last
7465 pushing step to already (if possible) make their first falling step in the
7466 same game frame, which is bad); this code is also needed to use the famous
7467 "spring push bug" which is used in older levels and might be wanted to be
7468 used also in newer levels, but in this case the buggy pushing code is only
7469 affecting the "spring" element and no other elements */
7472 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7474 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7477 for (i = 0; i < MAX_PLAYERS; i++)
7479 struct PlayerInfo *player = &stored_player[i];
7484 if (player->active && player->is_pushing && player->is_moving &&
7486 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7487 Feld[x][y] == EL_SPRING))
7489 if (player->active && player->is_pushing && player->is_moving &&
7493 ContinueMoving(x, y);
7495 /* continue moving after pushing (this is actually a bug) */
7496 if (!IS_MOVING(x, y))
7505 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7507 Changed[x][y] = CE_BITMASK_DEFAULT;
7508 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7511 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7513 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7514 printf("GameActions(): This should never happen!\n");
7516 ChangePage[x][y] = -1;
7521 if (WasJustMoving[x][y] > 0)
7522 WasJustMoving[x][y]--;
7523 if (WasJustFalling[x][y] > 0)
7524 WasJustFalling[x][y]--;
7529 /* reset finished pushing action (not done in ContinueMoving() to allow
7530 continous pushing animation for elements with zero push delay) */
7531 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7533 ResetGfxAnimation(x, y);
7534 DrawLevelField(x, y);
7539 if (IS_BLOCKED(x, y))
7543 Blocked2Moving(x, y, &oldx, &oldy);
7544 if (!IS_MOVING(oldx, oldy))
7546 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7547 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7548 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7549 printf("GameActions(): This should never happen!\n");
7555 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7557 element = Feld[x][y];
7559 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7561 graphic = el2img(element);
7567 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7569 element = graphic = 0;
7573 if (graphic_info[graphic].anim_global_sync)
7574 GfxFrame[x][y] = FrameCounter;
7576 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7577 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7578 ResetRandomAnimationValue(x, y);
7580 SetRandomAnimationValue(x, y);
7583 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7586 if (IS_INACTIVE(element))
7588 if (IS_ANIMATED(graphic))
7589 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7595 /* this may take place after moving, so 'element' may have changed */
7597 if (IS_CHANGING(x, y))
7599 if (IS_CHANGING(x, y) &&
7600 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7604 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7605 element_info[element].event_page_nr[CE_DELAY]);
7607 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7610 element = Feld[x][y];
7611 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7615 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7620 element = Feld[x][y];
7621 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7623 if (element == EL_MOLE)
7624 printf("::: %d, %d, %d [%d]\n",
7625 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7629 if (element == EL_YAMYAM)
7630 printf("::: %d, %d, %d\n",
7631 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7635 if (IS_ANIMATED(graphic) &&
7639 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7642 if (element == EL_BUG)
7643 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7647 if (element == EL_MOLE)
7648 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7652 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7653 EdelsteinFunkeln(x, y);
7655 else if ((element == EL_ACID ||
7656 element == EL_EXIT_OPEN ||
7657 element == EL_SP_EXIT_OPEN ||
7658 element == EL_SP_TERMINAL ||
7659 element == EL_SP_TERMINAL_ACTIVE ||
7660 element == EL_EXTRA_TIME ||
7661 element == EL_SHIELD_NORMAL ||
7662 element == EL_SHIELD_DEADLY) &&
7663 IS_ANIMATED(graphic))
7664 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7665 else if (IS_MOVING(x, y))
7666 ContinueMoving(x, y);
7667 else if (IS_ACTIVE_BOMB(element))
7668 CheckDynamite(x, y);
7670 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7671 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7673 else if (element == EL_AMOEBA_GROWING)
7674 AmoebeWaechst(x, y);
7675 else if (element == EL_AMOEBA_SHRINKING)
7676 AmoebaDisappearing(x, y);
7678 #if !USE_NEW_AMOEBA_CODE
7679 else if (IS_AMOEBALIVE(element))
7680 AmoebeAbleger(x, y);
7683 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7685 else if (element == EL_EXIT_CLOSED)
7687 else if (element == EL_SP_EXIT_CLOSED)
7689 else if (element == EL_EXPANDABLE_WALL_GROWING)
7691 else if (element == EL_EXPANDABLE_WALL ||
7692 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7693 element == EL_EXPANDABLE_WALL_VERTICAL ||
7694 element == EL_EXPANDABLE_WALL_ANY)
7696 else if (element == EL_FLAMES)
7697 CheckForDragon(x, y);
7699 else if (IS_AUTO_CHANGING(element))
7700 ChangeElement(x, y);
7702 else if (element == EL_EXPLOSION)
7703 ; /* drawing of correct explosion animation is handled separately */
7704 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7705 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7708 /* this may take place after moving, so 'element' may have changed */
7709 if (IS_AUTO_CHANGING(Feld[x][y]))
7710 ChangeElement(x, y);
7713 if (IS_BELT_ACTIVE(element))
7714 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7716 if (game.magic_wall_active)
7718 int jx = local_player->jx, jy = local_player->jy;
7720 /* play the element sound at the position nearest to the player */
7721 if ((element == EL_MAGIC_WALL_FULL ||
7722 element == EL_MAGIC_WALL_ACTIVE ||
7723 element == EL_MAGIC_WALL_EMPTYING ||
7724 element == EL_BD_MAGIC_WALL_FULL ||
7725 element == EL_BD_MAGIC_WALL_ACTIVE ||
7726 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7727 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7735 #if USE_NEW_AMOEBA_CODE
7736 /* new experimental amoeba growth stuff */
7738 if (!(FrameCounter % 8))
7741 static unsigned long random = 1684108901;
7743 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7746 x = (random >> 10) % lev_fieldx;
7747 y = (random >> 20) % lev_fieldy;
7749 x = RND(lev_fieldx);
7750 y = RND(lev_fieldy);
7752 element = Feld[x][y];
7754 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7755 if (!IS_PLAYER(x,y) &&
7756 (element == EL_EMPTY ||
7757 element == EL_SAND ||
7758 element == EL_QUICKSAND_EMPTY ||
7759 element == EL_ACID_SPLASH_LEFT ||
7760 element == EL_ACID_SPLASH_RIGHT))
7762 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7763 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7764 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7765 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7766 Feld[x][y] = EL_AMOEBA_DROP;
7769 random = random * 129 + 1;
7775 if (game.explosions_delayed)
7778 game.explosions_delayed = FALSE;
7780 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7782 element = Feld[x][y];
7784 if (ExplodeField[x][y])
7785 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7786 else if (element == EL_EXPLOSION)
7787 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7789 ExplodeField[x][y] = EX_NO_EXPLOSION;
7792 game.explosions_delayed = TRUE;
7795 if (game.magic_wall_active)
7797 if (!(game.magic_wall_time_left % 4))
7799 int element = Feld[magic_wall_x][magic_wall_y];
7801 if (element == EL_BD_MAGIC_WALL_FULL ||
7802 element == EL_BD_MAGIC_WALL_ACTIVE ||
7803 element == EL_BD_MAGIC_WALL_EMPTYING)
7804 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7806 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7809 if (game.magic_wall_time_left > 0)
7811 game.magic_wall_time_left--;
7812 if (!game.magic_wall_time_left)
7814 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7816 element = Feld[x][y];
7818 if (element == EL_MAGIC_WALL_ACTIVE ||
7819 element == EL_MAGIC_WALL_FULL)
7821 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7822 DrawLevelField(x, y);
7824 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7825 element == EL_BD_MAGIC_WALL_FULL)
7827 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7828 DrawLevelField(x, y);
7832 game.magic_wall_active = FALSE;
7837 if (game.light_time_left > 0)
7839 game.light_time_left--;
7841 if (game.light_time_left == 0)
7842 RedrawAllLightSwitchesAndInvisibleElements();
7845 if (game.timegate_time_left > 0)
7847 game.timegate_time_left--;
7849 if (game.timegate_time_left == 0)
7850 CloseAllOpenTimegates();
7853 for (i = 0; i < MAX_PLAYERS; i++)
7855 struct PlayerInfo *player = &stored_player[i];
7857 if (SHIELD_ON(player))
7859 if (player->shield_deadly_time_left)
7860 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7861 else if (player->shield_normal_time_left)
7862 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7866 if (TimeFrames >= FRAMES_PER_SECOND)
7871 if (!level.use_step_counter)
7875 for (i = 0; i < MAX_PLAYERS; i++)
7877 struct PlayerInfo *player = &stored_player[i];
7879 if (SHIELD_ON(player))
7881 player->shield_normal_time_left--;
7883 if (player->shield_deadly_time_left > 0)
7884 player->shield_deadly_time_left--;
7892 if (TimeLeft <= 10 && setup.time_limit)
7893 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7895 DrawGameValue_Time(TimeLeft);
7897 if (!TimeLeft && setup.time_limit)
7898 for (i = 0; i < MAX_PLAYERS; i++)
7899 KillHero(&stored_player[i]);
7901 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
7902 DrawGameValue_Time(TimePlayed);
7905 if (tape.recording || tape.playing)
7906 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
7910 PlayAllPlayersSound();
7912 if (options.debug) /* calculate frames per second */
7914 static unsigned long fps_counter = 0;
7915 static int fps_frames = 0;
7916 unsigned long fps_delay_ms = Counter() - fps_counter;
7920 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7922 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7925 fps_counter = Counter();
7928 redraw_mask |= REDRAW_FPS;
7932 if (stored_player[0].jx != stored_player[0].last_jx ||
7933 stored_player[0].jy != stored_player[0].last_jy)
7934 printf("::: %d, %d, %d, %d, %d\n",
7935 stored_player[0].MovDir,
7936 stored_player[0].MovPos,
7937 stored_player[0].GfxPos,
7938 stored_player[0].Frame,
7939 stored_player[0].StepFrame);
7946 for (i = 0; i < MAX_PLAYERS; i++)
7949 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7951 stored_player[i].Frame += move_frames;
7953 if (stored_player[i].MovPos != 0)
7954 stored_player[i].StepFrame += move_frames;
7956 if (stored_player[i].drop_delay > 0)
7957 stored_player[i].drop_delay--;
7962 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7964 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7966 local_player->show_envelope = 0;
7971 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7973 int min_x = x, min_y = y, max_x = x, max_y = y;
7976 for (i = 0; i < MAX_PLAYERS; i++)
7978 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7980 if (!stored_player[i].active || &stored_player[i] == player)
7983 min_x = MIN(min_x, jx);
7984 min_y = MIN(min_y, jy);
7985 max_x = MAX(max_x, jx);
7986 max_y = MAX(max_y, jy);
7989 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7992 static boolean AllPlayersInVisibleScreen()
7996 for (i = 0; i < MAX_PLAYERS; i++)
7998 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8000 if (!stored_player[i].active)
8003 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8010 void ScrollLevel(int dx, int dy)
8012 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8015 BlitBitmap(drawto_field, drawto_field,
8016 FX + TILEX * (dx == -1) - softscroll_offset,
8017 FY + TILEY * (dy == -1) - softscroll_offset,
8018 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8019 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8020 FX + TILEX * (dx == 1) - softscroll_offset,
8021 FY + TILEY * (dy == 1) - softscroll_offset);
8025 x = (dx == 1 ? BX1 : BX2);
8026 for (y = BY1; y <= BY2; y++)
8027 DrawScreenField(x, y);
8032 y = (dy == 1 ? BY1 : BY2);
8033 for (x = BX1; x <= BX2; x++)
8034 DrawScreenField(x, y);
8037 redraw_mask |= REDRAW_FIELD;
8040 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8042 int nextx = x + dx, nexty = y + dy;
8043 int element = Feld[x][y];
8046 element != EL_SP_PORT_LEFT &&
8047 element != EL_SP_GRAVITY_PORT_LEFT &&
8048 element != EL_SP_PORT_HORIZONTAL &&
8049 element != EL_SP_PORT_ANY) ||
8051 element != EL_SP_PORT_RIGHT &&
8052 element != EL_SP_GRAVITY_PORT_RIGHT &&
8053 element != EL_SP_PORT_HORIZONTAL &&
8054 element != EL_SP_PORT_ANY) ||
8056 element != EL_SP_PORT_UP &&
8057 element != EL_SP_GRAVITY_PORT_UP &&
8058 element != EL_SP_PORT_VERTICAL &&
8059 element != EL_SP_PORT_ANY) ||
8061 element != EL_SP_PORT_DOWN &&
8062 element != EL_SP_GRAVITY_PORT_DOWN &&
8063 element != EL_SP_PORT_VERTICAL &&
8064 element != EL_SP_PORT_ANY) ||
8065 !IN_LEV_FIELD(nextx, nexty) ||
8066 !IS_FREE(nextx, nexty))
8072 static void CheckGravityMovement(struct PlayerInfo *player)
8074 if (game.gravity && !player->programmed_action)
8076 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
8077 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
8079 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
8080 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8081 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8082 int jx = player->jx, jy = player->jy;
8083 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8084 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8085 int new_jx = jx + dx, new_jy = jy + dy;
8086 boolean field_under_player_is_free =
8087 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8088 boolean player_is_moving_to_valid_field =
8089 (IN_LEV_FIELD(new_jx, new_jy) &&
8090 (Feld[new_jx][new_jy] == EL_SP_BASE ||
8091 Feld[new_jx][new_jy] == EL_SAND ||
8092 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8093 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
8094 /* !!! extend EL_SAND to anything diggable !!! */
8096 boolean player_is_standing_on_valid_field =
8097 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8098 (IS_WALKABLE(Feld[jx][jy]) &&
8099 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8101 if (field_under_player_is_free &&
8102 !player_is_standing_on_valid_field &&
8103 !player_is_moving_to_valid_field)
8104 player->programmed_action = MV_DOWN;
8108 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8111 return CheckGravityMovement(player);
8114 if (game.gravity && !player->programmed_action)
8116 int jx = player->jx, jy = player->jy;
8117 boolean field_under_player_is_free =
8118 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8119 boolean player_is_standing_on_valid_field =
8120 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8121 (IS_WALKABLE(Feld[jx][jy]) &&
8122 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8124 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8125 player->programmed_action = MV_DOWN;
8131 -----------------------------------------------------------------------------
8132 dx, dy: direction (non-diagonal) to try to move the player to
8133 real_dx, real_dy: direction as read from input device (can be diagonal)
8136 boolean MovePlayerOneStep(struct PlayerInfo *player,
8137 int dx, int dy, int real_dx, int real_dy)
8140 static int trigger_sides[4][2] =
8142 /* enter side leave side */
8143 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8144 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8145 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8146 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8148 int move_direction = (dx == -1 ? MV_LEFT :
8149 dx == +1 ? MV_RIGHT :
8151 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8152 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8153 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8155 int jx = player->jx, jy = player->jy;
8156 int new_jx = jx + dx, new_jy = jy + dy;
8160 if (!player->active || (!dx && !dy))
8161 return MF_NO_ACTION;
8163 player->MovDir = (dx < 0 ? MV_LEFT :
8166 dy > 0 ? MV_DOWN : MV_NO_MOVING);
8168 if (!IN_LEV_FIELD(new_jx, new_jy))
8169 return MF_NO_ACTION;
8171 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8172 return MF_NO_ACTION;
8175 element = MovingOrBlocked2Element(new_jx, new_jy);
8177 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8180 if (DONT_RUN_INTO(element))
8182 if (element == EL_ACID && dx == 0 && dy == 1)
8184 SplashAcid(new_jx, new_jy);
8185 Feld[jx][jy] = EL_PLAYER_1;
8186 InitMovingField(jx, jy, MV_DOWN);
8187 Store[jx][jy] = EL_ACID;
8188 ContinueMoving(jx, jy);
8192 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8197 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8198 if (can_move != MF_MOVING)
8201 /* check if DigField() has caused relocation of the player */
8202 if (player->jx != jx || player->jy != jy)
8203 return MF_NO_ACTION;
8205 StorePlayer[jx][jy] = 0;
8206 player->last_jx = jx;
8207 player->last_jy = jy;
8208 player->jx = new_jx;
8209 player->jy = new_jy;
8210 StorePlayer[new_jx][new_jy] = player->element_nr;
8213 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8215 player->step_counter++;
8217 player->drop_delay = 0;
8219 PlayerVisit[jx][jy] = FrameCounter;
8221 ScrollPlayer(player, SCROLL_INIT);
8224 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8226 CheckTriggeredElementChangeSide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
8228 CheckElementChangeSide(jx, jy, Feld[jx][jy], CE_LEFT_BY_PLAYER,leave_side);
8231 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
8233 CheckTriggeredElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8234 CE_OTHER_GETS_ENTERED, enter_side);
8235 CheckElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8236 CE_ENTERED_BY_PLAYER, enter_side);
8243 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8245 int jx = player->jx, jy = player->jy;
8246 int old_jx = jx, old_jy = jy;
8247 int moved = MF_NO_ACTION;
8250 if (!player->active)
8255 if (player->MovPos == 0)
8257 player->is_moving = FALSE;
8258 player->is_digging = FALSE;
8259 player->is_collecting = FALSE;
8260 player->is_snapping = FALSE;
8261 player->is_pushing = FALSE;
8267 if (!player->active || (!dx && !dy))
8272 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8278 if (!FrameReached(&player->move_delay, player->move_delay_value))
8281 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8282 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
8288 /* remove the last programmed player action */
8289 player->programmed_action = 0;
8293 /* should only happen if pre-1.2 tape recordings are played */
8294 /* this is only for backward compatibility */
8296 int original_move_delay_value = player->move_delay_value;
8299 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8303 /* scroll remaining steps with finest movement resolution */
8304 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8306 while (player->MovPos)
8308 ScrollPlayer(player, SCROLL_GO_ON);
8309 ScrollScreen(NULL, SCROLL_GO_ON);
8315 player->move_delay_value = original_move_delay_value;
8318 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
8320 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8321 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8325 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8326 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8332 if (moved & MF_MOVING && !ScreenMovPos &&
8333 (player == local_player || !options.network))
8335 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8336 int offset = (setup.scroll_delay ? 3 : 0);
8338 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8340 /* actual player has left the screen -- scroll in that direction */
8341 if (jx != old_jx) /* player has moved horizontally */
8342 scroll_x += (jx - old_jx);
8343 else /* player has moved vertically */
8344 scroll_y += (jy - old_jy);
8348 if (jx != old_jx) /* player has moved horizontally */
8350 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8351 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8352 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8354 /* don't scroll over playfield boundaries */
8355 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8356 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8358 /* don't scroll more than one field at a time */
8359 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8361 /* don't scroll against the player's moving direction */
8362 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8363 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8364 scroll_x = old_scroll_x;
8366 else /* player has moved vertically */
8368 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8369 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8370 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8372 /* don't scroll over playfield boundaries */
8373 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8374 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8376 /* don't scroll more than one field at a time */
8377 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8379 /* don't scroll against the player's moving direction */
8380 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8381 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8382 scroll_y = old_scroll_y;
8386 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8388 if (!options.network && !AllPlayersInVisibleScreen())
8390 scroll_x = old_scroll_x;
8391 scroll_y = old_scroll_y;
8395 ScrollScreen(player, SCROLL_INIT);
8396 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8403 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8405 if (!(moved & MF_MOVING) && !player->is_pushing)
8410 player->StepFrame = 0;
8412 if (moved & MF_MOVING)
8414 if (old_jx != jx && old_jy == jy)
8415 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8416 else if (old_jx == jx && old_jy != jy)
8417 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8419 DrawLevelField(jx, jy); /* for "crumbled sand" */
8421 player->last_move_dir = player->MovDir;
8422 player->is_moving = TRUE;
8424 player->is_snapping = FALSE;
8428 player->is_switching = FALSE;
8431 player->is_dropping = FALSE;
8436 static int trigger_sides[4][2] =
8438 /* enter side leave side */
8439 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8440 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8441 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8442 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8444 int move_direction = player->MovDir;
8445 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8446 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8449 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8451 CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8453 player->index_bit, leave_side);
8454 CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8456 player->index_bit, leave_side);
8459 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8461 CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
8462 CE_OTHER_GETS_ENTERED,
8463 player->index_bit, enter_side);
8464 CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
8465 player->index_bit, enter_side);
8476 CheckGravityMovementWhenNotMoving(player);
8479 player->last_move_dir = MV_NO_MOVING;
8481 player->is_moving = FALSE;
8484 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8486 TestIfHeroTouchesBadThing(jx, jy);
8487 TestIfPlayerTouchesCustomElement(jx, jy);
8490 if (!player->active)
8496 void ScrollPlayer(struct PlayerInfo *player, int mode)
8498 int jx = player->jx, jy = player->jy;
8499 int last_jx = player->last_jx, last_jy = player->last_jy;
8500 int move_stepsize = TILEX / player->move_delay_value;
8502 if (!player->active || !player->MovPos)
8505 if (mode == SCROLL_INIT)
8507 player->actual_frame_counter = FrameCounter;
8508 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8510 if (Feld[last_jx][last_jy] == EL_EMPTY)
8511 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8519 else if (!FrameReached(&player->actual_frame_counter, 1))
8522 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8523 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8525 if (!player->block_last_field &&
8526 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8527 Feld[last_jx][last_jy] = EL_EMPTY;
8529 /* before DrawPlayer() to draw correct player graphic for this case */
8530 if (player->MovPos == 0)
8531 CheckGravityMovement(player);
8534 DrawPlayer(player); /* needed here only to cleanup last field */
8537 if (player->MovPos == 0) /* player reached destination field */
8540 if (player->move_delay_reset_counter > 0)
8542 player->move_delay_reset_counter--;
8544 if (player->move_delay_reset_counter == 0)
8546 /* continue with normal speed after quickly moving through gate */
8547 HALVE_PLAYER_SPEED(player);
8549 /* be able to make the next move without delay */
8550 player->move_delay = 0;
8554 if (IS_PASSABLE(Feld[last_jx][last_jy]))
8556 /* continue with normal speed after quickly moving through gate */
8557 HALVE_PLAYER_SPEED(player);
8559 /* be able to make the next move without delay */
8560 player->move_delay = 0;
8564 if (player->block_last_field &&
8565 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8566 Feld[last_jx][last_jy] = EL_EMPTY;
8568 player->last_jx = jx;
8569 player->last_jy = jy;
8571 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8572 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8573 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8575 DrawPlayer(player); /* needed here only to cleanup last field */
8578 if (local_player->friends_still_needed == 0 ||
8579 IS_SP_ELEMENT(Feld[jx][jy]))
8580 player->LevelSolved = player->GameOver = TRUE;
8583 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8585 TestIfHeroTouchesBadThing(jx, jy);
8586 TestIfPlayerTouchesCustomElement(jx, jy);
8588 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8591 if (!player->active)
8595 if (level.use_step_counter)
8601 for (i = 0; i < MAX_PLAYERS; i++)
8603 struct PlayerInfo *player = &stored_player[i];
8605 if (SHIELD_ON(player))
8607 player->shield_normal_time_left--;
8609 if (player->shield_deadly_time_left > 0)
8610 player->shield_deadly_time_left--;
8618 if (TimeLeft <= 10 && setup.time_limit)
8619 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8621 DrawGameValue_Time(TimeLeft);
8623 if (!TimeLeft && setup.time_limit)
8624 for (i = 0; i < MAX_PLAYERS; i++)
8625 KillHero(&stored_player[i]);
8627 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8628 DrawGameValue_Time(TimePlayed);
8631 if (tape.single_step && tape.recording && !tape.pausing &&
8632 !player->programmed_action)
8633 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8637 void ScrollScreen(struct PlayerInfo *player, int mode)
8639 static unsigned long screen_frame_counter = 0;
8641 if (mode == SCROLL_INIT)
8643 /* set scrolling step size according to actual player's moving speed */
8644 ScrollStepSize = TILEX / player->move_delay_value;
8646 screen_frame_counter = FrameCounter;
8647 ScreenMovDir = player->MovDir;
8648 ScreenMovPos = player->MovPos;
8649 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8652 else if (!FrameReached(&screen_frame_counter, 1))
8657 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8658 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8659 redraw_mask |= REDRAW_FIELD;
8662 ScreenMovDir = MV_NO_MOVING;
8665 void TestIfPlayerTouchesCustomElement(int x, int y)
8667 static int xy[4][2] =
8674 static int trigger_sides[4][2] =
8676 /* center side border side */
8677 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8678 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8679 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8680 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8682 static int touch_dir[4] =
8689 int center_element = Feld[x][y]; /* should always be non-moving! */
8692 for (i = 0; i < NUM_DIRECTIONS; i++)
8694 int xx = x + xy[i][0];
8695 int yy = y + xy[i][1];
8696 int center_side = trigger_sides[i][0];
8697 int border_side = trigger_sides[i][1];
8700 if (!IN_LEV_FIELD(xx, yy))
8703 if (IS_PLAYER(x, y))
8705 struct PlayerInfo *player = PLAYERINFO(x, y);
8707 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8708 border_element = Feld[xx][yy]; /* may be moving! */
8709 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8710 border_element = Feld[xx][yy];
8711 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8712 border_element = MovingOrBlocked2Element(xx, yy);
8714 continue; /* center and border element do not touch */
8716 CheckTriggeredElementChangePlayer(xx, yy, border_element,
8717 CE_OTHER_GETS_TOUCHED,
8718 player->index_bit, border_side);
8719 CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
8720 player->index_bit, border_side);
8722 else if (IS_PLAYER(xx, yy))
8724 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8726 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8728 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8729 continue; /* center and border element do not touch */
8732 CheckTriggeredElementChangePlayer(x, y, center_element,
8733 CE_OTHER_GETS_TOUCHED,
8734 player->index_bit, center_side);
8735 CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
8736 player->index_bit, center_side);
8743 void TestIfElementTouchesCustomElement(int x, int y)
8745 static int xy[4][2] =
8752 static int trigger_sides[4][2] =
8754 /* center side border side */
8755 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8756 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8757 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8758 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8760 static int touch_dir[4] =
8767 boolean change_center_element = FALSE;
8768 int center_element_change_page = 0;
8769 int center_element = Feld[x][y]; /* should always be non-moving! */
8772 for (i = 0; i < NUM_DIRECTIONS; i++)
8774 int xx = x + xy[i][0];
8775 int yy = y + xy[i][1];
8776 int center_side = trigger_sides[i][0];
8777 int border_side = trigger_sides[i][1];
8780 if (!IN_LEV_FIELD(xx, yy))
8783 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8784 border_element = Feld[xx][yy]; /* may be moving! */
8785 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8786 border_element = Feld[xx][yy];
8787 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8788 border_element = MovingOrBlocked2Element(xx, yy);
8790 continue; /* center and border element do not touch */
8792 /* check for change of center element (but change it only once) */
8793 if (IS_CUSTOM_ELEMENT(center_element) &&
8794 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8795 !change_center_element)
8797 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8799 struct ElementChangeInfo *change =
8800 &element_info[center_element].change_page[j];
8802 if (change->can_change &&
8803 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8804 change->trigger_side & border_side &&
8806 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8808 change->trigger_element == border_element
8812 change_center_element = TRUE;
8813 center_element_change_page = j;
8820 /* check for change of border element */
8821 if (IS_CUSTOM_ELEMENT(border_element) &&
8822 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8824 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8826 struct ElementChangeInfo *change =
8827 &element_info[border_element].change_page[j];
8829 if (change->can_change &&
8830 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8831 change->trigger_side & center_side &&
8833 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8835 change->trigger_element == center_element
8839 CheckElementChangePage(xx, yy, border_element, CE_OTHER_IS_TOUCHING,
8847 if (change_center_element)
8848 CheckElementChangePage(x, y, center_element, CE_OTHER_IS_TOUCHING,
8849 center_element_change_page);
8852 void TestIfElementHitsCustomElement(int x, int y, int direction)
8854 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8855 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8856 int hitx = x + dx, hity = y + dy;
8857 int hitting_element = Feld[x][y];
8859 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8860 !IS_FREE(hitx, hity) &&
8861 (!IS_MOVING(hitx, hity) ||
8862 MovDir[hitx][hity] != direction ||
8863 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8866 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8870 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8874 CheckElementChangeSide(x, y, hitting_element, CE_HITTING_SOMETHING,
8877 if (IN_LEV_FIELD(hitx, hity))
8879 int opposite_direction = MV_DIR_OPPOSITE(direction);
8880 int hitting_side = direction;
8881 int touched_side = opposite_direction;
8882 int touched_element = MovingOrBlocked2Element(hitx, hity);
8884 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8885 MovDir[hitx][hity] != direction ||
8886 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8895 CheckElementChangeSide(hitx, hity, touched_element, CE_HIT_BY_SOMETHING,
8896 opposite_direction);
8898 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8899 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8901 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8903 struct ElementChangeInfo *change =
8904 &element_info[hitting_element].change_page[i];
8906 if (change->can_change &&
8907 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8908 change->trigger_side & touched_side &&
8911 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8913 change->trigger_element == touched_element
8917 CheckElementChangePage(x, y, hitting_element, CE_OTHER_IS_HITTING,
8924 if (IS_CUSTOM_ELEMENT(touched_element) &&
8925 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8927 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8929 struct ElementChangeInfo *change =
8930 &element_info[touched_element].change_page[i];
8932 if (change->can_change &&
8933 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8934 change->trigger_side & hitting_side &&
8936 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8938 change->trigger_element == hitting_element
8942 CheckElementChangePage(hitx, hity, touched_element,
8943 CE_OTHER_GETS_HIT, i);
8952 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8954 int i, kill_x = -1, kill_y = -1;
8955 static int test_xy[4][2] =
8962 static int test_dir[4] =
8970 for (i = 0; i < NUM_DIRECTIONS; i++)
8972 int test_x, test_y, test_move_dir, test_element;
8974 test_x = good_x + test_xy[i][0];
8975 test_y = good_y + test_xy[i][1];
8976 if (!IN_LEV_FIELD(test_x, test_y))
8980 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8983 test_element = Feld[test_x][test_y];
8985 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8988 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8989 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8991 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8992 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9000 if (kill_x != -1 || kill_y != -1)
9002 if (IS_PLAYER(good_x, good_y))
9004 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9006 if (player->shield_deadly_time_left > 0)
9007 Bang(kill_x, kill_y);
9008 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9012 Bang(good_x, good_y);
9016 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9018 int i, kill_x = -1, kill_y = -1;
9019 int bad_element = Feld[bad_x][bad_y];
9020 static int test_xy[4][2] =
9027 static int touch_dir[4] =
9034 static int test_dir[4] =
9042 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9045 for (i = 0; i < NUM_DIRECTIONS; i++)
9047 int test_x, test_y, test_move_dir, test_element;
9049 test_x = bad_x + test_xy[i][0];
9050 test_y = bad_y + test_xy[i][1];
9051 if (!IN_LEV_FIELD(test_x, test_y))
9055 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9057 test_element = Feld[test_x][test_y];
9059 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9060 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9062 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9063 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9065 /* good thing is player or penguin that does not move away */
9066 if (IS_PLAYER(test_x, test_y))
9068 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9070 if (bad_element == EL_ROBOT && player->is_moving)
9071 continue; /* robot does not kill player if he is moving */
9073 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9075 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9076 continue; /* center and border element do not touch */
9083 else if (test_element == EL_PENGUIN)
9092 if (kill_x != -1 || kill_y != -1)
9094 if (IS_PLAYER(kill_x, kill_y))
9096 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9098 if (player->shield_deadly_time_left > 0)
9100 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9104 Bang(kill_x, kill_y);
9108 void TestIfHeroTouchesBadThing(int x, int y)
9110 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9113 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
9115 TestIfGoodThingHitsBadThing(x, y, move_dir);
9118 void TestIfBadThingTouchesHero(int x, int y)
9120 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9123 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
9125 TestIfBadThingHitsGoodThing(x, y, move_dir);
9128 void TestIfFriendTouchesBadThing(int x, int y)
9130 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9133 void TestIfBadThingTouchesFriend(int x, int y)
9135 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9138 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9140 int i, kill_x = bad_x, kill_y = bad_y;
9141 static int xy[4][2] =
9149 for (i = 0; i < NUM_DIRECTIONS; i++)
9153 x = bad_x + xy[i][0];
9154 y = bad_y + xy[i][1];
9155 if (!IN_LEV_FIELD(x, y))
9158 element = Feld[x][y];
9159 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9160 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9168 if (kill_x != bad_x || kill_y != bad_y)
9172 void KillHero(struct PlayerInfo *player)
9174 int jx = player->jx, jy = player->jy;
9176 if (!player->active)
9179 /* remove accessible field at the player's position */
9180 Feld[jx][jy] = EL_EMPTY;
9182 /* deactivate shield (else Bang()/Explode() would not work right) */
9183 player->shield_normal_time_left = 0;
9184 player->shield_deadly_time_left = 0;
9190 static void KillHeroUnlessEnemyProtected(int x, int y)
9192 if (!PLAYER_ENEMY_PROTECTED(x, y))
9193 KillHero(PLAYERINFO(x, y));
9196 static void KillHeroUnlessExplosionProtected(int x, int y)
9198 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9199 KillHero(PLAYERINFO(x, y));
9202 void BuryHero(struct PlayerInfo *player)
9204 int jx = player->jx, jy = player->jy;
9206 if (!player->active)
9210 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9212 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
9214 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9216 player->GameOver = TRUE;
9220 void RemoveHero(struct PlayerInfo *player)
9222 int jx = player->jx, jy = player->jy;
9223 int i, found = FALSE;
9225 player->present = FALSE;
9226 player->active = FALSE;
9228 if (!ExplodeField[jx][jy])
9229 StorePlayer[jx][jy] = 0;
9231 for (i = 0; i < MAX_PLAYERS; i++)
9232 if (stored_player[i].active)
9236 AllPlayersGone = TRUE;
9243 =============================================================================
9244 checkDiagonalPushing()
9245 -----------------------------------------------------------------------------
9246 check if diagonal input device direction results in pushing of object
9247 (by checking if the alternative direction is walkable, diggable, ...)
9248 =============================================================================
9251 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9252 int x, int y, int real_dx, int real_dy)
9254 int jx, jy, dx, dy, xx, yy;
9256 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9259 /* diagonal direction: check alternative direction */
9264 xx = jx + (dx == 0 ? real_dx : 0);
9265 yy = jy + (dy == 0 ? real_dy : 0);
9267 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9271 =============================================================================
9273 -----------------------------------------------------------------------------
9274 x, y: field next to player (non-diagonal) to try to dig to
9275 real_dx, real_dy: direction as read from input device (can be diagonal)
9276 =============================================================================
9279 int DigField(struct PlayerInfo *player,
9280 int oldx, int oldy, int x, int y,
9281 int real_dx, int real_dy, int mode)
9283 static int trigger_sides[4] =
9285 CH_SIDE_RIGHT, /* moving left */
9286 CH_SIDE_LEFT, /* moving right */
9287 CH_SIDE_BOTTOM, /* moving up */
9288 CH_SIDE_TOP, /* moving down */
9291 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
9293 int jx = oldx, jy = oldy;
9294 int dx = x - jx, dy = y - jy;
9295 int nextx = x + dx, nexty = y + dy;
9296 int move_direction = (dx == -1 ? MV_LEFT :
9297 dx == +1 ? MV_RIGHT :
9299 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9300 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9301 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
9302 int old_element = Feld[jx][jy];
9305 if (player->MovPos == 0)
9307 player->is_digging = FALSE;
9308 player->is_collecting = FALSE;
9311 if (player->MovPos == 0) /* last pushing move finished */
9312 player->is_pushing = FALSE;
9314 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9316 player->is_switching = FALSE;
9317 player->push_delay = 0;
9319 return MF_NO_ACTION;
9322 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9323 return MF_NO_ACTION;
9328 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
9330 if (IS_TUBE(Feld[jx][jy]) ||
9331 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
9335 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
9336 int tube_leave_directions[][2] =
9338 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9339 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9340 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9341 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
9342 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
9343 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
9344 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
9345 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
9346 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
9347 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
9348 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
9349 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
9352 while (tube_leave_directions[i][0] != tube_element)
9355 if (tube_leave_directions[i][0] == -1) /* should not happen */
9359 if (!(tube_leave_directions[i][1] & move_direction))
9360 return MF_NO_ACTION; /* tube has no opening in this direction */
9365 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9366 old_element = Back[jx][jy];
9370 if (IS_WALKABLE(old_element) &&
9371 !(element_info[old_element].access_direction & move_direction))
9372 return MF_NO_ACTION; /* field has no opening in this direction */
9374 element = Feld[x][y];
9376 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9377 game.engine_version >= VERSION_IDENT(2,2,0,0))
9378 return MF_NO_ACTION;
9382 case EL_SP_PORT_LEFT:
9383 case EL_SP_PORT_RIGHT:
9385 case EL_SP_PORT_DOWN:
9386 case EL_SP_PORT_HORIZONTAL:
9387 case EL_SP_PORT_VERTICAL:
9388 case EL_SP_PORT_ANY:
9389 case EL_SP_GRAVITY_PORT_LEFT:
9390 case EL_SP_GRAVITY_PORT_RIGHT:
9391 case EL_SP_GRAVITY_PORT_UP:
9392 case EL_SP_GRAVITY_PORT_DOWN:
9394 if (!canEnterSupaplexPort(x, y, dx, dy))
9395 return MF_NO_ACTION;
9398 element != EL_SP_PORT_LEFT &&
9399 element != EL_SP_GRAVITY_PORT_LEFT &&
9400 element != EL_SP_PORT_HORIZONTAL &&
9401 element != EL_SP_PORT_ANY) ||
9403 element != EL_SP_PORT_RIGHT &&
9404 element != EL_SP_GRAVITY_PORT_RIGHT &&
9405 element != EL_SP_PORT_HORIZONTAL &&
9406 element != EL_SP_PORT_ANY) ||
9408 element != EL_SP_PORT_UP &&
9409 element != EL_SP_GRAVITY_PORT_UP &&
9410 element != EL_SP_PORT_VERTICAL &&
9411 element != EL_SP_PORT_ANY) ||
9413 element != EL_SP_PORT_DOWN &&
9414 element != EL_SP_GRAVITY_PORT_DOWN &&
9415 element != EL_SP_PORT_VERTICAL &&
9416 element != EL_SP_PORT_ANY) ||
9417 !IN_LEV_FIELD(nextx, nexty) ||
9418 !IS_FREE(nextx, nexty))
9419 return MF_NO_ACTION;
9422 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9423 element == EL_SP_GRAVITY_PORT_RIGHT ||
9424 element == EL_SP_GRAVITY_PORT_UP ||
9425 element == EL_SP_GRAVITY_PORT_DOWN)
9426 game.gravity = !game.gravity;
9428 /* automatically move to the next field with double speed */
9429 player->programmed_action = move_direction;
9431 if (player->move_delay_reset_counter == 0)
9433 player->move_delay_reset_counter = 2; /* two double speed steps */
9435 DOUBLE_PLAYER_SPEED(player);
9438 player->move_delay_reset_counter = 2;
9440 DOUBLE_PLAYER_SPEED(player);
9443 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9448 case EL_TUBE_VERTICAL:
9449 case EL_TUBE_HORIZONTAL:
9450 case EL_TUBE_VERTICAL_LEFT:
9451 case EL_TUBE_VERTICAL_RIGHT:
9452 case EL_TUBE_HORIZONTAL_UP:
9453 case EL_TUBE_HORIZONTAL_DOWN:
9454 case EL_TUBE_LEFT_UP:
9455 case EL_TUBE_LEFT_DOWN:
9456 case EL_TUBE_RIGHT_UP:
9457 case EL_TUBE_RIGHT_DOWN:
9460 int tube_enter_directions[][2] =
9462 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9463 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9464 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9465 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
9466 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
9467 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
9468 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
9469 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
9470 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
9471 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
9472 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
9473 { -1, MV_NO_MOVING }
9476 while (tube_enter_directions[i][0] != element)
9479 if (tube_enter_directions[i][0] == -1) /* should not happen */
9483 if (!(tube_enter_directions[i][1] & move_direction))
9484 return MF_NO_ACTION; /* tube has no opening in this direction */
9486 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9493 if (IS_WALKABLE(element))
9495 int sound_action = ACTION_WALKING;
9497 if (!(element_info[element].access_direction & opposite_direction))
9498 return MF_NO_ACTION; /* field not accessible from this direction */
9500 if (element >= EL_GATE_1 && element <= EL_GATE_4)
9502 if (!player->key[element - EL_GATE_1])
9503 return MF_NO_ACTION;
9505 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9507 if (!player->key[element - EL_GATE_1_GRAY])
9508 return MF_NO_ACTION;
9510 else if (element == EL_EXIT_OPEN ||
9511 element == EL_SP_EXIT_OPEN ||
9512 element == EL_SP_EXIT_OPENING)
9514 sound_action = ACTION_PASSING; /* player is passing exit */
9516 else if (element == EL_EMPTY)
9518 sound_action = ACTION_MOVING; /* nothing to walk on */
9521 /* play sound from background or player, whatever is available */
9522 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9523 PlayLevelSoundElementAction(x, y, element, sound_action);
9525 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9529 else if (IS_PASSABLE(element))
9531 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9532 return MF_NO_ACTION;
9534 if (IS_CUSTOM_ELEMENT(element) &&
9535 !(element_info[element].access_direction & opposite_direction))
9536 return MF_NO_ACTION; /* field not accessible from this direction */
9539 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9540 return MF_NO_ACTION;
9543 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9545 if (!player->key[element - EL_EM_GATE_1])
9546 return MF_NO_ACTION;
9548 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9550 if (!player->key[element - EL_EM_GATE_1_GRAY])
9551 return MF_NO_ACTION;
9554 /* automatically move to the next field with double speed */
9555 player->programmed_action = move_direction;
9557 if (player->move_delay_reset_counter == 0)
9559 player->move_delay_reset_counter = 2; /* two double speed steps */
9561 DOUBLE_PLAYER_SPEED(player);
9564 player->move_delay_reset_counter = 2;
9566 DOUBLE_PLAYER_SPEED(player);
9569 PlayLevelSoundAction(x, y, ACTION_PASSING);
9573 else if (IS_DIGGABLE(element))
9577 if (mode != DF_SNAP)
9580 GfxElement[x][y] = GFX_ELEMENT(element);
9583 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9585 player->is_digging = TRUE;
9588 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9590 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED,
9591 player->index_bit, CH_SIDE_ANY);
9594 if (mode == DF_SNAP)
9595 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9600 else if (IS_COLLECTIBLE(element))
9604 if (mode != DF_SNAP)
9606 GfxElement[x][y] = element;
9607 player->is_collecting = TRUE;
9610 if (element == EL_SPEED_PILL)
9611 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9612 else if (element == EL_EXTRA_TIME && level.time > 0)
9615 DrawGameValue_Time(TimeLeft);
9617 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9619 player->shield_normal_time_left += 10;
9620 if (element == EL_SHIELD_DEADLY)
9621 player->shield_deadly_time_left += 10;
9623 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9625 if (player->inventory_size < MAX_INVENTORY_SIZE)
9626 player->inventory_element[player->inventory_size++] = element;
9628 DrawGameValue_Dynamite(local_player->inventory_size);
9630 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9632 player->dynabomb_count++;
9633 player->dynabombs_left++;
9635 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9637 player->dynabomb_size++;
9639 else if (element == EL_DYNABOMB_INCREASE_POWER)
9641 player->dynabomb_xl = TRUE;
9643 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9644 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9646 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9647 element - EL_KEY_1 : element - EL_EM_KEY_1);
9649 player->key[key_nr] = TRUE;
9651 DrawGameValue_Keys(player);
9653 redraw_mask |= REDRAW_DOOR_1;
9655 else if (IS_ENVELOPE(element))
9658 player->show_envelope = element;
9660 ShowEnvelope(element - EL_ENVELOPE_1);
9663 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9667 if (element_info[element].collect_count == 0)
9668 player->inventory_infinite_element = element;
9670 for (i = 0; i < element_info[element].collect_count; i++)
9671 if (player->inventory_size < MAX_INVENTORY_SIZE)
9672 player->inventory_element[player->inventory_size++] = element;
9674 DrawGameValue_Dynamite(local_player->inventory_size);
9676 else if (element_info[element].collect_count > 0)
9678 local_player->gems_still_needed -=
9679 element_info[element].collect_count;
9680 if (local_player->gems_still_needed < 0)
9681 local_player->gems_still_needed = 0;
9683 DrawGameValue_Emeralds(local_player->gems_still_needed);
9686 RaiseScoreElement(element);
9687 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9689 CheckTriggeredElementChangePlayer(x, y, element,
9690 CE_OTHER_GETS_COLLECTED,
9691 player->index_bit, CH_SIDE_ANY);
9694 if (mode == DF_SNAP)
9695 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9700 else if (IS_PUSHABLE(element))
9702 if (mode == DF_SNAP && element != EL_BD_ROCK)
9703 return MF_NO_ACTION;
9705 if (CAN_FALL(element) && dy)
9706 return MF_NO_ACTION;
9708 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9709 !(element == EL_SPRING && level.use_spring_bug))
9710 return MF_NO_ACTION;
9713 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9714 ((move_direction & MV_VERTICAL &&
9715 ((element_info[element].move_pattern & MV_LEFT &&
9716 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9717 (element_info[element].move_pattern & MV_RIGHT &&
9718 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9719 (move_direction & MV_HORIZONTAL &&
9720 ((element_info[element].move_pattern & MV_UP &&
9721 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9722 (element_info[element].move_pattern & MV_DOWN &&
9723 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9724 return MF_NO_ACTION;
9728 /* do not push elements already moving away faster than player */
9729 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9730 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9731 return MF_NO_ACTION;
9733 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9734 return MF_NO_ACTION;
9738 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9740 if (player->push_delay_value == -1)
9741 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9743 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9745 if (!player->is_pushing)
9746 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9750 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9751 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9752 !player_is_pushing))
9753 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9756 if (!player->is_pushing &&
9757 game.engine_version >= VERSION_IDENT(2,2,0,7))
9758 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9762 printf("::: push delay: %ld [%d, %d] [%d]\n",
9763 player->push_delay_value, FrameCounter, game.engine_version,
9764 player->is_pushing);
9767 player->is_pushing = TRUE;
9769 if (!(IN_LEV_FIELD(nextx, nexty) &&
9770 (IS_FREE(nextx, nexty) ||
9771 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9772 IS_SB_ELEMENT(element)))))
9773 return MF_NO_ACTION;
9775 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9776 return MF_NO_ACTION;
9778 if (player->push_delay == 0) /* new pushing; restart delay */
9779 player->push_delay = FrameCounter;
9781 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9782 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9783 element != EL_SPRING && element != EL_BALLOON)
9785 /* make sure that there is no move delay before next try to push */
9786 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9787 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9789 return MF_NO_ACTION;
9793 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9796 if (IS_SB_ELEMENT(element))
9798 if (element == EL_SOKOBAN_FIELD_FULL)
9800 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9801 local_player->sokobanfields_still_needed++;
9804 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9806 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9807 local_player->sokobanfields_still_needed--;
9810 Feld[x][y] = EL_SOKOBAN_OBJECT;
9812 if (Back[x][y] == Back[nextx][nexty])
9813 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9814 else if (Back[x][y] != 0)
9815 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9818 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9821 if (local_player->sokobanfields_still_needed == 0 &&
9822 game.emulation == EMU_SOKOBAN)
9824 player->LevelSolved = player->GameOver = TRUE;
9825 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9829 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9831 InitMovingField(x, y, move_direction);
9832 GfxAction[x][y] = ACTION_PUSHING;
9834 if (mode == DF_SNAP)
9835 ContinueMoving(x, y);
9837 MovPos[x][y] = (dx != 0 ? dx : dy);
9839 Pushed[x][y] = TRUE;
9840 Pushed[nextx][nexty] = TRUE;
9842 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9843 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9845 player->push_delay_value = -1; /* get new value later */
9847 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED,
9848 player->index_bit, dig_side);
9849 CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER,
9850 player->index_bit, dig_side);
9854 else if (IS_SWITCHABLE(element))
9856 if (PLAYER_SWITCHING(player, x, y))
9859 player->is_switching = TRUE;
9860 player->switch_x = x;
9861 player->switch_y = y;
9863 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9865 if (element == EL_ROBOT_WHEEL)
9867 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9871 DrawLevelField(x, y);
9873 else if (element == EL_SP_TERMINAL)
9877 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9879 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9881 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9882 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9885 else if (IS_BELT_SWITCH(element))
9887 ToggleBeltSwitch(x, y);
9889 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9890 element == EL_SWITCHGATE_SWITCH_DOWN)
9892 ToggleSwitchgateSwitch(x, y);
9894 else if (element == EL_LIGHT_SWITCH ||
9895 element == EL_LIGHT_SWITCH_ACTIVE)
9897 ToggleLightSwitch(x, y);
9900 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9901 SND_LIGHT_SWITCH_ACTIVATING :
9902 SND_LIGHT_SWITCH_DEACTIVATING);
9905 else if (element == EL_TIMEGATE_SWITCH)
9907 ActivateTimegateSwitch(x, y);
9909 else if (element == EL_BALLOON_SWITCH_LEFT ||
9910 element == EL_BALLOON_SWITCH_RIGHT ||
9911 element == EL_BALLOON_SWITCH_UP ||
9912 element == EL_BALLOON_SWITCH_DOWN ||
9913 element == EL_BALLOON_SWITCH_ANY)
9915 if (element == EL_BALLOON_SWITCH_ANY)
9916 game.balloon_dir = move_direction;
9918 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9919 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9920 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9921 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9924 else if (element == EL_LAMP)
9926 Feld[x][y] = EL_LAMP_ACTIVE;
9927 local_player->lights_still_needed--;
9929 DrawLevelField(x, y);
9931 else if (element == EL_TIME_ORB_FULL)
9933 Feld[x][y] = EL_TIME_ORB_EMPTY;
9935 DrawGameValue_Time(TimeLeft);
9937 DrawLevelField(x, y);
9940 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9948 if (!PLAYER_SWITCHING(player, x, y))
9950 player->is_switching = TRUE;
9951 player->switch_x = x;
9952 player->switch_y = y;
9954 CheckTriggeredElementChangePlayer(x, y, element,
9955 CE_OTHER_IS_SWITCHING,
9956 player->index_bit, dig_side);
9957 CheckElementChangePlayer(x, y, element, CE_SWITCHED,
9958 player->index_bit, dig_side);
9961 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED,
9962 player->index_bit, dig_side);
9963 CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER,
9964 player->index_bit, dig_side);
9967 return MF_NO_ACTION;
9970 player->push_delay = 0;
9972 if (Feld[x][y] != element) /* really digged/collected something */
9973 player->is_collecting = !player->is_digging;
9978 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9980 int jx = player->jx, jy = player->jy;
9981 int x = jx + dx, y = jy + dy;
9982 int snap_direction = (dx == -1 ? MV_LEFT :
9983 dx == +1 ? MV_RIGHT :
9985 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9987 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9990 if (!player->active || !IN_LEV_FIELD(x, y))
9998 if (player->MovPos == 0)
9999 player->is_pushing = FALSE;
10001 player->is_snapping = FALSE;
10003 if (player->MovPos == 0)
10005 player->is_moving = FALSE;
10006 player->is_digging = FALSE;
10007 player->is_collecting = FALSE;
10013 if (player->is_snapping)
10016 player->MovDir = snap_direction;
10019 if (player->MovPos == 0)
10022 player->is_moving = FALSE;
10023 player->is_digging = FALSE;
10024 player->is_collecting = FALSE;
10027 player->is_dropping = FALSE;
10029 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10032 player->is_snapping = TRUE;
10035 if (player->MovPos == 0)
10038 player->is_moving = FALSE;
10039 player->is_digging = FALSE;
10040 player->is_collecting = FALSE;
10043 DrawLevelField(x, y);
10049 boolean DropElement(struct PlayerInfo *player)
10051 int jx = player->jx, jy = player->jy;
10052 int old_element = Feld[jx][jy];
10053 int new_element = (player->inventory_size > 0 ?
10054 player->inventory_element[player->inventory_size - 1] :
10055 player->inventory_infinite_element != EL_UNDEFINED ?
10056 player->inventory_infinite_element :
10057 player->dynabombs_left > 0 ?
10058 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10061 /* check if player is active, not moving and ready to drop */
10062 if (!player->active || player->MovPos || player->drop_delay > 0)
10065 /* check if player has anything that can be dropped */
10067 if (new_element == EL_UNDEFINED)
10070 if (player->inventory_size == 0 &&
10071 player->inventory_infinite_element == EL_UNDEFINED &&
10072 player->dynabombs_left == 0)
10076 /* check if anything can be dropped at the current position */
10077 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10080 /* collected custom elements can only be dropped on empty fields */
10082 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10085 if (player->inventory_size > 0 &&
10086 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
10087 && old_element != EL_EMPTY)
10091 if (old_element != EL_EMPTY)
10092 Back[jx][jy] = old_element; /* store old element on this field */
10094 ResetGfxAnimation(jx, jy);
10095 ResetRandomAnimationValue(jx, jy);
10097 if (player->inventory_size > 0 ||
10098 player->inventory_infinite_element != EL_UNDEFINED)
10100 if (player->inventory_size > 0)
10102 player->inventory_size--;
10105 new_element = player->inventory_element[player->inventory_size];
10108 DrawGameValue_Dynamite(local_player->inventory_size);
10110 if (new_element == EL_DYNAMITE)
10111 new_element = EL_DYNAMITE_ACTIVE;
10112 else if (new_element == EL_SP_DISK_RED)
10113 new_element = EL_SP_DISK_RED_ACTIVE;
10116 Feld[jx][jy] = new_element;
10118 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10119 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10121 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10124 /* needed if previous element just changed to "empty" in the last frame */
10125 Changed[jx][jy] = 0; /* allow another change */
10128 CheckTriggeredElementChangePlayer(jx, jy, new_element,
10129 CE_OTHER_GETS_DROPPED,
10130 player->index_bit, CH_SIDE_ANY);
10131 CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
10132 player->index_bit, CH_SIDE_ANY);
10134 TestIfElementTouchesCustomElement(jx, jy);
10136 else /* player is dropping a dyna bomb */
10138 player->dynabombs_left--;
10141 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
10144 Feld[jx][jy] = new_element;
10146 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10147 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10149 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10156 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
10159 InitField_WithBug1(jx, jy, FALSE);
10161 InitField(jx, jy, FALSE);
10162 if (CAN_MOVE(Feld[jx][jy]))
10163 InitMovDir(jx, jy);
10167 new_element = Feld[jx][jy];
10169 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10170 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10172 int move_stepsize = element_info[new_element].move_stepsize;
10173 int direction, dx, dy, nextx, nexty;
10175 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10176 MovDir[jx][jy] = player->MovDir;
10178 direction = MovDir[jx][jy];
10179 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10180 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10184 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
10187 WasJustMoving[jx][jy] = 3;
10189 InitMovingField(jx, jy, direction);
10190 ContinueMoving(jx, jy);
10195 Changed[jx][jy] = 0; /* allow another change */
10198 TestIfElementHitsCustomElement(jx, jy, direction);
10200 CheckElementChangeSide(jx, jy, new_element, CE_HITTING_SOMETHING,
10205 player->drop_delay = 2 * TILEX / move_stepsize + 1;
10209 player->drop_delay = 8 + 8 + 8;
10214 player->is_dropping = TRUE;
10220 /* ------------------------------------------------------------------------- */
10221 /* game sound playing functions */
10222 /* ------------------------------------------------------------------------- */
10224 static int *loop_sound_frame = NULL;
10225 static int *loop_sound_volume = NULL;
10227 void InitPlayLevelSound()
10229 int num_sounds = getSoundListSize();
10231 checked_free(loop_sound_frame);
10232 checked_free(loop_sound_volume);
10234 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10235 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10238 static void PlayLevelSound(int x, int y, int nr)
10240 int sx = SCREENX(x), sy = SCREENY(y);
10241 int volume, stereo_position;
10242 int max_distance = 8;
10243 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10245 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10246 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10249 if (!IN_LEV_FIELD(x, y) ||
10250 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10251 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10254 volume = SOUND_MAX_VOLUME;
10256 if (!IN_SCR_FIELD(sx, sy))
10258 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10259 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10261 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10264 stereo_position = (SOUND_MAX_LEFT +
10265 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10266 (SCR_FIELDX + 2 * max_distance));
10268 if (IS_LOOP_SOUND(nr))
10270 /* This assures that quieter loop sounds do not overwrite louder ones,
10271 while restarting sound volume comparison with each new game frame. */
10273 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10276 loop_sound_volume[nr] = volume;
10277 loop_sound_frame[nr] = FrameCounter;
10280 PlaySoundExt(nr, volume, stereo_position, type);
10283 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10285 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10286 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10287 y < LEVELY(BY1) ? LEVELY(BY1) :
10288 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10292 static void PlayLevelSoundAction(int x, int y, int action)
10294 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10297 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10299 int sound_effect = element_info[element].sound[action];
10301 if (sound_effect != SND_UNDEFINED)
10302 PlayLevelSound(x, y, sound_effect);
10305 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10308 int sound_effect = element_info[element].sound[action];
10310 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10311 PlayLevelSound(x, y, sound_effect);
10314 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10316 int sound_effect = element_info[Feld[x][y]].sound[action];
10318 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10319 PlayLevelSound(x, y, sound_effect);
10322 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10324 int sound_effect = element_info[Feld[x][y]].sound[action];
10326 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10327 StopSound(sound_effect);
10330 static void PlayLevelMusic()
10332 if (levelset.music[level_nr] != MUS_UNDEFINED)
10333 PlayMusic(levelset.music[level_nr]); /* from config file */
10335 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10338 void RaiseScore(int value)
10340 local_player->score += value;
10342 DrawGameValue_Score(local_player->score);
10345 void RaiseScoreElement(int element)
10350 case EL_BD_DIAMOND:
10351 case EL_EMERALD_YELLOW:
10352 case EL_EMERALD_RED:
10353 case EL_EMERALD_PURPLE:
10354 case EL_SP_INFOTRON:
10355 RaiseScore(level.score[SC_EMERALD]);
10358 RaiseScore(level.score[SC_DIAMOND]);
10361 RaiseScore(level.score[SC_CRYSTAL]);
10364 RaiseScore(level.score[SC_PEARL]);
10367 case EL_BD_BUTTERFLY:
10368 case EL_SP_ELECTRON:
10369 RaiseScore(level.score[SC_BUG]);
10372 case EL_BD_FIREFLY:
10373 case EL_SP_SNIKSNAK:
10374 RaiseScore(level.score[SC_SPACESHIP]);
10377 case EL_DARK_YAMYAM:
10378 RaiseScore(level.score[SC_YAMYAM]);
10381 RaiseScore(level.score[SC_ROBOT]);
10384 RaiseScore(level.score[SC_PACMAN]);
10387 RaiseScore(level.score[SC_NUT]);
10390 case EL_SP_DISK_RED:
10391 case EL_DYNABOMB_INCREASE_NUMBER:
10392 case EL_DYNABOMB_INCREASE_SIZE:
10393 case EL_DYNABOMB_INCREASE_POWER:
10394 RaiseScore(level.score[SC_DYNAMITE]);
10396 case EL_SHIELD_NORMAL:
10397 case EL_SHIELD_DEADLY:
10398 RaiseScore(level.score[SC_SHIELD]);
10400 case EL_EXTRA_TIME:
10401 RaiseScore(level.score[SC_TIME_BONUS]);
10407 RaiseScore(level.score[SC_KEY]);
10410 RaiseScore(element_info[element].collect_score);
10415 void RequestQuitGame(boolean ask_if_really_quit)
10417 if (AllPlayersGone ||
10418 !ask_if_really_quit ||
10419 level_editor_test_game ||
10420 Request("Do you really want to quit the game ?",
10421 REQ_ASK | REQ_STAY_CLOSED))
10423 #if defined(PLATFORM_UNIX)
10424 if (options.network)
10425 SendToServer_StopPlaying();
10429 game_status = GAME_MODE_MAIN;
10435 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10440 /* ---------- new game button stuff ---------------------------------------- */
10442 /* graphic position values for game buttons */
10443 #define GAME_BUTTON_XSIZE 30
10444 #define GAME_BUTTON_YSIZE 30
10445 #define GAME_BUTTON_XPOS 5
10446 #define GAME_BUTTON_YPOS 215
10447 #define SOUND_BUTTON_XPOS 5
10448 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10450 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10451 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10452 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10453 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10454 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10455 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10462 } gamebutton_info[NUM_GAME_BUTTONS] =
10465 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10470 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10471 GAME_CTRL_ID_PAUSE,
10475 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10480 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10481 SOUND_CTRL_ID_MUSIC,
10482 "background music on/off"
10485 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10486 SOUND_CTRL_ID_LOOPS,
10487 "sound loops on/off"
10490 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10491 SOUND_CTRL_ID_SIMPLE,
10492 "normal sounds on/off"
10496 void CreateGameButtons()
10500 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10502 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10503 struct GadgetInfo *gi;
10506 unsigned long event_mask;
10507 int gd_xoffset, gd_yoffset;
10508 int gd_x1, gd_x2, gd_y1, gd_y2;
10511 gd_xoffset = gamebutton_info[i].x;
10512 gd_yoffset = gamebutton_info[i].y;
10513 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10514 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10516 if (id == GAME_CTRL_ID_STOP ||
10517 id == GAME_CTRL_ID_PAUSE ||
10518 id == GAME_CTRL_ID_PLAY)
10520 button_type = GD_TYPE_NORMAL_BUTTON;
10522 event_mask = GD_EVENT_RELEASED;
10523 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10524 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10528 button_type = GD_TYPE_CHECK_BUTTON;
10530 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10531 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10532 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10533 event_mask = GD_EVENT_PRESSED;
10534 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10535 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10538 gi = CreateGadget(GDI_CUSTOM_ID, id,
10539 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10540 GDI_X, DX + gd_xoffset,
10541 GDI_Y, DY + gd_yoffset,
10542 GDI_WIDTH, GAME_BUTTON_XSIZE,
10543 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10544 GDI_TYPE, button_type,
10545 GDI_STATE, GD_BUTTON_UNPRESSED,
10546 GDI_CHECKED, checked,
10547 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10548 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10549 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10550 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10551 GDI_EVENT_MASK, event_mask,
10552 GDI_CALLBACK_ACTION, HandleGameButtons,
10556 Error(ERR_EXIT, "cannot create gadget");
10558 game_gadget[id] = gi;
10562 void FreeGameButtons()
10566 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10567 FreeGadget(game_gadget[i]);
10570 static void MapGameButtons()
10574 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10575 MapGadget(game_gadget[i]);
10578 void UnmapGameButtons()
10582 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10583 UnmapGadget(game_gadget[i]);
10586 static void HandleGameButtons(struct GadgetInfo *gi)
10588 int id = gi->custom_id;
10590 if (game_status != GAME_MODE_PLAYING)
10595 case GAME_CTRL_ID_STOP:
10596 RequestQuitGame(TRUE);
10599 case GAME_CTRL_ID_PAUSE:
10600 if (options.network)
10602 #if defined(PLATFORM_UNIX)
10604 SendToServer_ContinuePlaying();
10606 SendToServer_PausePlaying();
10610 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10613 case GAME_CTRL_ID_PLAY:
10616 #if defined(PLATFORM_UNIX)
10617 if (options.network)
10618 SendToServer_ContinuePlaying();
10622 tape.pausing = FALSE;
10623 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10628 case SOUND_CTRL_ID_MUSIC:
10629 if (setup.sound_music)
10631 setup.sound_music = FALSE;
10634 else if (audio.music_available)
10636 setup.sound = setup.sound_music = TRUE;
10638 SetAudioMode(setup.sound);
10644 case SOUND_CTRL_ID_LOOPS:
10645 if (setup.sound_loops)
10646 setup.sound_loops = FALSE;
10647 else if (audio.loops_available)
10649 setup.sound = setup.sound_loops = TRUE;
10650 SetAudioMode(setup.sound);
10654 case SOUND_CTRL_ID_SIMPLE:
10655 if (setup.sound_simple)
10656 setup.sound_simple = FALSE;
10657 else if (audio.sound_available)
10659 setup.sound = setup.sound_simple = TRUE;
10660 SetAudioMode(setup.sound);