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_TYPE_NONE 0
47 #define EX_TYPE_NORMAL (1 << 0)
48 #define EX_TYPE_CENTER (1 << 1)
49 #define EX_TYPE_BORDER (1 << 2)
50 #define EX_TYPE_CROSS (1 << 3)
51 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
53 /* special positions in the game control window (relative to control window) */
56 #define XX_EMERALDS 29
57 #define YY_EMERALDS 54
58 #define XX_DYNAMITE 29
59 #define YY_DYNAMITE 89
68 /* special positions in the game control window (relative to main window) */
69 #define DX_LEVEL (DX + XX_LEVEL)
70 #define DY_LEVEL (DY + YY_LEVEL)
71 #define DX_EMERALDS (DX + XX_EMERALDS)
72 #define DY_EMERALDS (DY + YY_EMERALDS)
73 #define DX_DYNAMITE (DX + XX_DYNAMITE)
74 #define DY_DYNAMITE (DY + YY_DYNAMITE)
75 #define DX_KEYS (DX + XX_KEYS)
76 #define DY_KEYS (DY + YY_KEYS)
77 #define DX_SCORE (DX + XX_SCORE)
78 #define DY_SCORE (DY + YY_SCORE)
79 #define DX_TIME1 (DX + XX_TIME1)
80 #define DX_TIME2 (DX + XX_TIME2)
81 #define DY_TIME (DY + YY_TIME)
83 /* values for initial player move delay (initial delay counter value) */
84 #define INITIAL_MOVE_DELAY_OFF -1
85 #define INITIAL_MOVE_DELAY_ON 0
87 /* values for player movement speed (which is in fact a delay value) */
88 #define MOVE_DELAY_NORMAL_SPEED 8
89 #define MOVE_DELAY_HIGH_SPEED 4
91 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
92 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
93 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
94 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
96 /* values for other actions */
97 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
99 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
101 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
102 RND(element_info[e].push_delay_random))
103 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
104 RND(element_info[e].drop_delay_random))
105 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
106 RND(element_info[e].move_delay_random))
107 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
108 (element_info[e].move_delay_random))
110 #define GET_TARGET_ELEMENT(e, ch) \
111 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
112 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
114 #define CAN_GROW_INTO(e) \
115 (e == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
117 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
118 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
121 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
122 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
123 (CAN_MOVE_INTO_ACID(e) && \
124 Feld[x][y] == EL_ACID) || \
127 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
128 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
129 (CAN_MOVE_INTO_ACID(e) && \
130 Feld[x][y] == EL_ACID) || \
133 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
134 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
136 (CAN_MOVE_INTO_ACID(e) && \
137 Feld[x][y] == EL_ACID) || \
138 (DONT_COLLIDE_WITH(e) && \
140 !PLAYER_ENEMY_PROTECTED(x, y))))
143 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
144 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
146 (DONT_COLLIDE_WITH(e) && \
148 !PLAYER_ENEMY_PROTECTED(x, y))))
151 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
152 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
155 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
156 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
158 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
159 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
163 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
166 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
167 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
171 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
172 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
174 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
175 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
177 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
178 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
180 #define PIG_CAN_ENTER_FIELD(e, x, y) \
181 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
183 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
184 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
185 IS_FOOD_PENGUIN(Feld[x][y])))
186 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
187 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
189 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
192 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
193 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
197 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
198 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
199 (CAN_MOVE_INTO_ACID(e) && \
200 Feld[x][y] == EL_ACID) || \
201 Feld[x][y] == EL_DIAMOND))
203 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
204 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
205 (CAN_MOVE_INTO_ACID(e) && \
206 Feld[x][y] == EL_ACID) || \
207 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
209 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
210 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
211 (CAN_MOVE_INTO_ACID(e) && \
212 Feld[x][y] == EL_ACID) || \
213 IS_AMOEBOID(Feld[x][y])))
215 #define PIG_CAN_ENTER_FIELD(e, x, y) \
216 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
217 (CAN_MOVE_INTO_ACID(e) && \
218 Feld[x][y] == EL_ACID) || \
219 IS_FOOD_PIG(Feld[x][y])))
221 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
222 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
223 (CAN_MOVE_INTO_ACID(e) && \
224 Feld[x][y] == EL_ACID) || \
225 IS_FOOD_PENGUIN(Feld[x][y]) || \
226 Feld[x][y] == EL_EXIT_OPEN))
228 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
229 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
230 (CAN_MOVE_INTO_ACID(e) && \
231 Feld[x][y] == EL_ACID)))
233 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
234 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
235 (CAN_MOVE_INTO_ACID(e) && \
236 Feld[x][y] == EL_ACID) || \
239 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
240 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
241 (CAN_MOVE_INTO_ACID(e) && \
242 Feld[x][y] == EL_ACID)))
246 #define GROUP_NR(e) ((e) - EL_GROUP_START)
247 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
248 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
249 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
251 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
252 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
255 #define CE_ENTER_FIELD_COND(e, x, y) \
256 (!IS_PLAYER(x, y) && \
257 (Feld[x][y] == EL_ACID || \
258 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
260 #define CE_ENTER_FIELD_COND(e, x, y) \
261 (!IS_PLAYER(x, y) && \
262 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
265 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
266 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
268 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
269 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
271 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
272 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
273 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
274 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
276 /* game button identifiers */
277 #define GAME_CTRL_ID_STOP 0
278 #define GAME_CTRL_ID_PAUSE 1
279 #define GAME_CTRL_ID_PLAY 2
280 #define SOUND_CTRL_ID_MUSIC 3
281 #define SOUND_CTRL_ID_LOOPS 4
282 #define SOUND_CTRL_ID_SIMPLE 5
284 #define NUM_GAME_BUTTONS 6
287 /* forward declaration for internal use */
289 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
290 static boolean MovePlayer(struct PlayerInfo *, int, int);
291 static void ScrollPlayer(struct PlayerInfo *, int);
292 static void ScrollScreen(struct PlayerInfo *, int);
294 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
296 static void InitBeltMovement(void);
297 static void CloseAllOpenTimegates(void);
298 static void CheckGravityMovement(struct PlayerInfo *);
299 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
300 static void KillHeroUnlessEnemyProtected(int, int);
301 static void KillHeroUnlessExplosionProtected(int, int);
303 static void TestIfPlayerTouchesCustomElement(int, int);
304 static void TestIfElementTouchesCustomElement(int, int);
305 static void TestIfElementHitsCustomElement(int, int, int);
307 static void TestIfElementSmashesCustomElement(int, int, int);
310 static void ChangeElement(int, int, int);
312 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
313 #define CheckTriggeredElementChange(x, y, e, ev) \
314 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
316 #define CheckTriggeredElementChangePlayer(x, y, e, ev, p, s) \
317 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
318 #define CheckTriggeredElementChangeSide(x, y, e, ev, s) \
319 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
320 #define CheckTriggeredElementChangePage(x, y, e, ev, p) \
321 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
324 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
325 #define CheckElementChange(x, y, e, te, ev) \
326 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
327 #define CheckElementChangePlayer(x, y, e, ev, p, s) \
328 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
329 #define CheckElementChangeSide(x, y, e, te, ev, s) \
330 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
331 #define CheckElementChangePage(x, y, e, te, ev, p) \
332 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
334 static void PlayLevelSound(int, int, int);
335 static void PlayLevelSoundNearest(int, int, int);
336 static void PlayLevelSoundAction(int, int, int);
337 static void PlayLevelSoundElementAction(int, int, int, int);
338 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
339 static void PlayLevelSoundActionIfLoop(int, int, int);
340 static void StopLevelSoundActionIfLoop(int, int, int);
341 static void PlayLevelMusic();
343 static void MapGameButtons();
344 static void HandleGameButtons(struct GadgetInfo *);
346 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
349 /* ------------------------------------------------------------------------- */
350 /* definition of elements that automatically change to other elements after */
351 /* a specified time, eventually calling a function when changing */
352 /* ------------------------------------------------------------------------- */
354 /* forward declaration for changer functions */
355 static void InitBuggyBase(int x, int y);
356 static void WarnBuggyBase(int x, int y);
358 static void InitTrap(int x, int y);
359 static void ActivateTrap(int x, int y);
360 static void ChangeActiveTrap(int x, int y);
362 static void InitRobotWheel(int x, int y);
363 static void RunRobotWheel(int x, int y);
364 static void StopRobotWheel(int x, int y);
366 static void InitTimegateWheel(int x, int y);
367 static void RunTimegateWheel(int x, int y);
369 struct ChangingElementInfo
374 void (*pre_change_function)(int x, int y);
375 void (*change_function)(int x, int y);
376 void (*post_change_function)(int x, int y);
379 static struct ChangingElementInfo change_delay_list[] =
430 EL_SWITCHGATE_OPENING,
438 EL_SWITCHGATE_CLOSING,
439 EL_SWITCHGATE_CLOSED,
471 EL_ACID_SPLASH_RIGHT,
480 EL_SP_BUGGY_BASE_ACTIVATING,
487 EL_SP_BUGGY_BASE_ACTIVATING,
488 EL_SP_BUGGY_BASE_ACTIVE,
495 EL_SP_BUGGY_BASE_ACTIVE,
519 EL_ROBOT_WHEEL_ACTIVE,
527 EL_TIMEGATE_SWITCH_ACTIVE,
548 int push_delay_fixed, push_delay_random;
553 { EL_BALLOON, 0, 0 },
555 { EL_SOKOBAN_OBJECT, 2, 0 },
556 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
557 { EL_SATELLITE, 2, 0 },
558 { EL_SP_DISK_YELLOW, 2, 0 },
560 { EL_UNDEFINED, 0, 0 },
568 move_stepsize_list[] =
570 { EL_AMOEBA_DROP, 2 },
571 { EL_AMOEBA_DROPPING, 2 },
572 { EL_QUICKSAND_FILLING, 1 },
573 { EL_QUICKSAND_EMPTYING, 1 },
574 { EL_MAGIC_WALL_FILLING, 2 },
575 { EL_BD_MAGIC_WALL_FILLING, 2 },
576 { EL_MAGIC_WALL_EMPTYING, 2 },
577 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
587 collect_count_list[] =
590 { EL_BD_DIAMOND, 1 },
591 { EL_EMERALD_YELLOW, 1 },
592 { EL_EMERALD_RED, 1 },
593 { EL_EMERALD_PURPLE, 1 },
595 { EL_SP_INFOTRON, 1 },
607 access_direction_list[] =
609 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
610 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
611 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
612 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
613 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
614 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
615 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
616 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
617 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
618 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
619 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
621 { EL_SP_PORT_LEFT, MV_RIGHT },
622 { EL_SP_PORT_RIGHT, MV_LEFT },
623 { EL_SP_PORT_UP, MV_DOWN },
624 { EL_SP_PORT_DOWN, MV_UP },
625 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
626 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
627 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
628 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
629 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
630 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
631 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
633 { EL_UNDEFINED, MV_NO_MOVING }
636 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
638 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
639 CH_EVENT_BIT(CE_DELAY))
640 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
641 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
642 IS_JUST_CHANGING(x, y))
644 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
647 void GetPlayerConfig()
649 if (!audio.sound_available)
650 setup.sound_simple = FALSE;
652 if (!audio.loops_available)
653 setup.sound_loops = FALSE;
655 if (!audio.music_available)
656 setup.sound_music = FALSE;
658 if (!video.fullscreen_available)
659 setup.fullscreen = FALSE;
661 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
663 SetAudioMode(setup.sound);
667 static int getBeltNrFromBeltElement(int element)
669 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
670 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
671 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
674 static int getBeltNrFromBeltActiveElement(int element)
676 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
677 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
678 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
681 static int getBeltNrFromBeltSwitchElement(int element)
683 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
684 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
685 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
688 static int getBeltDirNrFromBeltSwitchElement(int element)
690 static int belt_base_element[4] =
692 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
693 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
694 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
695 EL_CONVEYOR_BELT_4_SWITCH_LEFT
698 int belt_nr = getBeltNrFromBeltSwitchElement(element);
699 int belt_dir_nr = element - belt_base_element[belt_nr];
701 return (belt_dir_nr % 3);
704 static int getBeltDirFromBeltSwitchElement(int element)
706 static int belt_move_dir[3] =
713 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
715 return belt_move_dir[belt_dir_nr];
718 static void InitPlayerField(int x, int y, int element, boolean init_game)
720 if (element == EL_SP_MURPHY)
724 if (stored_player[0].present)
726 Feld[x][y] = EL_SP_MURPHY_CLONE;
732 stored_player[0].use_murphy_graphic = TRUE;
735 Feld[x][y] = EL_PLAYER_1;
741 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
742 int jx = player->jx, jy = player->jy;
744 player->present = TRUE;
746 player->block_last_field = (element == EL_SP_MURPHY ?
747 level.sp_block_last_field :
748 level.block_last_field);
750 if (!options.network || player->connected)
752 player->active = TRUE;
754 /* remove potentially duplicate players */
755 if (StorePlayer[jx][jy] == Feld[x][y])
756 StorePlayer[jx][jy] = 0;
758 StorePlayer[x][y] = Feld[x][y];
762 printf("Player %d activated.\n", player->element_nr);
763 printf("[Local player is %d and currently %s.]\n",
764 local_player->element_nr,
765 local_player->active ? "active" : "not active");
769 Feld[x][y] = EL_EMPTY;
770 player->jx = player->last_jx = x;
771 player->jy = player->last_jy = y;
775 static void InitField(int x, int y, boolean init_game)
777 int element = Feld[x][y];
786 InitPlayerField(x, y, element, init_game);
789 case EL_SOKOBAN_FIELD_PLAYER:
790 element = Feld[x][y] = EL_PLAYER_1;
791 InitField(x, y, init_game);
793 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
794 InitField(x, y, init_game);
797 case EL_SOKOBAN_FIELD_EMPTY:
798 local_player->sokobanfields_still_needed++;
802 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
803 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
804 else if (x > 0 && Feld[x-1][y] == EL_ACID)
805 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
806 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
807 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
808 else if (y > 0 && Feld[x][y-1] == EL_ACID)
809 Feld[x][y] = EL_ACID_POOL_BOTTOM;
810 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
811 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
819 case EL_SPACESHIP_RIGHT:
820 case EL_SPACESHIP_UP:
821 case EL_SPACESHIP_LEFT:
822 case EL_SPACESHIP_DOWN:
824 case EL_BD_BUTTERFLY_RIGHT:
825 case EL_BD_BUTTERFLY_UP:
826 case EL_BD_BUTTERFLY_LEFT:
827 case EL_BD_BUTTERFLY_DOWN:
828 case EL_BD_BUTTERFLY:
829 case EL_BD_FIREFLY_RIGHT:
830 case EL_BD_FIREFLY_UP:
831 case EL_BD_FIREFLY_LEFT:
832 case EL_BD_FIREFLY_DOWN:
834 case EL_PACMAN_RIGHT:
858 if (y == lev_fieldy - 1)
860 Feld[x][y] = EL_AMOEBA_GROWING;
861 Store[x][y] = EL_AMOEBA_WET;
865 case EL_DYNAMITE_ACTIVE:
866 case EL_SP_DISK_RED_ACTIVE:
867 case EL_DYNABOMB_PLAYER_1_ACTIVE:
868 case EL_DYNABOMB_PLAYER_2_ACTIVE:
869 case EL_DYNABOMB_PLAYER_3_ACTIVE:
870 case EL_DYNABOMB_PLAYER_4_ACTIVE:
875 local_player->lights_still_needed++;
879 local_player->friends_still_needed++;
884 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
889 Feld[x][y] = EL_EMPTY;
894 case EL_EM_KEY_1_FILE:
895 Feld[x][y] = EL_EM_KEY_1;
897 case EL_EM_KEY_2_FILE:
898 Feld[x][y] = EL_EM_KEY_2;
900 case EL_EM_KEY_3_FILE:
901 Feld[x][y] = EL_EM_KEY_3;
903 case EL_EM_KEY_4_FILE:
904 Feld[x][y] = EL_EM_KEY_4;
908 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
909 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
910 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
911 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
912 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
913 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
914 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
915 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
916 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
917 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
918 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
919 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
922 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
923 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
924 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
926 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
928 game.belt_dir[belt_nr] = belt_dir;
929 game.belt_dir_nr[belt_nr] = belt_dir_nr;
931 else /* more than one switch -- set it like the first switch */
933 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
938 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
940 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
943 case EL_LIGHT_SWITCH_ACTIVE:
945 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
949 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
951 else if (IS_GROUP_ELEMENT(element))
953 struct ElementGroupInfo *group = element_info[element].group;
954 int last_anim_random_frame = gfx.anim_random_frame;
957 if (group->choice_mode == ANIM_RANDOM)
958 gfx.anim_random_frame = RND(group->num_elements_resolved);
960 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
961 group->choice_mode, 0,
964 if (group->choice_mode == ANIM_RANDOM)
965 gfx.anim_random_frame = last_anim_random_frame;
969 Feld[x][y] = group->element_resolved[element_pos];
971 InitField(x, y, init_game);
977 static inline void InitField_WithBug1(int x, int y, boolean init_game)
979 InitField(x, y, init_game);
981 /* not needed to call InitMovDir() -- already done by InitField()! */
982 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
983 CAN_MOVE(Feld[x][y]))
987 static inline void InitField_WithBug2(int x, int y, boolean init_game)
989 int old_element = Feld[x][y];
991 InitField(x, y, init_game);
993 /* not needed to call InitMovDir() -- already done by InitField()! */
994 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
995 CAN_MOVE(old_element) &&
996 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
999 /* this case is in fact a combination of not less than three bugs:
1000 first, it calls InitMovDir() for elements that can move, although this is
1001 already done by InitField(); then, it checks the element that was at this
1002 field _before_ the call to InitField() (which can change it); lastly, it
1003 was not called for "mole with direction" elements, which were treated as
1004 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1008 inline void DrawGameValue_Emeralds(int value)
1010 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1013 inline void DrawGameValue_Dynamite(int value)
1015 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1018 inline void DrawGameValue_Keys(struct PlayerInfo *player)
1022 for (i = 0; i < MAX_KEYS; i++)
1024 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1025 el2edimg(EL_KEY_1 + i));
1028 inline void DrawGameValue_Score(int value)
1030 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1033 inline void DrawGameValue_Time(int value)
1036 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1038 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1041 inline void DrawGameValue_Level(int value)
1044 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1047 /* misuse area for displaying emeralds to draw bigger level number */
1048 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1049 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1051 /* now copy it to the area for displaying level number */
1052 BlitBitmap(drawto, drawto,
1053 DX_EMERALDS, DY_EMERALDS + 1,
1054 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1055 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1056 DX_LEVEL - 1, DY_LEVEL + 1);
1058 /* restore the area for displaying emeralds */
1059 DrawGameValue_Emeralds(local_player->gems_still_needed);
1061 /* yes, this is all really ugly :-) */
1065 void DrawGameDoorValues()
1069 DrawGameValue_Level(level_nr);
1071 for (i = 0; i < MAX_PLAYERS; i++)
1072 DrawGameValue_Keys(&stored_player[i]);
1074 DrawGameValue_Emeralds(local_player->gems_still_needed);
1075 DrawGameValue_Dynamite(local_player->inventory_size);
1076 DrawGameValue_Score(local_player->score);
1077 DrawGameValue_Time(TimeLeft);
1080 static void resolve_group_element(int group_element, int recursion_depth)
1082 static int group_nr;
1083 static struct ElementGroupInfo *group;
1084 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1087 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1089 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1090 group_element - EL_GROUP_START + 1);
1092 /* replace element which caused too deep recursion by question mark */
1093 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1098 if (recursion_depth == 0) /* initialization */
1100 group = element_info[group_element].group;
1101 group_nr = group_element - EL_GROUP_START;
1103 group->num_elements_resolved = 0;
1104 group->choice_pos = 0;
1107 for (i = 0; i < actual_group->num_elements; i++)
1109 int element = actual_group->element[i];
1111 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1114 if (IS_GROUP_ELEMENT(element))
1115 resolve_group_element(element, recursion_depth + 1);
1118 group->element_resolved[group->num_elements_resolved++] = element;
1119 element_info[element].in_group[group_nr] = TRUE;
1124 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1126 printf("::: group %d: %d resolved elements\n",
1127 group_element - EL_GROUP_START, group->num_elements_resolved);
1128 for (i = 0; i < group->num_elements_resolved; i++)
1129 printf("::: - %d ['%s']\n", group->element_resolved[i],
1130 element_info[group->element_resolved[i]].token_name);
1137 =============================================================================
1139 -----------------------------------------------------------------------------
1140 initialize game engine due to level / tape version number
1141 =============================================================================
1144 static void InitGameEngine()
1148 /* set game engine from tape file when re-playing, else from level file */
1149 game.engine_version = (tape.playing ? tape.engine_version :
1150 level.game_version);
1152 /* dynamically adjust element properties according to game engine version */
1153 InitElementPropertiesEngine(game.engine_version);
1156 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1157 printf(" tape version == %06d [%s] [file: %06d]\n",
1158 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1160 printf(" => game.engine_version == %06d\n", game.engine_version);
1163 /* ---------- recursively resolve group elements ------------------------- */
1165 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1166 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1167 element_info[i].in_group[j] = FALSE;
1169 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1170 resolve_group_element(EL_GROUP_START + i, 0);
1172 /* ---------- initialize player's initial move delay --------------------- */
1174 /* dynamically adjust player properties according to game engine version */
1175 game.initial_move_delay =
1176 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1177 INITIAL_MOVE_DELAY_OFF);
1179 /* dynamically adjust player properties according to level information */
1180 game.initial_move_delay_value =
1181 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1183 /* ---------- initialize player's initial push delay --------------------- */
1185 /* dynamically adjust player properties according to game engine version */
1186 game.initial_push_delay_value =
1187 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1189 /* ---------- initialize changing elements ------------------------------- */
1191 /* initialize changing elements information */
1192 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1194 struct ElementInfo *ei = &element_info[i];
1196 /* this pointer might have been changed in the level editor */
1197 ei->change = &ei->change_page[0];
1199 if (!IS_CUSTOM_ELEMENT(i))
1201 ei->change->target_element = EL_EMPTY_SPACE;
1202 ei->change->delay_fixed = 0;
1203 ei->change->delay_random = 0;
1204 ei->change->delay_frames = 1;
1207 ei->change_events = CE_BITMASK_DEFAULT;
1208 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1210 ei->event_page_nr[j] = 0;
1211 ei->event_page[j] = &ei->change_page[0];
1215 /* add changing elements from pre-defined list */
1216 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1218 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1219 struct ElementInfo *ei = &element_info[ch_delay->element];
1221 ei->change->target_element = ch_delay->target_element;
1222 ei->change->delay_fixed = ch_delay->change_delay;
1224 ei->change->pre_change_function = ch_delay->pre_change_function;
1225 ei->change->change_function = ch_delay->change_function;
1226 ei->change->post_change_function = ch_delay->post_change_function;
1228 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1231 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1236 /* add change events from custom element configuration */
1237 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1239 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1241 for (j = 0; j < ei->num_change_pages; j++)
1243 if (!ei->change_page[j].can_change)
1246 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1248 /* only add event page for the first page found with this event */
1249 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1250 !(ei->change_events & CH_EVENT_BIT(k)))
1252 ei->change_events |= CH_EVENT_BIT(k);
1253 ei->event_page_nr[k] = j;
1254 ei->event_page[k] = &ei->change_page[j];
1262 /* add change events from custom element configuration */
1263 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1265 int element = EL_CUSTOM_START + i;
1267 /* only add custom elements that change after fixed/random frame delay */
1268 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1269 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1273 /* ---------- initialize run-time trigger player and element ------------- */
1275 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1277 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1279 for (j = 0; j < ei->num_change_pages; j++)
1281 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1282 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1286 /* ---------- initialize trigger events ---------------------------------- */
1288 /* initialize trigger events information */
1289 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1290 trigger_events[i] = EP_BITMASK_DEFAULT;
1293 /* add trigger events from element change event properties */
1294 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1296 struct ElementInfo *ei = &element_info[i];
1298 for (j = 0; j < ei->num_change_pages; j++)
1300 if (!ei->change_page[j].can_change)
1303 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1305 int trigger_element = ei->change_page[j].trigger_element;
1307 if (IS_GROUP_ELEMENT(trigger_element))
1309 struct ElementGroupInfo *group = element_info[trigger_element].group;
1311 for (k = 0; k < group->num_elements_resolved; k++)
1312 trigger_events[group->element_resolved[k]]
1313 |= ei->change_page[j].events;
1316 trigger_events[trigger_element] |= ei->change_page[j].events;
1321 /* add trigger events from element change event properties */
1322 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1323 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1324 trigger_events[element_info[i].change->trigger_element] |=
1325 element_info[i].change->events;
1328 /* ---------- initialize push delay -------------------------------------- */
1330 /* initialize push delay values to default */
1331 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1333 if (!IS_CUSTOM_ELEMENT(i))
1335 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1336 element_info[i].push_delay_random = game.default_push_delay_random;
1340 /* set push delay value for certain elements from pre-defined list */
1341 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1343 int e = push_delay_list[i].element;
1345 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1346 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1349 /* set push delay value for Supaplex elements for newer engine versions */
1350 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1352 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1354 if (IS_SP_ELEMENT(i))
1356 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1357 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1362 /* ---------- initialize move stepsize ----------------------------------- */
1364 /* initialize move stepsize values to default */
1365 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1366 if (!IS_CUSTOM_ELEMENT(i))
1367 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1369 /* set move stepsize value for certain elements from pre-defined list */
1370 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1372 int e = move_stepsize_list[i].element;
1374 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1378 /* ---------- initialize move dig/leave ---------------------------------- */
1380 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1382 element_info[i].can_leave_element = FALSE;
1383 element_info[i].can_leave_element_last = FALSE;
1387 /* ---------- initialize gem count --------------------------------------- */
1389 /* initialize gem count values for each element */
1390 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1391 if (!IS_CUSTOM_ELEMENT(i))
1392 element_info[i].collect_count = 0;
1394 /* add gem count values for all elements from pre-defined list */
1395 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1396 element_info[collect_count_list[i].element].collect_count =
1397 collect_count_list[i].count;
1399 /* ---------- initialize access direction -------------------------------- */
1401 /* initialize access direction values to default (access from every side) */
1402 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1403 if (!IS_CUSTOM_ELEMENT(i))
1404 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1406 /* set access direction value for certain elements from pre-defined list */
1407 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1408 element_info[access_direction_list[i].element].access_direction =
1409 access_direction_list[i].direction;
1414 =============================================================================
1416 -----------------------------------------------------------------------------
1417 initialize and start new game
1418 =============================================================================
1423 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1424 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1425 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1432 #if USE_NEW_AMOEBA_CODE
1433 printf("Using new amoeba code.\n");
1435 printf("Using old amoeba code.\n");
1440 /* don't play tapes over network */
1441 network_playing = (options.network && !tape.playing);
1443 for (i = 0; i < MAX_PLAYERS; i++)
1445 struct PlayerInfo *player = &stored_player[i];
1447 player->index_nr = i;
1448 player->index_bit = (1 << i);
1449 player->element_nr = EL_PLAYER_1 + i;
1451 player->present = FALSE;
1452 player->active = FALSE;
1455 player->effective_action = 0;
1456 player->programmed_action = 0;
1459 player->gems_still_needed = level.gems_needed;
1460 player->sokobanfields_still_needed = 0;
1461 player->lights_still_needed = 0;
1462 player->friends_still_needed = 0;
1464 for (j = 0; j < MAX_KEYS; j++)
1465 player->key[j] = FALSE;
1467 player->dynabomb_count = 0;
1468 player->dynabomb_size = 1;
1469 player->dynabombs_left = 0;
1470 player->dynabomb_xl = FALSE;
1472 player->MovDir = MV_NO_MOVING;
1475 player->GfxDir = MV_NO_MOVING;
1476 player->GfxAction = ACTION_DEFAULT;
1478 player->StepFrame = 0;
1480 player->use_murphy_graphic = FALSE;
1482 player->block_last_field = FALSE;
1483 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1485 player->actual_frame_counter = 0;
1487 player->step_counter = 0;
1489 player->last_move_dir = MV_NO_MOVING;
1491 player->is_waiting = FALSE;
1492 player->is_moving = FALSE;
1493 player->is_auto_moving = FALSE;
1494 player->is_digging = FALSE;
1495 player->is_snapping = FALSE;
1496 player->is_collecting = FALSE;
1497 player->is_pushing = FALSE;
1498 player->is_switching = FALSE;
1499 player->is_dropping = FALSE;
1501 player->is_bored = FALSE;
1502 player->is_sleeping = FALSE;
1504 player->frame_counter_bored = -1;
1505 player->frame_counter_sleeping = -1;
1507 player->anim_delay_counter = 0;
1508 player->post_delay_counter = 0;
1510 player->action_waiting = ACTION_DEFAULT;
1511 player->last_action_waiting = ACTION_DEFAULT;
1512 player->special_action_bored = ACTION_DEFAULT;
1513 player->special_action_sleeping = ACTION_DEFAULT;
1515 player->num_special_action_bored = 0;
1516 player->num_special_action_sleeping = 0;
1518 /* determine number of special actions for bored and sleeping animation */
1519 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1521 boolean found = FALSE;
1523 for (k = 0; k < NUM_DIRECTIONS; k++)
1524 if (el_act_dir2img(player->element_nr, j, k) !=
1525 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1529 player->num_special_action_bored++;
1533 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1535 boolean found = FALSE;
1537 for (k = 0; k < NUM_DIRECTIONS; k++)
1538 if (el_act_dir2img(player->element_nr, j, k) !=
1539 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1543 player->num_special_action_sleeping++;
1548 player->switch_x = -1;
1549 player->switch_y = -1;
1551 player->show_envelope = 0;
1553 player->move_delay = game.initial_move_delay;
1554 player->move_delay_value = game.initial_move_delay_value;
1556 player->move_delay_reset_counter = 0;
1558 player->push_delay = 0;
1559 player->push_delay_value = game.initial_push_delay_value;
1561 player->drop_delay = 0;
1563 player->last_jx = player->last_jy = 0;
1564 player->jx = player->jy = 0;
1566 player->shield_normal_time_left = 0;
1567 player->shield_deadly_time_left = 0;
1569 player->inventory_infinite_element = EL_UNDEFINED;
1570 player->inventory_size = 0;
1572 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1573 SnapField(player, 0, 0);
1575 player->LevelSolved = FALSE;
1576 player->GameOver = FALSE;
1579 network_player_action_received = FALSE;
1581 #if defined(PLATFORM_UNIX)
1582 /* initial null action */
1583 if (network_playing)
1584 SendToServer_MovePlayer(MV_NO_MOVING);
1592 TimeLeft = level.time;
1595 ScreenMovDir = MV_NO_MOVING;
1599 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1601 AllPlayersGone = FALSE;
1603 game.yamyam_content_nr = 0;
1604 game.magic_wall_active = FALSE;
1605 game.magic_wall_time_left = 0;
1606 game.light_time_left = 0;
1607 game.timegate_time_left = 0;
1608 game.switchgate_pos = 0;
1609 game.balloon_dir = MV_NO_MOVING;
1610 game.gravity = level.initial_gravity;
1611 game.explosions_delayed = TRUE;
1613 game.envelope_active = FALSE;
1615 for (i = 0; i < NUM_BELTS; i++)
1617 game.belt_dir[i] = MV_NO_MOVING;
1618 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1621 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1622 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1624 for (x = 0; x < lev_fieldx; x++)
1626 for (y = 0; y < lev_fieldy; y++)
1628 Feld[x][y] = level.field[x][y];
1629 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1630 ChangeDelay[x][y] = 0;
1631 ChangePage[x][y] = -1;
1632 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1634 WasJustMoving[x][y] = 0;
1635 WasJustFalling[x][y] = 0;
1637 Pushed[x][y] = FALSE;
1639 Changed[x][y] = CE_BITMASK_DEFAULT;
1640 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1642 ExplodePhase[x][y] = 0;
1643 ExplodeDelay[x][y] = 0;
1644 ExplodeField[x][y] = EX_TYPE_NONE;
1646 RunnerVisit[x][y] = 0;
1647 PlayerVisit[x][y] = 0;
1650 GfxRandom[x][y] = INIT_GFX_RANDOM();
1651 GfxElement[x][y] = EL_UNDEFINED;
1652 GfxAction[x][y] = ACTION_DEFAULT;
1653 GfxDir[x][y] = MV_NO_MOVING;
1657 for (y = 0; y < lev_fieldy; y++)
1659 for (x = 0; x < lev_fieldx; x++)
1661 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1663 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1665 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1668 InitField(x, y, TRUE);
1674 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1675 emulate_sb ? EMU_SOKOBAN :
1676 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1678 /* initialize explosion and ignition delay */
1679 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1681 if (!IS_CUSTOM_ELEMENT(i))
1684 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1685 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1686 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1687 int last_phase = (num_phase + 1) * delay;
1688 int half_phase = (num_phase / 2) * delay;
1690 element_info[i].explosion_delay = last_phase - 1;
1691 element_info[i].ignition_delay = half_phase;
1694 if (i == EL_BLACK_ORB)
1695 element_info[i].ignition_delay = 0;
1697 if (i == EL_BLACK_ORB)
1698 element_info[i].ignition_delay = 1;
1703 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1704 element_info[i].explosion_delay = 1;
1706 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1707 element_info[i].ignition_delay = 1;
1711 /* correct non-moving belts to start moving left */
1712 for (i = 0; i < NUM_BELTS; i++)
1713 if (game.belt_dir[i] == MV_NO_MOVING)
1714 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1716 /* check if any connected player was not found in playfield */
1717 for (i = 0; i < MAX_PLAYERS; i++)
1719 struct PlayerInfo *player = &stored_player[i];
1721 if (player->connected && !player->present)
1723 for (j = 0; j < MAX_PLAYERS; j++)
1725 struct PlayerInfo *some_player = &stored_player[j];
1726 int jx = some_player->jx, jy = some_player->jy;
1728 /* assign first free player found that is present in the playfield */
1729 if (some_player->present && !some_player->connected)
1731 player->present = TRUE;
1732 player->active = TRUE;
1734 some_player->present = FALSE;
1735 some_player->active = FALSE;
1738 player->element_nr = some_player->element_nr;
1741 StorePlayer[jx][jy] = player->element_nr;
1742 player->jx = player->last_jx = jx;
1743 player->jy = player->last_jy = jy;
1753 /* when playing a tape, eliminate all players which do not participate */
1755 for (i = 0; i < MAX_PLAYERS; i++)
1757 if (stored_player[i].active && !tape.player_participates[i])
1759 struct PlayerInfo *player = &stored_player[i];
1760 int jx = player->jx, jy = player->jy;
1762 player->active = FALSE;
1763 StorePlayer[jx][jy] = 0;
1764 Feld[jx][jy] = EL_EMPTY;
1768 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1770 /* when in single player mode, eliminate all but the first active player */
1772 for (i = 0; i < MAX_PLAYERS; i++)
1774 if (stored_player[i].active)
1776 for (j = i + 1; j < MAX_PLAYERS; j++)
1778 if (stored_player[j].active)
1780 struct PlayerInfo *player = &stored_player[j];
1781 int jx = player->jx, jy = player->jy;
1783 player->active = FALSE;
1784 player->present = FALSE;
1786 StorePlayer[jx][jy] = 0;
1787 Feld[jx][jy] = EL_EMPTY;
1794 /* when recording the game, store which players take part in the game */
1797 for (i = 0; i < MAX_PLAYERS; i++)
1798 if (stored_player[i].active)
1799 tape.player_participates[i] = TRUE;
1804 for (i = 0; i < MAX_PLAYERS; i++)
1806 struct PlayerInfo *player = &stored_player[i];
1808 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1813 if (local_player == player)
1814 printf("Player %d is local player.\n", i+1);
1818 if (BorderElement == EL_EMPTY)
1821 SBX_Right = lev_fieldx - SCR_FIELDX;
1823 SBY_Lower = lev_fieldy - SCR_FIELDY;
1828 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1830 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1833 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1834 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1836 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1837 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1839 /* if local player not found, look for custom element that might create
1840 the player (make some assumptions about the right custom element) */
1841 if (!local_player->present)
1843 int start_x = 0, start_y = 0;
1844 int found_rating = 0;
1845 int found_element = EL_UNDEFINED;
1847 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1849 int element = Feld[x][y];
1854 if (!IS_CUSTOM_ELEMENT(element))
1857 if (CAN_CHANGE(element))
1859 for (i = 0; i < element_info[element].num_change_pages; i++)
1861 content = element_info[element].change_page[i].target_element;
1862 is_player = ELEM_IS_PLAYER(content);
1864 if (is_player && (found_rating < 3 || element < found_element))
1870 found_element = element;
1875 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1877 content = element_info[element].content[xx][yy];
1878 is_player = ELEM_IS_PLAYER(content);
1880 if (is_player && (found_rating < 2 || element < found_element))
1882 start_x = x + xx - 1;
1883 start_y = y + yy - 1;
1886 found_element = element;
1889 if (!CAN_CHANGE(element))
1892 for (i = 0; i < element_info[element].num_change_pages; i++)
1894 content= element_info[element].change_page[i].target_content[xx][yy];
1895 is_player = ELEM_IS_PLAYER(content);
1897 if (is_player && (found_rating < 1 || element < found_element))
1899 start_x = x + xx - 1;
1900 start_y = y + yy - 1;
1903 found_element = element;
1909 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1910 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1913 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1914 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1920 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1921 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1922 local_player->jx - MIDPOSX);
1924 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1925 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1926 local_player->jy - MIDPOSY);
1928 scroll_x = SBX_Left;
1929 scroll_y = SBY_Upper;
1930 if (local_player->jx >= SBX_Left + MIDPOSX)
1931 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1932 local_player->jx - MIDPOSX :
1934 if (local_player->jy >= SBY_Upper + MIDPOSY)
1935 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1936 local_player->jy - MIDPOSY :
1941 CloseDoor(DOOR_CLOSE_1);
1946 /* after drawing the level, correct some elements */
1947 if (game.timegate_time_left == 0)
1948 CloseAllOpenTimegates();
1950 if (setup.soft_scrolling)
1951 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1953 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1956 /* copy default game door content to main double buffer */
1957 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1958 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1960 DrawGameDoorValues();
1964 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1965 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1966 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1970 /* copy actual game door content to door double buffer for OpenDoor() */
1971 BlitBitmap(drawto, bitmap_db_door,
1972 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1974 OpenDoor(DOOR_OPEN_ALL);
1976 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1978 if (setup.sound_music)
1981 KeyboardAutoRepeatOffUnlessAutoplay();
1985 for (i = 0; i < MAX_PLAYERS; i++)
1986 printf("Player %d %sactive.\n",
1987 i + 1, (stored_player[i].active ? "" : "not "));
1991 printf("::: starting game [%d]\n", FrameCounter);
1995 void InitMovDir(int x, int y)
1997 int i, element = Feld[x][y];
1998 static int xy[4][2] =
2005 static int direction[3][4] =
2007 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2008 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2009 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2018 Feld[x][y] = EL_BUG;
2019 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2022 case EL_SPACESHIP_RIGHT:
2023 case EL_SPACESHIP_UP:
2024 case EL_SPACESHIP_LEFT:
2025 case EL_SPACESHIP_DOWN:
2026 Feld[x][y] = EL_SPACESHIP;
2027 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2030 case EL_BD_BUTTERFLY_RIGHT:
2031 case EL_BD_BUTTERFLY_UP:
2032 case EL_BD_BUTTERFLY_LEFT:
2033 case EL_BD_BUTTERFLY_DOWN:
2034 Feld[x][y] = EL_BD_BUTTERFLY;
2035 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2038 case EL_BD_FIREFLY_RIGHT:
2039 case EL_BD_FIREFLY_UP:
2040 case EL_BD_FIREFLY_LEFT:
2041 case EL_BD_FIREFLY_DOWN:
2042 Feld[x][y] = EL_BD_FIREFLY;
2043 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2046 case EL_PACMAN_RIGHT:
2048 case EL_PACMAN_LEFT:
2049 case EL_PACMAN_DOWN:
2050 Feld[x][y] = EL_PACMAN;
2051 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2054 case EL_SP_SNIKSNAK:
2055 MovDir[x][y] = MV_UP;
2058 case EL_SP_ELECTRON:
2059 MovDir[x][y] = MV_LEFT;
2066 Feld[x][y] = EL_MOLE;
2067 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2071 if (IS_CUSTOM_ELEMENT(element))
2073 struct ElementInfo *ei = &element_info[element];
2074 int move_direction_initial = ei->move_direction_initial;
2075 int move_pattern = ei->move_pattern;
2077 if (move_direction_initial == MV_START_PREVIOUS)
2079 if (MovDir[x][y] != MV_NO_MOVING)
2082 move_direction_initial = MV_START_AUTOMATIC;
2085 if (move_direction_initial == MV_START_RANDOM)
2086 MovDir[x][y] = 1 << RND(4);
2087 else if (move_direction_initial & MV_ANY_DIRECTION)
2088 MovDir[x][y] = move_direction_initial;
2089 else if (move_pattern == MV_ALL_DIRECTIONS ||
2090 move_pattern == MV_TURNING_LEFT ||
2091 move_pattern == MV_TURNING_RIGHT ||
2092 move_pattern == MV_TURNING_LEFT_RIGHT ||
2093 move_pattern == MV_TURNING_RIGHT_LEFT ||
2094 move_pattern == MV_TURNING_RANDOM)
2095 MovDir[x][y] = 1 << RND(4);
2096 else if (move_pattern == MV_HORIZONTAL)
2097 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2098 else if (move_pattern == MV_VERTICAL)
2099 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2100 else if (move_pattern & MV_ANY_DIRECTION)
2101 MovDir[x][y] = element_info[element].move_pattern;
2102 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2103 move_pattern == MV_ALONG_RIGHT_SIDE)
2106 /* use random direction as default start direction */
2107 if (game.engine_version >= VERSION_IDENT(3,1,0,2))
2108 MovDir[x][y] = 1 << RND(4);
2111 for (i = 0; i < NUM_DIRECTIONS; i++)
2113 int x1 = x + xy[i][0];
2114 int y1 = y + xy[i][1];
2116 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2118 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2119 MovDir[x][y] = direction[0][i];
2121 MovDir[x][y] = direction[1][i];
2130 MovDir[x][y] = 1 << RND(4);
2132 if (element != EL_BUG &&
2133 element != EL_SPACESHIP &&
2134 element != EL_BD_BUTTERFLY &&
2135 element != EL_BD_FIREFLY)
2138 for (i = 0; i < NUM_DIRECTIONS; i++)
2140 int x1 = x + xy[i][0];
2141 int y1 = y + xy[i][1];
2143 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2145 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2147 MovDir[x][y] = direction[0][i];
2150 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2151 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2153 MovDir[x][y] = direction[1][i];
2162 GfxDir[x][y] = MovDir[x][y];
2165 void InitAmoebaNr(int x, int y)
2168 int group_nr = AmoebeNachbarNr(x, y);
2172 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2174 if (AmoebaCnt[i] == 0)
2182 AmoebaNr[x][y] = group_nr;
2183 AmoebaCnt[group_nr]++;
2184 AmoebaCnt2[group_nr]++;
2190 boolean raise_level = FALSE;
2192 if (local_player->MovPos)
2196 if (tape.auto_play) /* tape might already be stopped here */
2197 tape.auto_play_level_solved = TRUE;
2199 if (tape.playing && tape.auto_play)
2200 tape.auto_play_level_solved = TRUE;
2203 local_player->LevelSolved = FALSE;
2205 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2209 if (!tape.playing && setup.sound_loops)
2210 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2211 SND_CTRL_PLAY_LOOP);
2213 while (TimeLeft > 0)
2215 if (!tape.playing && !setup.sound_loops)
2216 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2217 if (TimeLeft > 0 && !(TimeLeft % 10))
2218 RaiseScore(level.score[SC_TIME_BONUS]);
2219 if (TimeLeft > 100 && !(TimeLeft % 10))
2224 DrawGameValue_Time(TimeLeft);
2232 if (!tape.playing && setup.sound_loops)
2233 StopSound(SND_GAME_LEVELTIME_BONUS);
2235 else if (level.time == 0) /* level without time limit */
2237 if (!tape.playing && setup.sound_loops)
2238 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2239 SND_CTRL_PLAY_LOOP);
2241 while (TimePlayed < 999)
2243 if (!tape.playing && !setup.sound_loops)
2244 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2245 if (TimePlayed < 999 && !(TimePlayed % 10))
2246 RaiseScore(level.score[SC_TIME_BONUS]);
2247 if (TimePlayed < 900 && !(TimePlayed % 10))
2252 DrawGameValue_Time(TimePlayed);
2260 if (!tape.playing && setup.sound_loops)
2261 StopSound(SND_GAME_LEVELTIME_BONUS);
2264 /* close exit door after last player */
2265 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2266 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2268 int element = Feld[ExitX][ExitY];
2270 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2271 EL_SP_EXIT_CLOSING);
2273 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2276 /* Hero disappears */
2277 DrawLevelField(ExitX, ExitY);
2283 CloseDoor(DOOR_CLOSE_1);
2288 SaveTape(tape.level_nr); /* Ask to save tape */
2291 if (level_nr == leveldir_current->handicap_level)
2293 leveldir_current->handicap_level++;
2294 SaveLevelSetup_SeriesInfo();
2297 if (level_editor_test_game)
2298 local_player->score = -1; /* no highscore when playing from editor */
2299 else if (level_nr < leveldir_current->last_level)
2300 raise_level = TRUE; /* advance to next level */
2302 if ((hi_pos = NewHiScore()) >= 0)
2304 game_status = GAME_MODE_SCORES;
2305 DrawHallOfFame(hi_pos);
2314 game_status = GAME_MODE_MAIN;
2331 LoadScore(level_nr);
2333 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2334 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2337 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2339 if (local_player->score > highscore[k].Score)
2341 /* player has made it to the hall of fame */
2343 if (k < MAX_SCORE_ENTRIES - 1)
2345 int m = MAX_SCORE_ENTRIES - 1;
2348 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2349 if (!strcmp(setup.player_name, highscore[l].Name))
2351 if (m == k) /* player's new highscore overwrites his old one */
2355 for (l = m; l > k; l--)
2357 strcpy(highscore[l].Name, highscore[l - 1].Name);
2358 highscore[l].Score = highscore[l - 1].Score;
2365 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2366 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2367 highscore[k].Score = local_player->score;
2373 else if (!strncmp(setup.player_name, highscore[k].Name,
2374 MAX_PLAYER_NAME_LEN))
2375 break; /* player already there with a higher score */
2381 SaveScore(level_nr);
2386 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2388 if (player->GfxAction != action || player->GfxDir != dir)
2391 printf("Player frame reset! (%d => %d, %d => %d)\n",
2392 player->GfxAction, action, player->GfxDir, dir);
2395 player->GfxAction = action;
2396 player->GfxDir = dir;
2398 player->StepFrame = 0;
2402 static void ResetRandomAnimationValue(int x, int y)
2404 GfxRandom[x][y] = INIT_GFX_RANDOM();
2407 static void ResetGfxAnimation(int x, int y)
2410 GfxAction[x][y] = ACTION_DEFAULT;
2411 GfxDir[x][y] = MovDir[x][y];
2414 void InitMovingField(int x, int y, int direction)
2416 int element = Feld[x][y];
2417 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2418 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2422 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2423 ResetGfxAnimation(x, y);
2425 MovDir[newx][newy] = MovDir[x][y] = direction;
2426 GfxDir[x][y] = direction;
2428 if (Feld[newx][newy] == EL_EMPTY)
2429 Feld[newx][newy] = EL_BLOCKED;
2431 if (direction == MV_DOWN && CAN_FALL(element))
2432 GfxAction[x][y] = ACTION_FALLING;
2434 GfxAction[x][y] = ACTION_MOVING;
2436 GfxFrame[newx][newy] = GfxFrame[x][y];
2437 GfxRandom[newx][newy] = GfxRandom[x][y];
2438 GfxAction[newx][newy] = GfxAction[x][y];
2439 GfxDir[newx][newy] = GfxDir[x][y];
2442 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2444 int direction = MovDir[x][y];
2445 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2446 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2452 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2454 int oldx = x, oldy = y;
2455 int direction = MovDir[x][y];
2457 if (direction == MV_LEFT)
2459 else if (direction == MV_RIGHT)
2461 else if (direction == MV_UP)
2463 else if (direction == MV_DOWN)
2466 *comes_from_x = oldx;
2467 *comes_from_y = oldy;
2470 int MovingOrBlocked2Element(int x, int y)
2472 int element = Feld[x][y];
2474 if (element == EL_BLOCKED)
2478 Blocked2Moving(x, y, &oldx, &oldy);
2479 return Feld[oldx][oldy];
2485 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2487 /* like MovingOrBlocked2Element(), but if element is moving
2488 and (x,y) is the field the moving element is just leaving,
2489 return EL_BLOCKED instead of the element value */
2490 int element = Feld[x][y];
2492 if (IS_MOVING(x, y))
2494 if (element == EL_BLOCKED)
2498 Blocked2Moving(x, y, &oldx, &oldy);
2499 return Feld[oldx][oldy];
2508 static void RemoveField(int x, int y)
2510 Feld[x][y] = EL_EMPTY;
2517 ChangeDelay[x][y] = 0;
2518 ChangePage[x][y] = -1;
2519 Pushed[x][y] = FALSE;
2521 GfxElement[x][y] = EL_UNDEFINED;
2522 GfxAction[x][y] = ACTION_DEFAULT;
2523 GfxDir[x][y] = MV_NO_MOVING;
2526 void RemoveMovingField(int x, int y)
2528 int oldx = x, oldy = y, newx = x, newy = y;
2529 int element = Feld[x][y];
2530 int next_element = EL_UNDEFINED;
2532 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2535 if (IS_MOVING(x, y))
2537 Moving2Blocked(x, y, &newx, &newy);
2539 if (Feld[newx][newy] != EL_BLOCKED)
2542 if (Feld[newx][newy] != EL_BLOCKED)
2544 /* element is moving, but target field is not free (blocked), but
2545 already occupied by something different (example: acid pool);
2546 in this case, only remove the moving field, but not the target */
2548 RemoveField(oldx, oldy);
2550 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2552 DrawLevelField(oldx, oldy);
2558 else if (element == EL_BLOCKED)
2560 Blocked2Moving(x, y, &oldx, &oldy);
2561 if (!IS_MOVING(oldx, oldy))
2565 if (element == EL_BLOCKED &&
2566 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2567 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2568 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2569 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2570 next_element = get_next_element(Feld[oldx][oldy]);
2572 RemoveField(oldx, oldy);
2573 RemoveField(newx, newy);
2575 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2577 if (next_element != EL_UNDEFINED)
2578 Feld[oldx][oldy] = next_element;
2580 DrawLevelField(oldx, oldy);
2581 DrawLevelField(newx, newy);
2584 void DrawDynamite(int x, int y)
2586 int sx = SCREENX(x), sy = SCREENY(y);
2587 int graphic = el2img(Feld[x][y]);
2590 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2593 if (IS_WALKABLE_INSIDE(Back[x][y]))
2597 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2598 else if (Store[x][y])
2599 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2601 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2604 if (Back[x][y] || Store[x][y])
2605 DrawGraphicThruMask(sx, sy, graphic, frame);
2607 DrawGraphic(sx, sy, graphic, frame);
2609 if (game.emulation == EMU_SUPAPLEX)
2610 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2611 else if (Store[x][y])
2612 DrawGraphicThruMask(sx, sy, graphic, frame);
2614 DrawGraphic(sx, sy, graphic, frame);
2618 void CheckDynamite(int x, int y)
2620 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2624 if (MovDelay[x][y] != 0)
2627 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2634 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2636 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2637 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2638 StopSound(SND_DYNAMITE_ACTIVE);
2640 StopSound(SND_DYNABOMB_ACTIVE);
2646 void RelocatePlayer(int x, int y, int element_raw)
2648 int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
2649 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2650 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2651 boolean no_delay = (tape.warp_forward);
2652 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2653 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2656 if (player->GameOver) /* do not reanimate dead player */
2659 RemoveField(x, y); /* temporarily remove newly placed player */
2660 DrawLevelField(x, y);
2662 if (player->present)
2664 while (player->MovPos)
2666 ScrollPlayer(player, SCROLL_GO_ON);
2667 ScrollScreen(NULL, SCROLL_GO_ON);
2673 Delay(wait_delay_value);
2676 DrawPlayer(player); /* needed here only to cleanup last field */
2677 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2679 player->is_moving = FALSE;
2682 old_jx = player->jx;
2683 old_jy = player->jy;
2685 Feld[x][y] = element;
2686 InitPlayerField(x, y, element, TRUE);
2688 if (player != local_player) /* do not visually relocate other players */
2691 if (level.instant_relocation)
2694 int offset = (setup.scroll_delay ? 3 : 0);
2695 int jx = local_player->jx;
2696 int jy = local_player->jy;
2698 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2700 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2701 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2702 local_player->jx - MIDPOSX);
2704 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2705 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2706 local_player->jy - MIDPOSY);
2710 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2711 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2712 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2714 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2715 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2716 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2718 /* don't scroll over playfield boundaries */
2719 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2720 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2722 /* don't scroll over playfield boundaries */
2723 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2724 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2727 scroll_x += (local_player->jx - old_jx);
2728 scroll_y += (local_player->jy - old_jy);
2730 /* don't scroll over playfield boundaries */
2731 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2732 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2734 /* don't scroll over playfield boundaries */
2735 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2736 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2739 RedrawPlayfield(TRUE, 0,0,0,0);
2745 int offset = (setup.scroll_delay ? 3 : 0);
2746 int jx = local_player->jx;
2747 int jy = local_player->jy;
2749 int scroll_xx = -999, scroll_yy = -999;
2751 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2753 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2756 int fx = FX, fy = FY;
2758 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2759 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2760 local_player->jx - MIDPOSX);
2762 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2763 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2764 local_player->jy - MIDPOSY);
2766 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2767 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2770 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2773 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2780 fx += dx * TILEX / 2;
2781 fy += dy * TILEY / 2;
2783 ScrollLevel(dx, dy);
2786 /* scroll in two steps of half tile size to make things smoother */
2787 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2789 Delay(wait_delay_value);
2791 /* scroll second step to align at full tile size */
2793 Delay(wait_delay_value);
2796 int scroll_xx = -999, scroll_yy = -999;
2798 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2800 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2803 int fx = FX, fy = FY;
2805 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2806 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2807 local_player->jx - MIDPOSX);
2809 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2810 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2811 local_player->jy - MIDPOSY);
2813 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2814 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2817 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2820 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2827 fx += dx * TILEX / 2;
2828 fy += dy * TILEY / 2;
2830 ScrollLevel(dx, dy);
2833 /* scroll in two steps of half tile size to make things smoother */
2834 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2836 Delay(wait_delay_value);
2838 /* scroll second step to align at full tile size */
2840 Delay(wait_delay_value);
2846 void Explode(int ex, int ey, int phase, int mode)
2853 /* !!! eliminate this variable !!! */
2854 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2859 int last_phase = num_phase * delay;
2860 int half_phase = (num_phase / 2) * delay;
2861 int first_phase_after_start = EX_PHASE_START + 1;
2865 if (game.explosions_delayed)
2867 ExplodeField[ex][ey] = mode;
2871 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2873 int center_element = Feld[ex][ey];
2876 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
2880 /* --- This is only really needed (and now handled) in "Impact()". --- */
2881 /* do not explode moving elements that left the explode field in time */
2882 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2883 center_element == EL_EMPTY &&
2884 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
2888 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
2889 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2891 /* remove things displayed in background while burning dynamite */
2892 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2895 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2897 /* put moving element to center field (and let it explode there) */
2898 center_element = MovingOrBlocked2Element(ex, ey);
2899 RemoveMovingField(ex, ey);
2900 Feld[ex][ey] = center_element;
2906 last_phase = element_info[center_element].explosion_delay + 1;
2908 last_phase = element_info[center_element].explosion_delay;
2912 printf("::: %d -> %d\n", center_element, last_phase);
2916 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2918 int xx = x - ex + 1;
2919 int yy = y - ey + 1;
2924 if (!IN_LEV_FIELD(x, y) ||
2925 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
2926 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
2929 if (!IN_LEV_FIELD(x, y) ||
2930 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
2934 if (!IN_LEV_FIELD(x, y) ||
2935 ((mode != EX_TYPE_NORMAL ||
2936 center_element == EL_AMOEBA_TO_DIAMOND) &&
2937 (x != ex || y != ey)))
2941 element = Feld[x][y];
2943 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2945 element = MovingOrBlocked2Element(x, y);
2947 if (!IS_EXPLOSION_PROOF(element))
2948 RemoveMovingField(x, y);
2954 if (IS_EXPLOSION_PROOF(element))
2957 /* indestructible elements can only explode in center (but not flames) */
2958 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2959 element == EL_FLAMES)
2964 if ((IS_INDESTRUCTIBLE(element) &&
2965 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2966 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2967 element == EL_FLAMES)
2971 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2973 if (IS_ACTIVE_BOMB(element))
2975 /* re-activate things under the bomb like gate or penguin */
2976 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2983 /* save walkable background elements while explosion on same tile */
2985 if (IS_INDESTRUCTIBLE(element))
2986 Back[x][y] = element;
2988 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2989 Back[x][y] = element;
2992 /* ignite explodable elements reached by other explosion */
2993 if (element == EL_EXPLOSION)
2994 element = Store2[x][y];
2997 if (AmoebaNr[x][y] &&
2998 (element == EL_AMOEBA_FULL ||
2999 element == EL_BD_AMOEBA ||
3000 element == EL_AMOEBA_GROWING))
3002 AmoebaCnt[AmoebaNr[x][y]]--;
3003 AmoebaCnt2[AmoebaNr[x][y]]--;
3009 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3011 switch(StorePlayer[ex][ey])
3014 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3017 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3020 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3024 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3029 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3030 Store[x][y] = EL_EMPTY;
3032 if (game.emulation == EMU_SUPAPLEX)
3033 Store[x][y] = EL_EMPTY;
3036 else if (center_element == EL_MOLE)
3037 Store[x][y] = EL_EMERALD_RED;
3038 else if (center_element == EL_PENGUIN)
3039 Store[x][y] = EL_EMERALD_PURPLE;
3040 else if (center_element == EL_BUG)
3041 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3042 else if (center_element == EL_BD_BUTTERFLY)
3043 Store[x][y] = EL_BD_DIAMOND;
3044 else if (center_element == EL_SP_ELECTRON)
3045 Store[x][y] = EL_SP_INFOTRON;
3046 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3047 Store[x][y] = level.amoeba_content;
3048 else if (center_element == EL_YAMYAM)
3049 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3050 else if (IS_CUSTOM_ELEMENT(center_element) &&
3051 element_info[center_element].content[xx][yy] != EL_EMPTY)
3052 Store[x][y] = element_info[center_element].content[xx][yy];
3053 else if (element == EL_WALL_EMERALD)
3054 Store[x][y] = EL_EMERALD;
3055 else if (element == EL_WALL_DIAMOND)
3056 Store[x][y] = EL_DIAMOND;
3057 else if (element == EL_WALL_BD_DIAMOND)
3058 Store[x][y] = EL_BD_DIAMOND;
3059 else if (element == EL_WALL_EMERALD_YELLOW)
3060 Store[x][y] = EL_EMERALD_YELLOW;
3061 else if (element == EL_WALL_EMERALD_RED)
3062 Store[x][y] = EL_EMERALD_RED;
3063 else if (element == EL_WALL_EMERALD_PURPLE)
3064 Store[x][y] = EL_EMERALD_PURPLE;
3065 else if (element == EL_WALL_PEARL)
3066 Store[x][y] = EL_PEARL;
3067 else if (element == EL_WALL_CRYSTAL)
3068 Store[x][y] = EL_CRYSTAL;
3069 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3070 Store[x][y] = element_info[element].content[1][1];
3072 Store[x][y] = EL_EMPTY;
3074 if (x != ex || y != ey ||
3075 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_TYPE_BORDER)
3076 Store2[x][y] = element;
3079 if (AmoebaNr[x][y] &&
3080 (element == EL_AMOEBA_FULL ||
3081 element == EL_BD_AMOEBA ||
3082 element == EL_AMOEBA_GROWING))
3084 AmoebaCnt[AmoebaNr[x][y]]--;
3085 AmoebaCnt2[AmoebaNr[x][y]]--;
3091 MovDir[x][y] = MovPos[x][y] = 0;
3092 GfxDir[x][y] = MovDir[x][y];
3097 Feld[x][y] = EL_EXPLOSION;
3099 GfxElement[x][y] = center_element;
3101 GfxElement[x][y] = EL_UNDEFINED;
3104 ExplodePhase[x][y] = 1;
3106 ExplodeDelay[x][y] = last_phase;
3111 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3113 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3120 if (center_element == EL_YAMYAM)
3121 game.yamyam_content_nr =
3122 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3135 GfxFrame[x][y] = 0; /* restart explosion animation */
3139 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3143 last_phase = ExplodeDelay[x][y];
3146 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3150 /* activate this even in non-DEBUG version until cause for crash in
3151 getGraphicAnimationFrame() (see below) is found and eliminated */
3155 if (GfxElement[x][y] == EL_UNDEFINED)
3158 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3159 printf("Explode(): This should never happen!\n");
3162 GfxElement[x][y] = EL_EMPTY;
3168 border_element = Store2[x][y];
3169 if (IS_PLAYER(x, y))
3170 border_element = StorePlayer[x][y];
3173 printf("::: phase == %d\n", phase);
3176 if (phase == element_info[border_element].ignition_delay ||
3177 phase == last_phase)
3179 boolean border_explosion = FALSE;
3182 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3184 if (IS_PLAYER(x, y))
3187 KillHeroUnlessExplosionProtected(x, y);
3188 border_explosion = TRUE;
3191 if (phase == last_phase)
3192 printf("::: IS_PLAYER\n");
3195 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3197 Feld[x][y] = Store2[x][y];
3200 border_explosion = TRUE;
3203 if (phase == last_phase)
3204 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3207 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3209 AmoebeUmwandeln(x, y);
3211 border_explosion = TRUE;
3214 if (phase == last_phase)
3215 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3216 element_info[border_element].explosion_delay,
3217 element_info[border_element].ignition_delay,
3223 /* if an element just explodes due to another explosion (chain-reaction),
3224 do not immediately end the new explosion when it was the last frame of
3225 the explosion (as it would be done in the following "if"-statement!) */
3226 if (border_explosion && phase == last_phase)
3233 if (phase == first_phase_after_start)
3235 int element = Store2[x][y];
3237 if (element == EL_BLACK_ORB)
3239 Feld[x][y] = Store2[x][y];
3244 else if (phase == half_phase)
3246 int element = Store2[x][y];
3248 if (IS_PLAYER(x, y))
3249 KillHeroUnlessExplosionProtected(x, y);
3250 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3252 Feld[x][y] = Store2[x][y];
3256 else if (element == EL_AMOEBA_TO_DIAMOND)
3257 AmoebeUmwandeln(x, y);
3261 if (phase == last_phase)
3266 printf("::: done: phase == %d\n", phase);
3270 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3273 element = Feld[x][y] = Store[x][y];
3274 Store[x][y] = Store2[x][y] = 0;
3275 GfxElement[x][y] = EL_UNDEFINED;
3277 /* player can escape from explosions and might therefore be still alive */
3278 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3279 element <= EL_PLAYER_IS_EXPLODING_4)
3280 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3282 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3283 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3284 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3287 /* restore probably existing indestructible background element */
3288 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3289 element = Feld[x][y] = Back[x][y];
3292 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3293 GfxDir[x][y] = MV_NO_MOVING;
3294 ChangeDelay[x][y] = 0;
3295 ChangePage[x][y] = -1;
3298 InitField_WithBug2(x, y, FALSE);
3300 InitField(x, y, FALSE);
3302 /* !!! not needed !!! */
3304 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3305 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3308 if (CAN_MOVE(element))
3313 DrawLevelField(x, y);
3315 TestIfElementTouchesCustomElement(x, y);
3317 if (GFX_CRUMBLED(element))
3318 DrawLevelFieldCrumbledSandNeighbours(x, y);
3320 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3321 StorePlayer[x][y] = 0;
3323 if (ELEM_IS_PLAYER(element))
3324 RelocatePlayer(x, y, element);
3327 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3329 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3333 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3335 int stored = Store[x][y];
3336 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3337 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3341 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3343 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3347 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3351 printf("::: %d / %d [%d - %d]\n",
3352 GfxFrame[x][y], phase - delay, phase, delay);
3356 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3357 element_info[GfxElement[x][y]].token_name,
3362 DrawLevelFieldCrumbledSand(x, y);
3364 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3366 DrawLevelElement(x, y, Back[x][y]);
3367 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3369 else if (IS_WALKABLE_UNDER(Back[x][y]))
3371 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3372 DrawLevelElementThruMask(x, y, Back[x][y]);
3374 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3375 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3379 void DynaExplode(int ex, int ey)
3382 int dynabomb_element = Feld[ex][ey];
3383 int dynabomb_size = 1;
3384 boolean dynabomb_xl = FALSE;
3385 struct PlayerInfo *player;
3386 static int xy[4][2] =
3394 if (IS_ACTIVE_BOMB(dynabomb_element))
3396 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3397 dynabomb_size = player->dynabomb_size;
3398 dynabomb_xl = player->dynabomb_xl;
3399 player->dynabombs_left++;
3402 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3404 for (i = 0; i < NUM_DIRECTIONS; i++)
3406 for (j = 1; j <= dynabomb_size; j++)
3408 int x = ex + j * xy[i][0];
3409 int y = ey + j * xy[i][1];
3412 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3415 element = Feld[x][y];
3417 /* do not restart explosions of fields with active bombs */
3418 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3421 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3424 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3425 !CAN_GROW_INTO(element) && !dynabomb_xl)
3428 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3429 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3430 element != EL_SAND && !dynabomb_xl)
3437 void Bang(int x, int y)
3440 int element = MovingOrBlocked2Element(x, y);
3442 int element = Feld[x][y];
3446 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3448 if (IS_PLAYER(x, y))
3451 struct PlayerInfo *player = PLAYERINFO(x, y);
3453 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3454 player->element_nr);
3459 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3461 if (game.emulation == EMU_SUPAPLEX)
3462 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3464 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3469 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3477 case EL_BD_BUTTERFLY:
3480 case EL_DARK_YAMYAM:
3484 RaiseScoreElement(element);
3485 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3487 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3488 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3489 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3490 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3491 case EL_DYNABOMB_INCREASE_NUMBER:
3492 case EL_DYNABOMB_INCREASE_SIZE:
3493 case EL_DYNABOMB_INCREASE_POWER:
3498 case EL_LAMP_ACTIVE:
3500 case EL_AMOEBA_TO_DIAMOND:
3502 if (IS_PLAYER(x, y))
3503 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3505 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3508 if (CAN_EXPLODE_CROSS(element))
3510 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3514 else if (CAN_EXPLODE_1X1(element))
3515 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3517 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3521 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3524 void SplashAcid(int x, int y)
3527 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3528 (!IN_LEV_FIELD(x - 1, y - 2) ||
3529 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3530 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3532 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3533 (!IN_LEV_FIELD(x + 1, y - 2) ||
3534 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3535 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3537 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3539 /* input: position of element entering acid (obsolete) */
3541 int element = Feld[x][y];
3543 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3546 if (element != EL_ACID_SPLASH_LEFT &&
3547 element != EL_ACID_SPLASH_RIGHT)
3549 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3551 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3552 (!IN_LEV_FIELD(x - 1, y - 1) ||
3553 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3554 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3556 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3557 (!IN_LEV_FIELD(x + 1, y - 1) ||
3558 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3559 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3564 static void InitBeltMovement()
3566 static int belt_base_element[4] =
3568 EL_CONVEYOR_BELT_1_LEFT,
3569 EL_CONVEYOR_BELT_2_LEFT,
3570 EL_CONVEYOR_BELT_3_LEFT,
3571 EL_CONVEYOR_BELT_4_LEFT
3573 static int belt_base_active_element[4] =
3575 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3576 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3577 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3578 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3583 /* set frame order for belt animation graphic according to belt direction */
3584 for (i = 0; i < NUM_BELTS; i++)
3588 for (j = 0; j < NUM_BELT_PARTS; j++)
3590 int element = belt_base_active_element[belt_nr] + j;
3591 int graphic = el2img(element);
3593 if (game.belt_dir[i] == MV_LEFT)
3594 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3596 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3600 for (y = 0; y < lev_fieldy; y++)
3602 for (x = 0; x < lev_fieldx; x++)
3604 int element = Feld[x][y];
3606 for (i = 0; i < NUM_BELTS; i++)
3608 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3610 int e_belt_nr = getBeltNrFromBeltElement(element);
3613 if (e_belt_nr == belt_nr)
3615 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3617 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3625 static void ToggleBeltSwitch(int x, int y)
3627 static int belt_base_element[4] =
3629 EL_CONVEYOR_BELT_1_LEFT,
3630 EL_CONVEYOR_BELT_2_LEFT,
3631 EL_CONVEYOR_BELT_3_LEFT,
3632 EL_CONVEYOR_BELT_4_LEFT
3634 static int belt_base_active_element[4] =
3636 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3637 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3638 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3639 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3641 static int belt_base_switch_element[4] =
3643 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3644 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3645 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3646 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3648 static int belt_move_dir[4] =
3656 int element = Feld[x][y];
3657 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3658 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3659 int belt_dir = belt_move_dir[belt_dir_nr];
3662 if (!IS_BELT_SWITCH(element))
3665 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3666 game.belt_dir[belt_nr] = belt_dir;
3668 if (belt_dir_nr == 3)
3671 /* set frame order for belt animation graphic according to belt direction */
3672 for (i = 0; i < NUM_BELT_PARTS; i++)
3674 int element = belt_base_active_element[belt_nr] + i;
3675 int graphic = el2img(element);
3677 if (belt_dir == MV_LEFT)
3678 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3680 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3683 for (yy = 0; yy < lev_fieldy; yy++)
3685 for (xx = 0; xx < lev_fieldx; xx++)
3687 int element = Feld[xx][yy];
3689 if (IS_BELT_SWITCH(element))
3691 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3693 if (e_belt_nr == belt_nr)
3695 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3696 DrawLevelField(xx, yy);
3699 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3701 int e_belt_nr = getBeltNrFromBeltElement(element);
3703 if (e_belt_nr == belt_nr)
3705 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3707 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3708 DrawLevelField(xx, yy);
3711 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3713 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3715 if (e_belt_nr == belt_nr)
3717 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3719 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3720 DrawLevelField(xx, yy);
3727 static void ToggleSwitchgateSwitch(int x, int y)
3731 game.switchgate_pos = !game.switchgate_pos;
3733 for (yy = 0; yy < lev_fieldy; yy++)
3735 for (xx = 0; xx < lev_fieldx; xx++)
3737 int element = Feld[xx][yy];
3739 if (element == EL_SWITCHGATE_SWITCH_UP ||
3740 element == EL_SWITCHGATE_SWITCH_DOWN)
3742 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3743 DrawLevelField(xx, yy);
3745 else if (element == EL_SWITCHGATE_OPEN ||
3746 element == EL_SWITCHGATE_OPENING)
3748 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3750 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3752 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3755 else if (element == EL_SWITCHGATE_CLOSED ||
3756 element == EL_SWITCHGATE_CLOSING)
3758 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3760 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3762 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3769 static int getInvisibleActiveFromInvisibleElement(int element)
3771 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3772 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3773 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3777 static int getInvisibleFromInvisibleActiveElement(int element)
3779 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3780 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3781 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3785 static void RedrawAllLightSwitchesAndInvisibleElements()
3789 for (y = 0; y < lev_fieldy; y++)
3791 for (x = 0; x < lev_fieldx; x++)
3793 int element = Feld[x][y];
3795 if (element == EL_LIGHT_SWITCH &&
3796 game.light_time_left > 0)
3798 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3799 DrawLevelField(x, y);
3801 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3802 game.light_time_left == 0)
3804 Feld[x][y] = EL_LIGHT_SWITCH;
3805 DrawLevelField(x, y);
3807 else if (element == EL_INVISIBLE_STEELWALL ||
3808 element == EL_INVISIBLE_WALL ||
3809 element == EL_INVISIBLE_SAND)
3811 if (game.light_time_left > 0)
3812 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3814 DrawLevelField(x, y);
3816 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3817 element == EL_INVISIBLE_WALL_ACTIVE ||
3818 element == EL_INVISIBLE_SAND_ACTIVE)
3820 if (game.light_time_left == 0)
3821 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3823 DrawLevelField(x, y);
3829 static void ToggleLightSwitch(int x, int y)
3831 int element = Feld[x][y];
3833 game.light_time_left =
3834 (element == EL_LIGHT_SWITCH ?
3835 level.time_light * FRAMES_PER_SECOND : 0);
3837 RedrawAllLightSwitchesAndInvisibleElements();
3840 static void ActivateTimegateSwitch(int x, int y)
3844 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3846 for (yy = 0; yy < lev_fieldy; yy++)
3848 for (xx = 0; xx < lev_fieldx; xx++)
3850 int element = Feld[xx][yy];
3852 if (element == EL_TIMEGATE_CLOSED ||
3853 element == EL_TIMEGATE_CLOSING)
3855 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3856 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3860 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3862 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3863 DrawLevelField(xx, yy);
3870 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3873 inline static int getElementMoveStepsize(int x, int y)
3875 int element = Feld[x][y];
3876 int direction = MovDir[x][y];
3877 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3878 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3879 int horiz_move = (dx != 0);
3880 int sign = (horiz_move ? dx : dy);
3881 int step = sign * element_info[element].move_stepsize;
3883 /* special values for move stepsize for spring and things on conveyor belt */
3887 if (element == EL_SPRING)
3888 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3889 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3890 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3891 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3893 if (CAN_FALL(element) &&
3894 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3895 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3896 else if (element == EL_SPRING)
3897 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3904 void Impact(int x, int y)
3906 boolean lastline = (y == lev_fieldy-1);
3907 boolean object_hit = FALSE;
3908 boolean impact = (lastline || object_hit);
3909 int element = Feld[x][y];
3910 int smashed = EL_STEELWALL;
3912 if (!lastline) /* check if element below was hit */
3914 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3917 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3918 MovDir[x][y + 1] != MV_DOWN ||
3919 MovPos[x][y + 1] <= TILEY / 2));
3922 object_hit = !IS_FREE(x, y + 1);
3925 /* do not smash moving elements that left the smashed field in time */
3926 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3927 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3931 smashed = MovingOrBlocked2Element(x, y + 1);
3933 impact = (lastline || object_hit);
3936 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3938 SplashAcid(x, y + 1);
3942 /* only reset graphic animation if graphic really changes after impact */
3944 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3946 ResetGfxAnimation(x, y);
3947 DrawLevelField(x, y);
3950 if (impact && CAN_EXPLODE_IMPACT(element))
3955 else if (impact && element == EL_PEARL)
3957 Feld[x][y] = EL_PEARL_BREAKING;
3958 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3961 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
3963 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3968 if (impact && element == EL_AMOEBA_DROP)
3970 if (object_hit && IS_PLAYER(x, y + 1))
3971 KillHeroUnlessEnemyProtected(x, y + 1);
3972 else if (object_hit && smashed == EL_PENGUIN)
3976 Feld[x][y] = EL_AMOEBA_GROWING;
3977 Store[x][y] = EL_AMOEBA_WET;
3979 ResetRandomAnimationValue(x, y);
3984 if (object_hit) /* check which object was hit */
3986 if (CAN_PASS_MAGIC_WALL(element) &&
3987 (smashed == EL_MAGIC_WALL ||
3988 smashed == EL_BD_MAGIC_WALL))
3991 int activated_magic_wall =
3992 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3993 EL_BD_MAGIC_WALL_ACTIVE);
3995 /* activate magic wall / mill */
3996 for (yy = 0; yy < lev_fieldy; yy++)
3997 for (xx = 0; xx < lev_fieldx; xx++)
3998 if (Feld[xx][yy] == smashed)
3999 Feld[xx][yy] = activated_magic_wall;
4001 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4002 game.magic_wall_active = TRUE;
4004 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4005 SND_MAGIC_WALL_ACTIVATING :
4006 SND_BD_MAGIC_WALL_ACTIVATING));
4009 if (IS_PLAYER(x, y + 1))
4011 if (CAN_SMASH_PLAYER(element))
4013 KillHeroUnlessEnemyProtected(x, y + 1);
4017 else if (smashed == EL_PENGUIN)
4019 if (CAN_SMASH_PLAYER(element))
4025 else if (element == EL_BD_DIAMOND)
4027 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4033 else if (((element == EL_SP_INFOTRON ||
4034 element == EL_SP_ZONK) &&
4035 (smashed == EL_SP_SNIKSNAK ||
4036 smashed == EL_SP_ELECTRON ||
4037 smashed == EL_SP_DISK_ORANGE)) ||
4038 (element == EL_SP_INFOTRON &&
4039 smashed == EL_SP_DISK_YELLOW))
4045 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4051 else if (CAN_SMASH_EVERYTHING(element))
4053 if (IS_CLASSIC_ENEMY(smashed) ||
4054 CAN_EXPLODE_SMASHED(smashed))
4059 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4061 if (smashed == EL_LAMP ||
4062 smashed == EL_LAMP_ACTIVE)
4067 else if (smashed == EL_NUT)
4069 Feld[x][y + 1] = EL_NUT_BREAKING;
4070 PlayLevelSound(x, y, SND_NUT_BREAKING);
4071 RaiseScoreElement(EL_NUT);
4074 else if (smashed == EL_PEARL)
4076 Feld[x][y + 1] = EL_PEARL_BREAKING;
4077 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4080 else if (smashed == EL_DIAMOND)
4082 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4083 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4086 else if (IS_BELT_SWITCH(smashed))
4088 ToggleBeltSwitch(x, y + 1);
4090 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4091 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4093 ToggleSwitchgateSwitch(x, y + 1);
4095 else if (smashed == EL_LIGHT_SWITCH ||
4096 smashed == EL_LIGHT_SWITCH_ACTIVE)
4098 ToggleLightSwitch(x, y + 1);
4103 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4106 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4108 CheckTriggeredElementChangeSide(x, y + 1, smashed,
4109 CE_OTHER_IS_SWITCHING, CH_SIDE_TOP);
4110 CheckElementChangeSide(x, y + 1, smashed, element,
4111 CE_SWITCHED, CH_SIDE_TOP);
4116 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4121 /* play sound of magic wall / mill */
4123 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4124 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4126 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4127 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4128 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4129 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4134 /* play sound of object that hits the ground */
4135 if (lastline || object_hit)
4136 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4139 inline static void TurnRoundExt(int x, int y)
4151 { 0, 0 }, { 0, 0 }, { 0, 0 },
4156 int left, right, back;
4160 { MV_DOWN, MV_UP, MV_RIGHT },
4161 { MV_UP, MV_DOWN, MV_LEFT },
4163 { MV_LEFT, MV_RIGHT, MV_DOWN },
4167 { MV_RIGHT, MV_LEFT, MV_UP }
4170 int element = Feld[x][y];
4171 int move_pattern = element_info[element].move_pattern;
4173 int old_move_dir = MovDir[x][y];
4174 int left_dir = turn[old_move_dir].left;
4175 int right_dir = turn[old_move_dir].right;
4176 int back_dir = turn[old_move_dir].back;
4178 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4179 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4180 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4181 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4183 int left_x = x + left_dx, left_y = y + left_dy;
4184 int right_x = x + right_dx, right_y = y + right_dy;
4185 int move_x = x + move_dx, move_y = y + move_dy;
4189 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4191 TestIfBadThingTouchesOtherBadThing(x, y);
4193 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4194 MovDir[x][y] = right_dir;
4195 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4196 MovDir[x][y] = left_dir;
4198 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4200 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4204 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4205 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4207 TestIfBadThingTouchesOtherBadThing(x, y);
4209 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4210 MovDir[x][y] = left_dir;
4211 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4212 MovDir[x][y] = right_dir;
4214 if ((element == EL_SPACESHIP ||
4215 element == EL_SP_SNIKSNAK ||
4216 element == EL_SP_ELECTRON)
4217 && MovDir[x][y] != old_move_dir)
4219 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4223 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4225 TestIfBadThingTouchesOtherBadThing(x, y);
4227 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4228 MovDir[x][y] = left_dir;
4229 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4230 MovDir[x][y] = right_dir;
4232 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4234 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4237 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4239 TestIfBadThingTouchesOtherBadThing(x, y);
4241 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4242 MovDir[x][y] = left_dir;
4243 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4244 MovDir[x][y] = right_dir;
4246 if (MovDir[x][y] != old_move_dir)
4250 else if (element == EL_YAMYAM)
4252 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4253 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4255 if (can_turn_left && can_turn_right)
4256 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4257 else if (can_turn_left)
4258 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4259 else if (can_turn_right)
4260 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4262 MovDir[x][y] = back_dir;
4264 MovDelay[x][y] = 16 + 16 * RND(3);
4266 else if (element == EL_DARK_YAMYAM)
4268 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4270 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4273 if (can_turn_left && can_turn_right)
4274 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4275 else if (can_turn_left)
4276 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4277 else if (can_turn_right)
4278 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4280 MovDir[x][y] = back_dir;
4282 MovDelay[x][y] = 16 + 16 * RND(3);
4284 else if (element == EL_PACMAN)
4286 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4287 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4289 if (can_turn_left && can_turn_right)
4290 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4291 else if (can_turn_left)
4292 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4293 else if (can_turn_right)
4294 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4296 MovDir[x][y] = back_dir;
4298 MovDelay[x][y] = 6 + RND(40);
4300 else if (element == EL_PIG)
4302 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4303 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4304 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4305 boolean should_turn_left, should_turn_right, should_move_on;
4307 int rnd = RND(rnd_value);
4309 should_turn_left = (can_turn_left &&
4311 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4312 y + back_dy + left_dy)));
4313 should_turn_right = (can_turn_right &&
4315 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4316 y + back_dy + right_dy)));
4317 should_move_on = (can_move_on &&
4320 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4321 y + move_dy + left_dy) ||
4322 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4323 y + move_dy + right_dy)));
4325 if (should_turn_left || should_turn_right || should_move_on)
4327 if (should_turn_left && should_turn_right && should_move_on)
4328 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4329 rnd < 2 * rnd_value / 3 ? right_dir :
4331 else if (should_turn_left && should_turn_right)
4332 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4333 else if (should_turn_left && should_move_on)
4334 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4335 else if (should_turn_right && should_move_on)
4336 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4337 else if (should_turn_left)
4338 MovDir[x][y] = left_dir;
4339 else if (should_turn_right)
4340 MovDir[x][y] = right_dir;
4341 else if (should_move_on)
4342 MovDir[x][y] = old_move_dir;
4344 else if (can_move_on && rnd > rnd_value / 8)
4345 MovDir[x][y] = old_move_dir;
4346 else if (can_turn_left && can_turn_right)
4347 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4348 else if (can_turn_left && rnd > rnd_value / 8)
4349 MovDir[x][y] = left_dir;
4350 else if (can_turn_right && rnd > rnd_value/8)
4351 MovDir[x][y] = right_dir;
4353 MovDir[x][y] = back_dir;
4355 xx = x + move_xy[MovDir[x][y]].x;
4356 yy = y + move_xy[MovDir[x][y]].y;
4358 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4359 MovDir[x][y] = old_move_dir;
4363 else if (element == EL_DRAGON)
4365 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4366 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4367 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4369 int rnd = RND(rnd_value);
4372 if (FrameCounter < 1 && x == 0 && y == 29)
4373 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4376 if (can_move_on && rnd > rnd_value / 8)
4377 MovDir[x][y] = old_move_dir;
4378 else if (can_turn_left && can_turn_right)
4379 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4380 else if (can_turn_left && rnd > rnd_value / 8)
4381 MovDir[x][y] = left_dir;
4382 else if (can_turn_right && rnd > rnd_value / 8)
4383 MovDir[x][y] = right_dir;
4385 MovDir[x][y] = back_dir;
4387 xx = x + move_xy[MovDir[x][y]].x;
4388 yy = y + move_xy[MovDir[x][y]].y;
4391 if (FrameCounter < 1 && x == 0 && y == 29)
4392 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4393 xx, yy, Feld[xx][yy],
4398 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4399 MovDir[x][y] = old_move_dir;
4401 if (!IS_FREE(xx, yy))
4402 MovDir[x][y] = old_move_dir;
4406 if (FrameCounter < 1 && x == 0 && y == 29)
4407 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4412 else if (element == EL_MOLE)
4414 boolean can_move_on =
4415 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4416 IS_AMOEBOID(Feld[move_x][move_y]) ||
4417 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4420 boolean can_turn_left =
4421 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4422 IS_AMOEBOID(Feld[left_x][left_y])));
4424 boolean can_turn_right =
4425 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4426 IS_AMOEBOID(Feld[right_x][right_y])));
4428 if (can_turn_left && can_turn_right)
4429 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4430 else if (can_turn_left)
4431 MovDir[x][y] = left_dir;
4433 MovDir[x][y] = right_dir;
4436 if (MovDir[x][y] != old_move_dir)
4439 else if (element == EL_BALLOON)
4441 MovDir[x][y] = game.balloon_dir;
4444 else if (element == EL_SPRING)
4447 if (MovDir[x][y] & MV_HORIZONTAL &&
4448 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4449 MovDir[x][y] = MV_NO_MOVING;
4451 if (MovDir[x][y] & MV_HORIZONTAL &&
4452 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4453 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4454 MovDir[x][y] = MV_NO_MOVING;
4459 else if (element == EL_ROBOT ||
4460 element == EL_SATELLITE ||
4461 element == EL_PENGUIN)
4463 int attr_x = -1, attr_y = -1;
4474 for (i = 0; i < MAX_PLAYERS; i++)
4476 struct PlayerInfo *player = &stored_player[i];
4477 int jx = player->jx, jy = player->jy;
4479 if (!player->active)
4483 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4491 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4497 if (element == EL_PENGUIN)
4500 static int xy[4][2] =
4508 for (i = 0; i < NUM_DIRECTIONS; i++)
4510 int ex = x + xy[i][0];
4511 int ey = y + xy[i][1];
4513 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4522 MovDir[x][y] = MV_NO_MOVING;
4524 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4525 else if (attr_x > x)
4526 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4528 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4529 else if (attr_y > y)
4530 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4532 if (element == EL_ROBOT)
4536 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4537 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4538 Moving2Blocked(x, y, &newx, &newy);
4540 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4541 MovDelay[x][y] = 8 + 8 * !RND(3);
4543 MovDelay[x][y] = 16;
4545 else if (element == EL_PENGUIN)
4551 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4553 boolean first_horiz = RND(2);
4554 int new_move_dir = MovDir[x][y];
4557 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4558 Moving2Blocked(x, y, &newx, &newy);
4560 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4564 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4565 Moving2Blocked(x, y, &newx, &newy);
4567 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4570 MovDir[x][y] = old_move_dir;
4574 else /* (element == EL_SATELLITE) */
4580 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4582 boolean first_horiz = RND(2);
4583 int new_move_dir = MovDir[x][y];
4586 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4587 Moving2Blocked(x, y, &newx, &newy);
4589 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4593 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4594 Moving2Blocked(x, y, &newx, &newy);
4596 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4599 MovDir[x][y] = old_move_dir;
4604 else if (move_pattern == MV_TURNING_LEFT ||
4605 move_pattern == MV_TURNING_RIGHT ||
4606 move_pattern == MV_TURNING_LEFT_RIGHT ||
4607 move_pattern == MV_TURNING_RIGHT_LEFT ||
4608 move_pattern == MV_TURNING_RANDOM ||
4609 move_pattern == MV_ALL_DIRECTIONS)
4611 boolean can_turn_left =
4612 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4613 boolean can_turn_right =
4614 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4616 if (move_pattern == MV_TURNING_LEFT)
4617 MovDir[x][y] = left_dir;
4618 else if (move_pattern == MV_TURNING_RIGHT)
4619 MovDir[x][y] = right_dir;
4620 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4621 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4622 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4623 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4624 else if (move_pattern == MV_TURNING_RANDOM)
4625 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4626 can_turn_right && !can_turn_left ? right_dir :
4627 RND(2) ? left_dir : right_dir);
4628 else if (can_turn_left && can_turn_right)
4629 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4630 else if (can_turn_left)
4631 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4632 else if (can_turn_right)
4633 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4635 MovDir[x][y] = back_dir;
4637 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4639 else if (move_pattern == MV_HORIZONTAL ||
4640 move_pattern == MV_VERTICAL)
4642 if (move_pattern & old_move_dir)
4643 MovDir[x][y] = back_dir;
4644 else if (move_pattern == MV_HORIZONTAL)
4645 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4646 else if (move_pattern == MV_VERTICAL)
4647 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4649 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4651 else if (move_pattern & MV_ANY_DIRECTION)
4653 MovDir[x][y] = move_pattern;
4654 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4656 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4658 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4659 MovDir[x][y] = left_dir;
4660 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4661 MovDir[x][y] = right_dir;
4663 if (MovDir[x][y] != old_move_dir)
4664 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4666 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4668 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4669 MovDir[x][y] = right_dir;
4670 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4671 MovDir[x][y] = left_dir;
4673 if (MovDir[x][y] != old_move_dir)
4674 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4676 else if (move_pattern == MV_TOWARDS_PLAYER ||
4677 move_pattern == MV_AWAY_FROM_PLAYER)
4679 int attr_x = -1, attr_y = -1;
4681 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4692 for (i = 0; i < MAX_PLAYERS; i++)
4694 struct PlayerInfo *player = &stored_player[i];
4695 int jx = player->jx, jy = player->jy;
4697 if (!player->active)
4701 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4709 MovDir[x][y] = MV_NO_MOVING;
4711 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4712 else if (attr_x > x)
4713 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4715 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4716 else if (attr_y > y)
4717 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4719 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4721 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4723 boolean first_horiz = RND(2);
4724 int new_move_dir = MovDir[x][y];
4727 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4728 Moving2Blocked(x, y, &newx, &newy);
4730 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4734 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4735 Moving2Blocked(x, y, &newx, &newy);
4737 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4740 MovDir[x][y] = old_move_dir;
4743 else if (move_pattern == MV_WHEN_PUSHED ||
4744 move_pattern == MV_WHEN_DROPPED)
4746 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4747 MovDir[x][y] = MV_NO_MOVING;
4751 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4753 static int test_xy[7][2] =
4763 static int test_dir[7] =
4773 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4774 int move_preference = -1000000; /* start with very low preference */
4775 int new_move_dir = MV_NO_MOVING;
4776 int start_test = RND(4);
4779 for (i = 0; i < NUM_DIRECTIONS; i++)
4781 int move_dir = test_dir[start_test + i];
4782 int move_dir_preference;
4784 xx = x + test_xy[start_test + i][0];
4785 yy = y + test_xy[start_test + i][1];
4787 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4788 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4790 new_move_dir = move_dir;
4795 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4798 move_dir_preference = -1 * RunnerVisit[xx][yy];
4799 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4800 move_dir_preference = PlayerVisit[xx][yy];
4802 if (move_dir_preference > move_preference)
4804 /* prefer field that has not been visited for the longest time */
4805 move_preference = move_dir_preference;
4806 new_move_dir = move_dir;
4808 else if (move_dir_preference == move_preference &&
4809 move_dir == old_move_dir)
4811 /* prefer last direction when all directions are preferred equally */
4812 move_preference = move_dir_preference;
4813 new_move_dir = move_dir;
4817 MovDir[x][y] = new_move_dir;
4818 if (old_move_dir != new_move_dir)
4823 static void TurnRound(int x, int y)
4825 int direction = MovDir[x][y];
4828 GfxDir[x][y] = MovDir[x][y];
4834 GfxDir[x][y] = MovDir[x][y];
4837 if (direction != MovDir[x][y])
4842 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4845 GfxAction[x][y] = ACTION_WAITING;
4849 static boolean JustBeingPushed(int x, int y)
4853 for (i = 0; i < MAX_PLAYERS; i++)
4855 struct PlayerInfo *player = &stored_player[i];
4857 if (player->active && player->is_pushing && player->MovPos)
4859 int next_jx = player->jx + (player->jx - player->last_jx);
4860 int next_jy = player->jy + (player->jy - player->last_jy);
4862 if (x == next_jx && y == next_jy)
4870 void StartMoving(int x, int y)
4873 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4875 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4876 int element = Feld[x][y];
4882 if (MovDelay[x][y] == 0)
4883 GfxAction[x][y] = ACTION_DEFAULT;
4885 /* !!! this should be handled more generic (not only for mole) !!! */
4886 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4887 GfxAction[x][y] = ACTION_DEFAULT;
4890 if (CAN_FALL(element) && y < lev_fieldy - 1)
4892 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4893 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4894 if (JustBeingPushed(x, y))
4897 if (element == EL_QUICKSAND_FULL)
4899 if (IS_FREE(x, y + 1))
4901 InitMovingField(x, y, MV_DOWN);
4902 started_moving = TRUE;
4904 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4905 Store[x][y] = EL_ROCK;
4907 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4909 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4912 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4914 if (!MovDelay[x][y])
4915 MovDelay[x][y] = TILEY + 1;
4924 Feld[x][y] = EL_QUICKSAND_EMPTY;
4925 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4926 Store[x][y + 1] = Store[x][y];
4929 PlayLevelSoundAction(x, y, ACTION_FILLING);
4931 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4935 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4936 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4938 InitMovingField(x, y, MV_DOWN);
4939 started_moving = TRUE;
4941 Feld[x][y] = EL_QUICKSAND_FILLING;
4942 Store[x][y] = element;
4944 PlayLevelSoundAction(x, y, ACTION_FILLING);
4946 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4949 else if (element == EL_MAGIC_WALL_FULL)
4951 if (IS_FREE(x, y + 1))
4953 InitMovingField(x, y, MV_DOWN);
4954 started_moving = TRUE;
4956 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4957 Store[x][y] = EL_CHANGED(Store[x][y]);
4959 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4961 if (!MovDelay[x][y])
4962 MovDelay[x][y] = TILEY/4 + 1;
4971 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4972 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4973 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4977 else if (element == EL_BD_MAGIC_WALL_FULL)
4979 if (IS_FREE(x, y + 1))
4981 InitMovingField(x, y, MV_DOWN);
4982 started_moving = TRUE;
4984 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4985 Store[x][y] = EL_CHANGED2(Store[x][y]);
4987 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4989 if (!MovDelay[x][y])
4990 MovDelay[x][y] = TILEY/4 + 1;
4999 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5000 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5001 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5005 else if (CAN_PASS_MAGIC_WALL(element) &&
5006 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5007 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5009 InitMovingField(x, y, MV_DOWN);
5010 started_moving = TRUE;
5013 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5014 EL_BD_MAGIC_WALL_FILLING);
5015 Store[x][y] = element;
5018 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5020 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5023 SplashAcid(x, y + 1);
5025 InitMovingField(x, y, MV_DOWN);
5026 started_moving = TRUE;
5028 Store[x][y] = EL_ACID;
5030 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5031 GfxAction[x][y + 1] = ACTION_ACTIVE;
5035 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
5036 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5037 (Feld[x][y + 1] == EL_BLOCKED)) ||
5038 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5039 CAN_SMASH(element) && WasJustFalling[x][y] &&
5040 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
5044 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5045 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5046 WasJustMoving[x][y] && !Pushed[x][y + 1])
5048 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5049 WasJustMoving[x][y])
5054 /* this is needed for a special case not covered by calling "Impact()"
5055 from "ContinueMoving()": if an element moves to a tile directly below
5056 another element which was just falling on that tile (which was empty
5057 in the previous frame), the falling element above would just stop
5058 instead of smashing the element below (in previous version, the above
5059 element was just checked for "moving" instead of "falling", resulting
5060 in incorrect smashes caused by horizontal movement of the above
5061 element; also, the case of the player being the element to smash was
5062 simply not covered here... :-/ ) */
5065 WasJustMoving[x][y] = 0;
5066 WasJustFalling[x][y] = 0;
5071 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5073 if (MovDir[x][y] == MV_NO_MOVING)
5075 InitMovingField(x, y, MV_DOWN);
5076 started_moving = TRUE;
5079 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5081 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5082 MovDir[x][y] = MV_DOWN;
5084 InitMovingField(x, y, MV_DOWN);
5085 started_moving = TRUE;
5087 else if (element == EL_AMOEBA_DROP)
5089 Feld[x][y] = EL_AMOEBA_GROWING;
5090 Store[x][y] = EL_AMOEBA_WET;
5092 /* Store[x][y + 1] must be zero, because:
5093 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5096 #if OLD_GAME_BEHAVIOUR
5097 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5099 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5100 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5101 element != EL_DX_SUPABOMB)
5104 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5105 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5106 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5107 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5110 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5111 (IS_FREE(x - 1, y + 1) ||
5112 Feld[x - 1][y + 1] == EL_ACID));
5113 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5114 (IS_FREE(x + 1, y + 1) ||
5115 Feld[x + 1][y + 1] == EL_ACID));
5116 boolean can_fall_any = (can_fall_left || can_fall_right);
5117 boolean can_fall_both = (can_fall_left && can_fall_right);
5119 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5121 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5123 if (slippery_type == SLIPPERY_ONLY_LEFT)
5124 can_fall_right = FALSE;
5125 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5126 can_fall_left = FALSE;
5127 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5128 can_fall_right = FALSE;
5129 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5130 can_fall_left = FALSE;
5132 can_fall_any = (can_fall_left || can_fall_right);
5133 can_fall_both = (can_fall_left && can_fall_right);
5138 if (can_fall_both &&
5139 (game.emulation != EMU_BOULDERDASH &&
5140 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5141 can_fall_left = !(can_fall_right = RND(2));
5143 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5144 started_moving = TRUE;
5148 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5150 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5153 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5154 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5155 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5156 int belt_dir = game.belt_dir[belt_nr];
5158 if ((belt_dir == MV_LEFT && left_is_free) ||
5159 (belt_dir == MV_RIGHT && right_is_free))
5162 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5165 InitMovingField(x, y, belt_dir);
5166 started_moving = TRUE;
5169 Pushed[x][y] = TRUE;
5170 Pushed[nextx][y] = TRUE;
5173 GfxAction[x][y] = ACTION_DEFAULT;
5177 MovDir[x][y] = 0; /* if element was moving, stop it */
5182 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5183 if (CAN_MOVE(element) && !started_moving)
5185 int move_pattern = element_info[element].move_pattern;
5188 Moving2Blocked(x, y, &newx, &newy);
5191 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5194 if ((element == EL_SATELLITE ||
5195 element == EL_BALLOON ||
5196 element == EL_SPRING)
5197 && JustBeingPushed(x, y))
5202 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5203 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5204 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5207 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5208 element, element_info[element].token_name,
5209 WasJustMoving[x][y],
5210 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5211 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5212 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5213 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5217 WasJustMoving[x][y] = 0;
5220 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5223 if (Feld[x][y] != element) /* element has changed */
5225 element = Feld[x][y];
5226 move_pattern = element_info[element].move_pattern;
5228 if (!CAN_MOVE(element))
5232 if (Feld[x][y] != element) /* element has changed */
5240 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5241 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5243 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5245 Moving2Blocked(x, y, &newx, &newy);
5246 if (Feld[newx][newy] == EL_BLOCKED)
5247 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5253 if (FrameCounter < 1 && x == 0 && y == 29)
5254 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5257 if (!MovDelay[x][y]) /* start new movement phase */
5259 /* all objects that can change their move direction after each step
5260 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5262 if (element != EL_YAMYAM &&
5263 element != EL_DARK_YAMYAM &&
5264 element != EL_PACMAN &&
5265 !(move_pattern & MV_ANY_DIRECTION) &&
5266 move_pattern != MV_TURNING_LEFT &&
5267 move_pattern != MV_TURNING_RIGHT &&
5268 move_pattern != MV_TURNING_LEFT_RIGHT &&
5269 move_pattern != MV_TURNING_RIGHT_LEFT &&
5270 move_pattern != MV_TURNING_RANDOM)
5275 if (FrameCounter < 1 && x == 0 && y == 29)
5276 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5279 if (MovDelay[x][y] && (element == EL_BUG ||
5280 element == EL_SPACESHIP ||
5281 element == EL_SP_SNIKSNAK ||
5282 element == EL_SP_ELECTRON ||
5283 element == EL_MOLE))
5284 DrawLevelField(x, y);
5288 if (MovDelay[x][y]) /* wait some time before next movement */
5293 if (element == EL_YAMYAM)
5296 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5297 DrawLevelElementAnimation(x, y, element);
5301 if (MovDelay[x][y]) /* element still has to wait some time */
5304 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5305 ResetGfxAnimation(x, y);
5309 if (GfxAction[x][y] != ACTION_WAITING)
5310 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5312 GfxAction[x][y] = ACTION_WAITING;
5316 if (element == EL_ROBOT ||
5318 element == EL_PACMAN ||
5320 element == EL_YAMYAM ||
5321 element == EL_DARK_YAMYAM)
5324 DrawLevelElementAnimation(x, y, element);
5326 DrawLevelElementAnimationIfNeeded(x, y, element);
5328 PlayLevelSoundAction(x, y, ACTION_WAITING);
5330 else if (element == EL_SP_ELECTRON)
5331 DrawLevelElementAnimationIfNeeded(x, y, element);
5332 else if (element == EL_DRAGON)
5335 int dir = MovDir[x][y];
5336 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5337 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5338 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5339 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5340 dir == MV_UP ? IMG_FLAMES_1_UP :
5341 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5342 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5345 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5348 GfxAction[x][y] = ACTION_ATTACKING;
5350 if (IS_PLAYER(x, y))
5351 DrawPlayerField(x, y);
5353 DrawLevelField(x, y);
5355 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5357 for (i = 1; i <= 3; i++)
5359 int xx = x + i * dx;
5360 int yy = y + i * dy;
5361 int sx = SCREENX(xx);
5362 int sy = SCREENY(yy);
5363 int flame_graphic = graphic + (i - 1);
5365 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5370 int flamed = MovingOrBlocked2Element(xx, yy);
5374 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5376 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5377 RemoveMovingField(xx, yy);
5379 RemoveField(xx, yy);
5381 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5384 RemoveMovingField(xx, yy);
5388 if (ChangeDelay[xx][yy])
5389 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5390 Feld[xx][yy] == EL_BLOCKED));
5394 ChangeDelay[xx][yy] = 0;
5396 Feld[xx][yy] = EL_FLAMES;
5397 if (IN_SCR_FIELD(sx, sy))
5399 DrawLevelFieldCrumbledSand(xx, yy);
5400 DrawGraphic(sx, sy, flame_graphic, frame);
5405 if (Feld[xx][yy] == EL_FLAMES)
5406 Feld[xx][yy] = EL_EMPTY;
5407 DrawLevelField(xx, yy);
5412 if (MovDelay[x][y]) /* element still has to wait some time */
5414 PlayLevelSoundAction(x, y, ACTION_WAITING);
5420 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5421 for all other elements GfxAction will be set by InitMovingField() */
5422 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5423 GfxAction[x][y] = ACTION_MOVING;
5427 /* now make next step */
5429 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5431 if (DONT_COLLIDE_WITH(element) &&
5432 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5433 !PLAYER_ENEMY_PROTECTED(newx, newy))
5436 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5440 /* player killed by element which is deadly when colliding with */
5442 KillHero(PLAYERINFO(newx, newy));
5449 else if (CAN_MOVE_INTO_ACID(element) &&
5450 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5451 (MovDir[x][y] == MV_DOWN ||
5452 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5454 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5455 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5459 else if ((element == EL_PENGUIN ||
5460 element == EL_ROBOT ||
5461 element == EL_SATELLITE ||
5462 element == EL_BALLOON ||
5463 IS_CUSTOM_ELEMENT(element)) &&
5464 IN_LEV_FIELD(newx, newy) &&
5465 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5468 SplashAcid(newx, newy);
5469 Store[x][y] = EL_ACID;
5471 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5473 if (Feld[newx][newy] == EL_EXIT_OPEN)
5477 DrawLevelField(x, y);
5479 Feld[x][y] = EL_EMPTY;
5480 DrawLevelField(x, y);
5483 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5484 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5485 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5487 local_player->friends_still_needed--;
5488 if (!local_player->friends_still_needed &&
5489 !local_player->GameOver && AllPlayersGone)
5490 local_player->LevelSolved = local_player->GameOver = TRUE;
5494 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5496 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5497 DrawLevelField(newx, newy);
5499 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5501 else if (!IS_FREE(newx, newy))
5503 GfxAction[x][y] = ACTION_WAITING;
5505 if (IS_PLAYER(x, y))
5506 DrawPlayerField(x, y);
5508 DrawLevelField(x, y);
5513 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5515 if (IS_FOOD_PIG(Feld[newx][newy]))
5517 if (IS_MOVING(newx, newy))
5518 RemoveMovingField(newx, newy);
5521 Feld[newx][newy] = EL_EMPTY;
5522 DrawLevelField(newx, newy);
5525 PlayLevelSound(x, y, SND_PIG_DIGGING);
5527 else if (!IS_FREE(newx, newy))
5529 if (IS_PLAYER(x, y))
5530 DrawPlayerField(x, y);
5532 DrawLevelField(x, y);
5541 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5544 else if (IS_CUSTOM_ELEMENT(element) &&
5545 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5549 !IS_FREE(newx, newy)
5554 int new_element = Feld[newx][newy];
5557 printf("::: '%s' digs '%s' [%d]\n",
5558 element_info[element].token_name,
5559 element_info[Feld[newx][newy]].token_name,
5560 StorePlayer[newx][newy]);
5563 if (!IS_FREE(newx, newy))
5565 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5566 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5569 /* no element can dig solid indestructible elements */
5570 if (IS_INDESTRUCTIBLE(new_element) &&
5571 !IS_DIGGABLE(new_element) &&
5572 !IS_COLLECTIBLE(new_element))
5575 if (AmoebaNr[newx][newy] &&
5576 (new_element == EL_AMOEBA_FULL ||
5577 new_element == EL_BD_AMOEBA ||
5578 new_element == EL_AMOEBA_GROWING))
5580 AmoebaCnt[AmoebaNr[newx][newy]]--;
5581 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5584 if (IS_MOVING(newx, newy))
5585 RemoveMovingField(newx, newy);
5588 RemoveField(newx, newy);
5589 DrawLevelField(newx, newy);
5592 PlayLevelSoundAction(x, y, action);
5597 Store[newx][newy] = EL_EMPTY;
5598 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5599 Store[newx][newy] = element_info[element].move_leave_element;
5601 Store[newx][newy] = EL_EMPTY;
5602 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5603 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5604 Store[newx][newy] = element_info[element].move_leave_element;
5607 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5608 element_info[element].can_leave_element = TRUE;
5611 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5613 RunnerVisit[x][y] = FrameCounter;
5614 PlayerVisit[x][y] /= 8; /* expire player visit path */
5620 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5622 if (!IS_FREE(newx, newy))
5624 if (IS_PLAYER(x, y))
5625 DrawPlayerField(x, y);
5627 DrawLevelField(x, y);
5633 boolean wanna_flame = !RND(10);
5634 int dx = newx - x, dy = newy - y;
5635 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5636 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5637 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5638 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5639 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5640 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5643 IS_CLASSIC_ENEMY(element1) ||
5644 IS_CLASSIC_ENEMY(element2)) &&
5645 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5646 element1 != EL_FLAMES && element2 != EL_FLAMES)
5649 ResetGfxAnimation(x, y);
5650 GfxAction[x][y] = ACTION_ATTACKING;
5653 if (IS_PLAYER(x, y))
5654 DrawPlayerField(x, y);
5656 DrawLevelField(x, y);
5658 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5660 MovDelay[x][y] = 50;
5664 RemoveField(newx, newy);
5666 Feld[newx][newy] = EL_FLAMES;
5667 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5670 RemoveField(newx1, newy1);
5672 Feld[newx1][newy1] = EL_FLAMES;
5674 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5677 RemoveField(newx2, newy2);
5679 Feld[newx2][newy2] = EL_FLAMES;
5686 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5687 Feld[newx][newy] == EL_DIAMOND)
5689 if (IS_MOVING(newx, newy))
5690 RemoveMovingField(newx, newy);
5693 Feld[newx][newy] = EL_EMPTY;
5694 DrawLevelField(newx, newy);
5697 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5699 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5700 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5702 if (AmoebaNr[newx][newy])
5704 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5705 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5706 Feld[newx][newy] == EL_BD_AMOEBA)
5707 AmoebaCnt[AmoebaNr[newx][newy]]--;
5712 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5714 if (IS_MOVING(newx, newy))
5717 RemoveMovingField(newx, newy);
5721 Feld[newx][newy] = EL_EMPTY;
5722 DrawLevelField(newx, newy);
5725 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5727 else if ((element == EL_PACMAN || element == EL_MOLE)
5728 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5730 if (AmoebaNr[newx][newy])
5732 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5733 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5734 Feld[newx][newy] == EL_BD_AMOEBA)
5735 AmoebaCnt[AmoebaNr[newx][newy]]--;
5738 if (element == EL_MOLE)
5740 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5741 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5743 ResetGfxAnimation(x, y);
5744 GfxAction[x][y] = ACTION_DIGGING;
5745 DrawLevelField(x, y);
5747 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5749 return; /* wait for shrinking amoeba */
5751 else /* element == EL_PACMAN */
5753 Feld[newx][newy] = EL_EMPTY;
5754 DrawLevelField(newx, newy);
5755 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5758 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5759 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5760 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5762 /* wait for shrinking amoeba to completely disappear */
5765 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5767 /* object was running against a wall */
5772 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5773 DrawLevelElementAnimation(x, y, element);
5775 if (element == EL_BUG ||
5776 element == EL_SPACESHIP ||
5777 element == EL_SP_SNIKSNAK)
5778 DrawLevelField(x, y);
5779 else if (element == EL_MOLE)
5780 DrawLevelField(x, y);
5781 else if (element == EL_BD_BUTTERFLY ||
5782 element == EL_BD_FIREFLY)
5783 DrawLevelElementAnimationIfNeeded(x, y, element);
5784 else if (element == EL_SATELLITE)
5785 DrawLevelElementAnimationIfNeeded(x, y, element);
5786 else if (element == EL_SP_ELECTRON)
5787 DrawLevelElementAnimationIfNeeded(x, y, element);
5790 if (DONT_TOUCH(element))
5791 TestIfBadThingTouchesHero(x, y);
5794 PlayLevelSoundAction(x, y, ACTION_WAITING);
5800 InitMovingField(x, y, MovDir[x][y]);
5802 PlayLevelSoundAction(x, y, ACTION_MOVING);
5806 ContinueMoving(x, y);
5809 void ContinueMoving(int x, int y)
5811 int element = Feld[x][y];
5812 int stored = Store[x][y];
5813 struct ElementInfo *ei = &element_info[element];
5814 int direction = MovDir[x][y];
5815 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5816 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5817 int newx = x + dx, newy = y + dy;
5819 int nextx = newx + dx, nexty = newy + dy;
5822 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5823 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5825 boolean pushed_by_player = Pushed[x][y];
5828 MovPos[x][y] += getElementMoveStepsize(x, y);
5831 if (pushed_by_player && IS_PLAYER(x, y))
5833 /* special case: moving object pushed by player */
5834 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5837 if (pushed_by_player) /* special case: moving object pushed by player */
5838 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5841 if (ABS(MovPos[x][y]) < TILEX)
5843 DrawLevelField(x, y);
5845 return; /* element is still moving */
5848 /* element reached destination field */
5850 Feld[x][y] = EL_EMPTY;
5851 Feld[newx][newy] = element;
5852 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5854 if (element == EL_MOLE)
5856 Feld[x][y] = EL_SAND;
5858 DrawLevelFieldCrumbledSandNeighbours(x, y);
5860 else if (element == EL_QUICKSAND_FILLING)
5862 element = Feld[newx][newy] = get_next_element(element);
5863 Store[newx][newy] = Store[x][y];
5865 else if (element == EL_QUICKSAND_EMPTYING)
5867 Feld[x][y] = get_next_element(element);
5868 element = Feld[newx][newy] = Store[x][y];
5870 else if (element == EL_MAGIC_WALL_FILLING)
5872 element = Feld[newx][newy] = get_next_element(element);
5873 if (!game.magic_wall_active)
5874 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5875 Store[newx][newy] = Store[x][y];
5877 else if (element == EL_MAGIC_WALL_EMPTYING)
5879 Feld[x][y] = get_next_element(element);
5880 if (!game.magic_wall_active)
5881 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5882 element = Feld[newx][newy] = Store[x][y];
5884 else if (element == EL_BD_MAGIC_WALL_FILLING)
5886 element = Feld[newx][newy] = get_next_element(element);
5887 if (!game.magic_wall_active)
5888 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5889 Store[newx][newy] = Store[x][y];
5891 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5893 Feld[x][y] = get_next_element(element);
5894 if (!game.magic_wall_active)
5895 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5896 element = Feld[newx][newy] = Store[x][y];
5898 else if (element == EL_AMOEBA_DROPPING)
5900 Feld[x][y] = get_next_element(element);
5901 element = Feld[newx][newy] = Store[x][y];
5903 else if (element == EL_SOKOBAN_OBJECT)
5906 Feld[x][y] = Back[x][y];
5908 if (Back[newx][newy])
5909 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5911 Back[x][y] = Back[newx][newy] = 0;
5913 else if (Store[x][y] == EL_ACID)
5915 element = Feld[newx][newy] = EL_ACID;
5918 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5919 ei->move_leave_element != EL_EMPTY &&
5920 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5921 Store[x][y] != EL_EMPTY))
5923 /* some elements can leave other elements behind after moving */
5925 Feld[x][y] = ei->move_leave_element;
5926 InitField(x, y, FALSE);
5928 if (GFX_CRUMBLED(Feld[x][y]))
5929 DrawLevelFieldCrumbledSandNeighbours(x, y);
5933 Store[x][y] = EL_EMPTY;
5934 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5935 MovDelay[newx][newy] = 0;
5937 if (CAN_CHANGE(element))
5939 /* copy element change control values to new field */
5940 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5941 ChangePage[newx][newy] = ChangePage[x][y];
5942 Changed[newx][newy] = Changed[x][y];
5943 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5946 ChangeDelay[x][y] = 0;
5947 ChangePage[x][y] = -1;
5948 Changed[x][y] = CE_BITMASK_DEFAULT;
5949 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5951 /* copy animation control values to new field */
5952 GfxFrame[newx][newy] = GfxFrame[x][y];
5953 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5954 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5955 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5957 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5959 ResetGfxAnimation(x, y); /* reset animation values for old field */
5962 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5963 ei->move_leave_element != EL_EMPTY &&
5964 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5965 stored != EL_EMPTY))
5967 /* some elements can leave other elements behind after moving */
5969 Feld[x][y] = ei->move_leave_element;
5970 InitField(x, y, FALSE);
5972 if (GFX_CRUMBLED(Feld[x][y]))
5973 DrawLevelFieldCrumbledSandNeighbours(x, y);
5978 /* some elements can leave other elements behind after moving */
5979 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5980 ei->move_leave_element != EL_EMPTY &&
5981 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5982 ei->can_leave_element_last))
5984 Feld[x][y] = ei->move_leave_element;
5985 InitField(x, y, FALSE);
5987 if (GFX_CRUMBLED(Feld[x][y]))
5988 DrawLevelFieldCrumbledSandNeighbours(x, y);
5991 ei->can_leave_element_last = ei->can_leave_element;
5992 ei->can_leave_element = FALSE;
5996 /* 2.1.1 (does not work correctly for spring) */
5997 if (!CAN_MOVE(element))
5998 MovDir[newx][newy] = 0;
6002 /* (does not work for falling objects that slide horizontally) */
6003 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6004 MovDir[newx][newy] = 0;
6007 if (!CAN_MOVE(element) ||
6008 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6009 MovDir[newx][newy] = 0;
6013 if (!CAN_MOVE(element) ||
6014 (CAN_FALL(element) && direction == MV_DOWN))
6015 GfxDir[x][y] = MovDir[newx][newy] = 0;
6017 if (!CAN_MOVE(element) ||
6018 (CAN_FALL(element) && direction == MV_DOWN &&
6019 (element == EL_SPRING ||
6020 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6021 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6022 GfxDir[x][y] = MovDir[newx][newy] = 0;
6028 DrawLevelField(x, y);
6029 DrawLevelField(newx, newy);
6031 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6033 /* prevent pushed element from moving on in pushed direction */
6034 if (pushed_by_player && CAN_MOVE(element) &&
6035 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6036 !(element_info[element].move_pattern & direction))
6037 TurnRound(newx, newy);
6040 /* prevent elements on conveyor belt from moving on in last direction */
6041 if (pushed_by_conveyor && CAN_FALL(element) &&
6042 direction & MV_HORIZONTAL)
6043 MovDir[newx][newy] = 0;
6046 if (!pushed_by_player)
6048 WasJustMoving[newx][newy] = 3;
6050 if (CAN_FALL(element) && direction == MV_DOWN)
6051 WasJustFalling[newx][newy] = 3;
6054 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6056 TestIfBadThingTouchesHero(newx, newy);
6057 TestIfBadThingTouchesFriend(newx, newy);
6059 if (!IS_CUSTOM_ELEMENT(element))
6060 TestIfBadThingTouchesOtherBadThing(newx, newy);
6062 else if (element == EL_PENGUIN)
6063 TestIfFriendTouchesBadThing(newx, newy);
6065 if (CAN_FALL(element) && direction == MV_DOWN &&
6066 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6070 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6074 if (ChangePage[newx][newy] != -1) /* delayed change */
6075 ChangeElement(newx, newy, ChangePage[newx][newy]);
6080 TestIfElementHitsCustomElement(newx, newy, direction);
6084 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6086 int hitting_element = Feld[newx][newy];
6088 /* !!! fix side (direction) orientation here and elsewhere !!! */
6089 CheckElementChangeSide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6093 if (IN_LEV_FIELD(nextx, nexty))
6095 int opposite_direction = MV_DIR_OPPOSITE(direction);
6096 int hitting_side = direction;
6097 int touched_side = opposite_direction;
6098 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6099 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6100 MovDir[nextx][nexty] != direction ||
6101 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6107 CheckElementChangeSide(nextx, nexty, touched_element,
6108 CE_HIT_BY_SOMETHING, opposite_direction);
6110 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6111 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6113 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6115 struct ElementChangeInfo *change =
6116 &element_info[hitting_element].change_page[i];
6118 if (change->can_change &&
6119 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6120 change->trigger_side & touched_side &&
6121 change->trigger_element == touched_element)
6123 CheckElementChangePage(newx, newy, hitting_element,
6124 touched_element, CE_OTHER_IS_HITTING, i);
6130 if (IS_CUSTOM_ELEMENT(touched_element) &&
6131 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6133 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6135 struct ElementChangeInfo *change =
6136 &element_info[touched_element].change_page[i];
6138 if (change->can_change &&
6139 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6140 change->trigger_side & hitting_side &&
6141 change->trigger_element == hitting_element)
6143 CheckElementChangePage(nextx, nexty, touched_element,
6144 hitting_element, CE_OTHER_GETS_HIT, i);
6155 TestIfPlayerTouchesCustomElement(newx, newy);
6156 TestIfElementTouchesCustomElement(newx, newy);
6159 int AmoebeNachbarNr(int ax, int ay)
6162 int element = Feld[ax][ay];
6164 static int xy[4][2] =
6172 for (i = 0; i < NUM_DIRECTIONS; i++)
6174 int x = ax + xy[i][0];
6175 int y = ay + xy[i][1];
6177 if (!IN_LEV_FIELD(x, y))
6180 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6181 group_nr = AmoebaNr[x][y];
6187 void AmoebenVereinigen(int ax, int ay)
6189 int i, x, y, xx, yy;
6190 int new_group_nr = AmoebaNr[ax][ay];
6191 static int xy[4][2] =
6199 if (new_group_nr == 0)
6202 for (i = 0; i < NUM_DIRECTIONS; i++)
6207 if (!IN_LEV_FIELD(x, y))
6210 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6211 Feld[x][y] == EL_BD_AMOEBA ||
6212 Feld[x][y] == EL_AMOEBA_DEAD) &&
6213 AmoebaNr[x][y] != new_group_nr)
6215 int old_group_nr = AmoebaNr[x][y];
6217 if (old_group_nr == 0)
6220 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6221 AmoebaCnt[old_group_nr] = 0;
6222 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6223 AmoebaCnt2[old_group_nr] = 0;
6225 for (yy = 0; yy < lev_fieldy; yy++)
6227 for (xx = 0; xx < lev_fieldx; xx++)
6229 if (AmoebaNr[xx][yy] == old_group_nr)
6230 AmoebaNr[xx][yy] = new_group_nr;
6237 void AmoebeUmwandeln(int ax, int ay)
6241 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6243 int group_nr = AmoebaNr[ax][ay];
6248 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6249 printf("AmoebeUmwandeln(): This should never happen!\n");
6254 for (y = 0; y < lev_fieldy; y++)
6256 for (x = 0; x < lev_fieldx; x++)
6258 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6261 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6265 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6266 SND_AMOEBA_TURNING_TO_GEM :
6267 SND_AMOEBA_TURNING_TO_ROCK));
6272 static int xy[4][2] =
6280 for (i = 0; i < NUM_DIRECTIONS; i++)
6285 if (!IN_LEV_FIELD(x, y))
6288 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6290 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6291 SND_AMOEBA_TURNING_TO_GEM :
6292 SND_AMOEBA_TURNING_TO_ROCK));
6299 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6302 int group_nr = AmoebaNr[ax][ay];
6303 boolean done = FALSE;
6308 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6309 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6314 for (y = 0; y < lev_fieldy; y++)
6316 for (x = 0; x < lev_fieldx; x++)
6318 if (AmoebaNr[x][y] == group_nr &&
6319 (Feld[x][y] == EL_AMOEBA_DEAD ||
6320 Feld[x][y] == EL_BD_AMOEBA ||
6321 Feld[x][y] == EL_AMOEBA_GROWING))
6324 Feld[x][y] = new_element;
6325 InitField(x, y, FALSE);
6326 DrawLevelField(x, y);
6333 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6334 SND_BD_AMOEBA_TURNING_TO_ROCK :
6335 SND_BD_AMOEBA_TURNING_TO_GEM));
6338 void AmoebeWaechst(int x, int y)
6340 static unsigned long sound_delay = 0;
6341 static unsigned long sound_delay_value = 0;
6343 if (!MovDelay[x][y]) /* start new growing cycle */
6347 if (DelayReached(&sound_delay, sound_delay_value))
6350 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6352 if (Store[x][y] == EL_BD_AMOEBA)
6353 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6355 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6357 sound_delay_value = 30;
6361 if (MovDelay[x][y]) /* wait some time before growing bigger */
6364 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6366 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6367 6 - MovDelay[x][y]);
6369 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6372 if (!MovDelay[x][y])
6374 Feld[x][y] = Store[x][y];
6376 DrawLevelField(x, y);
6381 void AmoebaDisappearing(int x, int y)
6383 static unsigned long sound_delay = 0;
6384 static unsigned long sound_delay_value = 0;
6386 if (!MovDelay[x][y]) /* start new shrinking cycle */
6390 if (DelayReached(&sound_delay, sound_delay_value))
6391 sound_delay_value = 30;
6394 if (MovDelay[x][y]) /* wait some time before shrinking */
6397 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6399 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6400 6 - MovDelay[x][y]);
6402 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6405 if (!MovDelay[x][y])
6407 Feld[x][y] = EL_EMPTY;
6408 DrawLevelField(x, y);
6410 /* don't let mole enter this field in this cycle;
6411 (give priority to objects falling to this field from above) */
6417 void AmoebeAbleger(int ax, int ay)
6420 int element = Feld[ax][ay];
6421 int graphic = el2img(element);
6422 int newax = ax, neway = ay;
6423 static int xy[4][2] =
6431 if (!level.amoeba_speed)
6433 Feld[ax][ay] = EL_AMOEBA_DEAD;
6434 DrawLevelField(ax, ay);
6438 if (IS_ANIMATED(graphic))
6439 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6441 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6442 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6444 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6447 if (MovDelay[ax][ay])
6451 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6454 int x = ax + xy[start][0];
6455 int y = ay + xy[start][1];
6457 if (!IN_LEV_FIELD(x, y))
6461 if (IS_FREE(x, y) ||
6462 CAN_GROW_INTO(Feld[x][y]) ||
6463 Feld[x][y] == EL_QUICKSAND_EMPTY)
6469 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6470 if (IS_FREE(x, y) ||
6471 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6478 if (newax == ax && neway == ay)
6481 else /* normal or "filled" (BD style) amoeba */
6484 boolean waiting_for_player = FALSE;
6486 for (i = 0; i < NUM_DIRECTIONS; i++)
6488 int j = (start + i) % 4;
6489 int x = ax + xy[j][0];
6490 int y = ay + xy[j][1];
6492 if (!IN_LEV_FIELD(x, y))
6496 if (IS_FREE(x, y) ||
6497 CAN_GROW_INTO(Feld[x][y]) ||
6498 Feld[x][y] == EL_QUICKSAND_EMPTY)
6505 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6506 if (IS_FREE(x, y) ||
6507 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6514 else if (IS_PLAYER(x, y))
6515 waiting_for_player = TRUE;
6518 if (newax == ax && neway == ay) /* amoeba cannot grow */
6520 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6522 Feld[ax][ay] = EL_AMOEBA_DEAD;
6523 DrawLevelField(ax, ay);
6524 AmoebaCnt[AmoebaNr[ax][ay]]--;
6526 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6528 if (element == EL_AMOEBA_FULL)
6529 AmoebeUmwandeln(ax, ay);
6530 else if (element == EL_BD_AMOEBA)
6531 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6536 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6538 /* amoeba gets larger by growing in some direction */
6540 int new_group_nr = AmoebaNr[ax][ay];
6543 if (new_group_nr == 0)
6545 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6546 printf("AmoebeAbleger(): This should never happen!\n");
6551 AmoebaNr[newax][neway] = new_group_nr;
6552 AmoebaCnt[new_group_nr]++;
6553 AmoebaCnt2[new_group_nr]++;
6555 /* if amoeba touches other amoeba(s) after growing, unify them */
6556 AmoebenVereinigen(newax, neway);
6558 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6560 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6566 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6567 (neway == lev_fieldy - 1 && newax != ax))
6569 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6570 Store[newax][neway] = element;
6572 else if (neway == ay)
6574 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6576 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6578 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6583 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6584 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6585 Store[ax][ay] = EL_AMOEBA_DROP;
6586 ContinueMoving(ax, ay);
6590 DrawLevelField(newax, neway);
6593 void Life(int ax, int ay)
6596 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6598 int element = Feld[ax][ay];
6599 int graphic = el2img(element);
6600 boolean changed = FALSE;
6602 if (IS_ANIMATED(graphic))
6603 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6608 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6609 MovDelay[ax][ay] = life_time;
6611 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6614 if (MovDelay[ax][ay])
6618 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6620 int xx = ax+x1, yy = ay+y1;
6623 if (!IN_LEV_FIELD(xx, yy))
6626 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6628 int x = xx+x2, y = yy+y2;
6630 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6633 if (((Feld[x][y] == element ||
6634 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6636 (IS_FREE(x, y) && Stop[x][y]))
6640 if (xx == ax && yy == ay) /* field in the middle */
6642 if (nachbarn < life[0] || nachbarn > life[1])
6644 Feld[xx][yy] = EL_EMPTY;
6646 DrawLevelField(xx, yy);
6647 Stop[xx][yy] = TRUE;
6652 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6653 { /* free border field */
6654 if (nachbarn >= life[2] && nachbarn <= life[3])
6656 Feld[xx][yy] = element;
6657 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6659 DrawLevelField(xx, yy);
6660 Stop[xx][yy] = TRUE;
6665 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6666 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6667 { /* free border field */
6668 if (nachbarn >= life[2] && nachbarn <= life[3])
6670 Feld[xx][yy] = element;
6671 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6673 DrawLevelField(xx, yy);
6674 Stop[xx][yy] = TRUE;
6682 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6683 SND_GAME_OF_LIFE_GROWING);
6686 static void InitRobotWheel(int x, int y)
6688 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6691 static void RunRobotWheel(int x, int y)
6693 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6696 static void StopRobotWheel(int x, int y)
6698 if (ZX == x && ZY == y)
6702 static void InitTimegateWheel(int x, int y)
6704 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6707 static void RunTimegateWheel(int x, int y)
6709 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6712 void CheckExit(int x, int y)
6714 if (local_player->gems_still_needed > 0 ||
6715 local_player->sokobanfields_still_needed > 0 ||
6716 local_player->lights_still_needed > 0)
6718 int element = Feld[x][y];
6719 int graphic = el2img(element);
6721 if (IS_ANIMATED(graphic))
6722 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6727 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6730 Feld[x][y] = EL_EXIT_OPENING;
6732 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6735 void CheckExitSP(int x, int y)
6737 if (local_player->gems_still_needed > 0)
6739 int element = Feld[x][y];
6740 int graphic = el2img(element);
6742 if (IS_ANIMATED(graphic))
6743 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6748 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6751 Feld[x][y] = EL_SP_EXIT_OPENING;
6753 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6756 static void CloseAllOpenTimegates()
6760 for (y = 0; y < lev_fieldy; y++)
6762 for (x = 0; x < lev_fieldx; x++)
6764 int element = Feld[x][y];
6766 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6768 Feld[x][y] = EL_TIMEGATE_CLOSING;
6770 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6772 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6779 void EdelsteinFunkeln(int x, int y)
6781 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6784 if (Feld[x][y] == EL_BD_DIAMOND)
6787 if (MovDelay[x][y] == 0) /* next animation frame */
6788 MovDelay[x][y] = 11 * !SimpleRND(500);
6790 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6794 if (setup.direct_draw && MovDelay[x][y])
6795 SetDrawtoField(DRAW_BUFFERED);
6797 DrawLevelElementAnimation(x, y, Feld[x][y]);
6799 if (MovDelay[x][y] != 0)
6801 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6802 10 - MovDelay[x][y]);
6804 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6806 if (setup.direct_draw)
6810 dest_x = FX + SCREENX(x) * TILEX;
6811 dest_y = FY + SCREENY(y) * TILEY;
6813 BlitBitmap(drawto_field, window,
6814 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6815 SetDrawtoField(DRAW_DIRECT);
6821 void MauerWaechst(int x, int y)
6825 if (!MovDelay[x][y]) /* next animation frame */
6826 MovDelay[x][y] = 3 * delay;
6828 if (MovDelay[x][y]) /* wait some time before next frame */
6832 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6834 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6835 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6837 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6840 if (!MovDelay[x][y])
6842 if (MovDir[x][y] == MV_LEFT)
6844 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6845 DrawLevelField(x - 1, y);
6847 else if (MovDir[x][y] == MV_RIGHT)
6849 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6850 DrawLevelField(x + 1, y);
6852 else if (MovDir[x][y] == MV_UP)
6854 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6855 DrawLevelField(x, y - 1);
6859 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6860 DrawLevelField(x, y + 1);
6863 Feld[x][y] = Store[x][y];
6865 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6866 DrawLevelField(x, y);
6871 void MauerAbleger(int ax, int ay)
6873 int element = Feld[ax][ay];
6874 int graphic = el2img(element);
6875 boolean oben_frei = FALSE, unten_frei = FALSE;
6876 boolean links_frei = FALSE, rechts_frei = FALSE;
6877 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6878 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6879 boolean new_wall = FALSE;
6881 if (IS_ANIMATED(graphic))
6882 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6884 if (!MovDelay[ax][ay]) /* start building new wall */
6885 MovDelay[ax][ay] = 6;
6887 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6890 if (MovDelay[ax][ay])
6894 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6896 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6898 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6900 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6903 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6904 element == EL_EXPANDABLE_WALL_ANY)
6908 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6909 Store[ax][ay-1] = element;
6910 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6911 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6912 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6913 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6918 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6919 Store[ax][ay+1] = element;
6920 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6921 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6922 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6923 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6928 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6929 element == EL_EXPANDABLE_WALL_ANY ||
6930 element == EL_EXPANDABLE_WALL)
6934 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6935 Store[ax-1][ay] = element;
6936 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6937 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6938 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6939 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6945 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6946 Store[ax+1][ay] = element;
6947 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6948 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6949 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6950 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6955 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6956 DrawLevelField(ax, ay);
6958 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6960 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6961 unten_massiv = TRUE;
6962 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6963 links_massiv = TRUE;
6964 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6965 rechts_massiv = TRUE;
6967 if (((oben_massiv && unten_massiv) ||
6968 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6969 element == EL_EXPANDABLE_WALL) &&
6970 ((links_massiv && rechts_massiv) ||
6971 element == EL_EXPANDABLE_WALL_VERTICAL))
6972 Feld[ax][ay] = EL_WALL;
6976 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6978 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6982 void CheckForDragon(int x, int y)
6985 boolean dragon_found = FALSE;
6986 static int xy[4][2] =
6994 for (i = 0; i < NUM_DIRECTIONS; i++)
6996 for (j = 0; j < 4; j++)
6998 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7000 if (IN_LEV_FIELD(xx, yy) &&
7001 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7003 if (Feld[xx][yy] == EL_DRAGON)
7004 dragon_found = TRUE;
7013 for (i = 0; i < NUM_DIRECTIONS; i++)
7015 for (j = 0; j < 3; j++)
7017 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7019 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7021 Feld[xx][yy] = EL_EMPTY;
7022 DrawLevelField(xx, yy);
7031 static void InitBuggyBase(int x, int y)
7033 int element = Feld[x][y];
7034 int activating_delay = FRAMES_PER_SECOND / 4;
7037 (element == EL_SP_BUGGY_BASE ?
7038 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7039 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7041 element == EL_SP_BUGGY_BASE_ACTIVE ?
7042 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7045 static void WarnBuggyBase(int x, int y)
7048 static int xy[4][2] =
7056 for (i = 0; i < NUM_DIRECTIONS; i++)
7058 int xx = x + xy[i][0], yy = y + xy[i][1];
7060 if (IS_PLAYER(xx, yy))
7062 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7069 static void InitTrap(int x, int y)
7071 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7074 static void ActivateTrap(int x, int y)
7076 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7079 static void ChangeActiveTrap(int x, int y)
7081 int graphic = IMG_TRAP_ACTIVE;
7083 /* if new animation frame was drawn, correct crumbled sand border */
7084 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7085 DrawLevelFieldCrumbledSand(x, y);
7088 static void ChangeElementNowExt(int x, int y, int target_element)
7090 int previous_move_direction = MovDir[x][y];
7092 /* check if element under player changes from accessible to unaccessible
7093 (needed for special case of dropping element which then changes) */
7094 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7095 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7102 Feld[x][y] = target_element;
7104 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7106 ResetGfxAnimation(x, y);
7107 ResetRandomAnimationValue(x, y);
7109 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7110 MovDir[x][y] = previous_move_direction;
7113 InitField_WithBug1(x, y, FALSE);
7115 InitField(x, y, FALSE);
7116 if (CAN_MOVE(Feld[x][y]))
7120 DrawLevelField(x, y);
7122 if (GFX_CRUMBLED(Feld[x][y]))
7123 DrawLevelFieldCrumbledSandNeighbours(x, y);
7125 TestIfBadThingTouchesHero(x, y);
7126 TestIfPlayerTouchesCustomElement(x, y);
7127 TestIfElementTouchesCustomElement(x, y);
7129 if (ELEM_IS_PLAYER(target_element))
7130 RelocatePlayer(x, y, target_element);
7133 static boolean ChangeElementNow(int x, int y, int element, int page)
7135 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7138 /* always use default change event to prevent running into a loop */
7139 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7140 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7142 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7144 /* reset actual trigger element and player */
7145 change->actual_trigger_element = EL_EMPTY;
7146 change->actual_trigger_player = EL_PLAYER_1;
7149 /* do not change already changed elements with same change event */
7151 if (Changed[x][y] & ChangeEvent[x][y])
7158 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7160 CheckTriggeredElementChangePage(x,y, Feld[x][y], CE_OTHER_IS_CHANGING, page);
7162 if (change->explode)
7169 if (change->use_target_content)
7171 boolean complete_replace = TRUE;
7172 boolean can_replace[3][3];
7175 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7178 boolean is_diggable;
7179 boolean is_destructible;
7180 int ex = x + xx - 1;
7181 int ey = y + yy - 1;
7182 int content_element = change->target_content[xx][yy];
7185 can_replace[xx][yy] = TRUE;
7187 if (ex == x && ey == y) /* do not check changing element itself */
7190 if (content_element == EL_EMPTY_SPACE)
7192 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7197 if (!IN_LEV_FIELD(ex, ey))
7199 can_replace[xx][yy] = FALSE;
7200 complete_replace = FALSE;
7207 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7208 e = MovingOrBlocked2Element(ex, ey);
7211 is_empty = (IS_FREE(ex, ey) || (IS_PLAYER(ex, ey) &&
7212 IS_WALKABLE(content_element)));
7213 is_diggable = (is_empty || IS_DIGGABLE(e));
7214 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7216 can_replace[xx][yy] =
7217 ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7218 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7219 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7221 if (!can_replace[xx][yy])
7222 complete_replace = FALSE;
7224 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7225 IS_WALKABLE(content_element)));
7227 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7229 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7232 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7233 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7234 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7236 can_replace[xx][yy] = FALSE;
7237 complete_replace = FALSE;
7242 if (!change->only_if_complete || complete_replace)
7244 boolean something_has_changed = FALSE;
7246 if (change->only_if_complete && change->use_random_replace &&
7247 RND(100) < change->random_percentage)
7250 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7252 int ex = x + xx - 1;
7253 int ey = y + yy - 1;
7254 int content_element;
7256 if (can_replace[xx][yy] && (!change->use_random_replace ||
7257 RND(100) < change->random_percentage))
7259 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7260 RemoveMovingField(ex, ey);
7262 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7264 content_element = change->target_content[xx][yy];
7265 target_element = GET_TARGET_ELEMENT(content_element, change);
7267 ChangeElementNowExt(ex, ey, target_element);
7269 something_has_changed = TRUE;
7271 /* for symmetry reasons, freeze newly created border elements */
7272 if (ex != x || ey != y)
7273 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7277 if (something_has_changed)
7278 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7283 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7285 ChangeElementNowExt(x, y, target_element);
7287 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7293 static void ChangeElement(int x, int y, int page)
7295 int element = MovingOrBlocked2Element(x, y);
7296 struct ElementInfo *ei = &element_info[element];
7297 struct ElementChangeInfo *change = &ei->change_page[page];
7300 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7303 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7304 x, y, element, element_info[element].token_name);
7305 printf("ChangeElement(): This should never happen!\n");
7310 /* this can happen with classic bombs on walkable, changing elements */
7311 if (!CAN_CHANGE(element))
7314 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7315 ChangeDelay[x][y] = 0;
7321 if (ChangeDelay[x][y] == 0) /* initialize element change */
7323 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7324 RND(change->delay_random * change->delay_frames)) + 1;
7326 ResetGfxAnimation(x, y);
7327 ResetRandomAnimationValue(x, y);
7329 if (change->pre_change_function)
7330 change->pre_change_function(x, y);
7333 ChangeDelay[x][y]--;
7335 if (ChangeDelay[x][y] != 0) /* continue element change */
7337 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7339 if (IS_ANIMATED(graphic))
7340 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7342 if (change->change_function)
7343 change->change_function(x, y);
7345 else /* finish element change */
7347 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7349 page = ChangePage[x][y];
7350 ChangePage[x][y] = -1;
7352 change = &ei->change_page[page];
7356 if (IS_MOVING(x, y) && !change->explode)
7358 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7361 ChangeDelay[x][y] = 1; /* try change after next move step */
7362 ChangePage[x][y] = page; /* remember page to use for change */
7367 if (ChangeElementNow(x, y, element, page))
7369 if (change->post_change_function)
7370 change->post_change_function(x, y);
7375 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7376 int trigger_element,
7383 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7385 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7388 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7390 int element = EL_CUSTOM_START + i;
7392 boolean change_element = FALSE;
7395 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7398 for (j = 0; j < element_info[element].num_change_pages; j++)
7400 struct ElementChangeInfo *change = &element_info[element].change_page[j];
7402 if (change->can_change &&
7403 change->events & CH_EVENT_BIT(trigger_event) &&
7404 change->trigger_side & trigger_side &&
7405 change->trigger_player & trigger_player &&
7406 change->trigger_page & trigger_page_bits &&
7407 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7410 if (!(change->events & CH_EVENT_BIT(trigger_event)))
7411 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7412 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7415 change_element = TRUE;
7418 change->actual_trigger_element = trigger_element;
7419 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7425 if (!change_element)
7428 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7431 if (x == lx && y == ly) /* do not change trigger element itself */
7435 if (Feld[x][y] == element)
7437 ChangeDelay[x][y] = 1;
7438 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7439 ChangeElement(x, y, page);
7447 static boolean CheckElementChangeExt(int x, int y,
7449 int trigger_element,
7455 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7458 if (Feld[x][y] == EL_BLOCKED)
7460 Blocked2Moving(x, y, &x, &y);
7461 element = Feld[x][y];
7465 if (Feld[x][y] != element) /* check if element has already changed */
7468 printf("::: %d ('%s') != %d ('%s') [%d]\n",
7469 Feld[x][y], element_info[Feld[x][y]].token_name,
7470 element, element_info[element].token_name,
7479 if (trigger_page < 0)
7481 boolean change_element = FALSE;
7484 for (i = 0; i < element_info[element].num_change_pages; i++)
7486 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7488 if (change->can_change &&
7489 change->events & CH_EVENT_BIT(trigger_event) &&
7490 change->trigger_side & trigger_side &&
7491 change->trigger_player & trigger_player)
7493 change_element = TRUE;
7496 change->actual_trigger_element = trigger_element;
7497 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7503 if (!change_element)
7508 struct ElementInfo *ei = &element_info[element];
7509 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7511 change->actual_trigger_element = trigger_element;
7512 change->actual_trigger_player = EL_PLAYER_1; /* unused */
7517 /* !!! this check misses pages with same event, but different side !!! */
7519 if (trigger_page < 0)
7520 trigger_page = element_info[element].event_page_nr[trigger_event];
7522 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7526 ChangeDelay[x][y] = 1;
7527 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7528 ChangeElement(x, y, trigger_page);
7533 static void PlayPlayerSound(struct PlayerInfo *player)
7535 int jx = player->jx, jy = player->jy;
7536 int element = player->element_nr;
7537 int last_action = player->last_action_waiting;
7538 int action = player->action_waiting;
7540 if (player->is_waiting)
7542 if (action != last_action)
7543 PlayLevelSoundElementAction(jx, jy, element, action);
7545 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7549 if (action != last_action)
7550 StopSound(element_info[element].sound[last_action]);
7552 if (last_action == ACTION_SLEEPING)
7553 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7557 static void PlayAllPlayersSound()
7561 for (i = 0; i < MAX_PLAYERS; i++)
7562 if (stored_player[i].active)
7563 PlayPlayerSound(&stored_player[i]);
7566 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7568 boolean last_waiting = player->is_waiting;
7569 int move_dir = player->MovDir;
7571 player->last_action_waiting = player->action_waiting;
7575 if (!last_waiting) /* not waiting -> waiting */
7577 player->is_waiting = TRUE;
7579 player->frame_counter_bored =
7581 game.player_boring_delay_fixed +
7582 SimpleRND(game.player_boring_delay_random);
7583 player->frame_counter_sleeping =
7585 game.player_sleeping_delay_fixed +
7586 SimpleRND(game.player_sleeping_delay_random);
7588 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7591 if (game.player_sleeping_delay_fixed +
7592 game.player_sleeping_delay_random > 0 &&
7593 player->anim_delay_counter == 0 &&
7594 player->post_delay_counter == 0 &&
7595 FrameCounter >= player->frame_counter_sleeping)
7596 player->is_sleeping = TRUE;
7597 else if (game.player_boring_delay_fixed +
7598 game.player_boring_delay_random > 0 &&
7599 FrameCounter >= player->frame_counter_bored)
7600 player->is_bored = TRUE;
7602 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7603 player->is_bored ? ACTION_BORING :
7606 if (player->is_sleeping)
7608 if (player->num_special_action_sleeping > 0)
7610 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7612 int last_special_action = player->special_action_sleeping;
7613 int num_special_action = player->num_special_action_sleeping;
7614 int special_action =
7615 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7616 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7617 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7618 last_special_action + 1 : ACTION_SLEEPING);
7619 int special_graphic =
7620 el_act_dir2img(player->element_nr, special_action, move_dir);
7622 player->anim_delay_counter =
7623 graphic_info[special_graphic].anim_delay_fixed +
7624 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7625 player->post_delay_counter =
7626 graphic_info[special_graphic].post_delay_fixed +
7627 SimpleRND(graphic_info[special_graphic].post_delay_random);
7629 player->special_action_sleeping = special_action;
7632 if (player->anim_delay_counter > 0)
7634 player->action_waiting = player->special_action_sleeping;
7635 player->anim_delay_counter--;
7637 else if (player->post_delay_counter > 0)
7639 player->post_delay_counter--;
7643 else if (player->is_bored)
7645 if (player->num_special_action_bored > 0)
7647 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7649 int special_action =
7650 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7651 int special_graphic =
7652 el_act_dir2img(player->element_nr, special_action, move_dir);
7654 player->anim_delay_counter =
7655 graphic_info[special_graphic].anim_delay_fixed +
7656 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7657 player->post_delay_counter =
7658 graphic_info[special_graphic].post_delay_fixed +
7659 SimpleRND(graphic_info[special_graphic].post_delay_random);
7661 player->special_action_bored = special_action;
7664 if (player->anim_delay_counter > 0)
7666 player->action_waiting = player->special_action_bored;
7667 player->anim_delay_counter--;
7669 else if (player->post_delay_counter > 0)
7671 player->post_delay_counter--;
7676 else if (last_waiting) /* waiting -> not waiting */
7678 player->is_waiting = FALSE;
7679 player->is_bored = FALSE;
7680 player->is_sleeping = FALSE;
7682 player->frame_counter_bored = -1;
7683 player->frame_counter_sleeping = -1;
7685 player->anim_delay_counter = 0;
7686 player->post_delay_counter = 0;
7688 player->action_waiting = ACTION_DEFAULT;
7690 player->special_action_bored = ACTION_DEFAULT;
7691 player->special_action_sleeping = ACTION_DEFAULT;
7696 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7699 static byte stored_player_action[MAX_PLAYERS];
7700 static int num_stored_actions = 0;
7702 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7703 int left = player_action & JOY_LEFT;
7704 int right = player_action & JOY_RIGHT;
7705 int up = player_action & JOY_UP;
7706 int down = player_action & JOY_DOWN;
7707 int button1 = player_action & JOY_BUTTON_1;
7708 int button2 = player_action & JOY_BUTTON_2;
7709 int dx = (left ? -1 : right ? 1 : 0);
7710 int dy = (up ? -1 : down ? 1 : 0);
7713 stored_player_action[player->index_nr] = 0;
7714 num_stored_actions++;
7718 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7721 if (!player->active || tape.pausing)
7725 printf("::: [%d %d %d %d] [%d %d]\n",
7726 left, right, up, down, button1, button2);
7732 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7737 if (player->MovPos == 0)
7738 CheckGravityMovement(player);
7741 snapped = SnapField(player, dx, dy);
7745 dropped = DropElement(player);
7747 moved = MovePlayer(player, dx, dy);
7750 if (tape.single_step && tape.recording && !tape.pausing)
7752 if (button1 || (dropped && !moved))
7754 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7755 SnapField(player, 0, 0); /* stop snapping */
7759 SetPlayerWaiting(player, FALSE);
7762 return player_action;
7764 stored_player_action[player->index_nr] = player_action;
7770 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7773 /* no actions for this player (no input at player's configured device) */
7775 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7776 SnapField(player, 0, 0);
7777 CheckGravityMovementWhenNotMoving(player);
7779 if (player->MovPos == 0)
7780 SetPlayerWaiting(player, TRUE);
7782 if (player->MovPos == 0) /* needed for tape.playing */
7783 player->is_moving = FALSE;
7785 player->is_dropping = FALSE;
7791 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7793 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7795 TapeRecordAction(stored_player_action);
7796 num_stored_actions = 0;
7803 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7805 static byte stored_player_action[MAX_PLAYERS];
7806 static int num_stored_actions = 0;
7807 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7808 int left = player_action & JOY_LEFT;
7809 int right = player_action & JOY_RIGHT;
7810 int up = player_action & JOY_UP;
7811 int down = player_action & JOY_DOWN;
7812 int button1 = player_action & JOY_BUTTON_1;
7813 int button2 = player_action & JOY_BUTTON_2;
7814 int dx = (left ? -1 : right ? 1 : 0);
7815 int dy = (up ? -1 : down ? 1 : 0);
7817 stored_player_action[player->index_nr] = 0;
7818 num_stored_actions++;
7820 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7822 if (!player->active || tape.pausing)
7827 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7830 snapped = SnapField(player, dx, dy);
7834 dropped = DropElement(player);
7836 moved = MovePlayer(player, dx, dy);
7839 if (tape.single_step && tape.recording && !tape.pausing)
7841 if (button1 || (dropped && !moved))
7843 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7844 SnapField(player, 0, 0); /* stop snapping */
7848 stored_player_action[player->index_nr] = player_action;
7852 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7854 /* no actions for this player (no input at player's configured device) */
7856 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7857 SnapField(player, 0, 0);
7858 CheckGravityMovementWhenNotMoving(player);
7860 if (player->MovPos == 0)
7861 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7863 if (player->MovPos == 0) /* needed for tape.playing */
7864 player->is_moving = FALSE;
7867 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7869 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7871 TapeRecordAction(stored_player_action);
7872 num_stored_actions = 0;
7879 static unsigned long action_delay = 0;
7880 unsigned long action_delay_value;
7881 int magic_wall_x = 0, magic_wall_y = 0;
7882 int i, x, y, element, graphic;
7883 byte *recorded_player_action;
7884 byte summarized_player_action = 0;
7886 byte tape_action[MAX_PLAYERS];
7889 if (game_status != GAME_MODE_PLAYING)
7892 action_delay_value =
7893 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7895 if (tape.playing && tape.warp_forward && !tape.pausing)
7896 action_delay_value = 0;
7898 /* ---------- main game synchronization point ---------- */
7900 WaitUntilDelayReached(&action_delay, action_delay_value);
7902 if (network_playing && !network_player_action_received)
7906 printf("DEBUG: try to get network player actions in time\n");
7910 #if defined(PLATFORM_UNIX)
7911 /* last chance to get network player actions without main loop delay */
7915 if (game_status != GAME_MODE_PLAYING)
7918 if (!network_player_action_received)
7922 printf("DEBUG: failed to get network player actions in time\n");
7933 printf("::: getting new tape action [%d]\n", FrameCounter);
7936 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7939 if (recorded_player_action == NULL && tape.pausing)
7944 printf("::: %d\n", stored_player[0].action);
7948 if (recorded_player_action != NULL)
7949 for (i = 0; i < MAX_PLAYERS; i++)
7950 stored_player[i].action = recorded_player_action[i];
7953 for (i = 0; i < MAX_PLAYERS; i++)
7955 summarized_player_action |= stored_player[i].action;
7957 if (!network_playing)
7958 stored_player[i].effective_action = stored_player[i].action;
7961 #if defined(PLATFORM_UNIX)
7962 if (network_playing)
7963 SendToServer_MovePlayer(summarized_player_action);
7966 if (!options.network && !setup.team_mode)
7967 local_player->effective_action = summarized_player_action;
7970 if (recorded_player_action != NULL)
7971 for (i = 0; i < MAX_PLAYERS; i++)
7972 stored_player[i].effective_action = recorded_player_action[i];
7976 for (i = 0; i < MAX_PLAYERS; i++)
7978 tape_action[i] = stored_player[i].effective_action;
7980 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7981 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7984 /* only save actions from input devices, but not programmed actions */
7986 TapeRecordAction(tape_action);
7989 for (i = 0; i < MAX_PLAYERS; i++)
7991 int actual_player_action = stored_player[i].effective_action;
7994 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
7995 - rnd_equinox_tetrachloride 048
7996 - rnd_equinox_tetrachloride_ii 096
7997 - rnd_emanuel_schmieg 002
7998 - doctor_sloan_ww 001, 020
8000 if (stored_player[i].MovPos == 0)
8001 CheckGravityMovement(&stored_player[i]);
8005 /* overwrite programmed action with tape action */
8006 if (stored_player[i].programmed_action)
8007 actual_player_action = stored_player[i].programmed_action;
8011 if (stored_player[i].programmed_action)
8012 printf("::: %d\n", stored_player[i].programmed_action);
8015 if (recorded_player_action)
8018 if (stored_player[i].programmed_action &&
8019 stored_player[i].programmed_action != recorded_player_action[i])
8020 printf("::: %d: %d <-> %d\n", i,
8021 stored_player[i].programmed_action, recorded_player_action[i]);
8025 actual_player_action = recorded_player_action[i];
8030 /* overwrite tape action with programmed action */
8031 if (stored_player[i].programmed_action)
8032 actual_player_action = stored_player[i].programmed_action;
8037 printf("::: action: %d: %x [%d]\n",
8038 stored_player[i].MovPos, actual_player_action, FrameCounter);
8042 PlayerActions(&stored_player[i], actual_player_action);
8044 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8046 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8047 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8050 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8055 TapeRecordAction(tape_action);
8058 network_player_action_received = FALSE;
8060 ScrollScreen(NULL, SCROLL_GO_ON);
8066 for (i = 0; i < MAX_PLAYERS; i++)
8067 stored_player[i].Frame++;
8071 /* for downwards compatibility, the following code emulates a fixed bug that
8072 occured when pushing elements (causing elements that just made their last
8073 pushing step to already (if possible) make their first falling step in the
8074 same game frame, which is bad); this code is also needed to use the famous
8075 "spring push bug" which is used in older levels and might be wanted to be
8076 used also in newer levels, but in this case the buggy pushing code is only
8077 affecting the "spring" element and no other elements */
8080 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8082 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8085 for (i = 0; i < MAX_PLAYERS; i++)
8087 struct PlayerInfo *player = &stored_player[i];
8092 if (player->active && player->is_pushing && player->is_moving &&
8094 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8095 Feld[x][y] == EL_SPRING))
8097 if (player->active && player->is_pushing && player->is_moving &&
8101 ContinueMoving(x, y);
8103 /* continue moving after pushing (this is actually a bug) */
8104 if (!IS_MOVING(x, y))
8113 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8115 Changed[x][y] = CE_BITMASK_DEFAULT;
8116 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8119 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8121 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8122 printf("GameActions(): This should never happen!\n");
8124 ChangePage[x][y] = -1;
8129 if (WasJustMoving[x][y] > 0)
8130 WasJustMoving[x][y]--;
8131 if (WasJustFalling[x][y] > 0)
8132 WasJustFalling[x][y]--;
8137 /* reset finished pushing action (not done in ContinueMoving() to allow
8138 continous pushing animation for elements with zero push delay) */
8139 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8141 ResetGfxAnimation(x, y);
8142 DrawLevelField(x, y);
8147 if (IS_BLOCKED(x, y))
8151 Blocked2Moving(x, y, &oldx, &oldy);
8152 if (!IS_MOVING(oldx, oldy))
8154 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8155 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8156 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8157 printf("GameActions(): This should never happen!\n");
8163 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8165 element = Feld[x][y];
8167 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8169 graphic = el2img(element);
8175 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8177 element = graphic = 0;
8181 if (graphic_info[graphic].anim_global_sync)
8182 GfxFrame[x][y] = FrameCounter;
8184 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8185 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8186 ResetRandomAnimationValue(x, y);
8188 SetRandomAnimationValue(x, y);
8191 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8194 if (IS_INACTIVE(element))
8196 if (IS_ANIMATED(graphic))
8197 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8203 /* this may take place after moving, so 'element' may have changed */
8205 if (IS_CHANGING(x, y))
8207 if (IS_CHANGING(x, y) &&
8208 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8212 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8213 element_info[element].event_page_nr[CE_DELAY]);
8215 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8218 element = Feld[x][y];
8219 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8223 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8228 element = Feld[x][y];
8229 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8231 if (element == EL_MOLE)
8232 printf("::: %d, %d, %d [%d]\n",
8233 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8237 if (element == EL_YAMYAM)
8238 printf("::: %d, %d, %d\n",
8239 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8243 if (IS_ANIMATED(graphic) &&
8247 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8250 if (element == EL_BUG)
8251 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8255 if (element == EL_MOLE)
8256 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8260 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8261 EdelsteinFunkeln(x, y);
8263 else if ((element == EL_ACID ||
8264 element == EL_EXIT_OPEN ||
8265 element == EL_SP_EXIT_OPEN ||
8266 element == EL_SP_TERMINAL ||
8267 element == EL_SP_TERMINAL_ACTIVE ||
8268 element == EL_EXTRA_TIME ||
8269 element == EL_SHIELD_NORMAL ||
8270 element == EL_SHIELD_DEADLY) &&
8271 IS_ANIMATED(graphic))
8272 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8273 else if (IS_MOVING(x, y))
8274 ContinueMoving(x, y);
8275 else if (IS_ACTIVE_BOMB(element))
8276 CheckDynamite(x, y);
8278 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8279 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8281 else if (element == EL_AMOEBA_GROWING)
8282 AmoebeWaechst(x, y);
8283 else if (element == EL_AMOEBA_SHRINKING)
8284 AmoebaDisappearing(x, y);
8286 #if !USE_NEW_AMOEBA_CODE
8287 else if (IS_AMOEBALIVE(element))
8288 AmoebeAbleger(x, y);
8291 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8293 else if (element == EL_EXIT_CLOSED)
8295 else if (element == EL_SP_EXIT_CLOSED)
8297 else if (element == EL_EXPANDABLE_WALL_GROWING)
8299 else if (element == EL_EXPANDABLE_WALL ||
8300 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8301 element == EL_EXPANDABLE_WALL_VERTICAL ||
8302 element == EL_EXPANDABLE_WALL_ANY)
8304 else if (element == EL_FLAMES)
8305 CheckForDragon(x, y);
8307 else if (IS_AUTO_CHANGING(element))
8308 ChangeElement(x, y);
8310 else if (element == EL_EXPLOSION)
8311 ; /* drawing of correct explosion animation is handled separately */
8312 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8313 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8316 /* this may take place after moving, so 'element' may have changed */
8317 if (IS_AUTO_CHANGING(Feld[x][y]))
8318 ChangeElement(x, y);
8321 if (IS_BELT_ACTIVE(element))
8322 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8324 if (game.magic_wall_active)
8326 int jx = local_player->jx, jy = local_player->jy;
8328 /* play the element sound at the position nearest to the player */
8329 if ((element == EL_MAGIC_WALL_FULL ||
8330 element == EL_MAGIC_WALL_ACTIVE ||
8331 element == EL_MAGIC_WALL_EMPTYING ||
8332 element == EL_BD_MAGIC_WALL_FULL ||
8333 element == EL_BD_MAGIC_WALL_ACTIVE ||
8334 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8335 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8343 #if USE_NEW_AMOEBA_CODE
8344 /* new experimental amoeba growth stuff */
8346 if (!(FrameCounter % 8))
8349 static unsigned long random = 1684108901;
8351 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8354 x = (random >> 10) % lev_fieldx;
8355 y = (random >> 20) % lev_fieldy;
8357 x = RND(lev_fieldx);
8358 y = RND(lev_fieldy);
8360 element = Feld[x][y];
8363 if (!IS_PLAYER(x,y) &&
8364 (element == EL_EMPTY ||
8365 CAN_GROW_INTO(element) ||
8366 element == EL_QUICKSAND_EMPTY ||
8367 element == EL_ACID_SPLASH_LEFT ||
8368 element == EL_ACID_SPLASH_RIGHT))
8370 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8371 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8372 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8373 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8374 Feld[x][y] = EL_AMOEBA_DROP;
8377 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8378 if (!IS_PLAYER(x,y) &&
8379 (element == EL_EMPTY ||
8380 element == EL_SAND ||
8381 element == EL_QUICKSAND_EMPTY ||
8382 element == EL_ACID_SPLASH_LEFT ||
8383 element == EL_ACID_SPLASH_RIGHT))
8385 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8386 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8387 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8388 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8389 Feld[x][y] = EL_AMOEBA_DROP;
8393 random = random * 129 + 1;
8399 if (game.explosions_delayed)
8402 game.explosions_delayed = FALSE;
8404 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8406 element = Feld[x][y];
8408 if (ExplodeField[x][y])
8409 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8410 else if (element == EL_EXPLOSION)
8411 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8413 ExplodeField[x][y] = EX_TYPE_NONE;
8416 game.explosions_delayed = TRUE;
8419 if (game.magic_wall_active)
8421 if (!(game.magic_wall_time_left % 4))
8423 int element = Feld[magic_wall_x][magic_wall_y];
8425 if (element == EL_BD_MAGIC_WALL_FULL ||
8426 element == EL_BD_MAGIC_WALL_ACTIVE ||
8427 element == EL_BD_MAGIC_WALL_EMPTYING)
8428 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8430 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8433 if (game.magic_wall_time_left > 0)
8435 game.magic_wall_time_left--;
8436 if (!game.magic_wall_time_left)
8438 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8440 element = Feld[x][y];
8442 if (element == EL_MAGIC_WALL_ACTIVE ||
8443 element == EL_MAGIC_WALL_FULL)
8445 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8446 DrawLevelField(x, y);
8448 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8449 element == EL_BD_MAGIC_WALL_FULL)
8451 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8452 DrawLevelField(x, y);
8456 game.magic_wall_active = FALSE;
8461 if (game.light_time_left > 0)
8463 game.light_time_left--;
8465 if (game.light_time_left == 0)
8466 RedrawAllLightSwitchesAndInvisibleElements();
8469 if (game.timegate_time_left > 0)
8471 game.timegate_time_left--;
8473 if (game.timegate_time_left == 0)
8474 CloseAllOpenTimegates();
8477 for (i = 0; i < MAX_PLAYERS; i++)
8479 struct PlayerInfo *player = &stored_player[i];
8481 if (SHIELD_ON(player))
8483 if (player->shield_deadly_time_left)
8484 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8485 else if (player->shield_normal_time_left)
8486 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8490 if (TimeFrames >= FRAMES_PER_SECOND)
8495 if (!level.use_step_counter)
8499 for (i = 0; i < MAX_PLAYERS; i++)
8501 struct PlayerInfo *player = &stored_player[i];
8503 if (SHIELD_ON(player))
8505 player->shield_normal_time_left--;
8507 if (player->shield_deadly_time_left > 0)
8508 player->shield_deadly_time_left--;
8516 if (TimeLeft <= 10 && setup.time_limit)
8517 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8519 DrawGameValue_Time(TimeLeft);
8521 if (!TimeLeft && setup.time_limit)
8522 for (i = 0; i < MAX_PLAYERS; i++)
8523 KillHero(&stored_player[i]);
8525 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8526 DrawGameValue_Time(TimePlayed);
8529 if (tape.recording || tape.playing)
8530 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8534 PlayAllPlayersSound();
8536 if (options.debug) /* calculate frames per second */
8538 static unsigned long fps_counter = 0;
8539 static int fps_frames = 0;
8540 unsigned long fps_delay_ms = Counter() - fps_counter;
8544 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8546 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8549 fps_counter = Counter();
8552 redraw_mask |= REDRAW_FPS;
8556 if (stored_player[0].jx != stored_player[0].last_jx ||
8557 stored_player[0].jy != stored_player[0].last_jy)
8558 printf("::: %d, %d, %d, %d, %d\n",
8559 stored_player[0].MovDir,
8560 stored_player[0].MovPos,
8561 stored_player[0].GfxPos,
8562 stored_player[0].Frame,
8563 stored_player[0].StepFrame);
8570 for (i = 0; i < MAX_PLAYERS; i++)
8573 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8575 stored_player[i].Frame += move_frames;
8577 if (stored_player[i].MovPos != 0)
8578 stored_player[i].StepFrame += move_frames;
8580 if (stored_player[i].drop_delay > 0)
8581 stored_player[i].drop_delay--;
8586 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8588 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8590 local_player->show_envelope = 0;
8595 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8597 int min_x = x, min_y = y, max_x = x, max_y = y;
8600 for (i = 0; i < MAX_PLAYERS; i++)
8602 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8604 if (!stored_player[i].active || &stored_player[i] == player)
8607 min_x = MIN(min_x, jx);
8608 min_y = MIN(min_y, jy);
8609 max_x = MAX(max_x, jx);
8610 max_y = MAX(max_y, jy);
8613 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8616 static boolean AllPlayersInVisibleScreen()
8620 for (i = 0; i < MAX_PLAYERS; i++)
8622 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8624 if (!stored_player[i].active)
8627 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8634 void ScrollLevel(int dx, int dy)
8636 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8639 BlitBitmap(drawto_field, drawto_field,
8640 FX + TILEX * (dx == -1) - softscroll_offset,
8641 FY + TILEY * (dy == -1) - softscroll_offset,
8642 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8643 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8644 FX + TILEX * (dx == 1) - softscroll_offset,
8645 FY + TILEY * (dy == 1) - softscroll_offset);
8649 x = (dx == 1 ? BX1 : BX2);
8650 for (y = BY1; y <= BY2; y++)
8651 DrawScreenField(x, y);
8656 y = (dy == 1 ? BY1 : BY2);
8657 for (x = BX1; x <= BX2; x++)
8658 DrawScreenField(x, y);
8661 redraw_mask |= REDRAW_FIELD;
8665 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8667 int nextx = x + dx, nexty = y + dy;
8668 int element = Feld[x][y];
8671 element != EL_SP_PORT_LEFT &&
8672 element != EL_SP_GRAVITY_PORT_LEFT &&
8673 element != EL_SP_PORT_HORIZONTAL &&
8674 element != EL_SP_PORT_ANY) ||
8676 element != EL_SP_PORT_RIGHT &&
8677 element != EL_SP_GRAVITY_PORT_RIGHT &&
8678 element != EL_SP_PORT_HORIZONTAL &&
8679 element != EL_SP_PORT_ANY) ||
8681 element != EL_SP_PORT_UP &&
8682 element != EL_SP_GRAVITY_PORT_UP &&
8683 element != EL_SP_PORT_VERTICAL &&
8684 element != EL_SP_PORT_ANY) ||
8686 element != EL_SP_PORT_DOWN &&
8687 element != EL_SP_GRAVITY_PORT_DOWN &&
8688 element != EL_SP_PORT_VERTICAL &&
8689 element != EL_SP_PORT_ANY) ||
8690 !IN_LEV_FIELD(nextx, nexty) ||
8691 !IS_FREE(nextx, nexty))
8698 static boolean canFallDown(struct PlayerInfo *player)
8700 int jx = player->jx, jy = player->jy;
8702 return (IN_LEV_FIELD(jx, jy + 1) &&
8703 (IS_FREE(jx, jy + 1) ||
8704 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8705 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8706 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8709 static boolean canPassField(int x, int y, int move_dir)
8711 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8712 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8713 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8716 int element = Feld[x][y];
8718 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8719 !CAN_MOVE(element) &&
8720 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8721 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8722 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8725 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8727 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8728 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8729 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8733 int nextx = newx + dx;
8734 int nexty = newy + dy;
8738 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8739 (IS_DIGGABLE(Feld[newx][newy]) ||
8740 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8741 canPassField(newx, newy, move_dir)));
8743 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8744 (IS_DIGGABLE(Feld[newx][newy]) ||
8745 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8746 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
8747 !CAN_MOVE(Feld[newx][newy]) &&
8748 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8749 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8750 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
8754 static void CheckGravityMovement(struct PlayerInfo *player)
8756 if (game.gravity && !player->programmed_action)
8759 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8760 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8762 int move_dir_horizontal = player->action & MV_HORIZONTAL;
8763 int move_dir_vertical = player->action & MV_VERTICAL;
8767 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8769 boolean player_is_snapping = player->action & JOY_BUTTON_1;
8772 int jx = player->jx, jy = player->jy;
8774 boolean player_is_moving_to_valid_field =
8775 (!player_is_snapping &&
8776 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8777 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8781 (player->last_move_dir & MV_HORIZONTAL ?
8782 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8783 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8787 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8788 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8789 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8790 int new_jx = jx + dx, new_jy = jy + dy;
8791 int nextx = new_jx + dx, nexty = new_jy + dy;
8797 boolean player_can_fall_down = canFallDown(player);
8799 boolean player_can_fall_down =
8800 (IN_LEV_FIELD(jx, jy + 1) &&
8801 (IS_FREE(jx, jy + 1) ||
8802 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
8806 boolean player_can_fall_down =
8807 (IN_LEV_FIELD(jx, jy + 1) &&
8808 (IS_FREE(jx, jy + 1)));
8812 boolean player_is_moving_to_valid_field =
8815 !player_is_snapping &&
8819 IN_LEV_FIELD(new_jx, new_jy) &&
8820 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
8821 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8822 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
8823 IN_LEV_FIELD(nextx, nexty) &&
8824 element_info[Feld[nextx][nexty]].access_direction & move_dir))
8826 IN_LEV_FIELD(new_jx, new_jy) &&
8827 (Feld[new_jx][new_jy] == EL_SP_BASE ||
8828 Feld[new_jx][new_jy] == EL_SAND ||
8829 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8830 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
8831 /* !!! extend EL_SAND to anything diggable !!! */
8837 boolean player_is_standing_on_valid_field =
8838 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8839 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
8843 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
8844 player_can_fall_down,
8845 player_is_standing_on_valid_field,
8846 player_is_moving_to_valid_field,
8847 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
8848 player->effective_action,
8849 player->can_fall_into_acid);
8852 if (player_can_fall_down &&
8854 !player_is_standing_on_valid_field &&
8856 !player_is_moving_to_valid_field)
8859 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
8860 jx, jy, FrameCounter);
8863 player->programmed_action = MV_DOWN;
8868 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8871 return CheckGravityMovement(player);
8874 if (game.gravity && !player->programmed_action)
8876 int jx = player->jx, jy = player->jy;
8877 boolean field_under_player_is_free =
8878 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8879 boolean player_is_standing_on_valid_field =
8880 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8881 (IS_WALKABLE(Feld[jx][jy]) &&
8882 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8884 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8885 player->programmed_action = MV_DOWN;
8891 -----------------------------------------------------------------------------
8892 dx, dy: direction (non-diagonal) to try to move the player to
8893 real_dx, real_dy: direction as read from input device (can be diagonal)
8896 boolean MovePlayerOneStep(struct PlayerInfo *player,
8897 int dx, int dy, int real_dx, int real_dy)
8900 static int trigger_sides[4][2] =
8902 /* enter side leave side */
8903 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8904 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8905 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8906 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8908 int move_direction = (dx == -1 ? MV_LEFT :
8909 dx == +1 ? MV_RIGHT :
8911 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8912 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8913 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8915 int jx = player->jx, jy = player->jy;
8916 int new_jx = jx + dx, new_jy = jy + dy;
8920 if (!player->active || (!dx && !dy))
8921 return MF_NO_ACTION;
8923 player->MovDir = (dx < 0 ? MV_LEFT :
8926 dy > 0 ? MV_DOWN : MV_NO_MOVING);
8928 if (!IN_LEV_FIELD(new_jx, new_jy))
8929 return MF_NO_ACTION;
8931 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8932 return MF_NO_ACTION;
8935 element = MovingOrBlocked2Element(new_jx, new_jy);
8937 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8940 if (DONT_RUN_INTO(element))
8942 if (element == EL_ACID && dx == 0 && dy == 1)
8944 SplashAcid(new_jx, new_jy);
8945 Feld[jx][jy] = EL_PLAYER_1;
8946 InitMovingField(jx, jy, MV_DOWN);
8947 Store[jx][jy] = EL_ACID;
8948 ContinueMoving(jx, jy);
8952 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8957 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8958 if (can_move != MF_MOVING)
8961 /* check if DigField() has caused relocation of the player */
8962 if (player->jx != jx || player->jy != jy)
8963 return MF_NO_ACTION;
8965 StorePlayer[jx][jy] = 0;
8966 player->last_jx = jx;
8967 player->last_jy = jy;
8968 player->jx = new_jx;
8969 player->jy = new_jy;
8970 StorePlayer[new_jx][new_jy] = player->element_nr;
8973 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8975 player->step_counter++;
8978 player->drop_delay = 0;
8981 PlayerVisit[jx][jy] = FrameCounter;
8983 ScrollPlayer(player, SCROLL_INIT);
8986 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8988 CheckTriggeredElementChangeSide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
8990 CheckElementChangeSide(jx, jy, Feld[jx][jy], CE_LEFT_BY_PLAYER,leave_side);
8993 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
8995 CheckTriggeredElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8996 CE_OTHER_GETS_ENTERED, enter_side);
8997 CheckElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8998 CE_ENTERED_BY_PLAYER, enter_side);
9005 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9007 int jx = player->jx, jy = player->jy;
9008 int old_jx = jx, old_jy = jy;
9009 int moved = MF_NO_ACTION;
9012 if (!player->active)
9017 if (player->MovPos == 0)
9019 player->is_moving = FALSE;
9020 player->is_digging = FALSE;
9021 player->is_collecting = FALSE;
9022 player->is_snapping = FALSE;
9023 player->is_pushing = FALSE;
9029 if (!player->active || (!dx && !dy))
9034 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9040 if (!FrameReached(&player->move_delay, player->move_delay_value))
9043 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9044 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9050 /* store if player is automatically moved to next field */
9051 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9053 /* remove the last programmed player action */
9054 player->programmed_action = 0;
9058 /* should only happen if pre-1.2 tape recordings are played */
9059 /* this is only for backward compatibility */
9061 int original_move_delay_value = player->move_delay_value;
9064 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9068 /* scroll remaining steps with finest movement resolution */
9069 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9071 while (player->MovPos)
9073 ScrollPlayer(player, SCROLL_GO_ON);
9074 ScrollScreen(NULL, SCROLL_GO_ON);
9080 player->move_delay_value = original_move_delay_value;
9083 if (player->last_move_dir & MV_HORIZONTAL)
9085 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9086 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9090 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9091 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9097 if (moved & MF_MOVING && !ScreenMovPos &&
9098 (player == local_player || !options.network))
9100 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9101 int offset = (setup.scroll_delay ? 3 : 0);
9103 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9105 /* actual player has left the screen -- scroll in that direction */
9106 if (jx != old_jx) /* player has moved horizontally */
9107 scroll_x += (jx - old_jx);
9108 else /* player has moved vertically */
9109 scroll_y += (jy - old_jy);
9113 if (jx != old_jx) /* player has moved horizontally */
9115 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9116 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9117 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9119 /* don't scroll over playfield boundaries */
9120 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9121 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9123 /* don't scroll more than one field at a time */
9124 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9126 /* don't scroll against the player's moving direction */
9127 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9128 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9129 scroll_x = old_scroll_x;
9131 else /* player has moved vertically */
9133 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9134 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9135 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9137 /* don't scroll over playfield boundaries */
9138 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9139 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9141 /* don't scroll more than one field at a time */
9142 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9144 /* don't scroll against the player's moving direction */
9145 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9146 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9147 scroll_y = old_scroll_y;
9151 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9153 if (!options.network && !AllPlayersInVisibleScreen())
9155 scroll_x = old_scroll_x;
9156 scroll_y = old_scroll_y;
9160 ScrollScreen(player, SCROLL_INIT);
9161 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9168 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9170 if (!(moved & MF_MOVING) && !player->is_pushing)
9175 player->StepFrame = 0;
9177 if (moved & MF_MOVING)
9179 if (old_jx != jx && old_jy == jy)
9180 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9181 else if (old_jx == jx && old_jy != jy)
9182 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9184 DrawLevelField(jx, jy); /* for "crumbled sand" */
9186 player->last_move_dir = player->MovDir;
9187 player->is_moving = TRUE;
9189 player->is_snapping = FALSE;
9193 player->is_switching = FALSE;
9196 player->is_dropping = FALSE;
9200 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9202 static int trigger_sides[4][2] =
9204 /* enter side leave side */
9205 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9206 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9207 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9208 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9210 int move_direction = player->MovDir;
9211 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9212 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9215 CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9217 player->index_bit, leave_side);
9219 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9220 CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9222 player->index_bit, leave_side);
9224 CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
9225 CE_OTHER_GETS_ENTERED,
9226 player->index_bit, enter_side);
9228 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9229 CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9230 player->index_bit, enter_side);
9240 CheckGravityMovementWhenNotMoving(player);
9243 player->last_move_dir = MV_NO_MOVING;
9245 player->is_moving = FALSE;
9248 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9250 TestIfHeroTouchesBadThing(jx, jy);
9251 TestIfPlayerTouchesCustomElement(jx, jy);
9254 if (!player->active)
9260 void ScrollPlayer(struct PlayerInfo *player, int mode)
9262 int jx = player->jx, jy = player->jy;
9263 int last_jx = player->last_jx, last_jy = player->last_jy;
9264 int move_stepsize = TILEX / player->move_delay_value;
9266 if (!player->active || !player->MovPos)
9269 if (mode == SCROLL_INIT)
9271 player->actual_frame_counter = FrameCounter;
9272 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9274 if (Feld[last_jx][last_jy] == EL_EMPTY)
9275 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9283 else if (!FrameReached(&player->actual_frame_counter, 1))
9286 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9287 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9289 if (!player->block_last_field &&
9290 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9291 Feld[last_jx][last_jy] = EL_EMPTY;
9293 /* before DrawPlayer() to draw correct player graphic for this case */
9294 if (player->MovPos == 0)
9295 CheckGravityMovement(player);
9298 DrawPlayer(player); /* needed here only to cleanup last field */
9301 if (player->MovPos == 0) /* player reached destination field */
9304 if (player->move_delay_reset_counter > 0)
9306 player->move_delay_reset_counter--;
9308 if (player->move_delay_reset_counter == 0)
9310 /* continue with normal speed after quickly moving through gate */
9311 HALVE_PLAYER_SPEED(player);
9313 /* be able to make the next move without delay */
9314 player->move_delay = 0;
9318 if (IS_PASSABLE(Feld[last_jx][last_jy]))
9320 /* continue with normal speed after quickly moving through gate */
9321 HALVE_PLAYER_SPEED(player);
9323 /* be able to make the next move without delay */
9324 player->move_delay = 0;
9328 if (player->block_last_field &&
9329 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9330 Feld[last_jx][last_jy] = EL_EMPTY;
9332 player->last_jx = jx;
9333 player->last_jy = jy;
9335 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9336 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9337 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9339 DrawPlayer(player); /* needed here only to cleanup last field */
9342 if (local_player->friends_still_needed == 0 ||
9343 IS_SP_ELEMENT(Feld[jx][jy]))
9344 player->LevelSolved = player->GameOver = TRUE;
9348 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9349 /* this breaks one level: "machine", level 000 */
9351 static int trigger_sides[4][2] =
9353 /* enter side leave side */
9354 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9355 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9356 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9357 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9359 int move_direction = player->MovDir;
9360 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9361 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9362 int old_jx = last_jx;
9363 int old_jy = last_jy;
9366 /* !!! TEST ONLY !!! */
9367 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9368 CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9370 player->index_bit, leave_side);
9372 CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9374 player->index_bit, leave_side);
9376 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9377 CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9378 player->index_bit, enter_side);
9380 CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
9381 CE_OTHER_GETS_ENTERED,
9382 player->index_bit, enter_side);
9388 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9390 TestIfHeroTouchesBadThing(jx, jy);
9391 TestIfPlayerTouchesCustomElement(jx, jy);
9393 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9396 if (!player->active)
9400 if (level.use_step_counter)
9406 for (i = 0; i < MAX_PLAYERS; i++)
9408 struct PlayerInfo *player = &stored_player[i];
9410 if (SHIELD_ON(player))
9412 player->shield_normal_time_left--;
9414 if (player->shield_deadly_time_left > 0)
9415 player->shield_deadly_time_left--;
9423 if (TimeLeft <= 10 && setup.time_limit)
9424 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9426 DrawGameValue_Time(TimeLeft);
9428 if (!TimeLeft && setup.time_limit)
9429 for (i = 0; i < MAX_PLAYERS; i++)
9430 KillHero(&stored_player[i]);
9432 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9433 DrawGameValue_Time(TimePlayed);
9436 if (tape.single_step && tape.recording && !tape.pausing &&
9437 !player->programmed_action)
9438 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9442 void ScrollScreen(struct PlayerInfo *player, int mode)
9444 static unsigned long screen_frame_counter = 0;
9446 if (mode == SCROLL_INIT)
9448 /* set scrolling step size according to actual player's moving speed */
9449 ScrollStepSize = TILEX / player->move_delay_value;
9451 screen_frame_counter = FrameCounter;
9452 ScreenMovDir = player->MovDir;
9453 ScreenMovPos = player->MovPos;
9454 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9457 else if (!FrameReached(&screen_frame_counter, 1))
9462 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9463 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9464 redraw_mask |= REDRAW_FIELD;
9467 ScreenMovDir = MV_NO_MOVING;
9470 void TestIfPlayerTouchesCustomElement(int x, int y)
9472 static int xy[4][2] =
9479 static int trigger_sides[4][2] =
9481 /* center side border side */
9482 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9483 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9484 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9485 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9487 static int touch_dir[4] =
9494 int center_element = Feld[x][y]; /* should always be non-moving! */
9497 for (i = 0; i < NUM_DIRECTIONS; i++)
9499 int xx = x + xy[i][0];
9500 int yy = y + xy[i][1];
9501 int center_side = trigger_sides[i][0];
9502 int border_side = trigger_sides[i][1];
9505 if (!IN_LEV_FIELD(xx, yy))
9508 if (IS_PLAYER(x, y))
9510 struct PlayerInfo *player = PLAYERINFO(x, y);
9512 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9513 border_element = Feld[xx][yy]; /* may be moving! */
9514 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9515 border_element = Feld[xx][yy];
9516 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9517 border_element = MovingOrBlocked2Element(xx, yy);
9519 continue; /* center and border element do not touch */
9522 /* !!! TEST ONLY !!! */
9523 CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9524 player->index_bit, border_side);
9525 CheckTriggeredElementChangePlayer(xx, yy, border_element,
9526 CE_OTHER_GETS_TOUCHED,
9527 player->index_bit, border_side);
9529 CheckTriggeredElementChangePlayer(xx, yy, border_element,
9530 CE_OTHER_GETS_TOUCHED,
9531 player->index_bit, border_side);
9532 CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9533 player->index_bit, border_side);
9536 else if (IS_PLAYER(xx, yy))
9538 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9540 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9542 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9543 continue; /* center and border element do not touch */
9547 /* !!! TEST ONLY !!! */
9548 CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9549 player->index_bit, center_side);
9550 CheckTriggeredElementChangePlayer(x, y, center_element,
9551 CE_OTHER_GETS_TOUCHED,
9552 player->index_bit, center_side);
9554 CheckTriggeredElementChangePlayer(x, y, center_element,
9555 CE_OTHER_GETS_TOUCHED,
9556 player->index_bit, center_side);
9557 CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9558 player->index_bit, center_side);
9566 void TestIfElementTouchesCustomElement(int x, int y)
9568 static int xy[4][2] =
9575 static int trigger_sides[4][2] =
9577 /* center side border side */
9578 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9579 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9580 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9581 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9583 static int touch_dir[4] =
9590 boolean change_center_element = FALSE;
9591 int center_element_change_page = 0;
9592 int center_element = Feld[x][y]; /* should always be non-moving! */
9593 int border_trigger_element;
9596 for (i = 0; i < NUM_DIRECTIONS; i++)
9598 int xx = x + xy[i][0];
9599 int yy = y + xy[i][1];
9600 int center_side = trigger_sides[i][0];
9601 int border_side = trigger_sides[i][1];
9604 if (!IN_LEV_FIELD(xx, yy))
9607 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9608 border_element = Feld[xx][yy]; /* may be moving! */
9609 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9610 border_element = Feld[xx][yy];
9611 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9612 border_element = MovingOrBlocked2Element(xx, yy);
9614 continue; /* center and border element do not touch */
9616 /* check for change of center element (but change it only once) */
9617 if (IS_CUSTOM_ELEMENT(center_element) &&
9618 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9619 !change_center_element)
9621 for (j = 0; j < element_info[center_element].num_change_pages; j++)
9623 struct ElementChangeInfo *change =
9624 &element_info[center_element].change_page[j];
9626 if (change->can_change &&
9627 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9628 change->trigger_side & border_side &&
9630 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9632 change->trigger_element == border_element
9636 change_center_element = TRUE;
9637 center_element_change_page = j;
9638 border_trigger_element = border_element;
9645 /* check for change of border element */
9646 if (IS_CUSTOM_ELEMENT(border_element) &&
9647 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9649 for (j = 0; j < element_info[border_element].num_change_pages; j++)
9651 struct ElementChangeInfo *change =
9652 &element_info[border_element].change_page[j];
9654 if (change->can_change &&
9655 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9656 change->trigger_side & center_side &&
9658 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9660 change->trigger_element == center_element
9665 printf("::: border_element %d, %d\n", x, y);
9668 CheckElementChangePage(xx, yy, border_element, center_element,
9669 CE_OTHER_IS_TOUCHING, j);
9676 if (change_center_element)
9679 printf("::: center_element %d, %d\n", x, y);
9682 CheckElementChangePage(x, y, center_element, border_trigger_element,
9683 CE_OTHER_IS_TOUCHING, center_element_change_page);
9687 void TestIfElementHitsCustomElement(int x, int y, int direction)
9689 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9690 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9691 int hitx = x + dx, hity = y + dy;
9692 int hitting_element = Feld[x][y];
9693 int touched_element;
9695 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9696 !IS_FREE(hitx, hity) &&
9697 (!IS_MOVING(hitx, hity) ||
9698 MovDir[hitx][hity] != direction ||
9699 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9702 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9706 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9710 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9711 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9713 CheckElementChangeSide(x, y, hitting_element, touched_element,
9714 CE_HITTING_SOMETHING, direction);
9716 if (IN_LEV_FIELD(hitx, hity))
9718 int opposite_direction = MV_DIR_OPPOSITE(direction);
9719 int hitting_side = direction;
9720 int touched_side = opposite_direction;
9722 int touched_element = MovingOrBlocked2Element(hitx, hity);
9725 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9726 MovDir[hitx][hity] != direction ||
9727 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9736 CheckElementChangeSide(hitx, hity, touched_element, hitting_element,
9737 CE_HIT_BY_SOMETHING, opposite_direction);
9739 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9740 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
9742 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9744 struct ElementChangeInfo *change =
9745 &element_info[hitting_element].change_page[i];
9747 if (change->can_change &&
9748 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
9749 change->trigger_side & touched_side &&
9752 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9754 change->trigger_element == touched_element
9758 CheckElementChangePage(x, y, hitting_element, touched_element,
9759 CE_OTHER_IS_HITTING, i);
9765 if (IS_CUSTOM_ELEMENT(touched_element) &&
9766 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
9768 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9770 struct ElementChangeInfo *change =
9771 &element_info[touched_element].change_page[i];
9773 if (change->can_change &&
9774 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
9775 change->trigger_side & hitting_side &&
9777 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9779 change->trigger_element == hitting_element
9783 CheckElementChangePage(hitx, hity, touched_element,
9784 hitting_element, CE_OTHER_GETS_HIT, i);
9794 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9796 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9797 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9798 int hitx = x + dx, hity = y + dy;
9799 int hitting_element = Feld[x][y];
9800 int touched_element;
9802 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9803 !IS_FREE(hitx, hity) &&
9804 (!IS_MOVING(hitx, hity) ||
9805 MovDir[hitx][hity] != direction ||
9806 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9809 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9813 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9817 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9818 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9820 CheckElementChangeSide(x, y, hitting_element, touched_element,
9821 EP_CAN_SMASH_EVERYTHING, direction);
9823 if (IN_LEV_FIELD(hitx, hity))
9825 int opposite_direction = MV_DIR_OPPOSITE(direction);
9826 int hitting_side = direction;
9827 int touched_side = opposite_direction;
9829 int touched_element = MovingOrBlocked2Element(hitx, hity);
9832 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9833 MovDir[hitx][hity] != direction ||
9834 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9843 CheckElementChangeSide(hitx, hity, touched_element, hitting_element,
9844 CE_SMASHED_BY_SOMETHING, opposite_direction);
9846 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9847 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
9849 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9851 struct ElementChangeInfo *change =
9852 &element_info[hitting_element].change_page[i];
9854 if (change->can_change &&
9855 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
9856 change->trigger_side & touched_side &&
9859 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9861 change->trigger_element == touched_element
9865 CheckElementChangePage(x, y, hitting_element, touched_element,
9866 CE_OTHER_IS_SMASHING, i);
9872 if (IS_CUSTOM_ELEMENT(touched_element) &&
9873 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
9875 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9877 struct ElementChangeInfo *change =
9878 &element_info[touched_element].change_page[i];
9880 if (change->can_change &&
9881 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
9882 change->trigger_side & hitting_side &&
9884 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9886 change->trigger_element == hitting_element
9890 CheckElementChangePage(hitx, hity, touched_element,
9891 hitting_element, CE_OTHER_GETS_SMASHED, i);
9901 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9903 int i, kill_x = -1, kill_y = -1;
9904 static int test_xy[4][2] =
9911 static int test_dir[4] =
9919 for (i = 0; i < NUM_DIRECTIONS; i++)
9921 int test_x, test_y, test_move_dir, test_element;
9923 test_x = good_x + test_xy[i][0];
9924 test_y = good_y + test_xy[i][1];
9925 if (!IN_LEV_FIELD(test_x, test_y))
9929 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9932 test_element = Feld[test_x][test_y];
9934 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9937 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9938 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9940 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9941 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9949 if (kill_x != -1 || kill_y != -1)
9951 if (IS_PLAYER(good_x, good_y))
9953 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9955 if (player->shield_deadly_time_left > 0)
9956 Bang(kill_x, kill_y);
9957 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9961 Bang(good_x, good_y);
9965 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9967 int i, kill_x = -1, kill_y = -1;
9968 int bad_element = Feld[bad_x][bad_y];
9969 static int test_xy[4][2] =
9976 static int touch_dir[4] =
9983 static int test_dir[4] =
9991 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9994 for (i = 0; i < NUM_DIRECTIONS; i++)
9996 int test_x, test_y, test_move_dir, test_element;
9998 test_x = bad_x + test_xy[i][0];
9999 test_y = bad_y + test_xy[i][1];
10000 if (!IN_LEV_FIELD(test_x, test_y))
10004 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10006 test_element = Feld[test_x][test_y];
10008 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10009 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10011 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10012 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10014 /* good thing is player or penguin that does not move away */
10015 if (IS_PLAYER(test_x, test_y))
10017 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10019 if (bad_element == EL_ROBOT && player->is_moving)
10020 continue; /* robot does not kill player if he is moving */
10022 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10024 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10025 continue; /* center and border element do not touch */
10032 else if (test_element == EL_PENGUIN)
10041 if (kill_x != -1 || kill_y != -1)
10043 if (IS_PLAYER(kill_x, kill_y))
10045 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10047 if (player->shield_deadly_time_left > 0)
10048 Bang(bad_x, bad_y);
10049 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10053 Bang(kill_x, kill_y);
10057 void TestIfHeroTouchesBadThing(int x, int y)
10059 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10062 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10064 TestIfGoodThingHitsBadThing(x, y, move_dir);
10067 void TestIfBadThingTouchesHero(int x, int y)
10069 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10072 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10074 TestIfBadThingHitsGoodThing(x, y, move_dir);
10077 void TestIfFriendTouchesBadThing(int x, int y)
10079 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10082 void TestIfBadThingTouchesFriend(int x, int y)
10084 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10087 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10089 int i, kill_x = bad_x, kill_y = bad_y;
10090 static int xy[4][2] =
10098 for (i = 0; i < NUM_DIRECTIONS; i++)
10102 x = bad_x + xy[i][0];
10103 y = bad_y + xy[i][1];
10104 if (!IN_LEV_FIELD(x, y))
10107 element = Feld[x][y];
10108 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10109 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10117 if (kill_x != bad_x || kill_y != bad_y)
10118 Bang(bad_x, bad_y);
10121 void KillHero(struct PlayerInfo *player)
10123 int jx = player->jx, jy = player->jy;
10125 if (!player->active)
10128 /* remove accessible field at the player's position */
10129 Feld[jx][jy] = EL_EMPTY;
10131 /* deactivate shield (else Bang()/Explode() would not work right) */
10132 player->shield_normal_time_left = 0;
10133 player->shield_deadly_time_left = 0;
10139 static void KillHeroUnlessEnemyProtected(int x, int y)
10141 if (!PLAYER_ENEMY_PROTECTED(x, y))
10142 KillHero(PLAYERINFO(x, y));
10145 static void KillHeroUnlessExplosionProtected(int x, int y)
10147 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10148 KillHero(PLAYERINFO(x, y));
10151 void BuryHero(struct PlayerInfo *player)
10153 int jx = player->jx, jy = player->jy;
10155 if (!player->active)
10159 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10161 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10163 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10165 player->GameOver = TRUE;
10166 RemoveHero(player);
10169 void RemoveHero(struct PlayerInfo *player)
10171 int jx = player->jx, jy = player->jy;
10172 int i, found = FALSE;
10174 player->present = FALSE;
10175 player->active = FALSE;
10177 if (!ExplodeField[jx][jy])
10178 StorePlayer[jx][jy] = 0;
10180 for (i = 0; i < MAX_PLAYERS; i++)
10181 if (stored_player[i].active)
10185 AllPlayersGone = TRUE;
10192 =============================================================================
10193 checkDiagonalPushing()
10194 -----------------------------------------------------------------------------
10195 check if diagonal input device direction results in pushing of object
10196 (by checking if the alternative direction is walkable, diggable, ...)
10197 =============================================================================
10200 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10201 int x, int y, int real_dx, int real_dy)
10203 int jx, jy, dx, dy, xx, yy;
10205 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10208 /* diagonal direction: check alternative direction */
10213 xx = jx + (dx == 0 ? real_dx : 0);
10214 yy = jy + (dy == 0 ? real_dy : 0);
10216 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10220 =============================================================================
10222 -----------------------------------------------------------------------------
10223 x, y: field next to player (non-diagonal) to try to dig to
10224 real_dx, real_dy: direction as read from input device (can be diagonal)
10225 =============================================================================
10228 int DigField(struct PlayerInfo *player,
10229 int oldx, int oldy, int x, int y,
10230 int real_dx, int real_dy, int mode)
10232 static int trigger_sides[4] =
10234 CH_SIDE_RIGHT, /* moving left */
10235 CH_SIDE_LEFT, /* moving right */
10236 CH_SIDE_BOTTOM, /* moving up */
10237 CH_SIDE_TOP, /* moving down */
10240 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10242 int jx = oldx, jy = oldy;
10243 int dx = x - jx, dy = y - jy;
10244 int nextx = x + dx, nexty = y + dy;
10245 int move_direction = (dx == -1 ? MV_LEFT :
10246 dx == +1 ? MV_RIGHT :
10248 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10249 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10250 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10251 int old_element = Feld[jx][jy];
10254 if (player->MovPos == 0)
10256 player->is_digging = FALSE;
10257 player->is_collecting = FALSE;
10260 if (player->MovPos == 0) /* last pushing move finished */
10261 player->is_pushing = FALSE;
10263 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10265 player->is_switching = FALSE;
10266 player->push_delay = 0;
10268 return MF_NO_ACTION;
10271 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10272 return MF_NO_ACTION;
10277 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10279 if (IS_TUBE(Feld[jx][jy]) ||
10280 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10284 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10285 int tube_leave_directions[][2] =
10287 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10288 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10289 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10290 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
10291 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
10292 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
10293 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
10294 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
10295 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
10296 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
10297 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
10298 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10301 while (tube_leave_directions[i][0] != tube_element)
10304 if (tube_leave_directions[i][0] == -1) /* should not happen */
10308 if (!(tube_leave_directions[i][1] & move_direction))
10309 return MF_NO_ACTION; /* tube has no opening in this direction */
10314 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10315 old_element = Back[jx][jy];
10319 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10320 return MF_NO_ACTION; /* field has no opening in this direction */
10322 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10323 return MF_NO_ACTION; /* field has no opening in this direction */
10325 element = Feld[x][y];
10327 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10328 game.engine_version >= VERSION_IDENT(2,2,0,0))
10329 return MF_NO_ACTION;
10334 case EL_SP_PORT_LEFT:
10335 case EL_SP_PORT_RIGHT:
10336 case EL_SP_PORT_UP:
10337 case EL_SP_PORT_DOWN:
10338 case EL_SP_PORT_HORIZONTAL:
10339 case EL_SP_PORT_VERTICAL:
10340 case EL_SP_PORT_ANY:
10341 case EL_SP_GRAVITY_PORT_LEFT:
10342 case EL_SP_GRAVITY_PORT_RIGHT:
10343 case EL_SP_GRAVITY_PORT_UP:
10344 case EL_SP_GRAVITY_PORT_DOWN:
10346 if (!canEnterSupaplexPort(x, y, dx, dy))
10347 return MF_NO_ACTION;
10350 element != EL_SP_PORT_LEFT &&
10351 element != EL_SP_GRAVITY_PORT_LEFT &&
10352 element != EL_SP_PORT_HORIZONTAL &&
10353 element != EL_SP_PORT_ANY) ||
10355 element != EL_SP_PORT_RIGHT &&
10356 element != EL_SP_GRAVITY_PORT_RIGHT &&
10357 element != EL_SP_PORT_HORIZONTAL &&
10358 element != EL_SP_PORT_ANY) ||
10360 element != EL_SP_PORT_UP &&
10361 element != EL_SP_GRAVITY_PORT_UP &&
10362 element != EL_SP_PORT_VERTICAL &&
10363 element != EL_SP_PORT_ANY) ||
10365 element != EL_SP_PORT_DOWN &&
10366 element != EL_SP_GRAVITY_PORT_DOWN &&
10367 element != EL_SP_PORT_VERTICAL &&
10368 element != EL_SP_PORT_ANY) ||
10369 !IN_LEV_FIELD(nextx, nexty) ||
10370 !IS_FREE(nextx, nexty))
10371 return MF_NO_ACTION;
10374 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10375 element == EL_SP_GRAVITY_PORT_RIGHT ||
10376 element == EL_SP_GRAVITY_PORT_UP ||
10377 element == EL_SP_GRAVITY_PORT_DOWN)
10378 game.gravity = !game.gravity;
10380 /* automatically move to the next field with double speed */
10381 player->programmed_action = move_direction;
10383 if (player->move_delay_reset_counter == 0)
10385 player->move_delay_reset_counter = 2; /* two double speed steps */
10387 DOUBLE_PLAYER_SPEED(player);
10390 player->move_delay_reset_counter = 2;
10392 DOUBLE_PLAYER_SPEED(player);
10396 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10399 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10405 case EL_TUBE_VERTICAL:
10406 case EL_TUBE_HORIZONTAL:
10407 case EL_TUBE_VERTICAL_LEFT:
10408 case EL_TUBE_VERTICAL_RIGHT:
10409 case EL_TUBE_HORIZONTAL_UP:
10410 case EL_TUBE_HORIZONTAL_DOWN:
10411 case EL_TUBE_LEFT_UP:
10412 case EL_TUBE_LEFT_DOWN:
10413 case EL_TUBE_RIGHT_UP:
10414 case EL_TUBE_RIGHT_DOWN:
10417 int tube_enter_directions[][2] =
10419 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10420 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10421 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10422 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
10423 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
10424 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
10425 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
10426 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
10427 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
10428 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
10429 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
10430 { -1, MV_NO_MOVING }
10433 while (tube_enter_directions[i][0] != element)
10436 if (tube_enter_directions[i][0] == -1) /* should not happen */
10440 if (!(tube_enter_directions[i][1] & move_direction))
10441 return MF_NO_ACTION; /* tube has no opening in this direction */
10443 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10450 if (IS_WALKABLE(element))
10452 int sound_action = ACTION_WALKING;
10454 if (!ACCESS_FROM(element, opposite_direction))
10455 return MF_NO_ACTION; /* field not accessible from this direction */
10458 if (element == EL_EMPTY_SPACE &&
10459 game.gravity && !player->is_auto_moving &&
10460 canFallDown(player) && move_direction != MV_DOWN)
10461 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10464 if (IS_GATE(element))
10466 if (!player->key[element - EL_GATE_1])
10467 return MF_NO_ACTION;
10469 else if (IS_GATE_GRAY(element))
10471 if (!player->key[element - EL_GATE_1_GRAY])
10472 return MF_NO_ACTION;
10474 else if (element == EL_EXIT_OPEN ||
10475 element == EL_SP_EXIT_OPEN ||
10476 element == EL_SP_EXIT_OPENING)
10478 sound_action = ACTION_PASSING; /* player is passing exit */
10480 else if (element == EL_EMPTY)
10482 sound_action = ACTION_MOVING; /* nothing to walk on */
10485 /* play sound from background or player, whatever is available */
10486 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
10487 PlayLevelSoundElementAction(x, y, element, sound_action);
10489 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10493 else if (IS_PASSABLE(element))
10496 if (!canPassField(x, y, move_direction))
10497 return MF_NO_ACTION;
10501 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
10502 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
10503 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
10504 return MF_NO_ACTION;
10506 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10507 return MF_NO_ACTION;
10511 if (!ACCESS_FROM(element, opposite_direction))
10512 return MF_NO_ACTION; /* field not accessible from this direction */
10514 if (IS_CUSTOM_ELEMENT(element) &&
10515 !ACCESS_FROM(element, opposite_direction))
10516 return MF_NO_ACTION; /* field not accessible from this direction */
10520 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10521 return MF_NO_ACTION;
10526 if (IS_EM_GATE(element))
10528 if (!player->key[element - EL_EM_GATE_1])
10529 return MF_NO_ACTION;
10531 else if (IS_EM_GATE_GRAY(element))
10533 if (!player->key[element - EL_EM_GATE_1_GRAY])
10534 return MF_NO_ACTION;
10536 else if (IS_SP_PORT(element))
10538 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10539 element == EL_SP_GRAVITY_PORT_RIGHT ||
10540 element == EL_SP_GRAVITY_PORT_UP ||
10541 element == EL_SP_GRAVITY_PORT_DOWN)
10542 game.gravity = !game.gravity;
10545 /* automatically move to the next field with double speed */
10546 player->programmed_action = move_direction;
10548 if (player->move_delay_reset_counter == 0)
10550 player->move_delay_reset_counter = 2; /* two double speed steps */
10552 DOUBLE_PLAYER_SPEED(player);
10555 player->move_delay_reset_counter = 2;
10557 DOUBLE_PLAYER_SPEED(player);
10560 PlayLevelSoundAction(x, y, ACTION_PASSING);
10564 else if (IS_DIGGABLE(element))
10568 if (mode != DF_SNAP)
10571 GfxElement[x][y] = GFX_ELEMENT(element);
10574 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
10576 player->is_digging = TRUE;
10579 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10581 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED,
10582 player->index_bit, dig_side);
10585 if (mode == DF_SNAP)
10586 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10591 else if (IS_COLLECTIBLE(element))
10595 if (mode != DF_SNAP)
10597 GfxElement[x][y] = element;
10598 player->is_collecting = TRUE;
10601 if (element == EL_SPEED_PILL)
10602 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10603 else if (element == EL_EXTRA_TIME && level.time > 0)
10606 DrawGameValue_Time(TimeLeft);
10608 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10610 player->shield_normal_time_left += 10;
10611 if (element == EL_SHIELD_DEADLY)
10612 player->shield_deadly_time_left += 10;
10614 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10616 if (player->inventory_size < MAX_INVENTORY_SIZE)
10617 player->inventory_element[player->inventory_size++] = element;
10619 DrawGameValue_Dynamite(local_player->inventory_size);
10621 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10623 player->dynabomb_count++;
10624 player->dynabombs_left++;
10626 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10628 player->dynabomb_size++;
10630 else if (element == EL_DYNABOMB_INCREASE_POWER)
10632 player->dynabomb_xl = TRUE;
10634 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
10635 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
10637 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
10638 element - EL_KEY_1 : element - EL_EM_KEY_1);
10640 player->key[key_nr] = TRUE;
10642 DrawGameValue_Keys(player);
10644 redraw_mask |= REDRAW_DOOR_1;
10646 else if (IS_ENVELOPE(element))
10649 player->show_envelope = element;
10651 ShowEnvelope(element - EL_ENVELOPE_1);
10654 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
10658 if (element_info[element].collect_count == 0)
10659 player->inventory_infinite_element = element;
10661 for (i = 0; i < element_info[element].collect_count; i++)
10662 if (player->inventory_size < MAX_INVENTORY_SIZE)
10663 player->inventory_element[player->inventory_size++] = element;
10665 DrawGameValue_Dynamite(local_player->inventory_size);
10667 else if (element_info[element].collect_count > 0)
10669 local_player->gems_still_needed -=
10670 element_info[element].collect_count;
10671 if (local_player->gems_still_needed < 0)
10672 local_player->gems_still_needed = 0;
10674 DrawGameValue_Emeralds(local_player->gems_still_needed);
10677 RaiseScoreElement(element);
10678 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10680 CheckTriggeredElementChangePlayer(x, y, element,
10681 CE_OTHER_GETS_COLLECTED,
10682 player->index_bit, dig_side);
10685 if (mode == DF_SNAP)
10686 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10691 else if (IS_PUSHABLE(element))
10693 if (mode == DF_SNAP && element != EL_BD_ROCK)
10694 return MF_NO_ACTION;
10696 if (CAN_FALL(element) && dy)
10697 return MF_NO_ACTION;
10699 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10700 !(element == EL_SPRING && level.use_spring_bug))
10701 return MF_NO_ACTION;
10704 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10705 ((move_direction & MV_VERTICAL &&
10706 ((element_info[element].move_pattern & MV_LEFT &&
10707 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10708 (element_info[element].move_pattern & MV_RIGHT &&
10709 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10710 (move_direction & MV_HORIZONTAL &&
10711 ((element_info[element].move_pattern & MV_UP &&
10712 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10713 (element_info[element].move_pattern & MV_DOWN &&
10714 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10715 return MF_NO_ACTION;
10719 /* do not push elements already moving away faster than player */
10720 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10721 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10722 return MF_NO_ACTION;
10724 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
10725 return MF_NO_ACTION;
10729 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10731 if (player->push_delay_value == -1)
10732 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10734 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10736 if (!player->is_pushing)
10737 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10741 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
10742 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
10743 !player_is_pushing))
10744 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10747 if (!player->is_pushing &&
10748 game.engine_version >= VERSION_IDENT(2,2,0,7))
10749 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10753 printf("::: push delay: %ld [%d, %d] [%d]\n",
10754 player->push_delay_value, FrameCounter, game.engine_version,
10755 player->is_pushing);
10758 player->is_pushing = TRUE;
10760 if (!(IN_LEV_FIELD(nextx, nexty) &&
10761 (IS_FREE(nextx, nexty) ||
10762 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10763 IS_SB_ELEMENT(element)))))
10764 return MF_NO_ACTION;
10766 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10767 return MF_NO_ACTION;
10769 if (player->push_delay == 0) /* new pushing; restart delay */
10770 player->push_delay = FrameCounter;
10772 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
10773 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10774 element != EL_SPRING && element != EL_BALLOON)
10776 /* make sure that there is no move delay before next try to push */
10777 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10778 player->move_delay = INITIAL_MOVE_DELAY_OFF;
10780 return MF_NO_ACTION;
10784 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
10787 if (IS_SB_ELEMENT(element))
10789 if (element == EL_SOKOBAN_FIELD_FULL)
10791 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10792 local_player->sokobanfields_still_needed++;
10795 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10797 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10798 local_player->sokobanfields_still_needed--;
10801 Feld[x][y] = EL_SOKOBAN_OBJECT;
10803 if (Back[x][y] == Back[nextx][nexty])
10804 PlayLevelSoundAction(x, y, ACTION_PUSHING);
10805 else if (Back[x][y] != 0)
10806 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10809 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10812 if (local_player->sokobanfields_still_needed == 0 &&
10813 game.emulation == EMU_SOKOBAN)
10815 player->LevelSolved = player->GameOver = TRUE;
10816 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10820 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10822 InitMovingField(x, y, move_direction);
10823 GfxAction[x][y] = ACTION_PUSHING;
10825 if (mode == DF_SNAP)
10826 ContinueMoving(x, y);
10828 MovPos[x][y] = (dx != 0 ? dx : dy);
10830 Pushed[x][y] = TRUE;
10831 Pushed[nextx][nexty] = TRUE;
10833 if (game.engine_version < VERSION_IDENT(2,2,0,7))
10834 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10836 player->push_delay_value = -1; /* get new value later */
10839 /* !!! TEST ONLY !!! */
10840 CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10841 player->index_bit, dig_side);
10842 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED,
10843 player->index_bit, dig_side);
10845 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED,
10846 player->index_bit, dig_side);
10847 CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10848 player->index_bit, dig_side);
10853 else if (IS_SWITCHABLE(element))
10855 if (PLAYER_SWITCHING(player, x, y))
10858 player->is_switching = TRUE;
10859 player->switch_x = x;
10860 player->switch_y = y;
10862 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10864 if (element == EL_ROBOT_WHEEL)
10866 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10870 DrawLevelField(x, y);
10872 else if (element == EL_SP_TERMINAL)
10876 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
10878 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10880 else if (Feld[xx][yy] == EL_SP_TERMINAL)
10881 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10884 else if (IS_BELT_SWITCH(element))
10886 ToggleBeltSwitch(x, y);
10888 else if (element == EL_SWITCHGATE_SWITCH_UP ||
10889 element == EL_SWITCHGATE_SWITCH_DOWN)
10891 ToggleSwitchgateSwitch(x, y);
10893 else if (element == EL_LIGHT_SWITCH ||
10894 element == EL_LIGHT_SWITCH_ACTIVE)
10896 ToggleLightSwitch(x, y);
10899 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
10900 SND_LIGHT_SWITCH_ACTIVATING :
10901 SND_LIGHT_SWITCH_DEACTIVATING);
10904 else if (element == EL_TIMEGATE_SWITCH)
10906 ActivateTimegateSwitch(x, y);
10908 else if (element == EL_BALLOON_SWITCH_LEFT ||
10909 element == EL_BALLOON_SWITCH_RIGHT ||
10910 element == EL_BALLOON_SWITCH_UP ||
10911 element == EL_BALLOON_SWITCH_DOWN ||
10912 element == EL_BALLOON_SWITCH_ANY)
10914 if (element == EL_BALLOON_SWITCH_ANY)
10915 game.balloon_dir = move_direction;
10917 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10918 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10919 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10920 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10923 else if (element == EL_LAMP)
10925 Feld[x][y] = EL_LAMP_ACTIVE;
10926 local_player->lights_still_needed--;
10928 DrawLevelField(x, y);
10930 else if (element == EL_TIME_ORB_FULL)
10932 Feld[x][y] = EL_TIME_ORB_EMPTY;
10934 DrawGameValue_Time(TimeLeft);
10936 DrawLevelField(x, y);
10939 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
10947 if (!PLAYER_SWITCHING(player, x, y))
10949 player->is_switching = TRUE;
10950 player->switch_x = x;
10951 player->switch_y = y;
10954 /* !!! TEST ONLY !!! */
10955 CheckElementChangePlayer(x, y, element, CE_SWITCHED,
10956 player->index_bit, dig_side);
10957 CheckTriggeredElementChangePlayer(x, y, element,
10958 CE_OTHER_IS_SWITCHING,
10959 player->index_bit, dig_side);
10961 CheckTriggeredElementChangePlayer(x, y, element,
10962 CE_OTHER_IS_SWITCHING,
10963 player->index_bit, dig_side);
10964 CheckElementChangePlayer(x, y, element, CE_SWITCHED,
10965 player->index_bit, dig_side);
10970 /* !!! TEST ONLY !!! */
10971 CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10972 player->index_bit, dig_side);
10973 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED,
10974 player->index_bit, dig_side);
10976 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED,
10977 player->index_bit, dig_side);
10978 CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10979 player->index_bit, dig_side);
10983 return MF_NO_ACTION;
10986 player->push_delay = 0;
10988 if (Feld[x][y] != element) /* really digged/collected something */
10989 player->is_collecting = !player->is_digging;
10994 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10996 int jx = player->jx, jy = player->jy;
10997 int x = jx + dx, y = jy + dy;
10998 int snap_direction = (dx == -1 ? MV_LEFT :
10999 dx == +1 ? MV_RIGHT :
11001 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11004 if (player->MovPos)
11007 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
11011 if (!player->active || !IN_LEV_FIELD(x, y))
11019 if (player->MovPos == 0)
11020 player->is_pushing = FALSE;
11022 player->is_snapping = FALSE;
11024 if (player->MovPos == 0)
11026 player->is_moving = FALSE;
11027 player->is_digging = FALSE;
11028 player->is_collecting = FALSE;
11034 if (player->is_snapping)
11037 player->MovDir = snap_direction;
11040 if (player->MovPos == 0)
11043 player->is_moving = FALSE;
11044 player->is_digging = FALSE;
11045 player->is_collecting = FALSE;
11048 player->is_dropping = FALSE;
11050 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11053 player->is_snapping = TRUE;
11056 if (player->MovPos == 0)
11059 player->is_moving = FALSE;
11060 player->is_digging = FALSE;
11061 player->is_collecting = FALSE;
11064 DrawLevelField(x, y);
11070 boolean DropElement(struct PlayerInfo *player)
11072 static int trigger_sides[4] =
11074 CH_SIDE_LEFT, /* dropping left */
11075 CH_SIDE_RIGHT, /* dropping right */
11076 CH_SIDE_TOP, /* dropping up */
11077 CH_SIDE_BOTTOM, /* dropping down */
11079 int jx = player->jx, jy = player->jy;
11080 int drop_direction = player->MovDir;
11081 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11082 int old_element = Feld[jx][jy];
11083 int drop_element = (player->inventory_size > 0 ?
11084 player->inventory_element[player->inventory_size - 1] :
11085 player->inventory_infinite_element != EL_UNDEFINED ?
11086 player->inventory_infinite_element :
11087 player->dynabombs_left > 0 ?
11088 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11090 int new_element = drop_element; /* default: element does not change */
11092 /* check if player is active, not moving and ready to drop */
11093 if (!player->active || player->MovPos || player->drop_delay > 0)
11096 /* check if player has anything that can be dropped */
11098 if (new_element == EL_UNDEFINED)
11101 if (player->inventory_size == 0 &&
11102 player->inventory_infinite_element == EL_UNDEFINED &&
11103 player->dynabombs_left == 0)
11107 /* check if anything can be dropped at the current position */
11108 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11111 /* collected custom elements can only be dropped on empty fields */
11113 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11116 if (player->inventory_size > 0 &&
11117 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11118 && old_element != EL_EMPTY)
11122 if (old_element != EL_EMPTY)
11123 Back[jx][jy] = old_element; /* store old element on this field */
11125 ResetGfxAnimation(jx, jy);
11126 ResetRandomAnimationValue(jx, jy);
11128 if (player->inventory_size > 0 ||
11129 player->inventory_infinite_element != EL_UNDEFINED)
11131 if (player->inventory_size > 0)
11133 player->inventory_size--;
11136 new_element = player->inventory_element[player->inventory_size];
11139 DrawGameValue_Dynamite(local_player->inventory_size);
11141 if (new_element == EL_DYNAMITE)
11142 new_element = EL_DYNAMITE_ACTIVE;
11143 else if (new_element == EL_SP_DISK_RED)
11144 new_element = EL_SP_DISK_RED_ACTIVE;
11147 Feld[jx][jy] = new_element;
11149 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
11150 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
11152 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
11155 /* needed if previous element just changed to "empty" in the last frame */
11156 Changed[jx][jy] = 0; /* allow another change */
11160 /* !!! TEST ONLY !!! */
11161 CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
11162 player->index_bit, drop_side);
11163 CheckTriggeredElementChangePlayer(jx, jy, new_element,
11164 CE_OTHER_GETS_DROPPED,
11165 player->index_bit, drop_side);
11167 CheckTriggeredElementChangePlayer(jx, jy, new_element,
11168 CE_OTHER_GETS_DROPPED,
11169 player->index_bit, drop_side);
11170 CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
11171 player->index_bit, drop_side);
11174 TestIfElementTouchesCustomElement(jx, jy);
11176 else /* player is dropping a dyna bomb */
11178 player->dynabombs_left--;
11181 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
11184 Feld[jx][jy] = new_element;
11186 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
11187 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
11189 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
11196 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
11199 InitField_WithBug1(jx, jy, FALSE);
11201 InitField(jx, jy, FALSE);
11202 if (CAN_MOVE(Feld[jx][jy]))
11203 InitMovDir(jx, jy);
11207 new_element = Feld[jx][jy]; /* element might have changed */
11209 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11210 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11213 int move_stepsize = element_info[new_element].move_stepsize;
11215 int direction, dx, dy, nextx, nexty;
11217 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11218 MovDir[jx][jy] = player->MovDir;
11220 direction = MovDir[jx][jy];
11221 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11222 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11226 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
11229 WasJustMoving[jx][jy] = 3;
11231 InitMovingField(jx, jy, direction);
11232 ContinueMoving(jx, jy);
11237 Changed[jx][jy] = 0; /* allow another change */
11240 TestIfElementHitsCustomElement(jx, jy, direction);
11242 CheckElementChangeSide(jx, jy, new_element, touched_element,
11243 CE_HITTING_SOMETHING, direction);
11248 player->drop_delay = 2 * TILEX / move_stepsize + 1;
11253 player->drop_delay = 8 + 8 + 8;
11257 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11262 player->is_dropping = TRUE;
11268 /* ------------------------------------------------------------------------- */
11269 /* game sound playing functions */
11270 /* ------------------------------------------------------------------------- */
11272 static int *loop_sound_frame = NULL;
11273 static int *loop_sound_volume = NULL;
11275 void InitPlayLevelSound()
11277 int num_sounds = getSoundListSize();
11279 checked_free(loop_sound_frame);
11280 checked_free(loop_sound_volume);
11282 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11283 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11286 static void PlayLevelSound(int x, int y, int nr)
11288 int sx = SCREENX(x), sy = SCREENY(y);
11289 int volume, stereo_position;
11290 int max_distance = 8;
11291 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11293 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11294 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11297 if (!IN_LEV_FIELD(x, y) ||
11298 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11299 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11302 volume = SOUND_MAX_VOLUME;
11304 if (!IN_SCR_FIELD(sx, sy))
11306 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11307 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11309 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11312 stereo_position = (SOUND_MAX_LEFT +
11313 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11314 (SCR_FIELDX + 2 * max_distance));
11316 if (IS_LOOP_SOUND(nr))
11318 /* This assures that quieter loop sounds do not overwrite louder ones,
11319 while restarting sound volume comparison with each new game frame. */
11321 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11324 loop_sound_volume[nr] = volume;
11325 loop_sound_frame[nr] = FrameCounter;
11328 PlaySoundExt(nr, volume, stereo_position, type);
11331 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11333 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11334 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11335 y < LEVELY(BY1) ? LEVELY(BY1) :
11336 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11340 static void PlayLevelSoundAction(int x, int y, int action)
11342 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11345 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11347 int sound_effect = element_info[element].sound[action];
11349 if (sound_effect != SND_UNDEFINED)
11350 PlayLevelSound(x, y, sound_effect);
11353 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11356 int sound_effect = element_info[element].sound[action];
11358 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11359 PlayLevelSound(x, y, sound_effect);
11362 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11364 int sound_effect = element_info[Feld[x][y]].sound[action];
11366 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11367 PlayLevelSound(x, y, sound_effect);
11370 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11372 int sound_effect = element_info[Feld[x][y]].sound[action];
11374 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11375 StopSound(sound_effect);
11378 static void PlayLevelMusic()
11380 if (levelset.music[level_nr] != MUS_UNDEFINED)
11381 PlayMusic(levelset.music[level_nr]); /* from config file */
11383 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11386 void RaiseScore(int value)
11388 local_player->score += value;
11390 DrawGameValue_Score(local_player->score);
11393 void RaiseScoreElement(int element)
11398 case EL_BD_DIAMOND:
11399 case EL_EMERALD_YELLOW:
11400 case EL_EMERALD_RED:
11401 case EL_EMERALD_PURPLE:
11402 case EL_SP_INFOTRON:
11403 RaiseScore(level.score[SC_EMERALD]);
11406 RaiseScore(level.score[SC_DIAMOND]);
11409 RaiseScore(level.score[SC_CRYSTAL]);
11412 RaiseScore(level.score[SC_PEARL]);
11415 case EL_BD_BUTTERFLY:
11416 case EL_SP_ELECTRON:
11417 RaiseScore(level.score[SC_BUG]);
11420 case EL_BD_FIREFLY:
11421 case EL_SP_SNIKSNAK:
11422 RaiseScore(level.score[SC_SPACESHIP]);
11425 case EL_DARK_YAMYAM:
11426 RaiseScore(level.score[SC_YAMYAM]);
11429 RaiseScore(level.score[SC_ROBOT]);
11432 RaiseScore(level.score[SC_PACMAN]);
11435 RaiseScore(level.score[SC_NUT]);
11438 case EL_SP_DISK_RED:
11439 case EL_DYNABOMB_INCREASE_NUMBER:
11440 case EL_DYNABOMB_INCREASE_SIZE:
11441 case EL_DYNABOMB_INCREASE_POWER:
11442 RaiseScore(level.score[SC_DYNAMITE]);
11444 case EL_SHIELD_NORMAL:
11445 case EL_SHIELD_DEADLY:
11446 RaiseScore(level.score[SC_SHIELD]);
11448 case EL_EXTRA_TIME:
11449 RaiseScore(level.score[SC_TIME_BONUS]);
11455 RaiseScore(level.score[SC_KEY]);
11458 RaiseScore(element_info[element].collect_score);
11463 void RequestQuitGame(boolean ask_if_really_quit)
11465 if (AllPlayersGone ||
11466 !ask_if_really_quit ||
11467 level_editor_test_game ||
11468 Request("Do you really want to quit the game ?",
11469 REQ_ASK | REQ_STAY_CLOSED))
11471 #if defined(PLATFORM_UNIX)
11472 if (options.network)
11473 SendToServer_StopPlaying();
11477 game_status = GAME_MODE_MAIN;
11485 if (tape.playing && tape.deactivate_display)
11486 TapeDeactivateDisplayOff(TRUE);
11489 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11492 if (tape.playing && tape.deactivate_display)
11493 TapeDeactivateDisplayOn();
11500 /* ---------- new game button stuff ---------------------------------------- */
11502 /* graphic position values for game buttons */
11503 #define GAME_BUTTON_XSIZE 30
11504 #define GAME_BUTTON_YSIZE 30
11505 #define GAME_BUTTON_XPOS 5
11506 #define GAME_BUTTON_YPOS 215
11507 #define SOUND_BUTTON_XPOS 5
11508 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11510 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11511 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11512 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11513 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11514 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11515 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11522 } gamebutton_info[NUM_GAME_BUTTONS] =
11525 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11530 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11531 GAME_CTRL_ID_PAUSE,
11535 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11540 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11541 SOUND_CTRL_ID_MUSIC,
11542 "background music on/off"
11545 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11546 SOUND_CTRL_ID_LOOPS,
11547 "sound loops on/off"
11550 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11551 SOUND_CTRL_ID_SIMPLE,
11552 "normal sounds on/off"
11556 void CreateGameButtons()
11560 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11562 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11563 struct GadgetInfo *gi;
11566 unsigned long event_mask;
11567 int gd_xoffset, gd_yoffset;
11568 int gd_x1, gd_x2, gd_y1, gd_y2;
11571 gd_xoffset = gamebutton_info[i].x;
11572 gd_yoffset = gamebutton_info[i].y;
11573 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11574 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11576 if (id == GAME_CTRL_ID_STOP ||
11577 id == GAME_CTRL_ID_PAUSE ||
11578 id == GAME_CTRL_ID_PLAY)
11580 button_type = GD_TYPE_NORMAL_BUTTON;
11582 event_mask = GD_EVENT_RELEASED;
11583 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11584 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11588 button_type = GD_TYPE_CHECK_BUTTON;
11590 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11591 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11592 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11593 event_mask = GD_EVENT_PRESSED;
11594 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11595 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11598 gi = CreateGadget(GDI_CUSTOM_ID, id,
11599 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11600 GDI_X, DX + gd_xoffset,
11601 GDI_Y, DY + gd_yoffset,
11602 GDI_WIDTH, GAME_BUTTON_XSIZE,
11603 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11604 GDI_TYPE, button_type,
11605 GDI_STATE, GD_BUTTON_UNPRESSED,
11606 GDI_CHECKED, checked,
11607 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11608 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11609 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11610 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11611 GDI_EVENT_MASK, event_mask,
11612 GDI_CALLBACK_ACTION, HandleGameButtons,
11616 Error(ERR_EXIT, "cannot create gadget");
11618 game_gadget[id] = gi;
11622 void FreeGameButtons()
11626 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11627 FreeGadget(game_gadget[i]);
11630 static void MapGameButtons()
11634 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11635 MapGadget(game_gadget[i]);
11638 void UnmapGameButtons()
11642 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11643 UnmapGadget(game_gadget[i]);
11646 static void HandleGameButtons(struct GadgetInfo *gi)
11648 int id = gi->custom_id;
11650 if (game_status != GAME_MODE_PLAYING)
11655 case GAME_CTRL_ID_STOP:
11656 RequestQuitGame(TRUE);
11659 case GAME_CTRL_ID_PAUSE:
11660 if (options.network)
11662 #if defined(PLATFORM_UNIX)
11664 SendToServer_ContinuePlaying();
11666 SendToServer_PausePlaying();
11670 TapeTogglePause(TAPE_TOGGLE_MANUAL);
11673 case GAME_CTRL_ID_PLAY:
11676 #if defined(PLATFORM_UNIX)
11677 if (options.network)
11678 SendToServer_ContinuePlaying();
11682 tape.pausing = FALSE;
11683 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11688 case SOUND_CTRL_ID_MUSIC:
11689 if (setup.sound_music)
11691 setup.sound_music = FALSE;
11694 else if (audio.music_available)
11696 setup.sound = setup.sound_music = TRUE;
11698 SetAudioMode(setup.sound);
11704 case SOUND_CTRL_ID_LOOPS:
11705 if (setup.sound_loops)
11706 setup.sound_loops = FALSE;
11707 else if (audio.loops_available)
11709 setup.sound = setup.sound_loops = TRUE;
11710 SetAudioMode(setup.sound);
11714 case SOUND_CTRL_ID_SIMPLE:
11715 if (setup.sound_simple)
11716 setup.sound_simple = FALSE;
11717 else if (audio.sound_available)
11719 setup.sound = setup.sound_simple = TRUE;
11720 SetAudioMode(setup.sound);