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
30 /* EXPERIMENTAL STUFF */
31 #define USE_NEW_MOVEMENT FALSE
38 /* for MovePlayer() */
39 #define MF_NO_ACTION 0
43 /* for ScrollPlayer() */
45 #define SCROLL_GO_ON 1
48 #define EX_PHASE_START 0
49 #define EX_TYPE_NONE 0
50 #define EX_TYPE_NORMAL (1 << 0)
51 #define EX_TYPE_CENTER (1 << 1)
52 #define EX_TYPE_BORDER (1 << 2)
53 #define EX_TYPE_CROSS (1 << 3)
54 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
56 /* special positions in the game control window (relative to control window) */
59 #define XX_EMERALDS 29
60 #define YY_EMERALDS 54
61 #define XX_DYNAMITE 29
62 #define YY_DYNAMITE 89
71 /* special positions in the game control window (relative to main window) */
72 #define DX_LEVEL (DX + XX_LEVEL)
73 #define DY_LEVEL (DY + YY_LEVEL)
74 #define DX_EMERALDS (DX + XX_EMERALDS)
75 #define DY_EMERALDS (DY + YY_EMERALDS)
76 #define DX_DYNAMITE (DX + XX_DYNAMITE)
77 #define DY_DYNAMITE (DY + YY_DYNAMITE)
78 #define DX_KEYS (DX + XX_KEYS)
79 #define DY_KEYS (DY + YY_KEYS)
80 #define DX_SCORE (DX + XX_SCORE)
81 #define DY_SCORE (DY + YY_SCORE)
82 #define DX_TIME1 (DX + XX_TIME1)
83 #define DX_TIME2 (DX + XX_TIME2)
84 #define DY_TIME (DY + YY_TIME)
86 /* values for initial player move delay (initial delay counter value) */
87 #define INITIAL_MOVE_DELAY_OFF -1
88 #define INITIAL_MOVE_DELAY_ON 0
90 /* values for player movement speed (which is in fact a delay value) */
91 #define MOVE_DELAY_NORMAL_SPEED 8
92 #define MOVE_DELAY_HIGH_SPEED 4
94 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
95 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
96 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
97 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
99 /* values for other actions */
100 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
102 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
103 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
105 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
107 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
108 RND(element_info[e].push_delay_random))
109 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
110 RND(element_info[e].drop_delay_random))
111 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
112 RND(element_info[e].move_delay_random))
113 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
114 (element_info[e].move_delay_random))
116 #define GET_TARGET_ELEMENT(e, ch) \
117 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
118 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
120 #define GET_VALID_PLAYER_ELEMENT(e) \
121 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
123 #define CAN_GROW_INTO(e) \
124 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
126 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
127 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
130 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
131 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
132 (CAN_MOVE_INTO_ACID(e) && \
133 Feld[x][y] == EL_ACID) || \
136 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
137 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
138 (CAN_MOVE_INTO_ACID(e) && \
139 Feld[x][y] == EL_ACID) || \
142 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
143 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
145 (CAN_MOVE_INTO_ACID(e) && \
146 Feld[x][y] == EL_ACID) || \
147 (DONT_COLLIDE_WITH(e) && \
149 !PLAYER_ENEMY_PROTECTED(x, y))))
152 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
153 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
155 (DONT_COLLIDE_WITH(e) && \
157 !PLAYER_ENEMY_PROTECTED(x, y))))
160 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
161 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
164 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
165 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
167 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
168 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
172 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
175 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
176 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
180 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
181 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
183 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
184 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
186 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
187 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
189 #define PIG_CAN_ENTER_FIELD(e, x, y) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
192 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
193 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
194 IS_FOOD_PENGUIN(Feld[x][y])))
195 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
196 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
198 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
199 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
201 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
202 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
206 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
207 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
208 (CAN_MOVE_INTO_ACID(e) && \
209 Feld[x][y] == EL_ACID) || \
210 Feld[x][y] == EL_DIAMOND))
212 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
213 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
214 (CAN_MOVE_INTO_ACID(e) && \
215 Feld[x][y] == EL_ACID) || \
216 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
218 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
219 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
220 (CAN_MOVE_INTO_ACID(e) && \
221 Feld[x][y] == EL_ACID) || \
222 IS_AMOEBOID(Feld[x][y])))
224 #define PIG_CAN_ENTER_FIELD(e, x, y) \
225 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
226 (CAN_MOVE_INTO_ACID(e) && \
227 Feld[x][y] == EL_ACID) || \
228 IS_FOOD_PIG(Feld[x][y])))
230 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
231 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
232 (CAN_MOVE_INTO_ACID(e) && \
233 Feld[x][y] == EL_ACID) || \
234 IS_FOOD_PENGUIN(Feld[x][y]) || \
235 Feld[x][y] == EL_EXIT_OPEN))
237 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
238 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
239 (CAN_MOVE_INTO_ACID(e) && \
240 Feld[x][y] == EL_ACID)))
242 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
243 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
244 (CAN_MOVE_INTO_ACID(e) && \
245 Feld[x][y] == EL_ACID) || \
248 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
249 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
250 (CAN_MOVE_INTO_ACID(e) && \
251 Feld[x][y] == EL_ACID)))
255 #define GROUP_NR(e) ((e) - EL_GROUP_START)
256 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
257 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
258 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
260 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
261 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
264 #define CE_ENTER_FIELD_COND(e, x, y) \
265 (!IS_PLAYER(x, y) && \
266 (Feld[x][y] == EL_ACID || \
267 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
269 #define CE_ENTER_FIELD_COND(e, x, y) \
270 (!IS_PLAYER(x, y) && \
271 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
274 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
275 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
277 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
278 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
280 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
281 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
282 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
283 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
285 /* game button identifiers */
286 #define GAME_CTRL_ID_STOP 0
287 #define GAME_CTRL_ID_PAUSE 1
288 #define GAME_CTRL_ID_PLAY 2
289 #define SOUND_CTRL_ID_MUSIC 3
290 #define SOUND_CTRL_ID_LOOPS 4
291 #define SOUND_CTRL_ID_SIMPLE 5
293 #define NUM_GAME_BUTTONS 6
296 /* forward declaration for internal use */
298 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
299 static boolean MovePlayer(struct PlayerInfo *, int, int);
300 static void ScrollPlayer(struct PlayerInfo *, int);
301 static void ScrollScreen(struct PlayerInfo *, int);
303 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
305 static void InitBeltMovement(void);
306 static void CloseAllOpenTimegates(void);
307 static void CheckGravityMovement(struct PlayerInfo *);
308 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
309 static void KillHeroUnlessEnemyProtected(int, int);
310 static void KillHeroUnlessExplosionProtected(int, int);
312 static void TestIfPlayerTouchesCustomElement(int, int);
313 static void TestIfElementTouchesCustomElement(int, int);
314 static void TestIfElementHitsCustomElement(int, int, int);
316 static void TestIfElementSmashesCustomElement(int, int, int);
319 static void ChangeElement(int, int, int);
321 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
322 #define CheckTriggeredElementChange(x, y, e, ev) \
323 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
325 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
326 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
327 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
328 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
329 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
330 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
333 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
334 #define CheckElementChange(x, y, e, te, ev) \
335 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
336 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
337 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
338 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
339 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
340 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
341 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
343 static void PlayLevelSound(int, int, int);
344 static void PlayLevelSoundNearest(int, int, int);
345 static void PlayLevelSoundAction(int, int, int);
346 static void PlayLevelSoundElementAction(int, int, int, int);
347 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
348 static void PlayLevelSoundActionIfLoop(int, int, int);
349 static void StopLevelSoundActionIfLoop(int, int, int);
350 static void PlayLevelMusic();
352 static void MapGameButtons();
353 static void HandleGameButtons(struct GadgetInfo *);
355 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
358 /* ------------------------------------------------------------------------- */
359 /* definition of elements that automatically change to other elements after */
360 /* a specified time, eventually calling a function when changing */
361 /* ------------------------------------------------------------------------- */
363 /* forward declaration for changer functions */
364 static void InitBuggyBase(int x, int y);
365 static void WarnBuggyBase(int x, int y);
367 static void InitTrap(int x, int y);
368 static void ActivateTrap(int x, int y);
369 static void ChangeActiveTrap(int x, int y);
371 static void InitRobotWheel(int x, int y);
372 static void RunRobotWheel(int x, int y);
373 static void StopRobotWheel(int x, int y);
375 static void InitTimegateWheel(int x, int y);
376 static void RunTimegateWheel(int x, int y);
378 struct ChangingElementInfo
383 void (*pre_change_function)(int x, int y);
384 void (*change_function)(int x, int y);
385 void (*post_change_function)(int x, int y);
388 static struct ChangingElementInfo change_delay_list[] =
439 EL_SWITCHGATE_OPENING,
447 EL_SWITCHGATE_CLOSING,
448 EL_SWITCHGATE_CLOSED,
480 EL_ACID_SPLASH_RIGHT,
489 EL_SP_BUGGY_BASE_ACTIVATING,
496 EL_SP_BUGGY_BASE_ACTIVATING,
497 EL_SP_BUGGY_BASE_ACTIVE,
504 EL_SP_BUGGY_BASE_ACTIVE,
528 EL_ROBOT_WHEEL_ACTIVE,
536 EL_TIMEGATE_SWITCH_ACTIVE,
557 int push_delay_fixed, push_delay_random;
562 { EL_BALLOON, 0, 0 },
564 { EL_SOKOBAN_OBJECT, 2, 0 },
565 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
566 { EL_SATELLITE, 2, 0 },
567 { EL_SP_DISK_YELLOW, 2, 0 },
569 { EL_UNDEFINED, 0, 0 },
577 move_stepsize_list[] =
579 { EL_AMOEBA_DROP, 2 },
580 { EL_AMOEBA_DROPPING, 2 },
581 { EL_QUICKSAND_FILLING, 1 },
582 { EL_QUICKSAND_EMPTYING, 1 },
583 { EL_MAGIC_WALL_FILLING, 2 },
584 { EL_BD_MAGIC_WALL_FILLING, 2 },
585 { EL_MAGIC_WALL_EMPTYING, 2 },
586 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
596 collect_count_list[] =
599 { EL_BD_DIAMOND, 1 },
600 { EL_EMERALD_YELLOW, 1 },
601 { EL_EMERALD_RED, 1 },
602 { EL_EMERALD_PURPLE, 1 },
604 { EL_SP_INFOTRON, 1 },
616 access_direction_list[] =
618 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
619 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
620 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
621 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
622 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
623 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
624 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
625 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
626 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
627 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
628 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
630 { EL_SP_PORT_LEFT, MV_RIGHT },
631 { EL_SP_PORT_RIGHT, MV_LEFT },
632 { EL_SP_PORT_UP, MV_DOWN },
633 { EL_SP_PORT_DOWN, MV_UP },
634 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
635 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
636 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
637 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
638 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
639 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
640 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
641 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
642 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
643 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
644 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
645 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
646 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
647 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
648 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
650 { EL_UNDEFINED, MV_NO_MOVING }
653 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
655 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
656 CH_EVENT_BIT(CE_DELAY))
657 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
658 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
659 IS_JUST_CHANGING(x, y))
661 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
664 void GetPlayerConfig()
666 if (!audio.sound_available)
667 setup.sound_simple = FALSE;
669 if (!audio.loops_available)
670 setup.sound_loops = FALSE;
672 if (!audio.music_available)
673 setup.sound_music = FALSE;
675 if (!video.fullscreen_available)
676 setup.fullscreen = FALSE;
678 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
680 SetAudioMode(setup.sound);
684 static int getBeltNrFromBeltElement(int element)
686 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
687 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
688 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
691 static int getBeltNrFromBeltActiveElement(int element)
693 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
694 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
695 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
698 static int getBeltNrFromBeltSwitchElement(int element)
700 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
701 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
702 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
705 static int getBeltDirNrFromBeltSwitchElement(int element)
707 static int belt_base_element[4] =
709 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
710 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
711 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
712 EL_CONVEYOR_BELT_4_SWITCH_LEFT
715 int belt_nr = getBeltNrFromBeltSwitchElement(element);
716 int belt_dir_nr = element - belt_base_element[belt_nr];
718 return (belt_dir_nr % 3);
721 static int getBeltDirFromBeltSwitchElement(int element)
723 static int belt_move_dir[3] =
730 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
732 return belt_move_dir[belt_dir_nr];
735 static void InitPlayerField(int x, int y, int element, boolean init_game)
737 if (element == EL_SP_MURPHY)
741 if (stored_player[0].present)
743 Feld[x][y] = EL_SP_MURPHY_CLONE;
749 stored_player[0].use_murphy_graphic = TRUE;
752 Feld[x][y] = EL_PLAYER_1;
758 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
759 int jx = player->jx, jy = player->jy;
761 player->present = TRUE;
763 player->block_last_field = (element == EL_SP_MURPHY ?
764 level.sp_block_last_field :
765 level.block_last_field);
767 if (!options.network || player->connected)
769 player->active = TRUE;
771 /* remove potentially duplicate players */
772 if (StorePlayer[jx][jy] == Feld[x][y])
773 StorePlayer[jx][jy] = 0;
775 StorePlayer[x][y] = Feld[x][y];
779 printf("Player %d activated.\n", player->element_nr);
780 printf("[Local player is %d and currently %s.]\n",
781 local_player->element_nr,
782 local_player->active ? "active" : "not active");
786 Feld[x][y] = EL_EMPTY;
788 player->jx = player->last_jx = x;
789 player->jy = player->last_jy = y;
793 static void InitField(int x, int y, boolean init_game)
795 int element = Feld[x][y];
804 InitPlayerField(x, y, element, init_game);
807 case EL_SOKOBAN_FIELD_PLAYER:
808 element = Feld[x][y] = EL_PLAYER_1;
809 InitField(x, y, init_game);
811 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
812 InitField(x, y, init_game);
815 case EL_SOKOBAN_FIELD_EMPTY:
816 local_player->sokobanfields_still_needed++;
820 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
821 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
822 else if (x > 0 && Feld[x-1][y] == EL_ACID)
823 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
824 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
825 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
826 else if (y > 0 && Feld[x][y-1] == EL_ACID)
827 Feld[x][y] = EL_ACID_POOL_BOTTOM;
828 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
829 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
837 case EL_SPACESHIP_RIGHT:
838 case EL_SPACESHIP_UP:
839 case EL_SPACESHIP_LEFT:
840 case EL_SPACESHIP_DOWN:
842 case EL_BD_BUTTERFLY_RIGHT:
843 case EL_BD_BUTTERFLY_UP:
844 case EL_BD_BUTTERFLY_LEFT:
845 case EL_BD_BUTTERFLY_DOWN:
846 case EL_BD_BUTTERFLY:
847 case EL_BD_FIREFLY_RIGHT:
848 case EL_BD_FIREFLY_UP:
849 case EL_BD_FIREFLY_LEFT:
850 case EL_BD_FIREFLY_DOWN:
852 case EL_PACMAN_RIGHT:
876 if (y == lev_fieldy - 1)
878 Feld[x][y] = EL_AMOEBA_GROWING;
879 Store[x][y] = EL_AMOEBA_WET;
883 case EL_DYNAMITE_ACTIVE:
884 case EL_SP_DISK_RED_ACTIVE:
885 case EL_DYNABOMB_PLAYER_1_ACTIVE:
886 case EL_DYNABOMB_PLAYER_2_ACTIVE:
887 case EL_DYNABOMB_PLAYER_3_ACTIVE:
888 case EL_DYNABOMB_PLAYER_4_ACTIVE:
893 local_player->lights_still_needed++;
897 local_player->friends_still_needed++;
902 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
907 Feld[x][y] = EL_EMPTY;
912 case EL_EM_KEY_1_FILE:
913 Feld[x][y] = EL_EM_KEY_1;
915 case EL_EM_KEY_2_FILE:
916 Feld[x][y] = EL_EM_KEY_2;
918 case EL_EM_KEY_3_FILE:
919 Feld[x][y] = EL_EM_KEY_3;
921 case EL_EM_KEY_4_FILE:
922 Feld[x][y] = EL_EM_KEY_4;
926 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
927 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
928 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
929 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
930 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
931 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
932 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
933 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
934 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
935 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
936 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
937 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
940 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
941 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
942 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
944 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
946 game.belt_dir[belt_nr] = belt_dir;
947 game.belt_dir_nr[belt_nr] = belt_dir_nr;
949 else /* more than one switch -- set it like the first switch */
951 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
956 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
958 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
961 case EL_LIGHT_SWITCH_ACTIVE:
963 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
967 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
969 else if (IS_GROUP_ELEMENT(element))
971 struct ElementGroupInfo *group = element_info[element].group;
972 int last_anim_random_frame = gfx.anim_random_frame;
975 if (group->choice_mode == ANIM_RANDOM)
976 gfx.anim_random_frame = RND(group->num_elements_resolved);
978 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
979 group->choice_mode, 0,
982 if (group->choice_mode == ANIM_RANDOM)
983 gfx.anim_random_frame = last_anim_random_frame;
987 Feld[x][y] = group->element_resolved[element_pos];
989 InitField(x, y, init_game);
995 static inline void InitField_WithBug1(int x, int y, boolean init_game)
997 InitField(x, y, init_game);
999 /* not needed to call InitMovDir() -- already done by InitField()! */
1000 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1001 CAN_MOVE(Feld[x][y]))
1005 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1007 int old_element = Feld[x][y];
1009 InitField(x, y, init_game);
1011 /* not needed to call InitMovDir() -- already done by InitField()! */
1012 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1013 CAN_MOVE(old_element) &&
1014 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1017 /* this case is in fact a combination of not less than three bugs:
1018 first, it calls InitMovDir() for elements that can move, although this is
1019 already done by InitField(); then, it checks the element that was at this
1020 field _before_ the call to InitField() (which can change it); lastly, it
1021 was not called for "mole with direction" elements, which were treated as
1022 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1026 inline void DrawGameValue_Emeralds(int value)
1028 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1031 inline void DrawGameValue_Dynamite(int value)
1033 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1036 inline void DrawGameValue_Keys(struct PlayerInfo *player)
1040 for (i = 0; i < MAX_KEYS; i++)
1042 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1043 el2edimg(EL_KEY_1 + i));
1046 inline void DrawGameValue_Score(int value)
1048 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1051 inline void DrawGameValue_Time(int value)
1054 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1056 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1059 inline void DrawGameValue_Level(int value)
1062 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1065 /* misuse area for displaying emeralds to draw bigger level number */
1066 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1067 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1069 /* now copy it to the area for displaying level number */
1070 BlitBitmap(drawto, drawto,
1071 DX_EMERALDS, DY_EMERALDS + 1,
1072 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1073 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1074 DX_LEVEL - 1, DY_LEVEL + 1);
1076 /* restore the area for displaying emeralds */
1077 DrawGameValue_Emeralds(local_player->gems_still_needed);
1079 /* yes, this is all really ugly :-) */
1083 void DrawGameDoorValues()
1087 DrawGameValue_Level(level_nr);
1089 for (i = 0; i < MAX_PLAYERS; i++)
1090 DrawGameValue_Keys(&stored_player[i]);
1092 DrawGameValue_Emeralds(local_player->gems_still_needed);
1093 DrawGameValue_Dynamite(local_player->inventory_size);
1094 DrawGameValue_Score(local_player->score);
1095 DrawGameValue_Time(TimeLeft);
1098 static void resolve_group_element(int group_element, int recursion_depth)
1100 static int group_nr;
1101 static struct ElementGroupInfo *group;
1102 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1105 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1107 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1108 group_element - EL_GROUP_START + 1);
1110 /* replace element which caused too deep recursion by question mark */
1111 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1116 if (recursion_depth == 0) /* initialization */
1118 group = element_info[group_element].group;
1119 group_nr = group_element - EL_GROUP_START;
1121 group->num_elements_resolved = 0;
1122 group->choice_pos = 0;
1125 for (i = 0; i < actual_group->num_elements; i++)
1127 int element = actual_group->element[i];
1129 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1132 if (IS_GROUP_ELEMENT(element))
1133 resolve_group_element(element, recursion_depth + 1);
1136 group->element_resolved[group->num_elements_resolved++] = element;
1137 element_info[element].in_group[group_nr] = TRUE;
1142 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1144 printf("::: group %d: %d resolved elements\n",
1145 group_element - EL_GROUP_START, group->num_elements_resolved);
1146 for (i = 0; i < group->num_elements_resolved; i++)
1147 printf("::: - %d ['%s']\n", group->element_resolved[i],
1148 element_info[group->element_resolved[i]].token_name);
1155 =============================================================================
1157 -----------------------------------------------------------------------------
1158 initialize game engine due to level / tape version number
1159 =============================================================================
1162 static void InitGameEngine()
1166 /* set game engine from tape file when re-playing, else from level file */
1167 game.engine_version = (tape.playing ? tape.engine_version :
1168 level.game_version);
1170 /* dynamically adjust element properties according to game engine version */
1171 InitElementPropertiesEngine(game.engine_version);
1174 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1175 printf(" tape version == %06d [%s] [file: %06d]\n",
1176 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1178 printf(" => game.engine_version == %06d\n", game.engine_version);
1181 /* ---------- recursively resolve group elements ------------------------- */
1183 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1184 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1185 element_info[i].in_group[j] = FALSE;
1187 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1188 resolve_group_element(EL_GROUP_START + i, 0);
1190 /* ---------- initialize player's initial move delay --------------------- */
1192 /* dynamically adjust player properties according to game engine version */
1193 game.initial_move_delay =
1194 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1195 INITIAL_MOVE_DELAY_OFF);
1197 /* dynamically adjust player properties according to level information */
1198 game.initial_move_delay_value =
1199 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1201 /* ---------- initialize player's initial push delay --------------------- */
1203 /* dynamically adjust player properties according to game engine version */
1204 game.initial_push_delay_value =
1205 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1207 /* ---------- initialize changing elements ------------------------------- */
1209 /* initialize changing elements information */
1210 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1212 struct ElementInfo *ei = &element_info[i];
1214 /* this pointer might have been changed in the level editor */
1215 ei->change = &ei->change_page[0];
1217 if (!IS_CUSTOM_ELEMENT(i))
1219 ei->change->target_element = EL_EMPTY_SPACE;
1220 ei->change->delay_fixed = 0;
1221 ei->change->delay_random = 0;
1222 ei->change->delay_frames = 1;
1225 ei->change_events = CE_BITMASK_DEFAULT;
1226 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1228 ei->event_page_nr[j] = 0;
1229 ei->event_page[j] = &ei->change_page[0];
1233 /* add changing elements from pre-defined list */
1234 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1236 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1237 struct ElementInfo *ei = &element_info[ch_delay->element];
1239 ei->change->target_element = ch_delay->target_element;
1240 ei->change->delay_fixed = ch_delay->change_delay;
1242 ei->change->pre_change_function = ch_delay->pre_change_function;
1243 ei->change->change_function = ch_delay->change_function;
1244 ei->change->post_change_function = ch_delay->post_change_function;
1246 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1249 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1254 /* add change events from custom element configuration */
1255 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1257 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1259 for (j = 0; j < ei->num_change_pages; j++)
1261 if (!ei->change_page[j].can_change)
1264 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1266 /* only add event page for the first page found with this event */
1267 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1268 !(ei->change_events & CH_EVENT_BIT(k)))
1270 ei->change_events |= CH_EVENT_BIT(k);
1271 ei->event_page_nr[k] = j;
1272 ei->event_page[k] = &ei->change_page[j];
1280 /* add change events from custom element configuration */
1281 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1283 int element = EL_CUSTOM_START + i;
1285 /* only add custom elements that change after fixed/random frame delay */
1286 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1287 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1291 /* ---------- initialize run-time trigger player and element ------------- */
1293 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1295 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1297 for (j = 0; j < ei->num_change_pages; j++)
1299 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1300 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1304 /* ---------- initialize trigger events ---------------------------------- */
1306 /* initialize trigger events information */
1307 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1308 trigger_events[i] = EP_BITMASK_DEFAULT;
1311 /* add trigger events from element change event properties */
1312 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1314 struct ElementInfo *ei = &element_info[i];
1316 for (j = 0; j < ei->num_change_pages; j++)
1318 if (!ei->change_page[j].can_change)
1321 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1323 int trigger_element = ei->change_page[j].trigger_element;
1325 if (IS_GROUP_ELEMENT(trigger_element))
1327 struct ElementGroupInfo *group = element_info[trigger_element].group;
1329 for (k = 0; k < group->num_elements_resolved; k++)
1330 trigger_events[group->element_resolved[k]]
1331 |= ei->change_page[j].events;
1334 trigger_events[trigger_element] |= ei->change_page[j].events;
1339 /* add trigger events from element change event properties */
1340 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1341 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1342 trigger_events[element_info[i].change->trigger_element] |=
1343 element_info[i].change->events;
1346 /* ---------- initialize push delay -------------------------------------- */
1348 /* initialize push delay values to default */
1349 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1351 if (!IS_CUSTOM_ELEMENT(i))
1353 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1354 element_info[i].push_delay_random = game.default_push_delay_random;
1358 /* set push delay value for certain elements from pre-defined list */
1359 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1361 int e = push_delay_list[i].element;
1363 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1364 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1367 /* set push delay value for Supaplex elements for newer engine versions */
1368 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1370 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1372 if (IS_SP_ELEMENT(i))
1374 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1375 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1380 /* ---------- initialize move stepsize ----------------------------------- */
1382 /* initialize move stepsize values to default */
1383 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1384 if (!IS_CUSTOM_ELEMENT(i))
1385 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1387 /* set move stepsize value for certain elements from pre-defined list */
1388 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1390 int e = move_stepsize_list[i].element;
1392 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1396 /* ---------- initialize move dig/leave ---------------------------------- */
1398 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1400 element_info[i].can_leave_element = FALSE;
1401 element_info[i].can_leave_element_last = FALSE;
1405 /* ---------- initialize gem count --------------------------------------- */
1407 /* initialize gem count values for each element */
1408 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1409 if (!IS_CUSTOM_ELEMENT(i))
1410 element_info[i].collect_count = 0;
1412 /* add gem count values for all elements from pre-defined list */
1413 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1414 element_info[collect_count_list[i].element].collect_count =
1415 collect_count_list[i].count;
1417 /* ---------- initialize access direction -------------------------------- */
1419 /* initialize access direction values to default (access from every side) */
1420 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1421 if (!IS_CUSTOM_ELEMENT(i))
1422 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1424 /* set access direction value for certain elements from pre-defined list */
1425 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1426 element_info[access_direction_list[i].element].access_direction =
1427 access_direction_list[i].direction;
1432 =============================================================================
1434 -----------------------------------------------------------------------------
1435 initialize and start new game
1436 =============================================================================
1441 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1442 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1443 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1450 #if USE_NEW_AMOEBA_CODE
1451 printf("Using new amoeba code.\n");
1453 printf("Using old amoeba code.\n");
1458 /* don't play tapes over network */
1459 network_playing = (options.network && !tape.playing);
1461 for (i = 0; i < MAX_PLAYERS; i++)
1463 struct PlayerInfo *player = &stored_player[i];
1465 player->index_nr = i;
1466 player->index_bit = (1 << i);
1467 player->element_nr = EL_PLAYER_1 + i;
1469 player->present = FALSE;
1470 player->active = FALSE;
1473 player->effective_action = 0;
1474 player->programmed_action = 0;
1477 player->gems_still_needed = level.gems_needed;
1478 player->sokobanfields_still_needed = 0;
1479 player->lights_still_needed = 0;
1480 player->friends_still_needed = 0;
1482 for (j = 0; j < MAX_KEYS; j++)
1483 player->key[j] = FALSE;
1485 player->dynabomb_count = 0;
1486 player->dynabomb_size = 1;
1487 player->dynabombs_left = 0;
1488 player->dynabomb_xl = FALSE;
1490 player->MovDir = MV_NO_MOVING;
1493 player->GfxDir = MV_NO_MOVING;
1494 player->GfxAction = ACTION_DEFAULT;
1496 player->StepFrame = 0;
1498 player->use_murphy_graphic = FALSE;
1500 player->block_last_field = FALSE;
1501 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1503 player->actual_frame_counter = 0;
1505 player->step_counter = 0;
1507 player->last_move_dir = MV_NO_MOVING;
1509 player->is_waiting = FALSE;
1510 player->is_moving = FALSE;
1511 player->is_auto_moving = FALSE;
1512 player->is_digging = FALSE;
1513 player->is_snapping = FALSE;
1514 player->is_collecting = FALSE;
1515 player->is_pushing = FALSE;
1516 player->is_switching = FALSE;
1517 player->is_dropping = FALSE;
1519 player->is_bored = FALSE;
1520 player->is_sleeping = FALSE;
1522 player->frame_counter_bored = -1;
1523 player->frame_counter_sleeping = -1;
1525 player->anim_delay_counter = 0;
1526 player->post_delay_counter = 0;
1528 player->action_waiting = ACTION_DEFAULT;
1529 player->last_action_waiting = ACTION_DEFAULT;
1530 player->special_action_bored = ACTION_DEFAULT;
1531 player->special_action_sleeping = ACTION_DEFAULT;
1533 player->num_special_action_bored = 0;
1534 player->num_special_action_sleeping = 0;
1536 /* determine number of special actions for bored and sleeping animation */
1537 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1539 boolean found = FALSE;
1541 for (k = 0; k < NUM_DIRECTIONS; k++)
1542 if (el_act_dir2img(player->element_nr, j, k) !=
1543 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1547 player->num_special_action_bored++;
1551 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1553 boolean found = FALSE;
1555 for (k = 0; k < NUM_DIRECTIONS; k++)
1556 if (el_act_dir2img(player->element_nr, j, k) !=
1557 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1561 player->num_special_action_sleeping++;
1566 player->switch_x = -1;
1567 player->switch_y = -1;
1569 player->show_envelope = 0;
1571 player->move_delay = game.initial_move_delay;
1572 player->move_delay_value = game.initial_move_delay_value;
1574 player->move_delay_reset_counter = 0;
1576 player->push_delay = 0;
1577 player->push_delay_value = game.initial_push_delay_value;
1579 player->drop_delay = 0;
1581 player->last_jx = player->last_jy = 0;
1582 player->jx = player->jy = 0;
1584 player->shield_normal_time_left = 0;
1585 player->shield_deadly_time_left = 0;
1587 player->inventory_infinite_element = EL_UNDEFINED;
1588 player->inventory_size = 0;
1590 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1591 SnapField(player, 0, 0);
1593 player->LevelSolved = FALSE;
1594 player->GameOver = FALSE;
1597 network_player_action_received = FALSE;
1599 #if defined(NETWORK_AVALIABLE)
1600 /* initial null action */
1601 if (network_playing)
1602 SendToServer_MovePlayer(MV_NO_MOVING);
1610 TimeLeft = level.time;
1613 ScreenMovDir = MV_NO_MOVING;
1617 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1619 AllPlayersGone = FALSE;
1621 game.yamyam_content_nr = 0;
1622 game.magic_wall_active = FALSE;
1623 game.magic_wall_time_left = 0;
1624 game.light_time_left = 0;
1625 game.timegate_time_left = 0;
1626 game.switchgate_pos = 0;
1627 game.balloon_dir = MV_NO_MOVING;
1628 game.gravity = level.initial_gravity;
1629 game.explosions_delayed = TRUE;
1631 game.envelope_active = FALSE;
1633 for (i = 0; i < NUM_BELTS; i++)
1635 game.belt_dir[i] = MV_NO_MOVING;
1636 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1639 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1640 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1642 for (x = 0; x < lev_fieldx; x++)
1644 for (y = 0; y < lev_fieldy; y++)
1646 Feld[x][y] = level.field[x][y];
1647 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1648 ChangeDelay[x][y] = 0;
1649 ChangePage[x][y] = -1;
1650 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1652 WasJustMoving[x][y] = 0;
1653 WasJustFalling[x][y] = 0;
1654 CheckCollision[x][y] = 0;
1656 Pushed[x][y] = FALSE;
1658 Changed[x][y] = CE_BITMASK_DEFAULT;
1659 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1661 ExplodePhase[x][y] = 0;
1662 ExplodeDelay[x][y] = 0;
1663 ExplodeField[x][y] = EX_TYPE_NONE;
1665 RunnerVisit[x][y] = 0;
1666 PlayerVisit[x][y] = 0;
1669 GfxRandom[x][y] = INIT_GFX_RANDOM();
1670 GfxElement[x][y] = EL_UNDEFINED;
1671 GfxAction[x][y] = ACTION_DEFAULT;
1672 GfxDir[x][y] = MV_NO_MOVING;
1676 for (y = 0; y < lev_fieldy; y++)
1678 for (x = 0; x < lev_fieldx; x++)
1680 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1682 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1684 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1687 InitField(x, y, TRUE);
1693 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1694 emulate_sb ? EMU_SOKOBAN :
1695 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1697 /* initialize explosion and ignition delay */
1698 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1700 if (!IS_CUSTOM_ELEMENT(i))
1703 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1704 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1705 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1706 int last_phase = (num_phase + 1) * delay;
1707 int half_phase = (num_phase / 2) * delay;
1709 element_info[i].explosion_delay = last_phase - 1;
1710 element_info[i].ignition_delay = half_phase;
1713 if (i == EL_BLACK_ORB)
1714 element_info[i].ignition_delay = 0;
1716 if (i == EL_BLACK_ORB)
1717 element_info[i].ignition_delay = 1;
1722 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1723 element_info[i].explosion_delay = 1;
1725 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1726 element_info[i].ignition_delay = 1;
1730 /* correct non-moving belts to start moving left */
1731 for (i = 0; i < NUM_BELTS; i++)
1732 if (game.belt_dir[i] == MV_NO_MOVING)
1733 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1735 /* check if any connected player was not found in playfield */
1736 for (i = 0; i < MAX_PLAYERS; i++)
1738 struct PlayerInfo *player = &stored_player[i];
1740 if (player->connected && !player->present)
1742 for (j = 0; j < MAX_PLAYERS; j++)
1744 struct PlayerInfo *some_player = &stored_player[j];
1745 int jx = some_player->jx, jy = some_player->jy;
1747 /* assign first free player found that is present in the playfield */
1748 if (some_player->present && !some_player->connected)
1750 player->present = TRUE;
1751 player->active = TRUE;
1753 some_player->present = FALSE;
1754 some_player->active = FALSE;
1757 player->element_nr = some_player->element_nr;
1760 StorePlayer[jx][jy] = player->element_nr;
1761 player->jx = player->last_jx = jx;
1762 player->jy = player->last_jy = jy;
1772 /* when playing a tape, eliminate all players which do not participate */
1774 for (i = 0; i < MAX_PLAYERS; i++)
1776 if (stored_player[i].active && !tape.player_participates[i])
1778 struct PlayerInfo *player = &stored_player[i];
1779 int jx = player->jx, jy = player->jy;
1781 player->active = FALSE;
1782 StorePlayer[jx][jy] = 0;
1783 Feld[jx][jy] = EL_EMPTY;
1787 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1789 /* when in single player mode, eliminate all but the first active player */
1791 for (i = 0; i < MAX_PLAYERS; i++)
1793 if (stored_player[i].active)
1795 for (j = i + 1; j < MAX_PLAYERS; j++)
1797 if (stored_player[j].active)
1799 struct PlayerInfo *player = &stored_player[j];
1800 int jx = player->jx, jy = player->jy;
1802 player->active = FALSE;
1803 player->present = FALSE;
1805 StorePlayer[jx][jy] = 0;
1806 Feld[jx][jy] = EL_EMPTY;
1813 /* when recording the game, store which players take part in the game */
1816 for (i = 0; i < MAX_PLAYERS; i++)
1817 if (stored_player[i].active)
1818 tape.player_participates[i] = TRUE;
1823 for (i = 0; i < MAX_PLAYERS; i++)
1825 struct PlayerInfo *player = &stored_player[i];
1827 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1832 if (local_player == player)
1833 printf("Player %d is local player.\n", i+1);
1837 if (BorderElement == EL_EMPTY)
1840 SBX_Right = lev_fieldx - SCR_FIELDX;
1842 SBY_Lower = lev_fieldy - SCR_FIELDY;
1847 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1849 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1852 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1853 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1855 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1856 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1858 /* if local player not found, look for custom element that might create
1859 the player (make some assumptions about the right custom element) */
1860 if (!local_player->present)
1862 int start_x = 0, start_y = 0;
1863 int found_rating = 0;
1864 int found_element = EL_UNDEFINED;
1866 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1868 int element = Feld[x][y];
1873 if (!IS_CUSTOM_ELEMENT(element))
1876 if (CAN_CHANGE(element))
1878 for (i = 0; i < element_info[element].num_change_pages; i++)
1880 content = element_info[element].change_page[i].target_element;
1881 is_player = ELEM_IS_PLAYER(content);
1883 if (is_player && (found_rating < 3 || element < found_element))
1889 found_element = element;
1894 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1896 content = element_info[element].content[xx][yy];
1897 is_player = ELEM_IS_PLAYER(content);
1899 if (is_player && (found_rating < 2 || element < found_element))
1901 start_x = x + xx - 1;
1902 start_y = y + yy - 1;
1905 found_element = element;
1908 if (!CAN_CHANGE(element))
1911 for (i = 0; i < element_info[element].num_change_pages; i++)
1913 content= element_info[element].change_page[i].target_content[xx][yy];
1914 is_player = ELEM_IS_PLAYER(content);
1916 if (is_player && (found_rating < 1 || element < found_element))
1918 start_x = x + xx - 1;
1919 start_y = y + yy - 1;
1922 found_element = element;
1928 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1929 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1932 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1933 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1939 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1940 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1941 local_player->jx - MIDPOSX);
1943 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1944 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1945 local_player->jy - MIDPOSY);
1947 scroll_x = SBX_Left;
1948 scroll_y = SBY_Upper;
1949 if (local_player->jx >= SBX_Left + MIDPOSX)
1950 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1951 local_player->jx - MIDPOSX :
1953 if (local_player->jy >= SBY_Upper + MIDPOSY)
1954 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1955 local_player->jy - MIDPOSY :
1960 CloseDoor(DOOR_CLOSE_1);
1965 /* after drawing the level, correct some elements */
1966 if (game.timegate_time_left == 0)
1967 CloseAllOpenTimegates();
1969 if (setup.soft_scrolling)
1970 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1972 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1975 /* copy default game door content to main double buffer */
1976 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1977 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1979 DrawGameDoorValues();
1983 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1984 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1985 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1989 /* copy actual game door content to door double buffer for OpenDoor() */
1990 BlitBitmap(drawto, bitmap_db_door,
1991 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1993 OpenDoor(DOOR_OPEN_ALL);
1995 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1997 if (setup.sound_music)
2000 KeyboardAutoRepeatOffUnlessAutoplay();
2004 for (i = 0; i < MAX_PLAYERS; i++)
2005 printf("Player %d %sactive.\n",
2006 i + 1, (stored_player[i].active ? "" : "not "));
2010 printf("::: starting game [%d]\n", FrameCounter);
2014 void InitMovDir(int x, int y)
2016 int i, element = Feld[x][y];
2017 static int xy[4][2] =
2024 static int direction[3][4] =
2026 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2027 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2028 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2037 Feld[x][y] = EL_BUG;
2038 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2041 case EL_SPACESHIP_RIGHT:
2042 case EL_SPACESHIP_UP:
2043 case EL_SPACESHIP_LEFT:
2044 case EL_SPACESHIP_DOWN:
2045 Feld[x][y] = EL_SPACESHIP;
2046 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2049 case EL_BD_BUTTERFLY_RIGHT:
2050 case EL_BD_BUTTERFLY_UP:
2051 case EL_BD_BUTTERFLY_LEFT:
2052 case EL_BD_BUTTERFLY_DOWN:
2053 Feld[x][y] = EL_BD_BUTTERFLY;
2054 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2057 case EL_BD_FIREFLY_RIGHT:
2058 case EL_BD_FIREFLY_UP:
2059 case EL_BD_FIREFLY_LEFT:
2060 case EL_BD_FIREFLY_DOWN:
2061 Feld[x][y] = EL_BD_FIREFLY;
2062 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2065 case EL_PACMAN_RIGHT:
2067 case EL_PACMAN_LEFT:
2068 case EL_PACMAN_DOWN:
2069 Feld[x][y] = EL_PACMAN;
2070 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2073 case EL_SP_SNIKSNAK:
2074 MovDir[x][y] = MV_UP;
2077 case EL_SP_ELECTRON:
2078 MovDir[x][y] = MV_LEFT;
2085 Feld[x][y] = EL_MOLE;
2086 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2090 if (IS_CUSTOM_ELEMENT(element))
2092 struct ElementInfo *ei = &element_info[element];
2093 int move_direction_initial = ei->move_direction_initial;
2094 int move_pattern = ei->move_pattern;
2096 if (move_direction_initial == MV_START_PREVIOUS)
2098 if (MovDir[x][y] != MV_NO_MOVING)
2101 move_direction_initial = MV_START_AUTOMATIC;
2104 if (move_direction_initial == MV_START_RANDOM)
2105 MovDir[x][y] = 1 << RND(4);
2106 else if (move_direction_initial & MV_ANY_DIRECTION)
2107 MovDir[x][y] = move_direction_initial;
2108 else if (move_pattern == MV_ALL_DIRECTIONS ||
2109 move_pattern == MV_TURNING_LEFT ||
2110 move_pattern == MV_TURNING_RIGHT ||
2111 move_pattern == MV_TURNING_LEFT_RIGHT ||
2112 move_pattern == MV_TURNING_RIGHT_LEFT ||
2113 move_pattern == MV_TURNING_RANDOM)
2114 MovDir[x][y] = 1 << RND(4);
2115 else if (move_pattern == MV_HORIZONTAL)
2116 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2117 else if (move_pattern == MV_VERTICAL)
2118 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2119 else if (move_pattern & MV_ANY_DIRECTION)
2120 MovDir[x][y] = element_info[element].move_pattern;
2121 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2122 move_pattern == MV_ALONG_RIGHT_SIDE)
2125 /* use random direction as default start direction */
2126 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2127 MovDir[x][y] = 1 << RND(4);
2130 for (i = 0; i < NUM_DIRECTIONS; i++)
2132 int x1 = x + xy[i][0];
2133 int y1 = y + xy[i][1];
2135 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2137 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2138 MovDir[x][y] = direction[0][i];
2140 MovDir[x][y] = direction[1][i];
2149 MovDir[x][y] = 1 << RND(4);
2151 if (element != EL_BUG &&
2152 element != EL_SPACESHIP &&
2153 element != EL_BD_BUTTERFLY &&
2154 element != EL_BD_FIREFLY)
2157 for (i = 0; i < NUM_DIRECTIONS; i++)
2159 int x1 = x + xy[i][0];
2160 int y1 = y + xy[i][1];
2162 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2164 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2166 MovDir[x][y] = direction[0][i];
2169 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2170 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2172 MovDir[x][y] = direction[1][i];
2181 GfxDir[x][y] = MovDir[x][y];
2184 void InitAmoebaNr(int x, int y)
2187 int group_nr = AmoebeNachbarNr(x, y);
2191 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2193 if (AmoebaCnt[i] == 0)
2201 AmoebaNr[x][y] = group_nr;
2202 AmoebaCnt[group_nr]++;
2203 AmoebaCnt2[group_nr]++;
2209 boolean raise_level = FALSE;
2211 if (local_player->MovPos)
2215 if (tape.auto_play) /* tape might already be stopped here */
2216 tape.auto_play_level_solved = TRUE;
2218 if (tape.playing && tape.auto_play)
2219 tape.auto_play_level_solved = TRUE;
2222 local_player->LevelSolved = FALSE;
2224 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2228 if (!tape.playing && setup.sound_loops)
2229 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2230 SND_CTRL_PLAY_LOOP);
2232 while (TimeLeft > 0)
2234 if (!tape.playing && !setup.sound_loops)
2235 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2236 if (TimeLeft > 0 && !(TimeLeft % 10))
2237 RaiseScore(level.score[SC_TIME_BONUS]);
2238 if (TimeLeft > 100 && !(TimeLeft % 10))
2243 DrawGameValue_Time(TimeLeft);
2251 if (!tape.playing && setup.sound_loops)
2252 StopSound(SND_GAME_LEVELTIME_BONUS);
2254 else if (level.time == 0) /* level without time limit */
2256 if (!tape.playing && setup.sound_loops)
2257 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2258 SND_CTRL_PLAY_LOOP);
2260 while (TimePlayed < 999)
2262 if (!tape.playing && !setup.sound_loops)
2263 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2264 if (TimePlayed < 999 && !(TimePlayed % 10))
2265 RaiseScore(level.score[SC_TIME_BONUS]);
2266 if (TimePlayed < 900 && !(TimePlayed % 10))
2271 DrawGameValue_Time(TimePlayed);
2279 if (!tape.playing && setup.sound_loops)
2280 StopSound(SND_GAME_LEVELTIME_BONUS);
2283 /* close exit door after last player */
2284 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2285 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2287 int element = Feld[ExitX][ExitY];
2289 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2290 EL_SP_EXIT_CLOSING);
2292 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2295 /* Hero disappears */
2296 DrawLevelField(ExitX, ExitY);
2302 CloseDoor(DOOR_CLOSE_1);
2307 SaveTape(tape.level_nr); /* Ask to save tape */
2310 if (level_nr == leveldir_current->handicap_level)
2312 leveldir_current->handicap_level++;
2313 SaveLevelSetup_SeriesInfo();
2316 if (level_editor_test_game)
2317 local_player->score = -1; /* no highscore when playing from editor */
2318 else if (level_nr < leveldir_current->last_level)
2319 raise_level = TRUE; /* advance to next level */
2321 if ((hi_pos = NewHiScore()) >= 0)
2323 game_status = GAME_MODE_SCORES;
2324 DrawHallOfFame(hi_pos);
2333 game_status = GAME_MODE_MAIN;
2350 LoadScore(level_nr);
2352 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2353 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2356 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2358 if (local_player->score > highscore[k].Score)
2360 /* player has made it to the hall of fame */
2362 if (k < MAX_SCORE_ENTRIES - 1)
2364 int m = MAX_SCORE_ENTRIES - 1;
2367 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2368 if (!strcmp(setup.player_name, highscore[l].Name))
2370 if (m == k) /* player's new highscore overwrites his old one */
2374 for (l = m; l > k; l--)
2376 strcpy(highscore[l].Name, highscore[l - 1].Name);
2377 highscore[l].Score = highscore[l - 1].Score;
2384 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2385 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2386 highscore[k].Score = local_player->score;
2392 else if (!strncmp(setup.player_name, highscore[k].Name,
2393 MAX_PLAYER_NAME_LEN))
2394 break; /* player already there with a higher score */
2400 SaveScore(level_nr);
2405 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2407 if (player->GfxAction != action || player->GfxDir != dir)
2410 printf("Player frame reset! (%d => %d, %d => %d)\n",
2411 player->GfxAction, action, player->GfxDir, dir);
2414 player->GfxAction = action;
2415 player->GfxDir = dir;
2417 player->StepFrame = 0;
2421 static void ResetRandomAnimationValue(int x, int y)
2423 GfxRandom[x][y] = INIT_GFX_RANDOM();
2426 static void ResetGfxAnimation(int x, int y)
2429 GfxAction[x][y] = ACTION_DEFAULT;
2430 GfxDir[x][y] = MovDir[x][y];
2433 void InitMovingField(int x, int y, int direction)
2435 int element = Feld[x][y];
2436 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2437 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2441 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2442 ResetGfxAnimation(x, y);
2444 MovDir[newx][newy] = MovDir[x][y] = direction;
2445 GfxDir[x][y] = direction;
2447 if (Feld[newx][newy] == EL_EMPTY)
2448 Feld[newx][newy] = EL_BLOCKED;
2450 if (direction == MV_DOWN && CAN_FALL(element))
2451 GfxAction[x][y] = ACTION_FALLING;
2453 GfxAction[x][y] = ACTION_MOVING;
2455 GfxFrame[newx][newy] = GfxFrame[x][y];
2456 GfxRandom[newx][newy] = GfxRandom[x][y];
2457 GfxAction[newx][newy] = GfxAction[x][y];
2458 GfxDir[newx][newy] = GfxDir[x][y];
2461 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2463 int direction = MovDir[x][y];
2464 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2465 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2471 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2473 int oldx = x, oldy = y;
2474 int direction = MovDir[x][y];
2476 if (direction == MV_LEFT)
2478 else if (direction == MV_RIGHT)
2480 else if (direction == MV_UP)
2482 else if (direction == MV_DOWN)
2485 *comes_from_x = oldx;
2486 *comes_from_y = oldy;
2489 int MovingOrBlocked2Element(int x, int y)
2491 int element = Feld[x][y];
2493 if (element == EL_BLOCKED)
2497 Blocked2Moving(x, y, &oldx, &oldy);
2498 return Feld[oldx][oldy];
2504 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2506 /* like MovingOrBlocked2Element(), but if element is moving
2507 and (x,y) is the field the moving element is just leaving,
2508 return EL_BLOCKED instead of the element value */
2509 int element = Feld[x][y];
2511 if (IS_MOVING(x, y))
2513 if (element == EL_BLOCKED)
2517 Blocked2Moving(x, y, &oldx, &oldy);
2518 return Feld[oldx][oldy];
2527 static void RemoveField(int x, int y)
2529 Feld[x][y] = EL_EMPTY;
2536 ChangeDelay[x][y] = 0;
2537 ChangePage[x][y] = -1;
2538 Pushed[x][y] = FALSE;
2541 ExplodeField[x][y] = EX_TYPE_NONE;
2544 GfxElement[x][y] = EL_UNDEFINED;
2545 GfxAction[x][y] = ACTION_DEFAULT;
2546 GfxDir[x][y] = MV_NO_MOVING;
2549 void RemoveMovingField(int x, int y)
2551 int oldx = x, oldy = y, newx = x, newy = y;
2552 int element = Feld[x][y];
2553 int next_element = EL_UNDEFINED;
2555 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2558 if (IS_MOVING(x, y))
2560 Moving2Blocked(x, y, &newx, &newy);
2562 if (Feld[newx][newy] != EL_BLOCKED)
2565 if (Feld[newx][newy] != EL_BLOCKED)
2567 /* element is moving, but target field is not free (blocked), but
2568 already occupied by something different (example: acid pool);
2569 in this case, only remove the moving field, but not the target */
2571 RemoveField(oldx, oldy);
2573 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2575 DrawLevelField(oldx, oldy);
2581 else if (element == EL_BLOCKED)
2583 Blocked2Moving(x, y, &oldx, &oldy);
2584 if (!IS_MOVING(oldx, oldy))
2588 if (element == EL_BLOCKED &&
2589 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2590 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2591 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2592 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2593 next_element = get_next_element(Feld[oldx][oldy]);
2595 RemoveField(oldx, oldy);
2596 RemoveField(newx, newy);
2598 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2600 if (next_element != EL_UNDEFINED)
2601 Feld[oldx][oldy] = next_element;
2603 DrawLevelField(oldx, oldy);
2604 DrawLevelField(newx, newy);
2607 void DrawDynamite(int x, int y)
2609 int sx = SCREENX(x), sy = SCREENY(y);
2610 int graphic = el2img(Feld[x][y]);
2613 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2616 if (IS_WALKABLE_INSIDE(Back[x][y]))
2620 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2621 else if (Store[x][y])
2622 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2624 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2627 if (Back[x][y] || Store[x][y])
2628 DrawGraphicThruMask(sx, sy, graphic, frame);
2630 DrawGraphic(sx, sy, graphic, frame);
2632 if (game.emulation == EMU_SUPAPLEX)
2633 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2634 else if (Store[x][y])
2635 DrawGraphicThruMask(sx, sy, graphic, frame);
2637 DrawGraphic(sx, sy, graphic, frame);
2641 void CheckDynamite(int x, int y)
2643 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2647 if (MovDelay[x][y] != 0)
2650 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2657 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2659 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2660 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2661 StopSound(SND_DYNAMITE_ACTIVE);
2663 StopSound(SND_DYNABOMB_ACTIVE);
2669 void DrawRelocatePlayer(struct PlayerInfo *player)
2671 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2672 boolean no_delay = (tape.warp_forward);
2673 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2674 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2675 int jx = player->jx;
2676 int jy = player->jy;
2678 if (level.instant_relocation)
2681 int offset = (setup.scroll_delay ? 3 : 0);
2683 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2685 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2686 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2687 local_player->jx - MIDPOSX);
2689 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2690 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2691 local_player->jy - MIDPOSY);
2695 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2696 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2697 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2699 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2700 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2701 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2703 /* don't scroll over playfield boundaries */
2704 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2705 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2707 /* don't scroll over playfield boundaries */
2708 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2709 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2712 scroll_x += (local_player->jx - old_jx);
2713 scroll_y += (local_player->jy - old_jy);
2715 /* don't scroll over playfield boundaries */
2716 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2717 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2719 /* don't scroll over playfield boundaries */
2720 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2721 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2724 RedrawPlayfield(TRUE, 0,0,0,0);
2730 int offset = (setup.scroll_delay ? 3 : 0);
2732 int scroll_xx = -999, scroll_yy = -999;
2734 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2736 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2739 int fx = FX, fy = FY;
2741 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2742 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2743 local_player->jx - MIDPOSX);
2745 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2746 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2747 local_player->jy - MIDPOSY);
2749 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2750 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2753 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2756 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2763 fx += dx * TILEX / 2;
2764 fy += dy * TILEY / 2;
2766 ScrollLevel(dx, dy);
2769 /* scroll in two steps of half tile size to make things smoother */
2770 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2772 Delay(wait_delay_value);
2774 /* scroll second step to align at full tile size */
2776 Delay(wait_delay_value);
2779 int scroll_xx = -999, scroll_yy = -999;
2781 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2783 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2786 int fx = FX, fy = FY;
2788 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2789 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2790 local_player->jx - MIDPOSX);
2792 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2793 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2794 local_player->jy - MIDPOSY);
2796 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2797 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2800 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2803 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2810 fx += dx * TILEX / 2;
2811 fy += dy * TILEY / 2;
2813 ScrollLevel(dx, dy);
2816 /* scroll in two steps of half tile size to make things smoother */
2817 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2819 Delay(wait_delay_value);
2821 /* scroll second step to align at full tile size */
2823 Delay(wait_delay_value);
2829 Delay(wait_delay_value);
2833 void RelocatePlayer(int jx, int jy, int el_player_raw)
2836 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
2838 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
2840 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2841 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2842 boolean no_delay = (tape.warp_forward);
2843 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2844 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2845 int old_jx = player->jx;
2846 int old_jy = player->jy;
2847 int old_element = Feld[old_jx][old_jy];
2848 int element = Feld[jx][jy];
2849 boolean player_relocated = (old_jx != jx || old_jy != jy);
2851 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2852 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2854 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2855 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2856 int leave_side_horiz = move_dir_horiz;
2857 int leave_side_vert = move_dir_vert;
2859 static int trigger_sides[4][2] =
2861 /* enter side leave side */
2862 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
2863 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
2864 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
2865 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
2867 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
2868 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
2869 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
2870 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
2872 int enter_side = enter_side_horiz | enter_side_vert;
2873 int leave_side = leave_side_horiz | leave_side_vert;
2875 if (player->GameOver) /* do not reanimate dead player */
2878 if (!player_relocated) /* no need to relocate the player */
2881 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2883 RemoveField(jx, jy); /* temporarily remove newly placed player */
2884 DrawLevelField(jx, jy);
2887 if (player->present)
2889 while (player->MovPos)
2891 ScrollPlayer(player, SCROLL_GO_ON);
2892 ScrollScreen(NULL, SCROLL_GO_ON);
2898 Delay(wait_delay_value);
2901 DrawPlayer(player); /* needed here only to cleanup last field */
2902 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2904 player->is_moving = FALSE;
2908 if (IS_CUSTOM_ELEMENT(old_element))
2909 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
2911 player->index_bit, leave_side);
2913 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
2915 player->index_bit, leave_side);
2918 Feld[jx][jy] = el_player;
2919 InitPlayerField(jx, jy, el_player, TRUE);
2921 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
2923 Feld[jx][jy] = element;
2924 InitField(jx, jy, FALSE);
2928 if (player == local_player) /* only visually relocate local player */
2929 DrawRelocatePlayer(player);
2933 TestIfHeroTouchesBadThing(jx, jy);
2934 TestIfPlayerTouchesCustomElement(jx, jy);
2938 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
2943 /* needed to allow change of walkable custom element by entering player */
2944 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
2945 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
2947 /* needed to allow change of walkable custom element by entering player */
2948 Changed[jx][jy] = 0; /* allow another change */
2953 printf("::: player entering %d, %d from %s ...\n", jx, jy,
2954 enter_side == MV_LEFT ? "left" :
2955 enter_side == MV_RIGHT ? "right" :
2956 enter_side == MV_UP ? "top" :
2957 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
2961 if (IS_CUSTOM_ELEMENT(element))
2962 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
2963 player->index_bit, enter_side);
2965 CheckTriggeredElementChangeByPlayer(jx, jy, element,
2966 CE_OTHER_GETS_ENTERED,
2967 player->index_bit, enter_side);
2971 void Explode(int ex, int ey, int phase, int mode)
2978 /* !!! eliminate this variable !!! */
2979 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2984 int last_phase = num_phase * delay;
2985 int half_phase = (num_phase / 2) * delay;
2986 int first_phase_after_start = EX_PHASE_START + 1;
2990 if (game.explosions_delayed)
2992 ExplodeField[ex][ey] = mode;
2996 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2998 int center_element = Feld[ex][ey];
3001 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3005 /* --- This is only really needed (and now handled) in "Impact()". --- */
3006 /* do not explode moving elements that left the explode field in time */
3007 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3008 center_element == EL_EMPTY &&
3009 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3013 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3014 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3016 /* remove things displayed in background while burning dynamite */
3017 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3020 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3022 /* put moving element to center field (and let it explode there) */
3023 center_element = MovingOrBlocked2Element(ex, ey);
3024 RemoveMovingField(ex, ey);
3025 Feld[ex][ey] = center_element;
3031 last_phase = element_info[center_element].explosion_delay + 1;
3033 last_phase = element_info[center_element].explosion_delay;
3037 printf("::: %d -> %d\n", center_element, last_phase);
3041 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3043 int xx = x - ex + 1;
3044 int yy = y - ey + 1;
3049 if (!IN_LEV_FIELD(x, y) ||
3050 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3051 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3054 if (!IN_LEV_FIELD(x, y) ||
3055 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3059 if (!IN_LEV_FIELD(x, y) ||
3060 ((mode != EX_TYPE_NORMAL ||
3061 center_element == EL_AMOEBA_TO_DIAMOND) &&
3062 (x != ex || y != ey)))
3066 element = Feld[x][y];
3068 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3070 element = MovingOrBlocked2Element(x, y);
3072 if (!IS_EXPLOSION_PROOF(element))
3073 RemoveMovingField(x, y);
3079 if (IS_EXPLOSION_PROOF(element))
3082 /* indestructible elements can only explode in center (but not flames) */
3084 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3085 mode == EX_TYPE_BORDER)) ||
3086 element == EL_FLAMES)
3089 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3090 element == EL_FLAMES)
3096 if ((IS_INDESTRUCTIBLE(element) &&
3097 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3098 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3099 element == EL_FLAMES)
3104 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3105 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3106 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3108 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3111 if (IS_ACTIVE_BOMB(element))
3113 /* re-activate things under the bomb like gate or penguin */
3115 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3118 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3123 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3124 element_info[Feld[x][y]].token_name,
3125 Store[x][y], Store2[x][y]);
3132 /* save walkable background elements while explosion on same tile */
3134 if (IS_INDESTRUCTIBLE(element))
3135 Back[x][y] = element;
3139 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3140 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3141 Back[x][y] = element;
3143 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3144 (x != ex || y != ey))
3145 Back[x][y] = element;
3148 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3149 Back[x][y] = element;
3153 /* ignite explodable elements reached by other explosion */
3154 if (element == EL_EXPLOSION)
3155 element = Store2[x][y];
3158 if (AmoebaNr[x][y] &&
3159 (element == EL_AMOEBA_FULL ||
3160 element == EL_BD_AMOEBA ||
3161 element == EL_AMOEBA_GROWING))
3163 AmoebaCnt[AmoebaNr[x][y]]--;
3164 AmoebaCnt2[AmoebaNr[x][y]]--;
3170 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3172 switch(StorePlayer[ex][ey])
3175 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3178 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3181 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3185 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3190 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3191 Store[x][y] = EL_EMPTY;
3193 if (game.emulation == EMU_SUPAPLEX)
3194 Store[x][y] = EL_EMPTY;
3197 else if (center_element == EL_MOLE)
3198 Store[x][y] = EL_EMERALD_RED;
3199 else if (center_element == EL_PENGUIN)
3200 Store[x][y] = EL_EMERALD_PURPLE;
3201 else if (center_element == EL_BUG)
3202 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3203 else if (center_element == EL_BD_BUTTERFLY)
3204 Store[x][y] = EL_BD_DIAMOND;
3205 else if (center_element == EL_SP_ELECTRON)
3206 Store[x][y] = EL_SP_INFOTRON;
3207 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3208 Store[x][y] = level.amoeba_content;
3209 else if (center_element == EL_YAMYAM)
3210 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3211 else if (IS_CUSTOM_ELEMENT(center_element) &&
3212 element_info[center_element].content[xx][yy] != EL_EMPTY)
3213 Store[x][y] = element_info[center_element].content[xx][yy];
3214 else if (element == EL_WALL_EMERALD)
3215 Store[x][y] = EL_EMERALD;
3216 else if (element == EL_WALL_DIAMOND)
3217 Store[x][y] = EL_DIAMOND;
3218 else if (element == EL_WALL_BD_DIAMOND)
3219 Store[x][y] = EL_BD_DIAMOND;
3220 else if (element == EL_WALL_EMERALD_YELLOW)
3221 Store[x][y] = EL_EMERALD_YELLOW;
3222 else if (element == EL_WALL_EMERALD_RED)
3223 Store[x][y] = EL_EMERALD_RED;
3224 else if (element == EL_WALL_EMERALD_PURPLE)
3225 Store[x][y] = EL_EMERALD_PURPLE;
3226 else if (element == EL_WALL_PEARL)
3227 Store[x][y] = EL_PEARL;
3228 else if (element == EL_WALL_CRYSTAL)
3229 Store[x][y] = EL_CRYSTAL;
3230 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3231 Store[x][y] = element_info[element].content[1][1];
3233 Store[x][y] = EL_EMPTY;
3235 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3236 center_element == EL_AMOEBA_TO_DIAMOND)
3237 Store2[x][y] = element;
3240 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3241 element_info[Store2[x][y]].token_name);
3245 if (AmoebaNr[x][y] &&
3246 (element == EL_AMOEBA_FULL ||
3247 element == EL_BD_AMOEBA ||
3248 element == EL_AMOEBA_GROWING))
3250 AmoebaCnt[AmoebaNr[x][y]]--;
3251 AmoebaCnt2[AmoebaNr[x][y]]--;
3257 MovDir[x][y] = MovPos[x][y] = 0;
3258 GfxDir[x][y] = MovDir[x][y];
3263 Feld[x][y] = EL_EXPLOSION;
3265 GfxElement[x][y] = center_element;
3267 GfxElement[x][y] = EL_UNDEFINED;
3270 ExplodePhase[x][y] = 1;
3272 ExplodeDelay[x][y] = last_phase;
3277 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3279 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3286 if (center_element == EL_YAMYAM)
3287 game.yamyam_content_nr =
3288 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3291 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3292 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3306 GfxFrame[x][y] = 0; /* restart explosion animation */
3310 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3314 last_phase = ExplodeDelay[x][y];
3317 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3321 /* activate this even in non-DEBUG version until cause for crash in
3322 getGraphicAnimationFrame() (see below) is found and eliminated */
3326 if (GfxElement[x][y] == EL_UNDEFINED)
3329 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3330 printf("Explode(): This should never happen!\n");
3333 GfxElement[x][y] = EL_EMPTY;
3339 border_element = Store2[x][y];
3341 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3342 border_element = StorePlayer[x][y];
3344 if (IS_PLAYER(x, y))
3345 border_element = StorePlayer[x][y];
3349 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3350 element_info[border_element].token_name, Store2[x][y]);
3354 printf("::: phase == %d\n", phase);
3357 if (phase == element_info[border_element].ignition_delay ||
3358 phase == last_phase)
3360 boolean border_explosion = FALSE;
3364 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3365 !PLAYER_EXPLOSION_PROTECTED(x, y))
3367 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3370 if (IS_PLAYER(x, y))
3373 KillHeroUnlessExplosionProtected(x, y);
3374 border_explosion = TRUE;
3377 if (phase == last_phase)
3378 printf("::: IS_PLAYER\n");
3381 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3384 printf("::: %d,%d: %d %s\n", x, y, border_element,
3385 element_info[border_element].token_name);
3388 Feld[x][y] = Store2[x][y];
3391 border_explosion = TRUE;
3394 if (phase == last_phase)
3395 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3398 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3400 AmoebeUmwandeln(x, y);
3402 border_explosion = TRUE;
3405 if (phase == last_phase)
3406 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3407 element_info[border_element].explosion_delay,
3408 element_info[border_element].ignition_delay,
3414 /* if an element just explodes due to another explosion (chain-reaction),
3415 do not immediately end the new explosion when it was the last frame of
3416 the explosion (as it would be done in the following "if"-statement!) */
3417 if (border_explosion && phase == last_phase)
3424 if (phase == first_phase_after_start)
3426 int element = Store2[x][y];
3428 if (element == EL_BLACK_ORB)
3430 Feld[x][y] = Store2[x][y];
3435 else if (phase == half_phase)
3437 int element = Store2[x][y];
3439 if (IS_PLAYER(x, y))
3440 KillHeroUnlessExplosionProtected(x, y);
3441 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3443 Feld[x][y] = Store2[x][y];
3447 else if (element == EL_AMOEBA_TO_DIAMOND)
3448 AmoebeUmwandeln(x, y);
3452 if (phase == last_phase)
3457 printf("::: done: phase == %d\n", phase);
3461 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3464 element = Feld[x][y] = Store[x][y];
3465 Store[x][y] = Store2[x][y] = 0;
3466 GfxElement[x][y] = EL_UNDEFINED;
3468 /* player can escape from explosions and might therefore be still alive */
3469 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3470 element <= EL_PLAYER_IS_EXPLODING_4)
3471 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3473 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3474 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3475 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3478 /* restore probably existing indestructible background element */
3479 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3480 element = Feld[x][y] = Back[x][y];
3483 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3484 GfxDir[x][y] = MV_NO_MOVING;
3485 ChangeDelay[x][y] = 0;
3486 ChangePage[x][y] = -1;
3489 InitField_WithBug2(x, y, FALSE);
3491 InitField(x, y, FALSE);
3493 /* !!! not needed !!! */
3495 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3496 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3499 if (CAN_MOVE(element))
3504 DrawLevelField(x, y);
3506 TestIfElementTouchesCustomElement(x, y);
3508 if (GFX_CRUMBLED(element))
3509 DrawLevelFieldCrumbledSandNeighbours(x, y);
3511 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3512 StorePlayer[x][y] = 0;
3514 if (ELEM_IS_PLAYER(element))
3515 RelocatePlayer(x, y, element);
3518 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3520 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3524 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3526 int stored = Store[x][y];
3527 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3528 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3532 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3534 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3538 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3542 printf("::: %d / %d [%d - %d]\n",
3543 GfxFrame[x][y], phase - delay, phase, delay);
3547 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3548 element_info[GfxElement[x][y]].token_name,
3553 DrawLevelFieldCrumbledSand(x, y);
3555 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3557 DrawLevelElement(x, y, Back[x][y]);
3558 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3560 else if (IS_WALKABLE_UNDER(Back[x][y]))
3562 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3563 DrawLevelElementThruMask(x, y, Back[x][y]);
3565 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3566 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3570 void DynaExplode(int ex, int ey)
3573 int dynabomb_element = Feld[ex][ey];
3574 int dynabomb_size = 1;
3575 boolean dynabomb_xl = FALSE;
3576 struct PlayerInfo *player;
3577 static int xy[4][2] =
3585 if (IS_ACTIVE_BOMB(dynabomb_element))
3587 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3588 dynabomb_size = player->dynabomb_size;
3589 dynabomb_xl = player->dynabomb_xl;
3590 player->dynabombs_left++;
3593 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3595 for (i = 0; i < NUM_DIRECTIONS; i++)
3597 for (j = 1; j <= dynabomb_size; j++)
3599 int x = ex + j * xy[i][0];
3600 int y = ey + j * xy[i][1];
3603 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3606 element = Feld[x][y];
3608 /* do not restart explosions of fields with active bombs */
3609 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3612 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3616 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3617 !IS_DIGGABLE(element) && !dynabomb_xl)
3620 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3621 !CAN_GROW_INTO(element) && !dynabomb_xl)
3625 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3626 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3627 element != EL_SAND && !dynabomb_xl)
3634 void Bang(int x, int y)
3637 int element = MovingOrBlocked2Element(x, y);
3639 int element = Feld[x][y];
3643 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3645 if (IS_PLAYER(x, y))
3648 struct PlayerInfo *player = PLAYERINFO(x, y);
3650 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3651 player->element_nr);
3656 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3658 if (game.emulation == EMU_SUPAPLEX)
3659 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3661 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3666 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3674 case EL_BD_BUTTERFLY:
3677 case EL_DARK_YAMYAM:
3681 RaiseScoreElement(element);
3682 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3684 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3685 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3686 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3687 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3688 case EL_DYNABOMB_INCREASE_NUMBER:
3689 case EL_DYNABOMB_INCREASE_SIZE:
3690 case EL_DYNABOMB_INCREASE_POWER:
3695 case EL_LAMP_ACTIVE:
3697 case EL_AMOEBA_TO_DIAMOND:
3699 if (IS_PLAYER(x, y))
3700 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3702 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3706 if (element_info[element].explosion_type == EXPLODES_CROSS)
3708 if (CAN_EXPLODE_CROSS(element))
3711 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3716 else if (element_info[element].explosion_type == EXPLODES_1X1)
3718 else if (CAN_EXPLODE_1X1(element))
3720 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3722 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3726 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3729 void SplashAcid(int x, int y)
3732 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3733 (!IN_LEV_FIELD(x - 1, y - 2) ||
3734 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3735 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3737 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3738 (!IN_LEV_FIELD(x + 1, y - 2) ||
3739 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3740 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3742 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3744 /* input: position of element entering acid (obsolete) */
3746 int element = Feld[x][y];
3748 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3751 if (element != EL_ACID_SPLASH_LEFT &&
3752 element != EL_ACID_SPLASH_RIGHT)
3754 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3756 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3757 (!IN_LEV_FIELD(x - 1, y - 1) ||
3758 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3759 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3761 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3762 (!IN_LEV_FIELD(x + 1, y - 1) ||
3763 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3764 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3769 static void InitBeltMovement()
3771 static int belt_base_element[4] =
3773 EL_CONVEYOR_BELT_1_LEFT,
3774 EL_CONVEYOR_BELT_2_LEFT,
3775 EL_CONVEYOR_BELT_3_LEFT,
3776 EL_CONVEYOR_BELT_4_LEFT
3778 static int belt_base_active_element[4] =
3780 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3781 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3782 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3783 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3788 /* set frame order for belt animation graphic according to belt direction */
3789 for (i = 0; i < NUM_BELTS; i++)
3793 for (j = 0; j < NUM_BELT_PARTS; j++)
3795 int element = belt_base_active_element[belt_nr] + j;
3796 int graphic = el2img(element);
3798 if (game.belt_dir[i] == MV_LEFT)
3799 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3801 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3805 for (y = 0; y < lev_fieldy; y++)
3807 for (x = 0; x < lev_fieldx; x++)
3809 int element = Feld[x][y];
3811 for (i = 0; i < NUM_BELTS; i++)
3813 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3815 int e_belt_nr = getBeltNrFromBeltElement(element);
3818 if (e_belt_nr == belt_nr)
3820 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3822 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3830 static void ToggleBeltSwitch(int x, int y)
3832 static int belt_base_element[4] =
3834 EL_CONVEYOR_BELT_1_LEFT,
3835 EL_CONVEYOR_BELT_2_LEFT,
3836 EL_CONVEYOR_BELT_3_LEFT,
3837 EL_CONVEYOR_BELT_4_LEFT
3839 static int belt_base_active_element[4] =
3841 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3842 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3843 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3844 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3846 static int belt_base_switch_element[4] =
3848 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3849 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3850 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3851 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3853 static int belt_move_dir[4] =
3861 int element = Feld[x][y];
3862 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3863 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3864 int belt_dir = belt_move_dir[belt_dir_nr];
3867 if (!IS_BELT_SWITCH(element))
3870 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3871 game.belt_dir[belt_nr] = belt_dir;
3873 if (belt_dir_nr == 3)
3876 /* set frame order for belt animation graphic according to belt direction */
3877 for (i = 0; i < NUM_BELT_PARTS; i++)
3879 int element = belt_base_active_element[belt_nr] + i;
3880 int graphic = el2img(element);
3882 if (belt_dir == MV_LEFT)
3883 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3885 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3888 for (yy = 0; yy < lev_fieldy; yy++)
3890 for (xx = 0; xx < lev_fieldx; xx++)
3892 int element = Feld[xx][yy];
3894 if (IS_BELT_SWITCH(element))
3896 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3898 if (e_belt_nr == belt_nr)
3900 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3901 DrawLevelField(xx, yy);
3904 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3906 int e_belt_nr = getBeltNrFromBeltElement(element);
3908 if (e_belt_nr == belt_nr)
3910 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3912 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3913 DrawLevelField(xx, yy);
3916 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3918 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3920 if (e_belt_nr == belt_nr)
3922 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3924 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3925 DrawLevelField(xx, yy);
3932 static void ToggleSwitchgateSwitch(int x, int y)
3936 game.switchgate_pos = !game.switchgate_pos;
3938 for (yy = 0; yy < lev_fieldy; yy++)
3940 for (xx = 0; xx < lev_fieldx; xx++)
3942 int element = Feld[xx][yy];
3944 if (element == EL_SWITCHGATE_SWITCH_UP ||
3945 element == EL_SWITCHGATE_SWITCH_DOWN)
3947 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3948 DrawLevelField(xx, yy);
3950 else if (element == EL_SWITCHGATE_OPEN ||
3951 element == EL_SWITCHGATE_OPENING)
3953 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3955 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3957 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3960 else if (element == EL_SWITCHGATE_CLOSED ||
3961 element == EL_SWITCHGATE_CLOSING)
3963 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3965 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3967 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3974 static int getInvisibleActiveFromInvisibleElement(int element)
3976 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3977 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3978 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3982 static int getInvisibleFromInvisibleActiveElement(int element)
3984 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3985 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3986 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3990 static void RedrawAllLightSwitchesAndInvisibleElements()
3994 for (y = 0; y < lev_fieldy; y++)
3996 for (x = 0; x < lev_fieldx; x++)
3998 int element = Feld[x][y];
4000 if (element == EL_LIGHT_SWITCH &&
4001 game.light_time_left > 0)
4003 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4004 DrawLevelField(x, y);
4006 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4007 game.light_time_left == 0)
4009 Feld[x][y] = EL_LIGHT_SWITCH;
4010 DrawLevelField(x, y);
4012 else if (element == EL_INVISIBLE_STEELWALL ||
4013 element == EL_INVISIBLE_WALL ||
4014 element == EL_INVISIBLE_SAND)
4016 if (game.light_time_left > 0)
4017 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4019 DrawLevelField(x, y);
4021 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4022 element == EL_INVISIBLE_WALL_ACTIVE ||
4023 element == EL_INVISIBLE_SAND_ACTIVE)
4025 if (game.light_time_left == 0)
4026 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4028 DrawLevelField(x, y);
4034 static void ToggleLightSwitch(int x, int y)
4036 int element = Feld[x][y];
4038 game.light_time_left =
4039 (element == EL_LIGHT_SWITCH ?
4040 level.time_light * FRAMES_PER_SECOND : 0);
4042 RedrawAllLightSwitchesAndInvisibleElements();
4045 static void ActivateTimegateSwitch(int x, int y)
4049 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4051 for (yy = 0; yy < lev_fieldy; yy++)
4053 for (xx = 0; xx < lev_fieldx; xx++)
4055 int element = Feld[xx][yy];
4057 if (element == EL_TIMEGATE_CLOSED ||
4058 element == EL_TIMEGATE_CLOSING)
4060 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4061 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4065 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4067 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4068 DrawLevelField(xx, yy);
4075 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4078 inline static int getElementMoveStepsize(int x, int y)
4080 int element = Feld[x][y];
4081 int direction = MovDir[x][y];
4082 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4083 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4084 int horiz_move = (dx != 0);
4085 int sign = (horiz_move ? dx : dy);
4086 int step = sign * element_info[element].move_stepsize;
4088 /* special values for move stepsize for spring and things on conveyor belt */
4092 if (element == EL_SPRING)
4093 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4094 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
4095 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4096 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4098 if (CAN_FALL(element) &&
4099 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4100 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4101 else if (element == EL_SPRING)
4102 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4109 void Impact(int x, int y)
4111 boolean lastline = (y == lev_fieldy-1);
4112 boolean object_hit = FALSE;
4113 boolean impact = (lastline || object_hit);
4114 int element = Feld[x][y];
4115 int smashed = EL_STEELWALL;
4117 if (!lastline) /* check if element below was hit */
4119 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4122 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4123 MovDir[x][y + 1] != MV_DOWN ||
4124 MovPos[x][y + 1] <= TILEY / 2));
4127 object_hit = !IS_FREE(x, y + 1);
4130 /* do not smash moving elements that left the smashed field in time */
4131 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4132 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4136 smashed = MovingOrBlocked2Element(x, y + 1);
4138 impact = (lastline || object_hit);
4141 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4143 SplashAcid(x, y + 1);
4147 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4148 /* only reset graphic animation if graphic really changes after impact */
4150 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4152 ResetGfxAnimation(x, y);
4153 DrawLevelField(x, y);
4156 if (impact && CAN_EXPLODE_IMPACT(element))
4161 else if (impact && element == EL_PEARL)
4163 ResetGfxAnimation(x, y);
4165 Feld[x][y] = EL_PEARL_BREAKING;
4166 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4169 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4171 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4176 if (impact && element == EL_AMOEBA_DROP)
4178 if (object_hit && IS_PLAYER(x, y + 1))
4179 KillHeroUnlessEnemyProtected(x, y + 1);
4180 else if (object_hit && smashed == EL_PENGUIN)
4184 Feld[x][y] = EL_AMOEBA_GROWING;
4185 Store[x][y] = EL_AMOEBA_WET;
4187 ResetRandomAnimationValue(x, y);
4192 if (object_hit) /* check which object was hit */
4194 if (CAN_PASS_MAGIC_WALL(element) &&
4195 (smashed == EL_MAGIC_WALL ||
4196 smashed == EL_BD_MAGIC_WALL))
4199 int activated_magic_wall =
4200 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4201 EL_BD_MAGIC_WALL_ACTIVE);
4203 /* activate magic wall / mill */
4204 for (yy = 0; yy < lev_fieldy; yy++)
4205 for (xx = 0; xx < lev_fieldx; xx++)
4206 if (Feld[xx][yy] == smashed)
4207 Feld[xx][yy] = activated_magic_wall;
4209 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4210 game.magic_wall_active = TRUE;
4212 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4213 SND_MAGIC_WALL_ACTIVATING :
4214 SND_BD_MAGIC_WALL_ACTIVATING));
4217 if (IS_PLAYER(x, y + 1))
4219 if (CAN_SMASH_PLAYER(element))
4221 KillHeroUnlessEnemyProtected(x, y + 1);
4225 else if (smashed == EL_PENGUIN)
4227 if (CAN_SMASH_PLAYER(element))
4233 else if (element == EL_BD_DIAMOND)
4235 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4241 else if (((element == EL_SP_INFOTRON ||
4242 element == EL_SP_ZONK) &&
4243 (smashed == EL_SP_SNIKSNAK ||
4244 smashed == EL_SP_ELECTRON ||
4245 smashed == EL_SP_DISK_ORANGE)) ||
4246 (element == EL_SP_INFOTRON &&
4247 smashed == EL_SP_DISK_YELLOW))
4253 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4259 else if (CAN_SMASH_EVERYTHING(element))
4261 if (IS_CLASSIC_ENEMY(smashed) ||
4262 CAN_EXPLODE_SMASHED(smashed))
4267 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4269 if (smashed == EL_LAMP ||
4270 smashed == EL_LAMP_ACTIVE)
4275 else if (smashed == EL_NUT)
4277 Feld[x][y + 1] = EL_NUT_BREAKING;
4278 PlayLevelSound(x, y, SND_NUT_BREAKING);
4279 RaiseScoreElement(EL_NUT);
4282 else if (smashed == EL_PEARL)
4284 ResetGfxAnimation(x, y);
4286 Feld[x][y + 1] = EL_PEARL_BREAKING;
4287 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4290 else if (smashed == EL_DIAMOND)
4292 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4293 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4296 else if (IS_BELT_SWITCH(smashed))
4298 ToggleBeltSwitch(x, y + 1);
4300 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4301 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4303 ToggleSwitchgateSwitch(x, y + 1);
4305 else if (smashed == EL_LIGHT_SWITCH ||
4306 smashed == EL_LIGHT_SWITCH_ACTIVE)
4308 ToggleLightSwitch(x, y + 1);
4313 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4316 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4319 /* !!! TEST ONLY !!! */
4320 CheckElementChangeBySide(x, y + 1, smashed, element,
4321 CE_SWITCHED, CH_SIDE_TOP);
4322 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4323 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4325 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4326 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4327 CheckElementChangeBySide(x, y + 1, smashed, element,
4328 CE_SWITCHED, CH_SIDE_TOP);
4334 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4339 /* play sound of magic wall / mill */
4341 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4342 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4344 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4345 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4346 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4347 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4352 /* play sound of object that hits the ground */
4353 if (lastline || object_hit)
4354 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4357 inline static void TurnRoundExt(int x, int y)
4369 { 0, 0 }, { 0, 0 }, { 0, 0 },
4374 int left, right, back;
4378 { MV_DOWN, MV_UP, MV_RIGHT },
4379 { MV_UP, MV_DOWN, MV_LEFT },
4381 { MV_LEFT, MV_RIGHT, MV_DOWN },
4385 { MV_RIGHT, MV_LEFT, MV_UP }
4388 int element = Feld[x][y];
4389 int move_pattern = element_info[element].move_pattern;
4391 int old_move_dir = MovDir[x][y];
4392 int left_dir = turn[old_move_dir].left;
4393 int right_dir = turn[old_move_dir].right;
4394 int back_dir = turn[old_move_dir].back;
4396 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4397 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4398 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4399 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4401 int left_x = x + left_dx, left_y = y + left_dy;
4402 int right_x = x + right_dx, right_y = y + right_dy;
4403 int move_x = x + move_dx, move_y = y + move_dy;
4407 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4409 TestIfBadThingTouchesOtherBadThing(x, y);
4411 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4412 MovDir[x][y] = right_dir;
4413 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4414 MovDir[x][y] = left_dir;
4416 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4418 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4422 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4423 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4425 TestIfBadThingTouchesOtherBadThing(x, y);
4427 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4428 MovDir[x][y] = left_dir;
4429 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4430 MovDir[x][y] = right_dir;
4432 if ((element == EL_SPACESHIP ||
4433 element == EL_SP_SNIKSNAK ||
4434 element == EL_SP_ELECTRON)
4435 && MovDir[x][y] != old_move_dir)
4437 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4441 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4443 TestIfBadThingTouchesOtherBadThing(x, y);
4445 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4446 MovDir[x][y] = left_dir;
4447 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4448 MovDir[x][y] = right_dir;
4450 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4452 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4455 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4457 TestIfBadThingTouchesOtherBadThing(x, y);
4459 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4460 MovDir[x][y] = left_dir;
4461 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4462 MovDir[x][y] = right_dir;
4464 if (MovDir[x][y] != old_move_dir)
4468 else if (element == EL_YAMYAM)
4470 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4471 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4473 if (can_turn_left && can_turn_right)
4474 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4475 else if (can_turn_left)
4476 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4477 else if (can_turn_right)
4478 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4480 MovDir[x][y] = back_dir;
4482 MovDelay[x][y] = 16 + 16 * RND(3);
4484 else if (element == EL_DARK_YAMYAM)
4486 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4488 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4491 if (can_turn_left && can_turn_right)
4492 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4493 else if (can_turn_left)
4494 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4495 else if (can_turn_right)
4496 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4498 MovDir[x][y] = back_dir;
4500 MovDelay[x][y] = 16 + 16 * RND(3);
4502 else if (element == EL_PACMAN)
4504 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4505 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4507 if (can_turn_left && can_turn_right)
4508 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4509 else if (can_turn_left)
4510 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4511 else if (can_turn_right)
4512 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4514 MovDir[x][y] = back_dir;
4516 MovDelay[x][y] = 6 + RND(40);
4518 else if (element == EL_PIG)
4520 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4521 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4522 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4523 boolean should_turn_left, should_turn_right, should_move_on;
4525 int rnd = RND(rnd_value);
4527 should_turn_left = (can_turn_left &&
4529 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4530 y + back_dy + left_dy)));
4531 should_turn_right = (can_turn_right &&
4533 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4534 y + back_dy + right_dy)));
4535 should_move_on = (can_move_on &&
4538 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4539 y + move_dy + left_dy) ||
4540 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4541 y + move_dy + right_dy)));
4543 if (should_turn_left || should_turn_right || should_move_on)
4545 if (should_turn_left && should_turn_right && should_move_on)
4546 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4547 rnd < 2 * rnd_value / 3 ? right_dir :
4549 else if (should_turn_left && should_turn_right)
4550 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4551 else if (should_turn_left && should_move_on)
4552 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4553 else if (should_turn_right && should_move_on)
4554 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4555 else if (should_turn_left)
4556 MovDir[x][y] = left_dir;
4557 else if (should_turn_right)
4558 MovDir[x][y] = right_dir;
4559 else if (should_move_on)
4560 MovDir[x][y] = old_move_dir;
4562 else if (can_move_on && rnd > rnd_value / 8)
4563 MovDir[x][y] = old_move_dir;
4564 else if (can_turn_left && can_turn_right)
4565 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4566 else if (can_turn_left && rnd > rnd_value / 8)
4567 MovDir[x][y] = left_dir;
4568 else if (can_turn_right && rnd > rnd_value/8)
4569 MovDir[x][y] = right_dir;
4571 MovDir[x][y] = back_dir;
4573 xx = x + move_xy[MovDir[x][y]].x;
4574 yy = y + move_xy[MovDir[x][y]].y;
4576 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4577 MovDir[x][y] = old_move_dir;
4581 else if (element == EL_DRAGON)
4583 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4584 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4585 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4587 int rnd = RND(rnd_value);
4590 if (FrameCounter < 1 && x == 0 && y == 29)
4591 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4594 if (can_move_on && rnd > rnd_value / 8)
4595 MovDir[x][y] = old_move_dir;
4596 else if (can_turn_left && can_turn_right)
4597 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4598 else if (can_turn_left && rnd > rnd_value / 8)
4599 MovDir[x][y] = left_dir;
4600 else if (can_turn_right && rnd > rnd_value / 8)
4601 MovDir[x][y] = right_dir;
4603 MovDir[x][y] = back_dir;
4605 xx = x + move_xy[MovDir[x][y]].x;
4606 yy = y + move_xy[MovDir[x][y]].y;
4609 if (FrameCounter < 1 && x == 0 && y == 29)
4610 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4611 xx, yy, Feld[xx][yy],
4616 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4617 MovDir[x][y] = old_move_dir;
4619 if (!IS_FREE(xx, yy))
4620 MovDir[x][y] = old_move_dir;
4624 if (FrameCounter < 1 && x == 0 && y == 29)
4625 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4630 else if (element == EL_MOLE)
4632 boolean can_move_on =
4633 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4634 IS_AMOEBOID(Feld[move_x][move_y]) ||
4635 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4638 boolean can_turn_left =
4639 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4640 IS_AMOEBOID(Feld[left_x][left_y])));
4642 boolean can_turn_right =
4643 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4644 IS_AMOEBOID(Feld[right_x][right_y])));
4646 if (can_turn_left && can_turn_right)
4647 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4648 else if (can_turn_left)
4649 MovDir[x][y] = left_dir;
4651 MovDir[x][y] = right_dir;
4654 if (MovDir[x][y] != old_move_dir)
4657 else if (element == EL_BALLOON)
4659 MovDir[x][y] = game.balloon_dir;
4662 else if (element == EL_SPRING)
4665 if (MovDir[x][y] & MV_HORIZONTAL &&
4666 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4667 MovDir[x][y] = MV_NO_MOVING;
4669 if (MovDir[x][y] & MV_HORIZONTAL &&
4670 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4671 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4672 MovDir[x][y] = MV_NO_MOVING;
4677 else if (element == EL_ROBOT ||
4678 element == EL_SATELLITE ||
4679 element == EL_PENGUIN)
4681 int attr_x = -1, attr_y = -1;
4692 for (i = 0; i < MAX_PLAYERS; i++)
4694 struct PlayerInfo *player = &stored_player[i];
4695 int jx = player->jx, jy = player->jy;
4697 if (!player->active)
4701 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4710 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4711 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4712 game.engine_version < VERSION_IDENT(3,1,0,0)))
4714 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4721 if (element == EL_PENGUIN)
4724 static int xy[4][2] =
4732 for (i = 0; i < NUM_DIRECTIONS; i++)
4734 int ex = x + xy[i][0];
4735 int ey = y + xy[i][1];
4737 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4746 MovDir[x][y] = MV_NO_MOVING;
4748 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4749 else if (attr_x > x)
4750 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4752 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4753 else if (attr_y > y)
4754 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4756 if (element == EL_ROBOT)
4760 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4761 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4762 Moving2Blocked(x, y, &newx, &newy);
4764 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4765 MovDelay[x][y] = 8 + 8 * !RND(3);
4767 MovDelay[x][y] = 16;
4769 else if (element == EL_PENGUIN)
4775 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4777 boolean first_horiz = RND(2);
4778 int new_move_dir = MovDir[x][y];
4781 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4782 Moving2Blocked(x, y, &newx, &newy);
4784 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4788 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4789 Moving2Blocked(x, y, &newx, &newy);
4791 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4794 MovDir[x][y] = old_move_dir;
4798 else /* (element == EL_SATELLITE) */
4804 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4806 boolean first_horiz = RND(2);
4807 int new_move_dir = MovDir[x][y];
4810 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4811 Moving2Blocked(x, y, &newx, &newy);
4813 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4817 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4818 Moving2Blocked(x, y, &newx, &newy);
4820 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4823 MovDir[x][y] = old_move_dir;
4828 else if (move_pattern == MV_TURNING_LEFT ||
4829 move_pattern == MV_TURNING_RIGHT ||
4830 move_pattern == MV_TURNING_LEFT_RIGHT ||
4831 move_pattern == MV_TURNING_RIGHT_LEFT ||
4832 move_pattern == MV_TURNING_RANDOM ||
4833 move_pattern == MV_ALL_DIRECTIONS)
4835 boolean can_turn_left =
4836 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4837 boolean can_turn_right =
4838 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4840 if (move_pattern == MV_TURNING_LEFT)
4841 MovDir[x][y] = left_dir;
4842 else if (move_pattern == MV_TURNING_RIGHT)
4843 MovDir[x][y] = right_dir;
4844 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4845 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4846 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4847 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4848 else if (move_pattern == MV_TURNING_RANDOM)
4849 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4850 can_turn_right && !can_turn_left ? right_dir :
4851 RND(2) ? left_dir : right_dir);
4852 else if (can_turn_left && can_turn_right)
4853 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4854 else if (can_turn_left)
4855 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4856 else if (can_turn_right)
4857 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4859 MovDir[x][y] = back_dir;
4861 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4863 else if (move_pattern == MV_HORIZONTAL ||
4864 move_pattern == MV_VERTICAL)
4866 if (move_pattern & old_move_dir)
4867 MovDir[x][y] = back_dir;
4868 else if (move_pattern == MV_HORIZONTAL)
4869 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4870 else if (move_pattern == MV_VERTICAL)
4871 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4873 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4875 else if (move_pattern & MV_ANY_DIRECTION)
4877 MovDir[x][y] = move_pattern;
4878 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4880 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4882 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4883 MovDir[x][y] = left_dir;
4884 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4885 MovDir[x][y] = right_dir;
4887 if (MovDir[x][y] != old_move_dir)
4888 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4890 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4892 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4893 MovDir[x][y] = right_dir;
4894 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4895 MovDir[x][y] = left_dir;
4897 if (MovDir[x][y] != old_move_dir)
4898 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4900 else if (move_pattern == MV_TOWARDS_PLAYER ||
4901 move_pattern == MV_AWAY_FROM_PLAYER)
4903 int attr_x = -1, attr_y = -1;
4905 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4916 for (i = 0; i < MAX_PLAYERS; i++)
4918 struct PlayerInfo *player = &stored_player[i];
4919 int jx = player->jx, jy = player->jy;
4921 if (!player->active)
4925 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4933 MovDir[x][y] = MV_NO_MOVING;
4935 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4936 else if (attr_x > x)
4937 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4939 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4940 else if (attr_y > y)
4941 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4943 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4945 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4947 boolean first_horiz = RND(2);
4948 int new_move_dir = MovDir[x][y];
4951 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4952 Moving2Blocked(x, y, &newx, &newy);
4954 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4958 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4959 Moving2Blocked(x, y, &newx, &newy);
4961 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4964 MovDir[x][y] = old_move_dir;
4967 else if (move_pattern == MV_WHEN_PUSHED ||
4968 move_pattern == MV_WHEN_DROPPED)
4970 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4971 MovDir[x][y] = MV_NO_MOVING;
4975 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4977 static int test_xy[7][2] =
4987 static int test_dir[7] =
4997 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4998 int move_preference = -1000000; /* start with very low preference */
4999 int new_move_dir = MV_NO_MOVING;
5000 int start_test = RND(4);
5003 for (i = 0; i < NUM_DIRECTIONS; i++)
5005 int move_dir = test_dir[start_test + i];
5006 int move_dir_preference;
5008 xx = x + test_xy[start_test + i][0];
5009 yy = y + test_xy[start_test + i][1];
5011 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5012 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5014 new_move_dir = move_dir;
5019 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5022 move_dir_preference = -1 * RunnerVisit[xx][yy];
5023 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5024 move_dir_preference = PlayerVisit[xx][yy];
5026 if (move_dir_preference > move_preference)
5028 /* prefer field that has not been visited for the longest time */
5029 move_preference = move_dir_preference;
5030 new_move_dir = move_dir;
5032 else if (move_dir_preference == move_preference &&
5033 move_dir == old_move_dir)
5035 /* prefer last direction when all directions are preferred equally */
5036 move_preference = move_dir_preference;
5037 new_move_dir = move_dir;
5041 MovDir[x][y] = new_move_dir;
5042 if (old_move_dir != new_move_dir)
5047 static void TurnRound(int x, int y)
5049 int direction = MovDir[x][y];
5052 GfxDir[x][y] = MovDir[x][y];
5058 GfxDir[x][y] = MovDir[x][y];
5061 if (direction != MovDir[x][y])
5066 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5069 GfxAction[x][y] = ACTION_WAITING;
5073 static boolean JustBeingPushed(int x, int y)
5077 for (i = 0; i < MAX_PLAYERS; i++)
5079 struct PlayerInfo *player = &stored_player[i];
5081 if (player->active && player->is_pushing && player->MovPos)
5083 int next_jx = player->jx + (player->jx - player->last_jx);
5084 int next_jy = player->jy + (player->jy - player->last_jy);
5086 if (x == next_jx && y == next_jy)
5094 void StartMoving(int x, int y)
5097 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5099 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5100 int element = Feld[x][y];
5106 if (MovDelay[x][y] == 0)
5107 GfxAction[x][y] = ACTION_DEFAULT;
5109 /* !!! this should be handled more generic (not only for mole) !!! */
5110 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5111 GfxAction[x][y] = ACTION_DEFAULT;
5114 if (CAN_FALL(element) && y < lev_fieldy - 1)
5116 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5117 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5118 if (JustBeingPushed(x, y))
5121 if (element == EL_QUICKSAND_FULL)
5123 if (IS_FREE(x, y + 1))
5125 InitMovingField(x, y, MV_DOWN);
5126 started_moving = TRUE;
5128 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5129 Store[x][y] = EL_ROCK;
5131 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5133 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5136 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5138 if (!MovDelay[x][y])
5139 MovDelay[x][y] = TILEY + 1;
5148 Feld[x][y] = EL_QUICKSAND_EMPTY;
5149 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5150 Store[x][y + 1] = Store[x][y];
5153 PlayLevelSoundAction(x, y, ACTION_FILLING);
5155 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5159 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5160 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5162 InitMovingField(x, y, MV_DOWN);
5163 started_moving = TRUE;
5165 Feld[x][y] = EL_QUICKSAND_FILLING;
5166 Store[x][y] = element;
5168 PlayLevelSoundAction(x, y, ACTION_FILLING);
5170 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5173 else if (element == EL_MAGIC_WALL_FULL)
5175 if (IS_FREE(x, y + 1))
5177 InitMovingField(x, y, MV_DOWN);
5178 started_moving = TRUE;
5180 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5181 Store[x][y] = EL_CHANGED(Store[x][y]);
5183 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5185 if (!MovDelay[x][y])
5186 MovDelay[x][y] = TILEY/4 + 1;
5195 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5196 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5197 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5201 else if (element == EL_BD_MAGIC_WALL_FULL)
5203 if (IS_FREE(x, y + 1))
5205 InitMovingField(x, y, MV_DOWN);
5206 started_moving = TRUE;
5208 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5209 Store[x][y] = EL_CHANGED2(Store[x][y]);
5211 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5213 if (!MovDelay[x][y])
5214 MovDelay[x][y] = TILEY/4 + 1;
5223 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5224 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5225 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5229 else if (CAN_PASS_MAGIC_WALL(element) &&
5230 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5231 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5233 InitMovingField(x, y, MV_DOWN);
5234 started_moving = TRUE;
5237 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5238 EL_BD_MAGIC_WALL_FILLING);
5239 Store[x][y] = element;
5242 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5244 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5247 SplashAcid(x, y + 1);
5249 InitMovingField(x, y, MV_DOWN);
5250 started_moving = TRUE;
5252 Store[x][y] = EL_ACID;
5254 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5255 GfxAction[x][y + 1] = ACTION_ACTIVE;
5259 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5260 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5262 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5263 CAN_SMASH(element) && WasJustFalling[x][y] &&
5264 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5266 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5267 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5268 (Feld[x][y + 1] == EL_BLOCKED)))
5272 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5273 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5274 WasJustMoving[x][y] && !Pushed[x][y + 1])
5276 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5277 WasJustMoving[x][y])
5282 /* this is needed for a special case not covered by calling "Impact()"
5283 from "ContinueMoving()": if an element moves to a tile directly below
5284 another element which was just falling on that tile (which was empty
5285 in the previous frame), the falling element above would just stop
5286 instead of smashing the element below (in previous version, the above
5287 element was just checked for "moving" instead of "falling", resulting
5288 in incorrect smashes caused by horizontal movement of the above
5289 element; also, the case of the player being the element to smash was
5290 simply not covered here... :-/ ) */
5293 WasJustMoving[x][y] = 0;
5294 WasJustFalling[x][y] = 0;
5297 CheckCollision[x][y] = 0;
5300 if (IS_PLAYER(x, y + 1))
5301 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5306 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5308 if (MovDir[x][y] == MV_NO_MOVING)
5310 InitMovingField(x, y, MV_DOWN);
5311 started_moving = TRUE;
5314 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5316 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5317 MovDir[x][y] = MV_DOWN;
5319 InitMovingField(x, y, MV_DOWN);
5320 started_moving = TRUE;
5322 else if (element == EL_AMOEBA_DROP)
5324 Feld[x][y] = EL_AMOEBA_GROWING;
5325 Store[x][y] = EL_AMOEBA_WET;
5327 /* Store[x][y + 1] must be zero, because:
5328 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5331 #if OLD_GAME_BEHAVIOUR
5332 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5334 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5335 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5336 element != EL_DX_SUPABOMB)
5339 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5340 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5341 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5342 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5345 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5346 (IS_FREE(x - 1, y + 1) ||
5347 Feld[x - 1][y + 1] == EL_ACID));
5348 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5349 (IS_FREE(x + 1, y + 1) ||
5350 Feld[x + 1][y + 1] == EL_ACID));
5351 boolean can_fall_any = (can_fall_left || can_fall_right);
5352 boolean can_fall_both = (can_fall_left && can_fall_right);
5354 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5356 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5358 if (slippery_type == SLIPPERY_ONLY_LEFT)
5359 can_fall_right = FALSE;
5360 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5361 can_fall_left = FALSE;
5362 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5363 can_fall_right = FALSE;
5364 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5365 can_fall_left = FALSE;
5367 can_fall_any = (can_fall_left || can_fall_right);
5368 can_fall_both = (can_fall_left && can_fall_right);
5373 if (can_fall_both &&
5374 (game.emulation != EMU_BOULDERDASH &&
5375 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5376 can_fall_left = !(can_fall_right = RND(2));
5378 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5379 started_moving = TRUE;
5383 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5385 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5388 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5389 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5390 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5391 int belt_dir = game.belt_dir[belt_nr];
5393 if ((belt_dir == MV_LEFT && left_is_free) ||
5394 (belt_dir == MV_RIGHT && right_is_free))
5397 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5400 InitMovingField(x, y, belt_dir);
5401 started_moving = TRUE;
5404 Pushed[x][y] = TRUE;
5405 Pushed[nextx][y] = TRUE;
5408 GfxAction[x][y] = ACTION_DEFAULT;
5412 MovDir[x][y] = 0; /* if element was moving, stop it */
5417 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5419 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5421 if (CAN_MOVE(element) && !started_moving)
5424 int move_pattern = element_info[element].move_pattern;
5429 if (MovDir[x][y] == MV_NO_MOVING)
5431 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5432 x, y, element, element_info[element].token_name);
5433 printf("StartMoving(): This should never happen!\n");
5438 Moving2Blocked(x, y, &newx, &newy);
5441 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5444 if ((element == EL_SATELLITE ||
5445 element == EL_BALLOON ||
5446 element == EL_SPRING)
5447 && JustBeingPushed(x, y))
5454 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5455 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5457 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5458 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5459 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5463 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5464 element, element_info[element].token_name,
5465 WasJustMoving[x][y],
5466 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5467 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5468 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5469 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5473 WasJustMoving[x][y] = 0;
5476 CheckCollision[x][y] = 0;
5478 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5481 if (Feld[x][y] != element) /* element has changed */
5483 element = Feld[x][y];
5484 move_pattern = element_info[element].move_pattern;
5486 if (!CAN_MOVE(element))
5490 if (Feld[x][y] != element) /* element has changed */
5498 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5499 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5501 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5503 Moving2Blocked(x, y, &newx, &newy);
5504 if (Feld[newx][newy] == EL_BLOCKED)
5505 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5511 if (FrameCounter < 1 && x == 0 && y == 29)
5512 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5515 if (!MovDelay[x][y]) /* start new movement phase */
5517 /* all objects that can change their move direction after each step
5518 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5520 if (element != EL_YAMYAM &&
5521 element != EL_DARK_YAMYAM &&
5522 element != EL_PACMAN &&
5523 !(move_pattern & MV_ANY_DIRECTION) &&
5524 move_pattern != MV_TURNING_LEFT &&
5525 move_pattern != MV_TURNING_RIGHT &&
5526 move_pattern != MV_TURNING_LEFT_RIGHT &&
5527 move_pattern != MV_TURNING_RIGHT_LEFT &&
5528 move_pattern != MV_TURNING_RANDOM)
5533 if (FrameCounter < 1 && x == 0 && y == 29)
5534 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5537 if (MovDelay[x][y] && (element == EL_BUG ||
5538 element == EL_SPACESHIP ||
5539 element == EL_SP_SNIKSNAK ||
5540 element == EL_SP_ELECTRON ||
5541 element == EL_MOLE))
5542 DrawLevelField(x, y);
5546 if (MovDelay[x][y]) /* wait some time before next movement */
5551 if (element == EL_YAMYAM)
5554 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5555 DrawLevelElementAnimation(x, y, element);
5559 if (MovDelay[x][y]) /* element still has to wait some time */
5562 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5563 ResetGfxAnimation(x, y);
5567 if (GfxAction[x][y] != ACTION_WAITING)
5568 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5570 GfxAction[x][y] = ACTION_WAITING;
5574 if (element == EL_ROBOT ||
5576 element == EL_PACMAN ||
5578 element == EL_YAMYAM ||
5579 element == EL_DARK_YAMYAM)
5582 DrawLevelElementAnimation(x, y, element);
5584 DrawLevelElementAnimationIfNeeded(x, y, element);
5586 PlayLevelSoundAction(x, y, ACTION_WAITING);
5588 else if (element == EL_SP_ELECTRON)
5589 DrawLevelElementAnimationIfNeeded(x, y, element);
5590 else if (element == EL_DRAGON)
5593 int dir = MovDir[x][y];
5594 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5595 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5596 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5597 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5598 dir == MV_UP ? IMG_FLAMES_1_UP :
5599 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5600 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5603 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5606 GfxAction[x][y] = ACTION_ATTACKING;
5608 if (IS_PLAYER(x, y))
5609 DrawPlayerField(x, y);
5611 DrawLevelField(x, y);
5613 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5615 for (i = 1; i <= 3; i++)
5617 int xx = x + i * dx;
5618 int yy = y + i * dy;
5619 int sx = SCREENX(xx);
5620 int sy = SCREENY(yy);
5621 int flame_graphic = graphic + (i - 1);
5623 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5628 int flamed = MovingOrBlocked2Element(xx, yy);
5632 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5634 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5635 RemoveMovingField(xx, yy);
5637 RemoveField(xx, yy);
5639 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5642 RemoveMovingField(xx, yy);
5646 if (ChangeDelay[xx][yy])
5647 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5648 Feld[xx][yy] == EL_BLOCKED));
5652 ChangeDelay[xx][yy] = 0;
5654 Feld[xx][yy] = EL_FLAMES;
5655 if (IN_SCR_FIELD(sx, sy))
5657 DrawLevelFieldCrumbledSand(xx, yy);
5658 DrawGraphic(sx, sy, flame_graphic, frame);
5663 if (Feld[xx][yy] == EL_FLAMES)
5664 Feld[xx][yy] = EL_EMPTY;
5665 DrawLevelField(xx, yy);
5670 if (MovDelay[x][y]) /* element still has to wait some time */
5672 PlayLevelSoundAction(x, y, ACTION_WAITING);
5678 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5679 for all other elements GfxAction will be set by InitMovingField() */
5680 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5681 GfxAction[x][y] = ACTION_MOVING;
5685 /* now make next step */
5687 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5689 if (DONT_COLLIDE_WITH(element) &&
5690 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5691 !PLAYER_ENEMY_PROTECTED(newx, newy))
5694 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5698 /* player killed by element which is deadly when colliding with */
5700 KillHero(PLAYERINFO(newx, newy));
5707 else if (CAN_MOVE_INTO_ACID(element) &&
5708 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5709 (MovDir[x][y] == MV_DOWN ||
5710 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5712 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5713 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5717 else if ((element == EL_PENGUIN ||
5718 element == EL_ROBOT ||
5719 element == EL_SATELLITE ||
5720 element == EL_BALLOON ||
5721 IS_CUSTOM_ELEMENT(element)) &&
5722 IN_LEV_FIELD(newx, newy) &&
5723 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5726 SplashAcid(newx, newy);
5727 Store[x][y] = EL_ACID;
5729 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5731 if (Feld[newx][newy] == EL_EXIT_OPEN)
5735 DrawLevelField(x, y);
5737 Feld[x][y] = EL_EMPTY;
5738 DrawLevelField(x, y);
5741 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5742 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5743 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5745 local_player->friends_still_needed--;
5746 if (!local_player->friends_still_needed &&
5747 !local_player->GameOver && AllPlayersGone)
5748 local_player->LevelSolved = local_player->GameOver = TRUE;
5752 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5754 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5755 DrawLevelField(newx, newy);
5757 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5759 else if (!IS_FREE(newx, newy))
5761 GfxAction[x][y] = ACTION_WAITING;
5763 if (IS_PLAYER(x, y))
5764 DrawPlayerField(x, y);
5766 DrawLevelField(x, y);
5771 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5773 if (IS_FOOD_PIG(Feld[newx][newy]))
5775 if (IS_MOVING(newx, newy))
5776 RemoveMovingField(newx, newy);
5779 Feld[newx][newy] = EL_EMPTY;
5780 DrawLevelField(newx, newy);
5783 PlayLevelSound(x, y, SND_PIG_DIGGING);
5785 else if (!IS_FREE(newx, newy))
5787 if (IS_PLAYER(x, y))
5788 DrawPlayerField(x, y);
5790 DrawLevelField(x, y);
5799 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5802 else if (IS_CUSTOM_ELEMENT(element) &&
5803 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5807 !IS_FREE(newx, newy)
5812 int new_element = Feld[newx][newy];
5815 printf("::: '%s' digs '%s' [%d]\n",
5816 element_info[element].token_name,
5817 element_info[Feld[newx][newy]].token_name,
5818 StorePlayer[newx][newy]);
5821 if (!IS_FREE(newx, newy))
5823 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5824 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5827 /* no element can dig solid indestructible elements */
5828 if (IS_INDESTRUCTIBLE(new_element) &&
5829 !IS_DIGGABLE(new_element) &&
5830 !IS_COLLECTIBLE(new_element))
5833 if (AmoebaNr[newx][newy] &&
5834 (new_element == EL_AMOEBA_FULL ||
5835 new_element == EL_BD_AMOEBA ||
5836 new_element == EL_AMOEBA_GROWING))
5838 AmoebaCnt[AmoebaNr[newx][newy]]--;
5839 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5842 if (IS_MOVING(newx, newy))
5843 RemoveMovingField(newx, newy);
5846 RemoveField(newx, newy);
5847 DrawLevelField(newx, newy);
5850 /* if digged element was about to explode, prevent the explosion */
5851 ExplodeField[newx][newy] = EX_TYPE_NONE;
5853 PlayLevelSoundAction(x, y, action);
5858 Store[newx][newy] = EL_EMPTY;
5859 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5860 Store[newx][newy] = element_info[element].move_leave_element;
5862 Store[newx][newy] = EL_EMPTY;
5863 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5864 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5865 Store[newx][newy] = element_info[element].move_leave_element;
5868 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5869 element_info[element].can_leave_element = TRUE;
5872 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5874 RunnerVisit[x][y] = FrameCounter;
5875 PlayerVisit[x][y] /= 8; /* expire player visit path */
5881 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5883 if (!IS_FREE(newx, newy))
5885 if (IS_PLAYER(x, y))
5886 DrawPlayerField(x, y);
5888 DrawLevelField(x, y);
5894 boolean wanna_flame = !RND(10);
5895 int dx = newx - x, dy = newy - y;
5896 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5897 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5898 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5899 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5900 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5901 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5904 IS_CLASSIC_ENEMY(element1) ||
5905 IS_CLASSIC_ENEMY(element2)) &&
5906 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5907 element1 != EL_FLAMES && element2 != EL_FLAMES)
5910 ResetGfxAnimation(x, y);
5911 GfxAction[x][y] = ACTION_ATTACKING;
5914 if (IS_PLAYER(x, y))
5915 DrawPlayerField(x, y);
5917 DrawLevelField(x, y);
5919 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5921 MovDelay[x][y] = 50;
5925 RemoveField(newx, newy);
5927 Feld[newx][newy] = EL_FLAMES;
5928 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5931 RemoveField(newx1, newy1);
5933 Feld[newx1][newy1] = EL_FLAMES;
5935 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5938 RemoveField(newx2, newy2);
5940 Feld[newx2][newy2] = EL_FLAMES;
5947 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5948 Feld[newx][newy] == EL_DIAMOND)
5950 if (IS_MOVING(newx, newy))
5951 RemoveMovingField(newx, newy);
5954 Feld[newx][newy] = EL_EMPTY;
5955 DrawLevelField(newx, newy);
5958 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5960 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5961 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5963 if (AmoebaNr[newx][newy])
5965 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5966 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5967 Feld[newx][newy] == EL_BD_AMOEBA)
5968 AmoebaCnt[AmoebaNr[newx][newy]]--;
5973 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5975 if (IS_MOVING(newx, newy))
5978 RemoveMovingField(newx, newy);
5982 Feld[newx][newy] = EL_EMPTY;
5983 DrawLevelField(newx, newy);
5986 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5988 else if ((element == EL_PACMAN || element == EL_MOLE)
5989 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5991 if (AmoebaNr[newx][newy])
5993 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5994 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5995 Feld[newx][newy] == EL_BD_AMOEBA)
5996 AmoebaCnt[AmoebaNr[newx][newy]]--;
5999 if (element == EL_MOLE)
6001 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6002 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6004 ResetGfxAnimation(x, y);
6005 GfxAction[x][y] = ACTION_DIGGING;
6006 DrawLevelField(x, y);
6008 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6010 return; /* wait for shrinking amoeba */
6012 else /* element == EL_PACMAN */
6014 Feld[newx][newy] = EL_EMPTY;
6015 DrawLevelField(newx, newy);
6016 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6019 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6020 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6021 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6023 /* wait for shrinking amoeba to completely disappear */
6026 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6028 /* object was running against a wall */
6033 if (move_pattern & MV_ANY_DIRECTION &&
6034 move_pattern == MovDir[x][y])
6036 int blocking_element =
6037 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6040 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6041 element_info[element].token_name,
6042 element_info[blocking_element].token_name,
6046 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6049 element = Feld[x][y]; /* element might have changed */
6054 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6055 DrawLevelElementAnimation(x, y, element);
6057 if (element == EL_BUG ||
6058 element == EL_SPACESHIP ||
6059 element == EL_SP_SNIKSNAK)
6060 DrawLevelField(x, y);
6061 else if (element == EL_MOLE)
6062 DrawLevelField(x, y);
6063 else if (element == EL_BD_BUTTERFLY ||
6064 element == EL_BD_FIREFLY)
6065 DrawLevelElementAnimationIfNeeded(x, y, element);
6066 else if (element == EL_SATELLITE)
6067 DrawLevelElementAnimationIfNeeded(x, y, element);
6068 else if (element == EL_SP_ELECTRON)
6069 DrawLevelElementAnimationIfNeeded(x, y, element);
6072 if (DONT_TOUCH(element))
6073 TestIfBadThingTouchesHero(x, y);
6076 PlayLevelSoundAction(x, y, ACTION_WAITING);
6082 InitMovingField(x, y, MovDir[x][y]);
6084 PlayLevelSoundAction(x, y, ACTION_MOVING);
6088 ContinueMoving(x, y);
6091 void ContinueMoving(int x, int y)
6093 int element = Feld[x][y];
6094 int stored = Store[x][y];
6095 struct ElementInfo *ei = &element_info[element];
6096 int direction = MovDir[x][y];
6097 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6098 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6099 int newx = x + dx, newy = y + dy;
6101 int nextx = newx + dx, nexty = newy + dy;
6104 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6105 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6107 boolean pushed_by_player = Pushed[x][y];
6110 MovPos[x][y] += getElementMoveStepsize(x, y);
6113 if (pushed_by_player && IS_PLAYER(x, y))
6115 /* special case: moving object pushed by player */
6116 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6119 if (pushed_by_player) /* special case: moving object pushed by player */
6120 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6123 if (ABS(MovPos[x][y]) < TILEX)
6125 DrawLevelField(x, y);
6127 return; /* element is still moving */
6130 /* element reached destination field */
6132 Feld[x][y] = EL_EMPTY;
6133 Feld[newx][newy] = element;
6134 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6137 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6139 element = Feld[newx][newy] = EL_ACID;
6142 else if (element == EL_MOLE)
6144 Feld[x][y] = EL_SAND;
6146 DrawLevelFieldCrumbledSandNeighbours(x, y);
6148 else if (element == EL_QUICKSAND_FILLING)
6150 element = Feld[newx][newy] = get_next_element(element);
6151 Store[newx][newy] = Store[x][y];
6153 else if (element == EL_QUICKSAND_EMPTYING)
6155 Feld[x][y] = get_next_element(element);
6156 element = Feld[newx][newy] = Store[x][y];
6158 else if (element == EL_MAGIC_WALL_FILLING)
6160 element = Feld[newx][newy] = get_next_element(element);
6161 if (!game.magic_wall_active)
6162 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6163 Store[newx][newy] = Store[x][y];
6165 else if (element == EL_MAGIC_WALL_EMPTYING)
6167 Feld[x][y] = get_next_element(element);
6168 if (!game.magic_wall_active)
6169 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6170 element = Feld[newx][newy] = Store[x][y];
6172 else if (element == EL_BD_MAGIC_WALL_FILLING)
6174 element = Feld[newx][newy] = get_next_element(element);
6175 if (!game.magic_wall_active)
6176 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6177 Store[newx][newy] = Store[x][y];
6179 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6181 Feld[x][y] = get_next_element(element);
6182 if (!game.magic_wall_active)
6183 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6184 element = Feld[newx][newy] = Store[x][y];
6186 else if (element == EL_AMOEBA_DROPPING)
6188 Feld[x][y] = get_next_element(element);
6189 element = Feld[newx][newy] = Store[x][y];
6191 else if (element == EL_SOKOBAN_OBJECT)
6194 Feld[x][y] = Back[x][y];
6196 if (Back[newx][newy])
6197 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6199 Back[x][y] = Back[newx][newy] = 0;
6202 else if (Store[x][y] == EL_ACID)
6204 element = Feld[newx][newy] = EL_ACID;
6208 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6209 ei->move_leave_element != EL_EMPTY &&
6210 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6211 Store[x][y] != EL_EMPTY))
6213 /* some elements can leave other elements behind after moving */
6215 Feld[x][y] = ei->move_leave_element;
6216 InitField(x, y, FALSE);
6218 if (GFX_CRUMBLED(Feld[x][y]))
6219 DrawLevelFieldCrumbledSandNeighbours(x, y);
6223 Store[x][y] = EL_EMPTY;
6224 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6225 MovDelay[newx][newy] = 0;
6227 if (CAN_CHANGE(element))
6229 /* copy element change control values to new field */
6230 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6231 ChangePage[newx][newy] = ChangePage[x][y];
6232 Changed[newx][newy] = Changed[x][y];
6233 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6236 ChangeDelay[x][y] = 0;
6237 ChangePage[x][y] = -1;
6238 Changed[x][y] = CE_BITMASK_DEFAULT;
6239 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6241 /* copy animation control values to new field */
6242 GfxFrame[newx][newy] = GfxFrame[x][y];
6243 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6244 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6245 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6247 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6249 ResetGfxAnimation(x, y); /* reset animation values for old field */
6252 /* some elements can leave other elements behind after moving */
6254 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6255 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6256 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6258 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6259 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6263 int move_leave_element = ei->move_leave_element;
6265 Feld[x][y] = move_leave_element;
6266 InitField(x, y, FALSE);
6268 if (GFX_CRUMBLED(Feld[x][y]))
6269 DrawLevelFieldCrumbledSandNeighbours(x, y);
6271 if (ELEM_IS_PLAYER(move_leave_element))
6272 RelocatePlayer(x, y, move_leave_element);
6277 /* some elements can leave other elements behind after moving */
6278 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6279 ei->move_leave_element != EL_EMPTY &&
6280 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6281 ei->can_leave_element_last))
6283 Feld[x][y] = ei->move_leave_element;
6284 InitField(x, y, FALSE);
6286 if (GFX_CRUMBLED(Feld[x][y]))
6287 DrawLevelFieldCrumbledSandNeighbours(x, y);
6290 ei->can_leave_element_last = ei->can_leave_element;
6291 ei->can_leave_element = FALSE;
6295 /* 2.1.1 (does not work correctly for spring) */
6296 if (!CAN_MOVE(element))
6297 MovDir[newx][newy] = 0;
6301 /* (does not work for falling objects that slide horizontally) */
6302 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6303 MovDir[newx][newy] = 0;
6306 if (!CAN_MOVE(element) ||
6307 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6308 MovDir[newx][newy] = 0;
6312 if (!CAN_MOVE(element) ||
6313 (CAN_FALL(element) && direction == MV_DOWN))
6314 GfxDir[x][y] = MovDir[newx][newy] = 0;
6316 if (!CAN_MOVE(element) ||
6317 (CAN_FALL(element) && direction == MV_DOWN &&
6318 (element == EL_SPRING ||
6319 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6320 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6321 GfxDir[x][y] = MovDir[newx][newy] = 0;
6327 DrawLevelField(x, y);
6328 DrawLevelField(newx, newy);
6330 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6332 /* prevent pushed element from moving on in pushed direction */
6333 if (pushed_by_player && CAN_MOVE(element) &&
6334 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6335 !(element_info[element].move_pattern & direction))
6336 TurnRound(newx, newy);
6339 /* prevent elements on conveyor belt from moving on in last direction */
6340 if (pushed_by_conveyor && CAN_FALL(element) &&
6341 direction & MV_HORIZONTAL)
6344 if (CAN_MOVE(element))
6345 InitMovDir(newx, newy);
6347 MovDir[newx][newy] = 0;
6349 MovDir[newx][newy] = 0;
6354 if (!pushed_by_player)
6356 int nextx = newx + dx, nexty = newy + dy;
6357 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6359 WasJustMoving[newx][newy] = 3;
6361 if (CAN_FALL(element) && direction == MV_DOWN)
6362 WasJustFalling[newx][newy] = 3;
6364 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6365 CheckCollision[newx][newy] = 2;
6368 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6370 TestIfBadThingTouchesHero(newx, newy);
6371 TestIfBadThingTouchesFriend(newx, newy);
6373 if (!IS_CUSTOM_ELEMENT(element))
6374 TestIfBadThingTouchesOtherBadThing(newx, newy);
6376 else if (element == EL_PENGUIN)
6377 TestIfFriendTouchesBadThing(newx, newy);
6379 #if USE_NEW_MOVEMENT
6381 if (CAN_FALL(element) && direction == MV_DOWN &&
6382 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6383 IS_PLAYER(x, newy + 1))
6384 printf("::: we would now kill the player [%d]\n", FrameCounter);
6387 /* give the player one last chance (one more frame) to move away */
6388 if (CAN_FALL(element) && direction == MV_DOWN &&
6389 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6390 !IS_PLAYER(x, newy + 1))
6393 if (CAN_FALL(element) && direction == MV_DOWN &&
6394 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6399 if (pushed_by_player)
6402 int dig_side = MV_DIR_OPPOSITE(direction);
6404 static int trigger_sides[4] =
6406 CH_SIDE_RIGHT, /* moving left */
6407 CH_SIDE_LEFT, /* moving right */
6408 CH_SIDE_BOTTOM, /* moving up */
6409 CH_SIDE_TOP, /* moving down */
6411 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6413 struct PlayerInfo *player = PLAYERINFO(x, y);
6415 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6416 player->index_bit, dig_side);
6417 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6418 player->index_bit, dig_side);
6423 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6427 if (ChangePage[newx][newy] != -1) /* delayed change */
6428 ChangeElement(newx, newy, ChangePage[newx][newy]);
6433 TestIfElementHitsCustomElement(newx, newy, direction);
6437 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6439 int hitting_element = Feld[newx][newy];
6441 /* !!! fix side (direction) orientation here and elsewhere !!! */
6442 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6446 if (IN_LEV_FIELD(nextx, nexty))
6448 int opposite_direction = MV_DIR_OPPOSITE(direction);
6449 int hitting_side = direction;
6450 int touched_side = opposite_direction;
6451 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6452 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6453 MovDir[nextx][nexty] != direction ||
6454 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6460 CheckElementChangeBySide(nextx, nexty, touched_element,
6461 CE_HIT_BY_SOMETHING, opposite_direction);
6463 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6464 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6466 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6468 struct ElementChangeInfo *change =
6469 &element_info[hitting_element].change_page[i];
6471 if (change->can_change &&
6472 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6473 change->trigger_side & touched_side &&
6474 change->trigger_element == touched_element)
6476 CheckElementChangeByPage(newx, newy, hitting_element,
6477 touched_element, CE_OTHER_IS_HITTING,i);
6483 if (IS_CUSTOM_ELEMENT(touched_element) &&
6484 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6486 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6488 struct ElementChangeInfo *change =
6489 &element_info[touched_element].change_page[i];
6491 if (change->can_change &&
6492 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6493 change->trigger_side & hitting_side &&
6494 change->trigger_element == hitting_element)
6496 CheckElementChangeByPage(nextx, nexty, touched_element,
6497 hitting_element, CE_OTHER_GETS_HIT, i);
6508 TestIfPlayerTouchesCustomElement(newx, newy);
6509 TestIfElementTouchesCustomElement(newx, newy);
6512 int AmoebeNachbarNr(int ax, int ay)
6515 int element = Feld[ax][ay];
6517 static int xy[4][2] =
6525 for (i = 0; i < NUM_DIRECTIONS; i++)
6527 int x = ax + xy[i][0];
6528 int y = ay + xy[i][1];
6530 if (!IN_LEV_FIELD(x, y))
6533 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6534 group_nr = AmoebaNr[x][y];
6540 void AmoebenVereinigen(int ax, int ay)
6542 int i, x, y, xx, yy;
6543 int new_group_nr = AmoebaNr[ax][ay];
6544 static int xy[4][2] =
6552 if (new_group_nr == 0)
6555 for (i = 0; i < NUM_DIRECTIONS; i++)
6560 if (!IN_LEV_FIELD(x, y))
6563 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6564 Feld[x][y] == EL_BD_AMOEBA ||
6565 Feld[x][y] == EL_AMOEBA_DEAD) &&
6566 AmoebaNr[x][y] != new_group_nr)
6568 int old_group_nr = AmoebaNr[x][y];
6570 if (old_group_nr == 0)
6573 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6574 AmoebaCnt[old_group_nr] = 0;
6575 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6576 AmoebaCnt2[old_group_nr] = 0;
6578 for (yy = 0; yy < lev_fieldy; yy++)
6580 for (xx = 0; xx < lev_fieldx; xx++)
6582 if (AmoebaNr[xx][yy] == old_group_nr)
6583 AmoebaNr[xx][yy] = new_group_nr;
6590 void AmoebeUmwandeln(int ax, int ay)
6594 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6596 int group_nr = AmoebaNr[ax][ay];
6601 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6602 printf("AmoebeUmwandeln(): This should never happen!\n");
6607 for (y = 0; y < lev_fieldy; y++)
6609 for (x = 0; x < lev_fieldx; x++)
6611 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6614 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6618 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6619 SND_AMOEBA_TURNING_TO_GEM :
6620 SND_AMOEBA_TURNING_TO_ROCK));
6625 static int xy[4][2] =
6633 for (i = 0; i < NUM_DIRECTIONS; i++)
6638 if (!IN_LEV_FIELD(x, y))
6641 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6643 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6644 SND_AMOEBA_TURNING_TO_GEM :
6645 SND_AMOEBA_TURNING_TO_ROCK));
6652 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6655 int group_nr = AmoebaNr[ax][ay];
6656 boolean done = FALSE;
6661 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6662 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6667 for (y = 0; y < lev_fieldy; y++)
6669 for (x = 0; x < lev_fieldx; x++)
6671 if (AmoebaNr[x][y] == group_nr &&
6672 (Feld[x][y] == EL_AMOEBA_DEAD ||
6673 Feld[x][y] == EL_BD_AMOEBA ||
6674 Feld[x][y] == EL_AMOEBA_GROWING))
6677 Feld[x][y] = new_element;
6678 InitField(x, y, FALSE);
6679 DrawLevelField(x, y);
6686 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6687 SND_BD_AMOEBA_TURNING_TO_ROCK :
6688 SND_BD_AMOEBA_TURNING_TO_GEM));
6691 void AmoebeWaechst(int x, int y)
6693 static unsigned long sound_delay = 0;
6694 static unsigned long sound_delay_value = 0;
6696 if (!MovDelay[x][y]) /* start new growing cycle */
6700 if (DelayReached(&sound_delay, sound_delay_value))
6703 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6705 if (Store[x][y] == EL_BD_AMOEBA)
6706 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6708 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6710 sound_delay_value = 30;
6714 if (MovDelay[x][y]) /* wait some time before growing bigger */
6717 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6719 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6720 6 - MovDelay[x][y]);
6722 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6725 if (!MovDelay[x][y])
6727 Feld[x][y] = Store[x][y];
6729 DrawLevelField(x, y);
6734 void AmoebaDisappearing(int x, int y)
6736 static unsigned long sound_delay = 0;
6737 static unsigned long sound_delay_value = 0;
6739 if (!MovDelay[x][y]) /* start new shrinking cycle */
6743 if (DelayReached(&sound_delay, sound_delay_value))
6744 sound_delay_value = 30;
6747 if (MovDelay[x][y]) /* wait some time before shrinking */
6750 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6752 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6753 6 - MovDelay[x][y]);
6755 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6758 if (!MovDelay[x][y])
6760 Feld[x][y] = EL_EMPTY;
6761 DrawLevelField(x, y);
6763 /* don't let mole enter this field in this cycle;
6764 (give priority to objects falling to this field from above) */
6770 void AmoebeAbleger(int ax, int ay)
6773 int element = Feld[ax][ay];
6774 int graphic = el2img(element);
6775 int newax = ax, neway = ay;
6776 static int xy[4][2] =
6784 if (!level.amoeba_speed)
6786 Feld[ax][ay] = EL_AMOEBA_DEAD;
6787 DrawLevelField(ax, ay);
6791 if (IS_ANIMATED(graphic))
6792 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6794 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6795 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6797 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6800 if (MovDelay[ax][ay])
6804 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6807 int x = ax + xy[start][0];
6808 int y = ay + xy[start][1];
6810 if (!IN_LEV_FIELD(x, y))
6814 if (IS_FREE(x, y) ||
6815 CAN_GROW_INTO(Feld[x][y]) ||
6816 Feld[x][y] == EL_QUICKSAND_EMPTY)
6822 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6823 if (IS_FREE(x, y) ||
6824 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6831 if (newax == ax && neway == ay)
6834 else /* normal or "filled" (BD style) amoeba */
6837 boolean waiting_for_player = FALSE;
6839 for (i = 0; i < NUM_DIRECTIONS; i++)
6841 int j = (start + i) % 4;
6842 int x = ax + xy[j][0];
6843 int y = ay + xy[j][1];
6845 if (!IN_LEV_FIELD(x, y))
6849 if (IS_FREE(x, y) ||
6850 CAN_GROW_INTO(Feld[x][y]) ||
6851 Feld[x][y] == EL_QUICKSAND_EMPTY)
6858 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6859 if (IS_FREE(x, y) ||
6860 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6867 else if (IS_PLAYER(x, y))
6868 waiting_for_player = TRUE;
6871 if (newax == ax && neway == ay) /* amoeba cannot grow */
6874 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6876 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6879 Feld[ax][ay] = EL_AMOEBA_DEAD;
6880 DrawLevelField(ax, ay);
6881 AmoebaCnt[AmoebaNr[ax][ay]]--;
6883 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6885 if (element == EL_AMOEBA_FULL)
6886 AmoebeUmwandeln(ax, ay);
6887 else if (element == EL_BD_AMOEBA)
6888 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6893 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6895 /* amoeba gets larger by growing in some direction */
6897 int new_group_nr = AmoebaNr[ax][ay];
6900 if (new_group_nr == 0)
6902 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6903 printf("AmoebeAbleger(): This should never happen!\n");
6908 AmoebaNr[newax][neway] = new_group_nr;
6909 AmoebaCnt[new_group_nr]++;
6910 AmoebaCnt2[new_group_nr]++;
6912 /* if amoeba touches other amoeba(s) after growing, unify them */
6913 AmoebenVereinigen(newax, neway);
6915 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6917 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6923 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6924 (neway == lev_fieldy - 1 && newax != ax))
6926 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6927 Store[newax][neway] = element;
6929 else if (neway == ay)
6931 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6933 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6935 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6940 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6941 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6942 Store[ax][ay] = EL_AMOEBA_DROP;
6943 ContinueMoving(ax, ay);
6947 DrawLevelField(newax, neway);
6950 void Life(int ax, int ay)
6953 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6955 int element = Feld[ax][ay];
6956 int graphic = el2img(element);
6957 boolean changed = FALSE;
6959 if (IS_ANIMATED(graphic))
6960 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6965 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6966 MovDelay[ax][ay] = life_time;
6968 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6971 if (MovDelay[ax][ay])
6975 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6977 int xx = ax+x1, yy = ay+y1;
6980 if (!IN_LEV_FIELD(xx, yy))
6983 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6985 int x = xx+x2, y = yy+y2;
6987 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6990 if (((Feld[x][y] == element ||
6991 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6993 (IS_FREE(x, y) && Stop[x][y]))
6997 if (xx == ax && yy == ay) /* field in the middle */
6999 if (nachbarn < life[0] || nachbarn > life[1])
7001 Feld[xx][yy] = EL_EMPTY;
7003 DrawLevelField(xx, yy);
7004 Stop[xx][yy] = TRUE;
7009 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7010 { /* free border field */
7011 if (nachbarn >= life[2] && nachbarn <= life[3])
7013 Feld[xx][yy] = element;
7014 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7016 DrawLevelField(xx, yy);
7017 Stop[xx][yy] = TRUE;
7022 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7023 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7024 { /* free border field */
7025 if (nachbarn >= life[2] && nachbarn <= life[3])
7027 Feld[xx][yy] = element;
7028 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7030 DrawLevelField(xx, yy);
7031 Stop[xx][yy] = TRUE;
7039 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7040 SND_GAME_OF_LIFE_GROWING);
7043 static void InitRobotWheel(int x, int y)
7045 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7048 static void RunRobotWheel(int x, int y)
7050 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7053 static void StopRobotWheel(int x, int y)
7055 if (ZX == x && ZY == y)
7059 static void InitTimegateWheel(int x, int y)
7062 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7064 /* another brainless, "type style" bug ... :-( */
7065 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7069 static void RunTimegateWheel(int x, int y)
7071 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7074 void CheckExit(int x, int y)
7076 if (local_player->gems_still_needed > 0 ||
7077 local_player->sokobanfields_still_needed > 0 ||
7078 local_player->lights_still_needed > 0)
7080 int element = Feld[x][y];
7081 int graphic = el2img(element);
7083 if (IS_ANIMATED(graphic))
7084 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7089 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7092 Feld[x][y] = EL_EXIT_OPENING;
7094 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7097 void CheckExitSP(int x, int y)
7099 if (local_player->gems_still_needed > 0)
7101 int element = Feld[x][y];
7102 int graphic = el2img(element);
7104 if (IS_ANIMATED(graphic))
7105 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7110 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7113 Feld[x][y] = EL_SP_EXIT_OPENING;
7115 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7118 static void CloseAllOpenTimegates()
7122 for (y = 0; y < lev_fieldy; y++)
7124 for (x = 0; x < lev_fieldx; x++)
7126 int element = Feld[x][y];
7128 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7130 Feld[x][y] = EL_TIMEGATE_CLOSING;
7132 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7134 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7141 void EdelsteinFunkeln(int x, int y)
7143 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7146 if (Feld[x][y] == EL_BD_DIAMOND)
7149 if (MovDelay[x][y] == 0) /* next animation frame */
7150 MovDelay[x][y] = 11 * !SimpleRND(500);
7152 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7156 if (setup.direct_draw && MovDelay[x][y])
7157 SetDrawtoField(DRAW_BUFFERED);
7159 DrawLevelElementAnimation(x, y, Feld[x][y]);
7161 if (MovDelay[x][y] != 0)
7163 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7164 10 - MovDelay[x][y]);
7166 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7168 if (setup.direct_draw)
7172 dest_x = FX + SCREENX(x) * TILEX;
7173 dest_y = FY + SCREENY(y) * TILEY;
7175 BlitBitmap(drawto_field, window,
7176 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7177 SetDrawtoField(DRAW_DIRECT);
7183 void MauerWaechst(int x, int y)
7187 if (!MovDelay[x][y]) /* next animation frame */
7188 MovDelay[x][y] = 3 * delay;
7190 if (MovDelay[x][y]) /* wait some time before next frame */
7194 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7196 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7197 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7199 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7202 if (!MovDelay[x][y])
7204 if (MovDir[x][y] == MV_LEFT)
7206 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7207 DrawLevelField(x - 1, y);
7209 else if (MovDir[x][y] == MV_RIGHT)
7211 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7212 DrawLevelField(x + 1, y);
7214 else if (MovDir[x][y] == MV_UP)
7216 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7217 DrawLevelField(x, y - 1);
7221 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7222 DrawLevelField(x, y + 1);
7225 Feld[x][y] = Store[x][y];
7227 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7228 DrawLevelField(x, y);
7233 void MauerAbleger(int ax, int ay)
7235 int element = Feld[ax][ay];
7236 int graphic = el2img(element);
7237 boolean oben_frei = FALSE, unten_frei = FALSE;
7238 boolean links_frei = FALSE, rechts_frei = FALSE;
7239 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7240 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7241 boolean new_wall = FALSE;
7243 if (IS_ANIMATED(graphic))
7244 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7246 if (!MovDelay[ax][ay]) /* start building new wall */
7247 MovDelay[ax][ay] = 6;
7249 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7252 if (MovDelay[ax][ay])
7256 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7258 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7260 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7262 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7265 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7266 element == EL_EXPANDABLE_WALL_ANY)
7270 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7271 Store[ax][ay-1] = element;
7272 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7273 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7274 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7275 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7280 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7281 Store[ax][ay+1] = element;
7282 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7283 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7284 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7285 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7290 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7291 element == EL_EXPANDABLE_WALL_ANY ||
7292 element == EL_EXPANDABLE_WALL)
7296 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7297 Store[ax-1][ay] = element;
7298 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7299 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7300 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7301 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7307 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7308 Store[ax+1][ay] = element;
7309 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7310 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7311 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7312 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7317 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7318 DrawLevelField(ax, ay);
7320 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7322 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7323 unten_massiv = TRUE;
7324 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7325 links_massiv = TRUE;
7326 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7327 rechts_massiv = TRUE;
7329 if (((oben_massiv && unten_massiv) ||
7330 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7331 element == EL_EXPANDABLE_WALL) &&
7332 ((links_massiv && rechts_massiv) ||
7333 element == EL_EXPANDABLE_WALL_VERTICAL))
7334 Feld[ax][ay] = EL_WALL;
7338 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7340 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7344 void CheckForDragon(int x, int y)
7347 boolean dragon_found = FALSE;
7348 static int xy[4][2] =
7356 for (i = 0; i < NUM_DIRECTIONS; i++)
7358 for (j = 0; j < 4; j++)
7360 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7362 if (IN_LEV_FIELD(xx, yy) &&
7363 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7365 if (Feld[xx][yy] == EL_DRAGON)
7366 dragon_found = TRUE;
7375 for (i = 0; i < NUM_DIRECTIONS; i++)
7377 for (j = 0; j < 3; j++)
7379 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7381 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7383 Feld[xx][yy] = EL_EMPTY;
7384 DrawLevelField(xx, yy);
7393 static void InitBuggyBase(int x, int y)
7395 int element = Feld[x][y];
7396 int activating_delay = FRAMES_PER_SECOND / 4;
7399 (element == EL_SP_BUGGY_BASE ?
7400 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7401 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7403 element == EL_SP_BUGGY_BASE_ACTIVE ?
7404 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7407 static void WarnBuggyBase(int x, int y)
7410 static int xy[4][2] =
7418 for (i = 0; i < NUM_DIRECTIONS; i++)
7420 int xx = x + xy[i][0], yy = y + xy[i][1];
7422 if (IS_PLAYER(xx, yy))
7424 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7431 static void InitTrap(int x, int y)
7433 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7436 static void ActivateTrap(int x, int y)
7438 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7441 static void ChangeActiveTrap(int x, int y)
7443 int graphic = IMG_TRAP_ACTIVE;
7445 /* if new animation frame was drawn, correct crumbled sand border */
7446 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7447 DrawLevelFieldCrumbledSand(x, y);
7450 static void ChangeElementNowExt(int x, int y, int target_element)
7452 int previous_move_direction = MovDir[x][y];
7454 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7455 IS_WALKABLE(Feld[x][y]));
7457 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7458 IS_WALKABLE(Feld[x][y]) &&
7462 /* check if element under player changes from accessible to unaccessible
7463 (needed for special case of dropping element which then changes) */
7464 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7465 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7468 printf("::: BOOOM! [%d, '%s']\n", target_element,
7469 element_info[target_element].token_name);
7481 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7482 RemoveMovingField(x, y);
7486 Feld[x][y] = target_element;
7489 Feld[x][y] = target_element;
7492 ResetGfxAnimation(x, y);
7493 ResetRandomAnimationValue(x, y);
7495 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7496 MovDir[x][y] = previous_move_direction;
7499 InitField_WithBug1(x, y, FALSE);
7501 InitField(x, y, FALSE);
7502 if (CAN_MOVE(Feld[x][y]))
7506 DrawLevelField(x, y);
7508 if (GFX_CRUMBLED(Feld[x][y]))
7509 DrawLevelFieldCrumbledSandNeighbours(x, y);
7513 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7517 TestIfBadThingTouchesHero(x, y);
7518 TestIfPlayerTouchesCustomElement(x, y);
7519 TestIfElementTouchesCustomElement(x, y);
7522 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7523 if (ELEM_IS_PLAYER(target_element))
7524 RelocatePlayer(x, y, target_element);
7527 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7531 TestIfBadThingTouchesHero(x, y);
7532 TestIfPlayerTouchesCustomElement(x, y);
7533 TestIfElementTouchesCustomElement(x, y);
7537 static boolean ChangeElementNow(int x, int y, int element, int page)
7539 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7541 int old_element = Feld[x][y];
7543 /* always use default change event to prevent running into a loop */
7544 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7545 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7547 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7549 /* reset actual trigger element and player */
7550 change->actual_trigger_element = EL_EMPTY;
7551 change->actual_trigger_player = EL_PLAYER_1;
7554 /* do not change already changed elements with same change event */
7556 if (Changed[x][y] & ChangeEvent[x][y])
7563 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7566 /* !!! indirect change before direct change !!! */
7567 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7570 if (change->explode)
7577 if (change->use_target_content)
7579 boolean complete_replace = TRUE;
7580 boolean can_replace[3][3];
7583 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7586 boolean is_walkable;
7587 boolean is_diggable;
7588 boolean is_collectible;
7589 boolean is_removable;
7590 boolean is_destructible;
7591 int ex = x + xx - 1;
7592 int ey = y + yy - 1;
7593 int content_element = change->target_content[xx][yy];
7596 can_replace[xx][yy] = TRUE;
7598 if (ex == x && ey == y) /* do not check changing element itself */
7601 if (content_element == EL_EMPTY_SPACE)
7603 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7608 if (!IN_LEV_FIELD(ex, ey))
7610 can_replace[xx][yy] = FALSE;
7611 complete_replace = FALSE;
7617 if (Changed[ex][ey]) /* do not change already changed elements */
7619 can_replace[xx][yy] = FALSE;
7620 complete_replace = FALSE;
7628 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7629 e = MovingOrBlocked2Element(ex, ey);
7634 is_empty = (IS_FREE(ex, ey) ||
7635 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7636 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7637 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7641 is_empty = (IS_FREE(ex, ey) ||
7642 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7644 is_empty = (IS_FREE(ex, ey) ||
7645 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7650 is_walkable = (is_empty || IS_WALKABLE(e));
7651 is_diggable = (is_empty || IS_DIGGABLE(e));
7652 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7653 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7654 is_removable = (is_diggable || is_collectible);
7656 can_replace[xx][yy] =
7657 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7658 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7659 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7660 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7661 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7662 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7663 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7665 if (!can_replace[xx][yy])
7666 complete_replace = FALSE;
7668 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7669 IS_WALKABLE(content_element)));
7671 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7673 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7676 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7677 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7678 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7680 can_replace[xx][yy] = FALSE;
7681 complete_replace = FALSE;
7686 if (!change->only_if_complete || complete_replace)
7688 boolean something_has_changed = FALSE;
7690 if (change->only_if_complete && change->use_random_replace &&
7691 RND(100) < change->random_percentage)
7694 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7696 int ex = x + xx - 1;
7697 int ey = y + yy - 1;
7698 int content_element;
7700 if (can_replace[xx][yy] && (!change->use_random_replace ||
7701 RND(100) < change->random_percentage))
7703 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7704 RemoveMovingField(ex, ey);
7706 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7708 content_element = change->target_content[xx][yy];
7709 target_element = GET_TARGET_ELEMENT(content_element, change);
7711 ChangeElementNowExt(ex, ey, target_element);
7713 something_has_changed = TRUE;
7715 /* for symmetry reasons, freeze newly created border elements */
7716 if (ex != x || ey != y)
7717 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7721 if (something_has_changed)
7722 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7727 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7729 ChangeElementNowExt(x, y, target_element);
7731 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7735 /* this uses direct change before indirect change */
7736 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7742 static void ChangeElement(int x, int y, int page)
7744 int element = MovingOrBlocked2Element(x, y);
7745 struct ElementInfo *ei = &element_info[element];
7746 struct ElementChangeInfo *change = &ei->change_page[page];
7749 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7752 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7753 x, y, element, element_info[element].token_name);
7754 printf("ChangeElement(): This should never happen!\n");
7759 /* this can happen with classic bombs on walkable, changing elements */
7760 if (!CAN_CHANGE(element))
7763 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7764 ChangeDelay[x][y] = 0;
7770 if (ChangeDelay[x][y] == 0) /* initialize element change */
7772 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7773 RND(change->delay_random * change->delay_frames)) + 1;
7775 ResetGfxAnimation(x, y);
7776 ResetRandomAnimationValue(x, y);
7778 if (change->pre_change_function)
7779 change->pre_change_function(x, y);
7782 ChangeDelay[x][y]--;
7784 if (ChangeDelay[x][y] != 0) /* continue element change */
7786 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7788 if (IS_ANIMATED(graphic))
7789 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7791 if (change->change_function)
7792 change->change_function(x, y);
7794 else /* finish element change */
7796 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7798 page = ChangePage[x][y];
7799 ChangePage[x][y] = -1;
7801 change = &ei->change_page[page];
7805 if (IS_MOVING(x, y) && !change->explode)
7807 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7810 ChangeDelay[x][y] = 1; /* try change after next move step */
7811 ChangePage[x][y] = page; /* remember page to use for change */
7816 if (ChangeElementNow(x, y, element, page))
7818 if (change->post_change_function)
7819 change->post_change_function(x, y);
7824 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7825 int trigger_element,
7832 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7834 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7837 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7839 int element = EL_CUSTOM_START + i;
7841 boolean change_element = FALSE;
7844 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7847 for (j = 0; j < element_info[element].num_change_pages; j++)
7849 struct ElementChangeInfo *change = &element_info[element].change_page[j];
7851 if (change->can_change &&
7852 change->events & CH_EVENT_BIT(trigger_event) &&
7853 change->trigger_side & trigger_side &&
7854 change->trigger_player & trigger_player &&
7855 change->trigger_page & trigger_page_bits &&
7856 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7859 if (!(change->events & CH_EVENT_BIT(trigger_event)))
7860 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7861 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7864 change_element = TRUE;
7867 change->actual_trigger_element = trigger_element;
7868 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7874 if (!change_element)
7877 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7880 if (x == lx && y == ly) /* do not change trigger element itself */
7884 if (Feld[x][y] == element)
7886 ChangeDelay[x][y] = 1;
7887 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7888 ChangeElement(x, y, page);
7896 static boolean CheckElementChangeExt(int x, int y,
7898 int trigger_element,
7904 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7907 if (Feld[x][y] == EL_BLOCKED)
7909 Blocked2Moving(x, y, &x, &y);
7910 element = Feld[x][y];
7914 if (Feld[x][y] != element) /* check if element has already changed */
7917 printf("::: %d ('%s') != %d ('%s') [%d]\n",
7918 Feld[x][y], element_info[Feld[x][y]].token_name,
7919 element, element_info[element].token_name,
7928 if (trigger_page < 0)
7930 boolean change_element = FALSE;
7933 for (i = 0; i < element_info[element].num_change_pages; i++)
7935 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7937 if (change->can_change &&
7938 change->events & CH_EVENT_BIT(trigger_event) &&
7939 change->trigger_side & trigger_side &&
7940 change->trigger_player & trigger_player)
7942 change_element = TRUE;
7945 change->actual_trigger_element = trigger_element;
7946 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7952 if (!change_element)
7957 struct ElementInfo *ei = &element_info[element];
7958 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7960 change->actual_trigger_element = trigger_element;
7961 change->actual_trigger_player = EL_PLAYER_1; /* unused */
7966 /* !!! this check misses pages with same event, but different side !!! */
7968 if (trigger_page < 0)
7969 trigger_page = element_info[element].event_page_nr[trigger_event];
7971 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7975 ChangeDelay[x][y] = 1;
7976 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7977 ChangeElement(x, y, trigger_page);
7982 static void PlayPlayerSound(struct PlayerInfo *player)
7984 int jx = player->jx, jy = player->jy;
7985 int element = player->element_nr;
7986 int last_action = player->last_action_waiting;
7987 int action = player->action_waiting;
7989 if (player->is_waiting)
7991 if (action != last_action)
7992 PlayLevelSoundElementAction(jx, jy, element, action);
7994 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7998 if (action != last_action)
7999 StopSound(element_info[element].sound[last_action]);
8001 if (last_action == ACTION_SLEEPING)
8002 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8006 static void PlayAllPlayersSound()
8010 for (i = 0; i < MAX_PLAYERS; i++)
8011 if (stored_player[i].active)
8012 PlayPlayerSound(&stored_player[i]);
8015 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8017 boolean last_waiting = player->is_waiting;
8018 int move_dir = player->MovDir;
8020 player->last_action_waiting = player->action_waiting;
8024 if (!last_waiting) /* not waiting -> waiting */
8026 player->is_waiting = TRUE;
8028 player->frame_counter_bored =
8030 game.player_boring_delay_fixed +
8031 SimpleRND(game.player_boring_delay_random);
8032 player->frame_counter_sleeping =
8034 game.player_sleeping_delay_fixed +
8035 SimpleRND(game.player_sleeping_delay_random);
8037 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8040 if (game.player_sleeping_delay_fixed +
8041 game.player_sleeping_delay_random > 0 &&
8042 player->anim_delay_counter == 0 &&
8043 player->post_delay_counter == 0 &&
8044 FrameCounter >= player->frame_counter_sleeping)
8045 player->is_sleeping = TRUE;
8046 else if (game.player_boring_delay_fixed +
8047 game.player_boring_delay_random > 0 &&
8048 FrameCounter >= player->frame_counter_bored)
8049 player->is_bored = TRUE;
8051 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8052 player->is_bored ? ACTION_BORING :
8055 if (player->is_sleeping)
8057 if (player->num_special_action_sleeping > 0)
8059 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8061 int last_special_action = player->special_action_sleeping;
8062 int num_special_action = player->num_special_action_sleeping;
8063 int special_action =
8064 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8065 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8066 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8067 last_special_action + 1 : ACTION_SLEEPING);
8068 int special_graphic =
8069 el_act_dir2img(player->element_nr, special_action, move_dir);
8071 player->anim_delay_counter =
8072 graphic_info[special_graphic].anim_delay_fixed +
8073 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8074 player->post_delay_counter =
8075 graphic_info[special_graphic].post_delay_fixed +
8076 SimpleRND(graphic_info[special_graphic].post_delay_random);
8078 player->special_action_sleeping = special_action;
8081 if (player->anim_delay_counter > 0)
8083 player->action_waiting = player->special_action_sleeping;
8084 player->anim_delay_counter--;
8086 else if (player->post_delay_counter > 0)
8088 player->post_delay_counter--;
8092 else if (player->is_bored)
8094 if (player->num_special_action_bored > 0)
8096 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8098 int special_action =
8099 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8100 int special_graphic =
8101 el_act_dir2img(player->element_nr, special_action, move_dir);
8103 player->anim_delay_counter =
8104 graphic_info[special_graphic].anim_delay_fixed +
8105 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8106 player->post_delay_counter =
8107 graphic_info[special_graphic].post_delay_fixed +
8108 SimpleRND(graphic_info[special_graphic].post_delay_random);
8110 player->special_action_bored = special_action;
8113 if (player->anim_delay_counter > 0)
8115 player->action_waiting = player->special_action_bored;
8116 player->anim_delay_counter--;
8118 else if (player->post_delay_counter > 0)
8120 player->post_delay_counter--;
8125 else if (last_waiting) /* waiting -> not waiting */
8127 player->is_waiting = FALSE;
8128 player->is_bored = FALSE;
8129 player->is_sleeping = FALSE;
8131 player->frame_counter_bored = -1;
8132 player->frame_counter_sleeping = -1;
8134 player->anim_delay_counter = 0;
8135 player->post_delay_counter = 0;
8137 player->action_waiting = ACTION_DEFAULT;
8139 player->special_action_bored = ACTION_DEFAULT;
8140 player->special_action_sleeping = ACTION_DEFAULT;
8145 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8148 static byte stored_player_action[MAX_PLAYERS];
8149 static int num_stored_actions = 0;
8151 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8152 int left = player_action & JOY_LEFT;
8153 int right = player_action & JOY_RIGHT;
8154 int up = player_action & JOY_UP;
8155 int down = player_action & JOY_DOWN;
8156 int button1 = player_action & JOY_BUTTON_1;
8157 int button2 = player_action & JOY_BUTTON_2;
8158 int dx = (left ? -1 : right ? 1 : 0);
8159 int dy = (up ? -1 : down ? 1 : 0);
8162 stored_player_action[player->index_nr] = 0;
8163 num_stored_actions++;
8167 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8170 if (!player->active || tape.pausing)
8174 printf("::: [%d %d %d %d] [%d %d]\n",
8175 left, right, up, down, button1, button2);
8181 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8186 if (player->MovPos == 0)
8187 CheckGravityMovement(player);
8190 snapped = SnapField(player, dx, dy);
8194 dropped = DropElement(player);
8196 moved = MovePlayer(player, dx, dy);
8199 if (tape.single_step && tape.recording && !tape.pausing)
8201 if (button1 || (dropped && !moved))
8203 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8204 SnapField(player, 0, 0); /* stop snapping */
8208 SetPlayerWaiting(player, FALSE);
8211 return player_action;
8213 stored_player_action[player->index_nr] = player_action;
8219 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8222 /* no actions for this player (no input at player's configured device) */
8224 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8225 SnapField(player, 0, 0);
8226 CheckGravityMovementWhenNotMoving(player);
8228 if (player->MovPos == 0)
8229 SetPlayerWaiting(player, TRUE);
8231 if (player->MovPos == 0) /* needed for tape.playing */
8232 player->is_moving = FALSE;
8234 player->is_dropping = FALSE;
8240 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8242 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8244 TapeRecordAction(stored_player_action);
8245 num_stored_actions = 0;
8252 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8254 static byte stored_player_action[MAX_PLAYERS];
8255 static int num_stored_actions = 0;
8256 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8257 int left = player_action & JOY_LEFT;
8258 int right = player_action & JOY_RIGHT;
8259 int up = player_action & JOY_UP;
8260 int down = player_action & JOY_DOWN;
8261 int button1 = player_action & JOY_BUTTON_1;
8262 int button2 = player_action & JOY_BUTTON_2;
8263 int dx = (left ? -1 : right ? 1 : 0);
8264 int dy = (up ? -1 : down ? 1 : 0);
8266 stored_player_action[player->index_nr] = 0;
8267 num_stored_actions++;
8269 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8271 if (!player->active || tape.pausing)
8276 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8279 snapped = SnapField(player, dx, dy);
8283 dropped = DropElement(player);
8285 moved = MovePlayer(player, dx, dy);
8288 if (tape.single_step && tape.recording && !tape.pausing)
8290 if (button1 || (dropped && !moved))
8292 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8293 SnapField(player, 0, 0); /* stop snapping */
8297 stored_player_action[player->index_nr] = player_action;
8301 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8303 /* no actions for this player (no input at player's configured device) */
8305 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8306 SnapField(player, 0, 0);
8307 CheckGravityMovementWhenNotMoving(player);
8309 if (player->MovPos == 0)
8310 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8312 if (player->MovPos == 0) /* needed for tape.playing */
8313 player->is_moving = FALSE;
8316 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8318 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8320 TapeRecordAction(stored_player_action);
8321 num_stored_actions = 0;
8328 static unsigned long action_delay = 0;
8329 unsigned long action_delay_value;
8330 int magic_wall_x = 0, magic_wall_y = 0;
8331 int i, x, y, element, graphic;
8332 byte *recorded_player_action;
8333 byte summarized_player_action = 0;
8335 byte tape_action[MAX_PLAYERS];
8338 if (game_status != GAME_MODE_PLAYING)
8341 action_delay_value =
8342 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8344 if (tape.playing && tape.warp_forward && !tape.pausing)
8345 action_delay_value = 0;
8347 /* ---------- main game synchronization point ---------- */
8349 WaitUntilDelayReached(&action_delay, action_delay_value);
8351 if (network_playing && !network_player_action_received)
8355 printf("DEBUG: try to get network player actions in time\n");
8359 #if defined(NETWORK_AVALIABLE)
8360 /* last chance to get network player actions without main loop delay */
8364 if (game_status != GAME_MODE_PLAYING)
8367 if (!network_player_action_received)
8371 printf("DEBUG: failed to get network player actions in time\n");
8382 printf("::: getting new tape action [%d]\n", FrameCounter);
8385 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8388 if (recorded_player_action == NULL && tape.pausing)
8393 printf("::: %d\n", stored_player[0].action);
8397 if (recorded_player_action != NULL)
8398 for (i = 0; i < MAX_PLAYERS; i++)
8399 stored_player[i].action = recorded_player_action[i];
8402 for (i = 0; i < MAX_PLAYERS; i++)
8404 summarized_player_action |= stored_player[i].action;
8406 if (!network_playing)
8407 stored_player[i].effective_action = stored_player[i].action;
8410 #if defined(NETWORK_AVALIABLE)
8411 if (network_playing)
8412 SendToServer_MovePlayer(summarized_player_action);
8415 if (!options.network && !setup.team_mode)
8416 local_player->effective_action = summarized_player_action;
8419 if (recorded_player_action != NULL)
8420 for (i = 0; i < MAX_PLAYERS; i++)
8421 stored_player[i].effective_action = recorded_player_action[i];
8425 for (i = 0; i < MAX_PLAYERS; i++)
8427 tape_action[i] = stored_player[i].effective_action;
8429 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8430 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8433 /* only save actions from input devices, but not programmed actions */
8435 TapeRecordAction(tape_action);
8438 for (i = 0; i < MAX_PLAYERS; i++)
8440 int actual_player_action = stored_player[i].effective_action;
8443 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8444 - rnd_equinox_tetrachloride 048
8445 - rnd_equinox_tetrachloride_ii 096
8446 - rnd_emanuel_schmieg 002
8447 - doctor_sloan_ww 001, 020
8449 if (stored_player[i].MovPos == 0)
8450 CheckGravityMovement(&stored_player[i]);
8454 /* overwrite programmed action with tape action */
8455 if (stored_player[i].programmed_action)
8456 actual_player_action = stored_player[i].programmed_action;
8460 if (stored_player[i].programmed_action)
8461 printf("::: %d\n", stored_player[i].programmed_action);
8464 if (recorded_player_action)
8467 if (stored_player[i].programmed_action &&
8468 stored_player[i].programmed_action != recorded_player_action[i])
8469 printf("::: %d: %d <-> %d\n", i,
8470 stored_player[i].programmed_action, recorded_player_action[i]);
8474 actual_player_action = recorded_player_action[i];
8479 /* overwrite tape action with programmed action */
8480 if (stored_player[i].programmed_action)
8481 actual_player_action = stored_player[i].programmed_action;
8486 printf("::: action: %d: %x [%d]\n",
8487 stored_player[i].MovPos, actual_player_action, FrameCounter);
8491 PlayerActions(&stored_player[i], actual_player_action);
8493 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8495 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8496 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8499 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8504 TapeRecordAction(tape_action);
8507 network_player_action_received = FALSE;
8509 ScrollScreen(NULL, SCROLL_GO_ON);
8515 for (i = 0; i < MAX_PLAYERS; i++)
8516 stored_player[i].Frame++;
8520 /* for downwards compatibility, the following code emulates a fixed bug that
8521 occured when pushing elements (causing elements that just made their last
8522 pushing step to already (if possible) make their first falling step in the
8523 same game frame, which is bad); this code is also needed to use the famous
8524 "spring push bug" which is used in older levels and might be wanted to be
8525 used also in newer levels, but in this case the buggy pushing code is only
8526 affecting the "spring" element and no other elements */
8529 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8531 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8534 for (i = 0; i < MAX_PLAYERS; i++)
8536 struct PlayerInfo *player = &stored_player[i];
8541 if (player->active && player->is_pushing && player->is_moving &&
8543 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8544 Feld[x][y] == EL_SPRING))
8546 if (player->active && player->is_pushing && player->is_moving &&
8550 ContinueMoving(x, y);
8552 /* continue moving after pushing (this is actually a bug) */
8553 if (!IS_MOVING(x, y))
8562 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8564 Changed[x][y] = CE_BITMASK_DEFAULT;
8565 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8568 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8570 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8571 printf("GameActions(): This should never happen!\n");
8573 ChangePage[x][y] = -1;
8578 if (WasJustMoving[x][y] > 0)
8579 WasJustMoving[x][y]--;
8580 if (WasJustFalling[x][y] > 0)
8581 WasJustFalling[x][y]--;
8582 if (CheckCollision[x][y] > 0)
8583 CheckCollision[x][y]--;
8588 /* reset finished pushing action (not done in ContinueMoving() to allow
8589 continous pushing animation for elements with zero push delay) */
8590 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8592 ResetGfxAnimation(x, y);
8593 DrawLevelField(x, y);
8598 if (IS_BLOCKED(x, y))
8602 Blocked2Moving(x, y, &oldx, &oldy);
8603 if (!IS_MOVING(oldx, oldy))
8605 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8606 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8607 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8608 printf("GameActions(): This should never happen!\n");
8614 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8616 element = Feld[x][y];
8618 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8620 graphic = el2img(element);
8626 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8628 element = graphic = 0;
8632 if (graphic_info[graphic].anim_global_sync)
8633 GfxFrame[x][y] = FrameCounter;
8635 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8636 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8637 ResetRandomAnimationValue(x, y);
8639 SetRandomAnimationValue(x, y);
8642 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8645 if (IS_INACTIVE(element))
8647 if (IS_ANIMATED(graphic))
8648 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8654 /* this may take place after moving, so 'element' may have changed */
8656 if (IS_CHANGING(x, y))
8658 if (IS_CHANGING(x, y) &&
8659 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8663 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8664 element_info[element].event_page_nr[CE_DELAY]);
8666 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8669 element = Feld[x][y];
8670 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8674 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8679 element = Feld[x][y];
8680 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8682 if (element == EL_MOLE)
8683 printf("::: %d, %d, %d [%d]\n",
8684 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8688 if (element == EL_YAMYAM)
8689 printf("::: %d, %d, %d\n",
8690 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8694 if (IS_ANIMATED(graphic) &&
8698 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8701 if (element == EL_BUG)
8702 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8706 if (element == EL_MOLE)
8707 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8711 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8712 EdelsteinFunkeln(x, y);
8714 else if ((element == EL_ACID ||
8715 element == EL_EXIT_OPEN ||
8716 element == EL_SP_EXIT_OPEN ||
8717 element == EL_SP_TERMINAL ||
8718 element == EL_SP_TERMINAL_ACTIVE ||
8719 element == EL_EXTRA_TIME ||
8720 element == EL_SHIELD_NORMAL ||
8721 element == EL_SHIELD_DEADLY) &&
8722 IS_ANIMATED(graphic))
8723 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8724 else if (IS_MOVING(x, y))
8725 ContinueMoving(x, y);
8726 else if (IS_ACTIVE_BOMB(element))
8727 CheckDynamite(x, y);
8729 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8730 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8732 else if (element == EL_AMOEBA_GROWING)
8733 AmoebeWaechst(x, y);
8734 else if (element == EL_AMOEBA_SHRINKING)
8735 AmoebaDisappearing(x, y);
8737 #if !USE_NEW_AMOEBA_CODE
8738 else if (IS_AMOEBALIVE(element))
8739 AmoebeAbleger(x, y);
8742 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8744 else if (element == EL_EXIT_CLOSED)
8746 else if (element == EL_SP_EXIT_CLOSED)
8748 else if (element == EL_EXPANDABLE_WALL_GROWING)
8750 else if (element == EL_EXPANDABLE_WALL ||
8751 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8752 element == EL_EXPANDABLE_WALL_VERTICAL ||
8753 element == EL_EXPANDABLE_WALL_ANY)
8755 else if (element == EL_FLAMES)
8756 CheckForDragon(x, y);
8758 else if (IS_AUTO_CHANGING(element))
8759 ChangeElement(x, y);
8761 else if (element == EL_EXPLOSION)
8762 ; /* drawing of correct explosion animation is handled separately */
8763 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8764 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8767 /* this may take place after moving, so 'element' may have changed */
8768 if (IS_AUTO_CHANGING(Feld[x][y]))
8769 ChangeElement(x, y);
8772 if (IS_BELT_ACTIVE(element))
8773 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8775 if (game.magic_wall_active)
8777 int jx = local_player->jx, jy = local_player->jy;
8779 /* play the element sound at the position nearest to the player */
8780 if ((element == EL_MAGIC_WALL_FULL ||
8781 element == EL_MAGIC_WALL_ACTIVE ||
8782 element == EL_MAGIC_WALL_EMPTYING ||
8783 element == EL_BD_MAGIC_WALL_FULL ||
8784 element == EL_BD_MAGIC_WALL_ACTIVE ||
8785 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8786 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8794 #if USE_NEW_AMOEBA_CODE
8795 /* new experimental amoeba growth stuff */
8797 if (!(FrameCounter % 8))
8800 static unsigned long random = 1684108901;
8802 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8805 x = (random >> 10) % lev_fieldx;
8806 y = (random >> 20) % lev_fieldy;
8808 x = RND(lev_fieldx);
8809 y = RND(lev_fieldy);
8811 element = Feld[x][y];
8814 if (!IS_PLAYER(x,y) &&
8815 (element == EL_EMPTY ||
8816 CAN_GROW_INTO(element) ||
8817 element == EL_QUICKSAND_EMPTY ||
8818 element == EL_ACID_SPLASH_LEFT ||
8819 element == EL_ACID_SPLASH_RIGHT))
8821 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8822 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8823 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8824 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8825 Feld[x][y] = EL_AMOEBA_DROP;
8828 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8829 if (!IS_PLAYER(x,y) &&
8830 (element == EL_EMPTY ||
8831 element == EL_SAND ||
8832 element == EL_QUICKSAND_EMPTY ||
8833 element == EL_ACID_SPLASH_LEFT ||
8834 element == EL_ACID_SPLASH_RIGHT))
8836 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8837 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8838 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8839 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8840 Feld[x][y] = EL_AMOEBA_DROP;
8844 random = random * 129 + 1;
8850 if (game.explosions_delayed)
8853 game.explosions_delayed = FALSE;
8855 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8857 element = Feld[x][y];
8859 if (ExplodeField[x][y])
8860 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8861 else if (element == EL_EXPLOSION)
8862 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8864 ExplodeField[x][y] = EX_TYPE_NONE;
8867 game.explosions_delayed = TRUE;
8870 if (game.magic_wall_active)
8872 if (!(game.magic_wall_time_left % 4))
8874 int element = Feld[magic_wall_x][magic_wall_y];
8876 if (element == EL_BD_MAGIC_WALL_FULL ||
8877 element == EL_BD_MAGIC_WALL_ACTIVE ||
8878 element == EL_BD_MAGIC_WALL_EMPTYING)
8879 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8881 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8884 if (game.magic_wall_time_left > 0)
8886 game.magic_wall_time_left--;
8887 if (!game.magic_wall_time_left)
8889 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8891 element = Feld[x][y];
8893 if (element == EL_MAGIC_WALL_ACTIVE ||
8894 element == EL_MAGIC_WALL_FULL)
8896 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8897 DrawLevelField(x, y);
8899 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8900 element == EL_BD_MAGIC_WALL_FULL)
8902 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8903 DrawLevelField(x, y);
8907 game.magic_wall_active = FALSE;
8912 if (game.light_time_left > 0)
8914 game.light_time_left--;
8916 if (game.light_time_left == 0)
8917 RedrawAllLightSwitchesAndInvisibleElements();
8920 if (game.timegate_time_left > 0)
8922 game.timegate_time_left--;
8924 if (game.timegate_time_left == 0)
8925 CloseAllOpenTimegates();
8928 for (i = 0; i < MAX_PLAYERS; i++)
8930 struct PlayerInfo *player = &stored_player[i];
8932 if (SHIELD_ON(player))
8934 if (player->shield_deadly_time_left)
8935 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8936 else if (player->shield_normal_time_left)
8937 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8941 if (TimeFrames >= FRAMES_PER_SECOND)
8946 for (i = 0; i < MAX_PLAYERS; i++)
8948 struct PlayerInfo *player = &stored_player[i];
8950 if (SHIELD_ON(player))
8952 player->shield_normal_time_left--;
8954 if (player->shield_deadly_time_left > 0)
8955 player->shield_deadly_time_left--;
8959 if (!level.use_step_counter)
8967 if (TimeLeft <= 10 && setup.time_limit)
8968 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8970 DrawGameValue_Time(TimeLeft);
8972 if (!TimeLeft && setup.time_limit)
8973 for (i = 0; i < MAX_PLAYERS; i++)
8974 KillHero(&stored_player[i]);
8976 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8977 DrawGameValue_Time(TimePlayed);
8980 if (tape.recording || tape.playing)
8981 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8985 PlayAllPlayersSound();
8987 if (options.debug) /* calculate frames per second */
8989 static unsigned long fps_counter = 0;
8990 static int fps_frames = 0;
8991 unsigned long fps_delay_ms = Counter() - fps_counter;
8995 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8997 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9000 fps_counter = Counter();
9003 redraw_mask |= REDRAW_FPS;
9007 if (stored_player[0].jx != stored_player[0].last_jx ||
9008 stored_player[0].jy != stored_player[0].last_jy)
9009 printf("::: %d, %d, %d, %d, %d\n",
9010 stored_player[0].MovDir,
9011 stored_player[0].MovPos,
9012 stored_player[0].GfxPos,
9013 stored_player[0].Frame,
9014 stored_player[0].StepFrame);
9021 for (i = 0; i < MAX_PLAYERS; i++)
9024 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9026 stored_player[i].Frame += move_frames;
9028 if (stored_player[i].MovPos != 0)
9029 stored_player[i].StepFrame += move_frames;
9031 if (stored_player[i].drop_delay > 0)
9032 stored_player[i].drop_delay--;
9037 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9039 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9041 local_player->show_envelope = 0;
9046 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9048 int min_x = x, min_y = y, max_x = x, max_y = y;
9051 for (i = 0; i < MAX_PLAYERS; i++)
9053 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9055 if (!stored_player[i].active || &stored_player[i] == player)
9058 min_x = MIN(min_x, jx);
9059 min_y = MIN(min_y, jy);
9060 max_x = MAX(max_x, jx);
9061 max_y = MAX(max_y, jy);
9064 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9067 static boolean AllPlayersInVisibleScreen()
9071 for (i = 0; i < MAX_PLAYERS; i++)
9073 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9075 if (!stored_player[i].active)
9078 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9085 void ScrollLevel(int dx, int dy)
9087 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9090 BlitBitmap(drawto_field, drawto_field,
9091 FX + TILEX * (dx == -1) - softscroll_offset,
9092 FY + TILEY * (dy == -1) - softscroll_offset,
9093 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9094 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9095 FX + TILEX * (dx == 1) - softscroll_offset,
9096 FY + TILEY * (dy == 1) - softscroll_offset);
9100 x = (dx == 1 ? BX1 : BX2);
9101 for (y = BY1; y <= BY2; y++)
9102 DrawScreenField(x, y);
9107 y = (dy == 1 ? BY1 : BY2);
9108 for (x = BX1; x <= BX2; x++)
9109 DrawScreenField(x, y);
9112 redraw_mask |= REDRAW_FIELD;
9116 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9118 int nextx = x + dx, nexty = y + dy;
9119 int element = Feld[x][y];
9122 element != EL_SP_PORT_LEFT &&
9123 element != EL_SP_GRAVITY_PORT_LEFT &&
9124 element != EL_SP_PORT_HORIZONTAL &&
9125 element != EL_SP_PORT_ANY) ||
9127 element != EL_SP_PORT_RIGHT &&
9128 element != EL_SP_GRAVITY_PORT_RIGHT &&
9129 element != EL_SP_PORT_HORIZONTAL &&
9130 element != EL_SP_PORT_ANY) ||
9132 element != EL_SP_PORT_UP &&
9133 element != EL_SP_GRAVITY_PORT_UP &&
9134 element != EL_SP_PORT_VERTICAL &&
9135 element != EL_SP_PORT_ANY) ||
9137 element != EL_SP_PORT_DOWN &&
9138 element != EL_SP_GRAVITY_PORT_DOWN &&
9139 element != EL_SP_PORT_VERTICAL &&
9140 element != EL_SP_PORT_ANY) ||
9141 !IN_LEV_FIELD(nextx, nexty) ||
9142 !IS_FREE(nextx, nexty))
9149 static boolean canFallDown(struct PlayerInfo *player)
9151 int jx = player->jx, jy = player->jy;
9153 return (IN_LEV_FIELD(jx, jy + 1) &&
9154 (IS_FREE(jx, jy + 1) ||
9155 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9156 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9157 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9160 static boolean canPassField(int x, int y, int move_dir)
9162 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9163 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9164 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9167 int element = Feld[x][y];
9169 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9170 !CAN_MOVE(element) &&
9171 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9172 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9173 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9176 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9178 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9179 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9180 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9184 int nextx = newx + dx;
9185 int nexty = newy + dy;
9189 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9190 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9192 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9194 (IS_DIGGABLE(Feld[newx][newy]) ||
9195 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9196 canPassField(newx, newy, move_dir)));
9199 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9200 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9201 (IS_DIGGABLE(Feld[newx][newy]) ||
9202 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9203 canPassField(newx, newy, move_dir)));
9206 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9207 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9208 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9209 canPassField(newx, newy, move_dir)));
9211 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9212 (IS_DIGGABLE(Feld[newx][newy]) ||
9213 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9214 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9215 !CAN_MOVE(Feld[newx][newy]) &&
9216 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9217 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9218 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9224 static void CheckGravityMovement(struct PlayerInfo *player)
9226 if (game.gravity && !player->programmed_action)
9229 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9230 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9232 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9233 int move_dir_vertical = player->action & MV_VERTICAL;
9237 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9239 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9242 int jx = player->jx, jy = player->jy;
9244 boolean player_is_moving_to_valid_field =
9245 (!player_is_snapping &&
9246 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9247 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9251 (player->last_move_dir & MV_HORIZONTAL ?
9252 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9253 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9257 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9258 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9259 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9260 int new_jx = jx + dx, new_jy = jy + dy;
9261 int nextx = new_jx + dx, nexty = new_jy + dy;
9267 boolean player_can_fall_down = canFallDown(player);
9269 boolean player_can_fall_down =
9270 (IN_LEV_FIELD(jx, jy + 1) &&
9271 (IS_FREE(jx, jy + 1) ||
9272 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9276 boolean player_can_fall_down =
9277 (IN_LEV_FIELD(jx, jy + 1) &&
9278 (IS_FREE(jx, jy + 1)));
9282 boolean player_is_moving_to_valid_field =
9285 !player_is_snapping &&
9289 IN_LEV_FIELD(new_jx, new_jy) &&
9290 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9291 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9292 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9293 IN_LEV_FIELD(nextx, nexty) &&
9294 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9296 IN_LEV_FIELD(new_jx, new_jy) &&
9297 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9298 Feld[new_jx][new_jy] == EL_SAND ||
9299 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9300 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9301 /* !!! extend EL_SAND to anything diggable !!! */
9307 boolean player_is_standing_on_valid_field =
9308 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9309 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9313 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9314 player_can_fall_down,
9315 player_is_standing_on_valid_field,
9316 player_is_moving_to_valid_field,
9317 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9318 player->effective_action,
9319 player->can_fall_into_acid);
9322 if (player_can_fall_down &&
9324 !player_is_standing_on_valid_field &&
9326 !player_is_moving_to_valid_field)
9329 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9330 jx, jy, FrameCounter);
9333 player->programmed_action = MV_DOWN;
9338 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9341 return CheckGravityMovement(player);
9344 if (game.gravity && !player->programmed_action)
9346 int jx = player->jx, jy = player->jy;
9347 boolean field_under_player_is_free =
9348 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9349 boolean player_is_standing_on_valid_field =
9350 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9351 (IS_WALKABLE(Feld[jx][jy]) &&
9352 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9354 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9355 player->programmed_action = MV_DOWN;
9361 -----------------------------------------------------------------------------
9362 dx, dy: direction (non-diagonal) to try to move the player to
9363 real_dx, real_dy: direction as read from input device (can be diagonal)
9366 boolean MovePlayerOneStep(struct PlayerInfo *player,
9367 int dx, int dy, int real_dx, int real_dy)
9370 static int trigger_sides[4][2] =
9372 /* enter side leave side */
9373 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9374 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9375 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9376 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9378 int move_direction = (dx == -1 ? MV_LEFT :
9379 dx == +1 ? MV_RIGHT :
9381 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9382 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9383 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9385 int jx = player->jx, jy = player->jy;
9386 int new_jx = jx + dx, new_jy = jy + dy;
9390 if (!player->active || (!dx && !dy))
9391 return MF_NO_ACTION;
9393 player->MovDir = (dx < 0 ? MV_LEFT :
9396 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9398 if (!IN_LEV_FIELD(new_jx, new_jy))
9399 return MF_NO_ACTION;
9401 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9402 return MF_NO_ACTION;
9405 element = MovingOrBlocked2Element(new_jx, new_jy);
9407 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9410 if (DONT_RUN_INTO(element))
9412 if (element == EL_ACID && dx == 0 && dy == 1)
9414 SplashAcid(new_jx, new_jy);
9415 Feld[jx][jy] = EL_PLAYER_1;
9416 InitMovingField(jx, jy, MV_DOWN);
9417 Store[jx][jy] = EL_ACID;
9418 ContinueMoving(jx, jy);
9422 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9427 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9428 if (can_move != MF_MOVING)
9431 /* check if DigField() has caused relocation of the player */
9432 if (player->jx != jx || player->jy != jy)
9433 return MF_NO_ACTION;
9435 StorePlayer[jx][jy] = 0;
9436 player->last_jx = jx;
9437 player->last_jy = jy;
9438 player->jx = new_jx;
9439 player->jy = new_jy;
9440 StorePlayer[new_jx][new_jy] = player->element_nr;
9443 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9445 player->step_counter++;
9448 player->drop_delay = 0;
9451 PlayerVisit[jx][jy] = FrameCounter;
9453 ScrollPlayer(player, SCROLL_INIT);
9456 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9458 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9460 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9463 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9465 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9466 CE_OTHER_GETS_ENTERED, enter_side);
9467 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9468 CE_ENTERED_BY_PLAYER, enter_side);
9475 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9477 int jx = player->jx, jy = player->jy;
9478 int old_jx = jx, old_jy = jy;
9479 int moved = MF_NO_ACTION;
9482 if (!player->active)
9487 if (player->MovPos == 0)
9489 player->is_moving = FALSE;
9490 player->is_digging = FALSE;
9491 player->is_collecting = FALSE;
9492 player->is_snapping = FALSE;
9493 player->is_pushing = FALSE;
9499 if (!player->active || (!dx && !dy))
9504 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9512 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9513 player->move_delay + player->move_delay_value);
9516 if (!FrameReached(&player->move_delay, player->move_delay_value))
9519 printf("::: can NOT move\n");
9525 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9526 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9533 printf("::: COULD move now\n");
9536 /* store if player is automatically moved to next field */
9537 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9539 /* remove the last programmed player action */
9540 player->programmed_action = 0;
9544 /* should only happen if pre-1.2 tape recordings are played */
9545 /* this is only for backward compatibility */
9547 int original_move_delay_value = player->move_delay_value;
9550 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9554 /* scroll remaining steps with finest movement resolution */
9555 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9557 while (player->MovPos)
9559 ScrollPlayer(player, SCROLL_GO_ON);
9560 ScrollScreen(NULL, SCROLL_GO_ON);
9566 player->move_delay_value = original_move_delay_value;
9569 if (player->last_move_dir & MV_HORIZONTAL)
9571 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9572 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9576 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9577 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9583 if (moved & MF_MOVING && !ScreenMovPos &&
9584 (player == local_player || !options.network))
9586 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9587 int offset = (setup.scroll_delay ? 3 : 0);
9589 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9591 /* actual player has left the screen -- scroll in that direction */
9592 if (jx != old_jx) /* player has moved horizontally */
9593 scroll_x += (jx - old_jx);
9594 else /* player has moved vertically */
9595 scroll_y += (jy - old_jy);
9599 if (jx != old_jx) /* player has moved horizontally */
9601 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9602 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9603 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9605 /* don't scroll over playfield boundaries */
9606 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9607 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9609 /* don't scroll more than one field at a time */
9610 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9612 /* don't scroll against the player's moving direction */
9613 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9614 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9615 scroll_x = old_scroll_x;
9617 else /* player has moved vertically */
9619 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9620 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9621 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9623 /* don't scroll over playfield boundaries */
9624 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9625 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9627 /* don't scroll more than one field at a time */
9628 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9630 /* don't scroll against the player's moving direction */
9631 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9632 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9633 scroll_y = old_scroll_y;
9637 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9639 if (!options.network && !AllPlayersInVisibleScreen())
9641 scroll_x = old_scroll_x;
9642 scroll_y = old_scroll_y;
9646 ScrollScreen(player, SCROLL_INIT);
9647 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9654 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9656 if (!(moved & MF_MOVING) && !player->is_pushing)
9661 player->StepFrame = 0;
9663 if (moved & MF_MOVING)
9666 printf("::: REALLY moves now\n");
9669 if (old_jx != jx && old_jy == jy)
9670 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9671 else if (old_jx == jx && old_jy != jy)
9672 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9674 DrawLevelField(jx, jy); /* for "crumbled sand" */
9676 player->last_move_dir = player->MovDir;
9677 player->is_moving = TRUE;
9679 player->is_snapping = FALSE;
9683 player->is_switching = FALSE;
9686 player->is_dropping = FALSE;
9690 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9693 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9696 int move_direction = player->MovDir;
9698 int enter_side = MV_DIR_OPPOSITE(move_direction);
9699 int leave_side = move_direction;
9701 static int trigger_sides[4][2] =
9703 /* enter side leave side */
9704 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9705 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9706 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9707 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9709 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9710 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9712 int old_element = Feld[old_jx][old_jy];
9713 int new_element = Feld[jx][jy];
9716 /* !!! TEST ONLY !!! */
9717 if (IS_CUSTOM_ELEMENT(old_element))
9718 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9720 player->index_bit, leave_side);
9722 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9724 player->index_bit, leave_side);
9726 if (IS_CUSTOM_ELEMENT(new_element))
9727 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9728 player->index_bit, enter_side);
9730 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9731 CE_OTHER_GETS_ENTERED,
9732 player->index_bit, enter_side);
9742 CheckGravityMovementWhenNotMoving(player);
9745 player->last_move_dir = MV_NO_MOVING;
9747 player->is_moving = FALSE;
9749 #if USE_NEW_MOVEMENT
9750 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
9751 /* ensure that the player is also allowed to move in the next frame */
9752 /* (currently, the player is forced to wait eight frames before he can try
9755 player->move_delay = -1; /* allow direct movement in the next frame */
9759 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9761 TestIfHeroTouchesBadThing(jx, jy);
9762 TestIfPlayerTouchesCustomElement(jx, jy);
9765 if (!player->active)
9771 void ScrollPlayer(struct PlayerInfo *player, int mode)
9773 int jx = player->jx, jy = player->jy;
9774 int last_jx = player->last_jx, last_jy = player->last_jy;
9775 int move_stepsize = TILEX / player->move_delay_value;
9777 if (!player->active || !player->MovPos)
9780 if (mode == SCROLL_INIT)
9782 player->actual_frame_counter = FrameCounter;
9783 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9785 #if USE_NEW_MOVEMENT
9786 if (player->block_last_field &&
9787 Feld[last_jx][last_jy] == EL_EMPTY)
9788 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9790 if (Feld[last_jx][last_jy] == EL_EMPTY)
9791 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9800 else if (!FrameReached(&player->actual_frame_counter, 1))
9803 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9804 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9806 if (!player->block_last_field &&
9807 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9808 Feld[last_jx][last_jy] = EL_EMPTY;
9810 /* before DrawPlayer() to draw correct player graphic for this case */
9811 if (player->MovPos == 0)
9812 CheckGravityMovement(player);
9815 DrawPlayer(player); /* needed here only to cleanup last field */
9818 if (player->MovPos == 0) /* player reached destination field */
9821 if (player->move_delay_reset_counter > 0)
9823 player->move_delay_reset_counter--;
9825 if (player->move_delay_reset_counter == 0)
9827 /* continue with normal speed after quickly moving through gate */
9828 HALVE_PLAYER_SPEED(player);
9830 /* be able to make the next move without delay */
9831 player->move_delay = 0;
9835 if (IS_PASSABLE(Feld[last_jx][last_jy]))
9837 /* continue with normal speed after quickly moving through gate */
9838 HALVE_PLAYER_SPEED(player);
9840 /* be able to make the next move without delay */
9841 player->move_delay = 0;
9845 if (player->block_last_field &&
9846 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9847 Feld[last_jx][last_jy] = EL_EMPTY;
9849 player->last_jx = jx;
9850 player->last_jy = jy;
9852 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9853 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9854 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9856 DrawPlayer(player); /* needed here only to cleanup last field */
9859 if (local_player->friends_still_needed == 0 ||
9860 IS_SP_ELEMENT(Feld[jx][jy]))
9861 player->LevelSolved = player->GameOver = TRUE;
9865 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9866 /* this breaks one level: "machine", level 000 */
9868 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9871 int move_direction = player->MovDir;
9873 int enter_side = MV_DIR_OPPOSITE(move_direction);
9874 int leave_side = move_direction;
9876 static int trigger_sides[4][2] =
9878 /* enter side leave side */
9879 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9880 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9881 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9882 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9884 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9885 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9887 int old_jx = last_jx;
9888 int old_jy = last_jy;
9889 int old_element = Feld[old_jx][old_jy];
9890 int new_element = Feld[jx][jy];
9893 /* !!! TEST ONLY !!! */
9894 if (IS_CUSTOM_ELEMENT(old_element))
9895 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9897 player->index_bit, leave_side);
9899 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9901 player->index_bit, leave_side);
9903 if (IS_CUSTOM_ELEMENT(new_element))
9904 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9905 player->index_bit, enter_side);
9907 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9908 CE_OTHER_GETS_ENTERED,
9909 player->index_bit, enter_side);
9915 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9917 TestIfHeroTouchesBadThing(jx, jy);
9918 TestIfPlayerTouchesCustomElement(jx, jy);
9921 /* needed because pushed element has not yet reached its destination,
9922 so it would trigger a change event at its previous field location */
9923 if (!player->is_pushing)
9925 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9928 if (!player->active)
9932 if (level.use_step_counter)
9942 if (TimeLeft <= 10 && setup.time_limit)
9943 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9945 DrawGameValue_Time(TimeLeft);
9947 if (!TimeLeft && setup.time_limit)
9948 for (i = 0; i < MAX_PLAYERS; i++)
9949 KillHero(&stored_player[i]);
9951 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9952 DrawGameValue_Time(TimePlayed);
9955 if (tape.single_step && tape.recording && !tape.pausing &&
9956 !player->programmed_action)
9957 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9961 void ScrollScreen(struct PlayerInfo *player, int mode)
9963 static unsigned long screen_frame_counter = 0;
9965 if (mode == SCROLL_INIT)
9967 /* set scrolling step size according to actual player's moving speed */
9968 ScrollStepSize = TILEX / player->move_delay_value;
9970 screen_frame_counter = FrameCounter;
9971 ScreenMovDir = player->MovDir;
9972 ScreenMovPos = player->MovPos;
9973 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9976 else if (!FrameReached(&screen_frame_counter, 1))
9981 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9982 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9983 redraw_mask |= REDRAW_FIELD;
9986 ScreenMovDir = MV_NO_MOVING;
9989 void TestIfPlayerTouchesCustomElement(int x, int y)
9991 static int xy[4][2] =
9998 static int trigger_sides[4][2] =
10000 /* center side border side */
10001 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10002 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10003 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10004 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10006 static int touch_dir[4] =
10008 MV_LEFT | MV_RIGHT,
10013 int center_element = Feld[x][y]; /* should always be non-moving! */
10016 for (i = 0; i < NUM_DIRECTIONS; i++)
10018 int xx = x + xy[i][0];
10019 int yy = y + xy[i][1];
10020 int center_side = trigger_sides[i][0];
10021 int border_side = trigger_sides[i][1];
10022 int border_element;
10024 if (!IN_LEV_FIELD(xx, yy))
10027 if (IS_PLAYER(x, y))
10029 struct PlayerInfo *player = PLAYERINFO(x, y);
10031 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10032 border_element = Feld[xx][yy]; /* may be moving! */
10033 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10034 border_element = Feld[xx][yy];
10035 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10036 border_element = MovingOrBlocked2Element(xx, yy);
10038 continue; /* center and border element do not touch */
10041 /* !!! TEST ONLY !!! */
10042 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10043 player->index_bit, border_side);
10044 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10045 CE_OTHER_GETS_TOUCHED,
10046 player->index_bit, border_side);
10048 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10049 CE_OTHER_GETS_TOUCHED,
10050 player->index_bit, border_side);
10051 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10052 player->index_bit, border_side);
10055 else if (IS_PLAYER(xx, yy))
10057 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10059 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10061 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10062 continue; /* center and border element do not touch */
10066 /* !!! TEST ONLY !!! */
10067 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10068 player->index_bit, center_side);
10069 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10070 CE_OTHER_GETS_TOUCHED,
10071 player->index_bit, center_side);
10073 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10074 CE_OTHER_GETS_TOUCHED,
10075 player->index_bit, center_side);
10076 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10077 player->index_bit, center_side);
10085 void TestIfElementTouchesCustomElement(int x, int y)
10087 static int xy[4][2] =
10094 static int trigger_sides[4][2] =
10096 /* center side border side */
10097 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10098 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10099 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10100 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10102 static int touch_dir[4] =
10104 MV_LEFT | MV_RIGHT,
10109 boolean change_center_element = FALSE;
10110 int center_element_change_page = 0;
10111 int center_element = Feld[x][y]; /* should always be non-moving! */
10112 int border_trigger_element = EL_UNDEFINED;
10115 for (i = 0; i < NUM_DIRECTIONS; i++)
10117 int xx = x + xy[i][0];
10118 int yy = y + xy[i][1];
10119 int center_side = trigger_sides[i][0];
10120 int border_side = trigger_sides[i][1];
10121 int border_element;
10123 if (!IN_LEV_FIELD(xx, yy))
10126 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10127 border_element = Feld[xx][yy]; /* may be moving! */
10128 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10129 border_element = Feld[xx][yy];
10130 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10131 border_element = MovingOrBlocked2Element(xx, yy);
10133 continue; /* center and border element do not touch */
10135 /* check for change of center element (but change it only once) */
10136 if (IS_CUSTOM_ELEMENT(center_element) &&
10137 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10138 !change_center_element)
10140 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10142 struct ElementChangeInfo *change =
10143 &element_info[center_element].change_page[j];
10145 if (change->can_change &&
10146 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10147 change->trigger_side & border_side &&
10149 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10151 change->trigger_element == border_element
10155 change_center_element = TRUE;
10156 center_element_change_page = j;
10157 border_trigger_element = border_element;
10164 /* check for change of border element */
10165 if (IS_CUSTOM_ELEMENT(border_element) &&
10166 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10168 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10170 struct ElementChangeInfo *change =
10171 &element_info[border_element].change_page[j];
10173 if (change->can_change &&
10174 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10175 change->trigger_side & center_side &&
10177 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10179 change->trigger_element == center_element
10184 printf("::: border_element %d, %d\n", x, y);
10187 CheckElementChangeByPage(xx, yy, border_element, center_element,
10188 CE_OTHER_IS_TOUCHING, j);
10195 if (change_center_element)
10198 printf("::: center_element %d, %d\n", x, y);
10201 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10202 CE_OTHER_IS_TOUCHING, center_element_change_page);
10206 void TestIfElementHitsCustomElement(int x, int y, int direction)
10208 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10209 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10210 int hitx = x + dx, hity = y + dy;
10211 int hitting_element = Feld[x][y];
10212 int touched_element;
10214 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10215 !IS_FREE(hitx, hity) &&
10216 (!IS_MOVING(hitx, hity) ||
10217 MovDir[hitx][hity] != direction ||
10218 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10221 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10225 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10229 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10230 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10232 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10233 CE_HITTING_SOMETHING, direction);
10235 if (IN_LEV_FIELD(hitx, hity))
10237 int opposite_direction = MV_DIR_OPPOSITE(direction);
10238 int hitting_side = direction;
10239 int touched_side = opposite_direction;
10241 int touched_element = MovingOrBlocked2Element(hitx, hity);
10244 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10245 MovDir[hitx][hity] != direction ||
10246 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10255 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10256 CE_HIT_BY_SOMETHING, opposite_direction);
10258 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10259 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10261 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10263 struct ElementChangeInfo *change =
10264 &element_info[hitting_element].change_page[i];
10266 if (change->can_change &&
10267 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10268 change->trigger_side & touched_side &&
10271 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10273 change->trigger_element == touched_element
10277 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10278 CE_OTHER_IS_HITTING, i);
10284 if (IS_CUSTOM_ELEMENT(touched_element) &&
10285 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10287 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10289 struct ElementChangeInfo *change =
10290 &element_info[touched_element].change_page[i];
10292 if (change->can_change &&
10293 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10294 change->trigger_side & hitting_side &&
10296 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10298 change->trigger_element == hitting_element
10302 CheckElementChangeByPage(hitx, hity, touched_element,
10303 hitting_element, CE_OTHER_GETS_HIT, i);
10313 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10315 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10316 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10317 int hitx = x + dx, hity = y + dy;
10318 int hitting_element = Feld[x][y];
10319 int touched_element;
10321 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10322 !IS_FREE(hitx, hity) &&
10323 (!IS_MOVING(hitx, hity) ||
10324 MovDir[hitx][hity] != direction ||
10325 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10328 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10332 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10336 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10337 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10339 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10340 EP_CAN_SMASH_EVERYTHING, direction);
10342 if (IN_LEV_FIELD(hitx, hity))
10344 int opposite_direction = MV_DIR_OPPOSITE(direction);
10345 int hitting_side = direction;
10346 int touched_side = opposite_direction;
10348 int touched_element = MovingOrBlocked2Element(hitx, hity);
10351 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10352 MovDir[hitx][hity] != direction ||
10353 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10362 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10363 CE_SMASHED_BY_SOMETHING, opposite_direction);
10365 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10366 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10368 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10370 struct ElementChangeInfo *change =
10371 &element_info[hitting_element].change_page[i];
10373 if (change->can_change &&
10374 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10375 change->trigger_side & touched_side &&
10378 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10380 change->trigger_element == touched_element
10384 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10385 CE_OTHER_IS_SMASHING, i);
10391 if (IS_CUSTOM_ELEMENT(touched_element) &&
10392 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10394 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10396 struct ElementChangeInfo *change =
10397 &element_info[touched_element].change_page[i];
10399 if (change->can_change &&
10400 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10401 change->trigger_side & hitting_side &&
10403 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10405 change->trigger_element == hitting_element
10409 CheckElementChangeByPage(hitx, hity, touched_element,
10410 hitting_element, CE_OTHER_GETS_SMASHED,i);
10420 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10422 int i, kill_x = -1, kill_y = -1;
10423 int bad_element = -1;
10424 static int test_xy[4][2] =
10431 static int test_dir[4] =
10439 for (i = 0; i < NUM_DIRECTIONS; i++)
10441 int test_x, test_y, test_move_dir, test_element;
10443 test_x = good_x + test_xy[i][0];
10444 test_y = good_y + test_xy[i][1];
10446 if (!IN_LEV_FIELD(test_x, test_y))
10450 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10453 test_element = Feld[test_x][test_y];
10455 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10458 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10459 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10461 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10462 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10466 bad_element = test_element;
10472 if (kill_x != -1 || kill_y != -1)
10474 if (IS_PLAYER(good_x, good_y))
10476 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10479 if (player->shield_deadly_time_left > 0 &&
10480 !IS_INDESTRUCTIBLE(bad_element))
10481 Bang(kill_x, kill_y);
10482 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10485 if (player->shield_deadly_time_left > 0)
10486 Bang(kill_x, kill_y);
10487 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10492 Bang(good_x, good_y);
10496 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10498 int i, kill_x = -1, kill_y = -1;
10499 int bad_element = Feld[bad_x][bad_y];
10500 static int test_xy[4][2] =
10507 static int touch_dir[4] =
10509 MV_LEFT | MV_RIGHT,
10514 static int test_dir[4] =
10522 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10525 for (i = 0; i < NUM_DIRECTIONS; i++)
10527 int test_x, test_y, test_move_dir, test_element;
10529 test_x = bad_x + test_xy[i][0];
10530 test_y = bad_y + test_xy[i][1];
10531 if (!IN_LEV_FIELD(test_x, test_y))
10535 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10537 test_element = Feld[test_x][test_y];
10539 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10540 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10542 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10543 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10545 /* good thing is player or penguin that does not move away */
10546 if (IS_PLAYER(test_x, test_y))
10548 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10550 if (bad_element == EL_ROBOT && player->is_moving)
10551 continue; /* robot does not kill player if he is moving */
10553 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10555 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10556 continue; /* center and border element do not touch */
10563 else if (test_element == EL_PENGUIN)
10572 if (kill_x != -1 || kill_y != -1)
10574 if (IS_PLAYER(kill_x, kill_y))
10576 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10579 if (player->shield_deadly_time_left > 0 &&
10580 !IS_INDESTRUCTIBLE(bad_element))
10581 Bang(bad_x, bad_y);
10582 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10585 if (player->shield_deadly_time_left > 0)
10586 Bang(bad_x, bad_y);
10587 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10592 Bang(kill_x, kill_y);
10596 void TestIfHeroTouchesBadThing(int x, int y)
10598 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10601 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10603 TestIfGoodThingHitsBadThing(x, y, move_dir);
10606 void TestIfBadThingTouchesHero(int x, int y)
10608 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10611 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10613 TestIfBadThingHitsGoodThing(x, y, move_dir);
10616 void TestIfFriendTouchesBadThing(int x, int y)
10618 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10621 void TestIfBadThingTouchesFriend(int x, int y)
10623 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10626 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10628 int i, kill_x = bad_x, kill_y = bad_y;
10629 static int xy[4][2] =
10637 for (i = 0; i < NUM_DIRECTIONS; i++)
10641 x = bad_x + xy[i][0];
10642 y = bad_y + xy[i][1];
10643 if (!IN_LEV_FIELD(x, y))
10646 element = Feld[x][y];
10647 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10648 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10656 if (kill_x != bad_x || kill_y != bad_y)
10657 Bang(bad_x, bad_y);
10660 void KillHero(struct PlayerInfo *player)
10662 int jx = player->jx, jy = player->jy;
10664 if (!player->active)
10667 /* remove accessible field at the player's position */
10668 Feld[jx][jy] = EL_EMPTY;
10670 /* deactivate shield (else Bang()/Explode() would not work right) */
10671 player->shield_normal_time_left = 0;
10672 player->shield_deadly_time_left = 0;
10678 static void KillHeroUnlessEnemyProtected(int x, int y)
10680 if (!PLAYER_ENEMY_PROTECTED(x, y))
10681 KillHero(PLAYERINFO(x, y));
10684 static void KillHeroUnlessExplosionProtected(int x, int y)
10686 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10687 KillHero(PLAYERINFO(x, y));
10690 void BuryHero(struct PlayerInfo *player)
10692 int jx = player->jx, jy = player->jy;
10694 if (!player->active)
10698 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10700 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10702 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10704 player->GameOver = TRUE;
10705 RemoveHero(player);
10708 void RemoveHero(struct PlayerInfo *player)
10710 int jx = player->jx, jy = player->jy;
10711 int i, found = FALSE;
10713 player->present = FALSE;
10714 player->active = FALSE;
10716 if (!ExplodeField[jx][jy])
10717 StorePlayer[jx][jy] = 0;
10719 for (i = 0; i < MAX_PLAYERS; i++)
10720 if (stored_player[i].active)
10724 AllPlayersGone = TRUE;
10731 =============================================================================
10732 checkDiagonalPushing()
10733 -----------------------------------------------------------------------------
10734 check if diagonal input device direction results in pushing of object
10735 (by checking if the alternative direction is walkable, diggable, ...)
10736 =============================================================================
10739 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10740 int x, int y, int real_dx, int real_dy)
10742 int jx, jy, dx, dy, xx, yy;
10744 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10747 /* diagonal direction: check alternative direction */
10752 xx = jx + (dx == 0 ? real_dx : 0);
10753 yy = jy + (dy == 0 ? real_dy : 0);
10755 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10759 =============================================================================
10761 -----------------------------------------------------------------------------
10762 x, y: field next to player (non-diagonal) to try to dig to
10763 real_dx, real_dy: direction as read from input device (can be diagonal)
10764 =============================================================================
10767 int DigField(struct PlayerInfo *player,
10768 int oldx, int oldy, int x, int y,
10769 int real_dx, int real_dy, int mode)
10772 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10774 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10775 boolean player_was_pushing = player->is_pushing;
10776 int jx = oldx, jy = oldy;
10777 int dx = x - jx, dy = y - jy;
10778 int nextx = x + dx, nexty = y + dy;
10779 int move_direction = (dx == -1 ? MV_LEFT :
10780 dx == +1 ? MV_RIGHT :
10782 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10783 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10785 int dig_side = MV_DIR_OPPOSITE(move_direction);
10787 static int trigger_sides[4] =
10789 CH_SIDE_RIGHT, /* moving left */
10790 CH_SIDE_LEFT, /* moving right */
10791 CH_SIDE_BOTTOM, /* moving up */
10792 CH_SIDE_TOP, /* moving down */
10794 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10796 int old_element = Feld[jx][jy];
10799 if (is_player) /* function can also be called by EL_PENGUIN */
10801 if (player->MovPos == 0)
10803 player->is_digging = FALSE;
10804 player->is_collecting = FALSE;
10807 if (player->MovPos == 0) /* last pushing move finished */
10808 player->is_pushing = FALSE;
10810 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10812 player->is_switching = FALSE;
10813 player->push_delay = 0;
10815 return MF_NO_ACTION;
10819 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10820 return MF_NO_ACTION;
10825 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10827 if (IS_TUBE(Feld[jx][jy]) ||
10828 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10832 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10833 int tube_leave_directions[][2] =
10835 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10836 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10837 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10838 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
10839 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
10840 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
10841 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
10842 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
10843 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
10844 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
10845 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
10846 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10849 while (tube_leave_directions[i][0] != tube_element)
10852 if (tube_leave_directions[i][0] == -1) /* should not happen */
10856 if (!(tube_leave_directions[i][1] & move_direction))
10857 return MF_NO_ACTION; /* tube has no opening in this direction */
10862 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10863 old_element = Back[jx][jy];
10867 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10868 return MF_NO_ACTION; /* field has no opening in this direction */
10870 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10871 return MF_NO_ACTION; /* field has no opening in this direction */
10873 element = Feld[x][y];
10875 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
10876 return MF_NO_ACTION;
10878 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10879 game.engine_version >= VERSION_IDENT(2,2,0,0))
10880 return MF_NO_ACTION;
10883 if (game.gravity && is_player && !player->is_auto_moving &&
10884 canFallDown(player) && move_direction != MV_DOWN &&
10885 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10886 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10890 if (element == EL_EMPTY_SPACE &&
10891 game.gravity && !player->is_auto_moving &&
10892 canFallDown(player) && move_direction != MV_DOWN)
10893 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10899 case EL_SP_PORT_LEFT:
10900 case EL_SP_PORT_RIGHT:
10901 case EL_SP_PORT_UP:
10902 case EL_SP_PORT_DOWN:
10903 case EL_SP_PORT_HORIZONTAL:
10904 case EL_SP_PORT_VERTICAL:
10905 case EL_SP_PORT_ANY:
10906 case EL_SP_GRAVITY_PORT_LEFT:
10907 case EL_SP_GRAVITY_PORT_RIGHT:
10908 case EL_SP_GRAVITY_PORT_UP:
10909 case EL_SP_GRAVITY_PORT_DOWN:
10911 if (!canEnterSupaplexPort(x, y, dx, dy))
10912 return MF_NO_ACTION;
10915 element != EL_SP_PORT_LEFT &&
10916 element != EL_SP_GRAVITY_PORT_LEFT &&
10917 element != EL_SP_PORT_HORIZONTAL &&
10918 element != EL_SP_PORT_ANY) ||
10920 element != EL_SP_PORT_RIGHT &&
10921 element != EL_SP_GRAVITY_PORT_RIGHT &&
10922 element != EL_SP_PORT_HORIZONTAL &&
10923 element != EL_SP_PORT_ANY) ||
10925 element != EL_SP_PORT_UP &&
10926 element != EL_SP_GRAVITY_PORT_UP &&
10927 element != EL_SP_PORT_VERTICAL &&
10928 element != EL_SP_PORT_ANY) ||
10930 element != EL_SP_PORT_DOWN &&
10931 element != EL_SP_GRAVITY_PORT_DOWN &&
10932 element != EL_SP_PORT_VERTICAL &&
10933 element != EL_SP_PORT_ANY) ||
10934 !IN_LEV_FIELD(nextx, nexty) ||
10935 !IS_FREE(nextx, nexty))
10936 return MF_NO_ACTION;
10939 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10940 element == EL_SP_GRAVITY_PORT_RIGHT ||
10941 element == EL_SP_GRAVITY_PORT_UP ||
10942 element == EL_SP_GRAVITY_PORT_DOWN)
10943 game.gravity = !game.gravity;
10945 /* automatically move to the next field with double speed */
10946 player->programmed_action = move_direction;
10948 if (player->move_delay_reset_counter == 0)
10950 player->move_delay_reset_counter = 2; /* two double speed steps */
10952 DOUBLE_PLAYER_SPEED(player);
10955 player->move_delay_reset_counter = 2;
10957 DOUBLE_PLAYER_SPEED(player);
10961 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10964 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10970 case EL_TUBE_VERTICAL:
10971 case EL_TUBE_HORIZONTAL:
10972 case EL_TUBE_VERTICAL_LEFT:
10973 case EL_TUBE_VERTICAL_RIGHT:
10974 case EL_TUBE_HORIZONTAL_UP:
10975 case EL_TUBE_HORIZONTAL_DOWN:
10976 case EL_TUBE_LEFT_UP:
10977 case EL_TUBE_LEFT_DOWN:
10978 case EL_TUBE_RIGHT_UP:
10979 case EL_TUBE_RIGHT_DOWN:
10982 int tube_enter_directions[][2] =
10984 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10985 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10986 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10987 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
10988 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
10989 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
10990 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
10991 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
10992 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
10993 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
10994 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
10995 { -1, MV_NO_MOVING }
10998 while (tube_enter_directions[i][0] != element)
11001 if (tube_enter_directions[i][0] == -1) /* should not happen */
11005 if (!(tube_enter_directions[i][1] & move_direction))
11006 return MF_NO_ACTION; /* tube has no opening in this direction */
11008 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11016 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11018 if (IS_WALKABLE(element))
11021 int sound_element = SND_ELEMENT(element);
11022 int sound_action = ACTION_WALKING;
11025 if (!ACCESS_FROM(element, opposite_direction))
11026 return MF_NO_ACTION; /* field not accessible from this direction */
11030 if (element == EL_EMPTY_SPACE &&
11031 game.gravity && !player->is_auto_moving &&
11032 canFallDown(player) && move_direction != MV_DOWN)
11033 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11036 if (IS_GATE(element))
11038 if (!player->key[element - EL_GATE_1])
11039 return MF_NO_ACTION;
11041 else if (IS_GATE_GRAY(element))
11043 if (!player->key[element - EL_GATE_1_GRAY])
11044 return MF_NO_ACTION;
11046 else if (element == EL_EXIT_OPEN ||
11047 element == EL_SP_EXIT_OPEN ||
11048 element == EL_SP_EXIT_OPENING)
11050 sound_action = ACTION_PASSING; /* player is passing exit */
11052 else if (element == EL_EMPTY)
11054 sound_action = ACTION_MOVING; /* nothing to walk on */
11057 /* play sound from background or player, whatever is available */
11058 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11059 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11061 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11066 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11068 else if (IS_PASSABLE(element))
11072 if (!canPassField(x, y, move_direction))
11073 return MF_NO_ACTION;
11078 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11079 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11080 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11081 return MF_NO_ACTION;
11083 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11084 return MF_NO_ACTION;
11089 if (!ACCESS_FROM(element, opposite_direction))
11090 return MF_NO_ACTION; /* field not accessible from this direction */
11092 if (IS_CUSTOM_ELEMENT(element) &&
11093 !ACCESS_FROM(element, opposite_direction))
11094 return MF_NO_ACTION; /* field not accessible from this direction */
11098 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11099 return MF_NO_ACTION;
11104 if (IS_EM_GATE(element))
11106 if (!player->key[element - EL_EM_GATE_1])
11107 return MF_NO_ACTION;
11109 else if (IS_EM_GATE_GRAY(element))
11111 if (!player->key[element - EL_EM_GATE_1_GRAY])
11112 return MF_NO_ACTION;
11114 else if (IS_SP_PORT(element))
11116 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11117 element == EL_SP_GRAVITY_PORT_RIGHT ||
11118 element == EL_SP_GRAVITY_PORT_UP ||
11119 element == EL_SP_GRAVITY_PORT_DOWN)
11120 game.gravity = !game.gravity;
11121 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11122 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11123 element == EL_SP_GRAVITY_ON_PORT_UP ||
11124 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11125 game.gravity = TRUE;
11126 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11127 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11128 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11129 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11130 game.gravity = FALSE;
11133 /* automatically move to the next field with double speed */
11134 player->programmed_action = move_direction;
11136 if (player->move_delay_reset_counter == 0)
11138 player->move_delay_reset_counter = 2; /* two double speed steps */
11140 DOUBLE_PLAYER_SPEED(player);
11143 player->move_delay_reset_counter = 2;
11145 DOUBLE_PLAYER_SPEED(player);
11148 PlayLevelSoundAction(x, y, ACTION_PASSING);
11152 else if (IS_DIGGABLE(element))
11156 if (mode != DF_SNAP)
11159 GfxElement[x][y] = GFX_ELEMENT(element);
11162 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11164 player->is_digging = TRUE;
11167 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11169 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11170 player->index_bit, dig_side);
11173 if (mode == DF_SNAP)
11174 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11179 else if (IS_COLLECTIBLE(element))
11183 if (is_player && mode != DF_SNAP)
11185 GfxElement[x][y] = element;
11186 player->is_collecting = TRUE;
11189 if (element == EL_SPEED_PILL)
11190 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11191 else if (element == EL_EXTRA_TIME && level.time > 0)
11194 DrawGameValue_Time(TimeLeft);
11196 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11198 player->shield_normal_time_left += 10;
11199 if (element == EL_SHIELD_DEADLY)
11200 player->shield_deadly_time_left += 10;
11202 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11204 if (player->inventory_size < MAX_INVENTORY_SIZE)
11205 player->inventory_element[player->inventory_size++] = element;
11207 DrawGameValue_Dynamite(local_player->inventory_size);
11209 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11211 player->dynabomb_count++;
11212 player->dynabombs_left++;
11214 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11216 player->dynabomb_size++;
11218 else if (element == EL_DYNABOMB_INCREASE_POWER)
11220 player->dynabomb_xl = TRUE;
11222 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
11223 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
11225 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
11226 element - EL_KEY_1 : element - EL_EM_KEY_1);
11228 player->key[key_nr] = TRUE;
11230 DrawGameValue_Keys(player);
11232 redraw_mask |= REDRAW_DOOR_1;
11234 else if (IS_ENVELOPE(element))
11237 player->show_envelope = element;
11239 ShowEnvelope(element - EL_ENVELOPE_1);
11242 else if (IS_DROPPABLE(element) ||
11243 IS_THROWABLE(element)) /* can be collected and dropped */
11247 if (element_info[element].collect_count == 0)
11248 player->inventory_infinite_element = element;
11250 for (i = 0; i < element_info[element].collect_count; i++)
11251 if (player->inventory_size < MAX_INVENTORY_SIZE)
11252 player->inventory_element[player->inventory_size++] = element;
11254 DrawGameValue_Dynamite(local_player->inventory_size);
11256 else if (element_info[element].collect_count > 0)
11258 local_player->gems_still_needed -=
11259 element_info[element].collect_count;
11260 if (local_player->gems_still_needed < 0)
11261 local_player->gems_still_needed = 0;
11263 DrawGameValue_Emeralds(local_player->gems_still_needed);
11266 RaiseScoreElement(element);
11267 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11270 CheckTriggeredElementChangeByPlayer(x, y, element,
11271 CE_OTHER_GETS_COLLECTED,
11272 player->index_bit, dig_side);
11275 if (mode == DF_SNAP)
11276 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11281 else if (IS_PUSHABLE(element))
11283 if (mode == DF_SNAP && element != EL_BD_ROCK)
11284 return MF_NO_ACTION;
11286 if (CAN_FALL(element) && dy)
11287 return MF_NO_ACTION;
11289 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11290 !(element == EL_SPRING && level.use_spring_bug))
11291 return MF_NO_ACTION;
11294 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11295 ((move_direction & MV_VERTICAL &&
11296 ((element_info[element].move_pattern & MV_LEFT &&
11297 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11298 (element_info[element].move_pattern & MV_RIGHT &&
11299 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11300 (move_direction & MV_HORIZONTAL &&
11301 ((element_info[element].move_pattern & MV_UP &&
11302 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11303 (element_info[element].move_pattern & MV_DOWN &&
11304 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11305 return MF_NO_ACTION;
11309 /* do not push elements already moving away faster than player */
11310 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11311 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11312 return MF_NO_ACTION;
11314 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11315 return MF_NO_ACTION;
11321 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11323 if (player->push_delay_value == -1 || !player_was_pushing)
11324 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11326 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11328 if (player->push_delay_value == -1)
11329 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11332 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11334 if (player->push_delay_value == -1 || !player_was_pushing)
11335 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11338 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11340 if (!player->is_pushing)
11341 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11345 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11346 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11347 !player_is_pushing))
11348 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11351 if (!player->is_pushing &&
11352 game.engine_version >= VERSION_IDENT(2,2,0,7))
11353 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11357 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11358 player->push_delay, player->push_delay_value,
11359 FrameCounter, game.engine_version,
11360 player_was_pushing, player->is_pushing,
11361 element, element_info[element].token_name,
11362 GET_NEW_PUSH_DELAY(element));
11365 player->is_pushing = TRUE;
11367 if (!(IN_LEV_FIELD(nextx, nexty) &&
11368 (IS_FREE(nextx, nexty) ||
11369 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11370 IS_SB_ELEMENT(element)))))
11371 return MF_NO_ACTION;
11373 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11374 return MF_NO_ACTION;
11376 if (player->push_delay == 0) /* new pushing; restart delay */
11377 player->push_delay = FrameCounter;
11379 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11380 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11381 element != EL_SPRING && element != EL_BALLOON)
11383 /* make sure that there is no move delay before next try to push */
11384 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11385 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11387 return MF_NO_ACTION;
11391 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11394 if (IS_SB_ELEMENT(element))
11396 if (element == EL_SOKOBAN_FIELD_FULL)
11398 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11399 local_player->sokobanfields_still_needed++;
11402 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11404 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11405 local_player->sokobanfields_still_needed--;
11408 Feld[x][y] = EL_SOKOBAN_OBJECT;
11410 if (Back[x][y] == Back[nextx][nexty])
11411 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11412 else if (Back[x][y] != 0)
11413 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11416 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11419 if (local_player->sokobanfields_still_needed == 0 &&
11420 game.emulation == EMU_SOKOBAN)
11422 player->LevelSolved = player->GameOver = TRUE;
11423 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11427 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11429 InitMovingField(x, y, move_direction);
11430 GfxAction[x][y] = ACTION_PUSHING;
11432 if (mode == DF_SNAP)
11433 ContinueMoving(x, y);
11435 MovPos[x][y] = (dx != 0 ? dx : dy);
11437 Pushed[x][y] = TRUE;
11438 Pushed[nextx][nexty] = TRUE;
11440 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11441 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11443 player->push_delay_value = -1; /* get new value later */
11446 /* check for element change _after_ element has been pushed! */
11450 /* !!! TEST ONLY !!! */
11451 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11452 player->index_bit, dig_side);
11453 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11454 player->index_bit, dig_side);
11456 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11457 player->index_bit, dig_side);
11458 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11459 player->index_bit, dig_side);
11465 else if (IS_SWITCHABLE(element))
11467 if (PLAYER_SWITCHING(player, x, y))
11469 CheckTriggeredElementChangeByPlayer(x,y, element,
11470 CE_OTHER_GETS_PRESSED,
11471 player->index_bit, dig_side);
11476 player->is_switching = TRUE;
11477 player->switch_x = x;
11478 player->switch_y = y;
11480 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11482 if (element == EL_ROBOT_WHEEL)
11484 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11488 DrawLevelField(x, y);
11490 else if (element == EL_SP_TERMINAL)
11494 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11496 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11498 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11499 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11502 else if (IS_BELT_SWITCH(element))
11504 ToggleBeltSwitch(x, y);
11506 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11507 element == EL_SWITCHGATE_SWITCH_DOWN)
11509 ToggleSwitchgateSwitch(x, y);
11511 else if (element == EL_LIGHT_SWITCH ||
11512 element == EL_LIGHT_SWITCH_ACTIVE)
11514 ToggleLightSwitch(x, y);
11517 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11518 SND_LIGHT_SWITCH_ACTIVATING :
11519 SND_LIGHT_SWITCH_DEACTIVATING);
11522 else if (element == EL_TIMEGATE_SWITCH)
11524 ActivateTimegateSwitch(x, y);
11526 else if (element == EL_BALLOON_SWITCH_LEFT ||
11527 element == EL_BALLOON_SWITCH_RIGHT ||
11528 element == EL_BALLOON_SWITCH_UP ||
11529 element == EL_BALLOON_SWITCH_DOWN ||
11530 element == EL_BALLOON_SWITCH_ANY)
11532 if (element == EL_BALLOON_SWITCH_ANY)
11533 game.balloon_dir = move_direction;
11535 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11536 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11537 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11538 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11541 else if (element == EL_LAMP)
11543 Feld[x][y] = EL_LAMP_ACTIVE;
11544 local_player->lights_still_needed--;
11546 DrawLevelField(x, y);
11548 else if (element == EL_TIME_ORB_FULL)
11550 Feld[x][y] = EL_TIME_ORB_EMPTY;
11552 DrawGameValue_Time(TimeLeft);
11554 DrawLevelField(x, y);
11557 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11561 CheckTriggeredElementChangeByPlayer(x, y, element,
11562 CE_OTHER_IS_SWITCHING,
11563 player->index_bit, dig_side);
11565 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11566 player->index_bit, dig_side);
11572 if (!PLAYER_SWITCHING(player, x, y))
11574 player->is_switching = TRUE;
11575 player->switch_x = x;
11576 player->switch_y = y;
11579 /* !!! TEST ONLY !!! */
11580 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11581 player->index_bit, dig_side);
11582 CheckTriggeredElementChangeByPlayer(x, y, element,
11583 CE_OTHER_IS_SWITCHING,
11584 player->index_bit, dig_side);
11586 CheckTriggeredElementChangeByPlayer(x, y, element,
11587 CE_OTHER_IS_SWITCHING,
11588 player->index_bit, dig_side);
11589 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11590 player->index_bit, dig_side);
11595 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11596 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11597 player->index_bit, dig_side);
11598 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11599 player->index_bit, dig_side);
11601 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11602 player->index_bit, dig_side);
11603 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11604 player->index_bit, dig_side);
11608 return MF_NO_ACTION;
11611 player->push_delay = 0;
11613 if (Feld[x][y] != element) /* really digged/collected something */
11614 player->is_collecting = !player->is_digging;
11619 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11621 int jx = player->jx, jy = player->jy;
11622 int x = jx + dx, y = jy + dy;
11623 int snap_direction = (dx == -1 ? MV_LEFT :
11624 dx == +1 ? MV_RIGHT :
11626 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11629 if (player->MovPos != 0)
11632 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11636 if (!player->active || !IN_LEV_FIELD(x, y))
11644 if (player->MovPos == 0)
11645 player->is_pushing = FALSE;
11647 player->is_snapping = FALSE;
11649 if (player->MovPos == 0)
11651 player->is_moving = FALSE;
11652 player->is_digging = FALSE;
11653 player->is_collecting = FALSE;
11659 if (player->is_snapping)
11662 player->MovDir = snap_direction;
11665 if (player->MovPos == 0)
11668 player->is_moving = FALSE;
11669 player->is_digging = FALSE;
11670 player->is_collecting = FALSE;
11673 player->is_dropping = FALSE;
11675 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11678 player->is_snapping = TRUE;
11681 if (player->MovPos == 0)
11684 player->is_moving = FALSE;
11685 player->is_digging = FALSE;
11686 player->is_collecting = FALSE;
11690 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11691 DrawLevelField(player->last_jx, player->last_jy);
11694 DrawLevelField(x, y);
11703 boolean DropElement(struct PlayerInfo *player)
11705 int old_element, new_element;
11706 int dropx = player->jx, dropy = player->jy;
11707 int drop_direction = player->MovDir;
11709 int drop_side = drop_direction;
11711 static int trigger_sides[4] =
11713 CH_SIDE_LEFT, /* dropping left */
11714 CH_SIDE_RIGHT, /* dropping right */
11715 CH_SIDE_TOP, /* dropping up */
11716 CH_SIDE_BOTTOM, /* dropping down */
11718 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11720 int drop_element = (player->inventory_size > 0 ?
11721 player->inventory_element[player->inventory_size - 1] :
11722 player->inventory_infinite_element != EL_UNDEFINED ?
11723 player->inventory_infinite_element :
11724 player->dynabombs_left > 0 ?
11725 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11728 if (IS_THROWABLE(drop_element))
11730 dropx += GET_DX_FROM_DIR(drop_direction);
11731 dropy += GET_DY_FROM_DIR(drop_direction);
11733 if (!IN_LEV_FIELD(dropx, dropy))
11737 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11738 new_element = drop_element; /* default: no change when dropping */
11740 /* check if player is active, not moving and ready to drop */
11741 if (!player->active || player->MovPos || player->drop_delay > 0)
11744 /* check if player has anything that can be dropped */
11746 if (new_element == EL_UNDEFINED)
11749 if (player->inventory_size == 0 &&
11750 player->inventory_infinite_element == EL_UNDEFINED &&
11751 player->dynabombs_left == 0)
11755 /* check if anything can be dropped at the current position */
11756 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11759 /* collected custom elements can only be dropped on empty fields */
11761 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11764 if (player->inventory_size > 0 &&
11765 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11766 && old_element != EL_EMPTY)
11770 if (old_element != EL_EMPTY)
11771 Back[dropx][dropy] = old_element; /* store old element on this field */
11773 ResetGfxAnimation(dropx, dropy);
11774 ResetRandomAnimationValue(dropx, dropy);
11776 if (player->inventory_size > 0 ||
11777 player->inventory_infinite_element != EL_UNDEFINED)
11779 if (player->inventory_size > 0)
11781 player->inventory_size--;
11784 new_element = player->inventory_element[player->inventory_size];
11787 DrawGameValue_Dynamite(local_player->inventory_size);
11789 if (new_element == EL_DYNAMITE)
11790 new_element = EL_DYNAMITE_ACTIVE;
11791 else if (new_element == EL_SP_DISK_RED)
11792 new_element = EL_SP_DISK_RED_ACTIVE;
11795 Feld[dropx][dropy] = new_element;
11797 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11798 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11799 el2img(Feld[dropx][dropy]), 0);
11801 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11804 /* needed if previous element just changed to "empty" in the last frame */
11805 Changed[dropx][dropy] = 0; /* allow another change */
11809 /* !!! TEST ONLY !!! */
11810 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11811 player->index_bit, drop_side);
11812 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11813 CE_OTHER_GETS_DROPPED,
11814 player->index_bit, drop_side);
11816 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11817 CE_OTHER_GETS_DROPPED,
11818 player->index_bit, drop_side);
11819 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11820 player->index_bit, drop_side);
11823 TestIfElementTouchesCustomElement(dropx, dropy);
11825 else /* player is dropping a dyna bomb */
11827 player->dynabombs_left--;
11830 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
11833 Feld[dropx][dropy] = new_element;
11835 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11836 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11837 el2img(Feld[dropx][dropy]), 0);
11839 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11846 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11849 InitField_WithBug1(dropx, dropy, FALSE);
11851 InitField(dropx, dropy, FALSE);
11852 if (CAN_MOVE(Feld[dropx][dropy]))
11853 InitMovDir(dropx, dropy);
11857 new_element = Feld[dropx][dropy]; /* element might have changed */
11859 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11860 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11863 int move_stepsize = element_info[new_element].move_stepsize;
11865 int move_direction, nextx, nexty;
11867 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11868 MovDir[dropx][dropy] = drop_direction;
11870 move_direction = MovDir[dropx][dropy];
11871 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11872 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11875 Changed[dropx][dropy] = 0; /* allow another change */
11876 CheckCollision[dropx][dropy] = 2;
11879 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
11882 WasJustMoving[dropx][dropy] = 3;
11885 InitMovingField(dropx, dropy, move_direction);
11886 ContinueMoving(dropx, dropy);
11891 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
11894 Changed[dropx][dropy] = 0; /* allow another change */
11897 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
11899 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
11900 CE_HITTING_SOMETHING, move_direction);
11908 player->drop_delay = 2 * TILEX / move_stepsize + 1;
11913 player->drop_delay = 8 + 8 + 8;
11917 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11922 player->is_dropping = TRUE;
11928 /* ------------------------------------------------------------------------- */
11929 /* game sound playing functions */
11930 /* ------------------------------------------------------------------------- */
11932 static int *loop_sound_frame = NULL;
11933 static int *loop_sound_volume = NULL;
11935 void InitPlayLevelSound()
11937 int num_sounds = getSoundListSize();
11939 checked_free(loop_sound_frame);
11940 checked_free(loop_sound_volume);
11942 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11943 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11946 static void PlayLevelSound(int x, int y, int nr)
11948 int sx = SCREENX(x), sy = SCREENY(y);
11949 int volume, stereo_position;
11950 int max_distance = 8;
11951 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11953 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11954 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11957 if (!IN_LEV_FIELD(x, y) ||
11958 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11959 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11962 volume = SOUND_MAX_VOLUME;
11964 if (!IN_SCR_FIELD(sx, sy))
11966 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11967 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11969 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11972 stereo_position = (SOUND_MAX_LEFT +
11973 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11974 (SCR_FIELDX + 2 * max_distance));
11976 if (IS_LOOP_SOUND(nr))
11978 /* This assures that quieter loop sounds do not overwrite louder ones,
11979 while restarting sound volume comparison with each new game frame. */
11981 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11984 loop_sound_volume[nr] = volume;
11985 loop_sound_frame[nr] = FrameCounter;
11988 PlaySoundExt(nr, volume, stereo_position, type);
11991 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11993 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11994 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11995 y < LEVELY(BY1) ? LEVELY(BY1) :
11996 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12000 static void PlayLevelSoundAction(int x, int y, int action)
12002 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12005 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12007 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12009 if (sound_effect != SND_UNDEFINED)
12010 PlayLevelSound(x, y, sound_effect);
12013 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12016 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12018 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12019 PlayLevelSound(x, y, sound_effect);
12022 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12024 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12026 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12027 PlayLevelSound(x, y, sound_effect);
12030 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12032 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12034 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12035 StopSound(sound_effect);
12038 static void PlayLevelMusic()
12040 if (levelset.music[level_nr] != MUS_UNDEFINED)
12041 PlayMusic(levelset.music[level_nr]); /* from config file */
12043 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12046 void RaiseScore(int value)
12048 local_player->score += value;
12050 DrawGameValue_Score(local_player->score);
12053 void RaiseScoreElement(int element)
12058 case EL_BD_DIAMOND:
12059 case EL_EMERALD_YELLOW:
12060 case EL_EMERALD_RED:
12061 case EL_EMERALD_PURPLE:
12062 case EL_SP_INFOTRON:
12063 RaiseScore(level.score[SC_EMERALD]);
12066 RaiseScore(level.score[SC_DIAMOND]);
12069 RaiseScore(level.score[SC_CRYSTAL]);
12072 RaiseScore(level.score[SC_PEARL]);
12075 case EL_BD_BUTTERFLY:
12076 case EL_SP_ELECTRON:
12077 RaiseScore(level.score[SC_BUG]);
12080 case EL_BD_FIREFLY:
12081 case EL_SP_SNIKSNAK:
12082 RaiseScore(level.score[SC_SPACESHIP]);
12085 case EL_DARK_YAMYAM:
12086 RaiseScore(level.score[SC_YAMYAM]);
12089 RaiseScore(level.score[SC_ROBOT]);
12092 RaiseScore(level.score[SC_PACMAN]);
12095 RaiseScore(level.score[SC_NUT]);
12098 case EL_SP_DISK_RED:
12099 case EL_DYNABOMB_INCREASE_NUMBER:
12100 case EL_DYNABOMB_INCREASE_SIZE:
12101 case EL_DYNABOMB_INCREASE_POWER:
12102 RaiseScore(level.score[SC_DYNAMITE]);
12104 case EL_SHIELD_NORMAL:
12105 case EL_SHIELD_DEADLY:
12106 RaiseScore(level.score[SC_SHIELD]);
12108 case EL_EXTRA_TIME:
12109 RaiseScore(level.score[SC_TIME_BONUS]);
12115 RaiseScore(level.score[SC_KEY]);
12118 RaiseScore(element_info[element].collect_score);
12123 void RequestQuitGame(boolean ask_if_really_quit)
12125 if (AllPlayersGone ||
12126 !ask_if_really_quit ||
12127 level_editor_test_game ||
12128 Request("Do you really want to quit the game ?",
12129 REQ_ASK | REQ_STAY_CLOSED))
12131 #if defined(NETWORK_AVALIABLE)
12132 if (options.network)
12133 SendToServer_StopPlaying();
12137 game_status = GAME_MODE_MAIN;
12145 if (tape.playing && tape.deactivate_display)
12146 TapeDeactivateDisplayOff(TRUE);
12149 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12152 if (tape.playing && tape.deactivate_display)
12153 TapeDeactivateDisplayOn();
12160 /* ---------- new game button stuff ---------------------------------------- */
12162 /* graphic position values for game buttons */
12163 #define GAME_BUTTON_XSIZE 30
12164 #define GAME_BUTTON_YSIZE 30
12165 #define GAME_BUTTON_XPOS 5
12166 #define GAME_BUTTON_YPOS 215
12167 #define SOUND_BUTTON_XPOS 5
12168 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12170 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12171 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12172 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12173 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12174 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12175 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12182 } gamebutton_info[NUM_GAME_BUTTONS] =
12185 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12190 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12191 GAME_CTRL_ID_PAUSE,
12195 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12200 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12201 SOUND_CTRL_ID_MUSIC,
12202 "background music on/off"
12205 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12206 SOUND_CTRL_ID_LOOPS,
12207 "sound loops on/off"
12210 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12211 SOUND_CTRL_ID_SIMPLE,
12212 "normal sounds on/off"
12216 void CreateGameButtons()
12220 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12222 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12223 struct GadgetInfo *gi;
12226 unsigned long event_mask;
12227 int gd_xoffset, gd_yoffset;
12228 int gd_x1, gd_x2, gd_y1, gd_y2;
12231 gd_xoffset = gamebutton_info[i].x;
12232 gd_yoffset = gamebutton_info[i].y;
12233 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12234 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12236 if (id == GAME_CTRL_ID_STOP ||
12237 id == GAME_CTRL_ID_PAUSE ||
12238 id == GAME_CTRL_ID_PLAY)
12240 button_type = GD_TYPE_NORMAL_BUTTON;
12242 event_mask = GD_EVENT_RELEASED;
12243 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12244 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12248 button_type = GD_TYPE_CHECK_BUTTON;
12250 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12251 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12252 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12253 event_mask = GD_EVENT_PRESSED;
12254 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12255 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12258 gi = CreateGadget(GDI_CUSTOM_ID, id,
12259 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12260 GDI_X, DX + gd_xoffset,
12261 GDI_Y, DY + gd_yoffset,
12262 GDI_WIDTH, GAME_BUTTON_XSIZE,
12263 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12264 GDI_TYPE, button_type,
12265 GDI_STATE, GD_BUTTON_UNPRESSED,
12266 GDI_CHECKED, checked,
12267 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12268 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12269 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12270 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12271 GDI_EVENT_MASK, event_mask,
12272 GDI_CALLBACK_ACTION, HandleGameButtons,
12276 Error(ERR_EXIT, "cannot create gadget");
12278 game_gadget[id] = gi;
12282 void FreeGameButtons()
12286 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12287 FreeGadget(game_gadget[i]);
12290 static void MapGameButtons()
12294 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12295 MapGadget(game_gadget[i]);
12298 void UnmapGameButtons()
12302 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12303 UnmapGadget(game_gadget[i]);
12306 static void HandleGameButtons(struct GadgetInfo *gi)
12308 int id = gi->custom_id;
12310 if (game_status != GAME_MODE_PLAYING)
12315 case GAME_CTRL_ID_STOP:
12316 RequestQuitGame(TRUE);
12319 case GAME_CTRL_ID_PAUSE:
12320 if (options.network)
12322 #if defined(NETWORK_AVALIABLE)
12324 SendToServer_ContinuePlaying();
12326 SendToServer_PausePlaying();
12330 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12333 case GAME_CTRL_ID_PLAY:
12336 #if defined(NETWORK_AVALIABLE)
12337 if (options.network)
12338 SendToServer_ContinuePlaying();
12342 tape.pausing = FALSE;
12343 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12348 case SOUND_CTRL_ID_MUSIC:
12349 if (setup.sound_music)
12351 setup.sound_music = FALSE;
12354 else if (audio.music_available)
12356 setup.sound = setup.sound_music = TRUE;
12358 SetAudioMode(setup.sound);
12364 case SOUND_CTRL_ID_LOOPS:
12365 if (setup.sound_loops)
12366 setup.sound_loops = FALSE;
12367 else if (audio.loops_available)
12369 setup.sound = setup.sound_loops = TRUE;
12370 SetAudioMode(setup.sound);
12374 case SOUND_CTRL_ID_SIMPLE:
12375 if (setup.sound_simple)
12376 setup.sound_simple = FALSE;
12377 else if (audio.sound_available)
12379 setup.sound = setup.sound_simple = TRUE;
12380 SetAudioMode(setup.sound);