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);
7385 for (i = 0; i < MAX_PLAYERS; i++)
7387 summarized_player_action |= stored_player[i].action;
7389 if (!network_playing)
7390 stored_player[i].effective_action = stored_player[i].action;
7393 #if defined(PLATFORM_UNIX)
7394 if (network_playing)
7395 SendToServer_MovePlayer(summarized_player_action);
7398 if (!options.network && !setup.team_mode)
7399 local_player->effective_action = summarized_player_action;
7401 for (i = 0; i < MAX_PLAYERS; i++)
7403 int actual_player_action = stored_player[i].effective_action;
7406 /* OLD: overwrite programmed action with tape action (BAD!!!) */
7407 if (stored_player[i].programmed_action)
7408 actual_player_action = stored_player[i].programmed_action;
7411 if (recorded_player_action)
7414 if (stored_player[i].programmed_action &&
7415 stored_player[i].programmed_action != recorded_player_action[i])
7416 printf("::: %d <-> %d\n",
7417 stored_player[i].programmed_action, recorded_player_action[i]);
7420 actual_player_action = recorded_player_action[i];
7424 /* NEW: overwrite tape action with programmed action */
7425 if (stored_player[i].programmed_action)
7426 actual_player_action = stored_player[i].programmed_action;
7429 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7431 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7432 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7434 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7439 TapeRecordAction(tape_action);
7442 network_player_action_received = FALSE;
7444 ScrollScreen(NULL, SCROLL_GO_ON);
7450 for (i = 0; i < MAX_PLAYERS; i++)
7451 stored_player[i].Frame++;
7455 /* for downwards compatibility, the following code emulates a fixed bug that
7456 occured when pushing elements (causing elements that just made their last
7457 pushing step to already (if possible) make their first falling step in the
7458 same game frame, which is bad); this code is also needed to use the famous
7459 "spring push bug" which is used in older levels and might be wanted to be
7460 used also in newer levels, but in this case the buggy pushing code is only
7461 affecting the "spring" element and no other elements */
7464 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7466 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7469 for (i = 0; i < MAX_PLAYERS; i++)
7471 struct PlayerInfo *player = &stored_player[i];
7476 if (player->active && player->is_pushing && player->is_moving &&
7478 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7479 Feld[x][y] == EL_SPRING))
7481 if (player->active && player->is_pushing && player->is_moving &&
7485 ContinueMoving(x, y);
7487 /* continue moving after pushing (this is actually a bug) */
7488 if (!IS_MOVING(x, y))
7497 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7499 Changed[x][y] = CE_BITMASK_DEFAULT;
7500 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7503 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7505 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7506 printf("GameActions(): This should never happen!\n");
7508 ChangePage[x][y] = -1;
7513 if (WasJustMoving[x][y] > 0)
7514 WasJustMoving[x][y]--;
7515 if (WasJustFalling[x][y] > 0)
7516 WasJustFalling[x][y]--;
7521 /* reset finished pushing action (not done in ContinueMoving() to allow
7522 continous pushing animation for elements with zero push delay) */
7523 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7525 ResetGfxAnimation(x, y);
7526 DrawLevelField(x, y);
7531 if (IS_BLOCKED(x, y))
7535 Blocked2Moving(x, y, &oldx, &oldy);
7536 if (!IS_MOVING(oldx, oldy))
7538 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7539 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7540 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7541 printf("GameActions(): This should never happen!\n");
7547 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7549 element = Feld[x][y];
7551 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7553 graphic = el2img(element);
7559 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7561 element = graphic = 0;
7565 if (graphic_info[graphic].anim_global_sync)
7566 GfxFrame[x][y] = FrameCounter;
7568 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7569 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7570 ResetRandomAnimationValue(x, y);
7572 SetRandomAnimationValue(x, y);
7575 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7578 if (IS_INACTIVE(element))
7580 if (IS_ANIMATED(graphic))
7581 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7587 /* this may take place after moving, so 'element' may have changed */
7589 if (IS_CHANGING(x, y))
7591 if (IS_CHANGING(x, y) &&
7592 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7596 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7597 element_info[element].event_page_nr[CE_DELAY]);
7599 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7602 element = Feld[x][y];
7603 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7607 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7612 element = Feld[x][y];
7613 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7615 if (element == EL_MOLE)
7616 printf("::: %d, %d, %d [%d]\n",
7617 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7621 if (element == EL_YAMYAM)
7622 printf("::: %d, %d, %d\n",
7623 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7627 if (IS_ANIMATED(graphic) &&
7631 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7634 if (element == EL_BUG)
7635 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7639 if (element == EL_MOLE)
7640 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7644 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7645 EdelsteinFunkeln(x, y);
7647 else if ((element == EL_ACID ||
7648 element == EL_EXIT_OPEN ||
7649 element == EL_SP_EXIT_OPEN ||
7650 element == EL_SP_TERMINAL ||
7651 element == EL_SP_TERMINAL_ACTIVE ||
7652 element == EL_EXTRA_TIME ||
7653 element == EL_SHIELD_NORMAL ||
7654 element == EL_SHIELD_DEADLY) &&
7655 IS_ANIMATED(graphic))
7656 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7657 else if (IS_MOVING(x, y))
7658 ContinueMoving(x, y);
7659 else if (IS_ACTIVE_BOMB(element))
7660 CheckDynamite(x, y);
7662 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7663 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7665 else if (element == EL_AMOEBA_GROWING)
7666 AmoebeWaechst(x, y);
7667 else if (element == EL_AMOEBA_SHRINKING)
7668 AmoebaDisappearing(x, y);
7670 #if !USE_NEW_AMOEBA_CODE
7671 else if (IS_AMOEBALIVE(element))
7672 AmoebeAbleger(x, y);
7675 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7677 else if (element == EL_EXIT_CLOSED)
7679 else if (element == EL_SP_EXIT_CLOSED)
7681 else if (element == EL_EXPANDABLE_WALL_GROWING)
7683 else if (element == EL_EXPANDABLE_WALL ||
7684 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7685 element == EL_EXPANDABLE_WALL_VERTICAL ||
7686 element == EL_EXPANDABLE_WALL_ANY)
7688 else if (element == EL_FLAMES)
7689 CheckForDragon(x, y);
7691 else if (IS_AUTO_CHANGING(element))
7692 ChangeElement(x, y);
7694 else if (element == EL_EXPLOSION)
7695 ; /* drawing of correct explosion animation is handled separately */
7696 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7697 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7700 /* this may take place after moving, so 'element' may have changed */
7701 if (IS_AUTO_CHANGING(Feld[x][y]))
7702 ChangeElement(x, y);
7705 if (IS_BELT_ACTIVE(element))
7706 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7708 if (game.magic_wall_active)
7710 int jx = local_player->jx, jy = local_player->jy;
7712 /* play the element sound at the position nearest to the player */
7713 if ((element == EL_MAGIC_WALL_FULL ||
7714 element == EL_MAGIC_WALL_ACTIVE ||
7715 element == EL_MAGIC_WALL_EMPTYING ||
7716 element == EL_BD_MAGIC_WALL_FULL ||
7717 element == EL_BD_MAGIC_WALL_ACTIVE ||
7718 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7719 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7727 #if USE_NEW_AMOEBA_CODE
7728 /* new experimental amoeba growth stuff */
7730 if (!(FrameCounter % 8))
7733 static unsigned long random = 1684108901;
7735 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7738 x = (random >> 10) % lev_fieldx;
7739 y = (random >> 20) % lev_fieldy;
7741 x = RND(lev_fieldx);
7742 y = RND(lev_fieldy);
7744 element = Feld[x][y];
7746 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7747 if (!IS_PLAYER(x,y) &&
7748 (element == EL_EMPTY ||
7749 element == EL_SAND ||
7750 element == EL_QUICKSAND_EMPTY ||
7751 element == EL_ACID_SPLASH_LEFT ||
7752 element == EL_ACID_SPLASH_RIGHT))
7754 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7755 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7756 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7757 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7758 Feld[x][y] = EL_AMOEBA_DROP;
7761 random = random * 129 + 1;
7767 if (game.explosions_delayed)
7770 game.explosions_delayed = FALSE;
7772 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7774 element = Feld[x][y];
7776 if (ExplodeField[x][y])
7777 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7778 else if (element == EL_EXPLOSION)
7779 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7781 ExplodeField[x][y] = EX_NO_EXPLOSION;
7784 game.explosions_delayed = TRUE;
7787 if (game.magic_wall_active)
7789 if (!(game.magic_wall_time_left % 4))
7791 int element = Feld[magic_wall_x][magic_wall_y];
7793 if (element == EL_BD_MAGIC_WALL_FULL ||
7794 element == EL_BD_MAGIC_WALL_ACTIVE ||
7795 element == EL_BD_MAGIC_WALL_EMPTYING)
7796 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7798 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7801 if (game.magic_wall_time_left > 0)
7803 game.magic_wall_time_left--;
7804 if (!game.magic_wall_time_left)
7806 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7808 element = Feld[x][y];
7810 if (element == EL_MAGIC_WALL_ACTIVE ||
7811 element == EL_MAGIC_WALL_FULL)
7813 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7814 DrawLevelField(x, y);
7816 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7817 element == EL_BD_MAGIC_WALL_FULL)
7819 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7820 DrawLevelField(x, y);
7824 game.magic_wall_active = FALSE;
7829 if (game.light_time_left > 0)
7831 game.light_time_left--;
7833 if (game.light_time_left == 0)
7834 RedrawAllLightSwitchesAndInvisibleElements();
7837 if (game.timegate_time_left > 0)
7839 game.timegate_time_left--;
7841 if (game.timegate_time_left == 0)
7842 CloseAllOpenTimegates();
7845 for (i = 0; i < MAX_PLAYERS; i++)
7847 struct PlayerInfo *player = &stored_player[i];
7849 if (SHIELD_ON(player))
7851 if (player->shield_deadly_time_left)
7852 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7853 else if (player->shield_normal_time_left)
7854 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7858 if (TimeFrames >= FRAMES_PER_SECOND)
7863 if (!level.use_step_counter)
7867 for (i = 0; i < MAX_PLAYERS; i++)
7869 struct PlayerInfo *player = &stored_player[i];
7871 if (SHIELD_ON(player))
7873 player->shield_normal_time_left--;
7875 if (player->shield_deadly_time_left > 0)
7876 player->shield_deadly_time_left--;
7884 if (TimeLeft <= 10 && setup.time_limit)
7885 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7887 DrawGameValue_Time(TimeLeft);
7889 if (!TimeLeft && setup.time_limit)
7890 for (i = 0; i < MAX_PLAYERS; i++)
7891 KillHero(&stored_player[i]);
7893 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
7894 DrawGameValue_Time(TimePlayed);
7897 if (tape.recording || tape.playing)
7898 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
7902 PlayAllPlayersSound();
7904 if (options.debug) /* calculate frames per second */
7906 static unsigned long fps_counter = 0;
7907 static int fps_frames = 0;
7908 unsigned long fps_delay_ms = Counter() - fps_counter;
7912 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7914 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7917 fps_counter = Counter();
7920 redraw_mask |= REDRAW_FPS;
7924 if (stored_player[0].jx != stored_player[0].last_jx ||
7925 stored_player[0].jy != stored_player[0].last_jy)
7926 printf("::: %d, %d, %d, %d, %d\n",
7927 stored_player[0].MovDir,
7928 stored_player[0].MovPos,
7929 stored_player[0].GfxPos,
7930 stored_player[0].Frame,
7931 stored_player[0].StepFrame);
7938 for (i = 0; i < MAX_PLAYERS; i++)
7941 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7943 stored_player[i].Frame += move_frames;
7945 if (stored_player[i].MovPos != 0)
7946 stored_player[i].StepFrame += move_frames;
7948 if (stored_player[i].drop_delay > 0)
7949 stored_player[i].drop_delay--;
7954 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7956 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7958 local_player->show_envelope = 0;
7963 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7965 int min_x = x, min_y = y, max_x = x, max_y = y;
7968 for (i = 0; i < MAX_PLAYERS; i++)
7970 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7972 if (!stored_player[i].active || &stored_player[i] == player)
7975 min_x = MIN(min_x, jx);
7976 min_y = MIN(min_y, jy);
7977 max_x = MAX(max_x, jx);
7978 max_y = MAX(max_y, jy);
7981 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7984 static boolean AllPlayersInVisibleScreen()
7988 for (i = 0; i < MAX_PLAYERS; i++)
7990 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7992 if (!stored_player[i].active)
7995 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8002 void ScrollLevel(int dx, int dy)
8004 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8007 BlitBitmap(drawto_field, drawto_field,
8008 FX + TILEX * (dx == -1) - softscroll_offset,
8009 FY + TILEY * (dy == -1) - softscroll_offset,
8010 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8011 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8012 FX + TILEX * (dx == 1) - softscroll_offset,
8013 FY + TILEY * (dy == 1) - softscroll_offset);
8017 x = (dx == 1 ? BX1 : BX2);
8018 for (y = BY1; y <= BY2; y++)
8019 DrawScreenField(x, y);
8024 y = (dy == 1 ? BY1 : BY2);
8025 for (x = BX1; x <= BX2; x++)
8026 DrawScreenField(x, y);
8029 redraw_mask |= REDRAW_FIELD;
8032 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8034 int nextx = x + dx, nexty = y + dy;
8035 int element = Feld[x][y];
8038 element != EL_SP_PORT_LEFT &&
8039 element != EL_SP_GRAVITY_PORT_LEFT &&
8040 element != EL_SP_PORT_HORIZONTAL &&
8041 element != EL_SP_PORT_ANY) ||
8043 element != EL_SP_PORT_RIGHT &&
8044 element != EL_SP_GRAVITY_PORT_RIGHT &&
8045 element != EL_SP_PORT_HORIZONTAL &&
8046 element != EL_SP_PORT_ANY) ||
8048 element != EL_SP_PORT_UP &&
8049 element != EL_SP_GRAVITY_PORT_UP &&
8050 element != EL_SP_PORT_VERTICAL &&
8051 element != EL_SP_PORT_ANY) ||
8053 element != EL_SP_PORT_DOWN &&
8054 element != EL_SP_GRAVITY_PORT_DOWN &&
8055 element != EL_SP_PORT_VERTICAL &&
8056 element != EL_SP_PORT_ANY) ||
8057 !IN_LEV_FIELD(nextx, nexty) ||
8058 !IS_FREE(nextx, nexty))
8064 static void CheckGravityMovement(struct PlayerInfo *player)
8066 if (game.gravity && !player->programmed_action)
8068 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
8069 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
8071 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
8072 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8073 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8074 int jx = player->jx, jy = player->jy;
8075 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8076 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8077 int new_jx = jx + dx, new_jy = jy + dy;
8078 boolean field_under_player_is_free =
8079 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8080 boolean player_is_moving_to_valid_field =
8081 (IN_LEV_FIELD(new_jx, new_jy) &&
8082 (Feld[new_jx][new_jy] == EL_SP_BASE ||
8083 Feld[new_jx][new_jy] == EL_SAND ||
8084 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8085 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
8086 /* !!! extend EL_SAND to anything diggable !!! */
8088 boolean player_is_standing_on_valid_field =
8089 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8090 (IS_WALKABLE(Feld[jx][jy]) &&
8091 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8093 if (field_under_player_is_free &&
8094 !player_is_standing_on_valid_field &&
8095 !player_is_moving_to_valid_field)
8096 player->programmed_action = MV_DOWN;
8100 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8103 return CheckGravityMovement(player);
8106 if (game.gravity && !player->programmed_action)
8108 int jx = player->jx, jy = player->jy;
8109 boolean field_under_player_is_free =
8110 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8111 boolean player_is_standing_on_valid_field =
8112 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8113 (IS_WALKABLE(Feld[jx][jy]) &&
8114 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8116 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8117 player->programmed_action = MV_DOWN;
8123 -----------------------------------------------------------------------------
8124 dx, dy: direction (non-diagonal) to try to move the player to
8125 real_dx, real_dy: direction as read from input device (can be diagonal)
8128 boolean MovePlayerOneStep(struct PlayerInfo *player,
8129 int dx, int dy, int real_dx, int real_dy)
8132 static int trigger_sides[4][2] =
8134 /* enter side leave side */
8135 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8136 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8137 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8138 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8140 int move_direction = (dx == -1 ? MV_LEFT :
8141 dx == +1 ? MV_RIGHT :
8143 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8144 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8145 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8147 int jx = player->jx, jy = player->jy;
8148 int new_jx = jx + dx, new_jy = jy + dy;
8152 if (!player->active || (!dx && !dy))
8153 return MF_NO_ACTION;
8155 player->MovDir = (dx < 0 ? MV_LEFT :
8158 dy > 0 ? MV_DOWN : MV_NO_MOVING);
8160 if (!IN_LEV_FIELD(new_jx, new_jy))
8161 return MF_NO_ACTION;
8163 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8164 return MF_NO_ACTION;
8167 element = MovingOrBlocked2Element(new_jx, new_jy);
8169 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8172 if (DONT_RUN_INTO(element))
8174 if (element == EL_ACID && dx == 0 && dy == 1)
8176 SplashAcid(new_jx, new_jy);
8177 Feld[jx][jy] = EL_PLAYER_1;
8178 InitMovingField(jx, jy, MV_DOWN);
8179 Store[jx][jy] = EL_ACID;
8180 ContinueMoving(jx, jy);
8184 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8189 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8190 if (can_move != MF_MOVING)
8193 /* check if DigField() has caused relocation of the player */
8194 if (player->jx != jx || player->jy != jy)
8195 return MF_NO_ACTION;
8197 StorePlayer[jx][jy] = 0;
8198 player->last_jx = jx;
8199 player->last_jy = jy;
8200 player->jx = new_jx;
8201 player->jy = new_jy;
8202 StorePlayer[new_jx][new_jy] = player->element_nr;
8205 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8207 player->step_counter++;
8209 player->drop_delay = 0;
8211 PlayerVisit[jx][jy] = FrameCounter;
8213 ScrollPlayer(player, SCROLL_INIT);
8216 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8218 CheckTriggeredElementChangeSide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
8220 CheckElementChangeSide(jx, jy, Feld[jx][jy], CE_LEFT_BY_PLAYER,leave_side);
8223 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
8225 CheckTriggeredElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8226 CE_OTHER_GETS_ENTERED, enter_side);
8227 CheckElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8228 CE_ENTERED_BY_PLAYER, enter_side);
8235 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8237 int jx = player->jx, jy = player->jy;
8238 int old_jx = jx, old_jy = jy;
8239 int moved = MF_NO_ACTION;
8242 if (!player->active)
8247 if (player->MovPos == 0)
8249 player->is_moving = FALSE;
8250 player->is_digging = FALSE;
8251 player->is_collecting = FALSE;
8252 player->is_snapping = FALSE;
8253 player->is_pushing = FALSE;
8259 if (!player->active || (!dx && !dy))
8264 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8270 if (!FrameReached(&player->move_delay, player->move_delay_value))
8273 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8274 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
8280 /* remove the last programmed player action */
8281 player->programmed_action = 0;
8285 /* should only happen if pre-1.2 tape recordings are played */
8286 /* this is only for backward compatibility */
8288 int original_move_delay_value = player->move_delay_value;
8291 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8295 /* scroll remaining steps with finest movement resolution */
8296 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8298 while (player->MovPos)
8300 ScrollPlayer(player, SCROLL_GO_ON);
8301 ScrollScreen(NULL, SCROLL_GO_ON);
8307 player->move_delay_value = original_move_delay_value;
8310 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
8312 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8313 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8317 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8318 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8324 if (moved & MF_MOVING && !ScreenMovPos &&
8325 (player == local_player || !options.network))
8327 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8328 int offset = (setup.scroll_delay ? 3 : 0);
8330 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8332 /* actual player has left the screen -- scroll in that direction */
8333 if (jx != old_jx) /* player has moved horizontally */
8334 scroll_x += (jx - old_jx);
8335 else /* player has moved vertically */
8336 scroll_y += (jy - old_jy);
8340 if (jx != old_jx) /* player has moved horizontally */
8342 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8343 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8344 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8346 /* don't scroll over playfield boundaries */
8347 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8348 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8350 /* don't scroll more than one field at a time */
8351 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8353 /* don't scroll against the player's moving direction */
8354 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8355 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8356 scroll_x = old_scroll_x;
8358 else /* player has moved vertically */
8360 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8361 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8362 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8364 /* don't scroll over playfield boundaries */
8365 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8366 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8368 /* don't scroll more than one field at a time */
8369 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8371 /* don't scroll against the player's moving direction */
8372 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8373 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8374 scroll_y = old_scroll_y;
8378 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8380 if (!options.network && !AllPlayersInVisibleScreen())
8382 scroll_x = old_scroll_x;
8383 scroll_y = old_scroll_y;
8387 ScrollScreen(player, SCROLL_INIT);
8388 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8395 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8397 if (!(moved & MF_MOVING) && !player->is_pushing)
8402 player->StepFrame = 0;
8404 if (moved & MF_MOVING)
8406 if (old_jx != jx && old_jy == jy)
8407 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8408 else if (old_jx == jx && old_jy != jy)
8409 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8411 DrawLevelField(jx, jy); /* for "crumbled sand" */
8413 player->last_move_dir = player->MovDir;
8414 player->is_moving = TRUE;
8416 player->is_snapping = FALSE;
8420 player->is_switching = FALSE;
8423 player->is_dropping = FALSE;
8428 static int trigger_sides[4][2] =
8430 /* enter side leave side */
8431 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8432 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8433 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8434 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8436 int move_direction = player->MovDir;
8437 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8438 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8441 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8443 CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8445 player->index_bit, leave_side);
8446 CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8448 player->index_bit, leave_side);
8451 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8453 CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
8454 CE_OTHER_GETS_ENTERED,
8455 player->index_bit, enter_side);
8456 CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
8457 player->index_bit, enter_side);
8468 CheckGravityMovementWhenNotMoving(player);
8471 player->last_move_dir = MV_NO_MOVING;
8473 player->is_moving = FALSE;
8476 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8478 TestIfHeroTouchesBadThing(jx, jy);
8479 TestIfPlayerTouchesCustomElement(jx, jy);
8482 if (!player->active)
8488 void ScrollPlayer(struct PlayerInfo *player, int mode)
8490 int jx = player->jx, jy = player->jy;
8491 int last_jx = player->last_jx, last_jy = player->last_jy;
8492 int move_stepsize = TILEX / player->move_delay_value;
8494 if (!player->active || !player->MovPos)
8497 if (mode == SCROLL_INIT)
8499 player->actual_frame_counter = FrameCounter;
8500 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8502 if (Feld[last_jx][last_jy] == EL_EMPTY)
8503 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8511 else if (!FrameReached(&player->actual_frame_counter, 1))
8514 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8515 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8517 if (!player->block_last_field &&
8518 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8519 Feld[last_jx][last_jy] = EL_EMPTY;
8521 /* before DrawPlayer() to draw correct player graphic for this case */
8522 if (player->MovPos == 0)
8523 CheckGravityMovement(player);
8526 DrawPlayer(player); /* needed here only to cleanup last field */
8529 if (player->MovPos == 0) /* player reached destination field */
8532 if (player->move_delay_reset_counter > 0)
8534 player->move_delay_reset_counter--;
8536 if (player->move_delay_reset_counter == 0)
8538 /* continue with normal speed after quickly moving through gate */
8539 HALVE_PLAYER_SPEED(player);
8541 /* be able to make the next move without delay */
8542 player->move_delay = 0;
8546 if (IS_PASSABLE(Feld[last_jx][last_jy]))
8548 /* continue with normal speed after quickly moving through gate */
8549 HALVE_PLAYER_SPEED(player);
8551 /* be able to make the next move without delay */
8552 player->move_delay = 0;
8556 if (player->block_last_field &&
8557 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8558 Feld[last_jx][last_jy] = EL_EMPTY;
8560 player->last_jx = jx;
8561 player->last_jy = jy;
8563 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8564 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8565 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8567 DrawPlayer(player); /* needed here only to cleanup last field */
8570 if (local_player->friends_still_needed == 0 ||
8571 IS_SP_ELEMENT(Feld[jx][jy]))
8572 player->LevelSolved = player->GameOver = TRUE;
8575 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8577 TestIfHeroTouchesBadThing(jx, jy);
8578 TestIfPlayerTouchesCustomElement(jx, jy);
8580 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8583 if (!player->active)
8587 if (level.use_step_counter)
8593 for (i = 0; i < MAX_PLAYERS; i++)
8595 struct PlayerInfo *player = &stored_player[i];
8597 if (SHIELD_ON(player))
8599 player->shield_normal_time_left--;
8601 if (player->shield_deadly_time_left > 0)
8602 player->shield_deadly_time_left--;
8610 if (TimeLeft <= 10 && setup.time_limit)
8611 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8613 DrawGameValue_Time(TimeLeft);
8615 if (!TimeLeft && setup.time_limit)
8616 for (i = 0; i < MAX_PLAYERS; i++)
8617 KillHero(&stored_player[i]);
8619 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8620 DrawGameValue_Time(TimePlayed);
8623 if (tape.single_step && tape.recording && !tape.pausing &&
8624 !player->programmed_action)
8625 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8629 void ScrollScreen(struct PlayerInfo *player, int mode)
8631 static unsigned long screen_frame_counter = 0;
8633 if (mode == SCROLL_INIT)
8635 /* set scrolling step size according to actual player's moving speed */
8636 ScrollStepSize = TILEX / player->move_delay_value;
8638 screen_frame_counter = FrameCounter;
8639 ScreenMovDir = player->MovDir;
8640 ScreenMovPos = player->MovPos;
8641 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8644 else if (!FrameReached(&screen_frame_counter, 1))
8649 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8650 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8651 redraw_mask |= REDRAW_FIELD;
8654 ScreenMovDir = MV_NO_MOVING;
8657 void TestIfPlayerTouchesCustomElement(int x, int y)
8659 static int xy[4][2] =
8666 static int trigger_sides[4][2] =
8668 /* center side border side */
8669 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8670 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8671 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8672 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8674 static int touch_dir[4] =
8681 int center_element = Feld[x][y]; /* should always be non-moving! */
8684 for (i = 0; i < NUM_DIRECTIONS; i++)
8686 int xx = x + xy[i][0];
8687 int yy = y + xy[i][1];
8688 int center_side = trigger_sides[i][0];
8689 int border_side = trigger_sides[i][1];
8692 if (!IN_LEV_FIELD(xx, yy))
8695 if (IS_PLAYER(x, y))
8697 struct PlayerInfo *player = PLAYERINFO(x, y);
8699 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8700 border_element = Feld[xx][yy]; /* may be moving! */
8701 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8702 border_element = Feld[xx][yy];
8703 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8704 border_element = MovingOrBlocked2Element(xx, yy);
8706 continue; /* center and border element do not touch */
8708 CheckTriggeredElementChangePlayer(xx, yy, border_element,
8709 CE_OTHER_GETS_TOUCHED,
8710 player->index_bit, border_side);
8711 CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
8712 player->index_bit, border_side);
8714 else if (IS_PLAYER(xx, yy))
8716 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8718 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8720 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8721 continue; /* center and border element do not touch */
8724 CheckTriggeredElementChangePlayer(x, y, center_element,
8725 CE_OTHER_GETS_TOUCHED,
8726 player->index_bit, center_side);
8727 CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
8728 player->index_bit, center_side);
8735 void TestIfElementTouchesCustomElement(int x, int y)
8737 static int xy[4][2] =
8744 static int trigger_sides[4][2] =
8746 /* center side border side */
8747 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8748 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8749 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8750 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8752 static int touch_dir[4] =
8759 boolean change_center_element = FALSE;
8760 int center_element_change_page = 0;
8761 int center_element = Feld[x][y]; /* should always be non-moving! */
8764 for (i = 0; i < NUM_DIRECTIONS; i++)
8766 int xx = x + xy[i][0];
8767 int yy = y + xy[i][1];
8768 int center_side = trigger_sides[i][0];
8769 int border_side = trigger_sides[i][1];
8772 if (!IN_LEV_FIELD(xx, yy))
8775 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8776 border_element = Feld[xx][yy]; /* may be moving! */
8777 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8778 border_element = Feld[xx][yy];
8779 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8780 border_element = MovingOrBlocked2Element(xx, yy);
8782 continue; /* center and border element do not touch */
8784 /* check for change of center element (but change it only once) */
8785 if (IS_CUSTOM_ELEMENT(center_element) &&
8786 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8787 !change_center_element)
8789 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8791 struct ElementChangeInfo *change =
8792 &element_info[center_element].change_page[j];
8794 if (change->can_change &&
8795 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8796 change->trigger_side & border_side &&
8798 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8800 change->trigger_element == border_element
8804 change_center_element = TRUE;
8805 center_element_change_page = j;
8812 /* check for change of border element */
8813 if (IS_CUSTOM_ELEMENT(border_element) &&
8814 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8816 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8818 struct ElementChangeInfo *change =
8819 &element_info[border_element].change_page[j];
8821 if (change->can_change &&
8822 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8823 change->trigger_side & center_side &&
8825 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8827 change->trigger_element == center_element
8831 CheckElementChangePage(xx, yy, border_element, CE_OTHER_IS_TOUCHING,
8839 if (change_center_element)
8840 CheckElementChangePage(x, y, center_element, CE_OTHER_IS_TOUCHING,
8841 center_element_change_page);
8844 void TestIfElementHitsCustomElement(int x, int y, int direction)
8846 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8847 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8848 int hitx = x + dx, hity = y + dy;
8849 int hitting_element = Feld[x][y];
8851 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8852 !IS_FREE(hitx, hity) &&
8853 (!IS_MOVING(hitx, hity) ||
8854 MovDir[hitx][hity] != direction ||
8855 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8858 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8862 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8866 CheckElementChangeSide(x, y, hitting_element, CE_HITTING_SOMETHING,
8869 if (IN_LEV_FIELD(hitx, hity))
8871 int opposite_direction = MV_DIR_OPPOSITE(direction);
8872 int hitting_side = direction;
8873 int touched_side = opposite_direction;
8874 int touched_element = MovingOrBlocked2Element(hitx, hity);
8876 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8877 MovDir[hitx][hity] != direction ||
8878 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8887 CheckElementChangeSide(hitx, hity, touched_element, CE_HIT_BY_SOMETHING,
8888 opposite_direction);
8890 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8891 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8893 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8895 struct ElementChangeInfo *change =
8896 &element_info[hitting_element].change_page[i];
8898 if (change->can_change &&
8899 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8900 change->trigger_side & touched_side &&
8903 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8905 change->trigger_element == touched_element
8909 CheckElementChangePage(x, y, hitting_element, CE_OTHER_IS_HITTING,
8916 if (IS_CUSTOM_ELEMENT(touched_element) &&
8917 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8919 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8921 struct ElementChangeInfo *change =
8922 &element_info[touched_element].change_page[i];
8924 if (change->can_change &&
8925 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8926 change->trigger_side & hitting_side &&
8928 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8930 change->trigger_element == hitting_element
8934 CheckElementChangePage(hitx, hity, touched_element,
8935 CE_OTHER_GETS_HIT, i);
8944 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8946 int i, kill_x = -1, kill_y = -1;
8947 static int test_xy[4][2] =
8954 static int test_dir[4] =
8962 for (i = 0; i < NUM_DIRECTIONS; i++)
8964 int test_x, test_y, test_move_dir, test_element;
8966 test_x = good_x + test_xy[i][0];
8967 test_y = good_y + test_xy[i][1];
8968 if (!IN_LEV_FIELD(test_x, test_y))
8972 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8975 test_element = Feld[test_x][test_y];
8977 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8980 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8981 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8983 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8984 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8992 if (kill_x != -1 || kill_y != -1)
8994 if (IS_PLAYER(good_x, good_y))
8996 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8998 if (player->shield_deadly_time_left > 0)
8999 Bang(kill_x, kill_y);
9000 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9004 Bang(good_x, good_y);
9008 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9010 int i, kill_x = -1, kill_y = -1;
9011 int bad_element = Feld[bad_x][bad_y];
9012 static int test_xy[4][2] =
9019 static int touch_dir[4] =
9026 static int test_dir[4] =
9034 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9037 for (i = 0; i < NUM_DIRECTIONS; i++)
9039 int test_x, test_y, test_move_dir, test_element;
9041 test_x = bad_x + test_xy[i][0];
9042 test_y = bad_y + test_xy[i][1];
9043 if (!IN_LEV_FIELD(test_x, test_y))
9047 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9049 test_element = Feld[test_x][test_y];
9051 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9052 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9054 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9055 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9057 /* good thing is player or penguin that does not move away */
9058 if (IS_PLAYER(test_x, test_y))
9060 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9062 if (bad_element == EL_ROBOT && player->is_moving)
9063 continue; /* robot does not kill player if he is moving */
9065 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9067 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9068 continue; /* center and border element do not touch */
9075 else if (test_element == EL_PENGUIN)
9084 if (kill_x != -1 || kill_y != -1)
9086 if (IS_PLAYER(kill_x, kill_y))
9088 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9090 if (player->shield_deadly_time_left > 0)
9092 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9096 Bang(kill_x, kill_y);
9100 void TestIfHeroTouchesBadThing(int x, int y)
9102 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9105 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
9107 TestIfGoodThingHitsBadThing(x, y, move_dir);
9110 void TestIfBadThingTouchesHero(int x, int y)
9112 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9115 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
9117 TestIfBadThingHitsGoodThing(x, y, move_dir);
9120 void TestIfFriendTouchesBadThing(int x, int y)
9122 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9125 void TestIfBadThingTouchesFriend(int x, int y)
9127 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9130 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9132 int i, kill_x = bad_x, kill_y = bad_y;
9133 static int xy[4][2] =
9141 for (i = 0; i < NUM_DIRECTIONS; i++)
9145 x = bad_x + xy[i][0];
9146 y = bad_y + xy[i][1];
9147 if (!IN_LEV_FIELD(x, y))
9150 element = Feld[x][y];
9151 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9152 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9160 if (kill_x != bad_x || kill_y != bad_y)
9164 void KillHero(struct PlayerInfo *player)
9166 int jx = player->jx, jy = player->jy;
9168 if (!player->active)
9171 /* remove accessible field at the player's position */
9172 Feld[jx][jy] = EL_EMPTY;
9174 /* deactivate shield (else Bang()/Explode() would not work right) */
9175 player->shield_normal_time_left = 0;
9176 player->shield_deadly_time_left = 0;
9182 static void KillHeroUnlessEnemyProtected(int x, int y)
9184 if (!PLAYER_ENEMY_PROTECTED(x, y))
9185 KillHero(PLAYERINFO(x, y));
9188 static void KillHeroUnlessExplosionProtected(int x, int y)
9190 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9191 KillHero(PLAYERINFO(x, y));
9194 void BuryHero(struct PlayerInfo *player)
9196 int jx = player->jx, jy = player->jy;
9198 if (!player->active)
9202 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9204 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
9206 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9208 player->GameOver = TRUE;
9212 void RemoveHero(struct PlayerInfo *player)
9214 int jx = player->jx, jy = player->jy;
9215 int i, found = FALSE;
9217 player->present = FALSE;
9218 player->active = FALSE;
9220 if (!ExplodeField[jx][jy])
9221 StorePlayer[jx][jy] = 0;
9223 for (i = 0; i < MAX_PLAYERS; i++)
9224 if (stored_player[i].active)
9228 AllPlayersGone = TRUE;
9235 =============================================================================
9236 checkDiagonalPushing()
9237 -----------------------------------------------------------------------------
9238 check if diagonal input device direction results in pushing of object
9239 (by checking if the alternative direction is walkable, diggable, ...)
9240 =============================================================================
9243 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9244 int x, int y, int real_dx, int real_dy)
9246 int jx, jy, dx, dy, xx, yy;
9248 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9251 /* diagonal direction: check alternative direction */
9256 xx = jx + (dx == 0 ? real_dx : 0);
9257 yy = jy + (dy == 0 ? real_dy : 0);
9259 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9263 =============================================================================
9265 -----------------------------------------------------------------------------
9266 x, y: field next to player (non-diagonal) to try to dig to
9267 real_dx, real_dy: direction as read from input device (can be diagonal)
9268 =============================================================================
9271 int DigField(struct PlayerInfo *player,
9272 int oldx, int oldy, int x, int y,
9273 int real_dx, int real_dy, int mode)
9275 static int trigger_sides[4] =
9277 CH_SIDE_RIGHT, /* moving left */
9278 CH_SIDE_LEFT, /* moving right */
9279 CH_SIDE_BOTTOM, /* moving up */
9280 CH_SIDE_TOP, /* moving down */
9283 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
9285 int jx = oldx, jy = oldy;
9286 int dx = x - jx, dy = y - jy;
9287 int nextx = x + dx, nexty = y + dy;
9288 int move_direction = (dx == -1 ? MV_LEFT :
9289 dx == +1 ? MV_RIGHT :
9291 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9292 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9293 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
9294 int old_element = Feld[jx][jy];
9297 if (player->MovPos == 0)
9299 player->is_digging = FALSE;
9300 player->is_collecting = FALSE;
9303 if (player->MovPos == 0) /* last pushing move finished */
9304 player->is_pushing = FALSE;
9306 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9308 player->is_switching = FALSE;
9309 player->push_delay = 0;
9311 return MF_NO_ACTION;
9314 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9315 return MF_NO_ACTION;
9320 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
9322 if (IS_TUBE(Feld[jx][jy]) ||
9323 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
9327 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
9328 int tube_leave_directions[][2] =
9330 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9331 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9332 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9333 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
9334 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
9335 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
9336 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
9337 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
9338 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
9339 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
9340 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
9341 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
9344 while (tube_leave_directions[i][0] != tube_element)
9347 if (tube_leave_directions[i][0] == -1) /* should not happen */
9351 if (!(tube_leave_directions[i][1] & move_direction))
9352 return MF_NO_ACTION; /* tube has no opening in this direction */
9357 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9358 old_element = Back[jx][jy];
9362 if (IS_WALKABLE(old_element) &&
9363 !(element_info[old_element].access_direction & move_direction))
9364 return MF_NO_ACTION; /* field has no opening in this direction */
9366 element = Feld[x][y];
9368 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9369 game.engine_version >= VERSION_IDENT(2,2,0,0))
9370 return MF_NO_ACTION;
9374 case EL_SP_PORT_LEFT:
9375 case EL_SP_PORT_RIGHT:
9377 case EL_SP_PORT_DOWN:
9378 case EL_SP_PORT_HORIZONTAL:
9379 case EL_SP_PORT_VERTICAL:
9380 case EL_SP_PORT_ANY:
9381 case EL_SP_GRAVITY_PORT_LEFT:
9382 case EL_SP_GRAVITY_PORT_RIGHT:
9383 case EL_SP_GRAVITY_PORT_UP:
9384 case EL_SP_GRAVITY_PORT_DOWN:
9386 if (!canEnterSupaplexPort(x, y, dx, dy))
9387 return MF_NO_ACTION;
9390 element != EL_SP_PORT_LEFT &&
9391 element != EL_SP_GRAVITY_PORT_LEFT &&
9392 element != EL_SP_PORT_HORIZONTAL &&
9393 element != EL_SP_PORT_ANY) ||
9395 element != EL_SP_PORT_RIGHT &&
9396 element != EL_SP_GRAVITY_PORT_RIGHT &&
9397 element != EL_SP_PORT_HORIZONTAL &&
9398 element != EL_SP_PORT_ANY) ||
9400 element != EL_SP_PORT_UP &&
9401 element != EL_SP_GRAVITY_PORT_UP &&
9402 element != EL_SP_PORT_VERTICAL &&
9403 element != EL_SP_PORT_ANY) ||
9405 element != EL_SP_PORT_DOWN &&
9406 element != EL_SP_GRAVITY_PORT_DOWN &&
9407 element != EL_SP_PORT_VERTICAL &&
9408 element != EL_SP_PORT_ANY) ||
9409 !IN_LEV_FIELD(nextx, nexty) ||
9410 !IS_FREE(nextx, nexty))
9411 return MF_NO_ACTION;
9414 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9415 element == EL_SP_GRAVITY_PORT_RIGHT ||
9416 element == EL_SP_GRAVITY_PORT_UP ||
9417 element == EL_SP_GRAVITY_PORT_DOWN)
9418 game.gravity = !game.gravity;
9420 /* automatically move to the next field with double speed */
9421 player->programmed_action = move_direction;
9423 if (player->move_delay_reset_counter == 0)
9425 player->move_delay_reset_counter = 2; /* two double speed steps */
9427 DOUBLE_PLAYER_SPEED(player);
9430 player->move_delay_reset_counter = 2;
9432 DOUBLE_PLAYER_SPEED(player);
9435 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9440 case EL_TUBE_VERTICAL:
9441 case EL_TUBE_HORIZONTAL:
9442 case EL_TUBE_VERTICAL_LEFT:
9443 case EL_TUBE_VERTICAL_RIGHT:
9444 case EL_TUBE_HORIZONTAL_UP:
9445 case EL_TUBE_HORIZONTAL_DOWN:
9446 case EL_TUBE_LEFT_UP:
9447 case EL_TUBE_LEFT_DOWN:
9448 case EL_TUBE_RIGHT_UP:
9449 case EL_TUBE_RIGHT_DOWN:
9452 int tube_enter_directions[][2] =
9454 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9455 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9456 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9457 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
9458 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
9459 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
9460 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
9461 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
9462 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
9463 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
9464 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
9465 { -1, MV_NO_MOVING }
9468 while (tube_enter_directions[i][0] != element)
9471 if (tube_enter_directions[i][0] == -1) /* should not happen */
9475 if (!(tube_enter_directions[i][1] & move_direction))
9476 return MF_NO_ACTION; /* tube has no opening in this direction */
9478 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9485 if (IS_WALKABLE(element))
9487 int sound_action = ACTION_WALKING;
9489 if (!(element_info[element].access_direction & opposite_direction))
9490 return MF_NO_ACTION; /* field not accessible from this direction */
9492 if (element >= EL_GATE_1 && element <= EL_GATE_4)
9494 if (!player->key[element - EL_GATE_1])
9495 return MF_NO_ACTION;
9497 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9499 if (!player->key[element - EL_GATE_1_GRAY])
9500 return MF_NO_ACTION;
9502 else if (element == EL_EXIT_OPEN ||
9503 element == EL_SP_EXIT_OPEN ||
9504 element == EL_SP_EXIT_OPENING)
9506 sound_action = ACTION_PASSING; /* player is passing exit */
9508 else if (element == EL_EMPTY)
9510 sound_action = ACTION_MOVING; /* nothing to walk on */
9513 /* play sound from background or player, whatever is available */
9514 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9515 PlayLevelSoundElementAction(x, y, element, sound_action);
9517 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9521 else if (IS_PASSABLE(element))
9523 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9524 return MF_NO_ACTION;
9526 if (IS_CUSTOM_ELEMENT(element) &&
9527 !(element_info[element].access_direction & opposite_direction))
9528 return MF_NO_ACTION; /* field not accessible from this direction */
9531 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9532 return MF_NO_ACTION;
9535 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9537 if (!player->key[element - EL_EM_GATE_1])
9538 return MF_NO_ACTION;
9540 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9542 if (!player->key[element - EL_EM_GATE_1_GRAY])
9543 return MF_NO_ACTION;
9546 /* automatically move to the next field with double speed */
9547 player->programmed_action = move_direction;
9549 if (player->move_delay_reset_counter == 0)
9551 player->move_delay_reset_counter = 2; /* two double speed steps */
9553 DOUBLE_PLAYER_SPEED(player);
9556 player->move_delay_reset_counter = 2;
9558 DOUBLE_PLAYER_SPEED(player);
9561 PlayLevelSoundAction(x, y, ACTION_PASSING);
9565 else if (IS_DIGGABLE(element))
9569 if (mode != DF_SNAP)
9572 GfxElement[x][y] = GFX_ELEMENT(element);
9575 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9577 player->is_digging = TRUE;
9580 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9582 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED,
9583 player->index_bit, CH_SIDE_ANY);
9586 if (mode == DF_SNAP)
9587 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9592 else if (IS_COLLECTIBLE(element))
9596 if (mode != DF_SNAP)
9598 GfxElement[x][y] = element;
9599 player->is_collecting = TRUE;
9602 if (element == EL_SPEED_PILL)
9603 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9604 else if (element == EL_EXTRA_TIME && level.time > 0)
9607 DrawGameValue_Time(TimeLeft);
9609 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9611 player->shield_normal_time_left += 10;
9612 if (element == EL_SHIELD_DEADLY)
9613 player->shield_deadly_time_left += 10;
9615 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9617 if (player->inventory_size < MAX_INVENTORY_SIZE)
9618 player->inventory_element[player->inventory_size++] = element;
9620 DrawGameValue_Dynamite(local_player->inventory_size);
9622 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9624 player->dynabomb_count++;
9625 player->dynabombs_left++;
9627 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9629 player->dynabomb_size++;
9631 else if (element == EL_DYNABOMB_INCREASE_POWER)
9633 player->dynabomb_xl = TRUE;
9635 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9636 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9638 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9639 element - EL_KEY_1 : element - EL_EM_KEY_1);
9641 player->key[key_nr] = TRUE;
9643 DrawGameValue_Keys(player);
9645 redraw_mask |= REDRAW_DOOR_1;
9647 else if (IS_ENVELOPE(element))
9650 player->show_envelope = element;
9652 ShowEnvelope(element - EL_ENVELOPE_1);
9655 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9659 if (element_info[element].collect_count == 0)
9660 player->inventory_infinite_element = element;
9662 for (i = 0; i < element_info[element].collect_count; i++)
9663 if (player->inventory_size < MAX_INVENTORY_SIZE)
9664 player->inventory_element[player->inventory_size++] = element;
9666 DrawGameValue_Dynamite(local_player->inventory_size);
9668 else if (element_info[element].collect_count > 0)
9670 local_player->gems_still_needed -=
9671 element_info[element].collect_count;
9672 if (local_player->gems_still_needed < 0)
9673 local_player->gems_still_needed = 0;
9675 DrawGameValue_Emeralds(local_player->gems_still_needed);
9678 RaiseScoreElement(element);
9679 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9681 CheckTriggeredElementChangePlayer(x, y, element,
9682 CE_OTHER_GETS_COLLECTED,
9683 player->index_bit, CH_SIDE_ANY);
9686 if (mode == DF_SNAP)
9687 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9692 else if (IS_PUSHABLE(element))
9694 if (mode == DF_SNAP && element != EL_BD_ROCK)
9695 return MF_NO_ACTION;
9697 if (CAN_FALL(element) && dy)
9698 return MF_NO_ACTION;
9700 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9701 !(element == EL_SPRING && level.use_spring_bug))
9702 return MF_NO_ACTION;
9705 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9706 ((move_direction & MV_VERTICAL &&
9707 ((element_info[element].move_pattern & MV_LEFT &&
9708 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9709 (element_info[element].move_pattern & MV_RIGHT &&
9710 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9711 (move_direction & MV_HORIZONTAL &&
9712 ((element_info[element].move_pattern & MV_UP &&
9713 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9714 (element_info[element].move_pattern & MV_DOWN &&
9715 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9716 return MF_NO_ACTION;
9720 /* do not push elements already moving away faster than player */
9721 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9722 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9723 return MF_NO_ACTION;
9725 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9726 return MF_NO_ACTION;
9730 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9732 if (player->push_delay_value == -1)
9733 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9735 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9737 if (!player->is_pushing)
9738 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9742 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9743 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9744 !player_is_pushing))
9745 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9748 if (!player->is_pushing &&
9749 game.engine_version >= VERSION_IDENT(2,2,0,7))
9750 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9754 printf("::: push delay: %ld [%d, %d] [%d]\n",
9755 player->push_delay_value, FrameCounter, game.engine_version,
9756 player->is_pushing);
9759 player->is_pushing = TRUE;
9761 if (!(IN_LEV_FIELD(nextx, nexty) &&
9762 (IS_FREE(nextx, nexty) ||
9763 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9764 IS_SB_ELEMENT(element)))))
9765 return MF_NO_ACTION;
9767 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9768 return MF_NO_ACTION;
9770 if (player->push_delay == 0) /* new pushing; restart delay */
9771 player->push_delay = FrameCounter;
9773 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9774 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9775 element != EL_SPRING && element != EL_BALLOON)
9777 /* make sure that there is no move delay before next try to push */
9778 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9779 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9781 return MF_NO_ACTION;
9785 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9788 if (IS_SB_ELEMENT(element))
9790 if (element == EL_SOKOBAN_FIELD_FULL)
9792 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9793 local_player->sokobanfields_still_needed++;
9796 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9798 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9799 local_player->sokobanfields_still_needed--;
9802 Feld[x][y] = EL_SOKOBAN_OBJECT;
9804 if (Back[x][y] == Back[nextx][nexty])
9805 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9806 else if (Back[x][y] != 0)
9807 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9810 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9813 if (local_player->sokobanfields_still_needed == 0 &&
9814 game.emulation == EMU_SOKOBAN)
9816 player->LevelSolved = player->GameOver = TRUE;
9817 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9821 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9823 InitMovingField(x, y, move_direction);
9824 GfxAction[x][y] = ACTION_PUSHING;
9826 if (mode == DF_SNAP)
9827 ContinueMoving(x, y);
9829 MovPos[x][y] = (dx != 0 ? dx : dy);
9831 Pushed[x][y] = TRUE;
9832 Pushed[nextx][nexty] = TRUE;
9834 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9835 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9837 player->push_delay_value = -1; /* get new value later */
9839 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED,
9840 player->index_bit, dig_side);
9841 CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER,
9842 player->index_bit, dig_side);
9846 else if (IS_SWITCHABLE(element))
9848 if (PLAYER_SWITCHING(player, x, y))
9851 player->is_switching = TRUE;
9852 player->switch_x = x;
9853 player->switch_y = y;
9855 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9857 if (element == EL_ROBOT_WHEEL)
9859 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9863 DrawLevelField(x, y);
9865 else if (element == EL_SP_TERMINAL)
9869 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9871 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9873 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9874 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9877 else if (IS_BELT_SWITCH(element))
9879 ToggleBeltSwitch(x, y);
9881 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9882 element == EL_SWITCHGATE_SWITCH_DOWN)
9884 ToggleSwitchgateSwitch(x, y);
9886 else if (element == EL_LIGHT_SWITCH ||
9887 element == EL_LIGHT_SWITCH_ACTIVE)
9889 ToggleLightSwitch(x, y);
9892 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9893 SND_LIGHT_SWITCH_ACTIVATING :
9894 SND_LIGHT_SWITCH_DEACTIVATING);
9897 else if (element == EL_TIMEGATE_SWITCH)
9899 ActivateTimegateSwitch(x, y);
9901 else if (element == EL_BALLOON_SWITCH_LEFT ||
9902 element == EL_BALLOON_SWITCH_RIGHT ||
9903 element == EL_BALLOON_SWITCH_UP ||
9904 element == EL_BALLOON_SWITCH_DOWN ||
9905 element == EL_BALLOON_SWITCH_ANY)
9907 if (element == EL_BALLOON_SWITCH_ANY)
9908 game.balloon_dir = move_direction;
9910 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9911 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9912 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9913 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9916 else if (element == EL_LAMP)
9918 Feld[x][y] = EL_LAMP_ACTIVE;
9919 local_player->lights_still_needed--;
9921 DrawLevelField(x, y);
9923 else if (element == EL_TIME_ORB_FULL)
9925 Feld[x][y] = EL_TIME_ORB_EMPTY;
9927 DrawGameValue_Time(TimeLeft);
9929 DrawLevelField(x, y);
9932 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9940 if (!PLAYER_SWITCHING(player, x, y))
9942 player->is_switching = TRUE;
9943 player->switch_x = x;
9944 player->switch_y = y;
9946 CheckTriggeredElementChangePlayer(x, y, element,
9947 CE_OTHER_IS_SWITCHING,
9948 player->index_bit, dig_side);
9949 CheckElementChangePlayer(x, y, element, CE_SWITCHED,
9950 player->index_bit, dig_side);
9953 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED,
9954 player->index_bit, dig_side);
9955 CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER,
9956 player->index_bit, dig_side);
9959 return MF_NO_ACTION;
9962 player->push_delay = 0;
9964 if (Feld[x][y] != element) /* really digged/collected something */
9965 player->is_collecting = !player->is_digging;
9970 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9972 int jx = player->jx, jy = player->jy;
9973 int x = jx + dx, y = jy + dy;
9974 int snap_direction = (dx == -1 ? MV_LEFT :
9975 dx == +1 ? MV_RIGHT :
9977 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9979 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9982 if (!player->active || !IN_LEV_FIELD(x, y))
9990 if (player->MovPos == 0)
9991 player->is_pushing = FALSE;
9993 player->is_snapping = FALSE;
9995 if (player->MovPos == 0)
9997 player->is_moving = FALSE;
9998 player->is_digging = FALSE;
9999 player->is_collecting = FALSE;
10005 if (player->is_snapping)
10008 player->MovDir = snap_direction;
10011 if (player->MovPos == 0)
10014 player->is_moving = FALSE;
10015 player->is_digging = FALSE;
10016 player->is_collecting = FALSE;
10019 player->is_dropping = FALSE;
10021 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10024 player->is_snapping = TRUE;
10027 if (player->MovPos == 0)
10030 player->is_moving = FALSE;
10031 player->is_digging = FALSE;
10032 player->is_collecting = FALSE;
10035 DrawLevelField(x, y);
10041 boolean DropElement(struct PlayerInfo *player)
10043 int jx = player->jx, jy = player->jy;
10044 int old_element = Feld[jx][jy];
10045 int new_element = (player->inventory_size > 0 ?
10046 player->inventory_element[player->inventory_size - 1] :
10047 player->inventory_infinite_element != EL_UNDEFINED ?
10048 player->inventory_infinite_element :
10049 player->dynabombs_left > 0 ?
10050 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10053 /* check if player is active, not moving and ready to drop */
10054 if (!player->active || player->MovPos || player->drop_delay > 0)
10057 /* check if player has anything that can be dropped */
10059 if (new_element == EL_UNDEFINED)
10062 if (player->inventory_size == 0 &&
10063 player->inventory_infinite_element == EL_UNDEFINED &&
10064 player->dynabombs_left == 0)
10068 /* check if anything can be dropped at the current position */
10069 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10072 /* collected custom elements can only be dropped on empty fields */
10074 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10077 if (player->inventory_size > 0 &&
10078 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
10079 && old_element != EL_EMPTY)
10083 if (old_element != EL_EMPTY)
10084 Back[jx][jy] = old_element; /* store old element on this field */
10086 ResetGfxAnimation(jx, jy);
10087 ResetRandomAnimationValue(jx, jy);
10089 if (player->inventory_size > 0 ||
10090 player->inventory_infinite_element != EL_UNDEFINED)
10092 if (player->inventory_size > 0)
10094 player->inventory_size--;
10097 new_element = player->inventory_element[player->inventory_size];
10100 DrawGameValue_Dynamite(local_player->inventory_size);
10102 if (new_element == EL_DYNAMITE)
10103 new_element = EL_DYNAMITE_ACTIVE;
10104 else if (new_element == EL_SP_DISK_RED)
10105 new_element = EL_SP_DISK_RED_ACTIVE;
10108 Feld[jx][jy] = new_element;
10110 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10111 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10113 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10116 /* needed if previous element just changed to "empty" in the last frame */
10117 Changed[jx][jy] = 0; /* allow another change */
10120 CheckTriggeredElementChangePlayer(jx, jy, new_element,
10121 CE_OTHER_GETS_DROPPED,
10122 player->index_bit, CH_SIDE_ANY);
10123 CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
10124 player->index_bit, CH_SIDE_ANY);
10126 TestIfElementTouchesCustomElement(jx, jy);
10128 else /* player is dropping a dyna bomb */
10130 player->dynabombs_left--;
10133 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
10136 Feld[jx][jy] = new_element;
10138 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10139 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10141 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10148 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
10151 InitField_WithBug1(jx, jy, FALSE);
10153 InitField(jx, jy, FALSE);
10154 if (CAN_MOVE(Feld[jx][jy]))
10155 InitMovDir(jx, jy);
10159 new_element = Feld[jx][jy];
10161 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10162 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10164 int move_stepsize = element_info[new_element].move_stepsize;
10165 int direction, dx, dy, nextx, nexty;
10167 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10168 MovDir[jx][jy] = player->MovDir;
10170 direction = MovDir[jx][jy];
10171 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10172 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10176 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
10179 WasJustMoving[jx][jy] = 3;
10181 InitMovingField(jx, jy, direction);
10182 ContinueMoving(jx, jy);
10187 Changed[jx][jy] = 0; /* allow another change */
10190 TestIfElementHitsCustomElement(jx, jy, direction);
10192 CheckElementChangeSide(jx, jy, new_element, CE_HITTING_SOMETHING,
10197 player->drop_delay = 2 * TILEX / move_stepsize + 1;
10201 player->drop_delay = 8 + 8 + 8;
10206 player->is_dropping = TRUE;
10212 /* ------------------------------------------------------------------------- */
10213 /* game sound playing functions */
10214 /* ------------------------------------------------------------------------- */
10216 static int *loop_sound_frame = NULL;
10217 static int *loop_sound_volume = NULL;
10219 void InitPlayLevelSound()
10221 int num_sounds = getSoundListSize();
10223 checked_free(loop_sound_frame);
10224 checked_free(loop_sound_volume);
10226 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10227 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10230 static void PlayLevelSound(int x, int y, int nr)
10232 int sx = SCREENX(x), sy = SCREENY(y);
10233 int volume, stereo_position;
10234 int max_distance = 8;
10235 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10237 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10238 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10241 if (!IN_LEV_FIELD(x, y) ||
10242 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10243 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10246 volume = SOUND_MAX_VOLUME;
10248 if (!IN_SCR_FIELD(sx, sy))
10250 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10251 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10253 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10256 stereo_position = (SOUND_MAX_LEFT +
10257 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10258 (SCR_FIELDX + 2 * max_distance));
10260 if (IS_LOOP_SOUND(nr))
10262 /* This assures that quieter loop sounds do not overwrite louder ones,
10263 while restarting sound volume comparison with each new game frame. */
10265 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10268 loop_sound_volume[nr] = volume;
10269 loop_sound_frame[nr] = FrameCounter;
10272 PlaySoundExt(nr, volume, stereo_position, type);
10275 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10277 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10278 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10279 y < LEVELY(BY1) ? LEVELY(BY1) :
10280 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10284 static void PlayLevelSoundAction(int x, int y, int action)
10286 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10289 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10291 int sound_effect = element_info[element].sound[action];
10293 if (sound_effect != SND_UNDEFINED)
10294 PlayLevelSound(x, y, sound_effect);
10297 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10300 int sound_effect = element_info[element].sound[action];
10302 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10303 PlayLevelSound(x, y, sound_effect);
10306 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10308 int sound_effect = element_info[Feld[x][y]].sound[action];
10310 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10311 PlayLevelSound(x, y, sound_effect);
10314 static void StopLevelSoundActionIfLoop(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 StopSound(sound_effect);
10322 static void PlayLevelMusic()
10324 if (levelset.music[level_nr] != MUS_UNDEFINED)
10325 PlayMusic(levelset.music[level_nr]); /* from config file */
10327 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10330 void RaiseScore(int value)
10332 local_player->score += value;
10334 DrawGameValue_Score(local_player->score);
10337 void RaiseScoreElement(int element)
10342 case EL_BD_DIAMOND:
10343 case EL_EMERALD_YELLOW:
10344 case EL_EMERALD_RED:
10345 case EL_EMERALD_PURPLE:
10346 case EL_SP_INFOTRON:
10347 RaiseScore(level.score[SC_EMERALD]);
10350 RaiseScore(level.score[SC_DIAMOND]);
10353 RaiseScore(level.score[SC_CRYSTAL]);
10356 RaiseScore(level.score[SC_PEARL]);
10359 case EL_BD_BUTTERFLY:
10360 case EL_SP_ELECTRON:
10361 RaiseScore(level.score[SC_BUG]);
10364 case EL_BD_FIREFLY:
10365 case EL_SP_SNIKSNAK:
10366 RaiseScore(level.score[SC_SPACESHIP]);
10369 case EL_DARK_YAMYAM:
10370 RaiseScore(level.score[SC_YAMYAM]);
10373 RaiseScore(level.score[SC_ROBOT]);
10376 RaiseScore(level.score[SC_PACMAN]);
10379 RaiseScore(level.score[SC_NUT]);
10382 case EL_SP_DISK_RED:
10383 case EL_DYNABOMB_INCREASE_NUMBER:
10384 case EL_DYNABOMB_INCREASE_SIZE:
10385 case EL_DYNABOMB_INCREASE_POWER:
10386 RaiseScore(level.score[SC_DYNAMITE]);
10388 case EL_SHIELD_NORMAL:
10389 case EL_SHIELD_DEADLY:
10390 RaiseScore(level.score[SC_SHIELD]);
10392 case EL_EXTRA_TIME:
10393 RaiseScore(level.score[SC_TIME_BONUS]);
10399 RaiseScore(level.score[SC_KEY]);
10402 RaiseScore(element_info[element].collect_score);
10407 void RequestQuitGame(boolean ask_if_really_quit)
10409 if (AllPlayersGone ||
10410 !ask_if_really_quit ||
10411 level_editor_test_game ||
10412 Request("Do you really want to quit the game ?",
10413 REQ_ASK | REQ_STAY_CLOSED))
10415 #if defined(PLATFORM_UNIX)
10416 if (options.network)
10417 SendToServer_StopPlaying();
10421 game_status = GAME_MODE_MAIN;
10427 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10432 /* ---------- new game button stuff ---------------------------------------- */
10434 /* graphic position values for game buttons */
10435 #define GAME_BUTTON_XSIZE 30
10436 #define GAME_BUTTON_YSIZE 30
10437 #define GAME_BUTTON_XPOS 5
10438 #define GAME_BUTTON_YPOS 215
10439 #define SOUND_BUTTON_XPOS 5
10440 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10442 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10443 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10444 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10445 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10446 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10447 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10454 } gamebutton_info[NUM_GAME_BUTTONS] =
10457 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10462 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10463 GAME_CTRL_ID_PAUSE,
10467 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10472 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10473 SOUND_CTRL_ID_MUSIC,
10474 "background music on/off"
10477 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10478 SOUND_CTRL_ID_LOOPS,
10479 "sound loops on/off"
10482 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10483 SOUND_CTRL_ID_SIMPLE,
10484 "normal sounds on/off"
10488 void CreateGameButtons()
10492 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10494 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10495 struct GadgetInfo *gi;
10498 unsigned long event_mask;
10499 int gd_xoffset, gd_yoffset;
10500 int gd_x1, gd_x2, gd_y1, gd_y2;
10503 gd_xoffset = gamebutton_info[i].x;
10504 gd_yoffset = gamebutton_info[i].y;
10505 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10506 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10508 if (id == GAME_CTRL_ID_STOP ||
10509 id == GAME_CTRL_ID_PAUSE ||
10510 id == GAME_CTRL_ID_PLAY)
10512 button_type = GD_TYPE_NORMAL_BUTTON;
10514 event_mask = GD_EVENT_RELEASED;
10515 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10516 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10520 button_type = GD_TYPE_CHECK_BUTTON;
10522 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10523 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10524 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10525 event_mask = GD_EVENT_PRESSED;
10526 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10527 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10530 gi = CreateGadget(GDI_CUSTOM_ID, id,
10531 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10532 GDI_X, DX + gd_xoffset,
10533 GDI_Y, DY + gd_yoffset,
10534 GDI_WIDTH, GAME_BUTTON_XSIZE,
10535 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10536 GDI_TYPE, button_type,
10537 GDI_STATE, GD_BUTTON_UNPRESSED,
10538 GDI_CHECKED, checked,
10539 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10540 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10541 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10542 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10543 GDI_EVENT_MASK, event_mask,
10544 GDI_CALLBACK_ACTION, HandleGameButtons,
10548 Error(ERR_EXIT, "cannot create gadget");
10550 game_gadget[id] = gi;
10554 void FreeGameButtons()
10558 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10559 FreeGadget(game_gadget[i]);
10562 static void MapGameButtons()
10566 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10567 MapGadget(game_gadget[i]);
10570 void UnmapGameButtons()
10574 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10575 UnmapGadget(game_gadget[i]);
10578 static void HandleGameButtons(struct GadgetInfo *gi)
10580 int id = gi->custom_id;
10582 if (game_status != GAME_MODE_PLAYING)
10587 case GAME_CTRL_ID_STOP:
10588 RequestQuitGame(TRUE);
10591 case GAME_CTRL_ID_PAUSE:
10592 if (options.network)
10594 #if defined(PLATFORM_UNIX)
10596 SendToServer_ContinuePlaying();
10598 SendToServer_PausePlaying();
10602 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10605 case GAME_CTRL_ID_PLAY:
10608 #if defined(PLATFORM_UNIX)
10609 if (options.network)
10610 SendToServer_ContinuePlaying();
10614 tape.pausing = FALSE;
10615 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10620 case SOUND_CTRL_ID_MUSIC:
10621 if (setup.sound_music)
10623 setup.sound_music = FALSE;
10626 else if (audio.music_available)
10628 setup.sound = setup.sound_music = TRUE;
10630 SetAudioMode(setup.sound);
10636 case SOUND_CTRL_ID_LOOPS:
10637 if (setup.sound_loops)
10638 setup.sound_loops = FALSE;
10639 else if (audio.loops_available)
10641 setup.sound = setup.sound_loops = TRUE;
10642 SetAudioMode(setup.sound);
10646 case SOUND_CTRL_ID_SIMPLE:
10647 if (setup.sound_simple)
10648 setup.sound_simple = FALSE;
10649 else if (audio.sound_available)
10651 setup.sound = setup.sound_simple = TRUE;
10652 SetAudioMode(setup.sound);