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 CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
317 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
318 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
319 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
320 #define CheckTriggeredElementChangeByPage(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 CheckElementChangeByPlayer(x, y, e, ev, p, s) \
328 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
329 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
330 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
331 #define CheckElementChangeByPage(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);
4109 /* !!! TEST ONLY !!! */
4110 CheckElementChangeBySide(x, y + 1, smashed, element,
4111 CE_SWITCHED, CH_SIDE_TOP);
4112 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4113 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4115 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4116 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4117 CheckElementChangeBySide(x, y + 1, smashed, element,
4118 CE_SWITCHED, CH_SIDE_TOP);
4124 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4129 /* play sound of magic wall / mill */
4131 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4132 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4134 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4135 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4136 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4137 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4142 /* play sound of object that hits the ground */
4143 if (lastline || object_hit)
4144 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4147 inline static void TurnRoundExt(int x, int y)
4159 { 0, 0 }, { 0, 0 }, { 0, 0 },
4164 int left, right, back;
4168 { MV_DOWN, MV_UP, MV_RIGHT },
4169 { MV_UP, MV_DOWN, MV_LEFT },
4171 { MV_LEFT, MV_RIGHT, MV_DOWN },
4175 { MV_RIGHT, MV_LEFT, MV_UP }
4178 int element = Feld[x][y];
4179 int move_pattern = element_info[element].move_pattern;
4181 int old_move_dir = MovDir[x][y];
4182 int left_dir = turn[old_move_dir].left;
4183 int right_dir = turn[old_move_dir].right;
4184 int back_dir = turn[old_move_dir].back;
4186 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4187 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4188 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4189 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4191 int left_x = x + left_dx, left_y = y + left_dy;
4192 int right_x = x + right_dx, right_y = y + right_dy;
4193 int move_x = x + move_dx, move_y = y + move_dy;
4197 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4199 TestIfBadThingTouchesOtherBadThing(x, y);
4201 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4202 MovDir[x][y] = right_dir;
4203 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4204 MovDir[x][y] = left_dir;
4206 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4208 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4212 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4213 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4215 TestIfBadThingTouchesOtherBadThing(x, y);
4217 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4218 MovDir[x][y] = left_dir;
4219 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4220 MovDir[x][y] = right_dir;
4222 if ((element == EL_SPACESHIP ||
4223 element == EL_SP_SNIKSNAK ||
4224 element == EL_SP_ELECTRON)
4225 && MovDir[x][y] != old_move_dir)
4227 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4231 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4233 TestIfBadThingTouchesOtherBadThing(x, y);
4235 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4236 MovDir[x][y] = left_dir;
4237 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4238 MovDir[x][y] = right_dir;
4240 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4242 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4245 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4247 TestIfBadThingTouchesOtherBadThing(x, y);
4249 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4250 MovDir[x][y] = left_dir;
4251 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4252 MovDir[x][y] = right_dir;
4254 if (MovDir[x][y] != old_move_dir)
4258 else if (element == EL_YAMYAM)
4260 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4261 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4263 if (can_turn_left && can_turn_right)
4264 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4265 else if (can_turn_left)
4266 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4267 else if (can_turn_right)
4268 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4270 MovDir[x][y] = back_dir;
4272 MovDelay[x][y] = 16 + 16 * RND(3);
4274 else if (element == EL_DARK_YAMYAM)
4276 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4278 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4281 if (can_turn_left && can_turn_right)
4282 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4283 else if (can_turn_left)
4284 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4285 else if (can_turn_right)
4286 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4288 MovDir[x][y] = back_dir;
4290 MovDelay[x][y] = 16 + 16 * RND(3);
4292 else if (element == EL_PACMAN)
4294 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4295 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4297 if (can_turn_left && can_turn_right)
4298 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4299 else if (can_turn_left)
4300 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4301 else if (can_turn_right)
4302 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4304 MovDir[x][y] = back_dir;
4306 MovDelay[x][y] = 6 + RND(40);
4308 else if (element == EL_PIG)
4310 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4311 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4312 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4313 boolean should_turn_left, should_turn_right, should_move_on;
4315 int rnd = RND(rnd_value);
4317 should_turn_left = (can_turn_left &&
4319 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4320 y + back_dy + left_dy)));
4321 should_turn_right = (can_turn_right &&
4323 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4324 y + back_dy + right_dy)));
4325 should_move_on = (can_move_on &&
4328 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4329 y + move_dy + left_dy) ||
4330 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4331 y + move_dy + right_dy)));
4333 if (should_turn_left || should_turn_right || should_move_on)
4335 if (should_turn_left && should_turn_right && should_move_on)
4336 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4337 rnd < 2 * rnd_value / 3 ? right_dir :
4339 else if (should_turn_left && should_turn_right)
4340 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4341 else if (should_turn_left && should_move_on)
4342 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4343 else if (should_turn_right && should_move_on)
4344 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4345 else if (should_turn_left)
4346 MovDir[x][y] = left_dir;
4347 else if (should_turn_right)
4348 MovDir[x][y] = right_dir;
4349 else if (should_move_on)
4350 MovDir[x][y] = old_move_dir;
4352 else if (can_move_on && rnd > rnd_value / 8)
4353 MovDir[x][y] = old_move_dir;
4354 else if (can_turn_left && can_turn_right)
4355 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4356 else if (can_turn_left && rnd > rnd_value / 8)
4357 MovDir[x][y] = left_dir;
4358 else if (can_turn_right && rnd > rnd_value/8)
4359 MovDir[x][y] = right_dir;
4361 MovDir[x][y] = back_dir;
4363 xx = x + move_xy[MovDir[x][y]].x;
4364 yy = y + move_xy[MovDir[x][y]].y;
4366 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4367 MovDir[x][y] = old_move_dir;
4371 else if (element == EL_DRAGON)
4373 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4374 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4375 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4377 int rnd = RND(rnd_value);
4380 if (FrameCounter < 1 && x == 0 && y == 29)
4381 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4384 if (can_move_on && rnd > rnd_value / 8)
4385 MovDir[x][y] = old_move_dir;
4386 else if (can_turn_left && can_turn_right)
4387 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4388 else if (can_turn_left && rnd > rnd_value / 8)
4389 MovDir[x][y] = left_dir;
4390 else if (can_turn_right && rnd > rnd_value / 8)
4391 MovDir[x][y] = right_dir;
4393 MovDir[x][y] = back_dir;
4395 xx = x + move_xy[MovDir[x][y]].x;
4396 yy = y + move_xy[MovDir[x][y]].y;
4399 if (FrameCounter < 1 && x == 0 && y == 29)
4400 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4401 xx, yy, Feld[xx][yy],
4406 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4407 MovDir[x][y] = old_move_dir;
4409 if (!IS_FREE(xx, yy))
4410 MovDir[x][y] = old_move_dir;
4414 if (FrameCounter < 1 && x == 0 && y == 29)
4415 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4420 else if (element == EL_MOLE)
4422 boolean can_move_on =
4423 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4424 IS_AMOEBOID(Feld[move_x][move_y]) ||
4425 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4428 boolean can_turn_left =
4429 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4430 IS_AMOEBOID(Feld[left_x][left_y])));
4432 boolean can_turn_right =
4433 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4434 IS_AMOEBOID(Feld[right_x][right_y])));
4436 if (can_turn_left && can_turn_right)
4437 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4438 else if (can_turn_left)
4439 MovDir[x][y] = left_dir;
4441 MovDir[x][y] = right_dir;
4444 if (MovDir[x][y] != old_move_dir)
4447 else if (element == EL_BALLOON)
4449 MovDir[x][y] = game.balloon_dir;
4452 else if (element == EL_SPRING)
4455 if (MovDir[x][y] & MV_HORIZONTAL &&
4456 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4457 MovDir[x][y] = MV_NO_MOVING;
4459 if (MovDir[x][y] & MV_HORIZONTAL &&
4460 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4461 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4462 MovDir[x][y] = MV_NO_MOVING;
4467 else if (element == EL_ROBOT ||
4468 element == EL_SATELLITE ||
4469 element == EL_PENGUIN)
4471 int attr_x = -1, attr_y = -1;
4482 for (i = 0; i < MAX_PLAYERS; i++)
4484 struct PlayerInfo *player = &stored_player[i];
4485 int jx = player->jx, jy = player->jy;
4487 if (!player->active)
4491 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4500 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4501 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4502 game.engine_version < VERSION_IDENT(3,1,0,0)))
4504 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4511 if (element == EL_PENGUIN)
4514 static int xy[4][2] =
4522 for (i = 0; i < NUM_DIRECTIONS; i++)
4524 int ex = x + xy[i][0];
4525 int ey = y + xy[i][1];
4527 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4536 MovDir[x][y] = MV_NO_MOVING;
4538 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4539 else if (attr_x > x)
4540 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4542 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4543 else if (attr_y > y)
4544 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4546 if (element == EL_ROBOT)
4550 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4551 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4552 Moving2Blocked(x, y, &newx, &newy);
4554 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4555 MovDelay[x][y] = 8 + 8 * !RND(3);
4557 MovDelay[x][y] = 16;
4559 else if (element == EL_PENGUIN)
4565 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4567 boolean first_horiz = RND(2);
4568 int new_move_dir = MovDir[x][y];
4571 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4572 Moving2Blocked(x, y, &newx, &newy);
4574 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4578 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4579 Moving2Blocked(x, y, &newx, &newy);
4581 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4584 MovDir[x][y] = old_move_dir;
4588 else /* (element == EL_SATELLITE) */
4594 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4596 boolean first_horiz = RND(2);
4597 int new_move_dir = MovDir[x][y];
4600 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4601 Moving2Blocked(x, y, &newx, &newy);
4603 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4607 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4608 Moving2Blocked(x, y, &newx, &newy);
4610 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4613 MovDir[x][y] = old_move_dir;
4618 else if (move_pattern == MV_TURNING_LEFT ||
4619 move_pattern == MV_TURNING_RIGHT ||
4620 move_pattern == MV_TURNING_LEFT_RIGHT ||
4621 move_pattern == MV_TURNING_RIGHT_LEFT ||
4622 move_pattern == MV_TURNING_RANDOM ||
4623 move_pattern == MV_ALL_DIRECTIONS)
4625 boolean can_turn_left =
4626 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4627 boolean can_turn_right =
4628 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4630 if (move_pattern == MV_TURNING_LEFT)
4631 MovDir[x][y] = left_dir;
4632 else if (move_pattern == MV_TURNING_RIGHT)
4633 MovDir[x][y] = right_dir;
4634 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4635 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4636 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4637 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4638 else if (move_pattern == MV_TURNING_RANDOM)
4639 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4640 can_turn_right && !can_turn_left ? right_dir :
4641 RND(2) ? left_dir : right_dir);
4642 else if (can_turn_left && can_turn_right)
4643 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4644 else if (can_turn_left)
4645 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4646 else if (can_turn_right)
4647 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4649 MovDir[x][y] = back_dir;
4651 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4653 else if (move_pattern == MV_HORIZONTAL ||
4654 move_pattern == MV_VERTICAL)
4656 if (move_pattern & old_move_dir)
4657 MovDir[x][y] = back_dir;
4658 else if (move_pattern == MV_HORIZONTAL)
4659 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4660 else if (move_pattern == MV_VERTICAL)
4661 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4663 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4665 else if (move_pattern & MV_ANY_DIRECTION)
4667 MovDir[x][y] = move_pattern;
4668 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4670 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4672 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4673 MovDir[x][y] = left_dir;
4674 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4675 MovDir[x][y] = right_dir;
4677 if (MovDir[x][y] != old_move_dir)
4678 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4680 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4682 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4683 MovDir[x][y] = right_dir;
4684 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4685 MovDir[x][y] = left_dir;
4687 if (MovDir[x][y] != old_move_dir)
4688 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4690 else if (move_pattern == MV_TOWARDS_PLAYER ||
4691 move_pattern == MV_AWAY_FROM_PLAYER)
4693 int attr_x = -1, attr_y = -1;
4695 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4706 for (i = 0; i < MAX_PLAYERS; i++)
4708 struct PlayerInfo *player = &stored_player[i];
4709 int jx = player->jx, jy = player->jy;
4711 if (!player->active)
4715 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4723 MovDir[x][y] = MV_NO_MOVING;
4725 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4726 else if (attr_x > x)
4727 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4729 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4730 else if (attr_y > y)
4731 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4733 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4735 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4737 boolean first_horiz = RND(2);
4738 int new_move_dir = MovDir[x][y];
4741 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4742 Moving2Blocked(x, y, &newx, &newy);
4744 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4748 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4749 Moving2Blocked(x, y, &newx, &newy);
4751 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4754 MovDir[x][y] = old_move_dir;
4757 else if (move_pattern == MV_WHEN_PUSHED ||
4758 move_pattern == MV_WHEN_DROPPED)
4760 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4761 MovDir[x][y] = MV_NO_MOVING;
4765 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4767 static int test_xy[7][2] =
4777 static int test_dir[7] =
4787 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4788 int move_preference = -1000000; /* start with very low preference */
4789 int new_move_dir = MV_NO_MOVING;
4790 int start_test = RND(4);
4793 for (i = 0; i < NUM_DIRECTIONS; i++)
4795 int move_dir = test_dir[start_test + i];
4796 int move_dir_preference;
4798 xx = x + test_xy[start_test + i][0];
4799 yy = y + test_xy[start_test + i][1];
4801 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4802 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4804 new_move_dir = move_dir;
4809 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4812 move_dir_preference = -1 * RunnerVisit[xx][yy];
4813 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4814 move_dir_preference = PlayerVisit[xx][yy];
4816 if (move_dir_preference > move_preference)
4818 /* prefer field that has not been visited for the longest time */
4819 move_preference = move_dir_preference;
4820 new_move_dir = move_dir;
4822 else if (move_dir_preference == move_preference &&
4823 move_dir == old_move_dir)
4825 /* prefer last direction when all directions are preferred equally */
4826 move_preference = move_dir_preference;
4827 new_move_dir = move_dir;
4831 MovDir[x][y] = new_move_dir;
4832 if (old_move_dir != new_move_dir)
4837 static void TurnRound(int x, int y)
4839 int direction = MovDir[x][y];
4842 GfxDir[x][y] = MovDir[x][y];
4848 GfxDir[x][y] = MovDir[x][y];
4851 if (direction != MovDir[x][y])
4856 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4859 GfxAction[x][y] = ACTION_WAITING;
4863 static boolean JustBeingPushed(int x, int y)
4867 for (i = 0; i < MAX_PLAYERS; i++)
4869 struct PlayerInfo *player = &stored_player[i];
4871 if (player->active && player->is_pushing && player->MovPos)
4873 int next_jx = player->jx + (player->jx - player->last_jx);
4874 int next_jy = player->jy + (player->jy - player->last_jy);
4876 if (x == next_jx && y == next_jy)
4884 void StartMoving(int x, int y)
4887 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4889 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4890 int element = Feld[x][y];
4896 if (MovDelay[x][y] == 0)
4897 GfxAction[x][y] = ACTION_DEFAULT;
4899 /* !!! this should be handled more generic (not only for mole) !!! */
4900 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4901 GfxAction[x][y] = ACTION_DEFAULT;
4904 if (CAN_FALL(element) && y < lev_fieldy - 1)
4906 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4907 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4908 if (JustBeingPushed(x, y))
4911 if (element == EL_QUICKSAND_FULL)
4913 if (IS_FREE(x, y + 1))
4915 InitMovingField(x, y, MV_DOWN);
4916 started_moving = TRUE;
4918 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4919 Store[x][y] = EL_ROCK;
4921 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4923 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4926 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4928 if (!MovDelay[x][y])
4929 MovDelay[x][y] = TILEY + 1;
4938 Feld[x][y] = EL_QUICKSAND_EMPTY;
4939 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4940 Store[x][y + 1] = Store[x][y];
4943 PlayLevelSoundAction(x, y, ACTION_FILLING);
4945 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4949 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4950 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4952 InitMovingField(x, y, MV_DOWN);
4953 started_moving = TRUE;
4955 Feld[x][y] = EL_QUICKSAND_FILLING;
4956 Store[x][y] = element;
4958 PlayLevelSoundAction(x, y, ACTION_FILLING);
4960 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4963 else if (element == EL_MAGIC_WALL_FULL)
4965 if (IS_FREE(x, y + 1))
4967 InitMovingField(x, y, MV_DOWN);
4968 started_moving = TRUE;
4970 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4971 Store[x][y] = EL_CHANGED(Store[x][y]);
4973 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4975 if (!MovDelay[x][y])
4976 MovDelay[x][y] = TILEY/4 + 1;
4985 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4986 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4987 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4991 else if (element == EL_BD_MAGIC_WALL_FULL)
4993 if (IS_FREE(x, y + 1))
4995 InitMovingField(x, y, MV_DOWN);
4996 started_moving = TRUE;
4998 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4999 Store[x][y] = EL_CHANGED2(Store[x][y]);
5001 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5003 if (!MovDelay[x][y])
5004 MovDelay[x][y] = TILEY/4 + 1;
5013 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5014 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5015 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5019 else if (CAN_PASS_MAGIC_WALL(element) &&
5020 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5021 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5023 InitMovingField(x, y, MV_DOWN);
5024 started_moving = TRUE;
5027 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5028 EL_BD_MAGIC_WALL_FILLING);
5029 Store[x][y] = element;
5032 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5034 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5037 SplashAcid(x, y + 1);
5039 InitMovingField(x, y, MV_DOWN);
5040 started_moving = TRUE;
5042 Store[x][y] = EL_ACID;
5044 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5045 GfxAction[x][y + 1] = ACTION_ACTIVE;
5049 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
5050 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5051 (Feld[x][y + 1] == EL_BLOCKED)) ||
5052 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5053 CAN_SMASH(element) && WasJustFalling[x][y] &&
5054 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
5058 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5059 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5060 WasJustMoving[x][y] && !Pushed[x][y + 1])
5062 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5063 WasJustMoving[x][y])
5068 /* this is needed for a special case not covered by calling "Impact()"
5069 from "ContinueMoving()": if an element moves to a tile directly below
5070 another element which was just falling on that tile (which was empty
5071 in the previous frame), the falling element above would just stop
5072 instead of smashing the element below (in previous version, the above
5073 element was just checked for "moving" instead of "falling", resulting
5074 in incorrect smashes caused by horizontal movement of the above
5075 element; also, the case of the player being the element to smash was
5076 simply not covered here... :-/ ) */
5079 WasJustMoving[x][y] = 0;
5080 WasJustFalling[x][y] = 0;
5085 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5087 if (MovDir[x][y] == MV_NO_MOVING)
5089 InitMovingField(x, y, MV_DOWN);
5090 started_moving = TRUE;
5093 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5095 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5096 MovDir[x][y] = MV_DOWN;
5098 InitMovingField(x, y, MV_DOWN);
5099 started_moving = TRUE;
5101 else if (element == EL_AMOEBA_DROP)
5103 Feld[x][y] = EL_AMOEBA_GROWING;
5104 Store[x][y] = EL_AMOEBA_WET;
5106 /* Store[x][y + 1] must be zero, because:
5107 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5110 #if OLD_GAME_BEHAVIOUR
5111 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5113 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5114 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5115 element != EL_DX_SUPABOMB)
5118 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5119 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5120 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5121 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5124 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5125 (IS_FREE(x - 1, y + 1) ||
5126 Feld[x - 1][y + 1] == EL_ACID));
5127 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5128 (IS_FREE(x + 1, y + 1) ||
5129 Feld[x + 1][y + 1] == EL_ACID));
5130 boolean can_fall_any = (can_fall_left || can_fall_right);
5131 boolean can_fall_both = (can_fall_left && can_fall_right);
5133 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5135 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5137 if (slippery_type == SLIPPERY_ONLY_LEFT)
5138 can_fall_right = FALSE;
5139 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5140 can_fall_left = FALSE;
5141 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5142 can_fall_right = FALSE;
5143 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5144 can_fall_left = FALSE;
5146 can_fall_any = (can_fall_left || can_fall_right);
5147 can_fall_both = (can_fall_left && can_fall_right);
5152 if (can_fall_both &&
5153 (game.emulation != EMU_BOULDERDASH &&
5154 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5155 can_fall_left = !(can_fall_right = RND(2));
5157 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5158 started_moving = TRUE;
5162 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5164 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5167 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5168 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5169 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5170 int belt_dir = game.belt_dir[belt_nr];
5172 if ((belt_dir == MV_LEFT && left_is_free) ||
5173 (belt_dir == MV_RIGHT && right_is_free))
5176 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5179 InitMovingField(x, y, belt_dir);
5180 started_moving = TRUE;
5183 Pushed[x][y] = TRUE;
5184 Pushed[nextx][y] = TRUE;
5187 GfxAction[x][y] = ACTION_DEFAULT;
5191 MovDir[x][y] = 0; /* if element was moving, stop it */
5196 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5197 if (CAN_MOVE(element) && !started_moving)
5199 int move_pattern = element_info[element].move_pattern;
5202 Moving2Blocked(x, y, &newx, &newy);
5205 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5208 if ((element == EL_SATELLITE ||
5209 element == EL_BALLOON ||
5210 element == EL_SPRING)
5211 && JustBeingPushed(x, y))
5216 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5217 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5218 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5221 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5222 element, element_info[element].token_name,
5223 WasJustMoving[x][y],
5224 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5225 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5226 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5227 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5231 WasJustMoving[x][y] = 0;
5234 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5237 if (Feld[x][y] != element) /* element has changed */
5239 element = Feld[x][y];
5240 move_pattern = element_info[element].move_pattern;
5242 if (!CAN_MOVE(element))
5246 if (Feld[x][y] != element) /* element has changed */
5254 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5255 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5257 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5259 Moving2Blocked(x, y, &newx, &newy);
5260 if (Feld[newx][newy] == EL_BLOCKED)
5261 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5267 if (FrameCounter < 1 && x == 0 && y == 29)
5268 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5271 if (!MovDelay[x][y]) /* start new movement phase */
5273 /* all objects that can change their move direction after each step
5274 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5276 if (element != EL_YAMYAM &&
5277 element != EL_DARK_YAMYAM &&
5278 element != EL_PACMAN &&
5279 !(move_pattern & MV_ANY_DIRECTION) &&
5280 move_pattern != MV_TURNING_LEFT &&
5281 move_pattern != MV_TURNING_RIGHT &&
5282 move_pattern != MV_TURNING_LEFT_RIGHT &&
5283 move_pattern != MV_TURNING_RIGHT_LEFT &&
5284 move_pattern != MV_TURNING_RANDOM)
5289 if (FrameCounter < 1 && x == 0 && y == 29)
5290 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5293 if (MovDelay[x][y] && (element == EL_BUG ||
5294 element == EL_SPACESHIP ||
5295 element == EL_SP_SNIKSNAK ||
5296 element == EL_SP_ELECTRON ||
5297 element == EL_MOLE))
5298 DrawLevelField(x, y);
5302 if (MovDelay[x][y]) /* wait some time before next movement */
5307 if (element == EL_YAMYAM)
5310 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5311 DrawLevelElementAnimation(x, y, element);
5315 if (MovDelay[x][y]) /* element still has to wait some time */
5318 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5319 ResetGfxAnimation(x, y);
5323 if (GfxAction[x][y] != ACTION_WAITING)
5324 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5326 GfxAction[x][y] = ACTION_WAITING;
5330 if (element == EL_ROBOT ||
5332 element == EL_PACMAN ||
5334 element == EL_YAMYAM ||
5335 element == EL_DARK_YAMYAM)
5338 DrawLevelElementAnimation(x, y, element);
5340 DrawLevelElementAnimationIfNeeded(x, y, element);
5342 PlayLevelSoundAction(x, y, ACTION_WAITING);
5344 else if (element == EL_SP_ELECTRON)
5345 DrawLevelElementAnimationIfNeeded(x, y, element);
5346 else if (element == EL_DRAGON)
5349 int dir = MovDir[x][y];
5350 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5351 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5352 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5353 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5354 dir == MV_UP ? IMG_FLAMES_1_UP :
5355 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5356 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5359 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5362 GfxAction[x][y] = ACTION_ATTACKING;
5364 if (IS_PLAYER(x, y))
5365 DrawPlayerField(x, y);
5367 DrawLevelField(x, y);
5369 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5371 for (i = 1; i <= 3; i++)
5373 int xx = x + i * dx;
5374 int yy = y + i * dy;
5375 int sx = SCREENX(xx);
5376 int sy = SCREENY(yy);
5377 int flame_graphic = graphic + (i - 1);
5379 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5384 int flamed = MovingOrBlocked2Element(xx, yy);
5388 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5390 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5391 RemoveMovingField(xx, yy);
5393 RemoveField(xx, yy);
5395 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5398 RemoveMovingField(xx, yy);
5402 if (ChangeDelay[xx][yy])
5403 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5404 Feld[xx][yy] == EL_BLOCKED));
5408 ChangeDelay[xx][yy] = 0;
5410 Feld[xx][yy] = EL_FLAMES;
5411 if (IN_SCR_FIELD(sx, sy))
5413 DrawLevelFieldCrumbledSand(xx, yy);
5414 DrawGraphic(sx, sy, flame_graphic, frame);
5419 if (Feld[xx][yy] == EL_FLAMES)
5420 Feld[xx][yy] = EL_EMPTY;
5421 DrawLevelField(xx, yy);
5426 if (MovDelay[x][y]) /* element still has to wait some time */
5428 PlayLevelSoundAction(x, y, ACTION_WAITING);
5434 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5435 for all other elements GfxAction will be set by InitMovingField() */
5436 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5437 GfxAction[x][y] = ACTION_MOVING;
5441 /* now make next step */
5443 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5445 if (DONT_COLLIDE_WITH(element) &&
5446 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5447 !PLAYER_ENEMY_PROTECTED(newx, newy))
5450 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5454 /* player killed by element which is deadly when colliding with */
5456 KillHero(PLAYERINFO(newx, newy));
5463 else if (CAN_MOVE_INTO_ACID(element) &&
5464 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5465 (MovDir[x][y] == MV_DOWN ||
5466 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5468 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5469 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5473 else if ((element == EL_PENGUIN ||
5474 element == EL_ROBOT ||
5475 element == EL_SATELLITE ||
5476 element == EL_BALLOON ||
5477 IS_CUSTOM_ELEMENT(element)) &&
5478 IN_LEV_FIELD(newx, newy) &&
5479 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5482 SplashAcid(newx, newy);
5483 Store[x][y] = EL_ACID;
5485 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5487 if (Feld[newx][newy] == EL_EXIT_OPEN)
5491 DrawLevelField(x, y);
5493 Feld[x][y] = EL_EMPTY;
5494 DrawLevelField(x, y);
5497 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5498 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5499 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5501 local_player->friends_still_needed--;
5502 if (!local_player->friends_still_needed &&
5503 !local_player->GameOver && AllPlayersGone)
5504 local_player->LevelSolved = local_player->GameOver = TRUE;
5508 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5510 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5511 DrawLevelField(newx, newy);
5513 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5515 else if (!IS_FREE(newx, newy))
5517 GfxAction[x][y] = ACTION_WAITING;
5519 if (IS_PLAYER(x, y))
5520 DrawPlayerField(x, y);
5522 DrawLevelField(x, y);
5527 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5529 if (IS_FOOD_PIG(Feld[newx][newy]))
5531 if (IS_MOVING(newx, newy))
5532 RemoveMovingField(newx, newy);
5535 Feld[newx][newy] = EL_EMPTY;
5536 DrawLevelField(newx, newy);
5539 PlayLevelSound(x, y, SND_PIG_DIGGING);
5541 else if (!IS_FREE(newx, newy))
5543 if (IS_PLAYER(x, y))
5544 DrawPlayerField(x, y);
5546 DrawLevelField(x, y);
5555 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5558 else if (IS_CUSTOM_ELEMENT(element) &&
5559 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5563 !IS_FREE(newx, newy)
5568 int new_element = Feld[newx][newy];
5571 printf("::: '%s' digs '%s' [%d]\n",
5572 element_info[element].token_name,
5573 element_info[Feld[newx][newy]].token_name,
5574 StorePlayer[newx][newy]);
5577 if (!IS_FREE(newx, newy))
5579 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5580 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5583 /* no element can dig solid indestructible elements */
5584 if (IS_INDESTRUCTIBLE(new_element) &&
5585 !IS_DIGGABLE(new_element) &&
5586 !IS_COLLECTIBLE(new_element))
5589 if (AmoebaNr[newx][newy] &&
5590 (new_element == EL_AMOEBA_FULL ||
5591 new_element == EL_BD_AMOEBA ||
5592 new_element == EL_AMOEBA_GROWING))
5594 AmoebaCnt[AmoebaNr[newx][newy]]--;
5595 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5598 if (IS_MOVING(newx, newy))
5599 RemoveMovingField(newx, newy);
5602 RemoveField(newx, newy);
5603 DrawLevelField(newx, newy);
5606 PlayLevelSoundAction(x, y, action);
5611 Store[newx][newy] = EL_EMPTY;
5612 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5613 Store[newx][newy] = element_info[element].move_leave_element;
5615 Store[newx][newy] = EL_EMPTY;
5616 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5617 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5618 Store[newx][newy] = element_info[element].move_leave_element;
5621 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5622 element_info[element].can_leave_element = TRUE;
5625 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5627 RunnerVisit[x][y] = FrameCounter;
5628 PlayerVisit[x][y] /= 8; /* expire player visit path */
5634 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5636 if (!IS_FREE(newx, newy))
5638 if (IS_PLAYER(x, y))
5639 DrawPlayerField(x, y);
5641 DrawLevelField(x, y);
5647 boolean wanna_flame = !RND(10);
5648 int dx = newx - x, dy = newy - y;
5649 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5650 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5651 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5652 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5653 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5654 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5657 IS_CLASSIC_ENEMY(element1) ||
5658 IS_CLASSIC_ENEMY(element2)) &&
5659 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5660 element1 != EL_FLAMES && element2 != EL_FLAMES)
5663 ResetGfxAnimation(x, y);
5664 GfxAction[x][y] = ACTION_ATTACKING;
5667 if (IS_PLAYER(x, y))
5668 DrawPlayerField(x, y);
5670 DrawLevelField(x, y);
5672 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5674 MovDelay[x][y] = 50;
5678 RemoveField(newx, newy);
5680 Feld[newx][newy] = EL_FLAMES;
5681 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5684 RemoveField(newx1, newy1);
5686 Feld[newx1][newy1] = EL_FLAMES;
5688 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5691 RemoveField(newx2, newy2);
5693 Feld[newx2][newy2] = EL_FLAMES;
5700 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5701 Feld[newx][newy] == EL_DIAMOND)
5703 if (IS_MOVING(newx, newy))
5704 RemoveMovingField(newx, newy);
5707 Feld[newx][newy] = EL_EMPTY;
5708 DrawLevelField(newx, newy);
5711 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5713 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5714 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5716 if (AmoebaNr[newx][newy])
5718 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5719 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5720 Feld[newx][newy] == EL_BD_AMOEBA)
5721 AmoebaCnt[AmoebaNr[newx][newy]]--;
5726 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5728 if (IS_MOVING(newx, newy))
5731 RemoveMovingField(newx, newy);
5735 Feld[newx][newy] = EL_EMPTY;
5736 DrawLevelField(newx, newy);
5739 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5741 else if ((element == EL_PACMAN || element == EL_MOLE)
5742 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5744 if (AmoebaNr[newx][newy])
5746 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5747 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5748 Feld[newx][newy] == EL_BD_AMOEBA)
5749 AmoebaCnt[AmoebaNr[newx][newy]]--;
5752 if (element == EL_MOLE)
5754 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5755 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5757 ResetGfxAnimation(x, y);
5758 GfxAction[x][y] = ACTION_DIGGING;
5759 DrawLevelField(x, y);
5761 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5763 return; /* wait for shrinking amoeba */
5765 else /* element == EL_PACMAN */
5767 Feld[newx][newy] = EL_EMPTY;
5768 DrawLevelField(newx, newy);
5769 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5772 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5773 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5774 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5776 /* wait for shrinking amoeba to completely disappear */
5779 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5781 /* object was running against a wall */
5786 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5787 DrawLevelElementAnimation(x, y, element);
5789 if (element == EL_BUG ||
5790 element == EL_SPACESHIP ||
5791 element == EL_SP_SNIKSNAK)
5792 DrawLevelField(x, y);
5793 else if (element == EL_MOLE)
5794 DrawLevelField(x, y);
5795 else if (element == EL_BD_BUTTERFLY ||
5796 element == EL_BD_FIREFLY)
5797 DrawLevelElementAnimationIfNeeded(x, y, element);
5798 else if (element == EL_SATELLITE)
5799 DrawLevelElementAnimationIfNeeded(x, y, element);
5800 else if (element == EL_SP_ELECTRON)
5801 DrawLevelElementAnimationIfNeeded(x, y, element);
5804 if (DONT_TOUCH(element))
5805 TestIfBadThingTouchesHero(x, y);
5808 PlayLevelSoundAction(x, y, ACTION_WAITING);
5814 InitMovingField(x, y, MovDir[x][y]);
5816 PlayLevelSoundAction(x, y, ACTION_MOVING);
5820 ContinueMoving(x, y);
5823 void ContinueMoving(int x, int y)
5825 int element = Feld[x][y];
5826 int stored = Store[x][y];
5827 struct ElementInfo *ei = &element_info[element];
5828 int direction = MovDir[x][y];
5829 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5830 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5831 int newx = x + dx, newy = y + dy;
5833 int nextx = newx + dx, nexty = newy + dy;
5836 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5837 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5839 boolean pushed_by_player = Pushed[x][y];
5842 MovPos[x][y] += getElementMoveStepsize(x, y);
5845 if (pushed_by_player && IS_PLAYER(x, y))
5847 /* special case: moving object pushed by player */
5848 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5851 if (pushed_by_player) /* special case: moving object pushed by player */
5852 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5855 if (ABS(MovPos[x][y]) < TILEX)
5857 DrawLevelField(x, y);
5859 return; /* element is still moving */
5862 /* element reached destination field */
5864 Feld[x][y] = EL_EMPTY;
5865 Feld[newx][newy] = element;
5866 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5868 if (element == EL_MOLE)
5870 Feld[x][y] = EL_SAND;
5872 DrawLevelFieldCrumbledSandNeighbours(x, y);
5874 else if (element == EL_QUICKSAND_FILLING)
5876 element = Feld[newx][newy] = get_next_element(element);
5877 Store[newx][newy] = Store[x][y];
5879 else if (element == EL_QUICKSAND_EMPTYING)
5881 Feld[x][y] = get_next_element(element);
5882 element = Feld[newx][newy] = Store[x][y];
5884 else if (element == EL_MAGIC_WALL_FILLING)
5886 element = Feld[newx][newy] = get_next_element(element);
5887 if (!game.magic_wall_active)
5888 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5889 Store[newx][newy] = Store[x][y];
5891 else if (element == EL_MAGIC_WALL_EMPTYING)
5893 Feld[x][y] = get_next_element(element);
5894 if (!game.magic_wall_active)
5895 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5896 element = Feld[newx][newy] = Store[x][y];
5898 else if (element == EL_BD_MAGIC_WALL_FILLING)
5900 element = Feld[newx][newy] = get_next_element(element);
5901 if (!game.magic_wall_active)
5902 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5903 Store[newx][newy] = Store[x][y];
5905 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5907 Feld[x][y] = get_next_element(element);
5908 if (!game.magic_wall_active)
5909 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5910 element = Feld[newx][newy] = Store[x][y];
5912 else if (element == EL_AMOEBA_DROPPING)
5914 Feld[x][y] = get_next_element(element);
5915 element = Feld[newx][newy] = Store[x][y];
5917 else if (element == EL_SOKOBAN_OBJECT)
5920 Feld[x][y] = Back[x][y];
5922 if (Back[newx][newy])
5923 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5925 Back[x][y] = Back[newx][newy] = 0;
5927 else if (Store[x][y] == EL_ACID)
5929 element = Feld[newx][newy] = EL_ACID;
5932 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5933 ei->move_leave_element != EL_EMPTY &&
5934 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5935 Store[x][y] != EL_EMPTY))
5937 /* some elements can leave other elements behind after moving */
5939 Feld[x][y] = ei->move_leave_element;
5940 InitField(x, y, FALSE);
5942 if (GFX_CRUMBLED(Feld[x][y]))
5943 DrawLevelFieldCrumbledSandNeighbours(x, y);
5947 Store[x][y] = EL_EMPTY;
5948 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5949 MovDelay[newx][newy] = 0;
5951 if (CAN_CHANGE(element))
5953 /* copy element change control values to new field */
5954 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5955 ChangePage[newx][newy] = ChangePage[x][y];
5956 Changed[newx][newy] = Changed[x][y];
5957 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5960 ChangeDelay[x][y] = 0;
5961 ChangePage[x][y] = -1;
5962 Changed[x][y] = CE_BITMASK_DEFAULT;
5963 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5965 /* copy animation control values to new field */
5966 GfxFrame[newx][newy] = GfxFrame[x][y];
5967 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5968 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5969 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5971 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5973 ResetGfxAnimation(x, y); /* reset animation values for old field */
5976 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5977 ei->move_leave_element != EL_EMPTY &&
5978 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5979 stored != EL_EMPTY))
5981 /* some elements can leave other elements behind after moving */
5983 Feld[x][y] = ei->move_leave_element;
5984 InitField(x, y, FALSE);
5986 if (GFX_CRUMBLED(Feld[x][y]))
5987 DrawLevelFieldCrumbledSandNeighbours(x, y);
5992 /* some elements can leave other elements behind after moving */
5993 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5994 ei->move_leave_element != EL_EMPTY &&
5995 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5996 ei->can_leave_element_last))
5998 Feld[x][y] = ei->move_leave_element;
5999 InitField(x, y, FALSE);
6001 if (GFX_CRUMBLED(Feld[x][y]))
6002 DrawLevelFieldCrumbledSandNeighbours(x, y);
6005 ei->can_leave_element_last = ei->can_leave_element;
6006 ei->can_leave_element = FALSE;
6010 /* 2.1.1 (does not work correctly for spring) */
6011 if (!CAN_MOVE(element))
6012 MovDir[newx][newy] = 0;
6016 /* (does not work for falling objects that slide horizontally) */
6017 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6018 MovDir[newx][newy] = 0;
6021 if (!CAN_MOVE(element) ||
6022 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6023 MovDir[newx][newy] = 0;
6027 if (!CAN_MOVE(element) ||
6028 (CAN_FALL(element) && direction == MV_DOWN))
6029 GfxDir[x][y] = MovDir[newx][newy] = 0;
6031 if (!CAN_MOVE(element) ||
6032 (CAN_FALL(element) && direction == MV_DOWN &&
6033 (element == EL_SPRING ||
6034 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6035 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6036 GfxDir[x][y] = MovDir[newx][newy] = 0;
6042 DrawLevelField(x, y);
6043 DrawLevelField(newx, newy);
6045 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6047 /* prevent pushed element from moving on in pushed direction */
6048 if (pushed_by_player && CAN_MOVE(element) &&
6049 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6050 !(element_info[element].move_pattern & direction))
6051 TurnRound(newx, newy);
6054 /* prevent elements on conveyor belt from moving on in last direction */
6055 if (pushed_by_conveyor && CAN_FALL(element) &&
6056 direction & MV_HORIZONTAL)
6057 MovDir[newx][newy] = 0;
6060 if (!pushed_by_player)
6062 WasJustMoving[newx][newy] = 3;
6064 if (CAN_FALL(element) && direction == MV_DOWN)
6065 WasJustFalling[newx][newy] = 3;
6068 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6070 TestIfBadThingTouchesHero(newx, newy);
6071 TestIfBadThingTouchesFriend(newx, newy);
6073 if (!IS_CUSTOM_ELEMENT(element))
6074 TestIfBadThingTouchesOtherBadThing(newx, newy);
6076 else if (element == EL_PENGUIN)
6077 TestIfFriendTouchesBadThing(newx, newy);
6079 if (CAN_FALL(element) && direction == MV_DOWN &&
6080 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6084 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6088 if (ChangePage[newx][newy] != -1) /* delayed change */
6089 ChangeElement(newx, newy, ChangePage[newx][newy]);
6094 TestIfElementHitsCustomElement(newx, newy, direction);
6098 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6100 int hitting_element = Feld[newx][newy];
6102 /* !!! fix side (direction) orientation here and elsewhere !!! */
6103 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6107 if (IN_LEV_FIELD(nextx, nexty))
6109 int opposite_direction = MV_DIR_OPPOSITE(direction);
6110 int hitting_side = direction;
6111 int touched_side = opposite_direction;
6112 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6113 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6114 MovDir[nextx][nexty] != direction ||
6115 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6121 CheckElementChangeBySide(nextx, nexty, touched_element,
6122 CE_HIT_BY_SOMETHING, opposite_direction);
6124 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6125 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6127 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6129 struct ElementChangeInfo *change =
6130 &element_info[hitting_element].change_page[i];
6132 if (change->can_change &&
6133 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6134 change->trigger_side & touched_side &&
6135 change->trigger_element == touched_element)
6137 CheckElementChangeByPage(newx, newy, hitting_element,
6138 touched_element, CE_OTHER_IS_HITTING,i);
6144 if (IS_CUSTOM_ELEMENT(touched_element) &&
6145 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6147 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6149 struct ElementChangeInfo *change =
6150 &element_info[touched_element].change_page[i];
6152 if (change->can_change &&
6153 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6154 change->trigger_side & hitting_side &&
6155 change->trigger_element == hitting_element)
6157 CheckElementChangeByPage(nextx, nexty, touched_element,
6158 hitting_element, CE_OTHER_GETS_HIT, i);
6169 TestIfPlayerTouchesCustomElement(newx, newy);
6170 TestIfElementTouchesCustomElement(newx, newy);
6173 int AmoebeNachbarNr(int ax, int ay)
6176 int element = Feld[ax][ay];
6178 static int xy[4][2] =
6186 for (i = 0; i < NUM_DIRECTIONS; i++)
6188 int x = ax + xy[i][0];
6189 int y = ay + xy[i][1];
6191 if (!IN_LEV_FIELD(x, y))
6194 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6195 group_nr = AmoebaNr[x][y];
6201 void AmoebenVereinigen(int ax, int ay)
6203 int i, x, y, xx, yy;
6204 int new_group_nr = AmoebaNr[ax][ay];
6205 static int xy[4][2] =
6213 if (new_group_nr == 0)
6216 for (i = 0; i < NUM_DIRECTIONS; i++)
6221 if (!IN_LEV_FIELD(x, y))
6224 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6225 Feld[x][y] == EL_BD_AMOEBA ||
6226 Feld[x][y] == EL_AMOEBA_DEAD) &&
6227 AmoebaNr[x][y] != new_group_nr)
6229 int old_group_nr = AmoebaNr[x][y];
6231 if (old_group_nr == 0)
6234 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6235 AmoebaCnt[old_group_nr] = 0;
6236 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6237 AmoebaCnt2[old_group_nr] = 0;
6239 for (yy = 0; yy < lev_fieldy; yy++)
6241 for (xx = 0; xx < lev_fieldx; xx++)
6243 if (AmoebaNr[xx][yy] == old_group_nr)
6244 AmoebaNr[xx][yy] = new_group_nr;
6251 void AmoebeUmwandeln(int ax, int ay)
6255 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6257 int group_nr = AmoebaNr[ax][ay];
6262 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6263 printf("AmoebeUmwandeln(): This should never happen!\n");
6268 for (y = 0; y < lev_fieldy; y++)
6270 for (x = 0; x < lev_fieldx; x++)
6272 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6275 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6279 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6280 SND_AMOEBA_TURNING_TO_GEM :
6281 SND_AMOEBA_TURNING_TO_ROCK));
6286 static int xy[4][2] =
6294 for (i = 0; i < NUM_DIRECTIONS; i++)
6299 if (!IN_LEV_FIELD(x, y))
6302 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6304 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6305 SND_AMOEBA_TURNING_TO_GEM :
6306 SND_AMOEBA_TURNING_TO_ROCK));
6313 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6316 int group_nr = AmoebaNr[ax][ay];
6317 boolean done = FALSE;
6322 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6323 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6328 for (y = 0; y < lev_fieldy; y++)
6330 for (x = 0; x < lev_fieldx; x++)
6332 if (AmoebaNr[x][y] == group_nr &&
6333 (Feld[x][y] == EL_AMOEBA_DEAD ||
6334 Feld[x][y] == EL_BD_AMOEBA ||
6335 Feld[x][y] == EL_AMOEBA_GROWING))
6338 Feld[x][y] = new_element;
6339 InitField(x, y, FALSE);
6340 DrawLevelField(x, y);
6347 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6348 SND_BD_AMOEBA_TURNING_TO_ROCK :
6349 SND_BD_AMOEBA_TURNING_TO_GEM));
6352 void AmoebeWaechst(int x, int y)
6354 static unsigned long sound_delay = 0;
6355 static unsigned long sound_delay_value = 0;
6357 if (!MovDelay[x][y]) /* start new growing cycle */
6361 if (DelayReached(&sound_delay, sound_delay_value))
6364 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6366 if (Store[x][y] == EL_BD_AMOEBA)
6367 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6369 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6371 sound_delay_value = 30;
6375 if (MovDelay[x][y]) /* wait some time before growing bigger */
6378 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6380 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6381 6 - MovDelay[x][y]);
6383 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6386 if (!MovDelay[x][y])
6388 Feld[x][y] = Store[x][y];
6390 DrawLevelField(x, y);
6395 void AmoebaDisappearing(int x, int y)
6397 static unsigned long sound_delay = 0;
6398 static unsigned long sound_delay_value = 0;
6400 if (!MovDelay[x][y]) /* start new shrinking cycle */
6404 if (DelayReached(&sound_delay, sound_delay_value))
6405 sound_delay_value = 30;
6408 if (MovDelay[x][y]) /* wait some time before shrinking */
6411 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6413 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6414 6 - MovDelay[x][y]);
6416 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6419 if (!MovDelay[x][y])
6421 Feld[x][y] = EL_EMPTY;
6422 DrawLevelField(x, y);
6424 /* don't let mole enter this field in this cycle;
6425 (give priority to objects falling to this field from above) */
6431 void AmoebeAbleger(int ax, int ay)
6434 int element = Feld[ax][ay];
6435 int graphic = el2img(element);
6436 int newax = ax, neway = ay;
6437 static int xy[4][2] =
6445 if (!level.amoeba_speed)
6447 Feld[ax][ay] = EL_AMOEBA_DEAD;
6448 DrawLevelField(ax, ay);
6452 if (IS_ANIMATED(graphic))
6453 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6455 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6456 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6458 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6461 if (MovDelay[ax][ay])
6465 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6468 int x = ax + xy[start][0];
6469 int y = ay + xy[start][1];
6471 if (!IN_LEV_FIELD(x, y))
6475 if (IS_FREE(x, y) ||
6476 CAN_GROW_INTO(Feld[x][y]) ||
6477 Feld[x][y] == EL_QUICKSAND_EMPTY)
6483 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6484 if (IS_FREE(x, y) ||
6485 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6492 if (newax == ax && neway == ay)
6495 else /* normal or "filled" (BD style) amoeba */
6498 boolean waiting_for_player = FALSE;
6500 for (i = 0; i < NUM_DIRECTIONS; i++)
6502 int j = (start + i) % 4;
6503 int x = ax + xy[j][0];
6504 int y = ay + xy[j][1];
6506 if (!IN_LEV_FIELD(x, y))
6510 if (IS_FREE(x, y) ||
6511 CAN_GROW_INTO(Feld[x][y]) ||
6512 Feld[x][y] == EL_QUICKSAND_EMPTY)
6519 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6520 if (IS_FREE(x, y) ||
6521 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6528 else if (IS_PLAYER(x, y))
6529 waiting_for_player = TRUE;
6532 if (newax == ax && neway == ay) /* amoeba cannot grow */
6535 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6537 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6540 Feld[ax][ay] = EL_AMOEBA_DEAD;
6541 DrawLevelField(ax, ay);
6542 AmoebaCnt[AmoebaNr[ax][ay]]--;
6544 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6546 if (element == EL_AMOEBA_FULL)
6547 AmoebeUmwandeln(ax, ay);
6548 else if (element == EL_BD_AMOEBA)
6549 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6554 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6556 /* amoeba gets larger by growing in some direction */
6558 int new_group_nr = AmoebaNr[ax][ay];
6561 if (new_group_nr == 0)
6563 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6564 printf("AmoebeAbleger(): This should never happen!\n");
6569 AmoebaNr[newax][neway] = new_group_nr;
6570 AmoebaCnt[new_group_nr]++;
6571 AmoebaCnt2[new_group_nr]++;
6573 /* if amoeba touches other amoeba(s) after growing, unify them */
6574 AmoebenVereinigen(newax, neway);
6576 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6578 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6584 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6585 (neway == lev_fieldy - 1 && newax != ax))
6587 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6588 Store[newax][neway] = element;
6590 else if (neway == ay)
6592 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6594 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6596 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6601 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6602 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6603 Store[ax][ay] = EL_AMOEBA_DROP;
6604 ContinueMoving(ax, ay);
6608 DrawLevelField(newax, neway);
6611 void Life(int ax, int ay)
6614 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6616 int element = Feld[ax][ay];
6617 int graphic = el2img(element);
6618 boolean changed = FALSE;
6620 if (IS_ANIMATED(graphic))
6621 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6626 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6627 MovDelay[ax][ay] = life_time;
6629 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6632 if (MovDelay[ax][ay])
6636 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6638 int xx = ax+x1, yy = ay+y1;
6641 if (!IN_LEV_FIELD(xx, yy))
6644 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6646 int x = xx+x2, y = yy+y2;
6648 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6651 if (((Feld[x][y] == element ||
6652 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6654 (IS_FREE(x, y) && Stop[x][y]))
6658 if (xx == ax && yy == ay) /* field in the middle */
6660 if (nachbarn < life[0] || nachbarn > life[1])
6662 Feld[xx][yy] = EL_EMPTY;
6664 DrawLevelField(xx, yy);
6665 Stop[xx][yy] = TRUE;
6670 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6671 { /* free border field */
6672 if (nachbarn >= life[2] && nachbarn <= life[3])
6674 Feld[xx][yy] = element;
6675 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6677 DrawLevelField(xx, yy);
6678 Stop[xx][yy] = TRUE;
6683 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6684 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6685 { /* free border field */
6686 if (nachbarn >= life[2] && nachbarn <= life[3])
6688 Feld[xx][yy] = element;
6689 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6691 DrawLevelField(xx, yy);
6692 Stop[xx][yy] = TRUE;
6700 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6701 SND_GAME_OF_LIFE_GROWING);
6704 static void InitRobotWheel(int x, int y)
6706 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6709 static void RunRobotWheel(int x, int y)
6711 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6714 static void StopRobotWheel(int x, int y)
6716 if (ZX == x && ZY == y)
6720 static void InitTimegateWheel(int x, int y)
6723 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6725 /* another brainless, "type style" bug ... :-( */
6726 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6730 static void RunTimegateWheel(int x, int y)
6732 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6735 void CheckExit(int x, int y)
6737 if (local_player->gems_still_needed > 0 ||
6738 local_player->sokobanfields_still_needed > 0 ||
6739 local_player->lights_still_needed > 0)
6741 int element = Feld[x][y];
6742 int graphic = el2img(element);
6744 if (IS_ANIMATED(graphic))
6745 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6750 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6753 Feld[x][y] = EL_EXIT_OPENING;
6755 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6758 void CheckExitSP(int x, int y)
6760 if (local_player->gems_still_needed > 0)
6762 int element = Feld[x][y];
6763 int graphic = el2img(element);
6765 if (IS_ANIMATED(graphic))
6766 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6771 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6774 Feld[x][y] = EL_SP_EXIT_OPENING;
6776 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6779 static void CloseAllOpenTimegates()
6783 for (y = 0; y < lev_fieldy; y++)
6785 for (x = 0; x < lev_fieldx; x++)
6787 int element = Feld[x][y];
6789 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6791 Feld[x][y] = EL_TIMEGATE_CLOSING;
6793 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6795 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6802 void EdelsteinFunkeln(int x, int y)
6804 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6807 if (Feld[x][y] == EL_BD_DIAMOND)
6810 if (MovDelay[x][y] == 0) /* next animation frame */
6811 MovDelay[x][y] = 11 * !SimpleRND(500);
6813 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6817 if (setup.direct_draw && MovDelay[x][y])
6818 SetDrawtoField(DRAW_BUFFERED);
6820 DrawLevelElementAnimation(x, y, Feld[x][y]);
6822 if (MovDelay[x][y] != 0)
6824 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6825 10 - MovDelay[x][y]);
6827 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6829 if (setup.direct_draw)
6833 dest_x = FX + SCREENX(x) * TILEX;
6834 dest_y = FY + SCREENY(y) * TILEY;
6836 BlitBitmap(drawto_field, window,
6837 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6838 SetDrawtoField(DRAW_DIRECT);
6844 void MauerWaechst(int x, int y)
6848 if (!MovDelay[x][y]) /* next animation frame */
6849 MovDelay[x][y] = 3 * delay;
6851 if (MovDelay[x][y]) /* wait some time before next frame */
6855 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6857 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6858 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6860 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6863 if (!MovDelay[x][y])
6865 if (MovDir[x][y] == MV_LEFT)
6867 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6868 DrawLevelField(x - 1, y);
6870 else if (MovDir[x][y] == MV_RIGHT)
6872 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6873 DrawLevelField(x + 1, y);
6875 else if (MovDir[x][y] == MV_UP)
6877 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6878 DrawLevelField(x, y - 1);
6882 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6883 DrawLevelField(x, y + 1);
6886 Feld[x][y] = Store[x][y];
6888 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6889 DrawLevelField(x, y);
6894 void MauerAbleger(int ax, int ay)
6896 int element = Feld[ax][ay];
6897 int graphic = el2img(element);
6898 boolean oben_frei = FALSE, unten_frei = FALSE;
6899 boolean links_frei = FALSE, rechts_frei = FALSE;
6900 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6901 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6902 boolean new_wall = FALSE;
6904 if (IS_ANIMATED(graphic))
6905 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6907 if (!MovDelay[ax][ay]) /* start building new wall */
6908 MovDelay[ax][ay] = 6;
6910 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6913 if (MovDelay[ax][ay])
6917 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6919 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6921 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6923 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6926 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6927 element == EL_EXPANDABLE_WALL_ANY)
6931 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6932 Store[ax][ay-1] = element;
6933 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6934 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6935 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6936 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6941 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6942 Store[ax][ay+1] = element;
6943 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6944 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6945 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6946 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6951 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6952 element == EL_EXPANDABLE_WALL_ANY ||
6953 element == EL_EXPANDABLE_WALL)
6957 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6958 Store[ax-1][ay] = element;
6959 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6960 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6961 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6962 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6968 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6969 Store[ax+1][ay] = element;
6970 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6971 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6972 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6973 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6978 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6979 DrawLevelField(ax, ay);
6981 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6983 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6984 unten_massiv = TRUE;
6985 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6986 links_massiv = TRUE;
6987 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6988 rechts_massiv = TRUE;
6990 if (((oben_massiv && unten_massiv) ||
6991 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6992 element == EL_EXPANDABLE_WALL) &&
6993 ((links_massiv && rechts_massiv) ||
6994 element == EL_EXPANDABLE_WALL_VERTICAL))
6995 Feld[ax][ay] = EL_WALL;
6999 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7001 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7005 void CheckForDragon(int x, int y)
7008 boolean dragon_found = FALSE;
7009 static int xy[4][2] =
7017 for (i = 0; i < NUM_DIRECTIONS; i++)
7019 for (j = 0; j < 4; j++)
7021 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7023 if (IN_LEV_FIELD(xx, yy) &&
7024 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7026 if (Feld[xx][yy] == EL_DRAGON)
7027 dragon_found = TRUE;
7036 for (i = 0; i < NUM_DIRECTIONS; i++)
7038 for (j = 0; j < 3; j++)
7040 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7042 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7044 Feld[xx][yy] = EL_EMPTY;
7045 DrawLevelField(xx, yy);
7054 static void InitBuggyBase(int x, int y)
7056 int element = Feld[x][y];
7057 int activating_delay = FRAMES_PER_SECOND / 4;
7060 (element == EL_SP_BUGGY_BASE ?
7061 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7062 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7064 element == EL_SP_BUGGY_BASE_ACTIVE ?
7065 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7068 static void WarnBuggyBase(int x, int y)
7071 static int xy[4][2] =
7079 for (i = 0; i < NUM_DIRECTIONS; i++)
7081 int xx = x + xy[i][0], yy = y + xy[i][1];
7083 if (IS_PLAYER(xx, yy))
7085 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7092 static void InitTrap(int x, int y)
7094 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7097 static void ActivateTrap(int x, int y)
7099 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7102 static void ChangeActiveTrap(int x, int y)
7104 int graphic = IMG_TRAP_ACTIVE;
7106 /* if new animation frame was drawn, correct crumbled sand border */
7107 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7108 DrawLevelFieldCrumbledSand(x, y);
7111 static void ChangeElementNowExt(int x, int y, int target_element)
7113 int previous_move_direction = MovDir[x][y];
7115 /* check if element under player changes from accessible to unaccessible
7116 (needed for special case of dropping element which then changes) */
7117 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7118 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7125 Feld[x][y] = target_element;
7127 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7129 ResetGfxAnimation(x, y);
7130 ResetRandomAnimationValue(x, y);
7132 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7133 MovDir[x][y] = previous_move_direction;
7136 InitField_WithBug1(x, y, FALSE);
7138 InitField(x, y, FALSE);
7139 if (CAN_MOVE(Feld[x][y]))
7143 DrawLevelField(x, y);
7145 if (GFX_CRUMBLED(Feld[x][y]))
7146 DrawLevelFieldCrumbledSandNeighbours(x, y);
7148 TestIfBadThingTouchesHero(x, y);
7149 TestIfPlayerTouchesCustomElement(x, y);
7150 TestIfElementTouchesCustomElement(x, y);
7152 if (ELEM_IS_PLAYER(target_element))
7153 RelocatePlayer(x, y, target_element);
7156 static boolean ChangeElementNow(int x, int y, int element, int page)
7158 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7161 /* always use default change event to prevent running into a loop */
7162 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7163 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7165 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7167 /* reset actual trigger element and player */
7168 change->actual_trigger_element = EL_EMPTY;
7169 change->actual_trigger_player = EL_PLAYER_1;
7172 /* do not change already changed elements with same change event */
7174 if (Changed[x][y] & ChangeEvent[x][y])
7181 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7183 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7185 if (change->explode)
7192 if (change->use_target_content)
7194 boolean complete_replace = TRUE;
7195 boolean can_replace[3][3];
7198 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7201 boolean is_diggable;
7202 boolean is_destructible;
7203 int ex = x + xx - 1;
7204 int ey = y + yy - 1;
7205 int content_element = change->target_content[xx][yy];
7208 can_replace[xx][yy] = TRUE;
7210 if (ex == x && ey == y) /* do not check changing element itself */
7213 if (content_element == EL_EMPTY_SPACE)
7215 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7220 if (!IN_LEV_FIELD(ex, ey))
7222 can_replace[xx][yy] = FALSE;
7223 complete_replace = FALSE;
7230 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7231 e = MovingOrBlocked2Element(ex, ey);
7236 is_empty = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7237 IS_WALKABLE(content_element)));
7239 is_empty = (IS_FREE(ex, ey) || (IS_PLAYER(ex, ey) &&
7240 IS_WALKABLE(content_element)));
7242 is_diggable = (is_empty || IS_DIGGABLE(e));
7243 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7245 can_replace[xx][yy] =
7246 ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7247 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7248 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7250 if (!can_replace[xx][yy])
7251 complete_replace = FALSE;
7253 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7254 IS_WALKABLE(content_element)));
7256 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7258 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7261 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7262 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7263 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7265 can_replace[xx][yy] = FALSE;
7266 complete_replace = FALSE;
7271 if (!change->only_if_complete || complete_replace)
7273 boolean something_has_changed = FALSE;
7275 if (change->only_if_complete && change->use_random_replace &&
7276 RND(100) < change->random_percentage)
7279 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7281 int ex = x + xx - 1;
7282 int ey = y + yy - 1;
7283 int content_element;
7285 if (can_replace[xx][yy] && (!change->use_random_replace ||
7286 RND(100) < change->random_percentage))
7288 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7289 RemoveMovingField(ex, ey);
7291 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7293 content_element = change->target_content[xx][yy];
7294 target_element = GET_TARGET_ELEMENT(content_element, change);
7296 ChangeElementNowExt(ex, ey, target_element);
7298 something_has_changed = TRUE;
7300 /* for symmetry reasons, freeze newly created border elements */
7301 if (ex != x || ey != y)
7302 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7306 if (something_has_changed)
7307 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7312 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7314 ChangeElementNowExt(x, y, target_element);
7316 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7322 static void ChangeElement(int x, int y, int page)
7324 int element = MovingOrBlocked2Element(x, y);
7325 struct ElementInfo *ei = &element_info[element];
7326 struct ElementChangeInfo *change = &ei->change_page[page];
7329 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7332 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7333 x, y, element, element_info[element].token_name);
7334 printf("ChangeElement(): This should never happen!\n");
7339 /* this can happen with classic bombs on walkable, changing elements */
7340 if (!CAN_CHANGE(element))
7343 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7344 ChangeDelay[x][y] = 0;
7350 if (ChangeDelay[x][y] == 0) /* initialize element change */
7352 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7353 RND(change->delay_random * change->delay_frames)) + 1;
7355 ResetGfxAnimation(x, y);
7356 ResetRandomAnimationValue(x, y);
7358 if (change->pre_change_function)
7359 change->pre_change_function(x, y);
7362 ChangeDelay[x][y]--;
7364 if (ChangeDelay[x][y] != 0) /* continue element change */
7366 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7368 if (IS_ANIMATED(graphic))
7369 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7371 if (change->change_function)
7372 change->change_function(x, y);
7374 else /* finish element change */
7376 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7378 page = ChangePage[x][y];
7379 ChangePage[x][y] = -1;
7381 change = &ei->change_page[page];
7385 if (IS_MOVING(x, y) && !change->explode)
7387 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7390 ChangeDelay[x][y] = 1; /* try change after next move step */
7391 ChangePage[x][y] = page; /* remember page to use for change */
7396 if (ChangeElementNow(x, y, element, page))
7398 if (change->post_change_function)
7399 change->post_change_function(x, y);
7404 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7405 int trigger_element,
7412 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7414 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7417 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7419 int element = EL_CUSTOM_START + i;
7421 boolean change_element = FALSE;
7424 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7427 for (j = 0; j < element_info[element].num_change_pages; j++)
7429 struct ElementChangeInfo *change = &element_info[element].change_page[j];
7431 if (change->can_change &&
7432 change->events & CH_EVENT_BIT(trigger_event) &&
7433 change->trigger_side & trigger_side &&
7434 change->trigger_player & trigger_player &&
7435 change->trigger_page & trigger_page_bits &&
7436 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7439 if (!(change->events & CH_EVENT_BIT(trigger_event)))
7440 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7441 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7444 change_element = TRUE;
7447 change->actual_trigger_element = trigger_element;
7448 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7454 if (!change_element)
7457 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7460 if (x == lx && y == ly) /* do not change trigger element itself */
7464 if (Feld[x][y] == element)
7466 ChangeDelay[x][y] = 1;
7467 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7468 ChangeElement(x, y, page);
7476 static boolean CheckElementChangeExt(int x, int y,
7478 int trigger_element,
7484 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7487 if (Feld[x][y] == EL_BLOCKED)
7489 Blocked2Moving(x, y, &x, &y);
7490 element = Feld[x][y];
7494 if (Feld[x][y] != element) /* check if element has already changed */
7497 printf("::: %d ('%s') != %d ('%s') [%d]\n",
7498 Feld[x][y], element_info[Feld[x][y]].token_name,
7499 element, element_info[element].token_name,
7508 if (trigger_page < 0)
7510 boolean change_element = FALSE;
7513 for (i = 0; i < element_info[element].num_change_pages; i++)
7515 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7517 if (change->can_change &&
7518 change->events & CH_EVENT_BIT(trigger_event) &&
7519 change->trigger_side & trigger_side &&
7520 change->trigger_player & trigger_player)
7522 change_element = TRUE;
7525 change->actual_trigger_element = trigger_element;
7526 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7532 if (!change_element)
7537 struct ElementInfo *ei = &element_info[element];
7538 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7540 change->actual_trigger_element = trigger_element;
7541 change->actual_trigger_player = EL_PLAYER_1; /* unused */
7546 /* !!! this check misses pages with same event, but different side !!! */
7548 if (trigger_page < 0)
7549 trigger_page = element_info[element].event_page_nr[trigger_event];
7551 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7555 ChangeDelay[x][y] = 1;
7556 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7557 ChangeElement(x, y, trigger_page);
7562 static void PlayPlayerSound(struct PlayerInfo *player)
7564 int jx = player->jx, jy = player->jy;
7565 int element = player->element_nr;
7566 int last_action = player->last_action_waiting;
7567 int action = player->action_waiting;
7569 if (player->is_waiting)
7571 if (action != last_action)
7572 PlayLevelSoundElementAction(jx, jy, element, action);
7574 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7578 if (action != last_action)
7579 StopSound(element_info[element].sound[last_action]);
7581 if (last_action == ACTION_SLEEPING)
7582 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7586 static void PlayAllPlayersSound()
7590 for (i = 0; i < MAX_PLAYERS; i++)
7591 if (stored_player[i].active)
7592 PlayPlayerSound(&stored_player[i]);
7595 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7597 boolean last_waiting = player->is_waiting;
7598 int move_dir = player->MovDir;
7600 player->last_action_waiting = player->action_waiting;
7604 if (!last_waiting) /* not waiting -> waiting */
7606 player->is_waiting = TRUE;
7608 player->frame_counter_bored =
7610 game.player_boring_delay_fixed +
7611 SimpleRND(game.player_boring_delay_random);
7612 player->frame_counter_sleeping =
7614 game.player_sleeping_delay_fixed +
7615 SimpleRND(game.player_sleeping_delay_random);
7617 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7620 if (game.player_sleeping_delay_fixed +
7621 game.player_sleeping_delay_random > 0 &&
7622 player->anim_delay_counter == 0 &&
7623 player->post_delay_counter == 0 &&
7624 FrameCounter >= player->frame_counter_sleeping)
7625 player->is_sleeping = TRUE;
7626 else if (game.player_boring_delay_fixed +
7627 game.player_boring_delay_random > 0 &&
7628 FrameCounter >= player->frame_counter_bored)
7629 player->is_bored = TRUE;
7631 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7632 player->is_bored ? ACTION_BORING :
7635 if (player->is_sleeping)
7637 if (player->num_special_action_sleeping > 0)
7639 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7641 int last_special_action = player->special_action_sleeping;
7642 int num_special_action = player->num_special_action_sleeping;
7643 int special_action =
7644 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7645 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7646 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7647 last_special_action + 1 : ACTION_SLEEPING);
7648 int special_graphic =
7649 el_act_dir2img(player->element_nr, special_action, move_dir);
7651 player->anim_delay_counter =
7652 graphic_info[special_graphic].anim_delay_fixed +
7653 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7654 player->post_delay_counter =
7655 graphic_info[special_graphic].post_delay_fixed +
7656 SimpleRND(graphic_info[special_graphic].post_delay_random);
7658 player->special_action_sleeping = special_action;
7661 if (player->anim_delay_counter > 0)
7663 player->action_waiting = player->special_action_sleeping;
7664 player->anim_delay_counter--;
7666 else if (player->post_delay_counter > 0)
7668 player->post_delay_counter--;
7672 else if (player->is_bored)
7674 if (player->num_special_action_bored > 0)
7676 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7678 int special_action =
7679 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7680 int special_graphic =
7681 el_act_dir2img(player->element_nr, special_action, move_dir);
7683 player->anim_delay_counter =
7684 graphic_info[special_graphic].anim_delay_fixed +
7685 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7686 player->post_delay_counter =
7687 graphic_info[special_graphic].post_delay_fixed +
7688 SimpleRND(graphic_info[special_graphic].post_delay_random);
7690 player->special_action_bored = special_action;
7693 if (player->anim_delay_counter > 0)
7695 player->action_waiting = player->special_action_bored;
7696 player->anim_delay_counter--;
7698 else if (player->post_delay_counter > 0)
7700 player->post_delay_counter--;
7705 else if (last_waiting) /* waiting -> not waiting */
7707 player->is_waiting = FALSE;
7708 player->is_bored = FALSE;
7709 player->is_sleeping = FALSE;
7711 player->frame_counter_bored = -1;
7712 player->frame_counter_sleeping = -1;
7714 player->anim_delay_counter = 0;
7715 player->post_delay_counter = 0;
7717 player->action_waiting = ACTION_DEFAULT;
7719 player->special_action_bored = ACTION_DEFAULT;
7720 player->special_action_sleeping = ACTION_DEFAULT;
7725 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7728 static byte stored_player_action[MAX_PLAYERS];
7729 static int num_stored_actions = 0;
7731 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7732 int left = player_action & JOY_LEFT;
7733 int right = player_action & JOY_RIGHT;
7734 int up = player_action & JOY_UP;
7735 int down = player_action & JOY_DOWN;
7736 int button1 = player_action & JOY_BUTTON_1;
7737 int button2 = player_action & JOY_BUTTON_2;
7738 int dx = (left ? -1 : right ? 1 : 0);
7739 int dy = (up ? -1 : down ? 1 : 0);
7742 stored_player_action[player->index_nr] = 0;
7743 num_stored_actions++;
7747 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7750 if (!player->active || tape.pausing)
7754 printf("::: [%d %d %d %d] [%d %d]\n",
7755 left, right, up, down, button1, button2);
7761 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7766 if (player->MovPos == 0)
7767 CheckGravityMovement(player);
7770 snapped = SnapField(player, dx, dy);
7774 dropped = DropElement(player);
7776 moved = MovePlayer(player, dx, dy);
7779 if (tape.single_step && tape.recording && !tape.pausing)
7781 if (button1 || (dropped && !moved))
7783 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7784 SnapField(player, 0, 0); /* stop snapping */
7788 SetPlayerWaiting(player, FALSE);
7791 return player_action;
7793 stored_player_action[player->index_nr] = player_action;
7799 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7802 /* no actions for this player (no input at player's configured device) */
7804 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7805 SnapField(player, 0, 0);
7806 CheckGravityMovementWhenNotMoving(player);
7808 if (player->MovPos == 0)
7809 SetPlayerWaiting(player, TRUE);
7811 if (player->MovPos == 0) /* needed for tape.playing */
7812 player->is_moving = FALSE;
7814 player->is_dropping = FALSE;
7820 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7822 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7824 TapeRecordAction(stored_player_action);
7825 num_stored_actions = 0;
7832 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7834 static byte stored_player_action[MAX_PLAYERS];
7835 static int num_stored_actions = 0;
7836 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7837 int left = player_action & JOY_LEFT;
7838 int right = player_action & JOY_RIGHT;
7839 int up = player_action & JOY_UP;
7840 int down = player_action & JOY_DOWN;
7841 int button1 = player_action & JOY_BUTTON_1;
7842 int button2 = player_action & JOY_BUTTON_2;
7843 int dx = (left ? -1 : right ? 1 : 0);
7844 int dy = (up ? -1 : down ? 1 : 0);
7846 stored_player_action[player->index_nr] = 0;
7847 num_stored_actions++;
7849 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7851 if (!player->active || tape.pausing)
7856 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7859 snapped = SnapField(player, dx, dy);
7863 dropped = DropElement(player);
7865 moved = MovePlayer(player, dx, dy);
7868 if (tape.single_step && tape.recording && !tape.pausing)
7870 if (button1 || (dropped && !moved))
7872 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7873 SnapField(player, 0, 0); /* stop snapping */
7877 stored_player_action[player->index_nr] = player_action;
7881 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7883 /* no actions for this player (no input at player's configured device) */
7885 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7886 SnapField(player, 0, 0);
7887 CheckGravityMovementWhenNotMoving(player);
7889 if (player->MovPos == 0)
7890 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7892 if (player->MovPos == 0) /* needed for tape.playing */
7893 player->is_moving = FALSE;
7896 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7898 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7900 TapeRecordAction(stored_player_action);
7901 num_stored_actions = 0;
7908 static unsigned long action_delay = 0;
7909 unsigned long action_delay_value;
7910 int magic_wall_x = 0, magic_wall_y = 0;
7911 int i, x, y, element, graphic;
7912 byte *recorded_player_action;
7913 byte summarized_player_action = 0;
7915 byte tape_action[MAX_PLAYERS];
7918 if (game_status != GAME_MODE_PLAYING)
7921 action_delay_value =
7922 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7924 if (tape.playing && tape.warp_forward && !tape.pausing)
7925 action_delay_value = 0;
7927 /* ---------- main game synchronization point ---------- */
7929 WaitUntilDelayReached(&action_delay, action_delay_value);
7931 if (network_playing && !network_player_action_received)
7935 printf("DEBUG: try to get network player actions in time\n");
7939 #if defined(PLATFORM_UNIX)
7940 /* last chance to get network player actions without main loop delay */
7944 if (game_status != GAME_MODE_PLAYING)
7947 if (!network_player_action_received)
7951 printf("DEBUG: failed to get network player actions in time\n");
7962 printf("::: getting new tape action [%d]\n", FrameCounter);
7965 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7968 if (recorded_player_action == NULL && tape.pausing)
7973 printf("::: %d\n", stored_player[0].action);
7977 if (recorded_player_action != NULL)
7978 for (i = 0; i < MAX_PLAYERS; i++)
7979 stored_player[i].action = recorded_player_action[i];
7982 for (i = 0; i < MAX_PLAYERS; i++)
7984 summarized_player_action |= stored_player[i].action;
7986 if (!network_playing)
7987 stored_player[i].effective_action = stored_player[i].action;
7990 #if defined(PLATFORM_UNIX)
7991 if (network_playing)
7992 SendToServer_MovePlayer(summarized_player_action);
7995 if (!options.network && !setup.team_mode)
7996 local_player->effective_action = summarized_player_action;
7999 if (recorded_player_action != NULL)
8000 for (i = 0; i < MAX_PLAYERS; i++)
8001 stored_player[i].effective_action = recorded_player_action[i];
8005 for (i = 0; i < MAX_PLAYERS; i++)
8007 tape_action[i] = stored_player[i].effective_action;
8009 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8010 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8013 /* only save actions from input devices, but not programmed actions */
8015 TapeRecordAction(tape_action);
8018 for (i = 0; i < MAX_PLAYERS; i++)
8020 int actual_player_action = stored_player[i].effective_action;
8023 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8024 - rnd_equinox_tetrachloride 048
8025 - rnd_equinox_tetrachloride_ii 096
8026 - rnd_emanuel_schmieg 002
8027 - doctor_sloan_ww 001, 020
8029 if (stored_player[i].MovPos == 0)
8030 CheckGravityMovement(&stored_player[i]);
8034 /* overwrite programmed action with tape action */
8035 if (stored_player[i].programmed_action)
8036 actual_player_action = stored_player[i].programmed_action;
8040 if (stored_player[i].programmed_action)
8041 printf("::: %d\n", stored_player[i].programmed_action);
8044 if (recorded_player_action)
8047 if (stored_player[i].programmed_action &&
8048 stored_player[i].programmed_action != recorded_player_action[i])
8049 printf("::: %d: %d <-> %d\n", i,
8050 stored_player[i].programmed_action, recorded_player_action[i]);
8054 actual_player_action = recorded_player_action[i];
8059 /* overwrite tape action with programmed action */
8060 if (stored_player[i].programmed_action)
8061 actual_player_action = stored_player[i].programmed_action;
8066 printf("::: action: %d: %x [%d]\n",
8067 stored_player[i].MovPos, actual_player_action, FrameCounter);
8071 PlayerActions(&stored_player[i], actual_player_action);
8073 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8075 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8076 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8079 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8084 TapeRecordAction(tape_action);
8087 network_player_action_received = FALSE;
8089 ScrollScreen(NULL, SCROLL_GO_ON);
8095 for (i = 0; i < MAX_PLAYERS; i++)
8096 stored_player[i].Frame++;
8100 /* for downwards compatibility, the following code emulates a fixed bug that
8101 occured when pushing elements (causing elements that just made their last
8102 pushing step to already (if possible) make their first falling step in the
8103 same game frame, which is bad); this code is also needed to use the famous
8104 "spring push bug" which is used in older levels and might be wanted to be
8105 used also in newer levels, but in this case the buggy pushing code is only
8106 affecting the "spring" element and no other elements */
8109 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8111 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8114 for (i = 0; i < MAX_PLAYERS; i++)
8116 struct PlayerInfo *player = &stored_player[i];
8121 if (player->active && player->is_pushing && player->is_moving &&
8123 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8124 Feld[x][y] == EL_SPRING))
8126 if (player->active && player->is_pushing && player->is_moving &&
8130 ContinueMoving(x, y);
8132 /* continue moving after pushing (this is actually a bug) */
8133 if (!IS_MOVING(x, y))
8142 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8144 Changed[x][y] = CE_BITMASK_DEFAULT;
8145 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8148 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8150 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8151 printf("GameActions(): This should never happen!\n");
8153 ChangePage[x][y] = -1;
8158 if (WasJustMoving[x][y] > 0)
8159 WasJustMoving[x][y]--;
8160 if (WasJustFalling[x][y] > 0)
8161 WasJustFalling[x][y]--;
8166 /* reset finished pushing action (not done in ContinueMoving() to allow
8167 continous pushing animation for elements with zero push delay) */
8168 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8170 ResetGfxAnimation(x, y);
8171 DrawLevelField(x, y);
8176 if (IS_BLOCKED(x, y))
8180 Blocked2Moving(x, y, &oldx, &oldy);
8181 if (!IS_MOVING(oldx, oldy))
8183 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8184 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8185 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8186 printf("GameActions(): This should never happen!\n");
8192 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8194 element = Feld[x][y];
8196 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8198 graphic = el2img(element);
8204 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8206 element = graphic = 0;
8210 if (graphic_info[graphic].anim_global_sync)
8211 GfxFrame[x][y] = FrameCounter;
8213 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8214 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8215 ResetRandomAnimationValue(x, y);
8217 SetRandomAnimationValue(x, y);
8220 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8223 if (IS_INACTIVE(element))
8225 if (IS_ANIMATED(graphic))
8226 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8232 /* this may take place after moving, so 'element' may have changed */
8234 if (IS_CHANGING(x, y))
8236 if (IS_CHANGING(x, y) &&
8237 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8241 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8242 element_info[element].event_page_nr[CE_DELAY]);
8244 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8247 element = Feld[x][y];
8248 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8252 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8257 element = Feld[x][y];
8258 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8260 if (element == EL_MOLE)
8261 printf("::: %d, %d, %d [%d]\n",
8262 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8266 if (element == EL_YAMYAM)
8267 printf("::: %d, %d, %d\n",
8268 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8272 if (IS_ANIMATED(graphic) &&
8276 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8279 if (element == EL_BUG)
8280 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8284 if (element == EL_MOLE)
8285 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8289 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8290 EdelsteinFunkeln(x, y);
8292 else if ((element == EL_ACID ||
8293 element == EL_EXIT_OPEN ||
8294 element == EL_SP_EXIT_OPEN ||
8295 element == EL_SP_TERMINAL ||
8296 element == EL_SP_TERMINAL_ACTIVE ||
8297 element == EL_EXTRA_TIME ||
8298 element == EL_SHIELD_NORMAL ||
8299 element == EL_SHIELD_DEADLY) &&
8300 IS_ANIMATED(graphic))
8301 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8302 else if (IS_MOVING(x, y))
8303 ContinueMoving(x, y);
8304 else if (IS_ACTIVE_BOMB(element))
8305 CheckDynamite(x, y);
8307 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8308 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8310 else if (element == EL_AMOEBA_GROWING)
8311 AmoebeWaechst(x, y);
8312 else if (element == EL_AMOEBA_SHRINKING)
8313 AmoebaDisappearing(x, y);
8315 #if !USE_NEW_AMOEBA_CODE
8316 else if (IS_AMOEBALIVE(element))
8317 AmoebeAbleger(x, y);
8320 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8322 else if (element == EL_EXIT_CLOSED)
8324 else if (element == EL_SP_EXIT_CLOSED)
8326 else if (element == EL_EXPANDABLE_WALL_GROWING)
8328 else if (element == EL_EXPANDABLE_WALL ||
8329 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8330 element == EL_EXPANDABLE_WALL_VERTICAL ||
8331 element == EL_EXPANDABLE_WALL_ANY)
8333 else if (element == EL_FLAMES)
8334 CheckForDragon(x, y);
8336 else if (IS_AUTO_CHANGING(element))
8337 ChangeElement(x, y);
8339 else if (element == EL_EXPLOSION)
8340 ; /* drawing of correct explosion animation is handled separately */
8341 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8342 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8345 /* this may take place after moving, so 'element' may have changed */
8346 if (IS_AUTO_CHANGING(Feld[x][y]))
8347 ChangeElement(x, y);
8350 if (IS_BELT_ACTIVE(element))
8351 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8353 if (game.magic_wall_active)
8355 int jx = local_player->jx, jy = local_player->jy;
8357 /* play the element sound at the position nearest to the player */
8358 if ((element == EL_MAGIC_WALL_FULL ||
8359 element == EL_MAGIC_WALL_ACTIVE ||
8360 element == EL_MAGIC_WALL_EMPTYING ||
8361 element == EL_BD_MAGIC_WALL_FULL ||
8362 element == EL_BD_MAGIC_WALL_ACTIVE ||
8363 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8364 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8372 #if USE_NEW_AMOEBA_CODE
8373 /* new experimental amoeba growth stuff */
8375 if (!(FrameCounter % 8))
8378 static unsigned long random = 1684108901;
8380 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8383 x = (random >> 10) % lev_fieldx;
8384 y = (random >> 20) % lev_fieldy;
8386 x = RND(lev_fieldx);
8387 y = RND(lev_fieldy);
8389 element = Feld[x][y];
8392 if (!IS_PLAYER(x,y) &&
8393 (element == EL_EMPTY ||
8394 CAN_GROW_INTO(element) ||
8395 element == EL_QUICKSAND_EMPTY ||
8396 element == EL_ACID_SPLASH_LEFT ||
8397 element == EL_ACID_SPLASH_RIGHT))
8399 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8400 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8401 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8402 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8403 Feld[x][y] = EL_AMOEBA_DROP;
8406 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8407 if (!IS_PLAYER(x,y) &&
8408 (element == EL_EMPTY ||
8409 element == EL_SAND ||
8410 element == EL_QUICKSAND_EMPTY ||
8411 element == EL_ACID_SPLASH_LEFT ||
8412 element == EL_ACID_SPLASH_RIGHT))
8414 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8415 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8416 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8417 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8418 Feld[x][y] = EL_AMOEBA_DROP;
8422 random = random * 129 + 1;
8428 if (game.explosions_delayed)
8431 game.explosions_delayed = FALSE;
8433 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8435 element = Feld[x][y];
8437 if (ExplodeField[x][y])
8438 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8439 else if (element == EL_EXPLOSION)
8440 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8442 ExplodeField[x][y] = EX_TYPE_NONE;
8445 game.explosions_delayed = TRUE;
8448 if (game.magic_wall_active)
8450 if (!(game.magic_wall_time_left % 4))
8452 int element = Feld[magic_wall_x][magic_wall_y];
8454 if (element == EL_BD_MAGIC_WALL_FULL ||
8455 element == EL_BD_MAGIC_WALL_ACTIVE ||
8456 element == EL_BD_MAGIC_WALL_EMPTYING)
8457 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8459 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8462 if (game.magic_wall_time_left > 0)
8464 game.magic_wall_time_left--;
8465 if (!game.magic_wall_time_left)
8467 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8469 element = Feld[x][y];
8471 if (element == EL_MAGIC_WALL_ACTIVE ||
8472 element == EL_MAGIC_WALL_FULL)
8474 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8475 DrawLevelField(x, y);
8477 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8478 element == EL_BD_MAGIC_WALL_FULL)
8480 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8481 DrawLevelField(x, y);
8485 game.magic_wall_active = FALSE;
8490 if (game.light_time_left > 0)
8492 game.light_time_left--;
8494 if (game.light_time_left == 0)
8495 RedrawAllLightSwitchesAndInvisibleElements();
8498 if (game.timegate_time_left > 0)
8500 game.timegate_time_left--;
8502 if (game.timegate_time_left == 0)
8503 CloseAllOpenTimegates();
8506 for (i = 0; i < MAX_PLAYERS; i++)
8508 struct PlayerInfo *player = &stored_player[i];
8510 if (SHIELD_ON(player))
8512 if (player->shield_deadly_time_left)
8513 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8514 else if (player->shield_normal_time_left)
8515 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8519 if (TimeFrames >= FRAMES_PER_SECOND)
8524 if (!level.use_step_counter)
8528 for (i = 0; i < MAX_PLAYERS; i++)
8530 struct PlayerInfo *player = &stored_player[i];
8532 if (SHIELD_ON(player))
8534 player->shield_normal_time_left--;
8536 if (player->shield_deadly_time_left > 0)
8537 player->shield_deadly_time_left--;
8545 if (TimeLeft <= 10 && setup.time_limit)
8546 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8548 DrawGameValue_Time(TimeLeft);
8550 if (!TimeLeft && setup.time_limit)
8551 for (i = 0; i < MAX_PLAYERS; i++)
8552 KillHero(&stored_player[i]);
8554 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8555 DrawGameValue_Time(TimePlayed);
8558 if (tape.recording || tape.playing)
8559 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8563 PlayAllPlayersSound();
8565 if (options.debug) /* calculate frames per second */
8567 static unsigned long fps_counter = 0;
8568 static int fps_frames = 0;
8569 unsigned long fps_delay_ms = Counter() - fps_counter;
8573 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8575 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8578 fps_counter = Counter();
8581 redraw_mask |= REDRAW_FPS;
8585 if (stored_player[0].jx != stored_player[0].last_jx ||
8586 stored_player[0].jy != stored_player[0].last_jy)
8587 printf("::: %d, %d, %d, %d, %d\n",
8588 stored_player[0].MovDir,
8589 stored_player[0].MovPos,
8590 stored_player[0].GfxPos,
8591 stored_player[0].Frame,
8592 stored_player[0].StepFrame);
8599 for (i = 0; i < MAX_PLAYERS; i++)
8602 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8604 stored_player[i].Frame += move_frames;
8606 if (stored_player[i].MovPos != 0)
8607 stored_player[i].StepFrame += move_frames;
8609 if (stored_player[i].drop_delay > 0)
8610 stored_player[i].drop_delay--;
8615 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8617 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8619 local_player->show_envelope = 0;
8624 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8626 int min_x = x, min_y = y, max_x = x, max_y = y;
8629 for (i = 0; i < MAX_PLAYERS; i++)
8631 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8633 if (!stored_player[i].active || &stored_player[i] == player)
8636 min_x = MIN(min_x, jx);
8637 min_y = MIN(min_y, jy);
8638 max_x = MAX(max_x, jx);
8639 max_y = MAX(max_y, jy);
8642 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8645 static boolean AllPlayersInVisibleScreen()
8649 for (i = 0; i < MAX_PLAYERS; i++)
8651 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8653 if (!stored_player[i].active)
8656 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8663 void ScrollLevel(int dx, int dy)
8665 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8668 BlitBitmap(drawto_field, drawto_field,
8669 FX + TILEX * (dx == -1) - softscroll_offset,
8670 FY + TILEY * (dy == -1) - softscroll_offset,
8671 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8672 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8673 FX + TILEX * (dx == 1) - softscroll_offset,
8674 FY + TILEY * (dy == 1) - softscroll_offset);
8678 x = (dx == 1 ? BX1 : BX2);
8679 for (y = BY1; y <= BY2; y++)
8680 DrawScreenField(x, y);
8685 y = (dy == 1 ? BY1 : BY2);
8686 for (x = BX1; x <= BX2; x++)
8687 DrawScreenField(x, y);
8690 redraw_mask |= REDRAW_FIELD;
8694 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8696 int nextx = x + dx, nexty = y + dy;
8697 int element = Feld[x][y];
8700 element != EL_SP_PORT_LEFT &&
8701 element != EL_SP_GRAVITY_PORT_LEFT &&
8702 element != EL_SP_PORT_HORIZONTAL &&
8703 element != EL_SP_PORT_ANY) ||
8705 element != EL_SP_PORT_RIGHT &&
8706 element != EL_SP_GRAVITY_PORT_RIGHT &&
8707 element != EL_SP_PORT_HORIZONTAL &&
8708 element != EL_SP_PORT_ANY) ||
8710 element != EL_SP_PORT_UP &&
8711 element != EL_SP_GRAVITY_PORT_UP &&
8712 element != EL_SP_PORT_VERTICAL &&
8713 element != EL_SP_PORT_ANY) ||
8715 element != EL_SP_PORT_DOWN &&
8716 element != EL_SP_GRAVITY_PORT_DOWN &&
8717 element != EL_SP_PORT_VERTICAL &&
8718 element != EL_SP_PORT_ANY) ||
8719 !IN_LEV_FIELD(nextx, nexty) ||
8720 !IS_FREE(nextx, nexty))
8727 static boolean canFallDown(struct PlayerInfo *player)
8729 int jx = player->jx, jy = player->jy;
8731 return (IN_LEV_FIELD(jx, jy + 1) &&
8732 (IS_FREE(jx, jy + 1) ||
8733 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8734 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8735 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8738 static boolean canPassField(int x, int y, int move_dir)
8740 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8741 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8742 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8745 int element = Feld[x][y];
8747 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8748 !CAN_MOVE(element) &&
8749 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8750 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8751 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8754 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8756 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8757 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8758 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8762 int nextx = newx + dx;
8763 int nexty = newy + dy;
8767 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8768 (IS_DIGGABLE(Feld[newx][newy]) ||
8769 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8770 canPassField(newx, newy, move_dir)));
8772 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8773 (IS_DIGGABLE(Feld[newx][newy]) ||
8774 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8775 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
8776 !CAN_MOVE(Feld[newx][newy]) &&
8777 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8778 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8779 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
8783 static void CheckGravityMovement(struct PlayerInfo *player)
8785 if (game.gravity && !player->programmed_action)
8788 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8789 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8791 int move_dir_horizontal = player->action & MV_HORIZONTAL;
8792 int move_dir_vertical = player->action & MV_VERTICAL;
8796 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8798 boolean player_is_snapping = player->action & JOY_BUTTON_1;
8801 int jx = player->jx, jy = player->jy;
8803 boolean player_is_moving_to_valid_field =
8804 (!player_is_snapping &&
8805 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8806 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8810 (player->last_move_dir & MV_HORIZONTAL ?
8811 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8812 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8816 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8817 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8818 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8819 int new_jx = jx + dx, new_jy = jy + dy;
8820 int nextx = new_jx + dx, nexty = new_jy + dy;
8826 boolean player_can_fall_down = canFallDown(player);
8828 boolean player_can_fall_down =
8829 (IN_LEV_FIELD(jx, jy + 1) &&
8830 (IS_FREE(jx, jy + 1) ||
8831 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
8835 boolean player_can_fall_down =
8836 (IN_LEV_FIELD(jx, jy + 1) &&
8837 (IS_FREE(jx, jy + 1)));
8841 boolean player_is_moving_to_valid_field =
8844 !player_is_snapping &&
8848 IN_LEV_FIELD(new_jx, new_jy) &&
8849 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
8850 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8851 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
8852 IN_LEV_FIELD(nextx, nexty) &&
8853 element_info[Feld[nextx][nexty]].access_direction & move_dir))
8855 IN_LEV_FIELD(new_jx, new_jy) &&
8856 (Feld[new_jx][new_jy] == EL_SP_BASE ||
8857 Feld[new_jx][new_jy] == EL_SAND ||
8858 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8859 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
8860 /* !!! extend EL_SAND to anything diggable !!! */
8866 boolean player_is_standing_on_valid_field =
8867 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8868 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
8872 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
8873 player_can_fall_down,
8874 player_is_standing_on_valid_field,
8875 player_is_moving_to_valid_field,
8876 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
8877 player->effective_action,
8878 player->can_fall_into_acid);
8881 if (player_can_fall_down &&
8883 !player_is_standing_on_valid_field &&
8885 !player_is_moving_to_valid_field)
8888 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
8889 jx, jy, FrameCounter);
8892 player->programmed_action = MV_DOWN;
8897 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8900 return CheckGravityMovement(player);
8903 if (game.gravity && !player->programmed_action)
8905 int jx = player->jx, jy = player->jy;
8906 boolean field_under_player_is_free =
8907 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8908 boolean player_is_standing_on_valid_field =
8909 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8910 (IS_WALKABLE(Feld[jx][jy]) &&
8911 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8913 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8914 player->programmed_action = MV_DOWN;
8920 -----------------------------------------------------------------------------
8921 dx, dy: direction (non-diagonal) to try to move the player to
8922 real_dx, real_dy: direction as read from input device (can be diagonal)
8925 boolean MovePlayerOneStep(struct PlayerInfo *player,
8926 int dx, int dy, int real_dx, int real_dy)
8929 static int trigger_sides[4][2] =
8931 /* enter side leave side */
8932 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8933 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8934 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8935 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8937 int move_direction = (dx == -1 ? MV_LEFT :
8938 dx == +1 ? MV_RIGHT :
8940 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8941 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8942 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8944 int jx = player->jx, jy = player->jy;
8945 int new_jx = jx + dx, new_jy = jy + dy;
8949 if (!player->active || (!dx && !dy))
8950 return MF_NO_ACTION;
8952 player->MovDir = (dx < 0 ? MV_LEFT :
8955 dy > 0 ? MV_DOWN : MV_NO_MOVING);
8957 if (!IN_LEV_FIELD(new_jx, new_jy))
8958 return MF_NO_ACTION;
8960 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8961 return MF_NO_ACTION;
8964 element = MovingOrBlocked2Element(new_jx, new_jy);
8966 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8969 if (DONT_RUN_INTO(element))
8971 if (element == EL_ACID && dx == 0 && dy == 1)
8973 SplashAcid(new_jx, new_jy);
8974 Feld[jx][jy] = EL_PLAYER_1;
8975 InitMovingField(jx, jy, MV_DOWN);
8976 Store[jx][jy] = EL_ACID;
8977 ContinueMoving(jx, jy);
8981 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8986 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8987 if (can_move != MF_MOVING)
8990 /* check if DigField() has caused relocation of the player */
8991 if (player->jx != jx || player->jy != jy)
8992 return MF_NO_ACTION;
8994 StorePlayer[jx][jy] = 0;
8995 player->last_jx = jx;
8996 player->last_jy = jy;
8997 player->jx = new_jx;
8998 player->jy = new_jy;
8999 StorePlayer[new_jx][new_jy] = player->element_nr;
9002 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9004 player->step_counter++;
9007 player->drop_delay = 0;
9010 PlayerVisit[jx][jy] = FrameCounter;
9012 ScrollPlayer(player, SCROLL_INIT);
9015 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9017 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9019 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9022 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9024 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9025 CE_OTHER_GETS_ENTERED, enter_side);
9026 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9027 CE_ENTERED_BY_PLAYER, enter_side);
9034 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9036 int jx = player->jx, jy = player->jy;
9037 int old_jx = jx, old_jy = jy;
9038 int moved = MF_NO_ACTION;
9041 if (!player->active)
9046 if (player->MovPos == 0)
9048 player->is_moving = FALSE;
9049 player->is_digging = FALSE;
9050 player->is_collecting = FALSE;
9051 player->is_snapping = FALSE;
9052 player->is_pushing = FALSE;
9058 if (!player->active || (!dx && !dy))
9063 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9069 if (!FrameReached(&player->move_delay, player->move_delay_value))
9072 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9073 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9079 /* store if player is automatically moved to next field */
9080 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9082 /* remove the last programmed player action */
9083 player->programmed_action = 0;
9087 /* should only happen if pre-1.2 tape recordings are played */
9088 /* this is only for backward compatibility */
9090 int original_move_delay_value = player->move_delay_value;
9093 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9097 /* scroll remaining steps with finest movement resolution */
9098 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9100 while (player->MovPos)
9102 ScrollPlayer(player, SCROLL_GO_ON);
9103 ScrollScreen(NULL, SCROLL_GO_ON);
9109 player->move_delay_value = original_move_delay_value;
9112 if (player->last_move_dir & MV_HORIZONTAL)
9114 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9115 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9119 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9120 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9126 if (moved & MF_MOVING && !ScreenMovPos &&
9127 (player == local_player || !options.network))
9129 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9130 int offset = (setup.scroll_delay ? 3 : 0);
9132 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9134 /* actual player has left the screen -- scroll in that direction */
9135 if (jx != old_jx) /* player has moved horizontally */
9136 scroll_x += (jx - old_jx);
9137 else /* player has moved vertically */
9138 scroll_y += (jy - old_jy);
9142 if (jx != old_jx) /* player has moved horizontally */
9144 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9145 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9146 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9148 /* don't scroll over playfield boundaries */
9149 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9150 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9152 /* don't scroll more than one field at a time */
9153 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9155 /* don't scroll against the player's moving direction */
9156 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9157 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9158 scroll_x = old_scroll_x;
9160 else /* player has moved vertically */
9162 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9163 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9164 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9166 /* don't scroll over playfield boundaries */
9167 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9168 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9170 /* don't scroll more than one field at a time */
9171 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9173 /* don't scroll against the player's moving direction */
9174 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9175 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9176 scroll_y = old_scroll_y;
9180 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9182 if (!options.network && !AllPlayersInVisibleScreen())
9184 scroll_x = old_scroll_x;
9185 scroll_y = old_scroll_y;
9189 ScrollScreen(player, SCROLL_INIT);
9190 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9197 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9199 if (!(moved & MF_MOVING) && !player->is_pushing)
9204 player->StepFrame = 0;
9206 if (moved & MF_MOVING)
9208 if (old_jx != jx && old_jy == jy)
9209 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9210 else if (old_jx == jx && old_jy != jy)
9211 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9213 DrawLevelField(jx, jy); /* for "crumbled sand" */
9215 player->last_move_dir = player->MovDir;
9216 player->is_moving = TRUE;
9218 player->is_snapping = FALSE;
9222 player->is_switching = FALSE;
9225 player->is_dropping = FALSE;
9229 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9232 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9235 static int trigger_sides[4][2] =
9237 /* enter side leave side */
9238 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9239 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9240 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9241 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9243 int move_direction = player->MovDir;
9244 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9245 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9248 /* !!! TEST ONLY !!! */
9249 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9250 CheckElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9252 player->index_bit, leave_side);
9254 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9256 player->index_bit, leave_side);
9258 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9259 CheckElementChangeByPlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9260 player->index_bit, enter_side);
9262 CheckTriggeredElementChangeByPlayer(jx, jy, Feld[jx][jy],
9263 CE_OTHER_GETS_ENTERED,
9264 player->index_bit, enter_side);
9274 CheckGravityMovementWhenNotMoving(player);
9277 player->last_move_dir = MV_NO_MOVING;
9279 player->is_moving = FALSE;
9282 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9284 TestIfHeroTouchesBadThing(jx, jy);
9285 TestIfPlayerTouchesCustomElement(jx, jy);
9288 if (!player->active)
9294 void ScrollPlayer(struct PlayerInfo *player, int mode)
9296 int jx = player->jx, jy = player->jy;
9297 int last_jx = player->last_jx, last_jy = player->last_jy;
9298 int move_stepsize = TILEX / player->move_delay_value;
9300 if (!player->active || !player->MovPos)
9303 if (mode == SCROLL_INIT)
9305 player->actual_frame_counter = FrameCounter;
9306 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9308 if (Feld[last_jx][last_jy] == EL_EMPTY)
9309 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9317 else if (!FrameReached(&player->actual_frame_counter, 1))
9320 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9321 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9323 if (!player->block_last_field &&
9324 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9325 Feld[last_jx][last_jy] = EL_EMPTY;
9327 /* before DrawPlayer() to draw correct player graphic for this case */
9328 if (player->MovPos == 0)
9329 CheckGravityMovement(player);
9332 DrawPlayer(player); /* needed here only to cleanup last field */
9335 if (player->MovPos == 0) /* player reached destination field */
9338 if (player->move_delay_reset_counter > 0)
9340 player->move_delay_reset_counter--;
9342 if (player->move_delay_reset_counter == 0)
9344 /* continue with normal speed after quickly moving through gate */
9345 HALVE_PLAYER_SPEED(player);
9347 /* be able to make the next move without delay */
9348 player->move_delay = 0;
9352 if (IS_PASSABLE(Feld[last_jx][last_jy]))
9354 /* continue with normal speed after quickly moving through gate */
9355 HALVE_PLAYER_SPEED(player);
9357 /* be able to make the next move without delay */
9358 player->move_delay = 0;
9362 if (player->block_last_field &&
9363 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9364 Feld[last_jx][last_jy] = EL_EMPTY;
9366 player->last_jx = jx;
9367 player->last_jy = jy;
9369 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9370 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9371 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9373 DrawPlayer(player); /* needed here only to cleanup last field */
9376 if (local_player->friends_still_needed == 0 ||
9377 IS_SP_ELEMENT(Feld[jx][jy]))
9378 player->LevelSolved = player->GameOver = TRUE;
9382 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9383 /* this breaks one level: "machine", level 000 */
9385 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9388 static int trigger_sides[4][2] =
9390 /* enter side leave side */
9391 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9392 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9393 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9394 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9396 int move_direction = player->MovDir;
9397 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9398 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9399 int old_jx = last_jx;
9400 int old_jy = last_jy;
9403 /* !!! TEST ONLY !!! */
9404 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9405 CheckElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9407 player->index_bit, leave_side);
9409 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9411 player->index_bit, leave_side);
9413 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9414 CheckElementChangeByPlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9415 player->index_bit, enter_side);
9417 CheckTriggeredElementChangeByPlayer(jx, jy, Feld[jx][jy],
9418 CE_OTHER_GETS_ENTERED,
9419 player->index_bit, enter_side);
9425 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9427 TestIfHeroTouchesBadThing(jx, jy);
9428 TestIfPlayerTouchesCustomElement(jx, jy);
9430 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9433 if (!player->active)
9437 if (level.use_step_counter)
9443 for (i = 0; i < MAX_PLAYERS; i++)
9445 struct PlayerInfo *player = &stored_player[i];
9447 if (SHIELD_ON(player))
9449 player->shield_normal_time_left--;
9451 if (player->shield_deadly_time_left > 0)
9452 player->shield_deadly_time_left--;
9460 if (TimeLeft <= 10 && setup.time_limit)
9461 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9463 DrawGameValue_Time(TimeLeft);
9465 if (!TimeLeft && setup.time_limit)
9466 for (i = 0; i < MAX_PLAYERS; i++)
9467 KillHero(&stored_player[i]);
9469 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9470 DrawGameValue_Time(TimePlayed);
9473 if (tape.single_step && tape.recording && !tape.pausing &&
9474 !player->programmed_action)
9475 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9479 void ScrollScreen(struct PlayerInfo *player, int mode)
9481 static unsigned long screen_frame_counter = 0;
9483 if (mode == SCROLL_INIT)
9485 /* set scrolling step size according to actual player's moving speed */
9486 ScrollStepSize = TILEX / player->move_delay_value;
9488 screen_frame_counter = FrameCounter;
9489 ScreenMovDir = player->MovDir;
9490 ScreenMovPos = player->MovPos;
9491 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9494 else if (!FrameReached(&screen_frame_counter, 1))
9499 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9500 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9501 redraw_mask |= REDRAW_FIELD;
9504 ScreenMovDir = MV_NO_MOVING;
9507 void TestIfPlayerTouchesCustomElement(int x, int y)
9509 static int xy[4][2] =
9516 static int trigger_sides[4][2] =
9518 /* center side border side */
9519 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9520 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9521 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9522 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9524 static int touch_dir[4] =
9531 int center_element = Feld[x][y]; /* should always be non-moving! */
9534 for (i = 0; i < NUM_DIRECTIONS; i++)
9536 int xx = x + xy[i][0];
9537 int yy = y + xy[i][1];
9538 int center_side = trigger_sides[i][0];
9539 int border_side = trigger_sides[i][1];
9542 if (!IN_LEV_FIELD(xx, yy))
9545 if (IS_PLAYER(x, y))
9547 struct PlayerInfo *player = PLAYERINFO(x, y);
9549 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9550 border_element = Feld[xx][yy]; /* may be moving! */
9551 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9552 border_element = Feld[xx][yy];
9553 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9554 border_element = MovingOrBlocked2Element(xx, yy);
9556 continue; /* center and border element do not touch */
9559 /* !!! TEST ONLY !!! */
9560 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9561 player->index_bit, border_side);
9562 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9563 CE_OTHER_GETS_TOUCHED,
9564 player->index_bit, border_side);
9566 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9567 CE_OTHER_GETS_TOUCHED,
9568 player->index_bit, border_side);
9569 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9570 player->index_bit, border_side);
9573 else if (IS_PLAYER(xx, yy))
9575 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9577 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9579 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9580 continue; /* center and border element do not touch */
9584 /* !!! TEST ONLY !!! */
9585 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9586 player->index_bit, center_side);
9587 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9588 CE_OTHER_GETS_TOUCHED,
9589 player->index_bit, center_side);
9591 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9592 CE_OTHER_GETS_TOUCHED,
9593 player->index_bit, center_side);
9594 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9595 player->index_bit, center_side);
9603 void TestIfElementTouchesCustomElement(int x, int y)
9605 static int xy[4][2] =
9612 static int trigger_sides[4][2] =
9614 /* center side border side */
9615 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9616 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9617 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9618 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9620 static int touch_dir[4] =
9627 boolean change_center_element = FALSE;
9628 int center_element_change_page = 0;
9629 int center_element = Feld[x][y]; /* should always be non-moving! */
9630 int border_trigger_element;
9633 for (i = 0; i < NUM_DIRECTIONS; i++)
9635 int xx = x + xy[i][0];
9636 int yy = y + xy[i][1];
9637 int center_side = trigger_sides[i][0];
9638 int border_side = trigger_sides[i][1];
9641 if (!IN_LEV_FIELD(xx, yy))
9644 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9645 border_element = Feld[xx][yy]; /* may be moving! */
9646 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9647 border_element = Feld[xx][yy];
9648 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9649 border_element = MovingOrBlocked2Element(xx, yy);
9651 continue; /* center and border element do not touch */
9653 /* check for change of center element (but change it only once) */
9654 if (IS_CUSTOM_ELEMENT(center_element) &&
9655 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9656 !change_center_element)
9658 for (j = 0; j < element_info[center_element].num_change_pages; j++)
9660 struct ElementChangeInfo *change =
9661 &element_info[center_element].change_page[j];
9663 if (change->can_change &&
9664 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9665 change->trigger_side & border_side &&
9667 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9669 change->trigger_element == border_element
9673 change_center_element = TRUE;
9674 center_element_change_page = j;
9675 border_trigger_element = border_element;
9682 /* check for change of border element */
9683 if (IS_CUSTOM_ELEMENT(border_element) &&
9684 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9686 for (j = 0; j < element_info[border_element].num_change_pages; j++)
9688 struct ElementChangeInfo *change =
9689 &element_info[border_element].change_page[j];
9691 if (change->can_change &&
9692 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9693 change->trigger_side & center_side &&
9695 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9697 change->trigger_element == center_element
9702 printf("::: border_element %d, %d\n", x, y);
9705 CheckElementChangeByPage(xx, yy, border_element, center_element,
9706 CE_OTHER_IS_TOUCHING, j);
9713 if (change_center_element)
9716 printf("::: center_element %d, %d\n", x, y);
9719 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
9720 CE_OTHER_IS_TOUCHING, center_element_change_page);
9724 void TestIfElementHitsCustomElement(int x, int y, int direction)
9726 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9727 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9728 int hitx = x + dx, hity = y + dy;
9729 int hitting_element = Feld[x][y];
9730 int touched_element;
9732 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9733 !IS_FREE(hitx, hity) &&
9734 (!IS_MOVING(hitx, hity) ||
9735 MovDir[hitx][hity] != direction ||
9736 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9739 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9743 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9747 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9748 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9750 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9751 CE_HITTING_SOMETHING, direction);
9753 if (IN_LEV_FIELD(hitx, hity))
9755 int opposite_direction = MV_DIR_OPPOSITE(direction);
9756 int hitting_side = direction;
9757 int touched_side = opposite_direction;
9759 int touched_element = MovingOrBlocked2Element(hitx, hity);
9762 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9763 MovDir[hitx][hity] != direction ||
9764 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9773 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9774 CE_HIT_BY_SOMETHING, opposite_direction);
9776 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9777 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
9779 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9781 struct ElementChangeInfo *change =
9782 &element_info[hitting_element].change_page[i];
9784 if (change->can_change &&
9785 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
9786 change->trigger_side & touched_side &&
9789 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9791 change->trigger_element == touched_element
9795 CheckElementChangeByPage(x, y, hitting_element, touched_element,
9796 CE_OTHER_IS_HITTING, i);
9802 if (IS_CUSTOM_ELEMENT(touched_element) &&
9803 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
9805 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9807 struct ElementChangeInfo *change =
9808 &element_info[touched_element].change_page[i];
9810 if (change->can_change &&
9811 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
9812 change->trigger_side & hitting_side &&
9814 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9816 change->trigger_element == hitting_element
9820 CheckElementChangeByPage(hitx, hity, touched_element,
9821 hitting_element, CE_OTHER_GETS_HIT, i);
9831 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9833 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9834 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9835 int hitx = x + dx, hity = y + dy;
9836 int hitting_element = Feld[x][y];
9837 int touched_element;
9839 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9840 !IS_FREE(hitx, hity) &&
9841 (!IS_MOVING(hitx, hity) ||
9842 MovDir[hitx][hity] != direction ||
9843 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9846 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9850 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9854 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9855 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9857 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9858 EP_CAN_SMASH_EVERYTHING, direction);
9860 if (IN_LEV_FIELD(hitx, hity))
9862 int opposite_direction = MV_DIR_OPPOSITE(direction);
9863 int hitting_side = direction;
9864 int touched_side = opposite_direction;
9866 int touched_element = MovingOrBlocked2Element(hitx, hity);
9869 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9870 MovDir[hitx][hity] != direction ||
9871 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9880 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9881 CE_SMASHED_BY_SOMETHING, opposite_direction);
9883 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9884 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
9886 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9888 struct ElementChangeInfo *change =
9889 &element_info[hitting_element].change_page[i];
9891 if (change->can_change &&
9892 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
9893 change->trigger_side & touched_side &&
9896 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9898 change->trigger_element == touched_element
9902 CheckElementChangeByPage(x, y, hitting_element, touched_element,
9903 CE_OTHER_IS_SMASHING, i);
9909 if (IS_CUSTOM_ELEMENT(touched_element) &&
9910 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
9912 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9914 struct ElementChangeInfo *change =
9915 &element_info[touched_element].change_page[i];
9917 if (change->can_change &&
9918 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
9919 change->trigger_side & hitting_side &&
9921 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9923 change->trigger_element == hitting_element
9927 CheckElementChangeByPage(hitx, hity, touched_element,
9928 hitting_element, CE_OTHER_GETS_SMASHED,i);
9938 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9940 int i, kill_x = -1, kill_y = -1;
9941 int bad_element = -1;
9942 static int test_xy[4][2] =
9949 static int test_dir[4] =
9957 for (i = 0; i < NUM_DIRECTIONS; i++)
9959 int test_x, test_y, test_move_dir, test_element;
9961 test_x = good_x + test_xy[i][0];
9962 test_y = good_y + test_xy[i][1];
9964 if (!IN_LEV_FIELD(test_x, test_y))
9968 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9971 test_element = Feld[test_x][test_y];
9973 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9976 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9977 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9979 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9980 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9984 bad_element = test_element;
9990 if (kill_x != -1 || kill_y != -1)
9992 if (IS_PLAYER(good_x, good_y))
9994 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9997 if (player->shield_deadly_time_left > 0 &&
9998 !IS_INDESTRUCTIBLE(bad_element))
9999 Bang(kill_x, kill_y);
10000 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10003 if (player->shield_deadly_time_left > 0)
10004 Bang(kill_x, kill_y);
10005 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10010 Bang(good_x, good_y);
10014 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10016 int i, kill_x = -1, kill_y = -1;
10017 int bad_element = Feld[bad_x][bad_y];
10018 static int test_xy[4][2] =
10025 static int touch_dir[4] =
10027 MV_LEFT | MV_RIGHT,
10032 static int test_dir[4] =
10040 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10043 for (i = 0; i < NUM_DIRECTIONS; i++)
10045 int test_x, test_y, test_move_dir, test_element;
10047 test_x = bad_x + test_xy[i][0];
10048 test_y = bad_y + test_xy[i][1];
10049 if (!IN_LEV_FIELD(test_x, test_y))
10053 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10055 test_element = Feld[test_x][test_y];
10057 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10058 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10060 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10061 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10063 /* good thing is player or penguin that does not move away */
10064 if (IS_PLAYER(test_x, test_y))
10066 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10068 if (bad_element == EL_ROBOT && player->is_moving)
10069 continue; /* robot does not kill player if he is moving */
10071 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10073 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10074 continue; /* center and border element do not touch */
10081 else if (test_element == EL_PENGUIN)
10090 if (kill_x != -1 || kill_y != -1)
10092 if (IS_PLAYER(kill_x, kill_y))
10094 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10097 if (player->shield_deadly_time_left > 0 &&
10098 !IS_INDESTRUCTIBLE(bad_element))
10099 Bang(bad_x, bad_y);
10100 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10103 if (player->shield_deadly_time_left > 0)
10104 Bang(bad_x, bad_y);
10105 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10110 Bang(kill_x, kill_y);
10114 void TestIfHeroTouchesBadThing(int x, int y)
10116 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10119 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10121 TestIfGoodThingHitsBadThing(x, y, move_dir);
10124 void TestIfBadThingTouchesHero(int x, int y)
10126 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10129 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10131 TestIfBadThingHitsGoodThing(x, y, move_dir);
10134 void TestIfFriendTouchesBadThing(int x, int y)
10136 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10139 void TestIfBadThingTouchesFriend(int x, int y)
10141 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10144 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10146 int i, kill_x = bad_x, kill_y = bad_y;
10147 static int xy[4][2] =
10155 for (i = 0; i < NUM_DIRECTIONS; i++)
10159 x = bad_x + xy[i][0];
10160 y = bad_y + xy[i][1];
10161 if (!IN_LEV_FIELD(x, y))
10164 element = Feld[x][y];
10165 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10166 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10174 if (kill_x != bad_x || kill_y != bad_y)
10175 Bang(bad_x, bad_y);
10178 void KillHero(struct PlayerInfo *player)
10180 int jx = player->jx, jy = player->jy;
10182 if (!player->active)
10185 /* remove accessible field at the player's position */
10186 Feld[jx][jy] = EL_EMPTY;
10188 /* deactivate shield (else Bang()/Explode() would not work right) */
10189 player->shield_normal_time_left = 0;
10190 player->shield_deadly_time_left = 0;
10196 static void KillHeroUnlessEnemyProtected(int x, int y)
10198 if (!PLAYER_ENEMY_PROTECTED(x, y))
10199 KillHero(PLAYERINFO(x, y));
10202 static void KillHeroUnlessExplosionProtected(int x, int y)
10204 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10205 KillHero(PLAYERINFO(x, y));
10208 void BuryHero(struct PlayerInfo *player)
10210 int jx = player->jx, jy = player->jy;
10212 if (!player->active)
10216 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10218 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10220 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10222 player->GameOver = TRUE;
10223 RemoveHero(player);
10226 void RemoveHero(struct PlayerInfo *player)
10228 int jx = player->jx, jy = player->jy;
10229 int i, found = FALSE;
10231 player->present = FALSE;
10232 player->active = FALSE;
10234 if (!ExplodeField[jx][jy])
10235 StorePlayer[jx][jy] = 0;
10237 for (i = 0; i < MAX_PLAYERS; i++)
10238 if (stored_player[i].active)
10242 AllPlayersGone = TRUE;
10249 =============================================================================
10250 checkDiagonalPushing()
10251 -----------------------------------------------------------------------------
10252 check if diagonal input device direction results in pushing of object
10253 (by checking if the alternative direction is walkable, diggable, ...)
10254 =============================================================================
10257 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10258 int x, int y, int real_dx, int real_dy)
10260 int jx, jy, dx, dy, xx, yy;
10262 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10265 /* diagonal direction: check alternative direction */
10270 xx = jx + (dx == 0 ? real_dx : 0);
10271 yy = jy + (dy == 0 ? real_dy : 0);
10273 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10277 =============================================================================
10279 -----------------------------------------------------------------------------
10280 x, y: field next to player (non-diagonal) to try to dig to
10281 real_dx, real_dy: direction as read from input device (can be diagonal)
10282 =============================================================================
10285 int DigField(struct PlayerInfo *player,
10286 int oldx, int oldy, int x, int y,
10287 int real_dx, int real_dy, int mode)
10289 static int trigger_sides[4] =
10291 CH_SIDE_RIGHT, /* moving left */
10292 CH_SIDE_LEFT, /* moving right */
10293 CH_SIDE_BOTTOM, /* moving up */
10294 CH_SIDE_TOP, /* moving down */
10297 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10299 int jx = oldx, jy = oldy;
10300 int dx = x - jx, dy = y - jy;
10301 int nextx = x + dx, nexty = y + dy;
10302 int move_direction = (dx == -1 ? MV_LEFT :
10303 dx == +1 ? MV_RIGHT :
10305 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10306 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10307 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10308 int old_element = Feld[jx][jy];
10311 if (player->MovPos == 0)
10313 player->is_digging = FALSE;
10314 player->is_collecting = FALSE;
10317 if (player->MovPos == 0) /* last pushing move finished */
10318 player->is_pushing = FALSE;
10320 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10322 player->is_switching = FALSE;
10323 player->push_delay = 0;
10325 return MF_NO_ACTION;
10328 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10329 return MF_NO_ACTION;
10334 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10336 if (IS_TUBE(Feld[jx][jy]) ||
10337 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10341 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10342 int tube_leave_directions[][2] =
10344 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10345 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10346 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10347 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
10348 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
10349 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
10350 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
10351 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
10352 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
10353 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
10354 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
10355 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10358 while (tube_leave_directions[i][0] != tube_element)
10361 if (tube_leave_directions[i][0] == -1) /* should not happen */
10365 if (!(tube_leave_directions[i][1] & move_direction))
10366 return MF_NO_ACTION; /* tube has no opening in this direction */
10371 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10372 old_element = Back[jx][jy];
10376 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10377 return MF_NO_ACTION; /* field has no opening in this direction */
10379 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10380 return MF_NO_ACTION; /* field has no opening in this direction */
10382 element = Feld[x][y];
10384 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10385 game.engine_version >= VERSION_IDENT(2,2,0,0))
10386 return MF_NO_ACTION;
10391 case EL_SP_PORT_LEFT:
10392 case EL_SP_PORT_RIGHT:
10393 case EL_SP_PORT_UP:
10394 case EL_SP_PORT_DOWN:
10395 case EL_SP_PORT_HORIZONTAL:
10396 case EL_SP_PORT_VERTICAL:
10397 case EL_SP_PORT_ANY:
10398 case EL_SP_GRAVITY_PORT_LEFT:
10399 case EL_SP_GRAVITY_PORT_RIGHT:
10400 case EL_SP_GRAVITY_PORT_UP:
10401 case EL_SP_GRAVITY_PORT_DOWN:
10403 if (!canEnterSupaplexPort(x, y, dx, dy))
10404 return MF_NO_ACTION;
10407 element != EL_SP_PORT_LEFT &&
10408 element != EL_SP_GRAVITY_PORT_LEFT &&
10409 element != EL_SP_PORT_HORIZONTAL &&
10410 element != EL_SP_PORT_ANY) ||
10412 element != EL_SP_PORT_RIGHT &&
10413 element != EL_SP_GRAVITY_PORT_RIGHT &&
10414 element != EL_SP_PORT_HORIZONTAL &&
10415 element != EL_SP_PORT_ANY) ||
10417 element != EL_SP_PORT_UP &&
10418 element != EL_SP_GRAVITY_PORT_UP &&
10419 element != EL_SP_PORT_VERTICAL &&
10420 element != EL_SP_PORT_ANY) ||
10422 element != EL_SP_PORT_DOWN &&
10423 element != EL_SP_GRAVITY_PORT_DOWN &&
10424 element != EL_SP_PORT_VERTICAL &&
10425 element != EL_SP_PORT_ANY) ||
10426 !IN_LEV_FIELD(nextx, nexty) ||
10427 !IS_FREE(nextx, nexty))
10428 return MF_NO_ACTION;
10431 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10432 element == EL_SP_GRAVITY_PORT_RIGHT ||
10433 element == EL_SP_GRAVITY_PORT_UP ||
10434 element == EL_SP_GRAVITY_PORT_DOWN)
10435 game.gravity = !game.gravity;
10437 /* automatically move to the next field with double speed */
10438 player->programmed_action = move_direction;
10440 if (player->move_delay_reset_counter == 0)
10442 player->move_delay_reset_counter = 2; /* two double speed steps */
10444 DOUBLE_PLAYER_SPEED(player);
10447 player->move_delay_reset_counter = 2;
10449 DOUBLE_PLAYER_SPEED(player);
10453 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10456 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10462 case EL_TUBE_VERTICAL:
10463 case EL_TUBE_HORIZONTAL:
10464 case EL_TUBE_VERTICAL_LEFT:
10465 case EL_TUBE_VERTICAL_RIGHT:
10466 case EL_TUBE_HORIZONTAL_UP:
10467 case EL_TUBE_HORIZONTAL_DOWN:
10468 case EL_TUBE_LEFT_UP:
10469 case EL_TUBE_LEFT_DOWN:
10470 case EL_TUBE_RIGHT_UP:
10471 case EL_TUBE_RIGHT_DOWN:
10474 int tube_enter_directions[][2] =
10476 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10477 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10478 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10479 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
10480 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
10481 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
10482 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
10483 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
10484 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
10485 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
10486 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
10487 { -1, MV_NO_MOVING }
10490 while (tube_enter_directions[i][0] != element)
10493 if (tube_enter_directions[i][0] == -1) /* should not happen */
10497 if (!(tube_enter_directions[i][1] & move_direction))
10498 return MF_NO_ACTION; /* tube has no opening in this direction */
10500 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10507 if (IS_WALKABLE(element))
10509 int sound_action = ACTION_WALKING;
10511 if (!ACCESS_FROM(element, opposite_direction))
10512 return MF_NO_ACTION; /* field not accessible from this direction */
10515 if (element == EL_EMPTY_SPACE &&
10516 game.gravity && !player->is_auto_moving &&
10517 canFallDown(player) && move_direction != MV_DOWN)
10518 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10521 if (IS_GATE(element))
10523 if (!player->key[element - EL_GATE_1])
10524 return MF_NO_ACTION;
10526 else if (IS_GATE_GRAY(element))
10528 if (!player->key[element - EL_GATE_1_GRAY])
10529 return MF_NO_ACTION;
10531 else if (element == EL_EXIT_OPEN ||
10532 element == EL_SP_EXIT_OPEN ||
10533 element == EL_SP_EXIT_OPENING)
10535 sound_action = ACTION_PASSING; /* player is passing exit */
10537 else if (element == EL_EMPTY)
10539 sound_action = ACTION_MOVING; /* nothing to walk on */
10542 /* play sound from background or player, whatever is available */
10543 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
10544 PlayLevelSoundElementAction(x, y, element, sound_action);
10546 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10550 else if (IS_PASSABLE(element))
10553 if (!canPassField(x, y, move_direction))
10554 return MF_NO_ACTION;
10558 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
10559 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
10560 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
10561 return MF_NO_ACTION;
10563 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10564 return MF_NO_ACTION;
10568 if (!ACCESS_FROM(element, opposite_direction))
10569 return MF_NO_ACTION; /* field not accessible from this direction */
10571 if (IS_CUSTOM_ELEMENT(element) &&
10572 !ACCESS_FROM(element, opposite_direction))
10573 return MF_NO_ACTION; /* field not accessible from this direction */
10577 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10578 return MF_NO_ACTION;
10583 if (IS_EM_GATE(element))
10585 if (!player->key[element - EL_EM_GATE_1])
10586 return MF_NO_ACTION;
10588 else if (IS_EM_GATE_GRAY(element))
10590 if (!player->key[element - EL_EM_GATE_1_GRAY])
10591 return MF_NO_ACTION;
10593 else if (IS_SP_PORT(element))
10595 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10596 element == EL_SP_GRAVITY_PORT_RIGHT ||
10597 element == EL_SP_GRAVITY_PORT_UP ||
10598 element == EL_SP_GRAVITY_PORT_DOWN)
10599 game.gravity = !game.gravity;
10602 /* automatically move to the next field with double speed */
10603 player->programmed_action = move_direction;
10605 if (player->move_delay_reset_counter == 0)
10607 player->move_delay_reset_counter = 2; /* two double speed steps */
10609 DOUBLE_PLAYER_SPEED(player);
10612 player->move_delay_reset_counter = 2;
10614 DOUBLE_PLAYER_SPEED(player);
10617 PlayLevelSoundAction(x, y, ACTION_PASSING);
10621 else if (IS_DIGGABLE(element))
10625 if (mode != DF_SNAP)
10628 GfxElement[x][y] = GFX_ELEMENT(element);
10631 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
10633 player->is_digging = TRUE;
10636 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10638 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
10639 player->index_bit, dig_side);
10642 if (mode == DF_SNAP)
10643 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10648 else if (IS_COLLECTIBLE(element))
10652 if (mode != DF_SNAP)
10654 GfxElement[x][y] = element;
10655 player->is_collecting = TRUE;
10658 if (element == EL_SPEED_PILL)
10659 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10660 else if (element == EL_EXTRA_TIME && level.time > 0)
10663 DrawGameValue_Time(TimeLeft);
10665 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10667 player->shield_normal_time_left += 10;
10668 if (element == EL_SHIELD_DEADLY)
10669 player->shield_deadly_time_left += 10;
10671 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10673 if (player->inventory_size < MAX_INVENTORY_SIZE)
10674 player->inventory_element[player->inventory_size++] = element;
10676 DrawGameValue_Dynamite(local_player->inventory_size);
10678 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10680 player->dynabomb_count++;
10681 player->dynabombs_left++;
10683 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10685 player->dynabomb_size++;
10687 else if (element == EL_DYNABOMB_INCREASE_POWER)
10689 player->dynabomb_xl = TRUE;
10691 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
10692 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
10694 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
10695 element - EL_KEY_1 : element - EL_EM_KEY_1);
10697 player->key[key_nr] = TRUE;
10699 DrawGameValue_Keys(player);
10701 redraw_mask |= REDRAW_DOOR_1;
10703 else if (IS_ENVELOPE(element))
10706 player->show_envelope = element;
10708 ShowEnvelope(element - EL_ENVELOPE_1);
10711 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
10715 if (element_info[element].collect_count == 0)
10716 player->inventory_infinite_element = element;
10718 for (i = 0; i < element_info[element].collect_count; i++)
10719 if (player->inventory_size < MAX_INVENTORY_SIZE)
10720 player->inventory_element[player->inventory_size++] = element;
10722 DrawGameValue_Dynamite(local_player->inventory_size);
10724 else if (element_info[element].collect_count > 0)
10726 local_player->gems_still_needed -=
10727 element_info[element].collect_count;
10728 if (local_player->gems_still_needed < 0)
10729 local_player->gems_still_needed = 0;
10731 DrawGameValue_Emeralds(local_player->gems_still_needed);
10734 RaiseScoreElement(element);
10735 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10737 CheckTriggeredElementChangeByPlayer(x, y, element,
10738 CE_OTHER_GETS_COLLECTED,
10739 player->index_bit, dig_side);
10742 if (mode == DF_SNAP)
10743 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10748 else if (IS_PUSHABLE(element))
10750 if (mode == DF_SNAP && element != EL_BD_ROCK)
10751 return MF_NO_ACTION;
10753 if (CAN_FALL(element) && dy)
10754 return MF_NO_ACTION;
10756 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10757 !(element == EL_SPRING && level.use_spring_bug))
10758 return MF_NO_ACTION;
10761 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10762 ((move_direction & MV_VERTICAL &&
10763 ((element_info[element].move_pattern & MV_LEFT &&
10764 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10765 (element_info[element].move_pattern & MV_RIGHT &&
10766 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10767 (move_direction & MV_HORIZONTAL &&
10768 ((element_info[element].move_pattern & MV_UP &&
10769 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10770 (element_info[element].move_pattern & MV_DOWN &&
10771 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10772 return MF_NO_ACTION;
10776 /* do not push elements already moving away faster than player */
10777 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10778 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10779 return MF_NO_ACTION;
10781 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
10782 return MF_NO_ACTION;
10786 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10788 if (player->push_delay_value == -1)
10789 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10791 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10793 if (!player->is_pushing)
10794 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10798 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
10799 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
10800 !player_is_pushing))
10801 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10804 if (!player->is_pushing &&
10805 game.engine_version >= VERSION_IDENT(2,2,0,7))
10806 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10810 printf("::: push delay: %ld [%d, %d] [%d]\n",
10811 player->push_delay_value, FrameCounter, game.engine_version,
10812 player->is_pushing);
10815 player->is_pushing = TRUE;
10817 if (!(IN_LEV_FIELD(nextx, nexty) &&
10818 (IS_FREE(nextx, nexty) ||
10819 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10820 IS_SB_ELEMENT(element)))))
10821 return MF_NO_ACTION;
10823 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10824 return MF_NO_ACTION;
10826 if (player->push_delay == 0) /* new pushing; restart delay */
10827 player->push_delay = FrameCounter;
10829 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
10830 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10831 element != EL_SPRING && element != EL_BALLOON)
10833 /* make sure that there is no move delay before next try to push */
10834 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10835 player->move_delay = INITIAL_MOVE_DELAY_OFF;
10837 return MF_NO_ACTION;
10841 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
10844 if (IS_SB_ELEMENT(element))
10846 if (element == EL_SOKOBAN_FIELD_FULL)
10848 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10849 local_player->sokobanfields_still_needed++;
10852 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10854 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10855 local_player->sokobanfields_still_needed--;
10858 Feld[x][y] = EL_SOKOBAN_OBJECT;
10860 if (Back[x][y] == Back[nextx][nexty])
10861 PlayLevelSoundAction(x, y, ACTION_PUSHING);
10862 else if (Back[x][y] != 0)
10863 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10866 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10869 if (local_player->sokobanfields_still_needed == 0 &&
10870 game.emulation == EMU_SOKOBAN)
10872 player->LevelSolved = player->GameOver = TRUE;
10873 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10877 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10879 InitMovingField(x, y, move_direction);
10880 GfxAction[x][y] = ACTION_PUSHING;
10882 if (mode == DF_SNAP)
10883 ContinueMoving(x, y);
10885 MovPos[x][y] = (dx != 0 ? dx : dy);
10887 Pushed[x][y] = TRUE;
10888 Pushed[nextx][nexty] = TRUE;
10890 if (game.engine_version < VERSION_IDENT(2,2,0,7))
10891 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10893 player->push_delay_value = -1; /* get new value later */
10896 /* !!! TEST ONLY !!! */
10897 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10898 player->index_bit, dig_side);
10899 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
10900 player->index_bit, dig_side);
10902 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
10903 player->index_bit, dig_side);
10904 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10905 player->index_bit, dig_side);
10910 else if (IS_SWITCHABLE(element))
10912 if (PLAYER_SWITCHING(player, x, y))
10915 player->is_switching = TRUE;
10916 player->switch_x = x;
10917 player->switch_y = y;
10919 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10921 if (element == EL_ROBOT_WHEEL)
10923 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10927 DrawLevelField(x, y);
10929 else if (element == EL_SP_TERMINAL)
10933 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
10935 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10937 else if (Feld[xx][yy] == EL_SP_TERMINAL)
10938 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10941 else if (IS_BELT_SWITCH(element))
10943 ToggleBeltSwitch(x, y);
10945 else if (element == EL_SWITCHGATE_SWITCH_UP ||
10946 element == EL_SWITCHGATE_SWITCH_DOWN)
10948 ToggleSwitchgateSwitch(x, y);
10950 else if (element == EL_LIGHT_SWITCH ||
10951 element == EL_LIGHT_SWITCH_ACTIVE)
10953 ToggleLightSwitch(x, y);
10956 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
10957 SND_LIGHT_SWITCH_ACTIVATING :
10958 SND_LIGHT_SWITCH_DEACTIVATING);
10961 else if (element == EL_TIMEGATE_SWITCH)
10963 ActivateTimegateSwitch(x, y);
10965 else if (element == EL_BALLOON_SWITCH_LEFT ||
10966 element == EL_BALLOON_SWITCH_RIGHT ||
10967 element == EL_BALLOON_SWITCH_UP ||
10968 element == EL_BALLOON_SWITCH_DOWN ||
10969 element == EL_BALLOON_SWITCH_ANY)
10971 if (element == EL_BALLOON_SWITCH_ANY)
10972 game.balloon_dir = move_direction;
10974 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10975 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10976 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10977 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10980 else if (element == EL_LAMP)
10982 Feld[x][y] = EL_LAMP_ACTIVE;
10983 local_player->lights_still_needed--;
10985 DrawLevelField(x, y);
10987 else if (element == EL_TIME_ORB_FULL)
10989 Feld[x][y] = EL_TIME_ORB_EMPTY;
10991 DrawGameValue_Time(TimeLeft);
10993 DrawLevelField(x, y);
10996 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11004 if (!PLAYER_SWITCHING(player, x, y))
11006 player->is_switching = TRUE;
11007 player->switch_x = x;
11008 player->switch_y = y;
11011 /* !!! TEST ONLY !!! */
11012 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11013 player->index_bit, dig_side);
11014 CheckTriggeredElementChangeByPlayer(x, y, element,
11015 CE_OTHER_IS_SWITCHING,
11016 player->index_bit, dig_side);
11018 CheckTriggeredElementChangeByPlayer(x, y, element,
11019 CE_OTHER_IS_SWITCHING,
11020 player->index_bit, dig_side);
11021 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11022 player->index_bit, dig_side);
11027 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11028 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11029 player->index_bit, dig_side);
11030 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11031 player->index_bit, dig_side);
11033 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11034 player->index_bit, dig_side);
11035 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11036 player->index_bit, dig_side);
11040 return MF_NO_ACTION;
11043 player->push_delay = 0;
11045 if (Feld[x][y] != element) /* really digged/collected something */
11046 player->is_collecting = !player->is_digging;
11051 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11053 int jx = player->jx, jy = player->jy;
11054 int x = jx + dx, y = jy + dy;
11055 int snap_direction = (dx == -1 ? MV_LEFT :
11056 dx == +1 ? MV_RIGHT :
11058 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11061 if (player->MovPos)
11064 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
11068 if (!player->active || !IN_LEV_FIELD(x, y))
11076 if (player->MovPos == 0)
11077 player->is_pushing = FALSE;
11079 player->is_snapping = FALSE;
11081 if (player->MovPos == 0)
11083 player->is_moving = FALSE;
11084 player->is_digging = FALSE;
11085 player->is_collecting = FALSE;
11091 if (player->is_snapping)
11094 player->MovDir = snap_direction;
11097 if (player->MovPos == 0)
11100 player->is_moving = FALSE;
11101 player->is_digging = FALSE;
11102 player->is_collecting = FALSE;
11105 player->is_dropping = FALSE;
11107 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11110 player->is_snapping = TRUE;
11113 if (player->MovPos == 0)
11116 player->is_moving = FALSE;
11117 player->is_digging = FALSE;
11118 player->is_collecting = FALSE;
11121 DrawLevelField(x, y);
11127 boolean DropElement(struct PlayerInfo *player)
11129 static int trigger_sides[4] =
11131 CH_SIDE_LEFT, /* dropping left */
11132 CH_SIDE_RIGHT, /* dropping right */
11133 CH_SIDE_TOP, /* dropping up */
11134 CH_SIDE_BOTTOM, /* dropping down */
11136 int jx = player->jx, jy = player->jy;
11137 int drop_direction = player->MovDir;
11138 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11139 int old_element = Feld[jx][jy];
11140 int drop_element = (player->inventory_size > 0 ?
11141 player->inventory_element[player->inventory_size - 1] :
11142 player->inventory_infinite_element != EL_UNDEFINED ?
11143 player->inventory_infinite_element :
11144 player->dynabombs_left > 0 ?
11145 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11147 int new_element = drop_element; /* default: element does not change */
11149 /* check if player is active, not moving and ready to drop */
11150 if (!player->active || player->MovPos || player->drop_delay > 0)
11153 /* check if player has anything that can be dropped */
11155 if (new_element == EL_UNDEFINED)
11158 if (player->inventory_size == 0 &&
11159 player->inventory_infinite_element == EL_UNDEFINED &&
11160 player->dynabombs_left == 0)
11164 /* check if anything can be dropped at the current position */
11165 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11168 /* collected custom elements can only be dropped on empty fields */
11170 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11173 if (player->inventory_size > 0 &&
11174 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11175 && old_element != EL_EMPTY)
11179 if (old_element != EL_EMPTY)
11180 Back[jx][jy] = old_element; /* store old element on this field */
11182 ResetGfxAnimation(jx, jy);
11183 ResetRandomAnimationValue(jx, jy);
11185 if (player->inventory_size > 0 ||
11186 player->inventory_infinite_element != EL_UNDEFINED)
11188 if (player->inventory_size > 0)
11190 player->inventory_size--;
11193 new_element = player->inventory_element[player->inventory_size];
11196 DrawGameValue_Dynamite(local_player->inventory_size);
11198 if (new_element == EL_DYNAMITE)
11199 new_element = EL_DYNAMITE_ACTIVE;
11200 else if (new_element == EL_SP_DISK_RED)
11201 new_element = EL_SP_DISK_RED_ACTIVE;
11204 Feld[jx][jy] = new_element;
11206 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
11207 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
11209 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
11212 /* needed if previous element just changed to "empty" in the last frame */
11213 Changed[jx][jy] = 0; /* allow another change */
11217 /* !!! TEST ONLY !!! */
11218 CheckElementChangeByPlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
11219 player->index_bit, drop_side);
11220 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11221 CE_OTHER_GETS_DROPPED,
11222 player->index_bit, drop_side);
11224 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11225 CE_OTHER_GETS_DROPPED,
11226 player->index_bit, drop_side);
11227 CheckElementChangeByPlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
11228 player->index_bit, drop_side);
11231 TestIfElementTouchesCustomElement(jx, jy);
11233 else /* player is dropping a dyna bomb */
11235 player->dynabombs_left--;
11238 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
11241 Feld[jx][jy] = new_element;
11243 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
11244 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
11246 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
11253 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
11256 InitField_WithBug1(jx, jy, FALSE);
11258 InitField(jx, jy, FALSE);
11259 if (CAN_MOVE(Feld[jx][jy]))
11260 InitMovDir(jx, jy);
11264 new_element = Feld[jx][jy]; /* element might have changed */
11266 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11267 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11270 int move_stepsize = element_info[new_element].move_stepsize;
11272 int direction, dx, dy, nextx, nexty;
11274 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11275 MovDir[jx][jy] = player->MovDir;
11277 direction = MovDir[jx][jy];
11278 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11279 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11283 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
11286 WasJustMoving[jx][jy] = 3;
11288 InitMovingField(jx, jy, direction);
11289 ContinueMoving(jx, jy);
11294 Changed[jx][jy] = 0; /* allow another change */
11297 TestIfElementHitsCustomElement(jx, jy, direction);
11299 CheckElementChangeBySide(jx, jy, new_element, touched_element,
11300 CE_HITTING_SOMETHING, direction);
11305 player->drop_delay = 2 * TILEX / move_stepsize + 1;
11310 player->drop_delay = 8 + 8 + 8;
11314 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11319 player->is_dropping = TRUE;
11325 /* ------------------------------------------------------------------------- */
11326 /* game sound playing functions */
11327 /* ------------------------------------------------------------------------- */
11329 static int *loop_sound_frame = NULL;
11330 static int *loop_sound_volume = NULL;
11332 void InitPlayLevelSound()
11334 int num_sounds = getSoundListSize();
11336 checked_free(loop_sound_frame);
11337 checked_free(loop_sound_volume);
11339 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11340 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11343 static void PlayLevelSound(int x, int y, int nr)
11345 int sx = SCREENX(x), sy = SCREENY(y);
11346 int volume, stereo_position;
11347 int max_distance = 8;
11348 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11350 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11351 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11354 if (!IN_LEV_FIELD(x, y) ||
11355 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11356 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11359 volume = SOUND_MAX_VOLUME;
11361 if (!IN_SCR_FIELD(sx, sy))
11363 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11364 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11366 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11369 stereo_position = (SOUND_MAX_LEFT +
11370 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11371 (SCR_FIELDX + 2 * max_distance));
11373 if (IS_LOOP_SOUND(nr))
11375 /* This assures that quieter loop sounds do not overwrite louder ones,
11376 while restarting sound volume comparison with each new game frame. */
11378 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11381 loop_sound_volume[nr] = volume;
11382 loop_sound_frame[nr] = FrameCounter;
11385 PlaySoundExt(nr, volume, stereo_position, type);
11388 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11390 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11391 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11392 y < LEVELY(BY1) ? LEVELY(BY1) :
11393 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11397 static void PlayLevelSoundAction(int x, int y, int action)
11399 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11402 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11404 int sound_effect = element_info[element].sound[action];
11406 if (sound_effect != SND_UNDEFINED)
11407 PlayLevelSound(x, y, sound_effect);
11410 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11413 int sound_effect = element_info[element].sound[action];
11415 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11416 PlayLevelSound(x, y, sound_effect);
11419 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11421 int sound_effect = element_info[Feld[x][y]].sound[action];
11423 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11424 PlayLevelSound(x, y, sound_effect);
11427 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11429 int sound_effect = element_info[Feld[x][y]].sound[action];
11431 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11432 StopSound(sound_effect);
11435 static void PlayLevelMusic()
11437 if (levelset.music[level_nr] != MUS_UNDEFINED)
11438 PlayMusic(levelset.music[level_nr]); /* from config file */
11440 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11443 void RaiseScore(int value)
11445 local_player->score += value;
11447 DrawGameValue_Score(local_player->score);
11450 void RaiseScoreElement(int element)
11455 case EL_BD_DIAMOND:
11456 case EL_EMERALD_YELLOW:
11457 case EL_EMERALD_RED:
11458 case EL_EMERALD_PURPLE:
11459 case EL_SP_INFOTRON:
11460 RaiseScore(level.score[SC_EMERALD]);
11463 RaiseScore(level.score[SC_DIAMOND]);
11466 RaiseScore(level.score[SC_CRYSTAL]);
11469 RaiseScore(level.score[SC_PEARL]);
11472 case EL_BD_BUTTERFLY:
11473 case EL_SP_ELECTRON:
11474 RaiseScore(level.score[SC_BUG]);
11477 case EL_BD_FIREFLY:
11478 case EL_SP_SNIKSNAK:
11479 RaiseScore(level.score[SC_SPACESHIP]);
11482 case EL_DARK_YAMYAM:
11483 RaiseScore(level.score[SC_YAMYAM]);
11486 RaiseScore(level.score[SC_ROBOT]);
11489 RaiseScore(level.score[SC_PACMAN]);
11492 RaiseScore(level.score[SC_NUT]);
11495 case EL_SP_DISK_RED:
11496 case EL_DYNABOMB_INCREASE_NUMBER:
11497 case EL_DYNABOMB_INCREASE_SIZE:
11498 case EL_DYNABOMB_INCREASE_POWER:
11499 RaiseScore(level.score[SC_DYNAMITE]);
11501 case EL_SHIELD_NORMAL:
11502 case EL_SHIELD_DEADLY:
11503 RaiseScore(level.score[SC_SHIELD]);
11505 case EL_EXTRA_TIME:
11506 RaiseScore(level.score[SC_TIME_BONUS]);
11512 RaiseScore(level.score[SC_KEY]);
11515 RaiseScore(element_info[element].collect_score);
11520 void RequestQuitGame(boolean ask_if_really_quit)
11522 if (AllPlayersGone ||
11523 !ask_if_really_quit ||
11524 level_editor_test_game ||
11525 Request("Do you really want to quit the game ?",
11526 REQ_ASK | REQ_STAY_CLOSED))
11528 #if defined(PLATFORM_UNIX)
11529 if (options.network)
11530 SendToServer_StopPlaying();
11534 game_status = GAME_MODE_MAIN;
11542 if (tape.playing && tape.deactivate_display)
11543 TapeDeactivateDisplayOff(TRUE);
11546 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11549 if (tape.playing && tape.deactivate_display)
11550 TapeDeactivateDisplayOn();
11557 /* ---------- new game button stuff ---------------------------------------- */
11559 /* graphic position values for game buttons */
11560 #define GAME_BUTTON_XSIZE 30
11561 #define GAME_BUTTON_YSIZE 30
11562 #define GAME_BUTTON_XPOS 5
11563 #define GAME_BUTTON_YPOS 215
11564 #define SOUND_BUTTON_XPOS 5
11565 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11567 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11568 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11569 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11570 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11571 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11572 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11579 } gamebutton_info[NUM_GAME_BUTTONS] =
11582 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11587 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11588 GAME_CTRL_ID_PAUSE,
11592 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11597 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11598 SOUND_CTRL_ID_MUSIC,
11599 "background music on/off"
11602 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11603 SOUND_CTRL_ID_LOOPS,
11604 "sound loops on/off"
11607 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11608 SOUND_CTRL_ID_SIMPLE,
11609 "normal sounds on/off"
11613 void CreateGameButtons()
11617 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11619 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11620 struct GadgetInfo *gi;
11623 unsigned long event_mask;
11624 int gd_xoffset, gd_yoffset;
11625 int gd_x1, gd_x2, gd_y1, gd_y2;
11628 gd_xoffset = gamebutton_info[i].x;
11629 gd_yoffset = gamebutton_info[i].y;
11630 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11631 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11633 if (id == GAME_CTRL_ID_STOP ||
11634 id == GAME_CTRL_ID_PAUSE ||
11635 id == GAME_CTRL_ID_PLAY)
11637 button_type = GD_TYPE_NORMAL_BUTTON;
11639 event_mask = GD_EVENT_RELEASED;
11640 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11641 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11645 button_type = GD_TYPE_CHECK_BUTTON;
11647 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11648 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11649 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11650 event_mask = GD_EVENT_PRESSED;
11651 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11652 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11655 gi = CreateGadget(GDI_CUSTOM_ID, id,
11656 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11657 GDI_X, DX + gd_xoffset,
11658 GDI_Y, DY + gd_yoffset,
11659 GDI_WIDTH, GAME_BUTTON_XSIZE,
11660 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11661 GDI_TYPE, button_type,
11662 GDI_STATE, GD_BUTTON_UNPRESSED,
11663 GDI_CHECKED, checked,
11664 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11665 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11666 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11667 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11668 GDI_EVENT_MASK, event_mask,
11669 GDI_CALLBACK_ACTION, HandleGameButtons,
11673 Error(ERR_EXIT, "cannot create gadget");
11675 game_gadget[id] = gi;
11679 void FreeGameButtons()
11683 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11684 FreeGadget(game_gadget[i]);
11687 static void MapGameButtons()
11691 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11692 MapGadget(game_gadget[i]);
11695 void UnmapGameButtons()
11699 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11700 UnmapGadget(game_gadget[i]);
11703 static void HandleGameButtons(struct GadgetInfo *gi)
11705 int id = gi->custom_id;
11707 if (game_status != GAME_MODE_PLAYING)
11712 case GAME_CTRL_ID_STOP:
11713 RequestQuitGame(TRUE);
11716 case GAME_CTRL_ID_PAUSE:
11717 if (options.network)
11719 #if defined(PLATFORM_UNIX)
11721 SendToServer_ContinuePlaying();
11723 SendToServer_PausePlaying();
11727 TapeTogglePause(TAPE_TOGGLE_MANUAL);
11730 case GAME_CTRL_ID_PLAY:
11733 #if defined(PLATFORM_UNIX)
11734 if (options.network)
11735 SendToServer_ContinuePlaying();
11739 tape.pausing = FALSE;
11740 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11745 case SOUND_CTRL_ID_MUSIC:
11746 if (setup.sound_music)
11748 setup.sound_music = FALSE;
11751 else if (audio.music_available)
11753 setup.sound = setup.sound_music = TRUE;
11755 SetAudioMode(setup.sound);
11761 case SOUND_CTRL_ID_LOOPS:
11762 if (setup.sound_loops)
11763 setup.sound_loops = FALSE;
11764 else if (audio.loops_available)
11766 setup.sound = setup.sound_loops = TRUE;
11767 SetAudioMode(setup.sound);
11771 case SOUND_CTRL_ID_SIMPLE:
11772 if (setup.sound_simple)
11773 setup.sound_simple = FALSE;
11774 else if (audio.sound_available)
11776 setup.sound = setup.sound_simple = TRUE;
11777 SetAudioMode(setup.sound);