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 KillHeroUnlessEnemyProtected(int, int);
245 static void KillHeroUnlessExplosionProtected(int, int);
247 static void TestIfPlayerTouchesCustomElement(int, int);
248 static void TestIfElementTouchesCustomElement(int, int);
249 static void TestIfElementHitsCustomElement(int, int, int);
251 static void ChangeElement(int, int, int);
252 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
253 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
254 static boolean CheckTriggeredElementPlayerChange(int, int, int, int, int, int);
255 static boolean CheckTriggeredElementPageChange(int, int, int, int, int);
256 static boolean CheckTriggeredElementChange(int, int, int, int);
257 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
258 static boolean CheckElementSideChange(int, int, int, int, int, int);
259 static boolean CheckElementPlayerChange(int, int, int, int, int, int, int);
260 static boolean CheckElementChange(int, int, int, int);
262 static void PlayLevelSound(int, int, int);
263 static void PlayLevelSoundNearest(int, int, int);
264 static void PlayLevelSoundAction(int, int, int);
265 static void PlayLevelSoundElementAction(int, int, int, int);
266 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
267 static void PlayLevelSoundActionIfLoop(int, int, int);
268 static void StopLevelSoundActionIfLoop(int, int, int);
269 static void PlayLevelMusic();
271 static void MapGameButtons();
272 static void HandleGameButtons(struct GadgetInfo *);
274 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
277 /* ------------------------------------------------------------------------- */
278 /* definition of elements that automatically change to other elements after */
279 /* a specified time, eventually calling a function when changing */
280 /* ------------------------------------------------------------------------- */
282 /* forward declaration for changer functions */
283 static void InitBuggyBase(int x, int y);
284 static void WarnBuggyBase(int x, int y);
286 static void InitTrap(int x, int y);
287 static void ActivateTrap(int x, int y);
288 static void ChangeActiveTrap(int x, int y);
290 static void InitRobotWheel(int x, int y);
291 static void RunRobotWheel(int x, int y);
292 static void StopRobotWheel(int x, int y);
294 static void InitTimegateWheel(int x, int y);
295 static void RunTimegateWheel(int x, int y);
297 struct ChangingElementInfo
302 void (*pre_change_function)(int x, int y);
303 void (*change_function)(int x, int y);
304 void (*post_change_function)(int x, int y);
307 static struct ChangingElementInfo change_delay_list[] =
358 EL_SWITCHGATE_OPENING,
366 EL_SWITCHGATE_CLOSING,
367 EL_SWITCHGATE_CLOSED,
399 EL_ACID_SPLASH_RIGHT,
408 EL_SP_BUGGY_BASE_ACTIVATING,
415 EL_SP_BUGGY_BASE_ACTIVATING,
416 EL_SP_BUGGY_BASE_ACTIVE,
423 EL_SP_BUGGY_BASE_ACTIVE,
447 EL_ROBOT_WHEEL_ACTIVE,
455 EL_TIMEGATE_SWITCH_ACTIVE,
476 int push_delay_fixed, push_delay_random;
481 { EL_BALLOON, 0, 0 },
483 { EL_SOKOBAN_OBJECT, 2, 0 },
484 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
485 { EL_SATELLITE, 2, 0 },
486 { EL_SP_DISK_YELLOW, 2, 0 },
488 { EL_UNDEFINED, 0, 0 },
496 move_stepsize_list[] =
498 { EL_AMOEBA_DROP, 2 },
499 { EL_AMOEBA_DROPPING, 2 },
500 { EL_QUICKSAND_FILLING, 1 },
501 { EL_QUICKSAND_EMPTYING, 1 },
502 { EL_MAGIC_WALL_FILLING, 2 },
503 { EL_BD_MAGIC_WALL_FILLING, 2 },
504 { EL_MAGIC_WALL_EMPTYING, 2 },
505 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
515 collect_count_list[] =
518 { EL_BD_DIAMOND, 1 },
519 { EL_EMERALD_YELLOW, 1 },
520 { EL_EMERALD_RED, 1 },
521 { EL_EMERALD_PURPLE, 1 },
523 { EL_SP_INFOTRON, 1 },
537 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
538 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
539 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
540 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
541 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
542 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
543 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
544 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
545 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
546 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
547 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
552 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
554 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
555 CH_EVENT_BIT(CE_DELAY))
556 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
557 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
558 IS_JUST_CHANGING(x, y))
560 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
563 void GetPlayerConfig()
565 if (!audio.sound_available)
566 setup.sound_simple = FALSE;
568 if (!audio.loops_available)
569 setup.sound_loops = FALSE;
571 if (!audio.music_available)
572 setup.sound_music = FALSE;
574 if (!video.fullscreen_available)
575 setup.fullscreen = FALSE;
577 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
579 SetAudioMode(setup.sound);
583 static int getBeltNrFromBeltElement(int element)
585 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
586 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
587 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
590 static int getBeltNrFromBeltActiveElement(int element)
592 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
593 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
594 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
597 static int getBeltNrFromBeltSwitchElement(int element)
599 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
600 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
601 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
604 static int getBeltDirNrFromBeltSwitchElement(int element)
606 static int belt_base_element[4] =
608 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
609 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
610 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
611 EL_CONVEYOR_BELT_4_SWITCH_LEFT
614 int belt_nr = getBeltNrFromBeltSwitchElement(element);
615 int belt_dir_nr = element - belt_base_element[belt_nr];
617 return (belt_dir_nr % 3);
620 static int getBeltDirFromBeltSwitchElement(int element)
622 static int belt_move_dir[3] =
629 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
631 return belt_move_dir[belt_dir_nr];
634 static void InitPlayerField(int x, int y, int element, boolean init_game)
636 if (element == EL_SP_MURPHY)
640 if (stored_player[0].present)
642 Feld[x][y] = EL_SP_MURPHY_CLONE;
648 stored_player[0].use_murphy_graphic = TRUE;
651 Feld[x][y] = EL_PLAYER_1;
657 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
658 int jx = player->jx, jy = player->jy;
660 player->present = TRUE;
662 player->block_last_field = (element == EL_SP_MURPHY ?
663 level.sp_block_last_field :
664 level.block_last_field);
666 if (!options.network || player->connected)
668 player->active = TRUE;
670 /* remove potentially duplicate players */
671 if (StorePlayer[jx][jy] == Feld[x][y])
672 StorePlayer[jx][jy] = 0;
674 StorePlayer[x][y] = Feld[x][y];
678 printf("Player %d activated.\n", player->element_nr);
679 printf("[Local player is %d and currently %s.]\n",
680 local_player->element_nr,
681 local_player->active ? "active" : "not active");
685 Feld[x][y] = EL_EMPTY;
686 player->jx = player->last_jx = x;
687 player->jy = player->last_jy = y;
691 static void InitField(int x, int y, boolean init_game)
693 int element = Feld[x][y];
702 InitPlayerField(x, y, element, init_game);
705 case EL_SOKOBAN_FIELD_PLAYER:
706 element = Feld[x][y] = EL_PLAYER_1;
707 InitField(x, y, init_game);
709 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
710 InitField(x, y, init_game);
713 case EL_SOKOBAN_FIELD_EMPTY:
714 local_player->sokobanfields_still_needed++;
718 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
719 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
720 else if (x > 0 && Feld[x-1][y] == EL_ACID)
721 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
722 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
723 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
724 else if (y > 0 && Feld[x][y-1] == EL_ACID)
725 Feld[x][y] = EL_ACID_POOL_BOTTOM;
726 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
727 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
735 case EL_SPACESHIP_RIGHT:
736 case EL_SPACESHIP_UP:
737 case EL_SPACESHIP_LEFT:
738 case EL_SPACESHIP_DOWN:
740 case EL_BD_BUTTERFLY_RIGHT:
741 case EL_BD_BUTTERFLY_UP:
742 case EL_BD_BUTTERFLY_LEFT:
743 case EL_BD_BUTTERFLY_DOWN:
744 case EL_BD_BUTTERFLY:
745 case EL_BD_FIREFLY_RIGHT:
746 case EL_BD_FIREFLY_UP:
747 case EL_BD_FIREFLY_LEFT:
748 case EL_BD_FIREFLY_DOWN:
750 case EL_PACMAN_RIGHT:
774 if (y == lev_fieldy - 1)
776 Feld[x][y] = EL_AMOEBA_GROWING;
777 Store[x][y] = EL_AMOEBA_WET;
781 case EL_DYNAMITE_ACTIVE:
782 case EL_SP_DISK_RED_ACTIVE:
783 case EL_DYNABOMB_PLAYER_1_ACTIVE:
784 case EL_DYNABOMB_PLAYER_2_ACTIVE:
785 case EL_DYNABOMB_PLAYER_3_ACTIVE:
786 case EL_DYNABOMB_PLAYER_4_ACTIVE:
791 local_player->lights_still_needed++;
795 local_player->friends_still_needed++;
800 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
805 Feld[x][y] = EL_EMPTY;
810 case EL_EM_KEY_1_FILE:
811 Feld[x][y] = EL_EM_KEY_1;
813 case EL_EM_KEY_2_FILE:
814 Feld[x][y] = EL_EM_KEY_2;
816 case EL_EM_KEY_3_FILE:
817 Feld[x][y] = EL_EM_KEY_3;
819 case EL_EM_KEY_4_FILE:
820 Feld[x][y] = EL_EM_KEY_4;
824 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
825 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
826 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
827 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
828 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
829 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
830 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
831 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
832 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
833 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
834 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
835 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
838 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
839 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
840 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
842 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
844 game.belt_dir[belt_nr] = belt_dir;
845 game.belt_dir_nr[belt_nr] = belt_dir_nr;
847 else /* more than one switch -- set it like the first switch */
849 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
854 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
856 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
859 case EL_LIGHT_SWITCH_ACTIVE:
861 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
865 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
867 else if (IS_GROUP_ELEMENT(element))
869 struct ElementGroupInfo *group = element_info[element].group;
870 int last_anim_random_frame = gfx.anim_random_frame;
873 if (group->choice_mode == ANIM_RANDOM)
874 gfx.anim_random_frame = RND(group->num_elements_resolved);
876 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
877 group->choice_mode, 0,
880 if (group->choice_mode == ANIM_RANDOM)
881 gfx.anim_random_frame = last_anim_random_frame;
885 Feld[x][y] = group->element_resolved[element_pos];
887 InitField(x, y, init_game);
893 static inline void InitField_WithBug1(int x, int y, boolean init_game)
895 InitField(x, y, init_game);
897 /* not needed to call InitMovDir() -- already done by InitField()! */
898 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
899 CAN_MOVE(Feld[x][y]))
903 static inline void InitField_WithBug2(int x, int y, boolean init_game)
905 int old_element = Feld[x][y];
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(old_element) &&
912 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
915 /* this case is in fact a combination of not less than three bugs:
916 first, it calls InitMovDir() for elements that can move, although this is
917 already done by InitField(); then, it checks the element that was at this
918 field _before_ the call to InitField() (which can change it)
923 inline void DrawGameValue_Emeralds(int value)
925 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
928 inline void DrawGameValue_Dynamite(int value)
930 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
933 inline void DrawGameValue_Keys(struct PlayerInfo *player)
937 for (i = 0; i < MAX_KEYS; i++)
939 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
940 el2edimg(EL_KEY_1 + i));
943 inline void DrawGameValue_Score(int value)
945 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
948 inline void DrawGameValue_Time(int value)
951 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
953 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
956 inline void DrawGameValue_Level(int value)
959 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
962 /* misuse area for displaying emeralds to draw bigger level number */
963 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
964 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
966 /* now copy it to the area for displaying level number */
967 BlitBitmap(drawto, drawto,
968 DX_EMERALDS, DY_EMERALDS + 1,
969 getFontWidth(FONT_LEVEL_NUMBER) * 3,
970 getFontHeight(FONT_LEVEL_NUMBER) - 1,
971 DX_LEVEL - 1, DY_LEVEL + 1);
973 /* restore the area for displaying emeralds */
974 DrawGameValue_Emeralds(local_player->gems_still_needed);
976 /* yes, this is all really ugly :-) */
980 void DrawGameDoorValues()
984 DrawGameValue_Level(level_nr);
986 for (i = 0; i < MAX_PLAYERS; i++)
987 DrawGameValue_Keys(&stored_player[i]);
989 DrawGameValue_Emeralds(local_player->gems_still_needed);
990 DrawGameValue_Dynamite(local_player->inventory_size);
991 DrawGameValue_Score(local_player->score);
992 DrawGameValue_Time(TimeLeft);
995 static void resolve_group_element(int group_element, int recursion_depth)
998 static struct ElementGroupInfo *group;
999 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1002 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1004 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1005 group_element - EL_GROUP_START + 1);
1007 /* replace element which caused too deep recursion by question mark */
1008 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1013 if (recursion_depth == 0) /* initialization */
1015 group = element_info[group_element].group;
1016 group_nr = group_element - EL_GROUP_START;
1018 group->num_elements_resolved = 0;
1019 group->choice_pos = 0;
1022 for (i = 0; i < actual_group->num_elements; i++)
1024 int element = actual_group->element[i];
1026 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1029 if (IS_GROUP_ELEMENT(element))
1030 resolve_group_element(element, recursion_depth + 1);
1033 group->element_resolved[group->num_elements_resolved++] = element;
1034 element_info[element].in_group[group_nr] = TRUE;
1039 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1041 printf("::: group %d: %d resolved elements\n",
1042 group_element - EL_GROUP_START, group->num_elements_resolved);
1043 for (i = 0; i < group->num_elements_resolved; i++)
1044 printf("::: - %d ['%s']\n", group->element_resolved[i],
1045 element_info[group->element_resolved[i]].token_name);
1052 =============================================================================
1054 -----------------------------------------------------------------------------
1055 initialize game engine due to level / tape version number
1056 =============================================================================
1059 static void InitGameEngine()
1063 /* set game engine from tape file when re-playing, else from level file */
1064 game.engine_version = (tape.playing ? tape.engine_version :
1065 level.game_version);
1067 /* dynamically adjust element properties according to game engine version */
1068 InitElementPropertiesEngine(game.engine_version);
1071 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1072 printf(" tape version == %06d [%s] [file: %06d]\n",
1073 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1075 printf(" => game.engine_version == %06d\n", game.engine_version);
1078 /* ---------- recursively resolve group elements ------------------------- */
1080 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1081 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1082 element_info[i].in_group[j] = FALSE;
1084 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1085 resolve_group_element(EL_GROUP_START + i, 0);
1087 /* ---------- initialize player's initial move delay --------------------- */
1089 /* dynamically adjust player properties according to game engine version */
1090 game.initial_move_delay =
1091 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1092 INITIAL_MOVE_DELAY_OFF);
1094 /* dynamically adjust player properties according to level information */
1095 game.initial_move_delay_value =
1096 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1098 /* ---------- initialize player's initial push delay --------------------- */
1100 /* dynamically adjust player properties according to game engine version */
1101 game.initial_push_delay_value =
1102 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1104 /* ---------- initialize changing elements ------------------------------- */
1106 /* initialize changing elements information */
1107 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1109 struct ElementInfo *ei = &element_info[i];
1111 /* this pointer might have been changed in the level editor */
1112 ei->change = &ei->change_page[0];
1114 if (!IS_CUSTOM_ELEMENT(i))
1116 ei->change->target_element = EL_EMPTY_SPACE;
1117 ei->change->delay_fixed = 0;
1118 ei->change->delay_random = 0;
1119 ei->change->delay_frames = 1;
1122 ei->change_events = CE_BITMASK_DEFAULT;
1123 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1125 ei->event_page_nr[j] = 0;
1126 ei->event_page[j] = &ei->change_page[0];
1130 /* add changing elements from pre-defined list */
1131 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1133 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1134 struct ElementInfo *ei = &element_info[ch_delay->element];
1136 ei->change->target_element = ch_delay->target_element;
1137 ei->change->delay_fixed = ch_delay->change_delay;
1139 ei->change->pre_change_function = ch_delay->pre_change_function;
1140 ei->change->change_function = ch_delay->change_function;
1141 ei->change->post_change_function = ch_delay->post_change_function;
1143 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1147 /* add change events from custom element configuration */
1148 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1150 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1152 for (j = 0; j < ei->num_change_pages; j++)
1154 if (!ei->change_page[j].can_change)
1157 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1159 /* only add event page for the first page found with this event */
1160 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1161 !(ei->change_events & CH_EVENT_BIT(k)))
1163 ei->change_events |= CH_EVENT_BIT(k);
1164 ei->event_page_nr[k] = j;
1165 ei->event_page[k] = &ei->change_page[j];
1173 /* add change events from custom element configuration */
1174 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1176 int element = EL_CUSTOM_START + i;
1178 /* only add custom elements that change after fixed/random frame delay */
1179 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1180 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1184 /* ---------- initialize trigger events ---------------------------------- */
1186 /* initialize trigger events information */
1187 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1188 trigger_events[i] = EP_BITMASK_DEFAULT;
1191 /* add trigger events from element change event properties */
1192 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1194 struct ElementInfo *ei = &element_info[i];
1196 for (j = 0; j < ei->num_change_pages; j++)
1198 if (!ei->change_page[j].can_change)
1201 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1203 int trigger_element = ei->change_page[j].trigger_element;
1205 if (IS_GROUP_ELEMENT(trigger_element))
1207 struct ElementGroupInfo *group = element_info[trigger_element].group;
1209 for (k = 0; k < group->num_elements_resolved; k++)
1210 trigger_events[group->element_resolved[k]]
1211 |= ei->change_page[j].events;
1214 trigger_events[trigger_element] |= ei->change_page[j].events;
1219 /* add trigger events from element change event properties */
1220 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1221 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1222 trigger_events[element_info[i].change->trigger_element] |=
1223 element_info[i].change->events;
1226 /* ---------- initialize push delay -------------------------------------- */
1228 /* initialize push delay values to default */
1229 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1231 if (!IS_CUSTOM_ELEMENT(i))
1233 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1234 element_info[i].push_delay_random = game.default_push_delay_random;
1238 /* set push delay value for certain elements from pre-defined list */
1239 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1241 int e = push_delay_list[i].element;
1243 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1244 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1247 /* set push delay value for Supaplex elements for newer engine versions */
1248 if (game.engine_version >= VERSION_IDENT(3,0,9,0))
1250 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1252 if (IS_SP_ELEMENT(i))
1254 element_info[i].push_delay_fixed = 6;
1255 element_info[i].push_delay_random = 0;
1260 /* ---------- initialize move stepsize ----------------------------------- */
1262 /* initialize move stepsize values to default */
1263 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1264 if (!IS_CUSTOM_ELEMENT(i))
1265 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1267 /* set move stepsize value for certain elements from pre-defined list */
1268 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1270 int e = move_stepsize_list[i].element;
1272 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1275 /* ---------- initialize move dig/leave ---------------------------------- */
1277 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1279 element_info[i].can_leave_element = FALSE;
1280 element_info[i].can_leave_element_last = FALSE;
1283 /* ---------- initialize gem count --------------------------------------- */
1285 /* initialize gem count values for each element */
1286 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1287 if (!IS_CUSTOM_ELEMENT(i))
1288 element_info[i].collect_count = 0;
1290 /* add gem count values for all elements from pre-defined list */
1291 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1292 element_info[collect_count_list[i].element].collect_count =
1293 collect_count_list[i].count;
1295 /* ---------- initialize access direction -------------------------------- */
1297 /* initialize access direction values to default */
1298 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1299 if (!IS_CUSTOM_ELEMENT(i))
1300 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1302 /* set access direction value for certain elements from pre-defined list */
1303 for (i = 0; tube_access[i].element != EL_UNDEFINED; i++)
1304 element_info[tube_access[i].element].access_direction =
1305 tube_access[i].direction;
1310 =============================================================================
1312 -----------------------------------------------------------------------------
1313 initialize and start new game
1314 =============================================================================
1319 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1320 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1321 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1328 #if USE_NEW_AMOEBA_CODE
1329 printf("Using new amoeba code.\n");
1331 printf("Using old amoeba code.\n");
1336 /* don't play tapes over network */
1337 network_playing = (options.network && !tape.playing);
1339 for (i = 0; i < MAX_PLAYERS; i++)
1341 struct PlayerInfo *player = &stored_player[i];
1343 player->index_nr = i;
1344 player->element_nr = EL_PLAYER_1 + i;
1346 player->present = FALSE;
1347 player->active = FALSE;
1350 player->effective_action = 0;
1351 player->programmed_action = 0;
1354 player->gems_still_needed = level.gems_needed;
1355 player->sokobanfields_still_needed = 0;
1356 player->lights_still_needed = 0;
1357 player->friends_still_needed = 0;
1359 for (j = 0; j < MAX_KEYS; j++)
1360 player->key[j] = FALSE;
1362 player->dynabomb_count = 0;
1363 player->dynabomb_size = 1;
1364 player->dynabombs_left = 0;
1365 player->dynabomb_xl = FALSE;
1367 player->MovDir = MV_NO_MOVING;
1370 player->GfxDir = MV_NO_MOVING;
1371 player->GfxAction = ACTION_DEFAULT;
1373 player->StepFrame = 0;
1375 player->use_murphy_graphic = FALSE;
1377 player->block_last_field = FALSE;
1379 player->actual_frame_counter = 0;
1381 player->step_counter = 0;
1383 player->last_move_dir = MV_NO_MOVING;
1385 player->is_waiting = FALSE;
1386 player->is_moving = FALSE;
1387 player->is_digging = FALSE;
1388 player->is_snapping = FALSE;
1389 player->is_collecting = FALSE;
1390 player->is_pushing = FALSE;
1391 player->is_switching = FALSE;
1392 player->is_dropping = FALSE;
1394 player->is_bored = FALSE;
1395 player->is_sleeping = FALSE;
1397 player->frame_counter_bored = -1;
1398 player->frame_counter_sleeping = -1;
1400 player->anim_delay_counter = 0;
1401 player->post_delay_counter = 0;
1403 player->action_waiting = ACTION_DEFAULT;
1404 player->last_action_waiting = ACTION_DEFAULT;
1405 player->special_action_bored = ACTION_DEFAULT;
1406 player->special_action_sleeping = ACTION_DEFAULT;
1408 player->num_special_action_bored = 0;
1409 player->num_special_action_sleeping = 0;
1411 /* determine number of special actions for bored and sleeping animation */
1412 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1414 boolean found = FALSE;
1416 for (k = 0; k < NUM_DIRECTIONS; k++)
1417 if (el_act_dir2img(player->element_nr, j, k) !=
1418 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1422 player->num_special_action_bored++;
1426 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1428 boolean found = FALSE;
1430 for (k = 0; k < NUM_DIRECTIONS; k++)
1431 if (el_act_dir2img(player->element_nr, j, k) !=
1432 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1436 player->num_special_action_sleeping++;
1441 player->switch_x = -1;
1442 player->switch_y = -1;
1444 player->show_envelope = 0;
1446 player->move_delay = game.initial_move_delay;
1447 player->move_delay_value = game.initial_move_delay_value;
1449 player->move_delay_reset_counter = 0;
1451 player->push_delay = 0;
1452 player->push_delay_value = game.initial_push_delay_value;
1454 player->drop_delay = 0;
1456 player->last_jx = player->last_jy = 0;
1457 player->jx = player->jy = 0;
1459 player->shield_normal_time_left = 0;
1460 player->shield_deadly_time_left = 0;
1462 player->inventory_size = 0;
1464 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1465 SnapField(player, 0, 0);
1467 player->LevelSolved = FALSE;
1468 player->GameOver = FALSE;
1471 network_player_action_received = FALSE;
1473 #if defined(PLATFORM_UNIX)
1474 /* initial null action */
1475 if (network_playing)
1476 SendToServer_MovePlayer(MV_NO_MOVING);
1484 TimeLeft = level.time;
1486 ScreenMovDir = MV_NO_MOVING;
1490 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1492 AllPlayersGone = FALSE;
1494 game.yamyam_content_nr = 0;
1495 game.magic_wall_active = FALSE;
1496 game.magic_wall_time_left = 0;
1497 game.light_time_left = 0;
1498 game.timegate_time_left = 0;
1499 game.switchgate_pos = 0;
1500 game.balloon_dir = MV_NO_MOVING;
1501 game.gravity = level.initial_gravity;
1502 game.explosions_delayed = TRUE;
1504 game.envelope_active = FALSE;
1506 for (i = 0; i < NUM_BELTS; i++)
1508 game.belt_dir[i] = MV_NO_MOVING;
1509 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1512 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1513 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1515 for (x = 0; x < lev_fieldx; x++)
1517 for (y = 0; y < lev_fieldy; y++)
1519 Feld[x][y] = level.field[x][y];
1520 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1521 ChangeDelay[x][y] = 0;
1522 ChangePage[x][y] = -1;
1523 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1525 WasJustMoving[x][y] = 0;
1526 WasJustFalling[x][y] = 0;
1528 Pushed[x][y] = FALSE;
1530 Changed[x][y] = CE_BITMASK_DEFAULT;
1531 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1533 ExplodePhase[x][y] = 0;
1534 ExplodeDelay[x][y] = 0;
1535 ExplodeField[x][y] = EX_NO_EXPLOSION;
1537 RunnerVisit[x][y] = 0;
1538 PlayerVisit[x][y] = 0;
1541 GfxRandom[x][y] = INIT_GFX_RANDOM();
1542 GfxElement[x][y] = EL_UNDEFINED;
1543 GfxAction[x][y] = ACTION_DEFAULT;
1544 GfxDir[x][y] = MV_NO_MOVING;
1548 for (y = 0; y < lev_fieldy; y++)
1550 for (x = 0; x < lev_fieldx; x++)
1552 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1554 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1556 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1559 InitField(x, y, TRUE);
1565 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1566 emulate_sb ? EMU_SOKOBAN :
1567 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1569 /* initialize explosion and ignition delay */
1570 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1572 if (!IS_CUSTOM_ELEMENT(i))
1575 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
1576 int last_phase = num_phase * delay;
1577 int half_phase = (num_phase / 2) * delay;
1579 element_info[i].explosion_delay = last_phase;
1580 element_info[i].ignition_delay = half_phase;
1582 if (i == EL_BLACK_ORB)
1583 element_info[i].ignition_delay = 1;
1586 if (element_info[i].explosion_delay < 2) /* !!! check again !!! */
1587 element_info[i].explosion_delay = 2;
1589 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1590 element_info[i].ignition_delay = 1;
1593 /* correct non-moving belts to start moving left */
1594 for (i = 0; i < NUM_BELTS; i++)
1595 if (game.belt_dir[i] == MV_NO_MOVING)
1596 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1598 /* check if any connected player was not found in playfield */
1599 for (i = 0; i < MAX_PLAYERS; i++)
1601 struct PlayerInfo *player = &stored_player[i];
1603 if (player->connected && !player->present)
1605 for (j = 0; j < MAX_PLAYERS; j++)
1607 struct PlayerInfo *some_player = &stored_player[j];
1608 int jx = some_player->jx, jy = some_player->jy;
1610 /* assign first free player found that is present in the playfield */
1611 if (some_player->present && !some_player->connected)
1613 player->present = TRUE;
1614 player->active = TRUE;
1616 some_player->present = FALSE;
1617 some_player->active = FALSE;
1619 StorePlayer[jx][jy] = player->element_nr;
1620 player->jx = player->last_jx = jx;
1621 player->jy = player->last_jy = jy;
1631 /* when playing a tape, eliminate all players which do not participate */
1633 for (i = 0; i < MAX_PLAYERS; i++)
1635 if (stored_player[i].active && !tape.player_participates[i])
1637 struct PlayerInfo *player = &stored_player[i];
1638 int jx = player->jx, jy = player->jy;
1640 player->active = FALSE;
1641 StorePlayer[jx][jy] = 0;
1642 Feld[jx][jy] = EL_EMPTY;
1646 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1648 /* when in single player mode, eliminate all but the first active player */
1650 for (i = 0; i < MAX_PLAYERS; i++)
1652 if (stored_player[i].active)
1654 for (j = i + 1; j < MAX_PLAYERS; j++)
1656 if (stored_player[j].active)
1658 struct PlayerInfo *player = &stored_player[j];
1659 int jx = player->jx, jy = player->jy;
1661 player->active = FALSE;
1662 player->present = FALSE;
1664 StorePlayer[jx][jy] = 0;
1665 Feld[jx][jy] = EL_EMPTY;
1672 /* when recording the game, store which players take part in the game */
1675 for (i = 0; i < MAX_PLAYERS; i++)
1676 if (stored_player[i].active)
1677 tape.player_participates[i] = TRUE;
1682 for (i = 0; i < MAX_PLAYERS; i++)
1684 struct PlayerInfo *player = &stored_player[i];
1686 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1691 if (local_player == player)
1692 printf("Player %d is local player.\n", i+1);
1696 if (BorderElement == EL_EMPTY)
1699 SBX_Right = lev_fieldx - SCR_FIELDX;
1701 SBY_Lower = lev_fieldy - SCR_FIELDY;
1706 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1708 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1711 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1712 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1714 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1715 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1717 /* if local player not found, look for custom element that might create
1718 the player (make some assumptions about the right custom element) */
1719 if (!local_player->present)
1721 int start_x = 0, start_y = 0;
1722 int found_rating = 0;
1723 int found_element = EL_UNDEFINED;
1725 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1727 int element = Feld[x][y];
1732 if (!IS_CUSTOM_ELEMENT(element))
1735 if (CAN_CHANGE(element))
1737 for (i = 0; i < element_info[element].num_change_pages; i++)
1739 content = element_info[element].change_page[i].target_element;
1740 is_player = ELEM_IS_PLAYER(content);
1742 if (is_player && (found_rating < 3 || element < found_element))
1748 found_element = element;
1753 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1755 content = element_info[element].content[xx][yy];
1756 is_player = ELEM_IS_PLAYER(content);
1758 if (is_player && (found_rating < 2 || element < found_element))
1760 start_x = x + xx - 1;
1761 start_y = y + yy - 1;
1764 found_element = element;
1767 if (!CAN_CHANGE(element))
1770 for (i = 0; i < element_info[element].num_change_pages; i++)
1772 content = element_info[element].change_page[i].content[xx][yy];
1773 is_player = ELEM_IS_PLAYER(content);
1775 if (is_player && (found_rating < 1 || element < found_element))
1777 start_x = x + xx - 1;
1778 start_y = y + yy - 1;
1781 found_element = element;
1787 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1788 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1791 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1792 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1798 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1799 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1800 local_player->jx - MIDPOSX);
1802 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1803 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1804 local_player->jy - MIDPOSY);
1806 scroll_x = SBX_Left;
1807 scroll_y = SBY_Upper;
1808 if (local_player->jx >= SBX_Left + MIDPOSX)
1809 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1810 local_player->jx - MIDPOSX :
1812 if (local_player->jy >= SBY_Upper + MIDPOSY)
1813 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1814 local_player->jy - MIDPOSY :
1819 CloseDoor(DOOR_CLOSE_1);
1824 /* after drawing the level, correct some elements */
1825 if (game.timegate_time_left == 0)
1826 CloseAllOpenTimegates();
1828 if (setup.soft_scrolling)
1829 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1831 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1834 /* copy default game door content to main double buffer */
1835 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1836 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1838 DrawGameDoorValues();
1842 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1843 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1844 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1848 /* copy actual game door content to door double buffer for OpenDoor() */
1849 BlitBitmap(drawto, bitmap_db_door,
1850 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1852 OpenDoor(DOOR_OPEN_ALL);
1854 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1856 if (setup.sound_music)
1859 KeyboardAutoRepeatOffUnlessAutoplay();
1863 for (i = 0; i < MAX_PLAYERS; i++)
1864 printf("Player %d %sactive.\n",
1865 i + 1, (stored_player[i].active ? "" : "not "));
1869 printf("::: starting game [%d]\n", FrameCounter);
1873 void InitMovDir(int x, int y)
1875 int i, element = Feld[x][y];
1876 static int xy[4][2] =
1883 static int direction[3][4] =
1885 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1886 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1887 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1896 Feld[x][y] = EL_BUG;
1897 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1900 case EL_SPACESHIP_RIGHT:
1901 case EL_SPACESHIP_UP:
1902 case EL_SPACESHIP_LEFT:
1903 case EL_SPACESHIP_DOWN:
1904 Feld[x][y] = EL_SPACESHIP;
1905 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1908 case EL_BD_BUTTERFLY_RIGHT:
1909 case EL_BD_BUTTERFLY_UP:
1910 case EL_BD_BUTTERFLY_LEFT:
1911 case EL_BD_BUTTERFLY_DOWN:
1912 Feld[x][y] = EL_BD_BUTTERFLY;
1913 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1916 case EL_BD_FIREFLY_RIGHT:
1917 case EL_BD_FIREFLY_UP:
1918 case EL_BD_FIREFLY_LEFT:
1919 case EL_BD_FIREFLY_DOWN:
1920 Feld[x][y] = EL_BD_FIREFLY;
1921 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1924 case EL_PACMAN_RIGHT:
1926 case EL_PACMAN_LEFT:
1927 case EL_PACMAN_DOWN:
1928 Feld[x][y] = EL_PACMAN;
1929 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1932 case EL_SP_SNIKSNAK:
1933 MovDir[x][y] = MV_UP;
1936 case EL_SP_ELECTRON:
1937 MovDir[x][y] = MV_LEFT;
1944 Feld[x][y] = EL_MOLE;
1945 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1949 if (IS_CUSTOM_ELEMENT(element))
1951 struct ElementInfo *ei = &element_info[element];
1952 int move_direction_initial = ei->move_direction_initial;
1953 int move_pattern = ei->move_pattern;
1955 if (move_direction_initial == MV_START_PREVIOUS)
1957 if (MovDir[x][y] != MV_NO_MOVING)
1960 move_direction_initial = MV_START_AUTOMATIC;
1963 if (move_direction_initial == MV_START_RANDOM)
1964 MovDir[x][y] = 1 << RND(4);
1965 else if (move_direction_initial & MV_ANY_DIRECTION)
1966 MovDir[x][y] = move_direction_initial;
1967 else if (move_pattern == MV_ALL_DIRECTIONS ||
1968 move_pattern == MV_TURNING_LEFT ||
1969 move_pattern == MV_TURNING_RIGHT ||
1970 move_pattern == MV_TURNING_LEFT_RIGHT ||
1971 move_pattern == MV_TURNING_RIGHT_LEFT ||
1972 move_pattern == MV_TURNING_RANDOM)
1973 MovDir[x][y] = 1 << RND(4);
1974 else if (move_pattern == MV_HORIZONTAL)
1975 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1976 else if (move_pattern == MV_VERTICAL)
1977 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1978 else if (move_pattern & MV_ANY_DIRECTION)
1979 MovDir[x][y] = element_info[element].move_pattern;
1980 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
1981 move_pattern == MV_ALONG_RIGHT_SIDE)
1983 for (i = 0; i < NUM_DIRECTIONS; i++)
1985 int x1 = x + xy[i][0];
1986 int y1 = y + xy[i][1];
1988 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1990 if (move_pattern == MV_ALONG_RIGHT_SIDE)
1991 MovDir[x][y] = direction[0][i];
1993 MovDir[x][y] = direction[1][i];
2002 MovDir[x][y] = 1 << RND(4);
2004 if (element != EL_BUG &&
2005 element != EL_SPACESHIP &&
2006 element != EL_BD_BUTTERFLY &&
2007 element != EL_BD_FIREFLY)
2010 for (i = 0; i < NUM_DIRECTIONS; i++)
2012 int x1 = x + xy[i][0];
2013 int y1 = y + xy[i][1];
2015 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2017 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2019 MovDir[x][y] = direction[0][i];
2022 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2023 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2025 MovDir[x][y] = direction[1][i];
2034 GfxDir[x][y] = MovDir[x][y];
2037 void InitAmoebaNr(int x, int y)
2040 int group_nr = AmoebeNachbarNr(x, y);
2044 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2046 if (AmoebaCnt[i] == 0)
2054 AmoebaNr[x][y] = group_nr;
2055 AmoebaCnt[group_nr]++;
2056 AmoebaCnt2[group_nr]++;
2062 boolean raise_level = FALSE;
2064 if (local_player->MovPos)
2068 if (tape.auto_play) /* tape might already be stopped here */
2069 tape.auto_play_level_solved = TRUE;
2071 if (tape.playing && tape.auto_play)
2072 tape.auto_play_level_solved = TRUE;
2075 local_player->LevelSolved = FALSE;
2077 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2081 if (!tape.playing && setup.sound_loops)
2082 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2083 SND_CTRL_PLAY_LOOP);
2085 while (TimeLeft > 0)
2087 if (!tape.playing && !setup.sound_loops)
2088 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2089 if (TimeLeft > 0 && !(TimeLeft % 10))
2090 RaiseScore(level.score[SC_TIME_BONUS]);
2091 if (TimeLeft > 100 && !(TimeLeft % 10))
2096 DrawGameValue_Time(TimeLeft);
2104 if (!tape.playing && setup.sound_loops)
2105 StopSound(SND_GAME_LEVELTIME_BONUS);
2107 else if (level.time == 0) /* level without time limit */
2109 if (!tape.playing && setup.sound_loops)
2110 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2111 SND_CTRL_PLAY_LOOP);
2113 while (TimePlayed < 999)
2115 if (!tape.playing && !setup.sound_loops)
2116 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2117 if (TimePlayed < 999 && !(TimePlayed % 10))
2118 RaiseScore(level.score[SC_TIME_BONUS]);
2119 if (TimePlayed < 900 && !(TimePlayed % 10))
2124 DrawGameValue_Time(TimePlayed);
2132 if (!tape.playing && setup.sound_loops)
2133 StopSound(SND_GAME_LEVELTIME_BONUS);
2136 /* close exit door after last player */
2137 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2138 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2140 int element = Feld[ExitX][ExitY];
2142 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2143 EL_SP_EXIT_CLOSING);
2145 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2148 /* Hero disappears */
2149 DrawLevelField(ExitX, ExitY);
2155 CloseDoor(DOOR_CLOSE_1);
2160 SaveTape(tape.level_nr); /* Ask to save tape */
2163 if (level_nr == leveldir_current->handicap_level)
2165 leveldir_current->handicap_level++;
2166 SaveLevelSetup_SeriesInfo();
2169 if (level_editor_test_game)
2170 local_player->score = -1; /* no highscore when playing from editor */
2171 else if (level_nr < leveldir_current->last_level)
2172 raise_level = TRUE; /* advance to next level */
2174 if ((hi_pos = NewHiScore()) >= 0)
2176 game_status = GAME_MODE_SCORES;
2177 DrawHallOfFame(hi_pos);
2186 game_status = GAME_MODE_MAIN;
2203 LoadScore(level_nr);
2205 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2206 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2209 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2211 if (local_player->score > highscore[k].Score)
2213 /* player has made it to the hall of fame */
2215 if (k < MAX_SCORE_ENTRIES - 1)
2217 int m = MAX_SCORE_ENTRIES - 1;
2220 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2221 if (!strcmp(setup.player_name, highscore[l].Name))
2223 if (m == k) /* player's new highscore overwrites his old one */
2227 for (l = m; l > k; l--)
2229 strcpy(highscore[l].Name, highscore[l - 1].Name);
2230 highscore[l].Score = highscore[l - 1].Score;
2237 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2238 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2239 highscore[k].Score = local_player->score;
2245 else if (!strncmp(setup.player_name, highscore[k].Name,
2246 MAX_PLAYER_NAME_LEN))
2247 break; /* player already there with a higher score */
2253 SaveScore(level_nr);
2258 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2260 if (player->GfxAction != action || player->GfxDir != dir)
2263 printf("Player frame reset! (%d => %d, %d => %d)\n",
2264 player->GfxAction, action, player->GfxDir, dir);
2267 player->GfxAction = action;
2268 player->GfxDir = dir;
2270 player->StepFrame = 0;
2274 static void ResetRandomAnimationValue(int x, int y)
2276 GfxRandom[x][y] = INIT_GFX_RANDOM();
2279 static void ResetGfxAnimation(int x, int y)
2282 GfxAction[x][y] = ACTION_DEFAULT;
2283 GfxDir[x][y] = MovDir[x][y];
2286 void InitMovingField(int x, int y, int direction)
2288 int element = Feld[x][y];
2289 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2290 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2294 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2295 ResetGfxAnimation(x, y);
2297 MovDir[newx][newy] = MovDir[x][y] = direction;
2298 GfxDir[x][y] = direction;
2300 if (Feld[newx][newy] == EL_EMPTY)
2301 Feld[newx][newy] = EL_BLOCKED;
2303 if (direction == MV_DOWN && CAN_FALL(element))
2304 GfxAction[x][y] = ACTION_FALLING;
2306 GfxAction[x][y] = ACTION_MOVING;
2308 GfxFrame[newx][newy] = GfxFrame[x][y];
2309 GfxRandom[newx][newy] = GfxRandom[x][y];
2310 GfxAction[newx][newy] = GfxAction[x][y];
2311 GfxDir[newx][newy] = GfxDir[x][y];
2314 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2316 int direction = MovDir[x][y];
2317 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2318 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2324 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2326 int oldx = x, oldy = y;
2327 int direction = MovDir[x][y];
2329 if (direction == MV_LEFT)
2331 else if (direction == MV_RIGHT)
2333 else if (direction == MV_UP)
2335 else if (direction == MV_DOWN)
2338 *comes_from_x = oldx;
2339 *comes_from_y = oldy;
2342 int MovingOrBlocked2Element(int x, int y)
2344 int element = Feld[x][y];
2346 if (element == EL_BLOCKED)
2350 Blocked2Moving(x, y, &oldx, &oldy);
2351 return Feld[oldx][oldy];
2357 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2359 /* like MovingOrBlocked2Element(), but if element is moving
2360 and (x,y) is the field the moving element is just leaving,
2361 return EL_BLOCKED instead of the element value */
2362 int element = Feld[x][y];
2364 if (IS_MOVING(x, y))
2366 if (element == EL_BLOCKED)
2370 Blocked2Moving(x, y, &oldx, &oldy);
2371 return Feld[oldx][oldy];
2380 static void RemoveField(int x, int y)
2382 Feld[x][y] = EL_EMPTY;
2389 ChangeDelay[x][y] = 0;
2390 ChangePage[x][y] = -1;
2391 Pushed[x][y] = FALSE;
2393 GfxElement[x][y] = EL_UNDEFINED;
2394 GfxAction[x][y] = ACTION_DEFAULT;
2395 GfxDir[x][y] = MV_NO_MOVING;
2398 void RemoveMovingField(int x, int y)
2400 int oldx = x, oldy = y, newx = x, newy = y;
2401 int element = Feld[x][y];
2402 int next_element = EL_UNDEFINED;
2404 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2407 if (IS_MOVING(x, y))
2409 Moving2Blocked(x, y, &newx, &newy);
2411 if (Feld[newx][newy] != EL_BLOCKED)
2414 if (Feld[newx][newy] != EL_BLOCKED)
2416 /* element is moving, but target field is not free (blocked), but
2417 already occupied by something different (example: acid pool);
2418 in this case, only remove the moving field, but not the target */
2420 RemoveField(oldx, oldy);
2422 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2424 DrawLevelField(oldx, oldy);
2430 else if (element == EL_BLOCKED)
2432 Blocked2Moving(x, y, &oldx, &oldy);
2433 if (!IS_MOVING(oldx, oldy))
2437 if (element == EL_BLOCKED &&
2438 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2439 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2440 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2441 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2442 next_element = get_next_element(Feld[oldx][oldy]);
2444 RemoveField(oldx, oldy);
2445 RemoveField(newx, newy);
2447 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2449 if (next_element != EL_UNDEFINED)
2450 Feld[oldx][oldy] = next_element;
2452 DrawLevelField(oldx, oldy);
2453 DrawLevelField(newx, newy);
2456 void DrawDynamite(int x, int y)
2458 int sx = SCREENX(x), sy = SCREENY(y);
2459 int graphic = el2img(Feld[x][y]);
2462 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2465 if (IS_WALKABLE_INSIDE(Back[x][y]))
2469 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2470 else if (Store[x][y])
2471 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2473 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2476 if (Back[x][y] || Store[x][y])
2477 DrawGraphicThruMask(sx, sy, graphic, frame);
2479 DrawGraphic(sx, sy, graphic, frame);
2481 if (game.emulation == EMU_SUPAPLEX)
2482 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2483 else if (Store[x][y])
2484 DrawGraphicThruMask(sx, sy, graphic, frame);
2486 DrawGraphic(sx, sy, graphic, frame);
2490 void CheckDynamite(int x, int y)
2492 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2496 if (MovDelay[x][y] != 0)
2499 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2506 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2508 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2509 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2510 StopSound(SND_DYNAMITE_ACTIVE);
2512 StopSound(SND_DYNABOMB_ACTIVE);
2518 void RelocatePlayer(int x, int y, int element_raw)
2520 int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
2521 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2522 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2523 boolean no_delay = (tape.index_search);
2524 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2525 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2527 if (player->GameOver) /* do not reanimate dead player */
2531 RemoveField(x, y); /* temporarily remove newly placed player */
2532 DrawLevelField(x, y);
2535 if (player->present)
2537 while (player->MovPos)
2539 ScrollPlayer(player, SCROLL_GO_ON);
2540 ScrollScreen(NULL, SCROLL_GO_ON);
2546 Delay(wait_delay_value);
2549 DrawPlayer(player); /* needed here only to cleanup last field */
2550 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2552 player->is_moving = FALSE;
2555 Feld[x][y] = element;
2556 InitPlayerField(x, y, element, TRUE);
2558 if (player == local_player)
2560 int scroll_xx = -999, scroll_yy = -999;
2562 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2565 int fx = FX, fy = FY;
2567 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2568 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2569 local_player->jx - MIDPOSX);
2571 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2572 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2573 local_player->jy - MIDPOSY);
2575 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2576 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2581 fx += dx * TILEX / 2;
2582 fy += dy * TILEY / 2;
2584 ScrollLevel(dx, dy);
2587 /* scroll in two steps of half tile size to make things smoother */
2588 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2590 Delay(wait_delay_value);
2592 /* scroll second step to align at full tile size */
2594 Delay(wait_delay_value);
2599 void Explode(int ex, int ey, int phase, int mode)
2606 /* !!! eliminate this variable !!! */
2607 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2612 int last_phase = num_phase * delay;
2613 int half_phase = (num_phase / 2) * delay;
2614 int first_phase_after_start = EX_PHASE_START + 1;
2618 if (game.explosions_delayed)
2620 ExplodeField[ex][ey] = mode;
2624 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2626 int center_element = Feld[ex][ey];
2629 /* --- This is only really needed (and now handled) in "Impact()". --- */
2630 /* do not explode moving elements that left the explode field in time */
2631 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2632 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2636 if (mode == EX_NORMAL || mode == EX_CENTER)
2637 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2639 /* remove things displayed in background while burning dynamite */
2640 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2643 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2645 /* put moving element to center field (and let it explode there) */
2646 center_element = MovingOrBlocked2Element(ex, ey);
2647 RemoveMovingField(ex, ey);
2648 Feld[ex][ey] = center_element;
2652 last_phase = element_info[center_element].explosion_delay;
2655 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2657 int xx = x - ex + 1;
2658 int yy = y - ey + 1;
2662 if (!IN_LEV_FIELD(x, y) || (mode != EX_NORMAL && (x != ex || y != ey)))
2665 if (!IN_LEV_FIELD(x, y) ||
2666 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2667 (x != ex || y != ey)))
2671 element = Feld[x][y];
2673 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2675 element = MovingOrBlocked2Element(x, y);
2677 if (!IS_EXPLOSION_PROOF(element))
2678 RemoveMovingField(x, y);
2684 if (IS_EXPLOSION_PROOF(element))
2687 /* indestructible elements can only explode in center (but not flames) */
2688 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2689 element == EL_FLAMES)
2694 if ((IS_INDESTRUCTIBLE(element) &&
2695 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2696 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2697 element == EL_FLAMES)
2701 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2703 if (IS_ACTIVE_BOMB(element))
2705 /* re-activate things under the bomb like gate or penguin */
2706 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2713 /* save walkable background elements while explosion on same tile */
2715 if (IS_INDESTRUCTIBLE(element))
2716 Back[x][y] = element;
2718 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2719 Back[x][y] = element;
2722 /* ignite explodable elements reached by other explosion */
2723 if (element == EL_EXPLOSION)
2724 element = Store2[x][y];
2727 if (AmoebaNr[x][y] &&
2728 (element == EL_AMOEBA_FULL ||
2729 element == EL_BD_AMOEBA ||
2730 element == EL_AMOEBA_GROWING))
2732 AmoebaCnt[AmoebaNr[x][y]]--;
2733 AmoebaCnt2[AmoebaNr[x][y]]--;
2739 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2741 switch(StorePlayer[ex][ey])
2744 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2747 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2750 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2754 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2758 if (game.emulation == EMU_SUPAPLEX)
2759 Store[x][y] = EL_EMPTY;
2761 else if (center_element == EL_MOLE)
2762 Store[x][y] = EL_EMERALD_RED;
2763 else if (center_element == EL_PENGUIN)
2764 Store[x][y] = EL_EMERALD_PURPLE;
2765 else if (center_element == EL_BUG)
2766 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2767 else if (center_element == EL_BD_BUTTERFLY)
2768 Store[x][y] = EL_BD_DIAMOND;
2769 else if (center_element == EL_SP_ELECTRON)
2770 Store[x][y] = EL_SP_INFOTRON;
2771 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2772 Store[x][y] = level.amoeba_content;
2773 else if (center_element == EL_YAMYAM)
2774 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2775 else if (IS_CUSTOM_ELEMENT(center_element) &&
2776 element_info[center_element].content[xx][yy] != EL_EMPTY)
2777 Store[x][y] = element_info[center_element].content[xx][yy];
2778 else if (element == EL_WALL_EMERALD)
2779 Store[x][y] = EL_EMERALD;
2780 else if (element == EL_WALL_DIAMOND)
2781 Store[x][y] = EL_DIAMOND;
2782 else if (element == EL_WALL_BD_DIAMOND)
2783 Store[x][y] = EL_BD_DIAMOND;
2784 else if (element == EL_WALL_EMERALD_YELLOW)
2785 Store[x][y] = EL_EMERALD_YELLOW;
2786 else if (element == EL_WALL_EMERALD_RED)
2787 Store[x][y] = EL_EMERALD_RED;
2788 else if (element == EL_WALL_EMERALD_PURPLE)
2789 Store[x][y] = EL_EMERALD_PURPLE;
2790 else if (element == EL_WALL_PEARL)
2791 Store[x][y] = EL_PEARL;
2792 else if (element == EL_WALL_CRYSTAL)
2793 Store[x][y] = EL_CRYSTAL;
2794 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2795 Store[x][y] = element_info[element].content[1][1];
2797 Store[x][y] = EL_EMPTY;
2799 if (x != ex || y != ey ||
2800 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2801 Store2[x][y] = element;
2804 if (AmoebaNr[x][y] &&
2805 (element == EL_AMOEBA_FULL ||
2806 element == EL_BD_AMOEBA ||
2807 element == EL_AMOEBA_GROWING))
2809 AmoebaCnt[AmoebaNr[x][y]]--;
2810 AmoebaCnt2[AmoebaNr[x][y]]--;
2816 MovDir[x][y] = MovPos[x][y] = 0;
2817 GfxDir[x][y] = MovDir[x][y];
2822 Feld[x][y] = EL_EXPLOSION;
2824 GfxElement[x][y] = center_element;
2826 GfxElement[x][y] = EL_UNDEFINED;
2829 ExplodePhase[x][y] = 1;
2831 ExplodeDelay[x][y] = last_phase;
2836 if (center_element == EL_YAMYAM)
2837 game.yamyam_content_nr =
2838 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2850 last_phase = ExplodeDelay[x][y];
2853 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2857 /* activate this even in non-DEBUG version until cause for crash in
2858 getGraphicAnimationFrame() (see below) is found and eliminated */
2862 if (GfxElement[x][y] == EL_UNDEFINED)
2865 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2866 printf("Explode(): This should never happen!\n");
2869 GfxElement[x][y] = EL_EMPTY;
2875 border_element = Store2[x][y];
2876 if (IS_PLAYER(x, y))
2877 border_element = StorePlayer[x][y];
2879 if (phase == element_info[border_element].ignition_delay ||
2880 phase == last_phase)
2882 boolean border_explosion = FALSE;
2885 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
2887 if (IS_PLAYER(x, y))
2890 KillHeroUnlessExplosionProtected(x, y);
2891 border_explosion = TRUE;
2894 if (phase == last_phase)
2895 printf("::: IS_PLAYER\n");
2898 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
2900 Feld[x][y] = Store2[x][y];
2903 border_explosion = TRUE;
2906 if (phase == last_phase)
2907 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
2910 else if (border_element == EL_AMOEBA_TO_DIAMOND)
2912 AmoebeUmwandeln(x, y);
2914 border_explosion = TRUE;
2917 if (phase == last_phase)
2918 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
2919 element_info[border_element].explosion_delay,
2920 element_info[border_element].ignition_delay,
2926 /* if an element just explodes due to another explosion (chain-reaction),
2927 do not immediately end the new explosion when it was the last frame of
2928 the explosion (as it would be done in the following "if"-statement!) */
2929 if (border_explosion && phase == last_phase)
2936 if (phase == first_phase_after_start)
2938 int element = Store2[x][y];
2940 if (element == EL_BLACK_ORB)
2942 Feld[x][y] = Store2[x][y];
2947 else if (phase == half_phase)
2949 int element = Store2[x][y];
2951 if (IS_PLAYER(x, y))
2952 KillHeroUnlessExplosionProtected(x, y);
2953 else if (CAN_EXPLODE_BY_EXPLOSION(element))
2955 Feld[x][y] = Store2[x][y];
2959 else if (element == EL_AMOEBA_TO_DIAMOND)
2960 AmoebeUmwandeln(x, y);
2964 if (phase == last_phase)
2968 element = Feld[x][y] = Store[x][y];
2969 Store[x][y] = Store2[x][y] = 0;
2970 GfxElement[x][y] = EL_UNDEFINED;
2972 /* player can escape from explosions and might therefore be still alive */
2973 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2974 element <= EL_PLAYER_IS_EXPLODING_4)
2975 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2977 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2978 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2979 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2982 /* restore probably existing indestructible background element */
2983 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2984 element = Feld[x][y] = Back[x][y];
2987 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2988 GfxDir[x][y] = MV_NO_MOVING;
2989 ChangeDelay[x][y] = 0;
2990 ChangePage[x][y] = -1;
2993 InitField_WithBug2(x, y, FALSE);
2995 InitField(x, y, FALSE);
2997 /* !!! not needed !!! */
2999 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
3000 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3003 if (CAN_MOVE(element))
3008 DrawLevelField(x, y);
3010 TestIfElementTouchesCustomElement(x, y);
3012 if (GFX_CRUMBLED(element))
3013 DrawLevelFieldCrumbledSandNeighbours(x, y);
3015 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3016 StorePlayer[x][y] = 0;
3018 if (ELEM_IS_PLAYER(element))
3019 RelocatePlayer(x, y, element);
3022 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3024 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3028 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3030 int stored = Store[x][y];
3031 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3032 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3035 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3038 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3039 element_info[GfxElement[x][y]].token_name,
3044 DrawLevelFieldCrumbledSand(x, y);
3046 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3048 DrawLevelElement(x, y, Back[x][y]);
3049 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3051 else if (IS_WALKABLE_UNDER(Back[x][y]))
3053 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3054 DrawLevelElementThruMask(x, y, Back[x][y]);
3056 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3057 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3061 void DynaExplode(int ex, int ey)
3064 int dynabomb_element = Feld[ex][ey];
3065 int dynabomb_size = 1;
3066 boolean dynabomb_xl = FALSE;
3067 struct PlayerInfo *player;
3068 static int xy[4][2] =
3076 if (IS_ACTIVE_BOMB(dynabomb_element))
3078 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3079 dynabomb_size = player->dynabomb_size;
3080 dynabomb_xl = player->dynabomb_xl;
3081 player->dynabombs_left++;
3084 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
3086 for (i = 0; i < NUM_DIRECTIONS; i++)
3088 for (j = 1; j <= dynabomb_size; j++)
3090 int x = ex + j * xy[i][0];
3091 int y = ey + j * xy[i][1];
3094 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3097 element = Feld[x][y];
3099 /* do not restart explosions of fields with active bombs */
3100 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3103 Explode(x, y, EX_PHASE_START, EX_BORDER);
3105 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3106 if (element != EL_EMPTY &&
3107 element != EL_SAND &&
3108 element != EL_EXPLOSION &&
3115 void Bang(int x, int y)
3118 int element = MovingOrBlocked2Element(x, y);
3120 int element = Feld[x][y];
3124 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3126 if (IS_PLAYER(x, y))
3129 struct PlayerInfo *player = PLAYERINFO(x, y);
3131 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3132 player->element_nr);
3137 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3139 if (game.emulation == EMU_SUPAPLEX)
3140 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3142 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3147 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3155 case EL_BD_BUTTERFLY:
3158 case EL_DARK_YAMYAM:
3162 RaiseScoreElement(element);
3163 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3165 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3166 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3167 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3168 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3169 case EL_DYNABOMB_INCREASE_NUMBER:
3170 case EL_DYNABOMB_INCREASE_SIZE:
3171 case EL_DYNABOMB_INCREASE_POWER:
3176 case EL_LAMP_ACTIVE:
3178 case EL_AMOEBA_TO_DIAMOND:
3180 if (IS_PLAYER(x, y))
3181 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3183 Explode(x, y, EX_PHASE_START, EX_CENTER);
3186 if (CAN_EXPLODE_DYNA(element))
3188 else if (CAN_EXPLODE_1X1(element))
3189 Explode(x, y, EX_PHASE_START, EX_CENTER);
3191 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3195 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3198 void SplashAcid(int x, int y)
3201 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3202 (!IN_LEV_FIELD(x - 1, y - 2) ||
3203 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3204 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3206 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3207 (!IN_LEV_FIELD(x + 1, y - 2) ||
3208 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3209 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3211 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3213 /* input: position of element entering acid (obsolete) */
3215 int element = Feld[x][y];
3217 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3220 if (element != EL_ACID_SPLASH_LEFT &&
3221 element != EL_ACID_SPLASH_RIGHT)
3223 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3225 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3226 (!IN_LEV_FIELD(x - 1, y - 1) ||
3227 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3228 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3230 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3231 (!IN_LEV_FIELD(x + 1, y - 1) ||
3232 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3233 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3238 static void InitBeltMovement()
3240 static int belt_base_element[4] =
3242 EL_CONVEYOR_BELT_1_LEFT,
3243 EL_CONVEYOR_BELT_2_LEFT,
3244 EL_CONVEYOR_BELT_3_LEFT,
3245 EL_CONVEYOR_BELT_4_LEFT
3247 static int belt_base_active_element[4] =
3249 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3250 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3251 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3252 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3257 /* set frame order for belt animation graphic according to belt direction */
3258 for (i = 0; i < NUM_BELTS; i++)
3262 for (j = 0; j < NUM_BELT_PARTS; j++)
3264 int element = belt_base_active_element[belt_nr] + j;
3265 int graphic = el2img(element);
3267 if (game.belt_dir[i] == MV_LEFT)
3268 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3270 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3274 for (y = 0; y < lev_fieldy; y++)
3276 for (x = 0; x < lev_fieldx; x++)
3278 int element = Feld[x][y];
3280 for (i = 0; i < NUM_BELTS; i++)
3282 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3284 int e_belt_nr = getBeltNrFromBeltElement(element);
3287 if (e_belt_nr == belt_nr)
3289 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3291 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3299 static void ToggleBeltSwitch(int x, int y)
3301 static int belt_base_element[4] =
3303 EL_CONVEYOR_BELT_1_LEFT,
3304 EL_CONVEYOR_BELT_2_LEFT,
3305 EL_CONVEYOR_BELT_3_LEFT,
3306 EL_CONVEYOR_BELT_4_LEFT
3308 static int belt_base_active_element[4] =
3310 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3311 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3312 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3313 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3315 static int belt_base_switch_element[4] =
3317 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3318 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3319 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3320 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3322 static int belt_move_dir[4] =
3330 int element = Feld[x][y];
3331 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3332 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3333 int belt_dir = belt_move_dir[belt_dir_nr];
3336 if (!IS_BELT_SWITCH(element))
3339 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3340 game.belt_dir[belt_nr] = belt_dir;
3342 if (belt_dir_nr == 3)
3345 /* set frame order for belt animation graphic according to belt direction */
3346 for (i = 0; i < NUM_BELT_PARTS; i++)
3348 int element = belt_base_active_element[belt_nr] + i;
3349 int graphic = el2img(element);
3351 if (belt_dir == MV_LEFT)
3352 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3354 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3357 for (yy = 0; yy < lev_fieldy; yy++)
3359 for (xx = 0; xx < lev_fieldx; xx++)
3361 int element = Feld[xx][yy];
3363 if (IS_BELT_SWITCH(element))
3365 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3367 if (e_belt_nr == belt_nr)
3369 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3370 DrawLevelField(xx, yy);
3373 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3375 int e_belt_nr = getBeltNrFromBeltElement(element);
3377 if (e_belt_nr == belt_nr)
3379 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3381 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3382 DrawLevelField(xx, yy);
3385 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3387 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3389 if (e_belt_nr == belt_nr)
3391 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3393 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3394 DrawLevelField(xx, yy);
3401 static void ToggleSwitchgateSwitch(int x, int y)
3405 game.switchgate_pos = !game.switchgate_pos;
3407 for (yy = 0; yy < lev_fieldy; yy++)
3409 for (xx = 0; xx < lev_fieldx; xx++)
3411 int element = Feld[xx][yy];
3413 if (element == EL_SWITCHGATE_SWITCH_UP ||
3414 element == EL_SWITCHGATE_SWITCH_DOWN)
3416 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3417 DrawLevelField(xx, yy);
3419 else if (element == EL_SWITCHGATE_OPEN ||
3420 element == EL_SWITCHGATE_OPENING)
3422 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3424 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3426 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3429 else if (element == EL_SWITCHGATE_CLOSED ||
3430 element == EL_SWITCHGATE_CLOSING)
3432 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3434 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3436 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3443 static int getInvisibleActiveFromInvisibleElement(int element)
3445 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3446 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3447 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3451 static int getInvisibleFromInvisibleActiveElement(int element)
3453 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3454 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3455 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3459 static void RedrawAllLightSwitchesAndInvisibleElements()
3463 for (y = 0; y < lev_fieldy; y++)
3465 for (x = 0; x < lev_fieldx; x++)
3467 int element = Feld[x][y];
3469 if (element == EL_LIGHT_SWITCH &&
3470 game.light_time_left > 0)
3472 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3473 DrawLevelField(x, y);
3475 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3476 game.light_time_left == 0)
3478 Feld[x][y] = EL_LIGHT_SWITCH;
3479 DrawLevelField(x, y);
3481 else if (element == EL_INVISIBLE_STEELWALL ||
3482 element == EL_INVISIBLE_WALL ||
3483 element == EL_INVISIBLE_SAND)
3485 if (game.light_time_left > 0)
3486 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3488 DrawLevelField(x, y);
3490 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3491 element == EL_INVISIBLE_WALL_ACTIVE ||
3492 element == EL_INVISIBLE_SAND_ACTIVE)
3494 if (game.light_time_left == 0)
3495 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3497 DrawLevelField(x, y);
3503 static void ToggleLightSwitch(int x, int y)
3505 int element = Feld[x][y];
3507 game.light_time_left =
3508 (element == EL_LIGHT_SWITCH ?
3509 level.time_light * FRAMES_PER_SECOND : 0);
3511 RedrawAllLightSwitchesAndInvisibleElements();
3514 static void ActivateTimegateSwitch(int x, int y)
3518 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3520 for (yy = 0; yy < lev_fieldy; yy++)
3522 for (xx = 0; xx < lev_fieldx; xx++)
3524 int element = Feld[xx][yy];
3526 if (element == EL_TIMEGATE_CLOSED ||
3527 element == EL_TIMEGATE_CLOSING)
3529 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3530 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3534 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3536 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3537 DrawLevelField(xx, yy);
3544 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3547 inline static int getElementMoveStepsize(int x, int y)
3549 int element = Feld[x][y];
3550 int direction = MovDir[x][y];
3551 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3552 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3553 int horiz_move = (dx != 0);
3554 int sign = (horiz_move ? dx : dy);
3555 int step = sign * element_info[element].move_stepsize;
3557 /* special values for move stepsize for spring and things on conveyor belt */
3561 if (element == EL_SPRING)
3562 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3563 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3564 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3565 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3567 if (CAN_FALL(element) &&
3568 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3569 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3570 else if (element == EL_SPRING)
3571 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3578 void Impact(int x, int y)
3580 boolean lastline = (y == lev_fieldy-1);
3581 boolean object_hit = FALSE;
3582 boolean impact = (lastline || object_hit);
3583 int element = Feld[x][y];
3584 int smashed = EL_UNDEFINED;
3586 if (!lastline) /* check if element below was hit */
3588 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3591 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3592 MovDir[x][y + 1] != MV_DOWN ||
3593 MovPos[x][y + 1] <= TILEY / 2));
3596 object_hit = !IS_FREE(x, y + 1);
3599 /* do not smash moving elements that left the smashed field in time */
3600 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3601 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3605 smashed = MovingOrBlocked2Element(x, y + 1);
3607 impact = (lastline || object_hit);
3610 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3612 SplashAcid(x, y + 1);
3616 /* only reset graphic animation if graphic really changes after impact */
3618 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3620 ResetGfxAnimation(x, y);
3621 DrawLevelField(x, y);
3624 if (impact && CAN_EXPLODE_IMPACT(element))
3629 else if (impact && element == EL_PEARL)
3631 Feld[x][y] = EL_PEARL_BREAKING;
3632 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3635 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3637 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3642 if (impact && element == EL_AMOEBA_DROP)
3644 if (object_hit && IS_PLAYER(x, y + 1))
3645 KillHeroUnlessEnemyProtected(x, y + 1);
3646 else if (object_hit && smashed == EL_PENGUIN)
3650 Feld[x][y] = EL_AMOEBA_GROWING;
3651 Store[x][y] = EL_AMOEBA_WET;
3653 ResetRandomAnimationValue(x, y);
3658 if (object_hit) /* check which object was hit */
3660 if (CAN_PASS_MAGIC_WALL(element) &&
3661 (smashed == EL_MAGIC_WALL ||
3662 smashed == EL_BD_MAGIC_WALL))
3665 int activated_magic_wall =
3666 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3667 EL_BD_MAGIC_WALL_ACTIVE);
3669 /* activate magic wall / mill */
3670 for (yy = 0; yy < lev_fieldy; yy++)
3671 for (xx = 0; xx < lev_fieldx; xx++)
3672 if (Feld[xx][yy] == smashed)
3673 Feld[xx][yy] = activated_magic_wall;
3675 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3676 game.magic_wall_active = TRUE;
3678 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3679 SND_MAGIC_WALL_ACTIVATING :
3680 SND_BD_MAGIC_WALL_ACTIVATING));
3683 if (IS_PLAYER(x, y + 1))
3685 if (CAN_SMASH_PLAYER(element))
3687 KillHeroUnlessEnemyProtected(x, y + 1);
3691 else if (smashed == EL_PENGUIN)
3693 if (CAN_SMASH_PLAYER(element))
3699 else if (element == EL_BD_DIAMOND)
3701 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3707 else if (((element == EL_SP_INFOTRON ||
3708 element == EL_SP_ZONK) &&
3709 (smashed == EL_SP_SNIKSNAK ||
3710 smashed == EL_SP_ELECTRON ||
3711 smashed == EL_SP_DISK_ORANGE)) ||
3712 (element == EL_SP_INFOTRON &&
3713 smashed == EL_SP_DISK_YELLOW))
3719 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3725 else if (CAN_SMASH_EVERYTHING(element))
3727 if (IS_CLASSIC_ENEMY(smashed) ||
3728 CAN_EXPLODE_SMASHED(smashed))
3733 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3735 if (smashed == EL_LAMP ||
3736 smashed == EL_LAMP_ACTIVE)
3741 else if (smashed == EL_NUT)
3743 Feld[x][y + 1] = EL_NUT_BREAKING;
3744 PlayLevelSound(x, y, SND_NUT_BREAKING);
3745 RaiseScoreElement(EL_NUT);
3748 else if (smashed == EL_PEARL)
3750 Feld[x][y + 1] = EL_PEARL_BREAKING;
3751 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3754 else if (smashed == EL_DIAMOND)
3756 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3757 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3760 else if (IS_BELT_SWITCH(smashed))
3762 ToggleBeltSwitch(x, y + 1);
3764 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3765 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3767 ToggleSwitchgateSwitch(x, y + 1);
3769 else if (smashed == EL_LIGHT_SWITCH ||
3770 smashed == EL_LIGHT_SWITCH_ACTIVE)
3772 ToggleLightSwitch(x, y + 1);
3776 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3778 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3779 CE_OTHER_IS_SWITCHING);
3780 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3786 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3791 /* play sound of magic wall / mill */
3793 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3794 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3796 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3797 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3798 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3799 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3804 /* play sound of object that hits the ground */
3805 if (lastline || object_hit)
3806 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3809 inline static void TurnRoundExt(int x, int y)
3821 { 0, 0 }, { 0, 0 }, { 0, 0 },
3826 int left, right, back;
3830 { MV_DOWN, MV_UP, MV_RIGHT },
3831 { MV_UP, MV_DOWN, MV_LEFT },
3833 { MV_LEFT, MV_RIGHT, MV_DOWN },
3837 { MV_RIGHT, MV_LEFT, MV_UP }
3840 int element = Feld[x][y];
3841 int move_pattern = element_info[element].move_pattern;
3843 int old_move_dir = MovDir[x][y];
3844 int left_dir = turn[old_move_dir].left;
3845 int right_dir = turn[old_move_dir].right;
3846 int back_dir = turn[old_move_dir].back;
3848 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3849 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3850 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3851 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3853 int left_x = x + left_dx, left_y = y + left_dy;
3854 int right_x = x + right_dx, right_y = y + right_dy;
3855 int move_x = x + move_dx, move_y = y + move_dy;
3859 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3861 TestIfBadThingTouchesOtherBadThing(x, y);
3863 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
3864 MovDir[x][y] = right_dir;
3865 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3866 MovDir[x][y] = left_dir;
3868 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3870 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3873 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3874 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3876 TestIfBadThingTouchesOtherBadThing(x, y);
3878 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
3879 MovDir[x][y] = left_dir;
3880 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3881 MovDir[x][y] = right_dir;
3883 if ((element == EL_SPACESHIP ||
3884 element == EL_SP_SNIKSNAK ||
3885 element == EL_SP_ELECTRON)
3886 && MovDir[x][y] != old_move_dir)
3888 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3891 else if (element == EL_YAMYAM)
3893 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3894 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3896 if (can_turn_left && can_turn_right)
3897 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3898 else if (can_turn_left)
3899 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3900 else if (can_turn_right)
3901 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3903 MovDir[x][y] = back_dir;
3905 MovDelay[x][y] = 16 + 16 * RND(3);
3907 else if (element == EL_DARK_YAMYAM)
3909 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3910 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3912 if (can_turn_left && can_turn_right)
3913 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3914 else if (can_turn_left)
3915 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3916 else if (can_turn_right)
3917 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3919 MovDir[x][y] = back_dir;
3921 MovDelay[x][y] = 16 + 16 * RND(3);
3923 else if (element == EL_PACMAN)
3925 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3926 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3928 if (can_turn_left && can_turn_right)
3929 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3930 else if (can_turn_left)
3931 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3932 else if (can_turn_right)
3933 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3935 MovDir[x][y] = back_dir;
3937 MovDelay[x][y] = 6 + RND(40);
3939 else if (element == EL_PIG)
3941 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3942 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3943 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3944 boolean should_turn_left, should_turn_right, should_move_on;
3946 int rnd = RND(rnd_value);
3948 should_turn_left = (can_turn_left &&
3950 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3951 y + back_dy + left_dy)));
3952 should_turn_right = (can_turn_right &&
3954 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3955 y + back_dy + right_dy)));
3956 should_move_on = (can_move_on &&
3959 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3960 y + move_dy + left_dy) ||
3961 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3962 y + move_dy + right_dy)));
3964 if (should_turn_left || should_turn_right || should_move_on)
3966 if (should_turn_left && should_turn_right && should_move_on)
3967 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3968 rnd < 2 * rnd_value / 3 ? right_dir :
3970 else if (should_turn_left && should_turn_right)
3971 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3972 else if (should_turn_left && should_move_on)
3973 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3974 else if (should_turn_right && should_move_on)
3975 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3976 else if (should_turn_left)
3977 MovDir[x][y] = left_dir;
3978 else if (should_turn_right)
3979 MovDir[x][y] = right_dir;
3980 else if (should_move_on)
3981 MovDir[x][y] = old_move_dir;
3983 else if (can_move_on && rnd > rnd_value / 8)
3984 MovDir[x][y] = old_move_dir;
3985 else if (can_turn_left && can_turn_right)
3986 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3987 else if (can_turn_left && rnd > rnd_value / 8)
3988 MovDir[x][y] = left_dir;
3989 else if (can_turn_right && rnd > rnd_value/8)
3990 MovDir[x][y] = right_dir;
3992 MovDir[x][y] = back_dir;
3994 xx = x + move_xy[MovDir[x][y]].x;
3995 yy = y + move_xy[MovDir[x][y]].y;
3997 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3998 MovDir[x][y] = old_move_dir;
4002 else if (element == EL_DRAGON)
4004 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(left_x, left_y);
4005 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(right_x, right_y);
4006 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(move_x, move_y);
4008 int rnd = RND(rnd_value);
4011 if (FrameCounter < 1 && x == 0 && y == 29)
4012 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4015 if (can_move_on && rnd > rnd_value / 8)
4016 MovDir[x][y] = old_move_dir;
4017 else if (can_turn_left && can_turn_right)
4018 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4019 else if (can_turn_left && rnd > rnd_value / 8)
4020 MovDir[x][y] = left_dir;
4021 else if (can_turn_right && rnd > rnd_value / 8)
4022 MovDir[x][y] = right_dir;
4024 MovDir[x][y] = back_dir;
4026 xx = x + move_xy[MovDir[x][y]].x;
4027 yy = y + move_xy[MovDir[x][y]].y;
4030 if (FrameCounter < 1 && x == 0 && y == 29)
4031 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4032 xx, yy, Feld[xx][yy],
4037 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4038 MovDir[x][y] = old_move_dir;
4040 if (!IS_FREE(xx, yy))
4041 MovDir[x][y] = old_move_dir;
4045 if (FrameCounter < 1 && x == 0 && y == 29)
4046 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4051 else if (element == EL_MOLE)
4053 boolean can_move_on =
4054 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
4055 IS_AMOEBOID(Feld[move_x][move_y]) ||
4056 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4059 boolean can_turn_left =
4060 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
4061 IS_AMOEBOID(Feld[left_x][left_y])));
4063 boolean can_turn_right =
4064 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
4065 IS_AMOEBOID(Feld[right_x][right_y])));
4067 if (can_turn_left && can_turn_right)
4068 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4069 else if (can_turn_left)
4070 MovDir[x][y] = left_dir;
4072 MovDir[x][y] = right_dir;
4075 if (MovDir[x][y] != old_move_dir)
4078 else if (element == EL_BALLOON)
4080 MovDir[x][y] = game.balloon_dir;
4083 else if (element == EL_SPRING)
4086 if (MovDir[x][y] & MV_HORIZONTAL &&
4087 !SPRING_CAN_ENTER_FIELD(move_x, move_y))
4088 MovDir[x][y] = MV_NO_MOVING;
4090 if (MovDir[x][y] & MV_HORIZONTAL &&
4091 (!SPRING_CAN_ENTER_FIELD(move_x, move_y) ||
4092 SPRING_CAN_ENTER_FIELD(x, y + 1)))
4093 MovDir[x][y] = MV_NO_MOVING;
4098 else if (element == EL_ROBOT ||
4099 element == EL_SATELLITE ||
4100 element == EL_PENGUIN)
4102 int attr_x = -1, attr_y = -1;
4113 for (i = 0; i < MAX_PLAYERS; i++)
4115 struct PlayerInfo *player = &stored_player[i];
4116 int jx = player->jx, jy = player->jy;
4118 if (!player->active)
4122 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4130 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4136 if (element == EL_PENGUIN)
4139 static int xy[4][2] =
4147 for (i = 0; i < NUM_DIRECTIONS; i++)
4149 int ex = x + xy[i][0];
4150 int ey = y + xy[i][1];
4152 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4161 MovDir[x][y] = MV_NO_MOVING;
4163 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4164 else if (attr_x > x)
4165 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4167 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4168 else if (attr_y > y)
4169 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4171 if (element == EL_ROBOT)
4175 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4176 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4177 Moving2Blocked(x, y, &newx, &newy);
4179 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4180 MovDelay[x][y] = 8 + 8 * !RND(3);
4182 MovDelay[x][y] = 16;
4184 else if (element == EL_PENGUIN)
4190 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4192 boolean first_horiz = RND(2);
4193 int new_move_dir = MovDir[x][y];
4196 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4197 Moving2Blocked(x, y, &newx, &newy);
4199 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4203 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4204 Moving2Blocked(x, y, &newx, &newy);
4206 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4209 MovDir[x][y] = old_move_dir;
4213 else /* (element == EL_SATELLITE) */
4219 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4221 boolean first_horiz = RND(2);
4222 int new_move_dir = MovDir[x][y];
4225 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4226 Moving2Blocked(x, y, &newx, &newy);
4228 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4232 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4233 Moving2Blocked(x, y, &newx, &newy);
4235 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4238 MovDir[x][y] = old_move_dir;
4243 else if (move_pattern == MV_TURNING_LEFT ||
4244 move_pattern == MV_TURNING_RIGHT ||
4245 move_pattern == MV_TURNING_LEFT_RIGHT ||
4246 move_pattern == MV_TURNING_RIGHT_LEFT ||
4247 move_pattern == MV_TURNING_RANDOM ||
4248 move_pattern == MV_ALL_DIRECTIONS)
4250 boolean can_turn_left =
4251 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4252 boolean can_turn_right =
4253 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4255 if (move_pattern == MV_TURNING_LEFT)
4256 MovDir[x][y] = left_dir;
4257 else if (move_pattern == MV_TURNING_RIGHT)
4258 MovDir[x][y] = right_dir;
4259 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4260 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4261 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4262 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4263 else if (move_pattern == MV_TURNING_RANDOM)
4264 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4265 can_turn_right && !can_turn_left ? right_dir :
4266 RND(2) ? left_dir : right_dir);
4267 else if (can_turn_left && can_turn_right)
4268 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4269 else if (can_turn_left)
4270 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4271 else if (can_turn_right)
4272 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4274 MovDir[x][y] = back_dir;
4276 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4278 else if (move_pattern == MV_HORIZONTAL ||
4279 move_pattern == MV_VERTICAL)
4281 if (move_pattern & old_move_dir)
4282 MovDir[x][y] = back_dir;
4283 else if (move_pattern == MV_HORIZONTAL)
4284 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4285 else if (move_pattern == MV_VERTICAL)
4286 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4288 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4290 else if (move_pattern & MV_ANY_DIRECTION)
4292 MovDir[x][y] = move_pattern;
4293 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4295 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4297 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4298 MovDir[x][y] = left_dir;
4299 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4300 MovDir[x][y] = right_dir;
4302 if (MovDir[x][y] != old_move_dir)
4303 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4305 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4307 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4308 MovDir[x][y] = right_dir;
4309 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4310 MovDir[x][y] = left_dir;
4312 if (MovDir[x][y] != old_move_dir)
4313 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4315 else if (move_pattern == MV_TOWARDS_PLAYER ||
4316 move_pattern == MV_AWAY_FROM_PLAYER)
4318 int attr_x = -1, attr_y = -1;
4320 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4331 for (i = 0; i < MAX_PLAYERS; i++)
4333 struct PlayerInfo *player = &stored_player[i];
4334 int jx = player->jx, jy = player->jy;
4336 if (!player->active)
4340 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4348 MovDir[x][y] = MV_NO_MOVING;
4350 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4351 else if (attr_x > x)
4352 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4354 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4355 else if (attr_y > y)
4356 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4358 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4360 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4362 boolean first_horiz = RND(2);
4363 int new_move_dir = MovDir[x][y];
4366 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4367 Moving2Blocked(x, y, &newx, &newy);
4369 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4373 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4374 Moving2Blocked(x, y, &newx, &newy);
4376 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4379 MovDir[x][y] = old_move_dir;
4382 else if (move_pattern == MV_WHEN_PUSHED ||
4383 move_pattern == MV_WHEN_DROPPED)
4385 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4386 MovDir[x][y] = MV_NO_MOVING;
4390 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4392 static int test_xy[7][2] =
4402 static int test_dir[7] =
4412 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4413 int move_preference = -1000000; /* start with very low preference */
4414 int new_move_dir = MV_NO_MOVING;
4415 int start_test = RND(4);
4418 for (i = 0; i < NUM_DIRECTIONS; i++)
4420 int move_dir = test_dir[start_test + i];
4421 int move_dir_preference;
4423 xx = x + test_xy[start_test + i][0];
4424 yy = y + test_xy[start_test + i][1];
4426 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4427 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4429 new_move_dir = move_dir;
4434 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4437 move_dir_preference = -1 * RunnerVisit[xx][yy];
4438 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4439 move_dir_preference = PlayerVisit[xx][yy];
4441 if (move_dir_preference > move_preference)
4443 /* prefer field that has not been visited for the longest time */
4444 move_preference = move_dir_preference;
4445 new_move_dir = move_dir;
4447 else if (move_dir_preference == move_preference &&
4448 move_dir == old_move_dir)
4450 /* prefer last direction when all directions are preferred equally */
4451 move_preference = move_dir_preference;
4452 new_move_dir = move_dir;
4456 MovDir[x][y] = new_move_dir;
4457 if (old_move_dir != new_move_dir)
4462 static void TurnRound(int x, int y)
4464 int direction = MovDir[x][y];
4467 GfxDir[x][y] = MovDir[x][y];
4473 GfxDir[x][y] = MovDir[x][y];
4476 if (direction != MovDir[x][y])
4481 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4484 GfxAction[x][y] = ACTION_WAITING;
4488 static boolean JustBeingPushed(int x, int y)
4492 for (i = 0; i < MAX_PLAYERS; i++)
4494 struct PlayerInfo *player = &stored_player[i];
4496 if (player->active && player->is_pushing && player->MovPos)
4498 int next_jx = player->jx + (player->jx - player->last_jx);
4499 int next_jy = player->jy + (player->jy - player->last_jy);
4501 if (x == next_jx && y == next_jy)
4509 void StartMoving(int x, int y)
4512 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4514 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4515 int element = Feld[x][y];
4521 if (MovDelay[x][y] == 0)
4522 GfxAction[x][y] = ACTION_DEFAULT;
4524 /* !!! this should be handled more generic (not only for mole) !!! */
4525 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4526 GfxAction[x][y] = ACTION_DEFAULT;
4529 if (CAN_FALL(element) && y < lev_fieldy - 1)
4531 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4532 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4533 if (JustBeingPushed(x, y))
4536 if (element == EL_QUICKSAND_FULL)
4538 if (IS_FREE(x, y + 1))
4540 InitMovingField(x, y, MV_DOWN);
4541 started_moving = TRUE;
4543 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4544 Store[x][y] = EL_ROCK;
4546 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4548 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4551 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4553 if (!MovDelay[x][y])
4554 MovDelay[x][y] = TILEY + 1;
4563 Feld[x][y] = EL_QUICKSAND_EMPTY;
4564 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4565 Store[x][y + 1] = Store[x][y];
4568 PlayLevelSoundAction(x, y, ACTION_FILLING);
4570 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4574 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4575 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4577 InitMovingField(x, y, MV_DOWN);
4578 started_moving = TRUE;
4580 Feld[x][y] = EL_QUICKSAND_FILLING;
4581 Store[x][y] = element;
4583 PlayLevelSoundAction(x, y, ACTION_FILLING);
4585 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4588 else if (element == EL_MAGIC_WALL_FULL)
4590 if (IS_FREE(x, y + 1))
4592 InitMovingField(x, y, MV_DOWN);
4593 started_moving = TRUE;
4595 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4596 Store[x][y] = EL_CHANGED(Store[x][y]);
4598 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4600 if (!MovDelay[x][y])
4601 MovDelay[x][y] = TILEY/4 + 1;
4610 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4611 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4612 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4616 else if (element == EL_BD_MAGIC_WALL_FULL)
4618 if (IS_FREE(x, y + 1))
4620 InitMovingField(x, y, MV_DOWN);
4621 started_moving = TRUE;
4623 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4624 Store[x][y] = EL_CHANGED2(Store[x][y]);
4626 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4628 if (!MovDelay[x][y])
4629 MovDelay[x][y] = TILEY/4 + 1;
4638 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4639 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4640 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4644 else if (CAN_PASS_MAGIC_WALL(element) &&
4645 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4646 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4648 InitMovingField(x, y, MV_DOWN);
4649 started_moving = TRUE;
4652 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4653 EL_BD_MAGIC_WALL_FILLING);
4654 Store[x][y] = element;
4657 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4659 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4662 SplashAcid(x, y + 1);
4664 InitMovingField(x, y, MV_DOWN);
4665 started_moving = TRUE;
4667 Store[x][y] = EL_ACID;
4669 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4670 GfxAction[x][y + 1] = ACTION_ACTIVE;
4674 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4675 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4676 (Feld[x][y + 1] == EL_BLOCKED)) ||
4677 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4678 CAN_SMASH(element) && WasJustFalling[x][y] &&
4679 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4683 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4684 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4685 WasJustMoving[x][y] && !Pushed[x][y + 1])
4687 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4688 WasJustMoving[x][y])
4693 /* this is needed for a special case not covered by calling "Impact()"
4694 from "ContinueMoving()": if an element moves to a tile directly below
4695 another element which was just falling on that tile (which was empty
4696 in the previous frame), the falling element above would just stop
4697 instead of smashing the element below (in previous version, the above
4698 element was just checked for "moving" instead of "falling", resulting
4699 in incorrect smashes caused by horizontal movement of the above
4700 element; also, the case of the player being the element to smash was
4701 simply not covered here... :-/ ) */
4704 WasJustMoving[x][y] = 0;
4705 WasJustFalling[x][y] = 0;
4710 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4712 if (MovDir[x][y] == MV_NO_MOVING)
4714 InitMovingField(x, y, MV_DOWN);
4715 started_moving = TRUE;
4718 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4720 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4721 MovDir[x][y] = MV_DOWN;
4723 InitMovingField(x, y, MV_DOWN);
4724 started_moving = TRUE;
4726 else if (element == EL_AMOEBA_DROP)
4728 Feld[x][y] = EL_AMOEBA_GROWING;
4729 Store[x][y] = EL_AMOEBA_WET;
4731 /* Store[x][y + 1] must be zero, because:
4732 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4735 #if OLD_GAME_BEHAVIOUR
4736 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4738 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4739 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4740 element != EL_DX_SUPABOMB)
4743 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4744 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4745 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4746 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4749 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4750 (IS_FREE(x - 1, y + 1) ||
4751 Feld[x - 1][y + 1] == EL_ACID));
4752 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4753 (IS_FREE(x + 1, y + 1) ||
4754 Feld[x + 1][y + 1] == EL_ACID));
4755 boolean can_fall_any = (can_fall_left || can_fall_right);
4756 boolean can_fall_both = (can_fall_left && can_fall_right);
4758 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4760 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4762 if (slippery_type == SLIPPERY_ONLY_LEFT)
4763 can_fall_right = FALSE;
4764 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4765 can_fall_left = FALSE;
4766 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4767 can_fall_right = FALSE;
4768 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4769 can_fall_left = FALSE;
4771 can_fall_any = (can_fall_left || can_fall_right);
4772 can_fall_both = (can_fall_left && can_fall_right);
4777 if (can_fall_both &&
4778 (game.emulation != EMU_BOULDERDASH &&
4779 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4780 can_fall_left = !(can_fall_right = RND(2));
4782 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4783 started_moving = TRUE;
4787 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4789 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4792 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4793 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4794 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4795 int belt_dir = game.belt_dir[belt_nr];
4797 if ((belt_dir == MV_LEFT && left_is_free) ||
4798 (belt_dir == MV_RIGHT && right_is_free))
4801 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4804 InitMovingField(x, y, belt_dir);
4805 started_moving = TRUE;
4808 Pushed[x][y] = TRUE;
4809 Pushed[nextx][y] = TRUE;
4812 GfxAction[x][y] = ACTION_DEFAULT;
4816 MovDir[x][y] = 0; /* if element was moving, stop it */
4821 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4822 if (CAN_MOVE(element) && !started_moving)
4824 int move_pattern = element_info[element].move_pattern;
4827 Moving2Blocked(x, y, &newx, &newy);
4830 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4833 if ((element == EL_SATELLITE ||
4834 element == EL_BALLOON ||
4835 element == EL_SPRING)
4836 && JustBeingPushed(x, y))
4841 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4842 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4843 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4846 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4847 element, element_info[element].token_name,
4848 WasJustMoving[x][y],
4849 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4850 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4851 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4852 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4856 WasJustMoving[x][y] = 0;
4859 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4862 if (Feld[x][y] != element) /* element has changed */
4864 element = Feld[x][y];
4865 move_pattern = element_info[element].move_pattern;
4867 if (!CAN_MOVE(element))
4871 if (Feld[x][y] != element) /* element has changed */
4879 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4880 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4882 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4884 Moving2Blocked(x, y, &newx, &newy);
4885 if (Feld[newx][newy] == EL_BLOCKED)
4886 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4892 if (FrameCounter < 1 && x == 0 && y == 29)
4893 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4896 if (!MovDelay[x][y]) /* start new movement phase */
4898 /* all objects that can change their move direction after each step
4899 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4901 if (element != EL_YAMYAM &&
4902 element != EL_DARK_YAMYAM &&
4903 element != EL_PACMAN &&
4904 !(move_pattern & MV_ANY_DIRECTION) &&
4905 move_pattern != MV_TURNING_LEFT &&
4906 move_pattern != MV_TURNING_RIGHT &&
4907 move_pattern != MV_TURNING_LEFT_RIGHT &&
4908 move_pattern != MV_TURNING_RIGHT_LEFT &&
4909 move_pattern != MV_TURNING_RANDOM)
4914 if (FrameCounter < 1 && x == 0 && y == 29)
4915 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4918 if (MovDelay[x][y] && (element == EL_BUG ||
4919 element == EL_SPACESHIP ||
4920 element == EL_SP_SNIKSNAK ||
4921 element == EL_SP_ELECTRON ||
4922 element == EL_MOLE))
4923 DrawLevelField(x, y);
4927 if (MovDelay[x][y]) /* wait some time before next movement */
4932 if (element == EL_YAMYAM)
4935 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4936 DrawLevelElementAnimation(x, y, element);
4940 if (MovDelay[x][y]) /* element still has to wait some time */
4943 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4944 ResetGfxAnimation(x, y);
4948 if (GfxAction[x][y] != ACTION_WAITING)
4949 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4951 GfxAction[x][y] = ACTION_WAITING;
4955 if (element == EL_ROBOT ||
4957 element == EL_PACMAN ||
4959 element == EL_YAMYAM ||
4960 element == EL_DARK_YAMYAM)
4963 DrawLevelElementAnimation(x, y, element);
4965 DrawLevelElementAnimationIfNeeded(x, y, element);
4967 PlayLevelSoundAction(x, y, ACTION_WAITING);
4969 else if (element == EL_SP_ELECTRON)
4970 DrawLevelElementAnimationIfNeeded(x, y, element);
4971 else if (element == EL_DRAGON)
4974 int dir = MovDir[x][y];
4975 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4976 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4977 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4978 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4979 dir == MV_UP ? IMG_FLAMES_1_UP :
4980 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4981 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4984 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4987 GfxAction[x][y] = ACTION_ATTACKING;
4989 if (IS_PLAYER(x, y))
4990 DrawPlayerField(x, y);
4992 DrawLevelField(x, y);
4994 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4996 for (i = 1; i <= 3; i++)
4998 int xx = x + i * dx;
4999 int yy = y + i * dy;
5000 int sx = SCREENX(xx);
5001 int sy = SCREENY(yy);
5002 int flame_graphic = graphic + (i - 1);
5004 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5009 int flamed = MovingOrBlocked2Element(xx, yy);
5011 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5014 RemoveMovingField(xx, yy);
5016 Feld[xx][yy] = EL_FLAMES;
5017 if (IN_SCR_FIELD(sx, sy))
5019 DrawLevelFieldCrumbledSand(xx, yy);
5020 DrawGraphic(sx, sy, flame_graphic, frame);
5025 if (Feld[xx][yy] == EL_FLAMES)
5026 Feld[xx][yy] = EL_EMPTY;
5027 DrawLevelField(xx, yy);
5032 if (MovDelay[x][y]) /* element still has to wait some time */
5034 PlayLevelSoundAction(x, y, ACTION_WAITING);
5040 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5041 for all other elements GfxAction will be set by InitMovingField() */
5042 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5043 GfxAction[x][y] = ACTION_MOVING;
5047 /* now make next step */
5049 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5051 if (DONT_COLLIDE_WITH(element) &&
5052 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5053 !PLAYER_ENEMY_PROTECTED(newx, newy))
5056 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5060 /* player killed by element which is deadly when colliding with */
5062 KillHero(PLAYERINFO(newx, newy));
5069 else if (CAN_MOVE_INTO_ACID(element) &&
5070 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5071 (MovDir[x][y] == MV_DOWN ||
5072 game.engine_version > VERSION_IDENT(3,0,8,0)))
5074 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5075 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5079 else if ((element == EL_PENGUIN ||
5080 element == EL_ROBOT ||
5081 element == EL_SATELLITE ||
5082 element == EL_BALLOON ||
5083 IS_CUSTOM_ELEMENT(element)) &&
5084 IN_LEV_FIELD(newx, newy) &&
5085 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5088 SplashAcid(newx, newy);
5089 Store[x][y] = EL_ACID;
5091 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5093 if (Feld[newx][newy] == EL_EXIT_OPEN)
5097 DrawLevelField(x, y);
5099 Feld[x][y] = EL_EMPTY;
5100 DrawLevelField(x, y);
5103 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5104 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5105 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5107 local_player->friends_still_needed--;
5108 if (!local_player->friends_still_needed &&
5109 !local_player->GameOver && AllPlayersGone)
5110 local_player->LevelSolved = local_player->GameOver = TRUE;
5114 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5116 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5117 DrawLevelField(newx, newy);
5119 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5121 else if (!IS_FREE(newx, newy))
5123 GfxAction[x][y] = ACTION_WAITING;
5125 if (IS_PLAYER(x, y))
5126 DrawPlayerField(x, y);
5128 DrawLevelField(x, y);
5133 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5135 if (IS_FOOD_PIG(Feld[newx][newy]))
5137 if (IS_MOVING(newx, newy))
5138 RemoveMovingField(newx, newy);
5141 Feld[newx][newy] = EL_EMPTY;
5142 DrawLevelField(newx, newy);
5145 PlayLevelSound(x, y, SND_PIG_DIGGING);
5147 else if (!IS_FREE(newx, newy))
5149 if (IS_PLAYER(x, y))
5150 DrawPlayerField(x, y);
5152 DrawLevelField(x, y);
5161 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5164 else if (IS_CUSTOM_ELEMENT(element) &&
5165 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5169 !IS_FREE(newx, newy)
5174 int new_element = Feld[newx][newy];
5177 printf("::: '%s' digs '%s' [%d]\n",
5178 element_info[element].token_name,
5179 element_info[Feld[newx][newy]].token_name,
5180 StorePlayer[newx][newy]);
5183 if (!IS_FREE(newx, newy))
5185 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5186 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5189 /* no element can dig solid indestructible elements */
5190 if (IS_INDESTRUCTIBLE(new_element) &&
5191 !IS_DIGGABLE(new_element) &&
5192 !IS_COLLECTIBLE(new_element))
5195 if (AmoebaNr[newx][newy] &&
5196 (new_element == EL_AMOEBA_FULL ||
5197 new_element == EL_BD_AMOEBA ||
5198 new_element == EL_AMOEBA_GROWING))
5200 AmoebaCnt[AmoebaNr[newx][newy]]--;
5201 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5204 if (IS_MOVING(newx, newy))
5205 RemoveMovingField(newx, newy);
5208 RemoveField(newx, newy);
5209 DrawLevelField(newx, newy);
5212 PlayLevelSoundAction(x, y, action);
5215 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5216 element_info[element].can_leave_element = TRUE;
5218 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5220 RunnerVisit[x][y] = FrameCounter;
5221 PlayerVisit[x][y] /= 8; /* expire player visit path */
5227 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5229 if (!IS_FREE(newx, newy))
5231 if (IS_PLAYER(x, y))
5232 DrawPlayerField(x, y);
5234 DrawLevelField(x, y);
5240 boolean wanna_flame = !RND(10);
5241 int dx = newx - x, dy = newy - y;
5242 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5243 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5244 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5245 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5246 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5247 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5250 IS_CLASSIC_ENEMY(element1) ||
5251 IS_CLASSIC_ENEMY(element2)) &&
5252 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5253 element1 != EL_FLAMES && element2 != EL_FLAMES)
5256 ResetGfxAnimation(x, y);
5257 GfxAction[x][y] = ACTION_ATTACKING;
5260 if (IS_PLAYER(x, y))
5261 DrawPlayerField(x, y);
5263 DrawLevelField(x, y);
5265 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5267 MovDelay[x][y] = 50;
5269 Feld[newx][newy] = EL_FLAMES;
5270 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5271 Feld[newx1][newy1] = EL_FLAMES;
5272 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5273 Feld[newx2][newy2] = EL_FLAMES;
5279 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5280 Feld[newx][newy] == EL_DIAMOND)
5282 if (IS_MOVING(newx, newy))
5283 RemoveMovingField(newx, newy);
5286 Feld[newx][newy] = EL_EMPTY;
5287 DrawLevelField(newx, newy);
5290 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5292 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5293 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5295 if (AmoebaNr[newx][newy])
5297 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5298 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5299 Feld[newx][newy] == EL_BD_AMOEBA)
5300 AmoebaCnt[AmoebaNr[newx][newy]]--;
5305 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5307 if (IS_MOVING(newx, newy))
5310 RemoveMovingField(newx, newy);
5314 Feld[newx][newy] = EL_EMPTY;
5315 DrawLevelField(newx, newy);
5318 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5320 else if ((element == EL_PACMAN || element == EL_MOLE)
5321 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5323 if (AmoebaNr[newx][newy])
5325 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5326 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5327 Feld[newx][newy] == EL_BD_AMOEBA)
5328 AmoebaCnt[AmoebaNr[newx][newy]]--;
5331 if (element == EL_MOLE)
5333 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5334 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5336 ResetGfxAnimation(x, y);
5337 GfxAction[x][y] = ACTION_DIGGING;
5338 DrawLevelField(x, y);
5340 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5342 return; /* wait for shrinking amoeba */
5344 else /* element == EL_PACMAN */
5346 Feld[newx][newy] = EL_EMPTY;
5347 DrawLevelField(newx, newy);
5348 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5351 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5352 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5353 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5355 /* wait for shrinking amoeba to completely disappear */
5358 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5360 /* object was running against a wall */
5365 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5366 DrawLevelElementAnimation(x, y, element);
5368 if (element == EL_BUG ||
5369 element == EL_SPACESHIP ||
5370 element == EL_SP_SNIKSNAK)
5371 DrawLevelField(x, y);
5372 else if (element == EL_MOLE)
5373 DrawLevelField(x, y);
5374 else if (element == EL_BD_BUTTERFLY ||
5375 element == EL_BD_FIREFLY)
5376 DrawLevelElementAnimationIfNeeded(x, y, element);
5377 else if (element == EL_SATELLITE)
5378 DrawLevelElementAnimationIfNeeded(x, y, element);
5379 else if (element == EL_SP_ELECTRON)
5380 DrawLevelElementAnimationIfNeeded(x, y, element);
5383 if (DONT_TOUCH(element))
5384 TestIfBadThingTouchesHero(x, y);
5387 PlayLevelSoundAction(x, y, ACTION_WAITING);
5393 InitMovingField(x, y, MovDir[x][y]);
5395 PlayLevelSoundAction(x, y, ACTION_MOVING);
5399 ContinueMoving(x, y);
5402 void ContinueMoving(int x, int y)
5404 int element = Feld[x][y];
5405 struct ElementInfo *ei = &element_info[element];
5406 int direction = MovDir[x][y];
5407 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5408 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5409 int newx = x + dx, newy = y + dy;
5411 int nextx = newx + dx, nexty = newy + dy;
5414 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5415 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5417 boolean pushed_by_player = Pushed[x][y];
5420 MovPos[x][y] += getElementMoveStepsize(x, y);
5423 if (pushed_by_player && IS_PLAYER(x, y))
5425 /* special case: moving object pushed by player */
5426 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5429 if (pushed_by_player) /* special case: moving object pushed by player */
5430 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5433 if (ABS(MovPos[x][y]) < TILEX)
5435 DrawLevelField(x, y);
5437 return; /* element is still moving */
5440 /* element reached destination field */
5442 Feld[x][y] = EL_EMPTY;
5443 Feld[newx][newy] = element;
5444 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5446 if (element == EL_MOLE)
5448 Feld[x][y] = EL_SAND;
5450 DrawLevelFieldCrumbledSandNeighbours(x, y);
5452 else if (element == EL_QUICKSAND_FILLING)
5454 element = Feld[newx][newy] = get_next_element(element);
5455 Store[newx][newy] = Store[x][y];
5457 else if (element == EL_QUICKSAND_EMPTYING)
5459 Feld[x][y] = get_next_element(element);
5460 element = Feld[newx][newy] = Store[x][y];
5462 else if (element == EL_MAGIC_WALL_FILLING)
5464 element = Feld[newx][newy] = get_next_element(element);
5465 if (!game.magic_wall_active)
5466 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5467 Store[newx][newy] = Store[x][y];
5469 else if (element == EL_MAGIC_WALL_EMPTYING)
5471 Feld[x][y] = get_next_element(element);
5472 if (!game.magic_wall_active)
5473 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5474 element = Feld[newx][newy] = Store[x][y];
5476 else if (element == EL_BD_MAGIC_WALL_FILLING)
5478 element = Feld[newx][newy] = get_next_element(element);
5479 if (!game.magic_wall_active)
5480 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5481 Store[newx][newy] = Store[x][y];
5483 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5485 Feld[x][y] = get_next_element(element);
5486 if (!game.magic_wall_active)
5487 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5488 element = Feld[newx][newy] = Store[x][y];
5490 else if (element == EL_AMOEBA_DROPPING)
5492 Feld[x][y] = get_next_element(element);
5493 element = Feld[newx][newy] = Store[x][y];
5495 else if (element == EL_SOKOBAN_OBJECT)
5498 Feld[x][y] = Back[x][y];
5500 if (Back[newx][newy])
5501 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5503 Back[x][y] = Back[newx][newy] = 0;
5505 else if (Store[x][y] == EL_ACID)
5507 element = Feld[newx][newy] = EL_ACID;
5511 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5512 MovDelay[newx][newy] = 0;
5514 /* copy element change control values to new field */
5515 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5516 ChangePage[newx][newy] = ChangePage[x][y];
5517 Changed[newx][newy] = Changed[x][y];
5518 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5520 ChangeDelay[x][y] = 0;
5521 ChangePage[x][y] = -1;
5522 Changed[x][y] = CE_BITMASK_DEFAULT;
5523 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5525 /* copy animation control values to new field */
5526 GfxFrame[newx][newy] = GfxFrame[x][y];
5527 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5528 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5529 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5531 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5533 ResetGfxAnimation(x, y); /* reset animation values for old field */
5536 /* some elements can leave other elements behind after moving */
5537 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5538 ei->move_leave_element != EL_EMPTY &&
5539 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5540 ei->can_leave_element_last))
5542 Feld[x][y] = ei->move_leave_element;
5543 InitField(x, y, FALSE);
5545 if (GFX_CRUMBLED(Feld[x][y]))
5546 DrawLevelFieldCrumbledSandNeighbours(x, y);
5549 ei->can_leave_element_last = ei->can_leave_element;
5550 ei->can_leave_element = FALSE;
5554 /* 2.1.1 (does not work correctly for spring) */
5555 if (!CAN_MOVE(element))
5556 MovDir[newx][newy] = 0;
5560 /* (does not work for falling objects that slide horizontally) */
5561 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5562 MovDir[newx][newy] = 0;
5565 if (!CAN_MOVE(element) ||
5566 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5567 MovDir[newx][newy] = 0;
5570 if (!CAN_MOVE(element) ||
5571 (CAN_FALL(element) && direction == MV_DOWN))
5572 GfxDir[x][y] = MovDir[newx][newy] = 0;
5577 DrawLevelField(x, y);
5578 DrawLevelField(newx, newy);
5580 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5582 /* prevent pushed element from moving on in pushed direction */
5583 if (pushed_by_player && CAN_MOVE(element) &&
5584 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5585 !(element_info[element].move_pattern & direction))
5586 TurnRound(newx, newy);
5589 /* prevent elements on conveyor belt from moving on in last direction */
5590 if (pushed_by_conveyor && CAN_FALL(element) &&
5591 direction & MV_HORIZONTAL)
5592 MovDir[newx][newy] = 0;
5595 if (!pushed_by_player)
5597 WasJustMoving[newx][newy] = 3;
5599 if (CAN_FALL(element) && direction == MV_DOWN)
5600 WasJustFalling[newx][newy] = 3;
5603 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5605 TestIfBadThingTouchesHero(newx, newy);
5606 TestIfBadThingTouchesFriend(newx, newy);
5608 if (!IS_CUSTOM_ELEMENT(element))
5609 TestIfBadThingTouchesOtherBadThing(newx, newy);
5611 else if (element == EL_PENGUIN)
5612 TestIfFriendTouchesBadThing(newx, newy);
5614 if (CAN_FALL(element) && direction == MV_DOWN &&
5615 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5619 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5623 if (ChangePage[newx][newy] != -1) /* delayed change */
5624 ChangeElement(newx, newy, ChangePage[newx][newy]);
5629 TestIfElementHitsCustomElement(newx, newy, direction);
5633 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5635 int hitting_element = Feld[newx][newy];
5637 /* !!! fix side (direction) orientation here and elsewhere !!! */
5638 CheckElementSideChange(newx, newy, hitting_element,
5639 direction, CE_HITTING_SOMETHING, -1);
5642 if (IN_LEV_FIELD(nextx, nexty))
5644 int opposite_direction = MV_DIR_OPPOSITE(direction);
5645 int hitting_side = direction;
5646 int touched_side = opposite_direction;
5647 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5648 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5649 MovDir[nextx][nexty] != direction ||
5650 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5656 CheckElementSideChange(nextx, nexty, touched_element,
5657 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5659 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5660 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5662 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5664 struct ElementChangeInfo *change =
5665 &element_info[hitting_element].change_page[i];
5667 if (change->can_change &&
5668 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5669 change->sides & touched_side &&
5670 change->trigger_element == touched_element)
5672 CheckElementSideChange(newx, newy, hitting_element,
5673 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5679 if (IS_CUSTOM_ELEMENT(touched_element) &&
5680 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5682 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5684 struct ElementChangeInfo *change =
5685 &element_info[touched_element].change_page[i];
5687 if (change->can_change &&
5688 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5689 change->sides & hitting_side &&
5690 change->trigger_element == hitting_element)
5692 CheckElementSideChange(nextx, nexty, touched_element,
5693 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5704 TestIfPlayerTouchesCustomElement(newx, newy);
5705 TestIfElementTouchesCustomElement(newx, newy);
5708 int AmoebeNachbarNr(int ax, int ay)
5711 int element = Feld[ax][ay];
5713 static int xy[4][2] =
5721 for (i = 0; i < NUM_DIRECTIONS; i++)
5723 int x = ax + xy[i][0];
5724 int y = ay + xy[i][1];
5726 if (!IN_LEV_FIELD(x, y))
5729 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5730 group_nr = AmoebaNr[x][y];
5736 void AmoebenVereinigen(int ax, int ay)
5738 int i, x, y, xx, yy;
5739 int new_group_nr = AmoebaNr[ax][ay];
5740 static int xy[4][2] =
5748 if (new_group_nr == 0)
5751 for (i = 0; i < NUM_DIRECTIONS; i++)
5756 if (!IN_LEV_FIELD(x, y))
5759 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5760 Feld[x][y] == EL_BD_AMOEBA ||
5761 Feld[x][y] == EL_AMOEBA_DEAD) &&
5762 AmoebaNr[x][y] != new_group_nr)
5764 int old_group_nr = AmoebaNr[x][y];
5766 if (old_group_nr == 0)
5769 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5770 AmoebaCnt[old_group_nr] = 0;
5771 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5772 AmoebaCnt2[old_group_nr] = 0;
5774 for (yy = 0; yy < lev_fieldy; yy++)
5776 for (xx = 0; xx < lev_fieldx; xx++)
5778 if (AmoebaNr[xx][yy] == old_group_nr)
5779 AmoebaNr[xx][yy] = new_group_nr;
5786 void AmoebeUmwandeln(int ax, int ay)
5790 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5792 int group_nr = AmoebaNr[ax][ay];
5797 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5798 printf("AmoebeUmwandeln(): This should never happen!\n");
5803 for (y = 0; y < lev_fieldy; y++)
5805 for (x = 0; x < lev_fieldx; x++)
5807 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5810 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5814 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5815 SND_AMOEBA_TURNING_TO_GEM :
5816 SND_AMOEBA_TURNING_TO_ROCK));
5821 static int xy[4][2] =
5829 for (i = 0; i < NUM_DIRECTIONS; i++)
5834 if (!IN_LEV_FIELD(x, y))
5837 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5839 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5840 SND_AMOEBA_TURNING_TO_GEM :
5841 SND_AMOEBA_TURNING_TO_ROCK));
5848 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5851 int group_nr = AmoebaNr[ax][ay];
5852 boolean done = FALSE;
5857 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5858 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5863 for (y = 0; y < lev_fieldy; y++)
5865 for (x = 0; x < lev_fieldx; x++)
5867 if (AmoebaNr[x][y] == group_nr &&
5868 (Feld[x][y] == EL_AMOEBA_DEAD ||
5869 Feld[x][y] == EL_BD_AMOEBA ||
5870 Feld[x][y] == EL_AMOEBA_GROWING))
5873 Feld[x][y] = new_element;
5874 InitField(x, y, FALSE);
5875 DrawLevelField(x, y);
5882 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5883 SND_BD_AMOEBA_TURNING_TO_ROCK :
5884 SND_BD_AMOEBA_TURNING_TO_GEM));
5887 void AmoebeWaechst(int x, int y)
5889 static unsigned long sound_delay = 0;
5890 static unsigned long sound_delay_value = 0;
5892 if (!MovDelay[x][y]) /* start new growing cycle */
5896 if (DelayReached(&sound_delay, sound_delay_value))
5899 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5901 if (Store[x][y] == EL_BD_AMOEBA)
5902 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5904 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5906 sound_delay_value = 30;
5910 if (MovDelay[x][y]) /* wait some time before growing bigger */
5913 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5915 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5916 6 - MovDelay[x][y]);
5918 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5921 if (!MovDelay[x][y])
5923 Feld[x][y] = Store[x][y];
5925 DrawLevelField(x, y);
5930 void AmoebaDisappearing(int x, int y)
5932 static unsigned long sound_delay = 0;
5933 static unsigned long sound_delay_value = 0;
5935 if (!MovDelay[x][y]) /* start new shrinking cycle */
5939 if (DelayReached(&sound_delay, sound_delay_value))
5940 sound_delay_value = 30;
5943 if (MovDelay[x][y]) /* wait some time before shrinking */
5946 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5948 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5949 6 - MovDelay[x][y]);
5951 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5954 if (!MovDelay[x][y])
5956 Feld[x][y] = EL_EMPTY;
5957 DrawLevelField(x, y);
5959 /* don't let mole enter this field in this cycle;
5960 (give priority to objects falling to this field from above) */
5966 void AmoebeAbleger(int ax, int ay)
5969 int element = Feld[ax][ay];
5970 int graphic = el2img(element);
5971 int newax = ax, neway = ay;
5972 static int xy[4][2] =
5980 if (!level.amoeba_speed)
5982 Feld[ax][ay] = EL_AMOEBA_DEAD;
5983 DrawLevelField(ax, ay);
5987 if (IS_ANIMATED(graphic))
5988 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5990 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5991 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5993 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5996 if (MovDelay[ax][ay])
6000 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6003 int x = ax + xy[start][0];
6004 int y = ay + xy[start][1];
6006 if (!IN_LEV_FIELD(x, y))
6009 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6010 if (IS_FREE(x, y) ||
6011 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6017 if (newax == ax && neway == ay)
6020 else /* normal or "filled" (BD style) amoeba */
6023 boolean waiting_for_player = FALSE;
6025 for (i = 0; i < NUM_DIRECTIONS; i++)
6027 int j = (start + i) % 4;
6028 int x = ax + xy[j][0];
6029 int y = ay + xy[j][1];
6031 if (!IN_LEV_FIELD(x, y))
6034 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6035 if (IS_FREE(x, y) ||
6036 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6042 else if (IS_PLAYER(x, y))
6043 waiting_for_player = TRUE;
6046 if (newax == ax && neway == ay) /* amoeba cannot grow */
6048 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6050 Feld[ax][ay] = EL_AMOEBA_DEAD;
6051 DrawLevelField(ax, ay);
6052 AmoebaCnt[AmoebaNr[ax][ay]]--;
6054 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6056 if (element == EL_AMOEBA_FULL)
6057 AmoebeUmwandeln(ax, ay);
6058 else if (element == EL_BD_AMOEBA)
6059 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6064 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6066 /* amoeba gets larger by growing in some direction */
6068 int new_group_nr = AmoebaNr[ax][ay];
6071 if (new_group_nr == 0)
6073 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6074 printf("AmoebeAbleger(): This should never happen!\n");
6079 AmoebaNr[newax][neway] = new_group_nr;
6080 AmoebaCnt[new_group_nr]++;
6081 AmoebaCnt2[new_group_nr]++;
6083 /* if amoeba touches other amoeba(s) after growing, unify them */
6084 AmoebenVereinigen(newax, neway);
6086 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6088 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6094 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6095 (neway == lev_fieldy - 1 && newax != ax))
6097 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6098 Store[newax][neway] = element;
6100 else if (neway == ay)
6102 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6104 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6106 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6111 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6112 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6113 Store[ax][ay] = EL_AMOEBA_DROP;
6114 ContinueMoving(ax, ay);
6118 DrawLevelField(newax, neway);
6121 void Life(int ax, int ay)
6124 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6126 int element = Feld[ax][ay];
6127 int graphic = el2img(element);
6128 boolean changed = FALSE;
6130 if (IS_ANIMATED(graphic))
6131 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6136 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6137 MovDelay[ax][ay] = life_time;
6139 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6142 if (MovDelay[ax][ay])
6146 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6148 int xx = ax+x1, yy = ay+y1;
6151 if (!IN_LEV_FIELD(xx, yy))
6154 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6156 int x = xx+x2, y = yy+y2;
6158 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6161 if (((Feld[x][y] == element ||
6162 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6164 (IS_FREE(x, y) && Stop[x][y]))
6168 if (xx == ax && yy == ay) /* field in the middle */
6170 if (nachbarn < life[0] || nachbarn > life[1])
6172 Feld[xx][yy] = EL_EMPTY;
6174 DrawLevelField(xx, yy);
6175 Stop[xx][yy] = TRUE;
6179 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6180 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6181 { /* free border field */
6182 if (nachbarn >= life[2] && nachbarn <= life[3])
6184 Feld[xx][yy] = element;
6185 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6187 DrawLevelField(xx, yy);
6188 Stop[xx][yy] = TRUE;
6195 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6196 SND_GAME_OF_LIFE_GROWING);
6199 static void InitRobotWheel(int x, int y)
6201 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6204 static void RunRobotWheel(int x, int y)
6206 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6209 static void StopRobotWheel(int x, int y)
6211 if (ZX == x && ZY == y)
6215 static void InitTimegateWheel(int x, int y)
6217 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6220 static void RunTimegateWheel(int x, int y)
6222 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6225 void CheckExit(int x, int y)
6227 if (local_player->gems_still_needed > 0 ||
6228 local_player->sokobanfields_still_needed > 0 ||
6229 local_player->lights_still_needed > 0)
6231 int element = Feld[x][y];
6232 int graphic = el2img(element);
6234 if (IS_ANIMATED(graphic))
6235 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6240 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6243 Feld[x][y] = EL_EXIT_OPENING;
6245 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6248 void CheckExitSP(int x, int y)
6250 if (local_player->gems_still_needed > 0)
6252 int element = Feld[x][y];
6253 int graphic = el2img(element);
6255 if (IS_ANIMATED(graphic))
6256 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6261 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6264 Feld[x][y] = EL_SP_EXIT_OPENING;
6266 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6269 static void CloseAllOpenTimegates()
6273 for (y = 0; y < lev_fieldy; y++)
6275 for (x = 0; x < lev_fieldx; x++)
6277 int element = Feld[x][y];
6279 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6281 Feld[x][y] = EL_TIMEGATE_CLOSING;
6283 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6285 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6292 void EdelsteinFunkeln(int x, int y)
6294 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6297 if (Feld[x][y] == EL_BD_DIAMOND)
6300 if (MovDelay[x][y] == 0) /* next animation frame */
6301 MovDelay[x][y] = 11 * !SimpleRND(500);
6303 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6307 if (setup.direct_draw && MovDelay[x][y])
6308 SetDrawtoField(DRAW_BUFFERED);
6310 DrawLevelElementAnimation(x, y, Feld[x][y]);
6312 if (MovDelay[x][y] != 0)
6314 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6315 10 - MovDelay[x][y]);
6317 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6319 if (setup.direct_draw)
6323 dest_x = FX + SCREENX(x) * TILEX;
6324 dest_y = FY + SCREENY(y) * TILEY;
6326 BlitBitmap(drawto_field, window,
6327 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6328 SetDrawtoField(DRAW_DIRECT);
6334 void MauerWaechst(int x, int y)
6338 if (!MovDelay[x][y]) /* next animation frame */
6339 MovDelay[x][y] = 3 * delay;
6341 if (MovDelay[x][y]) /* wait some time before next frame */
6345 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6347 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6348 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6350 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6353 if (!MovDelay[x][y])
6355 if (MovDir[x][y] == MV_LEFT)
6357 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6358 DrawLevelField(x - 1, y);
6360 else if (MovDir[x][y] == MV_RIGHT)
6362 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6363 DrawLevelField(x + 1, y);
6365 else if (MovDir[x][y] == MV_UP)
6367 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6368 DrawLevelField(x, y - 1);
6372 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6373 DrawLevelField(x, y + 1);
6376 Feld[x][y] = Store[x][y];
6378 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6379 DrawLevelField(x, y);
6384 void MauerAbleger(int ax, int ay)
6386 int element = Feld[ax][ay];
6387 int graphic = el2img(element);
6388 boolean oben_frei = FALSE, unten_frei = FALSE;
6389 boolean links_frei = FALSE, rechts_frei = FALSE;
6390 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6391 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6392 boolean new_wall = FALSE;
6394 if (IS_ANIMATED(graphic))
6395 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6397 if (!MovDelay[ax][ay]) /* start building new wall */
6398 MovDelay[ax][ay] = 6;
6400 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6403 if (MovDelay[ax][ay])
6407 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6409 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6411 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6413 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6416 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6417 element == EL_EXPANDABLE_WALL_ANY)
6421 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6422 Store[ax][ay-1] = element;
6423 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6424 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6425 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6426 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6431 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6432 Store[ax][ay+1] = element;
6433 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6434 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6435 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6436 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6441 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6442 element == EL_EXPANDABLE_WALL_ANY ||
6443 element == EL_EXPANDABLE_WALL)
6447 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6448 Store[ax-1][ay] = element;
6449 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6450 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6451 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6452 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6458 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6459 Store[ax+1][ay] = element;
6460 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6461 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6462 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6463 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6468 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6469 DrawLevelField(ax, ay);
6471 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6473 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6474 unten_massiv = TRUE;
6475 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6476 links_massiv = TRUE;
6477 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6478 rechts_massiv = TRUE;
6480 if (((oben_massiv && unten_massiv) ||
6481 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6482 element == EL_EXPANDABLE_WALL) &&
6483 ((links_massiv && rechts_massiv) ||
6484 element == EL_EXPANDABLE_WALL_VERTICAL))
6485 Feld[ax][ay] = EL_WALL;
6489 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6491 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6495 void CheckForDragon(int x, int y)
6498 boolean dragon_found = FALSE;
6499 static int xy[4][2] =
6507 for (i = 0; i < NUM_DIRECTIONS; i++)
6509 for (j = 0; j < 4; j++)
6511 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6513 if (IN_LEV_FIELD(xx, yy) &&
6514 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6516 if (Feld[xx][yy] == EL_DRAGON)
6517 dragon_found = TRUE;
6526 for (i = 0; i < NUM_DIRECTIONS; i++)
6528 for (j = 0; j < 3; j++)
6530 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6532 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6534 Feld[xx][yy] = EL_EMPTY;
6535 DrawLevelField(xx, yy);
6544 static void InitBuggyBase(int x, int y)
6546 int element = Feld[x][y];
6547 int activating_delay = FRAMES_PER_SECOND / 4;
6550 (element == EL_SP_BUGGY_BASE ?
6551 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6552 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6554 element == EL_SP_BUGGY_BASE_ACTIVE ?
6555 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6558 static void WarnBuggyBase(int x, int y)
6561 static int xy[4][2] =
6569 for (i = 0; i < NUM_DIRECTIONS; i++)
6571 int xx = x + xy[i][0], yy = y + xy[i][1];
6573 if (IS_PLAYER(xx, yy))
6575 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6582 static void InitTrap(int x, int y)
6584 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6587 static void ActivateTrap(int x, int y)
6589 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6592 static void ChangeActiveTrap(int x, int y)
6594 int graphic = IMG_TRAP_ACTIVE;
6596 /* if new animation frame was drawn, correct crumbled sand border */
6597 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6598 DrawLevelFieldCrumbledSand(x, y);
6601 static void ChangeElementNowExt(int x, int y, int target_element)
6603 int previous_move_direction = MovDir[x][y];
6605 /* check if element under player changes from accessible to unaccessible
6606 (needed for special case of dropping element which then changes) */
6607 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6608 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6615 Feld[x][y] = target_element;
6617 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6619 ResetGfxAnimation(x, y);
6620 ResetRandomAnimationValue(x, y);
6622 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6623 MovDir[x][y] = previous_move_direction;
6626 InitField_WithBug1(x, y, FALSE);
6628 InitField(x, y, FALSE);
6629 if (CAN_MOVE(Feld[x][y]))
6633 DrawLevelField(x, y);
6635 if (GFX_CRUMBLED(Feld[x][y]))
6636 DrawLevelFieldCrumbledSandNeighbours(x, y);
6638 TestIfBadThingTouchesHero(x, y);
6639 TestIfPlayerTouchesCustomElement(x, y);
6640 TestIfElementTouchesCustomElement(x, y);
6642 if (ELEM_IS_PLAYER(target_element))
6643 RelocatePlayer(x, y, target_element);
6646 static boolean ChangeElementNow(int x, int y, int element, int page)
6648 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6650 /* always use default change event to prevent running into a loop */
6651 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6652 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6654 /* do not change already changed elements with same change event */
6656 if (Changed[x][y] & ChangeEvent[x][y])
6663 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6665 CheckTriggeredElementPageChange(x,y, Feld[x][y], CE_OTHER_IS_CHANGING, page);
6667 if (change->explode)
6674 if (change->use_content)
6676 boolean complete_change = TRUE;
6677 boolean can_change[3][3];
6680 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6682 boolean half_destructible;
6683 int ex = x + xx - 1;
6684 int ey = y + yy - 1;
6687 can_change[xx][yy] = TRUE;
6689 if (ex == x && ey == y) /* do not check changing element itself */
6692 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6694 can_change[xx][yy] = FALSE; /* do not change empty borders */
6699 if (!IN_LEV_FIELD(ex, ey))
6701 can_change[xx][yy] = FALSE;
6702 complete_change = FALSE;
6709 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6710 e = MovingOrBlocked2Element(ex, ey);
6712 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6714 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6715 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6716 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6718 can_change[xx][yy] = FALSE;
6719 complete_change = FALSE;
6723 if (!change->only_complete || complete_change)
6725 boolean something_has_changed = FALSE;
6727 if (change->only_complete && change->use_random_change &&
6728 RND(100) < change->random)
6731 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6733 int ex = x + xx - 1;
6734 int ey = y + yy - 1;
6736 if (can_change[xx][yy] && (!change->use_random_change ||
6737 RND(100) < change->random))
6739 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6740 RemoveMovingField(ex, ey);
6742 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6744 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6746 something_has_changed = TRUE;
6748 /* for symmetry reasons, freeze newly created border elements */
6749 if (ex != x || ey != y)
6750 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6754 if (something_has_changed)
6755 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6760 ChangeElementNowExt(x, y, change->target_element);
6762 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6768 static void ChangeElement(int x, int y, int page)
6770 int element = MovingOrBlocked2Element(x, y);
6771 struct ElementInfo *ei = &element_info[element];
6772 struct ElementChangeInfo *change = &ei->change_page[page];
6776 if (!CAN_CHANGE(element))
6779 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6780 x, y, element, element_info[element].token_name);
6781 printf("ChangeElement(): This should never happen!\n");
6787 if (ChangeDelay[x][y] == 0) /* initialize element change */
6789 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6790 RND(change->delay_random * change->delay_frames)) + 1;
6792 ResetGfxAnimation(x, y);
6793 ResetRandomAnimationValue(x, y);
6795 if (change->pre_change_function)
6796 change->pre_change_function(x, y);
6799 ChangeDelay[x][y]--;
6801 if (ChangeDelay[x][y] != 0) /* continue element change */
6803 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6805 if (IS_ANIMATED(graphic))
6806 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6808 if (change->change_function)
6809 change->change_function(x, y);
6811 else /* finish element change */
6813 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6815 page = ChangePage[x][y];
6816 ChangePage[x][y] = -1;
6820 if (IS_MOVING(x, y) && !change->explode)
6822 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6825 ChangeDelay[x][y] = 1; /* try change after next move step */
6826 ChangePage[x][y] = page; /* remember page to use for change */
6831 if (ChangeElementNow(x, y, element, page))
6833 if (change->post_change_function)
6834 change->post_change_function(x, y);
6839 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
6840 int trigger_element,
6848 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6851 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6853 int element = EL_CUSTOM_START + i;
6855 boolean change_element = FALSE;
6858 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6861 for (j = 0; j < element_info[element].num_change_pages; j++)
6863 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6865 if (change->can_change &&
6867 change->events & CH_EVENT_BIT(trigger_event) &&
6869 change->sides & trigger_side &&
6871 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
6873 change->trigger_element == trigger_element
6878 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6879 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6880 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6883 change_element = TRUE;
6890 if (!change_element)
6893 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6896 if (x == lx && y == ly) /* do not change trigger element itself */
6900 if (Feld[x][y] == element)
6902 ChangeDelay[x][y] = 1;
6903 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6904 ChangeElement(x, y, page);
6912 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6915 return CheckTriggeredElementChangeExt(lx, ly, trigger_element, CH_SIDE_ANY,
6916 trigger_event, -1, -1);
6919 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6920 int trigger_element,
6924 return CheckTriggeredElementChangeExt(lx, ly, trigger_element, trigger_side,
6925 trigger_event, -1, -1);
6928 static boolean CheckTriggeredElementPlayerChange(int lx, int ly,
6929 int trigger_element,
6934 return CheckTriggeredElementChangeExt(lx, ly, trigger_element, trigger_side,
6935 trigger_event, trigger_player, -1);
6938 static boolean CheckTriggeredElementPageChange(int lx, int ly,
6939 int trigger_element,
6943 return CheckTriggeredElementChangeExt(lx, ly, trigger_element, CH_SIDE_ANY,
6944 trigger_event, -1, trigger_page);
6947 static boolean CheckElementChangeExt(int x, int y, int element, int player,
6948 int side, int trigger_event, int page)
6950 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6953 if (Feld[x][y] == EL_BLOCKED)
6955 Blocked2Moving(x, y, &x, &y);
6956 element = Feld[x][y];
6962 boolean change_element = FALSE;
6965 for (i = 0; i < element_info[element].num_change_pages; i++)
6967 struct ElementChangeInfo *change = &element_info[element].change_page[i];
6969 if (change->can_change &&
6970 change->events & CH_EVENT_BIT(trigger_event) &&
6971 change->sides & side)
6973 change_element = TRUE;
6980 if (!change_element)
6986 /* !!! this check misses pages with same event, but different side !!! */
6989 page = element_info[element].event_page_nr[trigger_event];
6991 if (!(element_info[element].change_page[page].sides & side))
6995 ChangeDelay[x][y] = 1;
6996 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6997 ChangeElement(x, y, page);
7002 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
7004 return CheckElementChangeExt(x, y, element, -1, CH_SIDE_ANY, trigger_event,
7008 static boolean CheckElementSideChange(int x, int y, int element,
7013 return CheckElementChangeExt(x, y, element, -1, trigger_side, trigger_event,
7017 static boolean CheckElementPlayerChange(int x, int y, int element,
7023 return CheckElementChangeExt(x, y, element, trigger_player, trigger_side,
7024 trigger_event, page);
7027 static void PlayPlayerSound(struct PlayerInfo *player)
7029 int jx = player->jx, jy = player->jy;
7030 int element = player->element_nr;
7031 int last_action = player->last_action_waiting;
7032 int action = player->action_waiting;
7034 if (player->is_waiting)
7036 if (action != last_action)
7037 PlayLevelSoundElementAction(jx, jy, element, action);
7039 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7043 if (action != last_action)
7044 StopSound(element_info[element].sound[last_action]);
7046 if (last_action == ACTION_SLEEPING)
7047 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7051 static void PlayAllPlayersSound()
7055 for (i = 0; i < MAX_PLAYERS; i++)
7056 if (stored_player[i].active)
7057 PlayPlayerSound(&stored_player[i]);
7060 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7062 boolean last_waiting = player->is_waiting;
7063 int move_dir = player->MovDir;
7065 player->last_action_waiting = player->action_waiting;
7069 if (!last_waiting) /* not waiting -> waiting */
7071 player->is_waiting = TRUE;
7073 player->frame_counter_bored =
7075 game.player_boring_delay_fixed +
7076 SimpleRND(game.player_boring_delay_random);
7077 player->frame_counter_sleeping =
7079 game.player_sleeping_delay_fixed +
7080 SimpleRND(game.player_sleeping_delay_random);
7082 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7085 if (game.player_sleeping_delay_fixed +
7086 game.player_sleeping_delay_random > 0 &&
7087 player->anim_delay_counter == 0 &&
7088 player->post_delay_counter == 0 &&
7089 FrameCounter >= player->frame_counter_sleeping)
7090 player->is_sleeping = TRUE;
7091 else if (game.player_boring_delay_fixed +
7092 game.player_boring_delay_random > 0 &&
7093 FrameCounter >= player->frame_counter_bored)
7094 player->is_bored = TRUE;
7096 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7097 player->is_bored ? ACTION_BORING :
7100 if (player->is_sleeping)
7102 if (player->num_special_action_sleeping > 0)
7104 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7106 int last_special_action = player->special_action_sleeping;
7107 int num_special_action = player->num_special_action_sleeping;
7108 int special_action =
7109 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7110 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7111 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7112 last_special_action + 1 : ACTION_SLEEPING);
7113 int special_graphic =
7114 el_act_dir2img(player->element_nr, special_action, move_dir);
7116 player->anim_delay_counter =
7117 graphic_info[special_graphic].anim_delay_fixed +
7118 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7119 player->post_delay_counter =
7120 graphic_info[special_graphic].post_delay_fixed +
7121 SimpleRND(graphic_info[special_graphic].post_delay_random);
7123 player->special_action_sleeping = special_action;
7126 if (player->anim_delay_counter > 0)
7128 player->action_waiting = player->special_action_sleeping;
7129 player->anim_delay_counter--;
7131 else if (player->post_delay_counter > 0)
7133 player->post_delay_counter--;
7137 else if (player->is_bored)
7139 if (player->num_special_action_bored > 0)
7141 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7143 int special_action =
7144 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7145 int special_graphic =
7146 el_act_dir2img(player->element_nr, special_action, move_dir);
7148 player->anim_delay_counter =
7149 graphic_info[special_graphic].anim_delay_fixed +
7150 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7151 player->post_delay_counter =
7152 graphic_info[special_graphic].post_delay_fixed +
7153 SimpleRND(graphic_info[special_graphic].post_delay_random);
7155 player->special_action_bored = special_action;
7158 if (player->anim_delay_counter > 0)
7160 player->action_waiting = player->special_action_bored;
7161 player->anim_delay_counter--;
7163 else if (player->post_delay_counter > 0)
7165 player->post_delay_counter--;
7170 else if (last_waiting) /* waiting -> not waiting */
7172 player->is_waiting = FALSE;
7173 player->is_bored = FALSE;
7174 player->is_sleeping = FALSE;
7176 player->frame_counter_bored = -1;
7177 player->frame_counter_sleeping = -1;
7179 player->anim_delay_counter = 0;
7180 player->post_delay_counter = 0;
7182 player->action_waiting = ACTION_DEFAULT;
7184 player->special_action_bored = ACTION_DEFAULT;
7185 player->special_action_sleeping = ACTION_DEFAULT;
7190 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7193 static byte stored_player_action[MAX_PLAYERS];
7194 static int num_stored_actions = 0;
7196 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7197 int left = player_action & JOY_LEFT;
7198 int right = player_action & JOY_RIGHT;
7199 int up = player_action & JOY_UP;
7200 int down = player_action & JOY_DOWN;
7201 int button1 = player_action & JOY_BUTTON_1;
7202 int button2 = player_action & JOY_BUTTON_2;
7203 int dx = (left ? -1 : right ? 1 : 0);
7204 int dy = (up ? -1 : down ? 1 : 0);
7207 stored_player_action[player->index_nr] = 0;
7208 num_stored_actions++;
7212 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7215 if (!player->active || tape.pausing)
7219 printf("::: [%d %d %d %d] [%d %d]\n",
7220 left, right, up, down, button1, button2);
7226 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7230 snapped = SnapField(player, dx, dy);
7234 dropped = DropElement(player);
7236 moved = MovePlayer(player, dx, dy);
7239 if (tape.single_step && tape.recording && !tape.pausing)
7241 if (button1 || (dropped && !moved))
7243 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7244 SnapField(player, 0, 0); /* stop snapping */
7248 SetPlayerWaiting(player, FALSE);
7251 return player_action;
7253 stored_player_action[player->index_nr] = player_action;
7259 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7262 /* no actions for this player (no input at player's configured device) */
7264 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7265 SnapField(player, 0, 0);
7266 CheckGravityMovement(player);
7268 if (player->MovPos == 0)
7269 SetPlayerWaiting(player, TRUE);
7271 if (player->MovPos == 0) /* needed for tape.playing */
7272 player->is_moving = FALSE;
7274 player->is_dropping = FALSE;
7280 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7282 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7284 TapeRecordAction(stored_player_action);
7285 num_stored_actions = 0;
7292 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7294 static byte stored_player_action[MAX_PLAYERS];
7295 static int num_stored_actions = 0;
7296 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7297 int left = player_action & JOY_LEFT;
7298 int right = player_action & JOY_RIGHT;
7299 int up = player_action & JOY_UP;
7300 int down = player_action & JOY_DOWN;
7301 int button1 = player_action & JOY_BUTTON_1;
7302 int button2 = player_action & JOY_BUTTON_2;
7303 int dx = (left ? -1 : right ? 1 : 0);
7304 int dy = (up ? -1 : down ? 1 : 0);
7306 stored_player_action[player->index_nr] = 0;
7307 num_stored_actions++;
7309 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7311 if (!player->active || tape.pausing)
7316 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7319 snapped = SnapField(player, dx, dy);
7323 dropped = DropElement(player);
7325 moved = MovePlayer(player, dx, dy);
7328 if (tape.single_step && tape.recording && !tape.pausing)
7330 if (button1 || (dropped && !moved))
7332 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7333 SnapField(player, 0, 0); /* stop snapping */
7337 stored_player_action[player->index_nr] = player_action;
7341 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7343 /* no actions for this player (no input at player's configured device) */
7345 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7346 SnapField(player, 0, 0);
7347 CheckGravityMovement(player);
7349 if (player->MovPos == 0)
7350 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7352 if (player->MovPos == 0) /* needed for tape.playing */
7353 player->is_moving = FALSE;
7356 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7358 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7360 TapeRecordAction(stored_player_action);
7361 num_stored_actions = 0;
7368 static unsigned long action_delay = 0;
7369 unsigned long action_delay_value;
7370 int magic_wall_x = 0, magic_wall_y = 0;
7371 int i, x, y, element, graphic;
7372 byte *recorded_player_action;
7373 byte summarized_player_action = 0;
7375 byte tape_action[MAX_PLAYERS];
7378 if (game_status != GAME_MODE_PLAYING)
7381 action_delay_value =
7382 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7384 if (tape.playing && tape.index_search && !tape.pausing)
7385 action_delay_value = 0;
7387 /* ---------- main game synchronization point ---------- */
7389 WaitUntilDelayReached(&action_delay, action_delay_value);
7391 if (network_playing && !network_player_action_received)
7395 printf("DEBUG: try to get network player actions in time\n");
7399 #if defined(PLATFORM_UNIX)
7400 /* last chance to get network player actions without main loop delay */
7404 if (game_status != GAME_MODE_PLAYING)
7407 if (!network_player_action_received)
7411 printf("DEBUG: failed to get network player actions in time\n");
7422 printf("::: getting new tape action [%d]\n", FrameCounter);
7425 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7427 for (i = 0; i < MAX_PLAYERS; i++)
7429 summarized_player_action |= stored_player[i].action;
7431 if (!network_playing)
7432 stored_player[i].effective_action = stored_player[i].action;
7435 #if defined(PLATFORM_UNIX)
7436 if (network_playing)
7437 SendToServer_MovePlayer(summarized_player_action);
7440 if (!options.network && !setup.team_mode)
7441 local_player->effective_action = summarized_player_action;
7443 for (i = 0; i < MAX_PLAYERS; i++)
7445 int actual_player_action = stored_player[i].effective_action;
7447 if (stored_player[i].programmed_action)
7448 actual_player_action = stored_player[i].programmed_action;
7450 if (recorded_player_action)
7451 actual_player_action = recorded_player_action[i];
7453 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7455 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7456 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7458 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7463 TapeRecordAction(tape_action);
7466 network_player_action_received = FALSE;
7468 ScrollScreen(NULL, SCROLL_GO_ON);
7474 for (i = 0; i < MAX_PLAYERS; i++)
7475 stored_player[i].Frame++;
7479 /* for downwards compatibility, the following code emulates a fixed bug that
7480 occured when pushing elements (causing elements that just made their last
7481 pushing step to already (if possible) make their first falling step in the
7482 same game frame, which is bad); this code is also needed to use the famous
7483 "spring push bug" which is used in older levels and might be wanted to be
7484 used also in newer levels, but in this case the buggy pushing code is only
7485 affecting the "spring" element and no other elements */
7488 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7490 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7493 for (i = 0; i < MAX_PLAYERS; i++)
7495 struct PlayerInfo *player = &stored_player[i];
7500 if (player->active && player->is_pushing && player->is_moving &&
7502 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7503 Feld[x][y] == EL_SPRING))
7505 if (player->active && player->is_pushing && player->is_moving &&
7509 ContinueMoving(x, y);
7511 /* continue moving after pushing (this is actually a bug) */
7512 if (!IS_MOVING(x, y))
7521 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7523 Changed[x][y] = CE_BITMASK_DEFAULT;
7524 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7527 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7529 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7530 printf("GameActions(): This should never happen!\n");
7532 ChangePage[x][y] = -1;
7537 if (WasJustMoving[x][y] > 0)
7538 WasJustMoving[x][y]--;
7539 if (WasJustFalling[x][y] > 0)
7540 WasJustFalling[x][y]--;
7545 /* reset finished pushing action (not done in ContinueMoving() to allow
7546 continous pushing animation for elements with zero push delay) */
7547 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7549 ResetGfxAnimation(x, y);
7550 DrawLevelField(x, y);
7555 if (IS_BLOCKED(x, y))
7559 Blocked2Moving(x, y, &oldx, &oldy);
7560 if (!IS_MOVING(oldx, oldy))
7562 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7563 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7564 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7565 printf("GameActions(): This should never happen!\n");
7571 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7573 element = Feld[x][y];
7575 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7577 graphic = el2img(element);
7583 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7585 element = graphic = 0;
7589 if (graphic_info[graphic].anim_global_sync)
7590 GfxFrame[x][y] = FrameCounter;
7592 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7593 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7594 ResetRandomAnimationValue(x, y);
7596 SetRandomAnimationValue(x, y);
7599 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7602 if (IS_INACTIVE(element))
7604 if (IS_ANIMATED(graphic))
7605 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7611 /* this may take place after moving, so 'element' may have changed */
7613 if (IS_CHANGING(x, y))
7615 if (IS_CHANGING(x, y) &&
7616 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7620 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7621 element_info[element].event_page_nr[CE_DELAY]);
7623 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7626 element = Feld[x][y];
7627 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7631 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7636 element = Feld[x][y];
7637 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7639 if (element == EL_MOLE)
7640 printf("::: %d, %d, %d [%d]\n",
7641 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7645 if (element == EL_YAMYAM)
7646 printf("::: %d, %d, %d\n",
7647 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7651 if (IS_ANIMATED(graphic) &&
7655 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7658 if (element == EL_BUG)
7659 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7663 if (element == EL_MOLE)
7664 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7668 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7669 EdelsteinFunkeln(x, y);
7671 else if ((element == EL_ACID ||
7672 element == EL_EXIT_OPEN ||
7673 element == EL_SP_EXIT_OPEN ||
7674 element == EL_SP_TERMINAL ||
7675 element == EL_SP_TERMINAL_ACTIVE ||
7676 element == EL_EXTRA_TIME ||
7677 element == EL_SHIELD_NORMAL ||
7678 element == EL_SHIELD_DEADLY) &&
7679 IS_ANIMATED(graphic))
7680 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7681 else if (IS_MOVING(x, y))
7682 ContinueMoving(x, y);
7683 else if (IS_ACTIVE_BOMB(element))
7684 CheckDynamite(x, y);
7686 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7687 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7689 else if (element == EL_AMOEBA_GROWING)
7690 AmoebeWaechst(x, y);
7691 else if (element == EL_AMOEBA_SHRINKING)
7692 AmoebaDisappearing(x, y);
7694 #if !USE_NEW_AMOEBA_CODE
7695 else if (IS_AMOEBALIVE(element))
7696 AmoebeAbleger(x, y);
7699 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7701 else if (element == EL_EXIT_CLOSED)
7703 else if (element == EL_SP_EXIT_CLOSED)
7705 else if (element == EL_EXPANDABLE_WALL_GROWING)
7707 else if (element == EL_EXPANDABLE_WALL ||
7708 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7709 element == EL_EXPANDABLE_WALL_VERTICAL ||
7710 element == EL_EXPANDABLE_WALL_ANY)
7712 else if (element == EL_FLAMES)
7713 CheckForDragon(x, y);
7715 else if (IS_AUTO_CHANGING(element))
7716 ChangeElement(x, y);
7718 else if (element == EL_EXPLOSION)
7719 ; /* drawing of correct explosion animation is handled separately */
7720 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7721 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7724 /* this may take place after moving, so 'element' may have changed */
7725 if (IS_AUTO_CHANGING(Feld[x][y]))
7726 ChangeElement(x, y);
7729 if (IS_BELT_ACTIVE(element))
7730 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7732 if (game.magic_wall_active)
7734 int jx = local_player->jx, jy = local_player->jy;
7736 /* play the element sound at the position nearest to the player */
7737 if ((element == EL_MAGIC_WALL_FULL ||
7738 element == EL_MAGIC_WALL_ACTIVE ||
7739 element == EL_MAGIC_WALL_EMPTYING ||
7740 element == EL_BD_MAGIC_WALL_FULL ||
7741 element == EL_BD_MAGIC_WALL_ACTIVE ||
7742 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7743 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7751 #if USE_NEW_AMOEBA_CODE
7752 /* new experimental amoeba growth stuff */
7754 if (!(FrameCounter % 8))
7757 static unsigned long random = 1684108901;
7759 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7762 x = (random >> 10) % lev_fieldx;
7763 y = (random >> 20) % lev_fieldy;
7765 x = RND(lev_fieldx);
7766 y = RND(lev_fieldy);
7768 element = Feld[x][y];
7770 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7771 if (!IS_PLAYER(x,y) &&
7772 (element == EL_EMPTY ||
7773 element == EL_SAND ||
7774 element == EL_QUICKSAND_EMPTY ||
7775 element == EL_ACID_SPLASH_LEFT ||
7776 element == EL_ACID_SPLASH_RIGHT))
7778 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7779 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7780 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7781 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7782 Feld[x][y] = EL_AMOEBA_DROP;
7785 random = random * 129 + 1;
7791 if (game.explosions_delayed)
7794 game.explosions_delayed = FALSE;
7796 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7798 element = Feld[x][y];
7800 if (ExplodeField[x][y])
7801 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7802 else if (element == EL_EXPLOSION)
7803 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7805 ExplodeField[x][y] = EX_NO_EXPLOSION;
7808 game.explosions_delayed = TRUE;
7811 if (game.magic_wall_active)
7813 if (!(game.magic_wall_time_left % 4))
7815 int element = Feld[magic_wall_x][magic_wall_y];
7817 if (element == EL_BD_MAGIC_WALL_FULL ||
7818 element == EL_BD_MAGIC_WALL_ACTIVE ||
7819 element == EL_BD_MAGIC_WALL_EMPTYING)
7820 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7822 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7825 if (game.magic_wall_time_left > 0)
7827 game.magic_wall_time_left--;
7828 if (!game.magic_wall_time_left)
7830 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7832 element = Feld[x][y];
7834 if (element == EL_MAGIC_WALL_ACTIVE ||
7835 element == EL_MAGIC_WALL_FULL)
7837 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7838 DrawLevelField(x, y);
7840 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7841 element == EL_BD_MAGIC_WALL_FULL)
7843 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7844 DrawLevelField(x, y);
7848 game.magic_wall_active = FALSE;
7853 if (game.light_time_left > 0)
7855 game.light_time_left--;
7857 if (game.light_time_left == 0)
7858 RedrawAllLightSwitchesAndInvisibleElements();
7861 if (game.timegate_time_left > 0)
7863 game.timegate_time_left--;
7865 if (game.timegate_time_left == 0)
7866 CloseAllOpenTimegates();
7869 for (i = 0; i < MAX_PLAYERS; i++)
7871 struct PlayerInfo *player = &stored_player[i];
7873 if (SHIELD_ON(player))
7875 if (player->shield_deadly_time_left)
7876 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7877 else if (player->shield_normal_time_left)
7878 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7882 if (TimeFrames >= FRAMES_PER_SECOND)
7887 for (i = 0; i < MAX_PLAYERS; i++)
7889 struct PlayerInfo *player = &stored_player[i];
7891 if (SHIELD_ON(player))
7893 player->shield_normal_time_left--;
7895 if (player->shield_deadly_time_left > 0)
7896 player->shield_deadly_time_left--;
7900 if (tape.recording || tape.playing)
7901 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7907 if (TimeLeft <= 10 && setup.time_limit)
7908 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7910 DrawGameValue_Time(TimeLeft);
7912 if (!TimeLeft && setup.time_limit)
7913 for (i = 0; i < MAX_PLAYERS; i++)
7914 KillHero(&stored_player[i]);
7916 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7917 DrawGameValue_Time(TimePlayed);
7921 PlayAllPlayersSound();
7923 if (options.debug) /* calculate frames per second */
7925 static unsigned long fps_counter = 0;
7926 static int fps_frames = 0;
7927 unsigned long fps_delay_ms = Counter() - fps_counter;
7931 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7933 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7936 fps_counter = Counter();
7939 redraw_mask |= REDRAW_FPS;
7943 if (stored_player[0].jx != stored_player[0].last_jx ||
7944 stored_player[0].jy != stored_player[0].last_jy)
7945 printf("::: %d, %d, %d, %d, %d\n",
7946 stored_player[0].MovDir,
7947 stored_player[0].MovPos,
7948 stored_player[0].GfxPos,
7949 stored_player[0].Frame,
7950 stored_player[0].StepFrame);
7957 for (i = 0; i < MAX_PLAYERS; i++)
7960 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7962 stored_player[i].Frame += move_frames;
7964 if (stored_player[i].MovPos != 0)
7965 stored_player[i].StepFrame += move_frames;
7967 if (stored_player[i].drop_delay > 0)
7968 stored_player[i].drop_delay--;
7973 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7975 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7977 local_player->show_envelope = 0;
7982 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7984 int min_x = x, min_y = y, max_x = x, max_y = y;
7987 for (i = 0; i < MAX_PLAYERS; i++)
7989 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7991 if (!stored_player[i].active || &stored_player[i] == player)
7994 min_x = MIN(min_x, jx);
7995 min_y = MIN(min_y, jy);
7996 max_x = MAX(max_x, jx);
7997 max_y = MAX(max_y, jy);
8000 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8003 static boolean AllPlayersInVisibleScreen()
8007 for (i = 0; i < MAX_PLAYERS; i++)
8009 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8011 if (!stored_player[i].active)
8014 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8021 void ScrollLevel(int dx, int dy)
8023 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8026 BlitBitmap(drawto_field, drawto_field,
8027 FX + TILEX * (dx == -1) - softscroll_offset,
8028 FY + TILEY * (dy == -1) - softscroll_offset,
8029 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8030 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8031 FX + TILEX * (dx == 1) - softscroll_offset,
8032 FY + TILEY * (dy == 1) - softscroll_offset);
8036 x = (dx == 1 ? BX1 : BX2);
8037 for (y = BY1; y <= BY2; y++)
8038 DrawScreenField(x, y);
8043 y = (dy == 1 ? BY1 : BY2);
8044 for (x = BX1; x <= BX2; x++)
8045 DrawScreenField(x, y);
8048 redraw_mask |= REDRAW_FIELD;
8051 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8053 int nextx = x + dx, nexty = y + dy;
8054 int element = Feld[x][y];
8057 element != EL_SP_PORT_LEFT &&
8058 element != EL_SP_GRAVITY_PORT_LEFT &&
8059 element != EL_SP_PORT_HORIZONTAL &&
8060 element != EL_SP_PORT_ANY) ||
8062 element != EL_SP_PORT_RIGHT &&
8063 element != EL_SP_GRAVITY_PORT_RIGHT &&
8064 element != EL_SP_PORT_HORIZONTAL &&
8065 element != EL_SP_PORT_ANY) ||
8067 element != EL_SP_PORT_UP &&
8068 element != EL_SP_GRAVITY_PORT_UP &&
8069 element != EL_SP_PORT_VERTICAL &&
8070 element != EL_SP_PORT_ANY) ||
8072 element != EL_SP_PORT_DOWN &&
8073 element != EL_SP_GRAVITY_PORT_DOWN &&
8074 element != EL_SP_PORT_VERTICAL &&
8075 element != EL_SP_PORT_ANY) ||
8076 !IN_LEV_FIELD(nextx, nexty) ||
8077 !IS_FREE(nextx, nexty))
8083 static void CheckGravityMovement(struct PlayerInfo *player)
8085 if (game.gravity && !player->programmed_action)
8087 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
8088 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
8090 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
8091 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8092 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8093 int jx = player->jx, jy = player->jy;
8094 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8095 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8096 int new_jx = jx + dx, new_jy = jy + dy;
8097 boolean field_under_player_is_free =
8098 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8099 boolean player_is_moving_to_valid_field =
8100 (IN_LEV_FIELD(new_jx, new_jy) &&
8101 (Feld[new_jx][new_jy] == EL_SP_BASE ||
8102 Feld[new_jx][new_jy] == EL_SAND ||
8103 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8104 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
8105 /* !!! extend EL_SAND to anything diggable !!! */
8107 boolean player_is_standing_on_valid_field =
8108 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8109 (IS_WALKABLE(Feld[jx][jy]) &&
8110 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8112 if (field_under_player_is_free &&
8113 !player_is_standing_on_valid_field &&
8114 !player_is_moving_to_valid_field)
8115 player->programmed_action = MV_DOWN;
8121 -----------------------------------------------------------------------------
8122 dx, dy: direction (non-diagonal) to try to move the player to
8123 real_dx, real_dy: direction as read from input device (can be diagonal)
8126 boolean MovePlayerOneStep(struct PlayerInfo *player,
8127 int dx, int dy, int real_dx, int real_dy)
8130 static int change_sides[4][2] =
8132 /* enter side leave side */
8133 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8134 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8135 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8136 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8138 int move_direction = (dx == -1 ? MV_LEFT :
8139 dx == +1 ? MV_RIGHT :
8141 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8142 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
8143 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
8145 int jx = player->jx, jy = player->jy;
8146 int new_jx = jx + dx, new_jy = jy + dy;
8150 if (!player->active || (!dx && !dy))
8151 return MF_NO_ACTION;
8153 player->MovDir = (dx < 0 ? MV_LEFT :
8156 dy > 0 ? MV_DOWN : MV_NO_MOVING);
8158 if (!IN_LEV_FIELD(new_jx, new_jy))
8159 return MF_NO_ACTION;
8161 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8162 return MF_NO_ACTION;
8165 element = MovingOrBlocked2Element(new_jx, new_jy);
8167 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8170 if (DONT_RUN_INTO(element))
8172 if (element == EL_ACID && dx == 0 && dy == 1)
8174 SplashAcid(new_jx, new_jy);
8175 Feld[jx][jy] = EL_PLAYER_1;
8176 InitMovingField(jx, jy, MV_DOWN);
8177 Store[jx][jy] = EL_ACID;
8178 ContinueMoving(jx, jy);
8182 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8187 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8188 if (can_move != MF_MOVING)
8191 /* check if DigField() has caused relocation of the player */
8192 if (player->jx != jx || player->jy != jy)
8193 return MF_NO_ACTION;
8195 StorePlayer[jx][jy] = 0;
8196 player->last_jx = jx;
8197 player->last_jy = jy;
8198 player->jx = new_jx;
8199 player->jy = new_jy;
8200 StorePlayer[new_jx][new_jy] = player->element_nr;
8203 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8205 player->step_counter++;
8207 player->drop_delay = 0;
8209 PlayerVisit[jx][jy] = FrameCounter;
8211 ScrollPlayer(player, SCROLL_INIT);
8214 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8216 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
8217 CE_OTHER_GETS_LEFT);
8218 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
8219 CE_LEFT_BY_PLAYER, -1);
8222 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
8224 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
8225 enter_side, CE_OTHER_GETS_ENTERED);
8226 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
8227 CE_ENTERED_BY_PLAYER, -1);
8234 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8236 int jx = player->jx, jy = player->jy;
8237 int old_jx = jx, old_jy = jy;
8238 int moved = MF_NO_ACTION;
8241 if (!player->active)
8246 if (player->MovPos == 0)
8248 player->is_moving = FALSE;
8249 player->is_digging = FALSE;
8250 player->is_collecting = FALSE;
8251 player->is_snapping = FALSE;
8252 player->is_pushing = FALSE;
8258 if (!player->active || (!dx && !dy))
8263 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8267 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8268 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
8272 /* remove the last programmed player action */
8273 player->programmed_action = 0;
8277 /* should only happen if pre-1.2 tape recordings are played */
8278 /* this is only for backward compatibility */
8280 int original_move_delay_value = player->move_delay_value;
8283 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8287 /* scroll remaining steps with finest movement resolution */
8288 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8290 while (player->MovPos)
8292 ScrollPlayer(player, SCROLL_GO_ON);
8293 ScrollScreen(NULL, SCROLL_GO_ON);
8299 player->move_delay_value = original_move_delay_value;
8302 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
8304 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8305 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8309 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8310 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8316 if (moved & MF_MOVING && !ScreenMovPos &&
8317 (player == local_player || !options.network))
8319 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8320 int offset = (setup.scroll_delay ? 3 : 0);
8322 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8324 /* actual player has left the screen -- scroll in that direction */
8325 if (jx != old_jx) /* player has moved horizontally */
8326 scroll_x += (jx - old_jx);
8327 else /* player has moved vertically */
8328 scroll_y += (jy - old_jy);
8332 if (jx != old_jx) /* player has moved horizontally */
8334 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8335 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8336 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8338 /* don't scroll over playfield boundaries */
8339 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8340 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8342 /* don't scroll more than one field at a time */
8343 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8345 /* don't scroll against the player's moving direction */
8346 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8347 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8348 scroll_x = old_scroll_x;
8350 else /* player has moved vertically */
8352 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8353 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8354 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8356 /* don't scroll over playfield boundaries */
8357 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8358 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8360 /* don't scroll more than one field at a time */
8361 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8363 /* don't scroll against the player's moving direction */
8364 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8365 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8366 scroll_y = old_scroll_y;
8370 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8372 if (!options.network && !AllPlayersInVisibleScreen())
8374 scroll_x = old_scroll_x;
8375 scroll_y = old_scroll_y;
8379 ScrollScreen(player, SCROLL_INIT);
8380 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8387 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8389 if (!(moved & MF_MOVING) && !player->is_pushing)
8394 player->StepFrame = 0;
8396 if (moved & MF_MOVING)
8398 if (old_jx != jx && old_jy == jy)
8399 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8400 else if (old_jx == jx && old_jy != jy)
8401 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8403 DrawLevelField(jx, jy); /* for "crumbled sand" */
8405 player->last_move_dir = player->MovDir;
8406 player->is_moving = TRUE;
8408 player->is_snapping = FALSE;
8412 player->is_switching = FALSE;
8415 player->is_dropping = FALSE;
8420 static int change_sides[4][2] =
8422 /* enter side leave side */
8423 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8424 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8425 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8426 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8428 int move_direction = player->MovDir;
8429 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
8430 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
8433 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8435 CheckTriggeredElementPlayerChange(old_jx, old_jy, Feld[old_jx][old_jy],
8436 player->index_nr, leave_side,
8437 CE_OTHER_GETS_LEFT);
8438 CheckElementPlayerChange(old_jx, old_jy, Feld[old_jx][old_jy],
8439 player->index_nr, leave_side,
8440 CE_LEFT_BY_PLAYER, -1);
8443 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8445 CheckTriggeredElementPlayerChange(jx, jy, Feld[jx][jy],
8446 player->index_nr, enter_side,
8447 CE_OTHER_GETS_ENTERED);
8448 CheckElementPlayerChange(jx, jy, Feld[jx][jy], player->index_nr,
8449 enter_side, CE_ENTERED_BY_PLAYER, -1);
8460 CheckGravityMovement(player);
8463 player->last_move_dir = MV_NO_MOVING;
8465 player->is_moving = FALSE;
8468 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8470 TestIfHeroTouchesBadThing(jx, jy);
8471 TestIfPlayerTouchesCustomElement(jx, jy);
8474 if (!player->active)
8480 void ScrollPlayer(struct PlayerInfo *player, int mode)
8482 int jx = player->jx, jy = player->jy;
8483 int last_jx = player->last_jx, last_jy = player->last_jy;
8484 int move_stepsize = TILEX / player->move_delay_value;
8486 if (!player->active || !player->MovPos)
8489 if (mode == SCROLL_INIT)
8491 player->actual_frame_counter = FrameCounter;
8492 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8494 if (Feld[last_jx][last_jy] == EL_EMPTY)
8495 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8503 else if (!FrameReached(&player->actual_frame_counter, 1))
8506 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8507 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8509 if (!player->block_last_field &&
8510 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8511 Feld[last_jx][last_jy] = EL_EMPTY;
8513 /* before DrawPlayer() to draw correct player graphic for this case */
8514 if (player->MovPos == 0)
8515 CheckGravityMovement(player);
8518 DrawPlayer(player); /* needed here only to cleanup last field */
8521 if (player->MovPos == 0) /* player reached destination field */
8524 if (player->move_delay_reset_counter > 0)
8526 player->move_delay_reset_counter--;
8528 if (player->move_delay_reset_counter == 0)
8530 /* continue with normal speed after quickly moving through gate */
8531 HALVE_PLAYER_SPEED(player);
8533 /* be able to make the next move without delay */
8534 player->move_delay = 0;
8538 if (IS_PASSABLE(Feld[last_jx][last_jy]))
8540 /* continue with normal speed after quickly moving through gate */
8541 HALVE_PLAYER_SPEED(player);
8543 /* be able to make the next move without delay */
8544 player->move_delay = 0;
8548 if (player->block_last_field &&
8549 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8550 Feld[last_jx][last_jy] = EL_EMPTY;
8552 player->last_jx = jx;
8553 player->last_jy = jy;
8555 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8556 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8557 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8559 DrawPlayer(player); /* needed here only to cleanup last field */
8562 if (local_player->friends_still_needed == 0 ||
8563 IS_SP_ELEMENT(Feld[jx][jy]))
8564 player->LevelSolved = player->GameOver = TRUE;
8567 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8569 TestIfHeroTouchesBadThing(jx, jy);
8570 TestIfPlayerTouchesCustomElement(jx, jy);
8572 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8575 if (!player->active)
8579 if (tape.single_step && tape.recording && !tape.pausing &&
8580 !player->programmed_action)
8581 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8585 void ScrollScreen(struct PlayerInfo *player, int mode)
8587 static unsigned long screen_frame_counter = 0;
8589 if (mode == SCROLL_INIT)
8591 /* set scrolling step size according to actual player's moving speed */
8592 ScrollStepSize = TILEX / player->move_delay_value;
8594 screen_frame_counter = FrameCounter;
8595 ScreenMovDir = player->MovDir;
8596 ScreenMovPos = player->MovPos;
8597 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8600 else if (!FrameReached(&screen_frame_counter, 1))
8605 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8606 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8607 redraw_mask |= REDRAW_FIELD;
8610 ScreenMovDir = MV_NO_MOVING;
8613 void TestIfPlayerTouchesCustomElement(int x, int y)
8615 static int xy[4][2] =
8622 static int change_sides[4][2] =
8624 /* center side border side */
8625 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8626 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8627 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8628 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8630 static int touch_dir[4] =
8637 int center_element = Feld[x][y]; /* should always be non-moving! */
8640 for (i = 0; i < NUM_DIRECTIONS; i++)
8642 int xx = x + xy[i][0];
8643 int yy = y + xy[i][1];
8644 int center_side = change_sides[i][0];
8645 int border_side = change_sides[i][1];
8648 if (!IN_LEV_FIELD(xx, yy))
8651 if (IS_PLAYER(x, y))
8653 struct PlayerInfo *player = PLAYERINFO(x, y);
8655 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8656 border_element = Feld[xx][yy]; /* may be moving! */
8657 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8658 border_element = Feld[xx][yy];
8659 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8660 border_element = MovingOrBlocked2Element(xx, yy);
8662 continue; /* center and border element do not touch */
8664 CheckTriggeredElementPlayerChange(xx, yy, border_element,
8665 player->index_nr, border_side,
8666 CE_OTHER_GETS_TOUCHED);
8667 CheckElementPlayerChange(xx, yy, border_element, player->index_nr,
8668 border_side, CE_TOUCHED_BY_PLAYER, -1);
8670 else if (IS_PLAYER(xx, yy))
8672 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8674 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8676 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8677 continue; /* center and border element do not touch */
8680 CheckTriggeredElementPlayerChange(x, y, center_element,
8681 player->index_nr, center_side,
8682 CE_OTHER_GETS_TOUCHED);
8683 CheckElementPlayerChange(x, y, center_element, player->index_nr,
8684 center_side, CE_TOUCHED_BY_PLAYER, -1);
8691 void TestIfElementTouchesCustomElement(int x, int y)
8693 static int xy[4][2] =
8700 static int change_sides[4][2] =
8702 /* center side border side */
8703 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8704 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8705 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8706 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8708 static int touch_dir[4] =
8715 boolean change_center_element = FALSE;
8716 int center_element_change_page = 0;
8717 int center_element = Feld[x][y]; /* should always be non-moving! */
8720 for (i = 0; i < NUM_DIRECTIONS; i++)
8722 int xx = x + xy[i][0];
8723 int yy = y + xy[i][1];
8724 int center_side = change_sides[i][0];
8725 int border_side = change_sides[i][1];
8728 if (!IN_LEV_FIELD(xx, yy))
8731 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8732 border_element = Feld[xx][yy]; /* may be moving! */
8733 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8734 border_element = Feld[xx][yy];
8735 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8736 border_element = MovingOrBlocked2Element(xx, yy);
8738 continue; /* center and border element do not touch */
8740 /* check for change of center element (but change it only once) */
8741 if (IS_CUSTOM_ELEMENT(center_element) &&
8742 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8743 !change_center_element)
8745 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8747 struct ElementChangeInfo *change =
8748 &element_info[center_element].change_page[j];
8750 if (change->can_change &&
8751 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8752 change->sides & border_side &&
8754 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8756 change->trigger_element == border_element
8760 change_center_element = TRUE;
8761 center_element_change_page = j;
8768 /* check for change of border element */
8769 if (IS_CUSTOM_ELEMENT(border_element) &&
8770 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8772 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8774 struct ElementChangeInfo *change =
8775 &element_info[border_element].change_page[j];
8777 if (change->can_change &&
8778 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8779 change->sides & center_side &&
8781 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8783 change->trigger_element == center_element
8787 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8788 CE_OTHER_IS_TOUCHING, j);
8795 if (change_center_element)
8796 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8797 CE_OTHER_IS_TOUCHING, center_element_change_page);
8800 void TestIfElementHitsCustomElement(int x, int y, int direction)
8802 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8803 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8804 int hitx = x + dx, hity = y + dy;
8805 int hitting_element = Feld[x][y];
8807 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8808 !IS_FREE(hitx, hity) &&
8809 (!IS_MOVING(hitx, hity) ||
8810 MovDir[hitx][hity] != direction ||
8811 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8814 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8818 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8822 CheckElementSideChange(x, y, hitting_element,
8823 direction, CE_HITTING_SOMETHING, -1);
8825 if (IN_LEV_FIELD(hitx, hity))
8827 int opposite_direction = MV_DIR_OPPOSITE(direction);
8828 int hitting_side = direction;
8829 int touched_side = opposite_direction;
8830 int touched_element = MovingOrBlocked2Element(hitx, hity);
8832 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8833 MovDir[hitx][hity] != direction ||
8834 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8843 CheckElementSideChange(hitx, hity, touched_element,
8844 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8846 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8847 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8849 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8851 struct ElementChangeInfo *change =
8852 &element_info[hitting_element].change_page[i];
8854 if (change->can_change &&
8855 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8856 change->sides & touched_side &&
8859 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8861 change->trigger_element == touched_element
8865 CheckElementSideChange(x, y, hitting_element,
8866 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8872 if (IS_CUSTOM_ELEMENT(touched_element) &&
8873 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8875 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8877 struct ElementChangeInfo *change =
8878 &element_info[touched_element].change_page[i];
8880 if (change->can_change &&
8881 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8882 change->sides & hitting_side &&
8884 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8886 change->trigger_element == hitting_element
8890 CheckElementSideChange(hitx, hity, touched_element,
8891 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8900 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8902 int i, kill_x = -1, kill_y = -1;
8903 static int test_xy[4][2] =
8910 static int test_dir[4] =
8918 for (i = 0; i < NUM_DIRECTIONS; i++)
8920 int test_x, test_y, test_move_dir, test_element;
8922 test_x = good_x + test_xy[i][0];
8923 test_y = good_y + test_xy[i][1];
8924 if (!IN_LEV_FIELD(test_x, test_y))
8928 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8931 test_element = Feld[test_x][test_y];
8933 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8936 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8937 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8939 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8940 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8948 if (kill_x != -1 || kill_y != -1)
8950 if (IS_PLAYER(good_x, good_y))
8952 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8954 if (player->shield_deadly_time_left > 0)
8955 Bang(kill_x, kill_y);
8956 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
8960 Bang(good_x, good_y);
8964 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8966 int i, kill_x = -1, kill_y = -1;
8967 int bad_element = Feld[bad_x][bad_y];
8968 static int test_xy[4][2] =
8975 static int touch_dir[4] =
8982 static int test_dir[4] =
8990 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8993 for (i = 0; i < NUM_DIRECTIONS; i++)
8995 int test_x, test_y, test_move_dir, test_element;
8997 test_x = bad_x + test_xy[i][0];
8998 test_y = bad_y + test_xy[i][1];
8999 if (!IN_LEV_FIELD(test_x, test_y))
9003 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9005 test_element = Feld[test_x][test_y];
9007 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9008 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9010 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9011 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9013 /* good thing is player or penguin that does not move away */
9014 if (IS_PLAYER(test_x, test_y))
9016 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9018 if (bad_element == EL_ROBOT && player->is_moving)
9019 continue; /* robot does not kill player if he is moving */
9021 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9023 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9024 continue; /* center and border element do not touch */
9031 else if (test_element == EL_PENGUIN)
9040 if (kill_x != -1 || kill_y != -1)
9042 if (IS_PLAYER(kill_x, kill_y))
9044 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9046 if (player->shield_deadly_time_left > 0)
9048 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9052 Bang(kill_x, kill_y);
9056 void TestIfHeroTouchesBadThing(int x, int y)
9058 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9061 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
9063 TestIfGoodThingHitsBadThing(x, y, move_dir);
9066 void TestIfBadThingTouchesHero(int x, int y)
9068 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9071 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
9073 TestIfBadThingHitsGoodThing(x, y, move_dir);
9076 void TestIfFriendTouchesBadThing(int x, int y)
9078 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9081 void TestIfBadThingTouchesFriend(int x, int y)
9083 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9086 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9088 int i, kill_x = bad_x, kill_y = bad_y;
9089 static int xy[4][2] =
9097 for (i = 0; i < NUM_DIRECTIONS; i++)
9101 x = bad_x + xy[i][0];
9102 y = bad_y + xy[i][1];
9103 if (!IN_LEV_FIELD(x, y))
9106 element = Feld[x][y];
9107 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9108 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9116 if (kill_x != bad_x || kill_y != bad_y)
9120 void KillHero(struct PlayerInfo *player)
9122 int jx = player->jx, jy = player->jy;
9124 if (!player->active)
9127 /* remove accessible field at the player's position */
9128 Feld[jx][jy] = EL_EMPTY;
9130 /* deactivate shield (else Bang()/Explode() would not work right) */
9131 player->shield_normal_time_left = 0;
9132 player->shield_deadly_time_left = 0;
9138 static void KillHeroUnlessEnemyProtected(int x, int y)
9140 if (!PLAYER_ENEMY_PROTECTED(x, y))
9141 KillHero(PLAYERINFO(x, y));
9144 static void KillHeroUnlessExplosionProtected(int x, int y)
9146 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9147 KillHero(PLAYERINFO(x, y));
9150 void BuryHero(struct PlayerInfo *player)
9152 int jx = player->jx, jy = player->jy;
9154 if (!player->active)
9158 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9160 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
9162 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9164 player->GameOver = TRUE;
9168 void RemoveHero(struct PlayerInfo *player)
9170 int jx = player->jx, jy = player->jy;
9171 int i, found = FALSE;
9173 player->present = FALSE;
9174 player->active = FALSE;
9176 if (!ExplodeField[jx][jy])
9177 StorePlayer[jx][jy] = 0;
9179 for (i = 0; i < MAX_PLAYERS; i++)
9180 if (stored_player[i].active)
9184 AllPlayersGone = TRUE;
9191 =============================================================================
9192 checkDiagonalPushing()
9193 -----------------------------------------------------------------------------
9194 check if diagonal input device direction results in pushing of object
9195 (by checking if the alternative direction is walkable, diggable, ...)
9196 =============================================================================
9199 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9200 int x, int y, int real_dx, int real_dy)
9202 int jx, jy, dx, dy, xx, yy;
9204 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9207 /* diagonal direction: check alternative direction */
9212 xx = jx + (dx == 0 ? real_dx : 0);
9213 yy = jy + (dy == 0 ? real_dy : 0);
9215 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9219 =============================================================================
9221 -----------------------------------------------------------------------------
9222 x, y: field next to player (non-diagonal) to try to dig to
9223 real_dx, real_dy: direction as read from input device (can be diagonal)
9224 =============================================================================
9227 int DigField(struct PlayerInfo *player,
9228 int oldx, int oldy, int x, int y,
9229 int real_dx, int real_dy, int mode)
9231 static int change_sides[4] =
9233 CH_SIDE_RIGHT, /* moving left */
9234 CH_SIDE_LEFT, /* moving right */
9235 CH_SIDE_BOTTOM, /* moving up */
9236 CH_SIDE_TOP, /* moving down */
9239 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
9241 int jx = oldx, jy = oldy;
9242 int dx = x - jx, dy = y - jy;
9243 int nextx = x + dx, nexty = y + dy;
9244 int move_direction = (dx == -1 ? MV_LEFT :
9245 dx == +1 ? MV_RIGHT :
9247 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9248 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9249 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
9250 int old_element = Feld[jx][jy];
9253 if (player->MovPos == 0)
9255 player->is_digging = FALSE;
9256 player->is_collecting = FALSE;
9259 if (player->MovPos == 0) /* last pushing move finished */
9260 player->is_pushing = FALSE;
9262 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9264 player->is_switching = FALSE;
9265 player->push_delay = 0;
9267 return MF_NO_ACTION;
9270 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9271 return MF_NO_ACTION;
9276 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
9278 if (IS_TUBE(Feld[jx][jy]) ||
9279 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
9283 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
9284 int tube_leave_directions[][2] =
9286 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9287 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9288 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9289 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
9290 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
9291 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
9292 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
9293 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
9294 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
9295 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
9296 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
9297 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
9300 while (tube_leave_directions[i][0] != tube_element)
9303 if (tube_leave_directions[i][0] == -1) /* should not happen */
9307 if (!(tube_leave_directions[i][1] & move_direction))
9308 return MF_NO_ACTION; /* tube has no opening in this direction */
9313 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9314 old_element = Back[jx][jy];
9318 if (IS_WALKABLE(old_element) &&
9319 !(element_info[old_element].access_direction & move_direction))
9320 return MF_NO_ACTION; /* field has no opening in this direction */
9322 element = Feld[x][y];
9324 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9325 game.engine_version >= VERSION_IDENT(2,2,0,0))
9326 return MF_NO_ACTION;
9330 case EL_SP_PORT_LEFT:
9331 case EL_SP_PORT_RIGHT:
9333 case EL_SP_PORT_DOWN:
9334 case EL_SP_PORT_HORIZONTAL:
9335 case EL_SP_PORT_VERTICAL:
9336 case EL_SP_PORT_ANY:
9337 case EL_SP_GRAVITY_PORT_LEFT:
9338 case EL_SP_GRAVITY_PORT_RIGHT:
9339 case EL_SP_GRAVITY_PORT_UP:
9340 case EL_SP_GRAVITY_PORT_DOWN:
9342 if (!canEnterSupaplexPort(x, y, dx, dy))
9343 return MF_NO_ACTION;
9346 element != EL_SP_PORT_LEFT &&
9347 element != EL_SP_GRAVITY_PORT_LEFT &&
9348 element != EL_SP_PORT_HORIZONTAL &&
9349 element != EL_SP_PORT_ANY) ||
9351 element != EL_SP_PORT_RIGHT &&
9352 element != EL_SP_GRAVITY_PORT_RIGHT &&
9353 element != EL_SP_PORT_HORIZONTAL &&
9354 element != EL_SP_PORT_ANY) ||
9356 element != EL_SP_PORT_UP &&
9357 element != EL_SP_GRAVITY_PORT_UP &&
9358 element != EL_SP_PORT_VERTICAL &&
9359 element != EL_SP_PORT_ANY) ||
9361 element != EL_SP_PORT_DOWN &&
9362 element != EL_SP_GRAVITY_PORT_DOWN &&
9363 element != EL_SP_PORT_VERTICAL &&
9364 element != EL_SP_PORT_ANY) ||
9365 !IN_LEV_FIELD(nextx, nexty) ||
9366 !IS_FREE(nextx, nexty))
9367 return MF_NO_ACTION;
9370 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9371 element == EL_SP_GRAVITY_PORT_RIGHT ||
9372 element == EL_SP_GRAVITY_PORT_UP ||
9373 element == EL_SP_GRAVITY_PORT_DOWN)
9374 game.gravity = !game.gravity;
9376 /* automatically move to the next field with double speed */
9377 player->programmed_action = move_direction;
9379 if (player->move_delay_reset_counter == 0)
9381 player->move_delay_reset_counter = 2; /* two double speed steps */
9383 DOUBLE_PLAYER_SPEED(player);
9386 player->move_delay_reset_counter = 2;
9388 DOUBLE_PLAYER_SPEED(player);
9391 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9396 case EL_TUBE_VERTICAL:
9397 case EL_TUBE_HORIZONTAL:
9398 case EL_TUBE_VERTICAL_LEFT:
9399 case EL_TUBE_VERTICAL_RIGHT:
9400 case EL_TUBE_HORIZONTAL_UP:
9401 case EL_TUBE_HORIZONTAL_DOWN:
9402 case EL_TUBE_LEFT_UP:
9403 case EL_TUBE_LEFT_DOWN:
9404 case EL_TUBE_RIGHT_UP:
9405 case EL_TUBE_RIGHT_DOWN:
9408 int tube_enter_directions[][2] =
9410 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9411 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9412 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9413 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
9414 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
9415 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
9416 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
9417 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
9418 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
9419 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
9420 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
9421 { -1, MV_NO_MOVING }
9424 while (tube_enter_directions[i][0] != element)
9427 if (tube_enter_directions[i][0] == -1) /* should not happen */
9431 if (!(tube_enter_directions[i][1] & move_direction))
9432 return MF_NO_ACTION; /* tube has no opening in this direction */
9434 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9441 if (IS_WALKABLE(element))
9443 int sound_action = ACTION_WALKING;
9445 if (!(element_info[element].access_direction & opposite_direction))
9446 return MF_NO_ACTION; /* field not accessible from this direction */
9448 if (element >= EL_GATE_1 && element <= EL_GATE_4)
9450 if (!player->key[element - EL_GATE_1])
9451 return MF_NO_ACTION;
9453 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9455 if (!player->key[element - EL_GATE_1_GRAY])
9456 return MF_NO_ACTION;
9458 else if (element == EL_EXIT_OPEN ||
9459 element == EL_SP_EXIT_OPEN ||
9460 element == EL_SP_EXIT_OPENING)
9462 sound_action = ACTION_PASSING; /* player is passing exit */
9464 else if (element == EL_EMPTY)
9466 sound_action = ACTION_MOVING; /* nothing to walk on */
9469 /* play sound from background or player, whatever is available */
9470 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9471 PlayLevelSoundElementAction(x, y, element, sound_action);
9473 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9477 else if (IS_PASSABLE(element))
9479 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9480 return MF_NO_ACTION;
9482 if (IS_CUSTOM_ELEMENT(element) &&
9483 !(element_info[element].access_direction & opposite_direction))
9484 return MF_NO_ACTION; /* field not accessible from this direction */
9487 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9488 return MF_NO_ACTION;
9491 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9493 if (!player->key[element - EL_EM_GATE_1])
9494 return MF_NO_ACTION;
9496 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9498 if (!player->key[element - EL_EM_GATE_1_GRAY])
9499 return MF_NO_ACTION;
9502 /* automatically move to the next field with double speed */
9503 player->programmed_action = move_direction;
9505 if (player->move_delay_reset_counter == 0)
9507 player->move_delay_reset_counter = 2; /* two double speed steps */
9509 DOUBLE_PLAYER_SPEED(player);
9512 player->move_delay_reset_counter = 2;
9514 DOUBLE_PLAYER_SPEED(player);
9517 PlayLevelSoundAction(x, y, ACTION_PASSING);
9521 else if (IS_DIGGABLE(element))
9525 if (mode != DF_SNAP)
9528 GfxElement[x][y] = GFX_ELEMENT(element);
9531 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9533 player->is_digging = TRUE;
9536 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9538 CheckTriggeredElementPlayerChange(x, y, element,
9539 player->index_nr, CH_SIDE_ANY,
9540 CE_OTHER_GETS_DIGGED);
9543 if (mode == DF_SNAP)
9544 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9549 else if (IS_COLLECTIBLE(element))
9553 if (mode != DF_SNAP)
9555 GfxElement[x][y] = element;
9556 player->is_collecting = TRUE;
9559 if (element == EL_SPEED_PILL)
9560 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9561 else if (element == EL_EXTRA_TIME && level.time > 0)
9564 DrawGameValue_Time(TimeLeft);
9566 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9568 player->shield_normal_time_left += 10;
9569 if (element == EL_SHIELD_DEADLY)
9570 player->shield_deadly_time_left += 10;
9572 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9574 if (player->inventory_size < MAX_INVENTORY_SIZE)
9575 player->inventory_element[player->inventory_size++] = element;
9577 DrawGameValue_Dynamite(local_player->inventory_size);
9579 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9581 player->dynabomb_count++;
9582 player->dynabombs_left++;
9584 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9586 player->dynabomb_size++;
9588 else if (element == EL_DYNABOMB_INCREASE_POWER)
9590 player->dynabomb_xl = TRUE;
9592 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9593 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9595 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9596 element - EL_KEY_1 : element - EL_EM_KEY_1);
9598 player->key[key_nr] = TRUE;
9600 DrawGameValue_Keys(player);
9602 redraw_mask |= REDRAW_DOOR_1;
9604 else if (IS_ENVELOPE(element))
9607 player->show_envelope = element;
9609 ShowEnvelope(element - EL_ENVELOPE_1);
9612 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9616 for (i = 0; i < element_info[element].collect_count; i++)
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_info[element].collect_count > 0)
9624 local_player->gems_still_needed -=
9625 element_info[element].collect_count;
9626 if (local_player->gems_still_needed < 0)
9627 local_player->gems_still_needed = 0;
9629 DrawGameValue_Emeralds(local_player->gems_still_needed);
9632 RaiseScoreElement(element);
9633 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9635 CheckTriggeredElementPlayerChange(x, y, element, player->index_nr,
9637 CE_OTHER_GETS_COLLECTED);
9640 if (mode == DF_SNAP)
9641 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9646 else if (IS_PUSHABLE(element))
9648 if (mode == DF_SNAP && element != EL_BD_ROCK)
9649 return MF_NO_ACTION;
9651 if (CAN_FALL(element) && dy)
9652 return MF_NO_ACTION;
9654 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9655 !(element == EL_SPRING && level.use_spring_bug))
9656 return MF_NO_ACTION;
9659 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9660 ((move_direction & MV_VERTICAL &&
9661 ((element_info[element].move_pattern & MV_LEFT &&
9662 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9663 (element_info[element].move_pattern & MV_RIGHT &&
9664 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9665 (move_direction & MV_HORIZONTAL &&
9666 ((element_info[element].move_pattern & MV_UP &&
9667 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9668 (element_info[element].move_pattern & MV_DOWN &&
9669 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9670 return MF_NO_ACTION;
9674 /* do not push elements already moving away faster than player */
9675 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9676 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9677 return MF_NO_ACTION;
9679 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9680 return MF_NO_ACTION;
9684 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9686 if (player->push_delay_value == -1)
9687 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9689 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9691 if (!player->is_pushing)
9692 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9696 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9697 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9698 !player_is_pushing))
9699 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9702 if (!player->is_pushing &&
9703 game.engine_version >= VERSION_IDENT(2,2,0,7))
9704 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9708 printf("::: push delay: %ld [%d, %d] [%d]\n",
9709 player->push_delay_value, FrameCounter, game.engine_version,
9710 player->is_pushing);
9713 player->is_pushing = TRUE;
9715 if (!(IN_LEV_FIELD(nextx, nexty) &&
9716 (IS_FREE(nextx, nexty) ||
9717 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9718 IS_SB_ELEMENT(element)))))
9719 return MF_NO_ACTION;
9721 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9722 return MF_NO_ACTION;
9724 if (player->push_delay == 0) /* new pushing; restart delay */
9725 player->push_delay = FrameCounter;
9727 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9728 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9729 element != EL_SPRING && element != EL_BALLOON)
9731 /* make sure that there is no move delay before next try to push */
9732 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9733 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9735 return MF_NO_ACTION;
9739 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9742 if (IS_SB_ELEMENT(element))
9744 if (element == EL_SOKOBAN_FIELD_FULL)
9746 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9747 local_player->sokobanfields_still_needed++;
9750 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9752 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9753 local_player->sokobanfields_still_needed--;
9756 Feld[x][y] = EL_SOKOBAN_OBJECT;
9758 if (Back[x][y] == Back[nextx][nexty])
9759 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9760 else if (Back[x][y] != 0)
9761 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9764 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9767 if (local_player->sokobanfields_still_needed == 0 &&
9768 game.emulation == EMU_SOKOBAN)
9770 player->LevelSolved = player->GameOver = TRUE;
9771 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9775 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9777 InitMovingField(x, y, move_direction);
9778 GfxAction[x][y] = ACTION_PUSHING;
9780 if (mode == DF_SNAP)
9781 ContinueMoving(x, y);
9783 MovPos[x][y] = (dx != 0 ? dx : dy);
9785 Pushed[x][y] = TRUE;
9786 Pushed[nextx][nexty] = TRUE;
9788 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9789 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9791 player->push_delay_value = -1; /* get new value later */
9793 CheckTriggeredElementPlayerChange(x, y, element, player->index_nr,
9794 dig_side, CE_OTHER_GETS_PUSHED);
9795 CheckElementPlayerChange(x, y, element, player->index_nr, dig_side,
9796 CE_PUSHED_BY_PLAYER, -1);
9800 else if (IS_SWITCHABLE(element))
9802 if (PLAYER_SWITCHING(player, x, y))
9805 player->is_switching = TRUE;
9806 player->switch_x = x;
9807 player->switch_y = y;
9809 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9811 if (element == EL_ROBOT_WHEEL)
9813 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9817 DrawLevelField(x, y);
9819 else if (element == EL_SP_TERMINAL)
9823 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9825 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9827 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9828 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9831 else if (IS_BELT_SWITCH(element))
9833 ToggleBeltSwitch(x, y);
9835 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9836 element == EL_SWITCHGATE_SWITCH_DOWN)
9838 ToggleSwitchgateSwitch(x, y);
9840 else if (element == EL_LIGHT_SWITCH ||
9841 element == EL_LIGHT_SWITCH_ACTIVE)
9843 ToggleLightSwitch(x, y);
9846 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9847 SND_LIGHT_SWITCH_ACTIVATING :
9848 SND_LIGHT_SWITCH_DEACTIVATING);
9851 else if (element == EL_TIMEGATE_SWITCH)
9853 ActivateTimegateSwitch(x, y);
9855 else if (element == EL_BALLOON_SWITCH_LEFT ||
9856 element == EL_BALLOON_SWITCH_RIGHT ||
9857 element == EL_BALLOON_SWITCH_UP ||
9858 element == EL_BALLOON_SWITCH_DOWN ||
9859 element == EL_BALLOON_SWITCH_ANY)
9861 if (element == EL_BALLOON_SWITCH_ANY)
9862 game.balloon_dir = move_direction;
9864 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9865 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9866 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9867 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9870 else if (element == EL_LAMP)
9872 Feld[x][y] = EL_LAMP_ACTIVE;
9873 local_player->lights_still_needed--;
9875 DrawLevelField(x, y);
9877 else if (element == EL_TIME_ORB_FULL)
9879 Feld[x][y] = EL_TIME_ORB_EMPTY;
9881 DrawGameValue_Time(TimeLeft);
9883 DrawLevelField(x, y);
9886 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9894 if (!PLAYER_SWITCHING(player, x, y))
9896 player->is_switching = TRUE;
9897 player->switch_x = x;
9898 player->switch_y = y;
9900 CheckTriggeredElementPlayerChange(x, y, element, player->index_nr,
9901 dig_side, CE_OTHER_IS_SWITCHING);
9902 CheckElementPlayerChange(x, y, element, player->index_nr, dig_side,
9906 CheckTriggeredElementPlayerChange(x, y, element, player->index_nr,
9907 dig_side, CE_OTHER_GETS_PRESSED);
9908 CheckElementPlayerChange(x, y, element, player->index_nr, dig_side,
9909 CE_PRESSED_BY_PLAYER, -1);
9912 return MF_NO_ACTION;
9915 player->push_delay = 0;
9917 if (Feld[x][y] != element) /* really digged/collected something */
9918 player->is_collecting = !player->is_digging;
9923 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9925 int jx = player->jx, jy = player->jy;
9926 int x = jx + dx, y = jy + dy;
9927 int snap_direction = (dx == -1 ? MV_LEFT :
9928 dx == +1 ? MV_RIGHT :
9930 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9932 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9935 if (!player->active || !IN_LEV_FIELD(x, y))
9943 if (player->MovPos == 0)
9944 player->is_pushing = FALSE;
9946 player->is_snapping = FALSE;
9948 if (player->MovPos == 0)
9950 player->is_moving = FALSE;
9951 player->is_digging = FALSE;
9952 player->is_collecting = FALSE;
9958 if (player->is_snapping)
9961 player->MovDir = snap_direction;
9964 if (player->MovPos == 0)
9967 player->is_moving = FALSE;
9968 player->is_digging = FALSE;
9969 player->is_collecting = FALSE;
9972 player->is_dropping = FALSE;
9974 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9977 player->is_snapping = TRUE;
9980 if (player->MovPos == 0)
9983 player->is_moving = FALSE;
9984 player->is_digging = FALSE;
9985 player->is_collecting = FALSE;
9988 DrawLevelField(x, y);
9994 boolean DropElement(struct PlayerInfo *player)
9996 int jx = player->jx, jy = player->jy;
9997 int old_element = Feld[jx][jy];
10000 /* check if player is active, not moving and ready to drop */
10001 if (!player->active || player->MovPos || player->drop_delay > 0)
10004 /* check if player has anything that can be dropped */
10005 if (player->inventory_size == 0 && player->dynabombs_left == 0)
10008 /* check if anything can be dropped at the current position */
10009 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10012 /* collected custom elements can only be dropped on empty fields */
10013 if (player->inventory_size > 0 &&
10014 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
10015 && old_element != EL_EMPTY)
10018 if (old_element != EL_EMPTY)
10019 Back[jx][jy] = old_element; /* store old element on this field */
10021 ResetGfxAnimation(jx, jy);
10022 ResetRandomAnimationValue(jx, jy);
10024 if (player->inventory_size > 0)
10026 player->inventory_size--;
10027 new_element = player->inventory_element[player->inventory_size];
10029 if (new_element == EL_DYNAMITE)
10030 new_element = EL_DYNAMITE_ACTIVE;
10031 else if (new_element == EL_SP_DISK_RED)
10032 new_element = EL_SP_DISK_RED_ACTIVE;
10034 Feld[jx][jy] = new_element;
10036 DrawGameValue_Dynamite(local_player->inventory_size);
10038 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10039 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10041 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10044 /* needed if previous element just changed to "empty" in the last frame */
10045 Changed[jx][jy] = 0; /* allow another change */
10048 CheckTriggeredElementPlayerChange(jx, jy, new_element, player->index_nr,
10049 CH_SIDE_ANY, CE_OTHER_GETS_DROPPED);
10050 CheckElementPlayerChange(jx, jy, new_element, player->index_nr,
10051 CH_SIDE_ANY, CE_DROPPED_BY_PLAYER, -1);
10053 TestIfElementTouchesCustomElement(jx, jy);
10055 else /* player is dropping a dyna bomb */
10057 player->dynabombs_left--;
10058 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
10060 Feld[jx][jy] = new_element;
10062 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10063 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10065 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10072 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
10075 InitField_WithBug1(jx, jy, FALSE);
10077 InitField(jx, jy, FALSE);
10078 if (CAN_MOVE(Feld[jx][jy]))
10079 InitMovDir(jx, jy);
10083 new_element = Feld[jx][jy];
10085 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10086 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10088 int move_stepsize = element_info[new_element].move_stepsize;
10089 int direction, dx, dy, nextx, nexty;
10091 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10092 MovDir[jx][jy] = player->MovDir;
10094 direction = MovDir[jx][jy];
10095 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10096 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10100 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
10103 WasJustMoving[jx][jy] = 3;
10105 InitMovingField(jx, jy, direction);
10106 ContinueMoving(jx, jy);
10111 Changed[jx][jy] = 0; /* allow another change */
10114 TestIfElementHitsCustomElement(jx, jy, direction);
10116 CheckElementSideChange(jx, jy, new_element,
10117 direction, CE_HITTING_SOMETHING, -1);
10121 player->drop_delay = 2 * TILEX / move_stepsize + 1;
10125 player->drop_delay = 8 + 8 + 8;
10130 player->is_dropping = TRUE;
10136 /* ------------------------------------------------------------------------- */
10137 /* game sound playing functions */
10138 /* ------------------------------------------------------------------------- */
10140 static int *loop_sound_frame = NULL;
10141 static int *loop_sound_volume = NULL;
10143 void InitPlayLevelSound()
10145 int num_sounds = getSoundListSize();
10147 checked_free(loop_sound_frame);
10148 checked_free(loop_sound_volume);
10150 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10151 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10154 static void PlayLevelSound(int x, int y, int nr)
10156 int sx = SCREENX(x), sy = SCREENY(y);
10157 int volume, stereo_position;
10158 int max_distance = 8;
10159 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10161 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10162 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10165 if (!IN_LEV_FIELD(x, y) ||
10166 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10167 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10170 volume = SOUND_MAX_VOLUME;
10172 if (!IN_SCR_FIELD(sx, sy))
10174 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10175 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10177 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10180 stereo_position = (SOUND_MAX_LEFT +
10181 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10182 (SCR_FIELDX + 2 * max_distance));
10184 if (IS_LOOP_SOUND(nr))
10186 /* This assures that quieter loop sounds do not overwrite louder ones,
10187 while restarting sound volume comparison with each new game frame. */
10189 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10192 loop_sound_volume[nr] = volume;
10193 loop_sound_frame[nr] = FrameCounter;
10196 PlaySoundExt(nr, volume, stereo_position, type);
10199 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10201 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10202 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10203 y < LEVELY(BY1) ? LEVELY(BY1) :
10204 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10208 static void PlayLevelSoundAction(int x, int y, int action)
10210 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10213 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10215 int sound_effect = element_info[element].sound[action];
10217 if (sound_effect != SND_UNDEFINED)
10218 PlayLevelSound(x, y, sound_effect);
10221 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10224 int sound_effect = element_info[element].sound[action];
10226 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10227 PlayLevelSound(x, y, sound_effect);
10230 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10232 int sound_effect = element_info[Feld[x][y]].sound[action];
10234 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10235 PlayLevelSound(x, y, sound_effect);
10238 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10240 int sound_effect = element_info[Feld[x][y]].sound[action];
10242 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10243 StopSound(sound_effect);
10246 static void PlayLevelMusic()
10248 if (levelset.music[level_nr] != MUS_UNDEFINED)
10249 PlayMusic(levelset.music[level_nr]); /* from config file */
10251 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10254 void RaiseScore(int value)
10256 local_player->score += value;
10258 DrawGameValue_Score(local_player->score);
10261 void RaiseScoreElement(int element)
10266 case EL_BD_DIAMOND:
10267 case EL_EMERALD_YELLOW:
10268 case EL_EMERALD_RED:
10269 case EL_EMERALD_PURPLE:
10270 case EL_SP_INFOTRON:
10271 RaiseScore(level.score[SC_EMERALD]);
10274 RaiseScore(level.score[SC_DIAMOND]);
10277 RaiseScore(level.score[SC_CRYSTAL]);
10280 RaiseScore(level.score[SC_PEARL]);
10283 case EL_BD_BUTTERFLY:
10284 case EL_SP_ELECTRON:
10285 RaiseScore(level.score[SC_BUG]);
10288 case EL_BD_FIREFLY:
10289 case EL_SP_SNIKSNAK:
10290 RaiseScore(level.score[SC_SPACESHIP]);
10293 case EL_DARK_YAMYAM:
10294 RaiseScore(level.score[SC_YAMYAM]);
10297 RaiseScore(level.score[SC_ROBOT]);
10300 RaiseScore(level.score[SC_PACMAN]);
10303 RaiseScore(level.score[SC_NUT]);
10306 case EL_SP_DISK_RED:
10307 case EL_DYNABOMB_INCREASE_NUMBER:
10308 case EL_DYNABOMB_INCREASE_SIZE:
10309 case EL_DYNABOMB_INCREASE_POWER:
10310 RaiseScore(level.score[SC_DYNAMITE]);
10312 case EL_SHIELD_NORMAL:
10313 case EL_SHIELD_DEADLY:
10314 RaiseScore(level.score[SC_SHIELD]);
10316 case EL_EXTRA_TIME:
10317 RaiseScore(level.score[SC_TIME_BONUS]);
10323 RaiseScore(level.score[SC_KEY]);
10326 RaiseScore(element_info[element].collect_score);
10331 void RequestQuitGame(boolean ask_if_really_quit)
10333 if (AllPlayersGone ||
10334 !ask_if_really_quit ||
10335 level_editor_test_game ||
10336 Request("Do you really want to quit the game ?",
10337 REQ_ASK | REQ_STAY_CLOSED))
10339 #if defined(PLATFORM_UNIX)
10340 if (options.network)
10341 SendToServer_StopPlaying();
10345 game_status = GAME_MODE_MAIN;
10351 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10356 /* ---------- new game button stuff ---------------------------------------- */
10358 /* graphic position values for game buttons */
10359 #define GAME_BUTTON_XSIZE 30
10360 #define GAME_BUTTON_YSIZE 30
10361 #define GAME_BUTTON_XPOS 5
10362 #define GAME_BUTTON_YPOS 215
10363 #define SOUND_BUTTON_XPOS 5
10364 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10366 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10367 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10368 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10369 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10370 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10371 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10378 } gamebutton_info[NUM_GAME_BUTTONS] =
10381 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10386 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10387 GAME_CTRL_ID_PAUSE,
10391 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10396 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10397 SOUND_CTRL_ID_MUSIC,
10398 "background music on/off"
10401 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10402 SOUND_CTRL_ID_LOOPS,
10403 "sound loops on/off"
10406 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10407 SOUND_CTRL_ID_SIMPLE,
10408 "normal sounds on/off"
10412 void CreateGameButtons()
10416 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10418 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10419 struct GadgetInfo *gi;
10422 unsigned long event_mask;
10423 int gd_xoffset, gd_yoffset;
10424 int gd_x1, gd_x2, gd_y1, gd_y2;
10427 gd_xoffset = gamebutton_info[i].x;
10428 gd_yoffset = gamebutton_info[i].y;
10429 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10430 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10432 if (id == GAME_CTRL_ID_STOP ||
10433 id == GAME_CTRL_ID_PAUSE ||
10434 id == GAME_CTRL_ID_PLAY)
10436 button_type = GD_TYPE_NORMAL_BUTTON;
10438 event_mask = GD_EVENT_RELEASED;
10439 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10440 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10444 button_type = GD_TYPE_CHECK_BUTTON;
10446 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10447 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10448 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10449 event_mask = GD_EVENT_PRESSED;
10450 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10451 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10454 gi = CreateGadget(GDI_CUSTOM_ID, id,
10455 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10456 GDI_X, DX + gd_xoffset,
10457 GDI_Y, DY + gd_yoffset,
10458 GDI_WIDTH, GAME_BUTTON_XSIZE,
10459 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10460 GDI_TYPE, button_type,
10461 GDI_STATE, GD_BUTTON_UNPRESSED,
10462 GDI_CHECKED, checked,
10463 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10464 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10465 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10466 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10467 GDI_EVENT_MASK, event_mask,
10468 GDI_CALLBACK_ACTION, HandleGameButtons,
10472 Error(ERR_EXIT, "cannot create gadget");
10474 game_gadget[id] = gi;
10478 void FreeGameButtons()
10482 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10483 FreeGadget(game_gadget[i]);
10486 static void MapGameButtons()
10490 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10491 MapGadget(game_gadget[i]);
10494 void UnmapGameButtons()
10498 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10499 UnmapGadget(game_gadget[i]);
10502 static void HandleGameButtons(struct GadgetInfo *gi)
10504 int id = gi->custom_id;
10506 if (game_status != GAME_MODE_PLAYING)
10511 case GAME_CTRL_ID_STOP:
10512 RequestQuitGame(TRUE);
10515 case GAME_CTRL_ID_PAUSE:
10516 if (options.network)
10518 #if defined(PLATFORM_UNIX)
10520 SendToServer_ContinuePlaying();
10522 SendToServer_PausePlaying();
10526 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10529 case GAME_CTRL_ID_PLAY:
10532 #if defined(PLATFORM_UNIX)
10533 if (options.network)
10534 SendToServer_ContinuePlaying();
10538 tape.pausing = FALSE;
10539 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10544 case SOUND_CTRL_ID_MUSIC:
10545 if (setup.sound_music)
10547 setup.sound_music = FALSE;
10550 else if (audio.music_available)
10552 setup.sound = setup.sound_music = TRUE;
10554 SetAudioMode(setup.sound);
10560 case SOUND_CTRL_ID_LOOPS:
10561 if (setup.sound_loops)
10562 setup.sound_loops = FALSE;
10563 else if (audio.loops_available)
10565 setup.sound = setup.sound_loops = TRUE;
10566 SetAudioMode(setup.sound);
10570 case SOUND_CTRL_ID_SIMPLE:
10571 if (setup.sound_simple)
10572 setup.sound_simple = FALSE;
10573 else if (audio.sound_available)
10575 setup.sound = setup.sound_simple = TRUE;
10576 SetAudioMode(setup.sound);