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
32 #define USE_NEW_MOVE_DELAY TRUE *1
33 #define USE_NEW_PUSH_DELAY TRUE *1
40 /* for MovePlayer() */
41 #define MF_NO_ACTION 0
45 /* for ScrollPlayer() */
47 #define SCROLL_GO_ON 1
50 #define EX_PHASE_START 0
51 #define EX_TYPE_NONE 0
52 #define EX_TYPE_NORMAL (1 << 0)
53 #define EX_TYPE_CENTER (1 << 1)
54 #define EX_TYPE_BORDER (1 << 2)
55 #define EX_TYPE_CROSS (1 << 3)
56 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
58 /* special positions in the game control window (relative to control window) */
61 #define XX_EMERALDS 29
62 #define YY_EMERALDS 54
63 #define XX_DYNAMITE 29
64 #define YY_DYNAMITE 89
73 /* special positions in the game control window (relative to main window) */
74 #define DX_LEVEL (DX + XX_LEVEL)
75 #define DY_LEVEL (DY + YY_LEVEL)
76 #define DX_EMERALDS (DX + XX_EMERALDS)
77 #define DY_EMERALDS (DY + YY_EMERALDS)
78 #define DX_DYNAMITE (DX + XX_DYNAMITE)
79 #define DY_DYNAMITE (DY + YY_DYNAMITE)
80 #define DX_KEYS (DX + XX_KEYS)
81 #define DY_KEYS (DY + YY_KEYS)
82 #define DX_SCORE (DX + XX_SCORE)
83 #define DY_SCORE (DY + YY_SCORE)
84 #define DX_TIME1 (DX + XX_TIME1)
85 #define DX_TIME2 (DX + XX_TIME2)
86 #define DY_TIME (DY + YY_TIME)
88 /* values for initial player move delay (initial delay counter value) */
89 #define INITIAL_MOVE_DELAY_OFF -1
90 #define INITIAL_MOVE_DELAY_ON 0
92 /* values for player movement speed (which is in fact a delay value) */
93 #define MOVE_DELAY_NORMAL_SPEED 8
94 #define MOVE_DELAY_HIGH_SPEED 4
96 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
97 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
98 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
99 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
101 /* values for other actions */
102 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
104 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
105 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
107 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
109 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
110 RND(element_info[e].push_delay_random))
111 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
112 RND(element_info[e].drop_delay_random))
113 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
114 RND(element_info[e].move_delay_random))
115 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
116 (element_info[e].move_delay_random))
118 #define GET_TARGET_ELEMENT(e, ch) \
119 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
120 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
122 #define GET_VALID_PLAYER_ELEMENT(e) \
123 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
125 #define CAN_GROW_INTO(e) \
126 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
128 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
129 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
132 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
133 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
134 (CAN_MOVE_INTO_ACID(e) && \
135 Feld[x][y] == EL_ACID) || \
138 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
139 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
140 (CAN_MOVE_INTO_ACID(e) && \
141 Feld[x][y] == EL_ACID) || \
144 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
145 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
147 (CAN_MOVE_INTO_ACID(e) && \
148 Feld[x][y] == EL_ACID) || \
149 (DONT_COLLIDE_WITH(e) && \
151 !PLAYER_ENEMY_PROTECTED(x, y))))
154 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
155 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
157 (DONT_COLLIDE_WITH(e) && \
159 !PLAYER_ENEMY_PROTECTED(x, y))))
162 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
163 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
166 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
167 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
169 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
170 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
174 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
177 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
178 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
182 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
183 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
185 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
186 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
188 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
189 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
191 #define PIG_CAN_ENTER_FIELD(e, x, y) \
192 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
194 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
195 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
196 IS_FOOD_PENGUIN(Feld[x][y])))
197 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
198 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
200 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
201 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
203 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
204 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
208 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
209 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
210 (CAN_MOVE_INTO_ACID(e) && \
211 Feld[x][y] == EL_ACID) || \
212 Feld[x][y] == EL_DIAMOND))
214 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
215 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
216 (CAN_MOVE_INTO_ACID(e) && \
217 Feld[x][y] == EL_ACID) || \
218 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
220 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
221 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
222 (CAN_MOVE_INTO_ACID(e) && \
223 Feld[x][y] == EL_ACID) || \
224 IS_AMOEBOID(Feld[x][y])))
226 #define PIG_CAN_ENTER_FIELD(e, x, y) \
227 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
228 (CAN_MOVE_INTO_ACID(e) && \
229 Feld[x][y] == EL_ACID) || \
230 IS_FOOD_PIG(Feld[x][y])))
232 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
233 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
234 (CAN_MOVE_INTO_ACID(e) && \
235 Feld[x][y] == EL_ACID) || \
236 IS_FOOD_PENGUIN(Feld[x][y]) || \
237 Feld[x][y] == EL_EXIT_OPEN))
239 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
240 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
241 (CAN_MOVE_INTO_ACID(e) && \
242 Feld[x][y] == EL_ACID)))
244 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
245 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
246 (CAN_MOVE_INTO_ACID(e) && \
247 Feld[x][y] == EL_ACID) || \
250 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
251 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
252 (CAN_MOVE_INTO_ACID(e) && \
253 Feld[x][y] == EL_ACID)))
257 #define GROUP_NR(e) ((e) - EL_GROUP_START)
258 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
259 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
260 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
262 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
263 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
266 #define CE_ENTER_FIELD_COND(e, x, y) \
267 (!IS_PLAYER(x, y) && \
268 (Feld[x][y] == EL_ACID || \
269 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
271 #define CE_ENTER_FIELD_COND(e, x, y) \
272 (!IS_PLAYER(x, y) && \
273 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
276 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
277 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
279 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
280 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
282 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
283 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
284 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
285 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
287 /* game button identifiers */
288 #define GAME_CTRL_ID_STOP 0
289 #define GAME_CTRL_ID_PAUSE 1
290 #define GAME_CTRL_ID_PLAY 2
291 #define SOUND_CTRL_ID_MUSIC 3
292 #define SOUND_CTRL_ID_LOOPS 4
293 #define SOUND_CTRL_ID_SIMPLE 5
295 #define NUM_GAME_BUTTONS 6
298 /* forward declaration for internal use */
300 static void AdvanceFrameAndPlayerCounters(int);
302 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
303 static boolean MovePlayer(struct PlayerInfo *, int, int);
304 static void ScrollPlayer(struct PlayerInfo *, int);
305 static void ScrollScreen(struct PlayerInfo *, int);
307 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
309 static void InitBeltMovement(void);
310 static void CloseAllOpenTimegates(void);
311 static void CheckGravityMovement(struct PlayerInfo *);
312 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
313 static void KillHeroUnlessEnemyProtected(int, int);
314 static void KillHeroUnlessExplosionProtected(int, int);
316 static void TestIfPlayerTouchesCustomElement(int, int);
317 static void TestIfElementTouchesCustomElement(int, int);
318 static void TestIfElementHitsCustomElement(int, int, int);
320 static void TestIfElementSmashesCustomElement(int, int, int);
323 static void ChangeElement(int, int, int);
325 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
326 #define CheckTriggeredElementChange(x, y, e, ev) \
327 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
329 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
330 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
331 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
332 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
333 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
334 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
337 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
338 #define CheckElementChange(x, y, e, te, ev) \
339 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
340 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
341 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
342 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
343 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
344 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
345 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
347 static void PlayLevelSound(int, int, int);
348 static void PlayLevelSoundNearest(int, int, int);
349 static void PlayLevelSoundAction(int, int, int);
350 static void PlayLevelSoundElementAction(int, int, int, int);
351 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
352 static void PlayLevelSoundActionIfLoop(int, int, int);
353 static void StopLevelSoundActionIfLoop(int, int, int);
354 static void PlayLevelMusic();
356 static void MapGameButtons();
357 static void HandleGameButtons(struct GadgetInfo *);
359 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
362 /* ------------------------------------------------------------------------- */
363 /* definition of elements that automatically change to other elements after */
364 /* a specified time, eventually calling a function when changing */
365 /* ------------------------------------------------------------------------- */
367 /* forward declaration for changer functions */
368 static void InitBuggyBase(int x, int y);
369 static void WarnBuggyBase(int x, int y);
371 static void InitTrap(int x, int y);
372 static void ActivateTrap(int x, int y);
373 static void ChangeActiveTrap(int x, int y);
375 static void InitRobotWheel(int x, int y);
376 static void RunRobotWheel(int x, int y);
377 static void StopRobotWheel(int x, int y);
379 static void InitTimegateWheel(int x, int y);
380 static void RunTimegateWheel(int x, int y);
382 struct ChangingElementInfo
387 void (*pre_change_function)(int x, int y);
388 void (*change_function)(int x, int y);
389 void (*post_change_function)(int x, int y);
392 static struct ChangingElementInfo change_delay_list[] =
443 EL_SWITCHGATE_OPENING,
451 EL_SWITCHGATE_CLOSING,
452 EL_SWITCHGATE_CLOSED,
484 EL_ACID_SPLASH_RIGHT,
493 EL_SP_BUGGY_BASE_ACTIVATING,
500 EL_SP_BUGGY_BASE_ACTIVATING,
501 EL_SP_BUGGY_BASE_ACTIVE,
508 EL_SP_BUGGY_BASE_ACTIVE,
532 EL_ROBOT_WHEEL_ACTIVE,
540 EL_TIMEGATE_SWITCH_ACTIVE,
561 int push_delay_fixed, push_delay_random;
566 { EL_BALLOON, 0, 0 },
568 { EL_SOKOBAN_OBJECT, 2, 0 },
569 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
570 { EL_SATELLITE, 2, 0 },
571 { EL_SP_DISK_YELLOW, 2, 0 },
573 { EL_UNDEFINED, 0, 0 },
581 move_stepsize_list[] =
583 { EL_AMOEBA_DROP, 2 },
584 { EL_AMOEBA_DROPPING, 2 },
585 { EL_QUICKSAND_FILLING, 1 },
586 { EL_QUICKSAND_EMPTYING, 1 },
587 { EL_MAGIC_WALL_FILLING, 2 },
588 { EL_BD_MAGIC_WALL_FILLING, 2 },
589 { EL_MAGIC_WALL_EMPTYING, 2 },
590 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
600 collect_count_list[] =
603 { EL_BD_DIAMOND, 1 },
604 { EL_EMERALD_YELLOW, 1 },
605 { EL_EMERALD_RED, 1 },
606 { EL_EMERALD_PURPLE, 1 },
608 { EL_SP_INFOTRON, 1 },
620 access_direction_list[] =
622 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
623 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
624 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
625 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
626 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
627 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
628 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
629 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
630 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
631 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
632 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
634 { EL_SP_PORT_LEFT, MV_RIGHT },
635 { EL_SP_PORT_RIGHT, MV_LEFT },
636 { EL_SP_PORT_UP, MV_DOWN },
637 { EL_SP_PORT_DOWN, MV_UP },
638 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
639 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
640 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
641 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
642 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
643 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
644 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
645 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
646 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
647 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
648 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
649 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
650 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
651 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
652 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
654 { EL_UNDEFINED, MV_NO_MOVING }
657 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
659 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
660 CH_EVENT_BIT(CE_DELAY))
661 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
662 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
663 IS_JUST_CHANGING(x, y))
665 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
668 void GetPlayerConfig()
670 if (!audio.sound_available)
671 setup.sound_simple = FALSE;
673 if (!audio.loops_available)
674 setup.sound_loops = FALSE;
676 if (!audio.music_available)
677 setup.sound_music = FALSE;
679 if (!video.fullscreen_available)
680 setup.fullscreen = FALSE;
682 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
684 SetAudioMode(setup.sound);
688 static int getBeltNrFromBeltElement(int element)
690 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
691 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
692 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
695 static int getBeltNrFromBeltActiveElement(int element)
697 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
698 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
699 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
702 static int getBeltNrFromBeltSwitchElement(int element)
704 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
705 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
706 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
709 static int getBeltDirNrFromBeltSwitchElement(int element)
711 static int belt_base_element[4] =
713 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
714 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
715 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
716 EL_CONVEYOR_BELT_4_SWITCH_LEFT
719 int belt_nr = getBeltNrFromBeltSwitchElement(element);
720 int belt_dir_nr = element - belt_base_element[belt_nr];
722 return (belt_dir_nr % 3);
725 static int getBeltDirFromBeltSwitchElement(int element)
727 static int belt_move_dir[3] =
734 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
736 return belt_move_dir[belt_dir_nr];
739 static void InitPlayerField(int x, int y, int element, boolean init_game)
741 if (element == EL_SP_MURPHY)
745 if (stored_player[0].present)
747 Feld[x][y] = EL_SP_MURPHY_CLONE;
753 stored_player[0].use_murphy_graphic = TRUE;
756 Feld[x][y] = EL_PLAYER_1;
762 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
763 int jx = player->jx, jy = player->jy;
765 player->present = TRUE;
767 player->block_last_field = (element == EL_SP_MURPHY ?
768 level.sp_block_last_field :
769 level.block_last_field);
771 if (!options.network || player->connected)
773 player->active = TRUE;
775 /* remove potentially duplicate players */
776 if (StorePlayer[jx][jy] == Feld[x][y])
777 StorePlayer[jx][jy] = 0;
779 StorePlayer[x][y] = Feld[x][y];
783 printf("Player %d activated.\n", player->element_nr);
784 printf("[Local player is %d and currently %s.]\n",
785 local_player->element_nr,
786 local_player->active ? "active" : "not active");
790 Feld[x][y] = EL_EMPTY;
792 player->jx = player->last_jx = x;
793 player->jy = player->last_jy = y;
797 static void InitField(int x, int y, boolean init_game)
799 int element = Feld[x][y];
808 InitPlayerField(x, y, element, init_game);
811 case EL_SOKOBAN_FIELD_PLAYER:
812 element = Feld[x][y] = EL_PLAYER_1;
813 InitField(x, y, init_game);
815 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
816 InitField(x, y, init_game);
819 case EL_SOKOBAN_FIELD_EMPTY:
820 local_player->sokobanfields_still_needed++;
824 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
825 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
826 else if (x > 0 && Feld[x-1][y] == EL_ACID)
827 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
828 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
829 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
830 else if (y > 0 && Feld[x][y-1] == EL_ACID)
831 Feld[x][y] = EL_ACID_POOL_BOTTOM;
832 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
833 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
841 case EL_SPACESHIP_RIGHT:
842 case EL_SPACESHIP_UP:
843 case EL_SPACESHIP_LEFT:
844 case EL_SPACESHIP_DOWN:
846 case EL_BD_BUTTERFLY_RIGHT:
847 case EL_BD_BUTTERFLY_UP:
848 case EL_BD_BUTTERFLY_LEFT:
849 case EL_BD_BUTTERFLY_DOWN:
850 case EL_BD_BUTTERFLY:
851 case EL_BD_FIREFLY_RIGHT:
852 case EL_BD_FIREFLY_UP:
853 case EL_BD_FIREFLY_LEFT:
854 case EL_BD_FIREFLY_DOWN:
856 case EL_PACMAN_RIGHT:
880 if (y == lev_fieldy - 1)
882 Feld[x][y] = EL_AMOEBA_GROWING;
883 Store[x][y] = EL_AMOEBA_WET;
887 case EL_DYNAMITE_ACTIVE:
888 case EL_SP_DISK_RED_ACTIVE:
889 case EL_DYNABOMB_PLAYER_1_ACTIVE:
890 case EL_DYNABOMB_PLAYER_2_ACTIVE:
891 case EL_DYNABOMB_PLAYER_3_ACTIVE:
892 case EL_DYNABOMB_PLAYER_4_ACTIVE:
897 local_player->lights_still_needed++;
901 local_player->friends_still_needed++;
906 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
911 Feld[x][y] = EL_EMPTY;
916 case EL_EM_KEY_1_FILE:
917 Feld[x][y] = EL_EM_KEY_1;
919 case EL_EM_KEY_2_FILE:
920 Feld[x][y] = EL_EM_KEY_2;
922 case EL_EM_KEY_3_FILE:
923 Feld[x][y] = EL_EM_KEY_3;
925 case EL_EM_KEY_4_FILE:
926 Feld[x][y] = EL_EM_KEY_4;
930 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
931 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
932 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
933 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
934 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
935 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
936 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
937 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
938 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
939 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
940 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
941 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
944 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
945 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
946 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
948 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
950 game.belt_dir[belt_nr] = belt_dir;
951 game.belt_dir_nr[belt_nr] = belt_dir_nr;
953 else /* more than one switch -- set it like the first switch */
955 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
960 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
962 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
965 case EL_LIGHT_SWITCH_ACTIVE:
967 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
971 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
973 else if (IS_GROUP_ELEMENT(element))
975 struct ElementGroupInfo *group = element_info[element].group;
976 int last_anim_random_frame = gfx.anim_random_frame;
979 if (group->choice_mode == ANIM_RANDOM)
980 gfx.anim_random_frame = RND(group->num_elements_resolved);
982 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
983 group->choice_mode, 0,
986 if (group->choice_mode == ANIM_RANDOM)
987 gfx.anim_random_frame = last_anim_random_frame;
991 Feld[x][y] = group->element_resolved[element_pos];
993 InitField(x, y, init_game);
999 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1001 InitField(x, y, init_game);
1003 /* not needed to call InitMovDir() -- already done by InitField()! */
1004 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1005 CAN_MOVE(Feld[x][y]))
1009 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1011 int old_element = Feld[x][y];
1013 InitField(x, y, init_game);
1015 /* not needed to call InitMovDir() -- already done by InitField()! */
1016 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1017 CAN_MOVE(old_element) &&
1018 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1021 /* this case is in fact a combination of not less than three bugs:
1022 first, it calls InitMovDir() for elements that can move, although this is
1023 already done by InitField(); then, it checks the element that was at this
1024 field _before_ the call to InitField() (which can change it); lastly, it
1025 was not called for "mole with direction" elements, which were treated as
1026 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1030 inline void DrawGameValue_Emeralds(int value)
1032 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1035 inline void DrawGameValue_Dynamite(int value)
1037 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1040 inline void DrawGameValue_Keys(struct PlayerInfo *player)
1044 for (i = 0; i < MAX_KEYS; i++)
1046 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1047 el2edimg(EL_KEY_1 + i));
1050 inline void DrawGameValue_Score(int value)
1052 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1055 inline void DrawGameValue_Time(int value)
1058 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1060 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1063 inline void DrawGameValue_Level(int value)
1066 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1069 /* misuse area for displaying emeralds to draw bigger level number */
1070 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1071 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1073 /* now copy it to the area for displaying level number */
1074 BlitBitmap(drawto, drawto,
1075 DX_EMERALDS, DY_EMERALDS + 1,
1076 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1077 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1078 DX_LEVEL - 1, DY_LEVEL + 1);
1080 /* restore the area for displaying emeralds */
1081 DrawGameValue_Emeralds(local_player->gems_still_needed);
1083 /* yes, this is all really ugly :-) */
1087 void DrawGameDoorValues()
1091 DrawGameValue_Level(level_nr);
1093 for (i = 0; i < MAX_PLAYERS; i++)
1094 DrawGameValue_Keys(&stored_player[i]);
1096 DrawGameValue_Emeralds(local_player->gems_still_needed);
1097 DrawGameValue_Dynamite(local_player->inventory_size);
1098 DrawGameValue_Score(local_player->score);
1099 DrawGameValue_Time(TimeLeft);
1102 static void resolve_group_element(int group_element, int recursion_depth)
1104 static int group_nr;
1105 static struct ElementGroupInfo *group;
1106 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1109 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1111 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1112 group_element - EL_GROUP_START + 1);
1114 /* replace element which caused too deep recursion by question mark */
1115 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1120 if (recursion_depth == 0) /* initialization */
1122 group = element_info[group_element].group;
1123 group_nr = group_element - EL_GROUP_START;
1125 group->num_elements_resolved = 0;
1126 group->choice_pos = 0;
1129 for (i = 0; i < actual_group->num_elements; i++)
1131 int element = actual_group->element[i];
1133 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1136 if (IS_GROUP_ELEMENT(element))
1137 resolve_group_element(element, recursion_depth + 1);
1140 group->element_resolved[group->num_elements_resolved++] = element;
1141 element_info[element].in_group[group_nr] = TRUE;
1146 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1148 printf("::: group %d: %d resolved elements\n",
1149 group_element - EL_GROUP_START, group->num_elements_resolved);
1150 for (i = 0; i < group->num_elements_resolved; i++)
1151 printf("::: - %d ['%s']\n", group->element_resolved[i],
1152 element_info[group->element_resolved[i]].token_name);
1159 =============================================================================
1161 -----------------------------------------------------------------------------
1162 initialize game engine due to level / tape version number
1163 =============================================================================
1166 static void InitGameEngine()
1170 /* set game engine from tape file when re-playing, else from level file */
1171 game.engine_version = (tape.playing ? tape.engine_version :
1172 level.game_version);
1174 /* dynamically adjust element properties according to game engine version */
1175 InitElementPropertiesEngine(game.engine_version);
1178 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1179 printf(" tape version == %06d [%s] [file: %06d]\n",
1180 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1182 printf(" => game.engine_version == %06d\n", game.engine_version);
1185 /* ---------- recursively resolve group elements ------------------------- */
1187 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1188 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1189 element_info[i].in_group[j] = FALSE;
1191 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1192 resolve_group_element(EL_GROUP_START + i, 0);
1194 /* ---------- initialize player's initial move delay --------------------- */
1196 #if USE_NEW_MOVE_DELAY
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 /* dynamically adjust player properties according to game engine version */
1202 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1203 game.initial_move_delay_value : 0);
1205 /* dynamically adjust player properties according to game engine version */
1206 game.initial_move_delay =
1207 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1208 INITIAL_MOVE_DELAY_OFF);
1210 /* dynamically adjust player properties according to level information */
1211 game.initial_move_delay_value =
1212 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1215 /* ---------- initialize player's initial push delay --------------------- */
1217 /* dynamically adjust player properties according to game engine version */
1218 game.initial_push_delay_value =
1219 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1221 /* ---------- initialize changing elements ------------------------------- */
1223 /* initialize changing elements information */
1224 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1226 struct ElementInfo *ei = &element_info[i];
1228 /* this pointer might have been changed in the level editor */
1229 ei->change = &ei->change_page[0];
1231 if (!IS_CUSTOM_ELEMENT(i))
1233 ei->change->target_element = EL_EMPTY_SPACE;
1234 ei->change->delay_fixed = 0;
1235 ei->change->delay_random = 0;
1236 ei->change->delay_frames = 1;
1239 ei->change_events = CE_BITMASK_DEFAULT;
1240 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1242 ei->event_page_nr[j] = 0;
1243 ei->event_page[j] = &ei->change_page[0];
1247 /* add changing elements from pre-defined list */
1248 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1250 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1251 struct ElementInfo *ei = &element_info[ch_delay->element];
1253 ei->change->target_element = ch_delay->target_element;
1254 ei->change->delay_fixed = ch_delay->change_delay;
1256 ei->change->pre_change_function = ch_delay->pre_change_function;
1257 ei->change->change_function = ch_delay->change_function;
1258 ei->change->post_change_function = ch_delay->post_change_function;
1260 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1263 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1268 /* add change events from custom element configuration */
1269 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1271 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1273 for (j = 0; j < ei->num_change_pages; j++)
1275 if (!ei->change_page[j].can_change)
1278 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1280 /* only add event page for the first page found with this event */
1281 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1282 !(ei->change_events & CH_EVENT_BIT(k)))
1284 ei->change_events |= CH_EVENT_BIT(k);
1285 ei->event_page_nr[k] = j;
1286 ei->event_page[k] = &ei->change_page[j];
1294 /* add change events from custom element configuration */
1295 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1297 int element = EL_CUSTOM_START + i;
1299 /* only add custom elements that change after fixed/random frame delay */
1300 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1301 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1305 /* ---------- initialize run-time trigger player and element ------------- */
1307 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1309 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1311 for (j = 0; j < ei->num_change_pages; j++)
1313 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1314 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1318 /* ---------- initialize trigger events ---------------------------------- */
1320 /* initialize trigger events information */
1321 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1322 trigger_events[i] = EP_BITMASK_DEFAULT;
1325 /* add trigger events from element change event properties */
1326 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1328 struct ElementInfo *ei = &element_info[i];
1330 for (j = 0; j < ei->num_change_pages; j++)
1332 if (!ei->change_page[j].can_change)
1335 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1337 int trigger_element = ei->change_page[j].trigger_element;
1339 if (IS_GROUP_ELEMENT(trigger_element))
1341 struct ElementGroupInfo *group = element_info[trigger_element].group;
1343 for (k = 0; k < group->num_elements_resolved; k++)
1344 trigger_events[group->element_resolved[k]]
1345 |= ei->change_page[j].events;
1348 trigger_events[trigger_element] |= ei->change_page[j].events;
1353 /* add trigger events from element change event properties */
1354 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1355 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1356 trigger_events[element_info[i].change->trigger_element] |=
1357 element_info[i].change->events;
1360 /* ---------- initialize push delay -------------------------------------- */
1362 /* initialize push delay values to default */
1363 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1365 if (!IS_CUSTOM_ELEMENT(i))
1367 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1368 element_info[i].push_delay_random = game.default_push_delay_random;
1372 /* set push delay value for certain elements from pre-defined list */
1373 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1375 int e = push_delay_list[i].element;
1377 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1378 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1381 /* set push delay value for Supaplex elements for newer engine versions */
1382 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1384 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1386 if (IS_SP_ELEMENT(i))
1388 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1389 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1394 /* ---------- initialize move stepsize ----------------------------------- */
1396 /* initialize move stepsize values to default */
1397 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1398 if (!IS_CUSTOM_ELEMENT(i))
1399 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1401 /* set move stepsize value for certain elements from pre-defined list */
1402 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1404 int e = move_stepsize_list[i].element;
1406 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1410 /* ---------- initialize move dig/leave ---------------------------------- */
1412 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1414 element_info[i].can_leave_element = FALSE;
1415 element_info[i].can_leave_element_last = FALSE;
1419 /* ---------- initialize gem count --------------------------------------- */
1421 /* initialize gem count values for each element */
1422 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1423 if (!IS_CUSTOM_ELEMENT(i))
1424 element_info[i].collect_count = 0;
1426 /* add gem count values for all elements from pre-defined list */
1427 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1428 element_info[collect_count_list[i].element].collect_count =
1429 collect_count_list[i].count;
1431 /* ---------- initialize access direction -------------------------------- */
1433 /* initialize access direction values to default (access from every side) */
1434 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1435 if (!IS_CUSTOM_ELEMENT(i))
1436 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1438 /* set access direction value for certain elements from pre-defined list */
1439 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1440 element_info[access_direction_list[i].element].access_direction =
1441 access_direction_list[i].direction;
1446 =============================================================================
1448 -----------------------------------------------------------------------------
1449 initialize and start new game
1450 =============================================================================
1455 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1456 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1457 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1464 #if USE_NEW_AMOEBA_CODE
1465 printf("Using new amoeba code.\n");
1467 printf("Using old amoeba code.\n");
1472 /* don't play tapes over network */
1473 network_playing = (options.network && !tape.playing);
1475 for (i = 0; i < MAX_PLAYERS; i++)
1477 struct PlayerInfo *player = &stored_player[i];
1479 player->index_nr = i;
1480 player->index_bit = (1 << i);
1481 player->element_nr = EL_PLAYER_1 + i;
1483 player->present = FALSE;
1484 player->active = FALSE;
1487 player->effective_action = 0;
1488 player->programmed_action = 0;
1491 player->gems_still_needed = level.gems_needed;
1492 player->sokobanfields_still_needed = 0;
1493 player->lights_still_needed = 0;
1494 player->friends_still_needed = 0;
1496 for (j = 0; j < MAX_KEYS; j++)
1497 player->key[j] = FALSE;
1499 player->dynabomb_count = 0;
1500 player->dynabomb_size = 1;
1501 player->dynabombs_left = 0;
1502 player->dynabomb_xl = FALSE;
1504 player->MovDir = MV_NO_MOVING;
1507 player->GfxDir = MV_NO_MOVING;
1508 player->GfxAction = ACTION_DEFAULT;
1510 player->StepFrame = 0;
1512 player->use_murphy_graphic = FALSE;
1514 player->block_last_field = FALSE;
1515 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1517 player->actual_frame_counter = 0;
1519 player->step_counter = 0;
1521 player->last_move_dir = MV_NO_MOVING;
1523 player->is_waiting = FALSE;
1524 player->is_moving = FALSE;
1525 player->is_auto_moving = FALSE;
1526 player->is_digging = FALSE;
1527 player->is_snapping = FALSE;
1528 player->is_collecting = FALSE;
1529 player->is_pushing = FALSE;
1530 player->is_switching = FALSE;
1531 player->is_dropping = FALSE;
1533 player->is_bored = FALSE;
1534 player->is_sleeping = FALSE;
1536 player->frame_counter_bored = -1;
1537 player->frame_counter_sleeping = -1;
1539 player->anim_delay_counter = 0;
1540 player->post_delay_counter = 0;
1542 player->action_waiting = ACTION_DEFAULT;
1543 player->last_action_waiting = ACTION_DEFAULT;
1544 player->special_action_bored = ACTION_DEFAULT;
1545 player->special_action_sleeping = ACTION_DEFAULT;
1547 player->num_special_action_bored = 0;
1548 player->num_special_action_sleeping = 0;
1550 /* determine number of special actions for bored and sleeping animation */
1551 for (j = ACTION_BORING_1; j <= ACTION_BORING_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_bored++;
1565 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1567 boolean found = FALSE;
1569 for (k = 0; k < NUM_DIRECTIONS; k++)
1570 if (el_act_dir2img(player->element_nr, j, k) !=
1571 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1575 player->num_special_action_sleeping++;
1580 player->switch_x = -1;
1581 player->switch_y = -1;
1583 player->show_envelope = 0;
1585 player->move_delay = game.initial_move_delay;
1586 player->move_delay_value = game.initial_move_delay_value;
1588 player->move_delay_reset_counter = 0;
1590 #if USE_NEW_PUSH_DELAY
1591 player->push_delay = -1; /* initialized when pushing starts */
1592 player->push_delay_value = game.initial_push_delay_value;
1594 player->push_delay = 0;
1595 player->push_delay_value = game.initial_push_delay_value;
1598 player->drop_delay = 0;
1600 player->last_jx = player->last_jy = 0;
1601 player->jx = player->jy = 0;
1603 player->shield_normal_time_left = 0;
1604 player->shield_deadly_time_left = 0;
1606 player->inventory_infinite_element = EL_UNDEFINED;
1607 player->inventory_size = 0;
1609 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1610 SnapField(player, 0, 0);
1612 player->LevelSolved = FALSE;
1613 player->GameOver = FALSE;
1616 network_player_action_received = FALSE;
1618 #if defined(NETWORK_AVALIABLE)
1619 /* initial null action */
1620 if (network_playing)
1621 SendToServer_MovePlayer(MV_NO_MOVING);
1629 TimeLeft = level.time;
1632 ScreenMovDir = MV_NO_MOVING;
1636 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1638 AllPlayersGone = FALSE;
1640 game.yamyam_content_nr = 0;
1641 game.magic_wall_active = FALSE;
1642 game.magic_wall_time_left = 0;
1643 game.light_time_left = 0;
1644 game.timegate_time_left = 0;
1645 game.switchgate_pos = 0;
1646 game.balloon_dir = MV_NO_MOVING;
1647 game.gravity = level.initial_gravity;
1648 game.explosions_delayed = TRUE;
1650 game.envelope_active = FALSE;
1652 for (i = 0; i < NUM_BELTS; i++)
1654 game.belt_dir[i] = MV_NO_MOVING;
1655 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1658 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1659 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1661 for (x = 0; x < lev_fieldx; x++)
1663 for (y = 0; y < lev_fieldy; y++)
1665 Feld[x][y] = level.field[x][y];
1666 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1667 ChangeDelay[x][y] = 0;
1668 ChangePage[x][y] = -1;
1669 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1671 WasJustMoving[x][y] = 0;
1672 WasJustFalling[x][y] = 0;
1673 CheckCollision[x][y] = 0;
1675 Pushed[x][y] = FALSE;
1677 Changed[x][y] = CE_BITMASK_DEFAULT;
1678 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1680 ExplodePhase[x][y] = 0;
1681 ExplodeDelay[x][y] = 0;
1682 ExplodeField[x][y] = EX_TYPE_NONE;
1684 RunnerVisit[x][y] = 0;
1685 PlayerVisit[x][y] = 0;
1688 GfxRandom[x][y] = INIT_GFX_RANDOM();
1689 GfxElement[x][y] = EL_UNDEFINED;
1690 GfxAction[x][y] = ACTION_DEFAULT;
1691 GfxDir[x][y] = MV_NO_MOVING;
1695 for (y = 0; y < lev_fieldy; y++)
1697 for (x = 0; x < lev_fieldx; x++)
1699 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1701 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1703 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1706 InitField(x, y, TRUE);
1712 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1713 emulate_sb ? EMU_SOKOBAN :
1714 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1716 /* initialize explosion and ignition delay */
1717 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1719 if (!IS_CUSTOM_ELEMENT(i))
1722 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1723 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1724 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1725 int last_phase = (num_phase + 1) * delay;
1726 int half_phase = (num_phase / 2) * delay;
1728 element_info[i].explosion_delay = last_phase - 1;
1729 element_info[i].ignition_delay = half_phase;
1732 if (i == EL_BLACK_ORB)
1733 element_info[i].ignition_delay = 0;
1735 if (i == EL_BLACK_ORB)
1736 element_info[i].ignition_delay = 1;
1741 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1742 element_info[i].explosion_delay = 1;
1744 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1745 element_info[i].ignition_delay = 1;
1749 /* correct non-moving belts to start moving left */
1750 for (i = 0; i < NUM_BELTS; i++)
1751 if (game.belt_dir[i] == MV_NO_MOVING)
1752 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1754 /* check if any connected player was not found in playfield */
1755 for (i = 0; i < MAX_PLAYERS; i++)
1757 struct PlayerInfo *player = &stored_player[i];
1759 if (player->connected && !player->present)
1761 for (j = 0; j < MAX_PLAYERS; j++)
1763 struct PlayerInfo *some_player = &stored_player[j];
1764 int jx = some_player->jx, jy = some_player->jy;
1766 /* assign first free player found that is present in the playfield */
1767 if (some_player->present && !some_player->connected)
1769 player->present = TRUE;
1770 player->active = TRUE;
1772 some_player->present = FALSE;
1773 some_player->active = FALSE;
1776 player->element_nr = some_player->element_nr;
1779 StorePlayer[jx][jy] = player->element_nr;
1780 player->jx = player->last_jx = jx;
1781 player->jy = player->last_jy = jy;
1791 /* when playing a tape, eliminate all players which do not participate */
1793 for (i = 0; i < MAX_PLAYERS; i++)
1795 if (stored_player[i].active && !tape.player_participates[i])
1797 struct PlayerInfo *player = &stored_player[i];
1798 int jx = player->jx, jy = player->jy;
1800 player->active = FALSE;
1801 StorePlayer[jx][jy] = 0;
1802 Feld[jx][jy] = EL_EMPTY;
1806 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1808 /* when in single player mode, eliminate all but the first active player */
1810 for (i = 0; i < MAX_PLAYERS; i++)
1812 if (stored_player[i].active)
1814 for (j = i + 1; j < MAX_PLAYERS; j++)
1816 if (stored_player[j].active)
1818 struct PlayerInfo *player = &stored_player[j];
1819 int jx = player->jx, jy = player->jy;
1821 player->active = FALSE;
1822 player->present = FALSE;
1824 StorePlayer[jx][jy] = 0;
1825 Feld[jx][jy] = EL_EMPTY;
1832 /* when recording the game, store which players take part in the game */
1835 for (i = 0; i < MAX_PLAYERS; i++)
1836 if (stored_player[i].active)
1837 tape.player_participates[i] = TRUE;
1842 for (i = 0; i < MAX_PLAYERS; i++)
1844 struct PlayerInfo *player = &stored_player[i];
1846 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1851 if (local_player == player)
1852 printf("Player %d is local player.\n", i+1);
1856 if (BorderElement == EL_EMPTY)
1859 SBX_Right = lev_fieldx - SCR_FIELDX;
1861 SBY_Lower = lev_fieldy - SCR_FIELDY;
1866 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1868 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1871 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1872 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1874 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1875 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1877 /* if local player not found, look for custom element that might create
1878 the player (make some assumptions about the right custom element) */
1879 if (!local_player->present)
1881 int start_x = 0, start_y = 0;
1882 int found_rating = 0;
1883 int found_element = EL_UNDEFINED;
1885 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1887 int element = Feld[x][y];
1892 if (!IS_CUSTOM_ELEMENT(element))
1895 if (CAN_CHANGE(element))
1897 for (i = 0; i < element_info[element].num_change_pages; i++)
1899 content = element_info[element].change_page[i].target_element;
1900 is_player = ELEM_IS_PLAYER(content);
1902 if (is_player && (found_rating < 3 || element < found_element))
1908 found_element = element;
1913 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1915 content = element_info[element].content[xx][yy];
1916 is_player = ELEM_IS_PLAYER(content);
1918 if (is_player && (found_rating < 2 || element < found_element))
1920 start_x = x + xx - 1;
1921 start_y = y + yy - 1;
1924 found_element = element;
1927 if (!CAN_CHANGE(element))
1930 for (i = 0; i < element_info[element].num_change_pages; i++)
1932 content= element_info[element].change_page[i].target_content[xx][yy];
1933 is_player = ELEM_IS_PLAYER(content);
1935 if (is_player && (found_rating < 1 || element < found_element))
1937 start_x = x + xx - 1;
1938 start_y = y + yy - 1;
1941 found_element = element;
1947 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1948 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1951 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1952 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1958 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1959 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1960 local_player->jx - MIDPOSX);
1962 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1963 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1964 local_player->jy - MIDPOSY);
1966 scroll_x = SBX_Left;
1967 scroll_y = SBY_Upper;
1968 if (local_player->jx >= SBX_Left + MIDPOSX)
1969 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1970 local_player->jx - MIDPOSX :
1972 if (local_player->jy >= SBY_Upper + MIDPOSY)
1973 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1974 local_player->jy - MIDPOSY :
1979 CloseDoor(DOOR_CLOSE_1);
1984 /* after drawing the level, correct some elements */
1985 if (game.timegate_time_left == 0)
1986 CloseAllOpenTimegates();
1988 if (setup.soft_scrolling)
1989 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1991 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1994 /* copy default game door content to main double buffer */
1995 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1996 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1998 DrawGameDoorValues();
2002 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2003 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2004 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2008 /* copy actual game door content to door double buffer for OpenDoor() */
2009 BlitBitmap(drawto, bitmap_db_door,
2010 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2012 OpenDoor(DOOR_OPEN_ALL);
2014 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2016 if (setup.sound_music)
2019 KeyboardAutoRepeatOffUnlessAutoplay();
2023 for (i = 0; i < MAX_PLAYERS; i++)
2024 printf("Player %d %sactive.\n",
2025 i + 1, (stored_player[i].active ? "" : "not "));
2029 printf("::: starting game [%d]\n", FrameCounter);
2033 void InitMovDir(int x, int y)
2035 int i, element = Feld[x][y];
2036 static int xy[4][2] =
2043 static int direction[3][4] =
2045 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2046 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2047 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2056 Feld[x][y] = EL_BUG;
2057 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2060 case EL_SPACESHIP_RIGHT:
2061 case EL_SPACESHIP_UP:
2062 case EL_SPACESHIP_LEFT:
2063 case EL_SPACESHIP_DOWN:
2064 Feld[x][y] = EL_SPACESHIP;
2065 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2068 case EL_BD_BUTTERFLY_RIGHT:
2069 case EL_BD_BUTTERFLY_UP:
2070 case EL_BD_BUTTERFLY_LEFT:
2071 case EL_BD_BUTTERFLY_DOWN:
2072 Feld[x][y] = EL_BD_BUTTERFLY;
2073 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2076 case EL_BD_FIREFLY_RIGHT:
2077 case EL_BD_FIREFLY_UP:
2078 case EL_BD_FIREFLY_LEFT:
2079 case EL_BD_FIREFLY_DOWN:
2080 Feld[x][y] = EL_BD_FIREFLY;
2081 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2084 case EL_PACMAN_RIGHT:
2086 case EL_PACMAN_LEFT:
2087 case EL_PACMAN_DOWN:
2088 Feld[x][y] = EL_PACMAN;
2089 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2092 case EL_SP_SNIKSNAK:
2093 MovDir[x][y] = MV_UP;
2096 case EL_SP_ELECTRON:
2097 MovDir[x][y] = MV_LEFT;
2104 Feld[x][y] = EL_MOLE;
2105 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2109 if (IS_CUSTOM_ELEMENT(element))
2111 struct ElementInfo *ei = &element_info[element];
2112 int move_direction_initial = ei->move_direction_initial;
2113 int move_pattern = ei->move_pattern;
2115 if (move_direction_initial == MV_START_PREVIOUS)
2117 if (MovDir[x][y] != MV_NO_MOVING)
2120 move_direction_initial = MV_START_AUTOMATIC;
2123 if (move_direction_initial == MV_START_RANDOM)
2124 MovDir[x][y] = 1 << RND(4);
2125 else if (move_direction_initial & MV_ANY_DIRECTION)
2126 MovDir[x][y] = move_direction_initial;
2127 else if (move_pattern == MV_ALL_DIRECTIONS ||
2128 move_pattern == MV_TURNING_LEFT ||
2129 move_pattern == MV_TURNING_RIGHT ||
2130 move_pattern == MV_TURNING_LEFT_RIGHT ||
2131 move_pattern == MV_TURNING_RIGHT_LEFT ||
2132 move_pattern == MV_TURNING_RANDOM)
2133 MovDir[x][y] = 1 << RND(4);
2134 else if (move_pattern == MV_HORIZONTAL)
2135 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2136 else if (move_pattern == MV_VERTICAL)
2137 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2138 else if (move_pattern & MV_ANY_DIRECTION)
2139 MovDir[x][y] = element_info[element].move_pattern;
2140 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2141 move_pattern == MV_ALONG_RIGHT_SIDE)
2144 /* use random direction as default start direction */
2145 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2146 MovDir[x][y] = 1 << RND(4);
2149 for (i = 0; i < NUM_DIRECTIONS; i++)
2151 int x1 = x + xy[i][0];
2152 int y1 = y + xy[i][1];
2154 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2156 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2157 MovDir[x][y] = direction[0][i];
2159 MovDir[x][y] = direction[1][i];
2168 MovDir[x][y] = 1 << RND(4);
2170 if (element != EL_BUG &&
2171 element != EL_SPACESHIP &&
2172 element != EL_BD_BUTTERFLY &&
2173 element != EL_BD_FIREFLY)
2176 for (i = 0; i < NUM_DIRECTIONS; i++)
2178 int x1 = x + xy[i][0];
2179 int y1 = y + xy[i][1];
2181 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2183 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2185 MovDir[x][y] = direction[0][i];
2188 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2189 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2191 MovDir[x][y] = direction[1][i];
2200 GfxDir[x][y] = MovDir[x][y];
2203 void InitAmoebaNr(int x, int y)
2206 int group_nr = AmoebeNachbarNr(x, y);
2210 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2212 if (AmoebaCnt[i] == 0)
2220 AmoebaNr[x][y] = group_nr;
2221 AmoebaCnt[group_nr]++;
2222 AmoebaCnt2[group_nr]++;
2228 boolean raise_level = FALSE;
2230 if (local_player->MovPos)
2234 if (tape.auto_play) /* tape might already be stopped here */
2235 tape.auto_play_level_solved = TRUE;
2237 if (tape.playing && tape.auto_play)
2238 tape.auto_play_level_solved = TRUE;
2241 local_player->LevelSolved = FALSE;
2243 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2247 if (!tape.playing && setup.sound_loops)
2248 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2249 SND_CTRL_PLAY_LOOP);
2251 while (TimeLeft > 0)
2253 if (!tape.playing && !setup.sound_loops)
2254 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2255 if (TimeLeft > 0 && !(TimeLeft % 10))
2256 RaiseScore(level.score[SC_TIME_BONUS]);
2257 if (TimeLeft > 100 && !(TimeLeft % 10))
2262 DrawGameValue_Time(TimeLeft);
2270 if (!tape.playing && setup.sound_loops)
2271 StopSound(SND_GAME_LEVELTIME_BONUS);
2273 else if (level.time == 0) /* level without time limit */
2275 if (!tape.playing && setup.sound_loops)
2276 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2277 SND_CTRL_PLAY_LOOP);
2279 while (TimePlayed < 999)
2281 if (!tape.playing && !setup.sound_loops)
2282 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2283 if (TimePlayed < 999 && !(TimePlayed % 10))
2284 RaiseScore(level.score[SC_TIME_BONUS]);
2285 if (TimePlayed < 900 && !(TimePlayed % 10))
2290 DrawGameValue_Time(TimePlayed);
2298 if (!tape.playing && setup.sound_loops)
2299 StopSound(SND_GAME_LEVELTIME_BONUS);
2302 /* close exit door after last player */
2303 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2304 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2306 int element = Feld[ExitX][ExitY];
2308 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2309 EL_SP_EXIT_CLOSING);
2311 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2314 /* Hero disappears */
2315 DrawLevelField(ExitX, ExitY);
2321 CloseDoor(DOOR_CLOSE_1);
2326 SaveTape(tape.level_nr); /* Ask to save tape */
2329 if (level_nr == leveldir_current->handicap_level)
2331 leveldir_current->handicap_level++;
2332 SaveLevelSetup_SeriesInfo();
2335 if (level_editor_test_game)
2336 local_player->score = -1; /* no highscore when playing from editor */
2337 else if (level_nr < leveldir_current->last_level)
2338 raise_level = TRUE; /* advance to next level */
2340 if ((hi_pos = NewHiScore()) >= 0)
2342 game_status = GAME_MODE_SCORES;
2343 DrawHallOfFame(hi_pos);
2352 game_status = GAME_MODE_MAIN;
2369 LoadScore(level_nr);
2371 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2372 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2375 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2377 if (local_player->score > highscore[k].Score)
2379 /* player has made it to the hall of fame */
2381 if (k < MAX_SCORE_ENTRIES - 1)
2383 int m = MAX_SCORE_ENTRIES - 1;
2386 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2387 if (!strcmp(setup.player_name, highscore[l].Name))
2389 if (m == k) /* player's new highscore overwrites his old one */
2393 for (l = m; l > k; l--)
2395 strcpy(highscore[l].Name, highscore[l - 1].Name);
2396 highscore[l].Score = highscore[l - 1].Score;
2403 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2404 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2405 highscore[k].Score = local_player->score;
2411 else if (!strncmp(setup.player_name, highscore[k].Name,
2412 MAX_PLAYER_NAME_LEN))
2413 break; /* player already there with a higher score */
2419 SaveScore(level_nr);
2424 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2426 if (player->GfxAction != action || player->GfxDir != dir)
2429 printf("Player frame reset! (%d => %d, %d => %d)\n",
2430 player->GfxAction, action, player->GfxDir, dir);
2433 player->GfxAction = action;
2434 player->GfxDir = dir;
2436 player->StepFrame = 0;
2440 static void ResetRandomAnimationValue(int x, int y)
2442 GfxRandom[x][y] = INIT_GFX_RANDOM();
2445 static void ResetGfxAnimation(int x, int y)
2448 GfxAction[x][y] = ACTION_DEFAULT;
2449 GfxDir[x][y] = MovDir[x][y];
2452 void InitMovingField(int x, int y, int direction)
2454 int element = Feld[x][y];
2455 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2456 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2460 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2461 ResetGfxAnimation(x, y);
2463 MovDir[newx][newy] = MovDir[x][y] = direction;
2464 GfxDir[x][y] = direction;
2466 if (Feld[newx][newy] == EL_EMPTY)
2467 Feld[newx][newy] = EL_BLOCKED;
2469 if (direction == MV_DOWN && CAN_FALL(element))
2470 GfxAction[x][y] = ACTION_FALLING;
2472 GfxAction[x][y] = ACTION_MOVING;
2474 GfxFrame[newx][newy] = GfxFrame[x][y];
2475 GfxRandom[newx][newy] = GfxRandom[x][y];
2476 GfxAction[newx][newy] = GfxAction[x][y];
2477 GfxDir[newx][newy] = GfxDir[x][y];
2480 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2482 int direction = MovDir[x][y];
2483 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2484 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2490 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2492 int oldx = x, oldy = y;
2493 int direction = MovDir[x][y];
2495 if (direction == MV_LEFT)
2497 else if (direction == MV_RIGHT)
2499 else if (direction == MV_UP)
2501 else if (direction == MV_DOWN)
2504 *comes_from_x = oldx;
2505 *comes_from_y = oldy;
2508 int MovingOrBlocked2Element(int x, int y)
2510 int element = Feld[x][y];
2512 if (element == EL_BLOCKED)
2516 Blocked2Moving(x, y, &oldx, &oldy);
2517 return Feld[oldx][oldy];
2523 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2525 /* like MovingOrBlocked2Element(), but if element is moving
2526 and (x,y) is the field the moving element is just leaving,
2527 return EL_BLOCKED instead of the element value */
2528 int element = Feld[x][y];
2530 if (IS_MOVING(x, y))
2532 if (element == EL_BLOCKED)
2536 Blocked2Moving(x, y, &oldx, &oldy);
2537 return Feld[oldx][oldy];
2546 static void RemoveField(int x, int y)
2548 Feld[x][y] = EL_EMPTY;
2555 ChangeDelay[x][y] = 0;
2556 ChangePage[x][y] = -1;
2557 Pushed[x][y] = FALSE;
2560 ExplodeField[x][y] = EX_TYPE_NONE;
2563 GfxElement[x][y] = EL_UNDEFINED;
2564 GfxAction[x][y] = ACTION_DEFAULT;
2565 GfxDir[x][y] = MV_NO_MOVING;
2568 void RemoveMovingField(int x, int y)
2570 int oldx = x, oldy = y, newx = x, newy = y;
2571 int element = Feld[x][y];
2572 int next_element = EL_UNDEFINED;
2574 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2577 if (IS_MOVING(x, y))
2579 Moving2Blocked(x, y, &newx, &newy);
2581 if (Feld[newx][newy] != EL_BLOCKED)
2584 if (Feld[newx][newy] != EL_BLOCKED)
2586 /* element is moving, but target field is not free (blocked), but
2587 already occupied by something different (example: acid pool);
2588 in this case, only remove the moving field, but not the target */
2590 RemoveField(oldx, oldy);
2592 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2594 DrawLevelField(oldx, oldy);
2600 else if (element == EL_BLOCKED)
2602 Blocked2Moving(x, y, &oldx, &oldy);
2603 if (!IS_MOVING(oldx, oldy))
2607 if (element == EL_BLOCKED &&
2608 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2609 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2610 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2611 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2612 next_element = get_next_element(Feld[oldx][oldy]);
2614 RemoveField(oldx, oldy);
2615 RemoveField(newx, newy);
2617 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2619 if (next_element != EL_UNDEFINED)
2620 Feld[oldx][oldy] = next_element;
2622 DrawLevelField(oldx, oldy);
2623 DrawLevelField(newx, newy);
2626 void DrawDynamite(int x, int y)
2628 int sx = SCREENX(x), sy = SCREENY(y);
2629 int graphic = el2img(Feld[x][y]);
2632 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2635 if (IS_WALKABLE_INSIDE(Back[x][y]))
2639 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2640 else if (Store[x][y])
2641 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2643 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2646 if (Back[x][y] || Store[x][y])
2647 DrawGraphicThruMask(sx, sy, graphic, frame);
2649 DrawGraphic(sx, sy, graphic, frame);
2651 if (game.emulation == EMU_SUPAPLEX)
2652 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2653 else if (Store[x][y])
2654 DrawGraphicThruMask(sx, sy, graphic, frame);
2656 DrawGraphic(sx, sy, graphic, frame);
2660 void CheckDynamite(int x, int y)
2662 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2666 if (MovDelay[x][y] != 0)
2669 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2676 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2678 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2679 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2680 StopSound(SND_DYNAMITE_ACTIVE);
2682 StopSound(SND_DYNABOMB_ACTIVE);
2688 void DrawRelocatePlayer(struct PlayerInfo *player)
2690 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2691 boolean no_delay = (tape.warp_forward);
2692 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2693 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2694 int jx = player->jx;
2695 int jy = player->jy;
2697 if (level.instant_relocation)
2700 int offset = (setup.scroll_delay ? 3 : 0);
2702 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2704 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2705 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2706 local_player->jx - MIDPOSX);
2708 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2709 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2710 local_player->jy - MIDPOSY);
2714 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2715 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2716 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2718 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2719 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2720 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2722 /* don't scroll over playfield boundaries */
2723 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2724 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2726 /* don't scroll over playfield boundaries */
2727 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2728 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2731 scroll_x += (local_player->jx - old_jx);
2732 scroll_y += (local_player->jy - old_jy);
2734 /* don't scroll over playfield boundaries */
2735 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2736 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2738 /* don't scroll over playfield boundaries */
2739 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2740 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2743 RedrawPlayfield(TRUE, 0,0,0,0);
2749 int offset = (setup.scroll_delay ? 3 : 0);
2751 int scroll_xx = -999, scroll_yy = -999;
2753 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2755 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2758 int fx = FX, fy = FY;
2760 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2761 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2762 local_player->jx - MIDPOSX);
2764 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2765 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2766 local_player->jy - MIDPOSY);
2768 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2769 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2772 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2775 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2782 fx += dx * TILEX / 2;
2783 fy += dy * TILEY / 2;
2785 ScrollLevel(dx, dy);
2788 /* scroll in two steps of half tile size to make things smoother */
2789 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2791 Delay(wait_delay_value);
2793 /* scroll second step to align at full tile size */
2795 Delay(wait_delay_value);
2798 int scroll_xx = -999, scroll_yy = -999;
2800 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2802 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2805 int fx = FX, fy = FY;
2807 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2808 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2809 local_player->jx - MIDPOSX);
2811 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2812 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2813 local_player->jy - MIDPOSY);
2815 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2816 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2819 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2822 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2829 fx += dx * TILEX / 2;
2830 fy += dy * TILEY / 2;
2832 ScrollLevel(dx, dy);
2835 /* scroll in two steps of half tile size to make things smoother */
2836 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2838 Delay(wait_delay_value);
2840 /* scroll second step to align at full tile size */
2842 Delay(wait_delay_value);
2848 Delay(wait_delay_value);
2852 void RelocatePlayer(int jx, int jy, int el_player_raw)
2855 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
2857 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
2859 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2860 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2861 boolean no_delay = (tape.warp_forward);
2862 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2863 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2864 int old_jx = player->jx;
2865 int old_jy = player->jy;
2866 int old_element = Feld[old_jx][old_jy];
2867 int element = Feld[jx][jy];
2868 boolean player_relocated = (old_jx != jx || old_jy != jy);
2870 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2871 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2873 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2874 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2875 int leave_side_horiz = move_dir_horiz;
2876 int leave_side_vert = move_dir_vert;
2878 static int trigger_sides[4][2] =
2880 /* enter side leave side */
2881 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
2882 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
2883 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
2884 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
2886 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
2887 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
2888 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
2889 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
2891 int enter_side = enter_side_horiz | enter_side_vert;
2892 int leave_side = leave_side_horiz | leave_side_vert;
2894 if (player->GameOver) /* do not reanimate dead player */
2897 if (!player_relocated) /* no need to relocate the player */
2900 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2902 RemoveField(jx, jy); /* temporarily remove newly placed player */
2903 DrawLevelField(jx, jy);
2906 if (player->present)
2908 while (player->MovPos)
2910 ScrollPlayer(player, SCROLL_GO_ON);
2911 ScrollScreen(NULL, SCROLL_GO_ON);
2913 #if USE_NEW_MOVE_DELAY
2914 AdvanceFrameAndPlayerCounters(player->index_nr);
2922 Delay(wait_delay_value);
2925 DrawPlayer(player); /* needed here only to cleanup last field */
2926 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2928 player->is_moving = FALSE;
2932 if (IS_CUSTOM_ELEMENT(old_element))
2933 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
2935 player->index_bit, leave_side);
2937 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
2939 player->index_bit, leave_side);
2942 Feld[jx][jy] = el_player;
2943 InitPlayerField(jx, jy, el_player, TRUE);
2945 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
2947 Feld[jx][jy] = element;
2948 InitField(jx, jy, FALSE);
2952 if (player == local_player) /* only visually relocate local player */
2953 DrawRelocatePlayer(player);
2957 TestIfHeroTouchesBadThing(jx, jy);
2958 TestIfPlayerTouchesCustomElement(jx, jy);
2962 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
2967 /* needed to allow change of walkable custom element by entering player */
2968 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
2969 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
2971 /* needed to allow change of walkable custom element by entering player */
2972 Changed[jx][jy] = 0; /* allow another change */
2977 printf("::: player entering %d, %d from %s ...\n", jx, jy,
2978 enter_side == MV_LEFT ? "left" :
2979 enter_side == MV_RIGHT ? "right" :
2980 enter_side == MV_UP ? "top" :
2981 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
2985 if (IS_CUSTOM_ELEMENT(element))
2986 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
2987 player->index_bit, enter_side);
2989 CheckTriggeredElementChangeByPlayer(jx, jy, element,
2990 CE_OTHER_GETS_ENTERED,
2991 player->index_bit, enter_side);
2995 void Explode(int ex, int ey, int phase, int mode)
3002 /* !!! eliminate this variable !!! */
3003 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3008 int last_phase = num_phase * delay;
3009 int half_phase = (num_phase / 2) * delay;
3010 int first_phase_after_start = EX_PHASE_START + 1;
3014 if (game.explosions_delayed)
3016 ExplodeField[ex][ey] = mode;
3020 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3022 int center_element = Feld[ex][ey];
3025 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3029 /* --- This is only really needed (and now handled) in "Impact()". --- */
3030 /* do not explode moving elements that left the explode field in time */
3031 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3032 center_element == EL_EMPTY &&
3033 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3037 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3038 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3040 /* remove things displayed in background while burning dynamite */
3041 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3044 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3046 /* put moving element to center field (and let it explode there) */
3047 center_element = MovingOrBlocked2Element(ex, ey);
3048 RemoveMovingField(ex, ey);
3049 Feld[ex][ey] = center_element;
3055 last_phase = element_info[center_element].explosion_delay + 1;
3057 last_phase = element_info[center_element].explosion_delay;
3061 printf("::: %d -> %d\n", center_element, last_phase);
3065 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3067 int xx = x - ex + 1;
3068 int yy = y - ey + 1;
3073 if (!IN_LEV_FIELD(x, y) ||
3074 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3075 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3078 if (!IN_LEV_FIELD(x, y) ||
3079 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3083 if (!IN_LEV_FIELD(x, y) ||
3084 ((mode != EX_TYPE_NORMAL ||
3085 center_element == EL_AMOEBA_TO_DIAMOND) &&
3086 (x != ex || y != ey)))
3090 element = Feld[x][y];
3092 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3094 element = MovingOrBlocked2Element(x, y);
3096 if (!IS_EXPLOSION_PROOF(element))
3097 RemoveMovingField(x, y);
3103 if (IS_EXPLOSION_PROOF(element))
3106 /* indestructible elements can only explode in center (but not flames) */
3108 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3109 mode == EX_TYPE_BORDER)) ||
3110 element == EL_FLAMES)
3113 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3114 element == EL_FLAMES)
3120 if ((IS_INDESTRUCTIBLE(element) &&
3121 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3122 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3123 element == EL_FLAMES)
3128 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3129 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3130 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3132 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3135 if (IS_ACTIVE_BOMB(element))
3137 /* re-activate things under the bomb like gate or penguin */
3139 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3142 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3147 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3148 element_info[Feld[x][y]].token_name,
3149 Store[x][y], Store2[x][y]);
3156 /* save walkable background elements while explosion on same tile */
3158 if (IS_INDESTRUCTIBLE(element))
3159 Back[x][y] = element;
3163 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3164 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3165 Back[x][y] = element;
3167 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3168 (x != ex || y != ey))
3169 Back[x][y] = element;
3172 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3173 Back[x][y] = element;
3177 /* ignite explodable elements reached by other explosion */
3178 if (element == EL_EXPLOSION)
3179 element = Store2[x][y];
3182 if (AmoebaNr[x][y] &&
3183 (element == EL_AMOEBA_FULL ||
3184 element == EL_BD_AMOEBA ||
3185 element == EL_AMOEBA_GROWING))
3187 AmoebaCnt[AmoebaNr[x][y]]--;
3188 AmoebaCnt2[AmoebaNr[x][y]]--;
3194 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3196 switch(StorePlayer[ex][ey])
3199 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3202 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3205 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3209 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3214 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3215 Store[x][y] = EL_EMPTY;
3217 if (game.emulation == EMU_SUPAPLEX)
3218 Store[x][y] = EL_EMPTY;
3221 else if (center_element == EL_MOLE)
3222 Store[x][y] = EL_EMERALD_RED;
3223 else if (center_element == EL_PENGUIN)
3224 Store[x][y] = EL_EMERALD_PURPLE;
3225 else if (center_element == EL_BUG)
3226 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3227 else if (center_element == EL_BD_BUTTERFLY)
3228 Store[x][y] = EL_BD_DIAMOND;
3229 else if (center_element == EL_SP_ELECTRON)
3230 Store[x][y] = EL_SP_INFOTRON;
3231 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3232 Store[x][y] = level.amoeba_content;
3233 else if (center_element == EL_YAMYAM)
3234 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3235 else if (IS_CUSTOM_ELEMENT(center_element) &&
3236 element_info[center_element].content[xx][yy] != EL_EMPTY)
3237 Store[x][y] = element_info[center_element].content[xx][yy];
3238 else if (element == EL_WALL_EMERALD)
3239 Store[x][y] = EL_EMERALD;
3240 else if (element == EL_WALL_DIAMOND)
3241 Store[x][y] = EL_DIAMOND;
3242 else if (element == EL_WALL_BD_DIAMOND)
3243 Store[x][y] = EL_BD_DIAMOND;
3244 else if (element == EL_WALL_EMERALD_YELLOW)
3245 Store[x][y] = EL_EMERALD_YELLOW;
3246 else if (element == EL_WALL_EMERALD_RED)
3247 Store[x][y] = EL_EMERALD_RED;
3248 else if (element == EL_WALL_EMERALD_PURPLE)
3249 Store[x][y] = EL_EMERALD_PURPLE;
3250 else if (element == EL_WALL_PEARL)
3251 Store[x][y] = EL_PEARL;
3252 else if (element == EL_WALL_CRYSTAL)
3253 Store[x][y] = EL_CRYSTAL;
3254 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3255 Store[x][y] = element_info[element].content[1][1];
3257 Store[x][y] = EL_EMPTY;
3259 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3260 center_element == EL_AMOEBA_TO_DIAMOND)
3261 Store2[x][y] = element;
3264 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3265 element_info[Store2[x][y]].token_name);
3269 if (AmoebaNr[x][y] &&
3270 (element == EL_AMOEBA_FULL ||
3271 element == EL_BD_AMOEBA ||
3272 element == EL_AMOEBA_GROWING))
3274 AmoebaCnt[AmoebaNr[x][y]]--;
3275 AmoebaCnt2[AmoebaNr[x][y]]--;
3281 MovDir[x][y] = MovPos[x][y] = 0;
3282 GfxDir[x][y] = MovDir[x][y];
3287 Feld[x][y] = EL_EXPLOSION;
3289 GfxElement[x][y] = center_element;
3291 GfxElement[x][y] = EL_UNDEFINED;
3294 ExplodePhase[x][y] = 1;
3296 ExplodeDelay[x][y] = last_phase;
3301 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3303 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3310 if (center_element == EL_YAMYAM)
3311 game.yamyam_content_nr =
3312 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3315 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3316 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3330 GfxFrame[x][y] = 0; /* restart explosion animation */
3334 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3338 last_phase = ExplodeDelay[x][y];
3341 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3345 /* activate this even in non-DEBUG version until cause for crash in
3346 getGraphicAnimationFrame() (see below) is found and eliminated */
3350 if (GfxElement[x][y] == EL_UNDEFINED)
3353 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3354 printf("Explode(): This should never happen!\n");
3357 GfxElement[x][y] = EL_EMPTY;
3363 border_element = Store2[x][y];
3365 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3366 border_element = StorePlayer[x][y];
3368 if (IS_PLAYER(x, y))
3369 border_element = StorePlayer[x][y];
3373 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3374 element_info[border_element].token_name, Store2[x][y]);
3378 printf("::: phase == %d\n", phase);
3381 if (phase == element_info[border_element].ignition_delay ||
3382 phase == last_phase)
3384 boolean border_explosion = FALSE;
3388 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3389 !PLAYER_EXPLOSION_PROTECTED(x, y))
3391 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3394 if (IS_PLAYER(x, y))
3397 KillHeroUnlessExplosionProtected(x, y);
3398 border_explosion = TRUE;
3401 if (phase == last_phase)
3402 printf("::: IS_PLAYER\n");
3405 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3408 printf("::: %d,%d: %d %s\n", x, y, border_element,
3409 element_info[border_element].token_name);
3412 Feld[x][y] = Store2[x][y];
3415 border_explosion = TRUE;
3418 if (phase == last_phase)
3419 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3422 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3424 AmoebeUmwandeln(x, y);
3426 border_explosion = TRUE;
3429 if (phase == last_phase)
3430 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3431 element_info[border_element].explosion_delay,
3432 element_info[border_element].ignition_delay,
3438 /* if an element just explodes due to another explosion (chain-reaction),
3439 do not immediately end the new explosion when it was the last frame of
3440 the explosion (as it would be done in the following "if"-statement!) */
3441 if (border_explosion && phase == last_phase)
3448 if (phase == first_phase_after_start)
3450 int element = Store2[x][y];
3452 if (element == EL_BLACK_ORB)
3454 Feld[x][y] = Store2[x][y];
3459 else if (phase == half_phase)
3461 int element = Store2[x][y];
3463 if (IS_PLAYER(x, y))
3464 KillHeroUnlessExplosionProtected(x, y);
3465 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3467 Feld[x][y] = Store2[x][y];
3471 else if (element == EL_AMOEBA_TO_DIAMOND)
3472 AmoebeUmwandeln(x, y);
3476 if (phase == last_phase)
3481 printf("::: done: phase == %d\n", phase);
3485 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3488 element = Feld[x][y] = Store[x][y];
3489 Store[x][y] = Store2[x][y] = 0;
3490 GfxElement[x][y] = EL_UNDEFINED;
3492 /* player can escape from explosions and might therefore be still alive */
3493 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3494 element <= EL_PLAYER_IS_EXPLODING_4)
3495 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3497 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3498 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3499 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3502 /* restore probably existing indestructible background element */
3503 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3504 element = Feld[x][y] = Back[x][y];
3507 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3508 GfxDir[x][y] = MV_NO_MOVING;
3509 ChangeDelay[x][y] = 0;
3510 ChangePage[x][y] = -1;
3513 InitField_WithBug2(x, y, FALSE);
3515 InitField(x, y, FALSE);
3517 /* !!! not needed !!! */
3519 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3520 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3523 if (CAN_MOVE(element))
3528 DrawLevelField(x, y);
3530 TestIfElementTouchesCustomElement(x, y);
3532 if (GFX_CRUMBLED(element))
3533 DrawLevelFieldCrumbledSandNeighbours(x, y);
3535 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3536 StorePlayer[x][y] = 0;
3538 if (ELEM_IS_PLAYER(element))
3539 RelocatePlayer(x, y, element);
3542 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3544 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3548 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3550 int stored = Store[x][y];
3551 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3552 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3556 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3558 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3562 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3566 printf("::: %d / %d [%d - %d]\n",
3567 GfxFrame[x][y], phase - delay, phase, delay);
3571 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3572 element_info[GfxElement[x][y]].token_name,
3577 DrawLevelFieldCrumbledSand(x, y);
3579 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3581 DrawLevelElement(x, y, Back[x][y]);
3582 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3584 else if (IS_WALKABLE_UNDER(Back[x][y]))
3586 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3587 DrawLevelElementThruMask(x, y, Back[x][y]);
3589 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3590 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3594 void DynaExplode(int ex, int ey)
3597 int dynabomb_element = Feld[ex][ey];
3598 int dynabomb_size = 1;
3599 boolean dynabomb_xl = FALSE;
3600 struct PlayerInfo *player;
3601 static int xy[4][2] =
3609 if (IS_ACTIVE_BOMB(dynabomb_element))
3611 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3612 dynabomb_size = player->dynabomb_size;
3613 dynabomb_xl = player->dynabomb_xl;
3614 player->dynabombs_left++;
3617 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3619 for (i = 0; i < NUM_DIRECTIONS; i++)
3621 for (j = 1; j <= dynabomb_size; j++)
3623 int x = ex + j * xy[i][0];
3624 int y = ey + j * xy[i][1];
3627 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3630 element = Feld[x][y];
3632 /* do not restart explosions of fields with active bombs */
3633 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3636 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3640 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3641 !IS_DIGGABLE(element) && !dynabomb_xl)
3644 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3645 !CAN_GROW_INTO(element) && !dynabomb_xl)
3649 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3650 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3651 element != EL_SAND && !dynabomb_xl)
3658 void Bang(int x, int y)
3661 int element = MovingOrBlocked2Element(x, y);
3663 int element = Feld[x][y];
3667 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3669 if (IS_PLAYER(x, y))
3672 struct PlayerInfo *player = PLAYERINFO(x, y);
3674 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3675 player->element_nr);
3680 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3682 if (game.emulation == EMU_SUPAPLEX)
3683 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3685 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3690 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3698 case EL_BD_BUTTERFLY:
3701 case EL_DARK_YAMYAM:
3705 RaiseScoreElement(element);
3706 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3708 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3709 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3710 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3711 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3712 case EL_DYNABOMB_INCREASE_NUMBER:
3713 case EL_DYNABOMB_INCREASE_SIZE:
3714 case EL_DYNABOMB_INCREASE_POWER:
3719 case EL_LAMP_ACTIVE:
3721 case EL_AMOEBA_TO_DIAMOND:
3723 if (IS_PLAYER(x, y))
3724 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3726 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3730 if (element_info[element].explosion_type == EXPLODES_CROSS)
3732 if (CAN_EXPLODE_CROSS(element))
3735 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3740 else if (element_info[element].explosion_type == EXPLODES_1X1)
3742 else if (CAN_EXPLODE_1X1(element))
3744 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3746 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3750 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3753 void SplashAcid(int x, int y)
3756 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3757 (!IN_LEV_FIELD(x - 1, y - 2) ||
3758 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3759 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3761 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3762 (!IN_LEV_FIELD(x + 1, y - 2) ||
3763 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3764 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3766 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3768 /* input: position of element entering acid (obsolete) */
3770 int element = Feld[x][y];
3772 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3775 if (element != EL_ACID_SPLASH_LEFT &&
3776 element != EL_ACID_SPLASH_RIGHT)
3778 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3780 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3781 (!IN_LEV_FIELD(x - 1, y - 1) ||
3782 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3783 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3785 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3786 (!IN_LEV_FIELD(x + 1, y - 1) ||
3787 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3788 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3793 static void InitBeltMovement()
3795 static int belt_base_element[4] =
3797 EL_CONVEYOR_BELT_1_LEFT,
3798 EL_CONVEYOR_BELT_2_LEFT,
3799 EL_CONVEYOR_BELT_3_LEFT,
3800 EL_CONVEYOR_BELT_4_LEFT
3802 static int belt_base_active_element[4] =
3804 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3805 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3806 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3807 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3812 /* set frame order for belt animation graphic according to belt direction */
3813 for (i = 0; i < NUM_BELTS; i++)
3817 for (j = 0; j < NUM_BELT_PARTS; j++)
3819 int element = belt_base_active_element[belt_nr] + j;
3820 int graphic = el2img(element);
3822 if (game.belt_dir[i] == MV_LEFT)
3823 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3825 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3829 for (y = 0; y < lev_fieldy; y++)
3831 for (x = 0; x < lev_fieldx; x++)
3833 int element = Feld[x][y];
3835 for (i = 0; i < NUM_BELTS; i++)
3837 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3839 int e_belt_nr = getBeltNrFromBeltElement(element);
3842 if (e_belt_nr == belt_nr)
3844 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3846 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3854 static void ToggleBeltSwitch(int x, int y)
3856 static int belt_base_element[4] =
3858 EL_CONVEYOR_BELT_1_LEFT,
3859 EL_CONVEYOR_BELT_2_LEFT,
3860 EL_CONVEYOR_BELT_3_LEFT,
3861 EL_CONVEYOR_BELT_4_LEFT
3863 static int belt_base_active_element[4] =
3865 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3866 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3867 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3868 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3870 static int belt_base_switch_element[4] =
3872 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3873 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3874 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3875 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3877 static int belt_move_dir[4] =
3885 int element = Feld[x][y];
3886 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3887 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3888 int belt_dir = belt_move_dir[belt_dir_nr];
3891 if (!IS_BELT_SWITCH(element))
3894 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3895 game.belt_dir[belt_nr] = belt_dir;
3897 if (belt_dir_nr == 3)
3900 /* set frame order for belt animation graphic according to belt direction */
3901 for (i = 0; i < NUM_BELT_PARTS; i++)
3903 int element = belt_base_active_element[belt_nr] + i;
3904 int graphic = el2img(element);
3906 if (belt_dir == MV_LEFT)
3907 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3909 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3912 for (yy = 0; yy < lev_fieldy; yy++)
3914 for (xx = 0; xx < lev_fieldx; xx++)
3916 int element = Feld[xx][yy];
3918 if (IS_BELT_SWITCH(element))
3920 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3922 if (e_belt_nr == belt_nr)
3924 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3925 DrawLevelField(xx, yy);
3928 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3930 int e_belt_nr = getBeltNrFromBeltElement(element);
3932 if (e_belt_nr == belt_nr)
3934 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3936 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3937 DrawLevelField(xx, yy);
3940 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3942 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3944 if (e_belt_nr == belt_nr)
3946 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3948 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3949 DrawLevelField(xx, yy);
3956 static void ToggleSwitchgateSwitch(int x, int y)
3960 game.switchgate_pos = !game.switchgate_pos;
3962 for (yy = 0; yy < lev_fieldy; yy++)
3964 for (xx = 0; xx < lev_fieldx; xx++)
3966 int element = Feld[xx][yy];
3968 if (element == EL_SWITCHGATE_SWITCH_UP ||
3969 element == EL_SWITCHGATE_SWITCH_DOWN)
3971 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3972 DrawLevelField(xx, yy);
3974 else if (element == EL_SWITCHGATE_OPEN ||
3975 element == EL_SWITCHGATE_OPENING)
3977 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3979 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3981 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3984 else if (element == EL_SWITCHGATE_CLOSED ||
3985 element == EL_SWITCHGATE_CLOSING)
3987 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3989 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3991 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3998 static int getInvisibleActiveFromInvisibleElement(int element)
4000 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4001 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4002 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4006 static int getInvisibleFromInvisibleActiveElement(int element)
4008 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4009 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4010 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4014 static void RedrawAllLightSwitchesAndInvisibleElements()
4018 for (y = 0; y < lev_fieldy; y++)
4020 for (x = 0; x < lev_fieldx; x++)
4022 int element = Feld[x][y];
4024 if (element == EL_LIGHT_SWITCH &&
4025 game.light_time_left > 0)
4027 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4028 DrawLevelField(x, y);
4030 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4031 game.light_time_left == 0)
4033 Feld[x][y] = EL_LIGHT_SWITCH;
4034 DrawLevelField(x, y);
4036 else if (element == EL_INVISIBLE_STEELWALL ||
4037 element == EL_INVISIBLE_WALL ||
4038 element == EL_INVISIBLE_SAND)
4040 if (game.light_time_left > 0)
4041 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4043 DrawLevelField(x, y);
4045 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4046 element == EL_INVISIBLE_WALL_ACTIVE ||
4047 element == EL_INVISIBLE_SAND_ACTIVE)
4049 if (game.light_time_left == 0)
4050 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4052 DrawLevelField(x, y);
4058 static void ToggleLightSwitch(int x, int y)
4060 int element = Feld[x][y];
4062 game.light_time_left =
4063 (element == EL_LIGHT_SWITCH ?
4064 level.time_light * FRAMES_PER_SECOND : 0);
4066 RedrawAllLightSwitchesAndInvisibleElements();
4069 static void ActivateTimegateSwitch(int x, int y)
4073 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4075 for (yy = 0; yy < lev_fieldy; yy++)
4077 for (xx = 0; xx < lev_fieldx; xx++)
4079 int element = Feld[xx][yy];
4081 if (element == EL_TIMEGATE_CLOSED ||
4082 element == EL_TIMEGATE_CLOSING)
4084 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4085 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4089 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4091 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4092 DrawLevelField(xx, yy);
4099 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4102 inline static int getElementMoveStepsize(int x, int y)
4104 int element = Feld[x][y];
4105 int direction = MovDir[x][y];
4106 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4107 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4108 int horiz_move = (dx != 0);
4109 int sign = (horiz_move ? dx : dy);
4110 int step = sign * element_info[element].move_stepsize;
4112 /* special values for move stepsize for spring and things on conveyor belt */
4116 if (element == EL_SPRING)
4117 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4118 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
4119 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4120 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4122 if (CAN_FALL(element) &&
4123 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4124 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4125 else if (element == EL_SPRING)
4126 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4133 void Impact(int x, int y)
4135 boolean lastline = (y == lev_fieldy-1);
4136 boolean object_hit = FALSE;
4137 boolean impact = (lastline || object_hit);
4138 int element = Feld[x][y];
4139 int smashed = EL_STEELWALL;
4141 if (!lastline) /* check if element below was hit */
4143 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4146 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4147 MovDir[x][y + 1] != MV_DOWN ||
4148 MovPos[x][y + 1] <= TILEY / 2));
4151 object_hit = !IS_FREE(x, y + 1);
4154 /* do not smash moving elements that left the smashed field in time */
4155 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4156 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4160 smashed = MovingOrBlocked2Element(x, y + 1);
4162 impact = (lastline || object_hit);
4165 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4167 SplashAcid(x, y + 1);
4171 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4172 /* only reset graphic animation if graphic really changes after impact */
4174 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4176 ResetGfxAnimation(x, y);
4177 DrawLevelField(x, y);
4180 if (impact && CAN_EXPLODE_IMPACT(element))
4185 else if (impact && element == EL_PEARL)
4187 ResetGfxAnimation(x, y);
4189 Feld[x][y] = EL_PEARL_BREAKING;
4190 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4193 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4195 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4200 if (impact && element == EL_AMOEBA_DROP)
4202 if (object_hit && IS_PLAYER(x, y + 1))
4203 KillHeroUnlessEnemyProtected(x, y + 1);
4204 else if (object_hit && smashed == EL_PENGUIN)
4208 Feld[x][y] = EL_AMOEBA_GROWING;
4209 Store[x][y] = EL_AMOEBA_WET;
4211 ResetRandomAnimationValue(x, y);
4216 if (object_hit) /* check which object was hit */
4218 if (CAN_PASS_MAGIC_WALL(element) &&
4219 (smashed == EL_MAGIC_WALL ||
4220 smashed == EL_BD_MAGIC_WALL))
4223 int activated_magic_wall =
4224 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4225 EL_BD_MAGIC_WALL_ACTIVE);
4227 /* activate magic wall / mill */
4228 for (yy = 0; yy < lev_fieldy; yy++)
4229 for (xx = 0; xx < lev_fieldx; xx++)
4230 if (Feld[xx][yy] == smashed)
4231 Feld[xx][yy] = activated_magic_wall;
4233 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4234 game.magic_wall_active = TRUE;
4236 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4237 SND_MAGIC_WALL_ACTIVATING :
4238 SND_BD_MAGIC_WALL_ACTIVATING));
4241 if (IS_PLAYER(x, y + 1))
4243 if (CAN_SMASH_PLAYER(element))
4245 KillHeroUnlessEnemyProtected(x, y + 1);
4249 else if (smashed == EL_PENGUIN)
4251 if (CAN_SMASH_PLAYER(element))
4257 else if (element == EL_BD_DIAMOND)
4259 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4265 else if (((element == EL_SP_INFOTRON ||
4266 element == EL_SP_ZONK) &&
4267 (smashed == EL_SP_SNIKSNAK ||
4268 smashed == EL_SP_ELECTRON ||
4269 smashed == EL_SP_DISK_ORANGE)) ||
4270 (element == EL_SP_INFOTRON &&
4271 smashed == EL_SP_DISK_YELLOW))
4277 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4283 else if (CAN_SMASH_EVERYTHING(element))
4285 if (IS_CLASSIC_ENEMY(smashed) ||
4286 CAN_EXPLODE_SMASHED(smashed))
4291 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4293 if (smashed == EL_LAMP ||
4294 smashed == EL_LAMP_ACTIVE)
4299 else if (smashed == EL_NUT)
4301 Feld[x][y + 1] = EL_NUT_BREAKING;
4302 PlayLevelSound(x, y, SND_NUT_BREAKING);
4303 RaiseScoreElement(EL_NUT);
4306 else if (smashed == EL_PEARL)
4308 ResetGfxAnimation(x, y);
4310 Feld[x][y + 1] = EL_PEARL_BREAKING;
4311 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4314 else if (smashed == EL_DIAMOND)
4316 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4317 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4320 else if (IS_BELT_SWITCH(smashed))
4322 ToggleBeltSwitch(x, y + 1);
4324 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4325 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4327 ToggleSwitchgateSwitch(x, y + 1);
4329 else if (smashed == EL_LIGHT_SWITCH ||
4330 smashed == EL_LIGHT_SWITCH_ACTIVE)
4332 ToggleLightSwitch(x, y + 1);
4337 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4340 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4343 /* !!! TEST ONLY !!! */
4344 CheckElementChangeBySide(x, y + 1, smashed, element,
4345 CE_SWITCHED, CH_SIDE_TOP);
4346 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4347 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4349 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4350 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4351 CheckElementChangeBySide(x, y + 1, smashed, element,
4352 CE_SWITCHED, CH_SIDE_TOP);
4358 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4363 /* play sound of magic wall / mill */
4365 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4366 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4368 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4369 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4370 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4371 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4376 /* play sound of object that hits the ground */
4377 if (lastline || object_hit)
4378 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4381 inline static void TurnRoundExt(int x, int y)
4393 { 0, 0 }, { 0, 0 }, { 0, 0 },
4398 int left, right, back;
4402 { MV_DOWN, MV_UP, MV_RIGHT },
4403 { MV_UP, MV_DOWN, MV_LEFT },
4405 { MV_LEFT, MV_RIGHT, MV_DOWN },
4409 { MV_RIGHT, MV_LEFT, MV_UP }
4412 int element = Feld[x][y];
4413 int move_pattern = element_info[element].move_pattern;
4415 int old_move_dir = MovDir[x][y];
4416 int left_dir = turn[old_move_dir].left;
4417 int right_dir = turn[old_move_dir].right;
4418 int back_dir = turn[old_move_dir].back;
4420 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4421 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4422 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4423 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4425 int left_x = x + left_dx, left_y = y + left_dy;
4426 int right_x = x + right_dx, right_y = y + right_dy;
4427 int move_x = x + move_dx, move_y = y + move_dy;
4431 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4433 TestIfBadThingTouchesOtherBadThing(x, y);
4435 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4436 MovDir[x][y] = right_dir;
4437 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4438 MovDir[x][y] = left_dir;
4440 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4442 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4446 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4447 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4449 TestIfBadThingTouchesOtherBadThing(x, y);
4451 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4452 MovDir[x][y] = left_dir;
4453 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4454 MovDir[x][y] = right_dir;
4456 if ((element == EL_SPACESHIP ||
4457 element == EL_SP_SNIKSNAK ||
4458 element == EL_SP_ELECTRON)
4459 && MovDir[x][y] != old_move_dir)
4461 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4465 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4467 TestIfBadThingTouchesOtherBadThing(x, y);
4469 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4470 MovDir[x][y] = left_dir;
4471 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4472 MovDir[x][y] = right_dir;
4474 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4476 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4479 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4481 TestIfBadThingTouchesOtherBadThing(x, y);
4483 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4484 MovDir[x][y] = left_dir;
4485 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4486 MovDir[x][y] = right_dir;
4488 if (MovDir[x][y] != old_move_dir)
4492 else if (element == EL_YAMYAM)
4494 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4495 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4497 if (can_turn_left && can_turn_right)
4498 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4499 else if (can_turn_left)
4500 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4501 else if (can_turn_right)
4502 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4504 MovDir[x][y] = back_dir;
4506 MovDelay[x][y] = 16 + 16 * RND(3);
4508 else if (element == EL_DARK_YAMYAM)
4510 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4512 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4515 if (can_turn_left && can_turn_right)
4516 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4517 else if (can_turn_left)
4518 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4519 else if (can_turn_right)
4520 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4522 MovDir[x][y] = back_dir;
4524 MovDelay[x][y] = 16 + 16 * RND(3);
4526 else if (element == EL_PACMAN)
4528 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4529 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4531 if (can_turn_left && can_turn_right)
4532 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4533 else if (can_turn_left)
4534 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4535 else if (can_turn_right)
4536 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4538 MovDir[x][y] = back_dir;
4540 MovDelay[x][y] = 6 + RND(40);
4542 else if (element == EL_PIG)
4544 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4545 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4546 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4547 boolean should_turn_left, should_turn_right, should_move_on;
4549 int rnd = RND(rnd_value);
4551 should_turn_left = (can_turn_left &&
4553 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4554 y + back_dy + left_dy)));
4555 should_turn_right = (can_turn_right &&
4557 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4558 y + back_dy + right_dy)));
4559 should_move_on = (can_move_on &&
4562 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4563 y + move_dy + left_dy) ||
4564 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4565 y + move_dy + right_dy)));
4567 if (should_turn_left || should_turn_right || should_move_on)
4569 if (should_turn_left && should_turn_right && should_move_on)
4570 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4571 rnd < 2 * rnd_value / 3 ? right_dir :
4573 else if (should_turn_left && should_turn_right)
4574 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4575 else if (should_turn_left && should_move_on)
4576 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4577 else if (should_turn_right && should_move_on)
4578 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4579 else if (should_turn_left)
4580 MovDir[x][y] = left_dir;
4581 else if (should_turn_right)
4582 MovDir[x][y] = right_dir;
4583 else if (should_move_on)
4584 MovDir[x][y] = old_move_dir;
4586 else if (can_move_on && rnd > rnd_value / 8)
4587 MovDir[x][y] = old_move_dir;
4588 else if (can_turn_left && can_turn_right)
4589 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4590 else if (can_turn_left && rnd > rnd_value / 8)
4591 MovDir[x][y] = left_dir;
4592 else if (can_turn_right && rnd > rnd_value/8)
4593 MovDir[x][y] = right_dir;
4595 MovDir[x][y] = back_dir;
4597 xx = x + move_xy[MovDir[x][y]].x;
4598 yy = y + move_xy[MovDir[x][y]].y;
4600 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4601 MovDir[x][y] = old_move_dir;
4605 else if (element == EL_DRAGON)
4607 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4608 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4609 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4611 int rnd = RND(rnd_value);
4614 if (FrameCounter < 1 && x == 0 && y == 29)
4615 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4618 if (can_move_on && rnd > rnd_value / 8)
4619 MovDir[x][y] = old_move_dir;
4620 else if (can_turn_left && can_turn_right)
4621 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4622 else if (can_turn_left && rnd > rnd_value / 8)
4623 MovDir[x][y] = left_dir;
4624 else if (can_turn_right && rnd > rnd_value / 8)
4625 MovDir[x][y] = right_dir;
4627 MovDir[x][y] = back_dir;
4629 xx = x + move_xy[MovDir[x][y]].x;
4630 yy = y + move_xy[MovDir[x][y]].y;
4633 if (FrameCounter < 1 && x == 0 && y == 29)
4634 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4635 xx, yy, Feld[xx][yy],
4640 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4641 MovDir[x][y] = old_move_dir;
4643 if (!IS_FREE(xx, yy))
4644 MovDir[x][y] = old_move_dir;
4648 if (FrameCounter < 1 && x == 0 && y == 29)
4649 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4654 else if (element == EL_MOLE)
4656 boolean can_move_on =
4657 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4658 IS_AMOEBOID(Feld[move_x][move_y]) ||
4659 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4662 boolean can_turn_left =
4663 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4664 IS_AMOEBOID(Feld[left_x][left_y])));
4666 boolean can_turn_right =
4667 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4668 IS_AMOEBOID(Feld[right_x][right_y])));
4670 if (can_turn_left && can_turn_right)
4671 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4672 else if (can_turn_left)
4673 MovDir[x][y] = left_dir;
4675 MovDir[x][y] = right_dir;
4678 if (MovDir[x][y] != old_move_dir)
4681 else if (element == EL_BALLOON)
4683 MovDir[x][y] = game.balloon_dir;
4686 else if (element == EL_SPRING)
4689 if (MovDir[x][y] & MV_HORIZONTAL &&
4690 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4691 MovDir[x][y] = MV_NO_MOVING;
4693 if (MovDir[x][y] & MV_HORIZONTAL &&
4694 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4695 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4696 MovDir[x][y] = MV_NO_MOVING;
4701 else if (element == EL_ROBOT ||
4702 element == EL_SATELLITE ||
4703 element == EL_PENGUIN)
4705 int attr_x = -1, attr_y = -1;
4716 for (i = 0; i < MAX_PLAYERS; i++)
4718 struct PlayerInfo *player = &stored_player[i];
4719 int jx = player->jx, jy = player->jy;
4721 if (!player->active)
4725 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4734 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4735 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4736 game.engine_version < VERSION_IDENT(3,1,0,0)))
4738 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4745 if (element == EL_PENGUIN)
4748 static int xy[4][2] =
4756 for (i = 0; i < NUM_DIRECTIONS; i++)
4758 int ex = x + xy[i][0];
4759 int ey = y + xy[i][1];
4761 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4770 MovDir[x][y] = MV_NO_MOVING;
4772 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4773 else if (attr_x > x)
4774 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4776 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4777 else if (attr_y > y)
4778 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4780 if (element == EL_ROBOT)
4784 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4785 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4786 Moving2Blocked(x, y, &newx, &newy);
4788 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4789 MovDelay[x][y] = 8 + 8 * !RND(3);
4791 MovDelay[x][y] = 16;
4793 else if (element == EL_PENGUIN)
4799 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4801 boolean first_horiz = RND(2);
4802 int new_move_dir = MovDir[x][y];
4805 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4806 Moving2Blocked(x, y, &newx, &newy);
4808 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4812 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4813 Moving2Blocked(x, y, &newx, &newy);
4815 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4818 MovDir[x][y] = old_move_dir;
4822 else /* (element == EL_SATELLITE) */
4828 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4830 boolean first_horiz = RND(2);
4831 int new_move_dir = MovDir[x][y];
4834 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4835 Moving2Blocked(x, y, &newx, &newy);
4837 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4841 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4842 Moving2Blocked(x, y, &newx, &newy);
4844 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4847 MovDir[x][y] = old_move_dir;
4852 else if (move_pattern == MV_TURNING_LEFT ||
4853 move_pattern == MV_TURNING_RIGHT ||
4854 move_pattern == MV_TURNING_LEFT_RIGHT ||
4855 move_pattern == MV_TURNING_RIGHT_LEFT ||
4856 move_pattern == MV_TURNING_RANDOM ||
4857 move_pattern == MV_ALL_DIRECTIONS)
4859 boolean can_turn_left =
4860 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4861 boolean can_turn_right =
4862 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4864 if (move_pattern == MV_TURNING_LEFT)
4865 MovDir[x][y] = left_dir;
4866 else if (move_pattern == MV_TURNING_RIGHT)
4867 MovDir[x][y] = right_dir;
4868 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4869 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4870 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4871 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4872 else if (move_pattern == MV_TURNING_RANDOM)
4873 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4874 can_turn_right && !can_turn_left ? right_dir :
4875 RND(2) ? left_dir : right_dir);
4876 else if (can_turn_left && can_turn_right)
4877 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4878 else if (can_turn_left)
4879 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4880 else if (can_turn_right)
4881 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4883 MovDir[x][y] = back_dir;
4885 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4887 else if (move_pattern == MV_HORIZONTAL ||
4888 move_pattern == MV_VERTICAL)
4890 if (move_pattern & old_move_dir)
4891 MovDir[x][y] = back_dir;
4892 else if (move_pattern == MV_HORIZONTAL)
4893 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4894 else if (move_pattern == MV_VERTICAL)
4895 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4897 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4899 else if (move_pattern & MV_ANY_DIRECTION)
4901 MovDir[x][y] = move_pattern;
4902 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4904 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4906 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4907 MovDir[x][y] = left_dir;
4908 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4909 MovDir[x][y] = right_dir;
4911 if (MovDir[x][y] != old_move_dir)
4912 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4914 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4916 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4917 MovDir[x][y] = right_dir;
4918 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4919 MovDir[x][y] = left_dir;
4921 if (MovDir[x][y] != old_move_dir)
4922 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4924 else if (move_pattern == MV_TOWARDS_PLAYER ||
4925 move_pattern == MV_AWAY_FROM_PLAYER)
4927 int attr_x = -1, attr_y = -1;
4929 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4940 for (i = 0; i < MAX_PLAYERS; i++)
4942 struct PlayerInfo *player = &stored_player[i];
4943 int jx = player->jx, jy = player->jy;
4945 if (!player->active)
4949 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4957 MovDir[x][y] = MV_NO_MOVING;
4959 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4960 else if (attr_x > x)
4961 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4963 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4964 else if (attr_y > y)
4965 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4967 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4969 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4971 boolean first_horiz = RND(2);
4972 int new_move_dir = MovDir[x][y];
4975 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4976 Moving2Blocked(x, y, &newx, &newy);
4978 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4982 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4983 Moving2Blocked(x, y, &newx, &newy);
4985 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4988 MovDir[x][y] = old_move_dir;
4991 else if (move_pattern == MV_WHEN_PUSHED ||
4992 move_pattern == MV_WHEN_DROPPED)
4994 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4995 MovDir[x][y] = MV_NO_MOVING;
4999 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5001 static int test_xy[7][2] =
5011 static int test_dir[7] =
5021 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5022 int move_preference = -1000000; /* start with very low preference */
5023 int new_move_dir = MV_NO_MOVING;
5024 int start_test = RND(4);
5027 for (i = 0; i < NUM_DIRECTIONS; i++)
5029 int move_dir = test_dir[start_test + i];
5030 int move_dir_preference;
5032 xx = x + test_xy[start_test + i][0];
5033 yy = y + test_xy[start_test + i][1];
5035 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5036 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5038 new_move_dir = move_dir;
5043 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5046 move_dir_preference = -1 * RunnerVisit[xx][yy];
5047 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5048 move_dir_preference = PlayerVisit[xx][yy];
5050 if (move_dir_preference > move_preference)
5052 /* prefer field that has not been visited for the longest time */
5053 move_preference = move_dir_preference;
5054 new_move_dir = move_dir;
5056 else if (move_dir_preference == move_preference &&
5057 move_dir == old_move_dir)
5059 /* prefer last direction when all directions are preferred equally */
5060 move_preference = move_dir_preference;
5061 new_move_dir = move_dir;
5065 MovDir[x][y] = new_move_dir;
5066 if (old_move_dir != new_move_dir)
5071 static void TurnRound(int x, int y)
5073 int direction = MovDir[x][y];
5076 GfxDir[x][y] = MovDir[x][y];
5082 GfxDir[x][y] = MovDir[x][y];
5085 if (direction != MovDir[x][y])
5090 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5093 GfxAction[x][y] = ACTION_WAITING;
5097 static boolean JustBeingPushed(int x, int y)
5101 for (i = 0; i < MAX_PLAYERS; i++)
5103 struct PlayerInfo *player = &stored_player[i];
5105 if (player->active && player->is_pushing && player->MovPos)
5107 int next_jx = player->jx + (player->jx - player->last_jx);
5108 int next_jy = player->jy + (player->jy - player->last_jy);
5110 if (x == next_jx && y == next_jy)
5118 void StartMoving(int x, int y)
5121 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5123 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5124 int element = Feld[x][y];
5130 if (MovDelay[x][y] == 0)
5131 GfxAction[x][y] = ACTION_DEFAULT;
5133 /* !!! this should be handled more generic (not only for mole) !!! */
5134 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5135 GfxAction[x][y] = ACTION_DEFAULT;
5138 if (CAN_FALL(element) && y < lev_fieldy - 1)
5140 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5141 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5142 if (JustBeingPushed(x, y))
5145 if (element == EL_QUICKSAND_FULL)
5147 if (IS_FREE(x, y + 1))
5149 InitMovingField(x, y, MV_DOWN);
5150 started_moving = TRUE;
5152 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5153 Store[x][y] = EL_ROCK;
5155 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5157 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5160 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5162 if (!MovDelay[x][y])
5163 MovDelay[x][y] = TILEY + 1;
5172 Feld[x][y] = EL_QUICKSAND_EMPTY;
5173 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5174 Store[x][y + 1] = Store[x][y];
5177 PlayLevelSoundAction(x, y, ACTION_FILLING);
5179 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5183 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5184 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5186 InitMovingField(x, y, MV_DOWN);
5187 started_moving = TRUE;
5189 Feld[x][y] = EL_QUICKSAND_FILLING;
5190 Store[x][y] = element;
5192 PlayLevelSoundAction(x, y, ACTION_FILLING);
5194 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5197 else if (element == EL_MAGIC_WALL_FULL)
5199 if (IS_FREE(x, y + 1))
5201 InitMovingField(x, y, MV_DOWN);
5202 started_moving = TRUE;
5204 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5205 Store[x][y] = EL_CHANGED(Store[x][y]);
5207 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5209 if (!MovDelay[x][y])
5210 MovDelay[x][y] = TILEY/4 + 1;
5219 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5220 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5221 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5225 else if (element == EL_BD_MAGIC_WALL_FULL)
5227 if (IS_FREE(x, y + 1))
5229 InitMovingField(x, y, MV_DOWN);
5230 started_moving = TRUE;
5232 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5233 Store[x][y] = EL_CHANGED2(Store[x][y]);
5235 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5237 if (!MovDelay[x][y])
5238 MovDelay[x][y] = TILEY/4 + 1;
5247 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5248 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5249 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5253 else if (CAN_PASS_MAGIC_WALL(element) &&
5254 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5255 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5257 InitMovingField(x, y, MV_DOWN);
5258 started_moving = TRUE;
5261 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5262 EL_BD_MAGIC_WALL_FILLING);
5263 Store[x][y] = element;
5266 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5268 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5271 SplashAcid(x, y + 1);
5273 InitMovingField(x, y, MV_DOWN);
5274 started_moving = TRUE;
5276 Store[x][y] = EL_ACID;
5278 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5279 GfxAction[x][y + 1] = ACTION_ACTIVE;
5283 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5284 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5286 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5287 CAN_SMASH(element) && WasJustFalling[x][y] &&
5288 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5290 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5291 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5292 (Feld[x][y + 1] == EL_BLOCKED)))
5296 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5297 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5298 WasJustMoving[x][y] && !Pushed[x][y + 1])
5300 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5301 WasJustMoving[x][y])
5306 /* this is needed for a special case not covered by calling "Impact()"
5307 from "ContinueMoving()": if an element moves to a tile directly below
5308 another element which was just falling on that tile (which was empty
5309 in the previous frame), the falling element above would just stop
5310 instead of smashing the element below (in previous version, the above
5311 element was just checked for "moving" instead of "falling", resulting
5312 in incorrect smashes caused by horizontal movement of the above
5313 element; also, the case of the player being the element to smash was
5314 simply not covered here... :-/ ) */
5317 WasJustMoving[x][y] = 0;
5318 WasJustFalling[x][y] = 0;
5321 CheckCollision[x][y] = 0;
5324 if (IS_PLAYER(x, y + 1))
5325 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5330 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5332 if (MovDir[x][y] == MV_NO_MOVING)
5334 InitMovingField(x, y, MV_DOWN);
5335 started_moving = TRUE;
5338 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5340 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5341 MovDir[x][y] = MV_DOWN;
5343 InitMovingField(x, y, MV_DOWN);
5344 started_moving = TRUE;
5346 else if (element == EL_AMOEBA_DROP)
5348 Feld[x][y] = EL_AMOEBA_GROWING;
5349 Store[x][y] = EL_AMOEBA_WET;
5351 /* Store[x][y + 1] must be zero, because:
5352 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5355 #if OLD_GAME_BEHAVIOUR
5356 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5358 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5359 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5360 element != EL_DX_SUPABOMB)
5363 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5364 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5365 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5366 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5369 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5370 (IS_FREE(x - 1, y + 1) ||
5371 Feld[x - 1][y + 1] == EL_ACID));
5372 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5373 (IS_FREE(x + 1, y + 1) ||
5374 Feld[x + 1][y + 1] == EL_ACID));
5375 boolean can_fall_any = (can_fall_left || can_fall_right);
5376 boolean can_fall_both = (can_fall_left && can_fall_right);
5378 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5380 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5382 if (slippery_type == SLIPPERY_ONLY_LEFT)
5383 can_fall_right = FALSE;
5384 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5385 can_fall_left = FALSE;
5386 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5387 can_fall_right = FALSE;
5388 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5389 can_fall_left = FALSE;
5391 can_fall_any = (can_fall_left || can_fall_right);
5392 can_fall_both = (can_fall_left && can_fall_right);
5397 if (can_fall_both &&
5398 (game.emulation != EMU_BOULDERDASH &&
5399 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5400 can_fall_left = !(can_fall_right = RND(2));
5402 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5403 started_moving = TRUE;
5407 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5409 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5412 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5413 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5414 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5415 int belt_dir = game.belt_dir[belt_nr];
5417 if ((belt_dir == MV_LEFT && left_is_free) ||
5418 (belt_dir == MV_RIGHT && right_is_free))
5421 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5424 InitMovingField(x, y, belt_dir);
5425 started_moving = TRUE;
5428 Pushed[x][y] = TRUE;
5429 Pushed[nextx][y] = TRUE;
5432 GfxAction[x][y] = ACTION_DEFAULT;
5436 MovDir[x][y] = 0; /* if element was moving, stop it */
5441 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5443 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5445 if (CAN_MOVE(element) && !started_moving)
5448 int move_pattern = element_info[element].move_pattern;
5453 if (MovDir[x][y] == MV_NO_MOVING)
5455 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5456 x, y, element, element_info[element].token_name);
5457 printf("StartMoving(): This should never happen!\n");
5462 Moving2Blocked(x, y, &newx, &newy);
5465 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5468 if ((element == EL_SATELLITE ||
5469 element == EL_BALLOON ||
5470 element == EL_SPRING)
5471 && JustBeingPushed(x, y))
5478 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5479 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5481 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5482 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5483 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5487 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5488 element, element_info[element].token_name,
5489 WasJustMoving[x][y],
5490 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5491 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5492 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5493 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5497 WasJustMoving[x][y] = 0;
5500 CheckCollision[x][y] = 0;
5502 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5505 if (Feld[x][y] != element) /* element has changed */
5507 element = Feld[x][y];
5508 move_pattern = element_info[element].move_pattern;
5510 if (!CAN_MOVE(element))
5514 if (Feld[x][y] != element) /* element has changed */
5522 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5523 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5525 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5527 Moving2Blocked(x, y, &newx, &newy);
5528 if (Feld[newx][newy] == EL_BLOCKED)
5529 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5535 if (FrameCounter < 1 && x == 0 && y == 29)
5536 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5539 if (!MovDelay[x][y]) /* start new movement phase */
5541 /* all objects that can change their move direction after each step
5542 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5544 if (element != EL_YAMYAM &&
5545 element != EL_DARK_YAMYAM &&
5546 element != EL_PACMAN &&
5547 !(move_pattern & MV_ANY_DIRECTION) &&
5548 move_pattern != MV_TURNING_LEFT &&
5549 move_pattern != MV_TURNING_RIGHT &&
5550 move_pattern != MV_TURNING_LEFT_RIGHT &&
5551 move_pattern != MV_TURNING_RIGHT_LEFT &&
5552 move_pattern != MV_TURNING_RANDOM)
5557 if (FrameCounter < 1 && x == 0 && y == 29)
5558 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5561 if (MovDelay[x][y] && (element == EL_BUG ||
5562 element == EL_SPACESHIP ||
5563 element == EL_SP_SNIKSNAK ||
5564 element == EL_SP_ELECTRON ||
5565 element == EL_MOLE))
5566 DrawLevelField(x, y);
5570 if (MovDelay[x][y]) /* wait some time before next movement */
5575 if (element == EL_YAMYAM)
5578 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5579 DrawLevelElementAnimation(x, y, element);
5583 if (MovDelay[x][y]) /* element still has to wait some time */
5586 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5587 ResetGfxAnimation(x, y);
5591 if (GfxAction[x][y] != ACTION_WAITING)
5592 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5594 GfxAction[x][y] = ACTION_WAITING;
5598 if (element == EL_ROBOT ||
5600 element == EL_PACMAN ||
5602 element == EL_YAMYAM ||
5603 element == EL_DARK_YAMYAM)
5606 DrawLevelElementAnimation(x, y, element);
5608 DrawLevelElementAnimationIfNeeded(x, y, element);
5610 PlayLevelSoundAction(x, y, ACTION_WAITING);
5612 else if (element == EL_SP_ELECTRON)
5613 DrawLevelElementAnimationIfNeeded(x, y, element);
5614 else if (element == EL_DRAGON)
5617 int dir = MovDir[x][y];
5618 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5619 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5620 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5621 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5622 dir == MV_UP ? IMG_FLAMES_1_UP :
5623 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5624 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5627 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5630 GfxAction[x][y] = ACTION_ATTACKING;
5632 if (IS_PLAYER(x, y))
5633 DrawPlayerField(x, y);
5635 DrawLevelField(x, y);
5637 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5639 for (i = 1; i <= 3; i++)
5641 int xx = x + i * dx;
5642 int yy = y + i * dy;
5643 int sx = SCREENX(xx);
5644 int sy = SCREENY(yy);
5645 int flame_graphic = graphic + (i - 1);
5647 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5652 int flamed = MovingOrBlocked2Element(xx, yy);
5656 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5658 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5659 RemoveMovingField(xx, yy);
5661 RemoveField(xx, yy);
5663 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5666 RemoveMovingField(xx, yy);
5670 if (ChangeDelay[xx][yy])
5671 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5672 Feld[xx][yy] == EL_BLOCKED));
5676 ChangeDelay[xx][yy] = 0;
5678 Feld[xx][yy] = EL_FLAMES;
5679 if (IN_SCR_FIELD(sx, sy))
5681 DrawLevelFieldCrumbledSand(xx, yy);
5682 DrawGraphic(sx, sy, flame_graphic, frame);
5687 if (Feld[xx][yy] == EL_FLAMES)
5688 Feld[xx][yy] = EL_EMPTY;
5689 DrawLevelField(xx, yy);
5694 if (MovDelay[x][y]) /* element still has to wait some time */
5696 PlayLevelSoundAction(x, y, ACTION_WAITING);
5702 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5703 for all other elements GfxAction will be set by InitMovingField() */
5704 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5705 GfxAction[x][y] = ACTION_MOVING;
5709 /* now make next step */
5711 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5713 if (DONT_COLLIDE_WITH(element) &&
5714 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5715 !PLAYER_ENEMY_PROTECTED(newx, newy))
5718 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5722 /* player killed by element which is deadly when colliding with */
5724 KillHero(PLAYERINFO(newx, newy));
5731 else if (CAN_MOVE_INTO_ACID(element) &&
5732 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5733 (MovDir[x][y] == MV_DOWN ||
5734 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5736 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5737 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5741 else if ((element == EL_PENGUIN ||
5742 element == EL_ROBOT ||
5743 element == EL_SATELLITE ||
5744 element == EL_BALLOON ||
5745 IS_CUSTOM_ELEMENT(element)) &&
5746 IN_LEV_FIELD(newx, newy) &&
5747 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5750 SplashAcid(newx, newy);
5751 Store[x][y] = EL_ACID;
5753 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5755 if (Feld[newx][newy] == EL_EXIT_OPEN)
5759 DrawLevelField(x, y);
5761 Feld[x][y] = EL_EMPTY;
5762 DrawLevelField(x, y);
5765 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5766 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5767 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5769 local_player->friends_still_needed--;
5770 if (!local_player->friends_still_needed &&
5771 !local_player->GameOver && AllPlayersGone)
5772 local_player->LevelSolved = local_player->GameOver = TRUE;
5776 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5778 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5779 DrawLevelField(newx, newy);
5781 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5783 else if (!IS_FREE(newx, newy))
5785 GfxAction[x][y] = ACTION_WAITING;
5787 if (IS_PLAYER(x, y))
5788 DrawPlayerField(x, y);
5790 DrawLevelField(x, y);
5795 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5797 if (IS_FOOD_PIG(Feld[newx][newy]))
5799 if (IS_MOVING(newx, newy))
5800 RemoveMovingField(newx, newy);
5803 Feld[newx][newy] = EL_EMPTY;
5804 DrawLevelField(newx, newy);
5807 PlayLevelSound(x, y, SND_PIG_DIGGING);
5809 else if (!IS_FREE(newx, newy))
5811 if (IS_PLAYER(x, y))
5812 DrawPlayerField(x, y);
5814 DrawLevelField(x, y);
5823 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5826 else if (IS_CUSTOM_ELEMENT(element) &&
5827 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5831 !IS_FREE(newx, newy)
5836 int new_element = Feld[newx][newy];
5839 printf("::: '%s' digs '%s' [%d]\n",
5840 element_info[element].token_name,
5841 element_info[Feld[newx][newy]].token_name,
5842 StorePlayer[newx][newy]);
5845 if (!IS_FREE(newx, newy))
5847 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5848 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5851 /* no element can dig solid indestructible elements */
5852 if (IS_INDESTRUCTIBLE(new_element) &&
5853 !IS_DIGGABLE(new_element) &&
5854 !IS_COLLECTIBLE(new_element))
5857 if (AmoebaNr[newx][newy] &&
5858 (new_element == EL_AMOEBA_FULL ||
5859 new_element == EL_BD_AMOEBA ||
5860 new_element == EL_AMOEBA_GROWING))
5862 AmoebaCnt[AmoebaNr[newx][newy]]--;
5863 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5866 if (IS_MOVING(newx, newy))
5867 RemoveMovingField(newx, newy);
5870 RemoveField(newx, newy);
5871 DrawLevelField(newx, newy);
5874 /* if digged element was about to explode, prevent the explosion */
5875 ExplodeField[newx][newy] = EX_TYPE_NONE;
5877 PlayLevelSoundAction(x, y, action);
5882 Store[newx][newy] = EL_EMPTY;
5883 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5884 Store[newx][newy] = element_info[element].move_leave_element;
5886 Store[newx][newy] = EL_EMPTY;
5887 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5888 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5889 Store[newx][newy] = element_info[element].move_leave_element;
5892 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5893 element_info[element].can_leave_element = TRUE;
5896 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5898 RunnerVisit[x][y] = FrameCounter;
5899 PlayerVisit[x][y] /= 8; /* expire player visit path */
5905 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5907 if (!IS_FREE(newx, newy))
5909 if (IS_PLAYER(x, y))
5910 DrawPlayerField(x, y);
5912 DrawLevelField(x, y);
5918 boolean wanna_flame = !RND(10);
5919 int dx = newx - x, dy = newy - y;
5920 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5921 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5922 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5923 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5924 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5925 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5928 IS_CLASSIC_ENEMY(element1) ||
5929 IS_CLASSIC_ENEMY(element2)) &&
5930 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5931 element1 != EL_FLAMES && element2 != EL_FLAMES)
5934 ResetGfxAnimation(x, y);
5935 GfxAction[x][y] = ACTION_ATTACKING;
5938 if (IS_PLAYER(x, y))
5939 DrawPlayerField(x, y);
5941 DrawLevelField(x, y);
5943 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5945 MovDelay[x][y] = 50;
5949 RemoveField(newx, newy);
5951 Feld[newx][newy] = EL_FLAMES;
5952 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5955 RemoveField(newx1, newy1);
5957 Feld[newx1][newy1] = EL_FLAMES;
5959 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5962 RemoveField(newx2, newy2);
5964 Feld[newx2][newy2] = EL_FLAMES;
5971 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5972 Feld[newx][newy] == EL_DIAMOND)
5974 if (IS_MOVING(newx, newy))
5975 RemoveMovingField(newx, newy);
5978 Feld[newx][newy] = EL_EMPTY;
5979 DrawLevelField(newx, newy);
5982 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5984 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5985 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5987 if (AmoebaNr[newx][newy])
5989 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5990 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5991 Feld[newx][newy] == EL_BD_AMOEBA)
5992 AmoebaCnt[AmoebaNr[newx][newy]]--;
5997 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5999 if (IS_MOVING(newx, newy))
6002 RemoveMovingField(newx, newy);
6006 Feld[newx][newy] = EL_EMPTY;
6007 DrawLevelField(newx, newy);
6010 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6012 else if ((element == EL_PACMAN || element == EL_MOLE)
6013 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6015 if (AmoebaNr[newx][newy])
6017 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6018 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6019 Feld[newx][newy] == EL_BD_AMOEBA)
6020 AmoebaCnt[AmoebaNr[newx][newy]]--;
6023 if (element == EL_MOLE)
6025 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6026 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6028 ResetGfxAnimation(x, y);
6029 GfxAction[x][y] = ACTION_DIGGING;
6030 DrawLevelField(x, y);
6032 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6034 return; /* wait for shrinking amoeba */
6036 else /* element == EL_PACMAN */
6038 Feld[newx][newy] = EL_EMPTY;
6039 DrawLevelField(newx, newy);
6040 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6043 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6044 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6045 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6047 /* wait for shrinking amoeba to completely disappear */
6050 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6052 /* object was running against a wall */
6057 if (move_pattern & MV_ANY_DIRECTION &&
6058 move_pattern == MovDir[x][y])
6060 int blocking_element =
6061 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6064 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6065 element_info[element].token_name,
6066 element_info[blocking_element].token_name,
6070 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6073 element = Feld[x][y]; /* element might have changed */
6078 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6079 DrawLevelElementAnimation(x, y, element);
6081 if (element == EL_BUG ||
6082 element == EL_SPACESHIP ||
6083 element == EL_SP_SNIKSNAK)
6084 DrawLevelField(x, y);
6085 else if (element == EL_MOLE)
6086 DrawLevelField(x, y);
6087 else if (element == EL_BD_BUTTERFLY ||
6088 element == EL_BD_FIREFLY)
6089 DrawLevelElementAnimationIfNeeded(x, y, element);
6090 else if (element == EL_SATELLITE)
6091 DrawLevelElementAnimationIfNeeded(x, y, element);
6092 else if (element == EL_SP_ELECTRON)
6093 DrawLevelElementAnimationIfNeeded(x, y, element);
6096 if (DONT_TOUCH(element))
6097 TestIfBadThingTouchesHero(x, y);
6100 PlayLevelSoundAction(x, y, ACTION_WAITING);
6106 InitMovingField(x, y, MovDir[x][y]);
6108 PlayLevelSoundAction(x, y, ACTION_MOVING);
6112 ContinueMoving(x, y);
6115 void ContinueMoving(int x, int y)
6117 int element = Feld[x][y];
6118 int stored = Store[x][y];
6119 struct ElementInfo *ei = &element_info[element];
6120 int direction = MovDir[x][y];
6121 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6122 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6123 int newx = x + dx, newy = y + dy;
6125 int nextx = newx + dx, nexty = newy + dy;
6128 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6129 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6131 boolean pushed_by_player = Pushed[x][y];
6134 MovPos[x][y] += getElementMoveStepsize(x, y);
6137 if (pushed_by_player && IS_PLAYER(x, y))
6139 /* special case: moving object pushed by player */
6140 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6143 if (pushed_by_player) /* special case: moving object pushed by player */
6144 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6147 if (ABS(MovPos[x][y]) < TILEX)
6149 DrawLevelField(x, y);
6151 return; /* element is still moving */
6154 /* element reached destination field */
6156 Feld[x][y] = EL_EMPTY;
6157 Feld[newx][newy] = element;
6158 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6161 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6163 element = Feld[newx][newy] = EL_ACID;
6166 else if (element == EL_MOLE)
6168 Feld[x][y] = EL_SAND;
6170 DrawLevelFieldCrumbledSandNeighbours(x, y);
6172 else if (element == EL_QUICKSAND_FILLING)
6174 element = Feld[newx][newy] = get_next_element(element);
6175 Store[newx][newy] = Store[x][y];
6177 else if (element == EL_QUICKSAND_EMPTYING)
6179 Feld[x][y] = get_next_element(element);
6180 element = Feld[newx][newy] = Store[x][y];
6182 else if (element == EL_MAGIC_WALL_FILLING)
6184 element = Feld[newx][newy] = get_next_element(element);
6185 if (!game.magic_wall_active)
6186 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6187 Store[newx][newy] = Store[x][y];
6189 else if (element == EL_MAGIC_WALL_EMPTYING)
6191 Feld[x][y] = get_next_element(element);
6192 if (!game.magic_wall_active)
6193 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6194 element = Feld[newx][newy] = Store[x][y];
6196 else if (element == EL_BD_MAGIC_WALL_FILLING)
6198 element = Feld[newx][newy] = get_next_element(element);
6199 if (!game.magic_wall_active)
6200 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6201 Store[newx][newy] = Store[x][y];
6203 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6205 Feld[x][y] = get_next_element(element);
6206 if (!game.magic_wall_active)
6207 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6208 element = Feld[newx][newy] = Store[x][y];
6210 else if (element == EL_AMOEBA_DROPPING)
6212 Feld[x][y] = get_next_element(element);
6213 element = Feld[newx][newy] = Store[x][y];
6215 else if (element == EL_SOKOBAN_OBJECT)
6218 Feld[x][y] = Back[x][y];
6220 if (Back[newx][newy])
6221 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6223 Back[x][y] = Back[newx][newy] = 0;
6226 else if (Store[x][y] == EL_ACID)
6228 element = Feld[newx][newy] = EL_ACID;
6232 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6233 ei->move_leave_element != EL_EMPTY &&
6234 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6235 Store[x][y] != EL_EMPTY))
6237 /* some elements can leave other elements behind after moving */
6239 Feld[x][y] = ei->move_leave_element;
6240 InitField(x, y, FALSE);
6242 if (GFX_CRUMBLED(Feld[x][y]))
6243 DrawLevelFieldCrumbledSandNeighbours(x, y);
6247 Store[x][y] = EL_EMPTY;
6248 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6249 MovDelay[newx][newy] = 0;
6251 if (CAN_CHANGE(element))
6253 /* copy element change control values to new field */
6254 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6255 ChangePage[newx][newy] = ChangePage[x][y];
6256 Changed[newx][newy] = Changed[x][y];
6257 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6260 ChangeDelay[x][y] = 0;
6261 ChangePage[x][y] = -1;
6262 Changed[x][y] = CE_BITMASK_DEFAULT;
6263 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6265 /* copy animation control values to new field */
6266 GfxFrame[newx][newy] = GfxFrame[x][y];
6267 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6268 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6269 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6271 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6273 ResetGfxAnimation(x, y); /* reset animation values for old field */
6276 /* some elements can leave other elements behind after moving */
6278 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6279 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6280 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6282 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6283 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6287 int move_leave_element = ei->move_leave_element;
6289 Feld[x][y] = move_leave_element;
6290 InitField(x, y, FALSE);
6292 if (GFX_CRUMBLED(Feld[x][y]))
6293 DrawLevelFieldCrumbledSandNeighbours(x, y);
6295 if (ELEM_IS_PLAYER(move_leave_element))
6296 RelocatePlayer(x, y, move_leave_element);
6301 /* some elements can leave other elements behind after moving */
6302 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6303 ei->move_leave_element != EL_EMPTY &&
6304 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6305 ei->can_leave_element_last))
6307 Feld[x][y] = ei->move_leave_element;
6308 InitField(x, y, FALSE);
6310 if (GFX_CRUMBLED(Feld[x][y]))
6311 DrawLevelFieldCrumbledSandNeighbours(x, y);
6314 ei->can_leave_element_last = ei->can_leave_element;
6315 ei->can_leave_element = FALSE;
6319 /* 2.1.1 (does not work correctly for spring) */
6320 if (!CAN_MOVE(element))
6321 MovDir[newx][newy] = 0;
6325 /* (does not work for falling objects that slide horizontally) */
6326 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6327 MovDir[newx][newy] = 0;
6330 if (!CAN_MOVE(element) ||
6331 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6332 MovDir[newx][newy] = 0;
6336 if (!CAN_MOVE(element) ||
6337 (CAN_FALL(element) && direction == MV_DOWN))
6338 GfxDir[x][y] = MovDir[newx][newy] = 0;
6340 if (!CAN_MOVE(element) ||
6341 (CAN_FALL(element) && direction == MV_DOWN &&
6342 (element == EL_SPRING ||
6343 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6344 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6345 GfxDir[x][y] = MovDir[newx][newy] = 0;
6351 DrawLevelField(x, y);
6352 DrawLevelField(newx, newy);
6354 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6356 /* prevent pushed element from moving on in pushed direction */
6357 if (pushed_by_player && CAN_MOVE(element) &&
6358 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6359 !(element_info[element].move_pattern & direction))
6360 TurnRound(newx, newy);
6363 /* prevent elements on conveyor belt from moving on in last direction */
6364 if (pushed_by_conveyor && CAN_FALL(element) &&
6365 direction & MV_HORIZONTAL)
6368 if (CAN_MOVE(element))
6369 InitMovDir(newx, newy);
6371 MovDir[newx][newy] = 0;
6373 MovDir[newx][newy] = 0;
6378 if (!pushed_by_player)
6380 int nextx = newx + dx, nexty = newy + dy;
6381 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6383 WasJustMoving[newx][newy] = 3;
6385 if (CAN_FALL(element) && direction == MV_DOWN)
6386 WasJustFalling[newx][newy] = 3;
6388 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6389 CheckCollision[newx][newy] = 2;
6392 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6394 TestIfBadThingTouchesHero(newx, newy);
6395 TestIfBadThingTouchesFriend(newx, newy);
6397 if (!IS_CUSTOM_ELEMENT(element))
6398 TestIfBadThingTouchesOtherBadThing(newx, newy);
6400 else if (element == EL_PENGUIN)
6401 TestIfFriendTouchesBadThing(newx, newy);
6403 #if USE_NEW_MOVEMENT
6405 if (CAN_FALL(element) && direction == MV_DOWN &&
6406 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6407 IS_PLAYER(x, newy + 1))
6408 printf("::: we would now kill the player [%d]\n", FrameCounter);
6411 /* give the player one last chance (one more frame) to move away */
6412 if (CAN_FALL(element) && direction == MV_DOWN &&
6413 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6414 !IS_PLAYER(x, newy + 1))
6417 if (CAN_FALL(element) && direction == MV_DOWN &&
6418 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6423 if (pushed_by_player)
6426 int dig_side = MV_DIR_OPPOSITE(direction);
6428 static int trigger_sides[4] =
6430 CH_SIDE_RIGHT, /* moving left */
6431 CH_SIDE_LEFT, /* moving right */
6432 CH_SIDE_BOTTOM, /* moving up */
6433 CH_SIDE_TOP, /* moving down */
6435 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6437 struct PlayerInfo *player = PLAYERINFO(x, y);
6439 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6440 player->index_bit, dig_side);
6441 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6442 player->index_bit, dig_side);
6447 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6451 if (ChangePage[newx][newy] != -1) /* delayed change */
6452 ChangeElement(newx, newy, ChangePage[newx][newy]);
6457 TestIfElementHitsCustomElement(newx, newy, direction);
6461 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6463 int hitting_element = Feld[newx][newy];
6465 /* !!! fix side (direction) orientation here and elsewhere !!! */
6466 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6470 if (IN_LEV_FIELD(nextx, nexty))
6472 int opposite_direction = MV_DIR_OPPOSITE(direction);
6473 int hitting_side = direction;
6474 int touched_side = opposite_direction;
6475 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6476 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6477 MovDir[nextx][nexty] != direction ||
6478 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6484 CheckElementChangeBySide(nextx, nexty, touched_element,
6485 CE_HIT_BY_SOMETHING, opposite_direction);
6487 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6488 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6490 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6492 struct ElementChangeInfo *change =
6493 &element_info[hitting_element].change_page[i];
6495 if (change->can_change &&
6496 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6497 change->trigger_side & touched_side &&
6498 change->trigger_element == touched_element)
6500 CheckElementChangeByPage(newx, newy, hitting_element,
6501 touched_element, CE_OTHER_IS_HITTING,i);
6507 if (IS_CUSTOM_ELEMENT(touched_element) &&
6508 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6510 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6512 struct ElementChangeInfo *change =
6513 &element_info[touched_element].change_page[i];
6515 if (change->can_change &&
6516 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6517 change->trigger_side & hitting_side &&
6518 change->trigger_element == hitting_element)
6520 CheckElementChangeByPage(nextx, nexty, touched_element,
6521 hitting_element, CE_OTHER_GETS_HIT, i);
6532 TestIfPlayerTouchesCustomElement(newx, newy);
6533 TestIfElementTouchesCustomElement(newx, newy);
6536 int AmoebeNachbarNr(int ax, int ay)
6539 int element = Feld[ax][ay];
6541 static int xy[4][2] =
6549 for (i = 0; i < NUM_DIRECTIONS; i++)
6551 int x = ax + xy[i][0];
6552 int y = ay + xy[i][1];
6554 if (!IN_LEV_FIELD(x, y))
6557 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6558 group_nr = AmoebaNr[x][y];
6564 void AmoebenVereinigen(int ax, int ay)
6566 int i, x, y, xx, yy;
6567 int new_group_nr = AmoebaNr[ax][ay];
6568 static int xy[4][2] =
6576 if (new_group_nr == 0)
6579 for (i = 0; i < NUM_DIRECTIONS; i++)
6584 if (!IN_LEV_FIELD(x, y))
6587 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6588 Feld[x][y] == EL_BD_AMOEBA ||
6589 Feld[x][y] == EL_AMOEBA_DEAD) &&
6590 AmoebaNr[x][y] != new_group_nr)
6592 int old_group_nr = AmoebaNr[x][y];
6594 if (old_group_nr == 0)
6597 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6598 AmoebaCnt[old_group_nr] = 0;
6599 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6600 AmoebaCnt2[old_group_nr] = 0;
6602 for (yy = 0; yy < lev_fieldy; yy++)
6604 for (xx = 0; xx < lev_fieldx; xx++)
6606 if (AmoebaNr[xx][yy] == old_group_nr)
6607 AmoebaNr[xx][yy] = new_group_nr;
6614 void AmoebeUmwandeln(int ax, int ay)
6618 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6620 int group_nr = AmoebaNr[ax][ay];
6625 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6626 printf("AmoebeUmwandeln(): This should never happen!\n");
6631 for (y = 0; y < lev_fieldy; y++)
6633 for (x = 0; x < lev_fieldx; x++)
6635 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6638 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6642 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6643 SND_AMOEBA_TURNING_TO_GEM :
6644 SND_AMOEBA_TURNING_TO_ROCK));
6649 static int xy[4][2] =
6657 for (i = 0; i < NUM_DIRECTIONS; i++)
6662 if (!IN_LEV_FIELD(x, y))
6665 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6667 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6668 SND_AMOEBA_TURNING_TO_GEM :
6669 SND_AMOEBA_TURNING_TO_ROCK));
6676 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6679 int group_nr = AmoebaNr[ax][ay];
6680 boolean done = FALSE;
6685 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6686 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6691 for (y = 0; y < lev_fieldy; y++)
6693 for (x = 0; x < lev_fieldx; x++)
6695 if (AmoebaNr[x][y] == group_nr &&
6696 (Feld[x][y] == EL_AMOEBA_DEAD ||
6697 Feld[x][y] == EL_BD_AMOEBA ||
6698 Feld[x][y] == EL_AMOEBA_GROWING))
6701 Feld[x][y] = new_element;
6702 InitField(x, y, FALSE);
6703 DrawLevelField(x, y);
6710 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6711 SND_BD_AMOEBA_TURNING_TO_ROCK :
6712 SND_BD_AMOEBA_TURNING_TO_GEM));
6715 void AmoebeWaechst(int x, int y)
6717 static unsigned long sound_delay = 0;
6718 static unsigned long sound_delay_value = 0;
6720 if (!MovDelay[x][y]) /* start new growing cycle */
6724 if (DelayReached(&sound_delay, sound_delay_value))
6727 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6729 if (Store[x][y] == EL_BD_AMOEBA)
6730 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6732 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6734 sound_delay_value = 30;
6738 if (MovDelay[x][y]) /* wait some time before growing bigger */
6741 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6743 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6744 6 - MovDelay[x][y]);
6746 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6749 if (!MovDelay[x][y])
6751 Feld[x][y] = Store[x][y];
6753 DrawLevelField(x, y);
6758 void AmoebaDisappearing(int x, int y)
6760 static unsigned long sound_delay = 0;
6761 static unsigned long sound_delay_value = 0;
6763 if (!MovDelay[x][y]) /* start new shrinking cycle */
6767 if (DelayReached(&sound_delay, sound_delay_value))
6768 sound_delay_value = 30;
6771 if (MovDelay[x][y]) /* wait some time before shrinking */
6774 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6776 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6777 6 - MovDelay[x][y]);
6779 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6782 if (!MovDelay[x][y])
6784 Feld[x][y] = EL_EMPTY;
6785 DrawLevelField(x, y);
6787 /* don't let mole enter this field in this cycle;
6788 (give priority to objects falling to this field from above) */
6794 void AmoebeAbleger(int ax, int ay)
6797 int element = Feld[ax][ay];
6798 int graphic = el2img(element);
6799 int newax = ax, neway = ay;
6800 static int xy[4][2] =
6808 if (!level.amoeba_speed)
6810 Feld[ax][ay] = EL_AMOEBA_DEAD;
6811 DrawLevelField(ax, ay);
6815 if (IS_ANIMATED(graphic))
6816 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6818 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6819 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6821 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6824 if (MovDelay[ax][ay])
6828 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6831 int x = ax + xy[start][0];
6832 int y = ay + xy[start][1];
6834 if (!IN_LEV_FIELD(x, y))
6838 if (IS_FREE(x, y) ||
6839 CAN_GROW_INTO(Feld[x][y]) ||
6840 Feld[x][y] == EL_QUICKSAND_EMPTY)
6846 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6847 if (IS_FREE(x, y) ||
6848 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6855 if (newax == ax && neway == ay)
6858 else /* normal or "filled" (BD style) amoeba */
6861 boolean waiting_for_player = FALSE;
6863 for (i = 0; i < NUM_DIRECTIONS; i++)
6865 int j = (start + i) % 4;
6866 int x = ax + xy[j][0];
6867 int y = ay + xy[j][1];
6869 if (!IN_LEV_FIELD(x, y))
6873 if (IS_FREE(x, y) ||
6874 CAN_GROW_INTO(Feld[x][y]) ||
6875 Feld[x][y] == EL_QUICKSAND_EMPTY)
6882 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6883 if (IS_FREE(x, y) ||
6884 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6891 else if (IS_PLAYER(x, y))
6892 waiting_for_player = TRUE;
6895 if (newax == ax && neway == ay) /* amoeba cannot grow */
6898 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6900 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6903 Feld[ax][ay] = EL_AMOEBA_DEAD;
6904 DrawLevelField(ax, ay);
6905 AmoebaCnt[AmoebaNr[ax][ay]]--;
6907 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6909 if (element == EL_AMOEBA_FULL)
6910 AmoebeUmwandeln(ax, ay);
6911 else if (element == EL_BD_AMOEBA)
6912 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6917 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6919 /* amoeba gets larger by growing in some direction */
6921 int new_group_nr = AmoebaNr[ax][ay];
6924 if (new_group_nr == 0)
6926 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6927 printf("AmoebeAbleger(): This should never happen!\n");
6932 AmoebaNr[newax][neway] = new_group_nr;
6933 AmoebaCnt[new_group_nr]++;
6934 AmoebaCnt2[new_group_nr]++;
6936 /* if amoeba touches other amoeba(s) after growing, unify them */
6937 AmoebenVereinigen(newax, neway);
6939 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6941 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6947 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6948 (neway == lev_fieldy - 1 && newax != ax))
6950 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6951 Store[newax][neway] = element;
6953 else if (neway == ay)
6955 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6957 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6959 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6964 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6965 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6966 Store[ax][ay] = EL_AMOEBA_DROP;
6967 ContinueMoving(ax, ay);
6971 DrawLevelField(newax, neway);
6974 void Life(int ax, int ay)
6977 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6979 int element = Feld[ax][ay];
6980 int graphic = el2img(element);
6981 boolean changed = FALSE;
6983 if (IS_ANIMATED(graphic))
6984 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6989 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6990 MovDelay[ax][ay] = life_time;
6992 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6995 if (MovDelay[ax][ay])
6999 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7001 int xx = ax+x1, yy = ay+y1;
7004 if (!IN_LEV_FIELD(xx, yy))
7007 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7009 int x = xx+x2, y = yy+y2;
7011 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7014 if (((Feld[x][y] == element ||
7015 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7017 (IS_FREE(x, y) && Stop[x][y]))
7021 if (xx == ax && yy == ay) /* field in the middle */
7023 if (nachbarn < life[0] || nachbarn > life[1])
7025 Feld[xx][yy] = EL_EMPTY;
7027 DrawLevelField(xx, yy);
7028 Stop[xx][yy] = TRUE;
7033 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7034 { /* free border field */
7035 if (nachbarn >= life[2] && nachbarn <= life[3])
7037 Feld[xx][yy] = element;
7038 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7040 DrawLevelField(xx, yy);
7041 Stop[xx][yy] = TRUE;
7046 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7047 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7048 { /* free border field */
7049 if (nachbarn >= life[2] && nachbarn <= life[3])
7051 Feld[xx][yy] = element;
7052 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7054 DrawLevelField(xx, yy);
7055 Stop[xx][yy] = TRUE;
7063 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7064 SND_GAME_OF_LIFE_GROWING);
7067 static void InitRobotWheel(int x, int y)
7069 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7072 static void RunRobotWheel(int x, int y)
7074 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7077 static void StopRobotWheel(int x, int y)
7079 if (ZX == x && ZY == y)
7083 static void InitTimegateWheel(int x, int y)
7086 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7088 /* another brainless, "type style" bug ... :-( */
7089 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7093 static void RunTimegateWheel(int x, int y)
7095 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7098 void CheckExit(int x, int y)
7100 if (local_player->gems_still_needed > 0 ||
7101 local_player->sokobanfields_still_needed > 0 ||
7102 local_player->lights_still_needed > 0)
7104 int element = Feld[x][y];
7105 int graphic = el2img(element);
7107 if (IS_ANIMATED(graphic))
7108 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7113 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7116 Feld[x][y] = EL_EXIT_OPENING;
7118 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7121 void CheckExitSP(int x, int y)
7123 if (local_player->gems_still_needed > 0)
7125 int element = Feld[x][y];
7126 int graphic = el2img(element);
7128 if (IS_ANIMATED(graphic))
7129 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7134 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7137 Feld[x][y] = EL_SP_EXIT_OPENING;
7139 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7142 static void CloseAllOpenTimegates()
7146 for (y = 0; y < lev_fieldy; y++)
7148 for (x = 0; x < lev_fieldx; x++)
7150 int element = Feld[x][y];
7152 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7154 Feld[x][y] = EL_TIMEGATE_CLOSING;
7156 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7158 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7165 void EdelsteinFunkeln(int x, int y)
7167 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7170 if (Feld[x][y] == EL_BD_DIAMOND)
7173 if (MovDelay[x][y] == 0) /* next animation frame */
7174 MovDelay[x][y] = 11 * !SimpleRND(500);
7176 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7180 if (setup.direct_draw && MovDelay[x][y])
7181 SetDrawtoField(DRAW_BUFFERED);
7183 DrawLevelElementAnimation(x, y, Feld[x][y]);
7185 if (MovDelay[x][y] != 0)
7187 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7188 10 - MovDelay[x][y]);
7190 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7192 if (setup.direct_draw)
7196 dest_x = FX + SCREENX(x) * TILEX;
7197 dest_y = FY + SCREENY(y) * TILEY;
7199 BlitBitmap(drawto_field, window,
7200 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7201 SetDrawtoField(DRAW_DIRECT);
7207 void MauerWaechst(int x, int y)
7211 if (!MovDelay[x][y]) /* next animation frame */
7212 MovDelay[x][y] = 3 * delay;
7214 if (MovDelay[x][y]) /* wait some time before next frame */
7218 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7220 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7221 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7223 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7226 if (!MovDelay[x][y])
7228 if (MovDir[x][y] == MV_LEFT)
7230 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7231 DrawLevelField(x - 1, y);
7233 else if (MovDir[x][y] == MV_RIGHT)
7235 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7236 DrawLevelField(x + 1, y);
7238 else if (MovDir[x][y] == MV_UP)
7240 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7241 DrawLevelField(x, y - 1);
7245 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7246 DrawLevelField(x, y + 1);
7249 Feld[x][y] = Store[x][y];
7251 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7252 DrawLevelField(x, y);
7257 void MauerAbleger(int ax, int ay)
7259 int element = Feld[ax][ay];
7260 int graphic = el2img(element);
7261 boolean oben_frei = FALSE, unten_frei = FALSE;
7262 boolean links_frei = FALSE, rechts_frei = FALSE;
7263 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7264 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7265 boolean new_wall = FALSE;
7267 if (IS_ANIMATED(graphic))
7268 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7270 if (!MovDelay[ax][ay]) /* start building new wall */
7271 MovDelay[ax][ay] = 6;
7273 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7276 if (MovDelay[ax][ay])
7280 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7282 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7284 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7286 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7289 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7290 element == EL_EXPANDABLE_WALL_ANY)
7294 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7295 Store[ax][ay-1] = element;
7296 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7297 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7298 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7299 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7304 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7305 Store[ax][ay+1] = element;
7306 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7307 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7308 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7309 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7314 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7315 element == EL_EXPANDABLE_WALL_ANY ||
7316 element == EL_EXPANDABLE_WALL)
7320 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7321 Store[ax-1][ay] = element;
7322 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7323 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7324 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7325 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7331 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7332 Store[ax+1][ay] = element;
7333 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7334 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7335 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7336 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7341 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7342 DrawLevelField(ax, ay);
7344 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7346 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7347 unten_massiv = TRUE;
7348 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7349 links_massiv = TRUE;
7350 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7351 rechts_massiv = TRUE;
7353 if (((oben_massiv && unten_massiv) ||
7354 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7355 element == EL_EXPANDABLE_WALL) &&
7356 ((links_massiv && rechts_massiv) ||
7357 element == EL_EXPANDABLE_WALL_VERTICAL))
7358 Feld[ax][ay] = EL_WALL;
7362 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7364 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7368 void CheckForDragon(int x, int y)
7371 boolean dragon_found = FALSE;
7372 static int xy[4][2] =
7380 for (i = 0; i < NUM_DIRECTIONS; i++)
7382 for (j = 0; j < 4; j++)
7384 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7386 if (IN_LEV_FIELD(xx, yy) &&
7387 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7389 if (Feld[xx][yy] == EL_DRAGON)
7390 dragon_found = TRUE;
7399 for (i = 0; i < NUM_DIRECTIONS; i++)
7401 for (j = 0; j < 3; j++)
7403 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7405 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7407 Feld[xx][yy] = EL_EMPTY;
7408 DrawLevelField(xx, yy);
7417 static void InitBuggyBase(int x, int y)
7419 int element = Feld[x][y];
7420 int activating_delay = FRAMES_PER_SECOND / 4;
7423 (element == EL_SP_BUGGY_BASE ?
7424 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7425 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7427 element == EL_SP_BUGGY_BASE_ACTIVE ?
7428 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7431 static void WarnBuggyBase(int x, int y)
7434 static int xy[4][2] =
7442 for (i = 0; i < NUM_DIRECTIONS; i++)
7444 int xx = x + xy[i][0], yy = y + xy[i][1];
7446 if (IS_PLAYER(xx, yy))
7448 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7455 static void InitTrap(int x, int y)
7457 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7460 static void ActivateTrap(int x, int y)
7462 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7465 static void ChangeActiveTrap(int x, int y)
7467 int graphic = IMG_TRAP_ACTIVE;
7469 /* if new animation frame was drawn, correct crumbled sand border */
7470 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7471 DrawLevelFieldCrumbledSand(x, y);
7474 static void ChangeElementNowExt(int x, int y, int target_element)
7476 int previous_move_direction = MovDir[x][y];
7478 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7479 IS_WALKABLE(Feld[x][y]));
7481 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7482 IS_WALKABLE(Feld[x][y]) &&
7486 /* check if element under player changes from accessible to unaccessible
7487 (needed for special case of dropping element which then changes) */
7488 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7489 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7492 printf("::: BOOOM! [%d, '%s']\n", target_element,
7493 element_info[target_element].token_name);
7505 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7506 RemoveMovingField(x, y);
7510 Feld[x][y] = target_element;
7513 Feld[x][y] = target_element;
7516 ResetGfxAnimation(x, y);
7517 ResetRandomAnimationValue(x, y);
7519 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7520 MovDir[x][y] = previous_move_direction;
7523 InitField_WithBug1(x, y, FALSE);
7525 InitField(x, y, FALSE);
7526 if (CAN_MOVE(Feld[x][y]))
7530 DrawLevelField(x, y);
7532 if (GFX_CRUMBLED(Feld[x][y]))
7533 DrawLevelFieldCrumbledSandNeighbours(x, y);
7537 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7541 TestIfBadThingTouchesHero(x, y);
7542 TestIfPlayerTouchesCustomElement(x, y);
7543 TestIfElementTouchesCustomElement(x, y);
7546 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7547 if (ELEM_IS_PLAYER(target_element))
7548 RelocatePlayer(x, y, target_element);
7551 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7555 TestIfBadThingTouchesHero(x, y);
7556 TestIfPlayerTouchesCustomElement(x, y);
7557 TestIfElementTouchesCustomElement(x, y);
7561 static boolean ChangeElementNow(int x, int y, int element, int page)
7563 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7565 int old_element = Feld[x][y];
7567 /* always use default change event to prevent running into a loop */
7568 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7569 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7571 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7573 /* reset actual trigger element and player */
7574 change->actual_trigger_element = EL_EMPTY;
7575 change->actual_trigger_player = EL_PLAYER_1;
7578 /* do not change already changed elements with same change event */
7580 if (Changed[x][y] & ChangeEvent[x][y])
7587 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7590 /* !!! indirect change before direct change !!! */
7591 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7594 if (change->explode)
7601 if (change->use_target_content)
7603 boolean complete_replace = TRUE;
7604 boolean can_replace[3][3];
7607 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7610 boolean is_walkable;
7611 boolean is_diggable;
7612 boolean is_collectible;
7613 boolean is_removable;
7614 boolean is_destructible;
7615 int ex = x + xx - 1;
7616 int ey = y + yy - 1;
7617 int content_element = change->target_content[xx][yy];
7620 can_replace[xx][yy] = TRUE;
7622 if (ex == x && ey == y) /* do not check changing element itself */
7625 if (content_element == EL_EMPTY_SPACE)
7627 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7632 if (!IN_LEV_FIELD(ex, ey))
7634 can_replace[xx][yy] = FALSE;
7635 complete_replace = FALSE;
7641 if (Changed[ex][ey]) /* do not change already changed elements */
7643 can_replace[xx][yy] = FALSE;
7644 complete_replace = FALSE;
7652 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7653 e = MovingOrBlocked2Element(ex, ey);
7658 is_empty = (IS_FREE(ex, ey) ||
7659 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7660 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7661 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7665 is_empty = (IS_FREE(ex, ey) ||
7666 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7668 is_empty = (IS_FREE(ex, ey) ||
7669 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7674 is_walkable = (is_empty || IS_WALKABLE(e));
7675 is_diggable = (is_empty || IS_DIGGABLE(e));
7676 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7677 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7678 is_removable = (is_diggable || is_collectible);
7680 can_replace[xx][yy] =
7681 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7682 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7683 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7684 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7685 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7686 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7687 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7689 if (!can_replace[xx][yy])
7690 complete_replace = FALSE;
7692 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7693 IS_WALKABLE(content_element)));
7695 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7697 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7700 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7701 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7702 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7704 can_replace[xx][yy] = FALSE;
7705 complete_replace = FALSE;
7710 if (!change->only_if_complete || complete_replace)
7712 boolean something_has_changed = FALSE;
7714 if (change->only_if_complete && change->use_random_replace &&
7715 RND(100) < change->random_percentage)
7718 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7720 int ex = x + xx - 1;
7721 int ey = y + yy - 1;
7722 int content_element;
7724 if (can_replace[xx][yy] && (!change->use_random_replace ||
7725 RND(100) < change->random_percentage))
7727 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7728 RemoveMovingField(ex, ey);
7730 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7732 content_element = change->target_content[xx][yy];
7733 target_element = GET_TARGET_ELEMENT(content_element, change);
7735 ChangeElementNowExt(ex, ey, target_element);
7737 something_has_changed = TRUE;
7739 /* for symmetry reasons, freeze newly created border elements */
7740 if (ex != x || ey != y)
7741 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7745 if (something_has_changed)
7746 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7751 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7753 ChangeElementNowExt(x, y, target_element);
7755 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7759 /* this uses direct change before indirect change */
7760 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7766 static void ChangeElement(int x, int y, int page)
7768 int element = MovingOrBlocked2Element(x, y);
7769 struct ElementInfo *ei = &element_info[element];
7770 struct ElementChangeInfo *change = &ei->change_page[page];
7773 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7776 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7777 x, y, element, element_info[element].token_name);
7778 printf("ChangeElement(): This should never happen!\n");
7783 /* this can happen with classic bombs on walkable, changing elements */
7784 if (!CAN_CHANGE(element))
7787 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7788 ChangeDelay[x][y] = 0;
7794 if (ChangeDelay[x][y] == 0) /* initialize element change */
7796 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7797 RND(change->delay_random * change->delay_frames)) + 1;
7799 ResetGfxAnimation(x, y);
7800 ResetRandomAnimationValue(x, y);
7802 if (change->pre_change_function)
7803 change->pre_change_function(x, y);
7806 ChangeDelay[x][y]--;
7808 if (ChangeDelay[x][y] != 0) /* continue element change */
7810 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7812 if (IS_ANIMATED(graphic))
7813 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7815 if (change->change_function)
7816 change->change_function(x, y);
7818 else /* finish element change */
7820 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7822 page = ChangePage[x][y];
7823 ChangePage[x][y] = -1;
7825 change = &ei->change_page[page];
7829 if (IS_MOVING(x, y) && !change->explode)
7831 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7834 ChangeDelay[x][y] = 1; /* try change after next move step */
7835 ChangePage[x][y] = page; /* remember page to use for change */
7840 if (ChangeElementNow(x, y, element, page))
7842 if (change->post_change_function)
7843 change->post_change_function(x, y);
7848 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7849 int trigger_element,
7856 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7858 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7861 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7863 int element = EL_CUSTOM_START + i;
7865 boolean change_element = FALSE;
7868 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7871 for (j = 0; j < element_info[element].num_change_pages; j++)
7873 struct ElementChangeInfo *change = &element_info[element].change_page[j];
7875 if (change->can_change &&
7876 change->events & CH_EVENT_BIT(trigger_event) &&
7877 change->trigger_side & trigger_side &&
7878 change->trigger_player & trigger_player &&
7879 change->trigger_page & trigger_page_bits &&
7880 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7883 if (!(change->events & CH_EVENT_BIT(trigger_event)))
7884 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7885 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7888 change_element = TRUE;
7891 change->actual_trigger_element = trigger_element;
7892 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7898 if (!change_element)
7901 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7904 if (x == lx && y == ly) /* do not change trigger element itself */
7908 if (Feld[x][y] == element)
7910 ChangeDelay[x][y] = 1;
7911 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7912 ChangeElement(x, y, page);
7920 static boolean CheckElementChangeExt(int x, int y,
7922 int trigger_element,
7928 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7931 if (Feld[x][y] == EL_BLOCKED)
7933 Blocked2Moving(x, y, &x, &y);
7934 element = Feld[x][y];
7938 if (Feld[x][y] != element) /* check if element has already changed */
7941 printf("::: %d ('%s') != %d ('%s') [%d]\n",
7942 Feld[x][y], element_info[Feld[x][y]].token_name,
7943 element, element_info[element].token_name,
7952 if (trigger_page < 0)
7954 boolean change_element = FALSE;
7957 for (i = 0; i < element_info[element].num_change_pages; i++)
7959 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7961 if (change->can_change &&
7962 change->events & CH_EVENT_BIT(trigger_event) &&
7963 change->trigger_side & trigger_side &&
7964 change->trigger_player & trigger_player)
7966 change_element = TRUE;
7969 change->actual_trigger_element = trigger_element;
7970 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7976 if (!change_element)
7981 struct ElementInfo *ei = &element_info[element];
7982 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7984 change->actual_trigger_element = trigger_element;
7985 change->actual_trigger_player = EL_PLAYER_1; /* unused */
7990 /* !!! this check misses pages with same event, but different side !!! */
7992 if (trigger_page < 0)
7993 trigger_page = element_info[element].event_page_nr[trigger_event];
7995 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7999 ChangeDelay[x][y] = 1;
8000 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8001 ChangeElement(x, y, trigger_page);
8006 static void PlayPlayerSound(struct PlayerInfo *player)
8008 int jx = player->jx, jy = player->jy;
8009 int element = player->element_nr;
8010 int last_action = player->last_action_waiting;
8011 int action = player->action_waiting;
8013 if (player->is_waiting)
8015 if (action != last_action)
8016 PlayLevelSoundElementAction(jx, jy, element, action);
8018 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8022 if (action != last_action)
8023 StopSound(element_info[element].sound[last_action]);
8025 if (last_action == ACTION_SLEEPING)
8026 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8030 static void PlayAllPlayersSound()
8034 for (i = 0; i < MAX_PLAYERS; i++)
8035 if (stored_player[i].active)
8036 PlayPlayerSound(&stored_player[i]);
8039 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8041 boolean last_waiting = player->is_waiting;
8042 int move_dir = player->MovDir;
8044 player->last_action_waiting = player->action_waiting;
8048 if (!last_waiting) /* not waiting -> waiting */
8050 player->is_waiting = TRUE;
8052 player->frame_counter_bored =
8054 game.player_boring_delay_fixed +
8055 SimpleRND(game.player_boring_delay_random);
8056 player->frame_counter_sleeping =
8058 game.player_sleeping_delay_fixed +
8059 SimpleRND(game.player_sleeping_delay_random);
8061 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8064 if (game.player_sleeping_delay_fixed +
8065 game.player_sleeping_delay_random > 0 &&
8066 player->anim_delay_counter == 0 &&
8067 player->post_delay_counter == 0 &&
8068 FrameCounter >= player->frame_counter_sleeping)
8069 player->is_sleeping = TRUE;
8070 else if (game.player_boring_delay_fixed +
8071 game.player_boring_delay_random > 0 &&
8072 FrameCounter >= player->frame_counter_bored)
8073 player->is_bored = TRUE;
8075 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8076 player->is_bored ? ACTION_BORING :
8079 if (player->is_sleeping)
8081 if (player->num_special_action_sleeping > 0)
8083 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8085 int last_special_action = player->special_action_sleeping;
8086 int num_special_action = player->num_special_action_sleeping;
8087 int special_action =
8088 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8089 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8090 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8091 last_special_action + 1 : ACTION_SLEEPING);
8092 int special_graphic =
8093 el_act_dir2img(player->element_nr, special_action, move_dir);
8095 player->anim_delay_counter =
8096 graphic_info[special_graphic].anim_delay_fixed +
8097 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8098 player->post_delay_counter =
8099 graphic_info[special_graphic].post_delay_fixed +
8100 SimpleRND(graphic_info[special_graphic].post_delay_random);
8102 player->special_action_sleeping = special_action;
8105 if (player->anim_delay_counter > 0)
8107 player->action_waiting = player->special_action_sleeping;
8108 player->anim_delay_counter--;
8110 else if (player->post_delay_counter > 0)
8112 player->post_delay_counter--;
8116 else if (player->is_bored)
8118 if (player->num_special_action_bored > 0)
8120 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8122 int special_action =
8123 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8124 int special_graphic =
8125 el_act_dir2img(player->element_nr, special_action, move_dir);
8127 player->anim_delay_counter =
8128 graphic_info[special_graphic].anim_delay_fixed +
8129 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8130 player->post_delay_counter =
8131 graphic_info[special_graphic].post_delay_fixed +
8132 SimpleRND(graphic_info[special_graphic].post_delay_random);
8134 player->special_action_bored = special_action;
8137 if (player->anim_delay_counter > 0)
8139 player->action_waiting = player->special_action_bored;
8140 player->anim_delay_counter--;
8142 else if (player->post_delay_counter > 0)
8144 player->post_delay_counter--;
8149 else if (last_waiting) /* waiting -> not waiting */
8151 player->is_waiting = FALSE;
8152 player->is_bored = FALSE;
8153 player->is_sleeping = FALSE;
8155 player->frame_counter_bored = -1;
8156 player->frame_counter_sleeping = -1;
8158 player->anim_delay_counter = 0;
8159 player->post_delay_counter = 0;
8161 player->action_waiting = ACTION_DEFAULT;
8163 player->special_action_bored = ACTION_DEFAULT;
8164 player->special_action_sleeping = ACTION_DEFAULT;
8169 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8172 static byte stored_player_action[MAX_PLAYERS];
8173 static int num_stored_actions = 0;
8175 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8176 int left = player_action & JOY_LEFT;
8177 int right = player_action & JOY_RIGHT;
8178 int up = player_action & JOY_UP;
8179 int down = player_action & JOY_DOWN;
8180 int button1 = player_action & JOY_BUTTON_1;
8181 int button2 = player_action & JOY_BUTTON_2;
8182 int dx = (left ? -1 : right ? 1 : 0);
8183 int dy = (up ? -1 : down ? 1 : 0);
8186 stored_player_action[player->index_nr] = 0;
8187 num_stored_actions++;
8191 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8194 if (!player->active || tape.pausing)
8198 printf("::: [%d %d %d %d] [%d %d]\n",
8199 left, right, up, down, button1, button2);
8205 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8210 if (player->MovPos == 0)
8211 CheckGravityMovement(player);
8214 snapped = SnapField(player, dx, dy);
8218 dropped = DropElement(player);
8220 moved = MovePlayer(player, dx, dy);
8223 if (tape.single_step && tape.recording && !tape.pausing)
8225 if (button1 || (dropped && !moved))
8227 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8228 SnapField(player, 0, 0); /* stop snapping */
8232 SetPlayerWaiting(player, FALSE);
8235 return player_action;
8237 stored_player_action[player->index_nr] = player_action;
8243 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8246 /* no actions for this player (no input at player's configured device) */
8248 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8249 SnapField(player, 0, 0);
8250 CheckGravityMovementWhenNotMoving(player);
8252 if (player->MovPos == 0)
8253 SetPlayerWaiting(player, TRUE);
8255 if (player->MovPos == 0) /* needed for tape.playing */
8256 player->is_moving = FALSE;
8258 player->is_dropping = FALSE;
8264 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8266 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8268 TapeRecordAction(stored_player_action);
8269 num_stored_actions = 0;
8276 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8278 static byte stored_player_action[MAX_PLAYERS];
8279 static int num_stored_actions = 0;
8280 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8281 int left = player_action & JOY_LEFT;
8282 int right = player_action & JOY_RIGHT;
8283 int up = player_action & JOY_UP;
8284 int down = player_action & JOY_DOWN;
8285 int button1 = player_action & JOY_BUTTON_1;
8286 int button2 = player_action & JOY_BUTTON_2;
8287 int dx = (left ? -1 : right ? 1 : 0);
8288 int dy = (up ? -1 : down ? 1 : 0);
8290 stored_player_action[player->index_nr] = 0;
8291 num_stored_actions++;
8293 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8295 if (!player->active || tape.pausing)
8300 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8303 snapped = SnapField(player, dx, dy);
8307 dropped = DropElement(player);
8309 moved = MovePlayer(player, dx, dy);
8312 if (tape.single_step && tape.recording && !tape.pausing)
8314 if (button1 || (dropped && !moved))
8316 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8317 SnapField(player, 0, 0); /* stop snapping */
8321 stored_player_action[player->index_nr] = player_action;
8325 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8327 /* no actions for this player (no input at player's configured device) */
8329 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8330 SnapField(player, 0, 0);
8331 CheckGravityMovementWhenNotMoving(player);
8333 if (player->MovPos == 0)
8334 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8336 if (player->MovPos == 0) /* needed for tape.playing */
8337 player->is_moving = FALSE;
8340 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8342 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8344 TapeRecordAction(stored_player_action);
8345 num_stored_actions = 0;
8350 void AdvanceFrameAndPlayerCounters(int player_nr)
8354 /* advance frame counters (global frame counter and time frame counter) */
8358 /* advance player counters (counters for move delay, move animation etc.) */
8359 for (i = 0; i < MAX_PLAYERS; i++)
8361 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8363 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8365 if (!advance_player_counters) /* not all players may be affected */
8368 stored_player[i].Frame += move_frames;
8370 if (stored_player[i].MovPos != 0)
8371 stored_player[i].StepFrame += move_frames;
8373 #if USE_NEW_MOVE_DELAY
8374 if (stored_player[i].move_delay > 0)
8375 stored_player[i].move_delay--;
8378 #if USE_NEW_PUSH_DELAY
8379 /* due to bugs in previous versions, counter must count up, not down */
8380 if (stored_player[i].push_delay != -1)
8381 stored_player[i].push_delay++;
8384 if (stored_player[i].drop_delay > 0)
8385 stored_player[i].drop_delay--;
8391 static unsigned long action_delay = 0;
8392 unsigned long action_delay_value;
8393 int magic_wall_x = 0, magic_wall_y = 0;
8394 int i, x, y, element, graphic;
8395 byte *recorded_player_action;
8396 byte summarized_player_action = 0;
8398 byte tape_action[MAX_PLAYERS];
8401 if (game_status != GAME_MODE_PLAYING)
8404 action_delay_value =
8405 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8407 if (tape.playing && tape.warp_forward && !tape.pausing)
8408 action_delay_value = 0;
8410 /* ---------- main game synchronization point ---------- */
8412 WaitUntilDelayReached(&action_delay, action_delay_value);
8414 if (network_playing && !network_player_action_received)
8418 printf("DEBUG: try to get network player actions in time\n");
8422 #if defined(NETWORK_AVALIABLE)
8423 /* last chance to get network player actions without main loop delay */
8427 if (game_status != GAME_MODE_PLAYING)
8430 if (!network_player_action_received)
8434 printf("DEBUG: failed to get network player actions in time\n");
8445 printf("::: getting new tape action [%d]\n", FrameCounter);
8448 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8451 if (recorded_player_action == NULL && tape.pausing)
8456 printf("::: %d\n", stored_player[0].action);
8460 if (recorded_player_action != NULL)
8461 for (i = 0; i < MAX_PLAYERS; i++)
8462 stored_player[i].action = recorded_player_action[i];
8465 for (i = 0; i < MAX_PLAYERS; i++)
8467 summarized_player_action |= stored_player[i].action;
8469 if (!network_playing)
8470 stored_player[i].effective_action = stored_player[i].action;
8473 #if defined(NETWORK_AVALIABLE)
8474 if (network_playing)
8475 SendToServer_MovePlayer(summarized_player_action);
8478 if (!options.network && !setup.team_mode)
8479 local_player->effective_action = summarized_player_action;
8482 if (recorded_player_action != NULL)
8483 for (i = 0; i < MAX_PLAYERS; i++)
8484 stored_player[i].effective_action = recorded_player_action[i];
8488 for (i = 0; i < MAX_PLAYERS; i++)
8490 tape_action[i] = stored_player[i].effective_action;
8492 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8493 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8496 /* only save actions from input devices, but not programmed actions */
8498 TapeRecordAction(tape_action);
8501 for (i = 0; i < MAX_PLAYERS; i++)
8503 int actual_player_action = stored_player[i].effective_action;
8506 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8507 - rnd_equinox_tetrachloride 048
8508 - rnd_equinox_tetrachloride_ii 096
8509 - rnd_emanuel_schmieg 002
8510 - doctor_sloan_ww 001, 020
8512 if (stored_player[i].MovPos == 0)
8513 CheckGravityMovement(&stored_player[i]);
8517 /* overwrite programmed action with tape action */
8518 if (stored_player[i].programmed_action)
8519 actual_player_action = stored_player[i].programmed_action;
8523 if (stored_player[i].programmed_action)
8524 printf("::: %d\n", stored_player[i].programmed_action);
8527 if (recorded_player_action)
8530 if (stored_player[i].programmed_action &&
8531 stored_player[i].programmed_action != recorded_player_action[i])
8532 printf("::: %d: %d <-> %d\n", i,
8533 stored_player[i].programmed_action, recorded_player_action[i]);
8537 actual_player_action = recorded_player_action[i];
8542 /* overwrite tape action with programmed action */
8543 if (stored_player[i].programmed_action)
8544 actual_player_action = stored_player[i].programmed_action;
8549 printf("::: action: %d: %x [%d]\n",
8550 stored_player[i].MovPos, actual_player_action, FrameCounter);
8554 PlayerActions(&stored_player[i], actual_player_action);
8556 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8558 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8559 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8562 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8567 TapeRecordAction(tape_action);
8570 network_player_action_received = FALSE;
8572 ScrollScreen(NULL, SCROLL_GO_ON);
8578 for (i = 0; i < MAX_PLAYERS; i++)
8579 stored_player[i].Frame++;
8583 /* for downwards compatibility, the following code emulates a fixed bug that
8584 occured when pushing elements (causing elements that just made their last
8585 pushing step to already (if possible) make their first falling step in the
8586 same game frame, which is bad); this code is also needed to use the famous
8587 "spring push bug" which is used in older levels and might be wanted to be
8588 used also in newer levels, but in this case the buggy pushing code is only
8589 affecting the "spring" element and no other elements */
8592 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8594 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8597 for (i = 0; i < MAX_PLAYERS; i++)
8599 struct PlayerInfo *player = &stored_player[i];
8604 if (player->active && player->is_pushing && player->is_moving &&
8606 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8607 Feld[x][y] == EL_SPRING))
8609 if (player->active && player->is_pushing && player->is_moving &&
8613 ContinueMoving(x, y);
8615 /* continue moving after pushing (this is actually a bug) */
8616 if (!IS_MOVING(x, y))
8625 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8627 Changed[x][y] = CE_BITMASK_DEFAULT;
8628 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8631 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8633 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8634 printf("GameActions(): This should never happen!\n");
8636 ChangePage[x][y] = -1;
8641 if (WasJustMoving[x][y] > 0)
8642 WasJustMoving[x][y]--;
8643 if (WasJustFalling[x][y] > 0)
8644 WasJustFalling[x][y]--;
8645 if (CheckCollision[x][y] > 0)
8646 CheckCollision[x][y]--;
8651 /* reset finished pushing action (not done in ContinueMoving() to allow
8652 continous pushing animation for elements with zero push delay) */
8653 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8655 ResetGfxAnimation(x, y);
8656 DrawLevelField(x, y);
8661 if (IS_BLOCKED(x, y))
8665 Blocked2Moving(x, y, &oldx, &oldy);
8666 if (!IS_MOVING(oldx, oldy))
8668 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8669 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8670 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8671 printf("GameActions(): This should never happen!\n");
8677 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8679 element = Feld[x][y];
8681 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8683 graphic = el2img(element);
8689 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8691 element = graphic = 0;
8695 if (graphic_info[graphic].anim_global_sync)
8696 GfxFrame[x][y] = FrameCounter;
8698 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8699 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8700 ResetRandomAnimationValue(x, y);
8702 SetRandomAnimationValue(x, y);
8705 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8708 if (IS_INACTIVE(element))
8710 if (IS_ANIMATED(graphic))
8711 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8717 /* this may take place after moving, so 'element' may have changed */
8719 if (IS_CHANGING(x, y))
8721 if (IS_CHANGING(x, y) &&
8722 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8726 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8727 element_info[element].event_page_nr[CE_DELAY]);
8729 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8732 element = Feld[x][y];
8733 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8737 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8742 element = Feld[x][y];
8743 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8745 if (element == EL_MOLE)
8746 printf("::: %d, %d, %d [%d]\n",
8747 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8751 if (element == EL_YAMYAM)
8752 printf("::: %d, %d, %d\n",
8753 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8757 if (IS_ANIMATED(graphic) &&
8761 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8764 if (element == EL_BUG)
8765 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8769 if (element == EL_MOLE)
8770 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8774 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8775 EdelsteinFunkeln(x, y);
8777 else if ((element == EL_ACID ||
8778 element == EL_EXIT_OPEN ||
8779 element == EL_SP_EXIT_OPEN ||
8780 element == EL_SP_TERMINAL ||
8781 element == EL_SP_TERMINAL_ACTIVE ||
8782 element == EL_EXTRA_TIME ||
8783 element == EL_SHIELD_NORMAL ||
8784 element == EL_SHIELD_DEADLY) &&
8785 IS_ANIMATED(graphic))
8786 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8787 else if (IS_MOVING(x, y))
8788 ContinueMoving(x, y);
8789 else if (IS_ACTIVE_BOMB(element))
8790 CheckDynamite(x, y);
8792 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8793 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8795 else if (element == EL_AMOEBA_GROWING)
8796 AmoebeWaechst(x, y);
8797 else if (element == EL_AMOEBA_SHRINKING)
8798 AmoebaDisappearing(x, y);
8800 #if !USE_NEW_AMOEBA_CODE
8801 else if (IS_AMOEBALIVE(element))
8802 AmoebeAbleger(x, y);
8805 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8807 else if (element == EL_EXIT_CLOSED)
8809 else if (element == EL_SP_EXIT_CLOSED)
8811 else if (element == EL_EXPANDABLE_WALL_GROWING)
8813 else if (element == EL_EXPANDABLE_WALL ||
8814 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8815 element == EL_EXPANDABLE_WALL_VERTICAL ||
8816 element == EL_EXPANDABLE_WALL_ANY)
8818 else if (element == EL_FLAMES)
8819 CheckForDragon(x, y);
8821 else if (IS_AUTO_CHANGING(element))
8822 ChangeElement(x, y);
8824 else if (element == EL_EXPLOSION)
8825 ; /* drawing of correct explosion animation is handled separately */
8826 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8827 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8830 /* this may take place after moving, so 'element' may have changed */
8831 if (IS_AUTO_CHANGING(Feld[x][y]))
8832 ChangeElement(x, y);
8835 if (IS_BELT_ACTIVE(element))
8836 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8838 if (game.magic_wall_active)
8840 int jx = local_player->jx, jy = local_player->jy;
8842 /* play the element sound at the position nearest to the player */
8843 if ((element == EL_MAGIC_WALL_FULL ||
8844 element == EL_MAGIC_WALL_ACTIVE ||
8845 element == EL_MAGIC_WALL_EMPTYING ||
8846 element == EL_BD_MAGIC_WALL_FULL ||
8847 element == EL_BD_MAGIC_WALL_ACTIVE ||
8848 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8849 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8857 #if USE_NEW_AMOEBA_CODE
8858 /* new experimental amoeba growth stuff */
8860 if (!(FrameCounter % 8))
8863 static unsigned long random = 1684108901;
8865 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8868 x = (random >> 10) % lev_fieldx;
8869 y = (random >> 20) % lev_fieldy;
8871 x = RND(lev_fieldx);
8872 y = RND(lev_fieldy);
8874 element = Feld[x][y];
8877 if (!IS_PLAYER(x,y) &&
8878 (element == EL_EMPTY ||
8879 CAN_GROW_INTO(element) ||
8880 element == EL_QUICKSAND_EMPTY ||
8881 element == EL_ACID_SPLASH_LEFT ||
8882 element == EL_ACID_SPLASH_RIGHT))
8884 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8885 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8886 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8887 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8888 Feld[x][y] = EL_AMOEBA_DROP;
8891 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8892 if (!IS_PLAYER(x,y) &&
8893 (element == EL_EMPTY ||
8894 element == EL_SAND ||
8895 element == EL_QUICKSAND_EMPTY ||
8896 element == EL_ACID_SPLASH_LEFT ||
8897 element == EL_ACID_SPLASH_RIGHT))
8899 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8900 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8901 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8902 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8903 Feld[x][y] = EL_AMOEBA_DROP;
8907 random = random * 129 + 1;
8913 if (game.explosions_delayed)
8916 game.explosions_delayed = FALSE;
8918 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8920 element = Feld[x][y];
8922 if (ExplodeField[x][y])
8923 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8924 else if (element == EL_EXPLOSION)
8925 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8927 ExplodeField[x][y] = EX_TYPE_NONE;
8930 game.explosions_delayed = TRUE;
8933 if (game.magic_wall_active)
8935 if (!(game.magic_wall_time_left % 4))
8937 int element = Feld[magic_wall_x][magic_wall_y];
8939 if (element == EL_BD_MAGIC_WALL_FULL ||
8940 element == EL_BD_MAGIC_WALL_ACTIVE ||
8941 element == EL_BD_MAGIC_WALL_EMPTYING)
8942 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8944 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8947 if (game.magic_wall_time_left > 0)
8949 game.magic_wall_time_left--;
8950 if (!game.magic_wall_time_left)
8952 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8954 element = Feld[x][y];
8956 if (element == EL_MAGIC_WALL_ACTIVE ||
8957 element == EL_MAGIC_WALL_FULL)
8959 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8960 DrawLevelField(x, y);
8962 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8963 element == EL_BD_MAGIC_WALL_FULL)
8965 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8966 DrawLevelField(x, y);
8970 game.magic_wall_active = FALSE;
8975 if (game.light_time_left > 0)
8977 game.light_time_left--;
8979 if (game.light_time_left == 0)
8980 RedrawAllLightSwitchesAndInvisibleElements();
8983 if (game.timegate_time_left > 0)
8985 game.timegate_time_left--;
8987 if (game.timegate_time_left == 0)
8988 CloseAllOpenTimegates();
8991 for (i = 0; i < MAX_PLAYERS; i++)
8993 struct PlayerInfo *player = &stored_player[i];
8995 if (SHIELD_ON(player))
8997 if (player->shield_deadly_time_left)
8998 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8999 else if (player->shield_normal_time_left)
9000 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9004 if (TimeFrames >= FRAMES_PER_SECOND)
9009 for (i = 0; i < MAX_PLAYERS; i++)
9011 struct PlayerInfo *player = &stored_player[i];
9013 if (SHIELD_ON(player))
9015 player->shield_normal_time_left--;
9017 if (player->shield_deadly_time_left > 0)
9018 player->shield_deadly_time_left--;
9022 if (!level.use_step_counter)
9030 if (TimeLeft <= 10 && setup.time_limit)
9031 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9033 DrawGameValue_Time(TimeLeft);
9035 if (!TimeLeft && setup.time_limit)
9036 for (i = 0; i < MAX_PLAYERS; i++)
9037 KillHero(&stored_player[i]);
9039 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9040 DrawGameValue_Time(TimePlayed);
9043 if (tape.recording || tape.playing)
9044 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9048 PlayAllPlayersSound();
9050 if (options.debug) /* calculate frames per second */
9052 static unsigned long fps_counter = 0;
9053 static int fps_frames = 0;
9054 unsigned long fps_delay_ms = Counter() - fps_counter;
9058 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9060 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9063 fps_counter = Counter();
9066 redraw_mask |= REDRAW_FPS;
9070 if (stored_player[0].jx != stored_player[0].last_jx ||
9071 stored_player[0].jy != stored_player[0].last_jy)
9072 printf("::: %d, %d, %d, %d, %d\n",
9073 stored_player[0].MovDir,
9074 stored_player[0].MovPos,
9075 stored_player[0].GfxPos,
9076 stored_player[0].Frame,
9077 stored_player[0].StepFrame);
9080 #if USE_NEW_MOVE_DELAY
9081 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9086 for (i = 0; i < MAX_PLAYERS; i++)
9089 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9091 stored_player[i].Frame += move_frames;
9093 if (stored_player[i].MovPos != 0)
9094 stored_player[i].StepFrame += move_frames;
9096 #if USE_NEW_MOVE_DELAY
9097 if (stored_player[i].move_delay > 0)
9098 stored_player[i].move_delay--;
9101 if (stored_player[i].drop_delay > 0)
9102 stored_player[i].drop_delay--;
9107 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9109 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9111 local_player->show_envelope = 0;
9116 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9118 int min_x = x, min_y = y, max_x = x, max_y = y;
9121 for (i = 0; i < MAX_PLAYERS; i++)
9123 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9125 if (!stored_player[i].active || &stored_player[i] == player)
9128 min_x = MIN(min_x, jx);
9129 min_y = MIN(min_y, jy);
9130 max_x = MAX(max_x, jx);
9131 max_y = MAX(max_y, jy);
9134 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9137 static boolean AllPlayersInVisibleScreen()
9141 for (i = 0; i < MAX_PLAYERS; i++)
9143 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9145 if (!stored_player[i].active)
9148 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9155 void ScrollLevel(int dx, int dy)
9157 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9160 BlitBitmap(drawto_field, drawto_field,
9161 FX + TILEX * (dx == -1) - softscroll_offset,
9162 FY + TILEY * (dy == -1) - softscroll_offset,
9163 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9164 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9165 FX + TILEX * (dx == 1) - softscroll_offset,
9166 FY + TILEY * (dy == 1) - softscroll_offset);
9170 x = (dx == 1 ? BX1 : BX2);
9171 for (y = BY1; y <= BY2; y++)
9172 DrawScreenField(x, y);
9177 y = (dy == 1 ? BY1 : BY2);
9178 for (x = BX1; x <= BX2; x++)
9179 DrawScreenField(x, y);
9182 redraw_mask |= REDRAW_FIELD;
9186 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9188 int nextx = x + dx, nexty = y + dy;
9189 int element = Feld[x][y];
9192 element != EL_SP_PORT_LEFT &&
9193 element != EL_SP_GRAVITY_PORT_LEFT &&
9194 element != EL_SP_PORT_HORIZONTAL &&
9195 element != EL_SP_PORT_ANY) ||
9197 element != EL_SP_PORT_RIGHT &&
9198 element != EL_SP_GRAVITY_PORT_RIGHT &&
9199 element != EL_SP_PORT_HORIZONTAL &&
9200 element != EL_SP_PORT_ANY) ||
9202 element != EL_SP_PORT_UP &&
9203 element != EL_SP_GRAVITY_PORT_UP &&
9204 element != EL_SP_PORT_VERTICAL &&
9205 element != EL_SP_PORT_ANY) ||
9207 element != EL_SP_PORT_DOWN &&
9208 element != EL_SP_GRAVITY_PORT_DOWN &&
9209 element != EL_SP_PORT_VERTICAL &&
9210 element != EL_SP_PORT_ANY) ||
9211 !IN_LEV_FIELD(nextx, nexty) ||
9212 !IS_FREE(nextx, nexty))
9219 static boolean canFallDown(struct PlayerInfo *player)
9221 int jx = player->jx, jy = player->jy;
9223 return (IN_LEV_FIELD(jx, jy + 1) &&
9224 (IS_FREE(jx, jy + 1) ||
9225 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9226 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9227 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9230 static boolean canPassField(int x, int y, int move_dir)
9232 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9233 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9234 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9237 int element = Feld[x][y];
9239 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9240 !CAN_MOVE(element) &&
9241 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9242 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9243 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9246 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9248 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9249 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9250 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9254 int nextx = newx + dx;
9255 int nexty = newy + dy;
9259 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9260 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9262 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9264 (IS_DIGGABLE(Feld[newx][newy]) ||
9265 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9266 canPassField(newx, newy, move_dir)));
9269 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9270 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9271 (IS_DIGGABLE(Feld[newx][newy]) ||
9272 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9273 canPassField(newx, newy, move_dir)));
9276 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9277 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9278 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9279 canPassField(newx, newy, move_dir)));
9281 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9282 (IS_DIGGABLE(Feld[newx][newy]) ||
9283 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9284 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9285 !CAN_MOVE(Feld[newx][newy]) &&
9286 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9287 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9288 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9294 static void CheckGravityMovement(struct PlayerInfo *player)
9296 if (game.gravity && !player->programmed_action)
9299 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9300 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9302 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9303 int move_dir_vertical = player->action & MV_VERTICAL;
9307 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9309 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9312 int jx = player->jx, jy = player->jy;
9314 boolean player_is_moving_to_valid_field =
9315 (!player_is_snapping &&
9316 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9317 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9321 (player->last_move_dir & MV_HORIZONTAL ?
9322 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9323 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9327 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9328 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9329 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9330 int new_jx = jx + dx, new_jy = jy + dy;
9331 int nextx = new_jx + dx, nexty = new_jy + dy;
9337 boolean player_can_fall_down = canFallDown(player);
9339 boolean player_can_fall_down =
9340 (IN_LEV_FIELD(jx, jy + 1) &&
9341 (IS_FREE(jx, jy + 1) ||
9342 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9346 boolean player_can_fall_down =
9347 (IN_LEV_FIELD(jx, jy + 1) &&
9348 (IS_FREE(jx, jy + 1)));
9352 boolean player_is_moving_to_valid_field =
9355 !player_is_snapping &&
9359 IN_LEV_FIELD(new_jx, new_jy) &&
9360 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9361 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9362 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9363 IN_LEV_FIELD(nextx, nexty) &&
9364 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9366 IN_LEV_FIELD(new_jx, new_jy) &&
9367 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9368 Feld[new_jx][new_jy] == EL_SAND ||
9369 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9370 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9371 /* !!! extend EL_SAND to anything diggable !!! */
9377 boolean player_is_standing_on_valid_field =
9378 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9379 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9383 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9384 player_can_fall_down,
9385 player_is_standing_on_valid_field,
9386 player_is_moving_to_valid_field,
9387 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9388 player->effective_action,
9389 player->can_fall_into_acid);
9392 if (player_can_fall_down &&
9394 !player_is_standing_on_valid_field &&
9396 !player_is_moving_to_valid_field)
9399 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9400 jx, jy, FrameCounter);
9403 player->programmed_action = MV_DOWN;
9408 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9411 return CheckGravityMovement(player);
9414 if (game.gravity && !player->programmed_action)
9416 int jx = player->jx, jy = player->jy;
9417 boolean field_under_player_is_free =
9418 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9419 boolean player_is_standing_on_valid_field =
9420 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9421 (IS_WALKABLE(Feld[jx][jy]) &&
9422 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9424 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9425 player->programmed_action = MV_DOWN;
9431 -----------------------------------------------------------------------------
9432 dx, dy: direction (non-diagonal) to try to move the player to
9433 real_dx, real_dy: direction as read from input device (can be diagonal)
9436 boolean MovePlayerOneStep(struct PlayerInfo *player,
9437 int dx, int dy, int real_dx, int real_dy)
9440 static int trigger_sides[4][2] =
9442 /* enter side leave side */
9443 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9444 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9445 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9446 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9448 int move_direction = (dx == -1 ? MV_LEFT :
9449 dx == +1 ? MV_RIGHT :
9451 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9452 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9453 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9455 int jx = player->jx, jy = player->jy;
9456 int new_jx = jx + dx, new_jy = jy + dy;
9460 if (!player->active || (!dx && !dy))
9461 return MF_NO_ACTION;
9463 player->MovDir = (dx < 0 ? MV_LEFT :
9466 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9468 if (!IN_LEV_FIELD(new_jx, new_jy))
9469 return MF_NO_ACTION;
9471 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9472 return MF_NO_ACTION;
9475 element = MovingOrBlocked2Element(new_jx, new_jy);
9477 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9480 if (DONT_RUN_INTO(element))
9482 if (element == EL_ACID && dx == 0 && dy == 1)
9484 SplashAcid(new_jx, new_jy);
9485 Feld[jx][jy] = EL_PLAYER_1;
9486 InitMovingField(jx, jy, MV_DOWN);
9487 Store[jx][jy] = EL_ACID;
9488 ContinueMoving(jx, jy);
9492 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9497 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9498 if (can_move != MF_MOVING)
9501 /* check if DigField() has caused relocation of the player */
9502 if (player->jx != jx || player->jy != jy)
9503 return MF_NO_ACTION;
9505 StorePlayer[jx][jy] = 0;
9506 player->last_jx = jx;
9507 player->last_jy = jy;
9508 player->jx = new_jx;
9509 player->jy = new_jy;
9510 StorePlayer[new_jx][new_jy] = player->element_nr;
9513 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9515 player->step_counter++;
9518 player->drop_delay = 0;
9521 PlayerVisit[jx][jy] = FrameCounter;
9523 ScrollPlayer(player, SCROLL_INIT);
9526 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9528 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9530 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9533 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9535 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9536 CE_OTHER_GETS_ENTERED, enter_side);
9537 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9538 CE_ENTERED_BY_PLAYER, enter_side);
9545 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9547 int jx = player->jx, jy = player->jy;
9548 int old_jx = jx, old_jy = jy;
9549 int moved = MF_NO_ACTION;
9552 if (!player->active)
9557 if (player->MovPos == 0)
9559 player->is_moving = FALSE;
9560 player->is_digging = FALSE;
9561 player->is_collecting = FALSE;
9562 player->is_snapping = FALSE;
9563 player->is_pushing = FALSE;
9569 if (!player->active || (!dx && !dy))
9574 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9582 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9583 player->move_delay + player->move_delay_value);
9586 #if USE_NEW_MOVE_DELAY
9587 if (player->move_delay > 0)
9589 if (!FrameReached(&player->move_delay, player->move_delay_value))
9593 printf("::: can NOT move\n");
9599 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9600 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9607 printf("::: COULD move now\n");
9610 #if USE_NEW_MOVE_DELAY
9611 player->move_delay = -1; /* set to "uninitialized" value */
9614 /* store if player is automatically moved to next field */
9615 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9617 /* remove the last programmed player action */
9618 player->programmed_action = 0;
9622 /* should only happen if pre-1.2 tape recordings are played */
9623 /* this is only for backward compatibility */
9625 int original_move_delay_value = player->move_delay_value;
9628 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9632 /* scroll remaining steps with finest movement resolution */
9633 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9635 while (player->MovPos)
9637 ScrollPlayer(player, SCROLL_GO_ON);
9638 ScrollScreen(NULL, SCROLL_GO_ON);
9640 #if USE_NEW_MOVE_DELAY
9641 AdvanceFrameAndPlayerCounters(player->index_nr);
9650 player->move_delay_value = original_move_delay_value;
9653 if (player->last_move_dir & MV_HORIZONTAL)
9655 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9656 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9660 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9661 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9667 if (moved & MF_MOVING && !ScreenMovPos &&
9668 (player == local_player || !options.network))
9670 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9671 int offset = (setup.scroll_delay ? 3 : 0);
9673 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9675 /* actual player has left the screen -- scroll in that direction */
9676 if (jx != old_jx) /* player has moved horizontally */
9677 scroll_x += (jx - old_jx);
9678 else /* player has moved vertically */
9679 scroll_y += (jy - old_jy);
9683 if (jx != old_jx) /* player has moved horizontally */
9685 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9686 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9687 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9689 /* don't scroll over playfield boundaries */
9690 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9691 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9693 /* don't scroll more than one field at a time */
9694 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9696 /* don't scroll against the player's moving direction */
9697 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9698 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9699 scroll_x = old_scroll_x;
9701 else /* player has moved vertically */
9703 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9704 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9705 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9707 /* don't scroll over playfield boundaries */
9708 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9709 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9711 /* don't scroll more than one field at a time */
9712 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9714 /* don't scroll against the player's moving direction */
9715 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9716 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9717 scroll_y = old_scroll_y;
9721 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9723 if (!options.network && !AllPlayersInVisibleScreen())
9725 scroll_x = old_scroll_x;
9726 scroll_y = old_scroll_y;
9730 ScrollScreen(player, SCROLL_INIT);
9731 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9738 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9740 if (!(moved & MF_MOVING) && !player->is_pushing)
9745 player->StepFrame = 0;
9747 if (moved & MF_MOVING)
9750 printf("::: REALLY moves now\n");
9753 if (old_jx != jx && old_jy == jy)
9754 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9755 else if (old_jx == jx && old_jy != jy)
9756 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9758 DrawLevelField(jx, jy); /* for "crumbled sand" */
9760 player->last_move_dir = player->MovDir;
9761 player->is_moving = TRUE;
9763 player->is_snapping = FALSE;
9767 player->is_switching = FALSE;
9770 player->is_dropping = FALSE;
9774 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9777 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9780 int move_direction = player->MovDir;
9782 int enter_side = MV_DIR_OPPOSITE(move_direction);
9783 int leave_side = move_direction;
9785 static int trigger_sides[4][2] =
9787 /* enter side leave side */
9788 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9789 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9790 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9791 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9793 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9794 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9796 int old_element = Feld[old_jx][old_jy];
9797 int new_element = Feld[jx][jy];
9800 /* !!! TEST ONLY !!! */
9801 if (IS_CUSTOM_ELEMENT(old_element))
9802 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9804 player->index_bit, leave_side);
9806 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9808 player->index_bit, leave_side);
9810 if (IS_CUSTOM_ELEMENT(new_element))
9811 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9812 player->index_bit, enter_side);
9814 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9815 CE_OTHER_GETS_ENTERED,
9816 player->index_bit, enter_side);
9826 CheckGravityMovementWhenNotMoving(player);
9829 player->last_move_dir = MV_NO_MOVING;
9831 player->is_moving = FALSE;
9833 #if USE_NEW_MOVEMENT
9834 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
9835 /* ensure that the player is also allowed to move in the next frame */
9836 /* (currently, the player is forced to wait eight frames before he can try
9839 player->move_delay = -1; /* allow direct movement in the next frame */
9843 #if USE_NEW_MOVE_DELAY
9844 if (player->move_delay == -1) /* not yet initialized by DigField() */
9845 player->move_delay = player->move_delay_value;
9848 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9850 TestIfHeroTouchesBadThing(jx, jy);
9851 TestIfPlayerTouchesCustomElement(jx, jy);
9854 if (!player->active)
9860 void ScrollPlayer(struct PlayerInfo *player, int mode)
9862 int jx = player->jx, jy = player->jy;
9863 int last_jx = player->last_jx, last_jy = player->last_jy;
9864 int move_stepsize = TILEX / player->move_delay_value;
9866 if (!player->active || !player->MovPos)
9869 if (mode == SCROLL_INIT)
9871 player->actual_frame_counter = FrameCounter;
9872 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9874 #if USE_NEW_MOVEMENT
9875 if (player->block_last_field &&
9876 Feld[last_jx][last_jy] == EL_EMPTY)
9877 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9879 if (Feld[last_jx][last_jy] == EL_EMPTY)
9880 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9889 else if (!FrameReached(&player->actual_frame_counter, 1))
9892 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9893 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9895 if (!player->block_last_field &&
9896 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9897 Feld[last_jx][last_jy] = EL_EMPTY;
9899 /* before DrawPlayer() to draw correct player graphic for this case */
9900 if (player->MovPos == 0)
9901 CheckGravityMovement(player);
9904 DrawPlayer(player); /* needed here only to cleanup last field */
9907 if (player->MovPos == 0) /* player reached destination field */
9910 if (player->move_delay_reset_counter > 0)
9912 player->move_delay_reset_counter--;
9914 if (player->move_delay_reset_counter == 0)
9916 /* continue with normal speed after quickly moving through gate */
9917 HALVE_PLAYER_SPEED(player);
9919 /* be able to make the next move without delay */
9920 player->move_delay = 0;
9924 if (IS_PASSABLE(Feld[last_jx][last_jy]))
9926 /* continue with normal speed after quickly moving through gate */
9927 HALVE_PLAYER_SPEED(player);
9929 /* be able to make the next move without delay */
9930 player->move_delay = 0;
9934 if (player->block_last_field &&
9935 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9936 Feld[last_jx][last_jy] = EL_EMPTY;
9938 player->last_jx = jx;
9939 player->last_jy = jy;
9941 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9942 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9943 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9945 DrawPlayer(player); /* needed here only to cleanup last field */
9948 if (local_player->friends_still_needed == 0 ||
9949 IS_SP_ELEMENT(Feld[jx][jy]))
9950 player->LevelSolved = player->GameOver = TRUE;
9954 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9955 /* this breaks one level: "machine", level 000 */
9957 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9960 int move_direction = player->MovDir;
9962 int enter_side = MV_DIR_OPPOSITE(move_direction);
9963 int leave_side = move_direction;
9965 static int trigger_sides[4][2] =
9967 /* enter side leave side */
9968 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9969 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9970 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9971 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9973 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9974 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9976 int old_jx = last_jx;
9977 int old_jy = last_jy;
9978 int old_element = Feld[old_jx][old_jy];
9979 int new_element = Feld[jx][jy];
9982 /* !!! TEST ONLY !!! */
9983 if (IS_CUSTOM_ELEMENT(old_element))
9984 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9986 player->index_bit, leave_side);
9988 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9990 player->index_bit, leave_side);
9992 if (IS_CUSTOM_ELEMENT(new_element))
9993 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9994 player->index_bit, enter_side);
9996 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9997 CE_OTHER_GETS_ENTERED,
9998 player->index_bit, enter_side);
10004 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10006 TestIfHeroTouchesBadThing(jx, jy);
10007 TestIfPlayerTouchesCustomElement(jx, jy);
10010 /* needed because pushed element has not yet reached its destination,
10011 so it would trigger a change event at its previous field location */
10012 if (!player->is_pushing)
10014 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10017 if (!player->active)
10018 RemoveHero(player);
10021 if (level.use_step_counter)
10031 if (TimeLeft <= 10 && setup.time_limit)
10032 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10034 DrawGameValue_Time(TimeLeft);
10036 if (!TimeLeft && setup.time_limit)
10037 for (i = 0; i < MAX_PLAYERS; i++)
10038 KillHero(&stored_player[i]);
10040 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10041 DrawGameValue_Time(TimePlayed);
10044 if (tape.single_step && tape.recording && !tape.pausing &&
10045 !player->programmed_action)
10046 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10050 void ScrollScreen(struct PlayerInfo *player, int mode)
10052 static unsigned long screen_frame_counter = 0;
10054 if (mode == SCROLL_INIT)
10056 /* set scrolling step size according to actual player's moving speed */
10057 ScrollStepSize = TILEX / player->move_delay_value;
10059 screen_frame_counter = FrameCounter;
10060 ScreenMovDir = player->MovDir;
10061 ScreenMovPos = player->MovPos;
10062 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10065 else if (!FrameReached(&screen_frame_counter, 1))
10070 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10071 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10072 redraw_mask |= REDRAW_FIELD;
10075 ScreenMovDir = MV_NO_MOVING;
10078 void TestIfPlayerTouchesCustomElement(int x, int y)
10080 static int xy[4][2] =
10087 static int trigger_sides[4][2] =
10089 /* center side border side */
10090 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10091 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10092 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10093 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10095 static int touch_dir[4] =
10097 MV_LEFT | MV_RIGHT,
10102 int center_element = Feld[x][y]; /* should always be non-moving! */
10105 for (i = 0; i < NUM_DIRECTIONS; i++)
10107 int xx = x + xy[i][0];
10108 int yy = y + xy[i][1];
10109 int center_side = trigger_sides[i][0];
10110 int border_side = trigger_sides[i][1];
10111 int border_element;
10113 if (!IN_LEV_FIELD(xx, yy))
10116 if (IS_PLAYER(x, y))
10118 struct PlayerInfo *player = PLAYERINFO(x, y);
10120 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10121 border_element = Feld[xx][yy]; /* may be moving! */
10122 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10123 border_element = Feld[xx][yy];
10124 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10125 border_element = MovingOrBlocked2Element(xx, yy);
10127 continue; /* center and border element do not touch */
10130 /* !!! TEST ONLY !!! */
10131 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10132 player->index_bit, border_side);
10133 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10134 CE_OTHER_GETS_TOUCHED,
10135 player->index_bit, border_side);
10137 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10138 CE_OTHER_GETS_TOUCHED,
10139 player->index_bit, border_side);
10140 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10141 player->index_bit, border_side);
10144 else if (IS_PLAYER(xx, yy))
10146 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10148 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10150 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10151 continue; /* center and border element do not touch */
10155 /* !!! TEST ONLY !!! */
10156 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10157 player->index_bit, center_side);
10158 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10159 CE_OTHER_GETS_TOUCHED,
10160 player->index_bit, center_side);
10162 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10163 CE_OTHER_GETS_TOUCHED,
10164 player->index_bit, center_side);
10165 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10166 player->index_bit, center_side);
10174 void TestIfElementTouchesCustomElement(int x, int y)
10176 static int xy[4][2] =
10183 static int trigger_sides[4][2] =
10185 /* center side border side */
10186 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10187 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10188 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10189 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10191 static int touch_dir[4] =
10193 MV_LEFT | MV_RIGHT,
10198 boolean change_center_element = FALSE;
10199 int center_element_change_page = 0;
10200 int center_element = Feld[x][y]; /* should always be non-moving! */
10201 int border_trigger_element = EL_UNDEFINED;
10204 for (i = 0; i < NUM_DIRECTIONS; i++)
10206 int xx = x + xy[i][0];
10207 int yy = y + xy[i][1];
10208 int center_side = trigger_sides[i][0];
10209 int border_side = trigger_sides[i][1];
10210 int border_element;
10212 if (!IN_LEV_FIELD(xx, yy))
10215 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10216 border_element = Feld[xx][yy]; /* may be moving! */
10217 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10218 border_element = Feld[xx][yy];
10219 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10220 border_element = MovingOrBlocked2Element(xx, yy);
10222 continue; /* center and border element do not touch */
10224 /* check for change of center element (but change it only once) */
10225 if (IS_CUSTOM_ELEMENT(center_element) &&
10226 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10227 !change_center_element)
10229 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10231 struct ElementChangeInfo *change =
10232 &element_info[center_element].change_page[j];
10234 if (change->can_change &&
10235 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10236 change->trigger_side & border_side &&
10238 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10240 change->trigger_element == border_element
10244 change_center_element = TRUE;
10245 center_element_change_page = j;
10246 border_trigger_element = border_element;
10253 /* check for change of border element */
10254 if (IS_CUSTOM_ELEMENT(border_element) &&
10255 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10257 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10259 struct ElementChangeInfo *change =
10260 &element_info[border_element].change_page[j];
10262 if (change->can_change &&
10263 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10264 change->trigger_side & center_side &&
10266 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10268 change->trigger_element == center_element
10273 printf("::: border_element %d, %d\n", x, y);
10276 CheckElementChangeByPage(xx, yy, border_element, center_element,
10277 CE_OTHER_IS_TOUCHING, j);
10284 if (change_center_element)
10287 printf("::: center_element %d, %d\n", x, y);
10290 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10291 CE_OTHER_IS_TOUCHING, center_element_change_page);
10295 void TestIfElementHitsCustomElement(int x, int y, int direction)
10297 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10298 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10299 int hitx = x + dx, hity = y + dy;
10300 int hitting_element = Feld[x][y];
10301 int touched_element;
10303 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10304 !IS_FREE(hitx, hity) &&
10305 (!IS_MOVING(hitx, hity) ||
10306 MovDir[hitx][hity] != direction ||
10307 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10310 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10314 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10318 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10319 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10321 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10322 CE_HITTING_SOMETHING, direction);
10324 if (IN_LEV_FIELD(hitx, hity))
10326 int opposite_direction = MV_DIR_OPPOSITE(direction);
10327 int hitting_side = direction;
10328 int touched_side = opposite_direction;
10330 int touched_element = MovingOrBlocked2Element(hitx, hity);
10333 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10334 MovDir[hitx][hity] != direction ||
10335 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10344 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10345 CE_HIT_BY_SOMETHING, opposite_direction);
10347 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10348 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10350 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10352 struct ElementChangeInfo *change =
10353 &element_info[hitting_element].change_page[i];
10355 if (change->can_change &&
10356 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10357 change->trigger_side & touched_side &&
10360 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10362 change->trigger_element == touched_element
10366 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10367 CE_OTHER_IS_HITTING, i);
10373 if (IS_CUSTOM_ELEMENT(touched_element) &&
10374 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10376 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10378 struct ElementChangeInfo *change =
10379 &element_info[touched_element].change_page[i];
10381 if (change->can_change &&
10382 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10383 change->trigger_side & hitting_side &&
10385 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10387 change->trigger_element == hitting_element
10391 CheckElementChangeByPage(hitx, hity, touched_element,
10392 hitting_element, CE_OTHER_GETS_HIT, i);
10402 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10404 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10405 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10406 int hitx = x + dx, hity = y + dy;
10407 int hitting_element = Feld[x][y];
10408 int touched_element;
10410 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10411 !IS_FREE(hitx, hity) &&
10412 (!IS_MOVING(hitx, hity) ||
10413 MovDir[hitx][hity] != direction ||
10414 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10417 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10421 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10425 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10426 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10428 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10429 EP_CAN_SMASH_EVERYTHING, direction);
10431 if (IN_LEV_FIELD(hitx, hity))
10433 int opposite_direction = MV_DIR_OPPOSITE(direction);
10434 int hitting_side = direction;
10435 int touched_side = opposite_direction;
10437 int touched_element = MovingOrBlocked2Element(hitx, hity);
10440 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10441 MovDir[hitx][hity] != direction ||
10442 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10451 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10452 CE_SMASHED_BY_SOMETHING, opposite_direction);
10454 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10455 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10457 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10459 struct ElementChangeInfo *change =
10460 &element_info[hitting_element].change_page[i];
10462 if (change->can_change &&
10463 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10464 change->trigger_side & touched_side &&
10467 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10469 change->trigger_element == touched_element
10473 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10474 CE_OTHER_IS_SMASHING, i);
10480 if (IS_CUSTOM_ELEMENT(touched_element) &&
10481 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10483 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10485 struct ElementChangeInfo *change =
10486 &element_info[touched_element].change_page[i];
10488 if (change->can_change &&
10489 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10490 change->trigger_side & hitting_side &&
10492 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10494 change->trigger_element == hitting_element
10498 CheckElementChangeByPage(hitx, hity, touched_element,
10499 hitting_element, CE_OTHER_GETS_SMASHED,i);
10509 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10511 int i, kill_x = -1, kill_y = -1;
10512 int bad_element = -1;
10513 static int test_xy[4][2] =
10520 static int test_dir[4] =
10528 for (i = 0; i < NUM_DIRECTIONS; i++)
10530 int test_x, test_y, test_move_dir, test_element;
10532 test_x = good_x + test_xy[i][0];
10533 test_y = good_y + test_xy[i][1];
10535 if (!IN_LEV_FIELD(test_x, test_y))
10539 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10542 test_element = Feld[test_x][test_y];
10544 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10547 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10548 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10550 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10551 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10555 bad_element = test_element;
10561 if (kill_x != -1 || kill_y != -1)
10563 if (IS_PLAYER(good_x, good_y))
10565 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10568 if (player->shield_deadly_time_left > 0 &&
10569 !IS_INDESTRUCTIBLE(bad_element))
10570 Bang(kill_x, kill_y);
10571 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10574 if (player->shield_deadly_time_left > 0)
10575 Bang(kill_x, kill_y);
10576 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10581 Bang(good_x, good_y);
10585 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10587 int i, kill_x = -1, kill_y = -1;
10588 int bad_element = Feld[bad_x][bad_y];
10589 static int test_xy[4][2] =
10596 static int touch_dir[4] =
10598 MV_LEFT | MV_RIGHT,
10603 static int test_dir[4] =
10611 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10614 for (i = 0; i < NUM_DIRECTIONS; i++)
10616 int test_x, test_y, test_move_dir, test_element;
10618 test_x = bad_x + test_xy[i][0];
10619 test_y = bad_y + test_xy[i][1];
10620 if (!IN_LEV_FIELD(test_x, test_y))
10624 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10626 test_element = Feld[test_x][test_y];
10628 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10629 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10631 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10632 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10634 /* good thing is player or penguin that does not move away */
10635 if (IS_PLAYER(test_x, test_y))
10637 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10639 if (bad_element == EL_ROBOT && player->is_moving)
10640 continue; /* robot does not kill player if he is moving */
10642 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10644 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10645 continue; /* center and border element do not touch */
10652 else if (test_element == EL_PENGUIN)
10661 if (kill_x != -1 || kill_y != -1)
10663 if (IS_PLAYER(kill_x, kill_y))
10665 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10668 if (player->shield_deadly_time_left > 0 &&
10669 !IS_INDESTRUCTIBLE(bad_element))
10670 Bang(bad_x, bad_y);
10671 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10674 if (player->shield_deadly_time_left > 0)
10675 Bang(bad_x, bad_y);
10676 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10681 Bang(kill_x, kill_y);
10685 void TestIfHeroTouchesBadThing(int x, int y)
10687 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10690 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10692 TestIfGoodThingHitsBadThing(x, y, move_dir);
10695 void TestIfBadThingTouchesHero(int x, int y)
10697 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10700 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10702 TestIfBadThingHitsGoodThing(x, y, move_dir);
10705 void TestIfFriendTouchesBadThing(int x, int y)
10707 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10710 void TestIfBadThingTouchesFriend(int x, int y)
10712 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10715 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10717 int i, kill_x = bad_x, kill_y = bad_y;
10718 static int xy[4][2] =
10726 for (i = 0; i < NUM_DIRECTIONS; i++)
10730 x = bad_x + xy[i][0];
10731 y = bad_y + xy[i][1];
10732 if (!IN_LEV_FIELD(x, y))
10735 element = Feld[x][y];
10736 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10737 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10745 if (kill_x != bad_x || kill_y != bad_y)
10746 Bang(bad_x, bad_y);
10749 void KillHero(struct PlayerInfo *player)
10751 int jx = player->jx, jy = player->jy;
10753 if (!player->active)
10756 /* remove accessible field at the player's position */
10757 Feld[jx][jy] = EL_EMPTY;
10759 /* deactivate shield (else Bang()/Explode() would not work right) */
10760 player->shield_normal_time_left = 0;
10761 player->shield_deadly_time_left = 0;
10767 static void KillHeroUnlessEnemyProtected(int x, int y)
10769 if (!PLAYER_ENEMY_PROTECTED(x, y))
10770 KillHero(PLAYERINFO(x, y));
10773 static void KillHeroUnlessExplosionProtected(int x, int y)
10775 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10776 KillHero(PLAYERINFO(x, y));
10779 void BuryHero(struct PlayerInfo *player)
10781 int jx = player->jx, jy = player->jy;
10783 if (!player->active)
10787 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10789 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10791 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10793 player->GameOver = TRUE;
10794 RemoveHero(player);
10797 void RemoveHero(struct PlayerInfo *player)
10799 int jx = player->jx, jy = player->jy;
10800 int i, found = FALSE;
10802 player->present = FALSE;
10803 player->active = FALSE;
10805 if (!ExplodeField[jx][jy])
10806 StorePlayer[jx][jy] = 0;
10808 for (i = 0; i < MAX_PLAYERS; i++)
10809 if (stored_player[i].active)
10813 AllPlayersGone = TRUE;
10820 =============================================================================
10821 checkDiagonalPushing()
10822 -----------------------------------------------------------------------------
10823 check if diagonal input device direction results in pushing of object
10824 (by checking if the alternative direction is walkable, diggable, ...)
10825 =============================================================================
10828 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10829 int x, int y, int real_dx, int real_dy)
10831 int jx, jy, dx, dy, xx, yy;
10833 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10836 /* diagonal direction: check alternative direction */
10841 xx = jx + (dx == 0 ? real_dx : 0);
10842 yy = jy + (dy == 0 ? real_dy : 0);
10844 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10848 =============================================================================
10850 -----------------------------------------------------------------------------
10851 x, y: field next to player (non-diagonal) to try to dig to
10852 real_dx, real_dy: direction as read from input device (can be diagonal)
10853 =============================================================================
10856 int DigField(struct PlayerInfo *player,
10857 int oldx, int oldy, int x, int y,
10858 int real_dx, int real_dy, int mode)
10861 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10863 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10864 boolean player_was_pushing = player->is_pushing;
10865 int jx = oldx, jy = oldy;
10866 int dx = x - jx, dy = y - jy;
10867 int nextx = x + dx, nexty = y + dy;
10868 int move_direction = (dx == -1 ? MV_LEFT :
10869 dx == +1 ? MV_RIGHT :
10871 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10872 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10874 int dig_side = MV_DIR_OPPOSITE(move_direction);
10876 static int trigger_sides[4] =
10878 CH_SIDE_RIGHT, /* moving left */
10879 CH_SIDE_LEFT, /* moving right */
10880 CH_SIDE_BOTTOM, /* moving up */
10881 CH_SIDE_TOP, /* moving down */
10883 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10885 int old_element = Feld[jx][jy];
10888 if (is_player) /* function can also be called by EL_PENGUIN */
10890 if (player->MovPos == 0)
10892 player->is_digging = FALSE;
10893 player->is_collecting = FALSE;
10896 if (player->MovPos == 0) /* last pushing move finished */
10897 player->is_pushing = FALSE;
10899 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10901 player->is_switching = FALSE;
10902 #if USE_NEW_PUSH_DELAY
10903 player->push_delay = -1;
10905 player->push_delay = 0;
10908 return MF_NO_ACTION;
10912 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10913 return MF_NO_ACTION;
10918 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10920 if (IS_TUBE(Feld[jx][jy]) ||
10921 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10925 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10926 int tube_leave_directions[][2] =
10928 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10929 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10930 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10931 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
10932 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
10933 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
10934 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
10935 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
10936 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
10937 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
10938 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
10939 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10942 while (tube_leave_directions[i][0] != tube_element)
10945 if (tube_leave_directions[i][0] == -1) /* should not happen */
10949 if (!(tube_leave_directions[i][1] & move_direction))
10950 return MF_NO_ACTION; /* tube has no opening in this direction */
10955 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10956 old_element = Back[jx][jy];
10960 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10961 return MF_NO_ACTION; /* field has no opening in this direction */
10963 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10964 return MF_NO_ACTION; /* field has no opening in this direction */
10966 element = Feld[x][y];
10968 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
10969 return MF_NO_ACTION;
10971 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10972 game.engine_version >= VERSION_IDENT(2,2,0,0))
10973 return MF_NO_ACTION;
10976 if (game.gravity && is_player && !player->is_auto_moving &&
10977 canFallDown(player) && move_direction != MV_DOWN &&
10978 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10979 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10983 if (element == EL_EMPTY_SPACE &&
10984 game.gravity && !player->is_auto_moving &&
10985 canFallDown(player) && move_direction != MV_DOWN)
10986 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10992 case EL_SP_PORT_LEFT:
10993 case EL_SP_PORT_RIGHT:
10994 case EL_SP_PORT_UP:
10995 case EL_SP_PORT_DOWN:
10996 case EL_SP_PORT_HORIZONTAL:
10997 case EL_SP_PORT_VERTICAL:
10998 case EL_SP_PORT_ANY:
10999 case EL_SP_GRAVITY_PORT_LEFT:
11000 case EL_SP_GRAVITY_PORT_RIGHT:
11001 case EL_SP_GRAVITY_PORT_UP:
11002 case EL_SP_GRAVITY_PORT_DOWN:
11004 if (!canEnterSupaplexPort(x, y, dx, dy))
11005 return MF_NO_ACTION;
11008 element != EL_SP_PORT_LEFT &&
11009 element != EL_SP_GRAVITY_PORT_LEFT &&
11010 element != EL_SP_PORT_HORIZONTAL &&
11011 element != EL_SP_PORT_ANY) ||
11013 element != EL_SP_PORT_RIGHT &&
11014 element != EL_SP_GRAVITY_PORT_RIGHT &&
11015 element != EL_SP_PORT_HORIZONTAL &&
11016 element != EL_SP_PORT_ANY) ||
11018 element != EL_SP_PORT_UP &&
11019 element != EL_SP_GRAVITY_PORT_UP &&
11020 element != EL_SP_PORT_VERTICAL &&
11021 element != EL_SP_PORT_ANY) ||
11023 element != EL_SP_PORT_DOWN &&
11024 element != EL_SP_GRAVITY_PORT_DOWN &&
11025 element != EL_SP_PORT_VERTICAL &&
11026 element != EL_SP_PORT_ANY) ||
11027 !IN_LEV_FIELD(nextx, nexty) ||
11028 !IS_FREE(nextx, nexty))
11029 return MF_NO_ACTION;
11032 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11033 element == EL_SP_GRAVITY_PORT_RIGHT ||
11034 element == EL_SP_GRAVITY_PORT_UP ||
11035 element == EL_SP_GRAVITY_PORT_DOWN)
11036 game.gravity = !game.gravity;
11038 /* automatically move to the next field with double speed */
11039 player->programmed_action = move_direction;
11041 if (player->move_delay_reset_counter == 0)
11043 player->move_delay_reset_counter = 2; /* two double speed steps */
11045 DOUBLE_PLAYER_SPEED(player);
11048 player->move_delay_reset_counter = 2;
11050 DOUBLE_PLAYER_SPEED(player);
11054 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11057 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11063 case EL_TUBE_VERTICAL:
11064 case EL_TUBE_HORIZONTAL:
11065 case EL_TUBE_VERTICAL_LEFT:
11066 case EL_TUBE_VERTICAL_RIGHT:
11067 case EL_TUBE_HORIZONTAL_UP:
11068 case EL_TUBE_HORIZONTAL_DOWN:
11069 case EL_TUBE_LEFT_UP:
11070 case EL_TUBE_LEFT_DOWN:
11071 case EL_TUBE_RIGHT_UP:
11072 case EL_TUBE_RIGHT_DOWN:
11075 int tube_enter_directions[][2] =
11077 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11078 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11079 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11080 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11081 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11082 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11083 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11084 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11085 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11086 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11087 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11088 { -1, MV_NO_MOVING }
11091 while (tube_enter_directions[i][0] != element)
11094 if (tube_enter_directions[i][0] == -1) /* should not happen */
11098 if (!(tube_enter_directions[i][1] & move_direction))
11099 return MF_NO_ACTION; /* tube has no opening in this direction */
11101 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11109 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11111 if (IS_WALKABLE(element))
11114 int sound_element = SND_ELEMENT(element);
11115 int sound_action = ACTION_WALKING;
11118 if (!ACCESS_FROM(element, opposite_direction))
11119 return MF_NO_ACTION; /* field not accessible from this direction */
11123 if (element == EL_EMPTY_SPACE &&
11124 game.gravity && !player->is_auto_moving &&
11125 canFallDown(player) && move_direction != MV_DOWN)
11126 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11129 if (IS_GATE(element))
11131 if (!player->key[element - EL_GATE_1])
11132 return MF_NO_ACTION;
11134 else if (IS_GATE_GRAY(element))
11136 if (!player->key[element - EL_GATE_1_GRAY])
11137 return MF_NO_ACTION;
11139 else if (element == EL_EXIT_OPEN ||
11140 element == EL_SP_EXIT_OPEN ||
11141 element == EL_SP_EXIT_OPENING)
11143 sound_action = ACTION_PASSING; /* player is passing exit */
11145 else if (element == EL_EMPTY)
11147 sound_action = ACTION_MOVING; /* nothing to walk on */
11150 /* play sound from background or player, whatever is available */
11151 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11152 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11154 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11159 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11161 else if (IS_PASSABLE(element))
11165 if (!canPassField(x, y, move_direction))
11166 return MF_NO_ACTION;
11171 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11172 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11173 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11174 return MF_NO_ACTION;
11176 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11177 return MF_NO_ACTION;
11182 if (!ACCESS_FROM(element, opposite_direction))
11183 return MF_NO_ACTION; /* field not accessible from this direction */
11185 if (IS_CUSTOM_ELEMENT(element) &&
11186 !ACCESS_FROM(element, opposite_direction))
11187 return MF_NO_ACTION; /* field not accessible from this direction */
11191 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11192 return MF_NO_ACTION;
11197 if (IS_EM_GATE(element))
11199 if (!player->key[element - EL_EM_GATE_1])
11200 return MF_NO_ACTION;
11202 else if (IS_EM_GATE_GRAY(element))
11204 if (!player->key[element - EL_EM_GATE_1_GRAY])
11205 return MF_NO_ACTION;
11207 else if (IS_SP_PORT(element))
11209 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11210 element == EL_SP_GRAVITY_PORT_RIGHT ||
11211 element == EL_SP_GRAVITY_PORT_UP ||
11212 element == EL_SP_GRAVITY_PORT_DOWN)
11213 game.gravity = !game.gravity;
11214 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11215 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11216 element == EL_SP_GRAVITY_ON_PORT_UP ||
11217 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11218 game.gravity = TRUE;
11219 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11220 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11221 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11222 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11223 game.gravity = FALSE;
11226 /* automatically move to the next field with double speed */
11227 player->programmed_action = move_direction;
11229 if (player->move_delay_reset_counter == 0)
11231 player->move_delay_reset_counter = 2; /* two double speed steps */
11233 DOUBLE_PLAYER_SPEED(player);
11236 player->move_delay_reset_counter = 2;
11238 DOUBLE_PLAYER_SPEED(player);
11241 PlayLevelSoundAction(x, y, ACTION_PASSING);
11245 else if (IS_DIGGABLE(element))
11249 if (mode != DF_SNAP)
11252 GfxElement[x][y] = GFX_ELEMENT(element);
11255 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11257 player->is_digging = TRUE;
11260 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11262 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11263 player->index_bit, dig_side);
11266 if (mode == DF_SNAP)
11267 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11272 else if (IS_COLLECTIBLE(element))
11276 if (is_player && mode != DF_SNAP)
11278 GfxElement[x][y] = element;
11279 player->is_collecting = TRUE;
11282 if (element == EL_SPEED_PILL)
11283 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11284 else if (element == EL_EXTRA_TIME && level.time > 0)
11287 DrawGameValue_Time(TimeLeft);
11289 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11291 player->shield_normal_time_left += 10;
11292 if (element == EL_SHIELD_DEADLY)
11293 player->shield_deadly_time_left += 10;
11295 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11297 if (player->inventory_size < MAX_INVENTORY_SIZE)
11298 player->inventory_element[player->inventory_size++] = element;
11300 DrawGameValue_Dynamite(local_player->inventory_size);
11302 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11304 player->dynabomb_count++;
11305 player->dynabombs_left++;
11307 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11309 player->dynabomb_size++;
11311 else if (element == EL_DYNABOMB_INCREASE_POWER)
11313 player->dynabomb_xl = TRUE;
11315 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
11316 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
11318 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
11319 element - EL_KEY_1 : element - EL_EM_KEY_1);
11321 player->key[key_nr] = TRUE;
11323 DrawGameValue_Keys(player);
11325 redraw_mask |= REDRAW_DOOR_1;
11327 else if (IS_ENVELOPE(element))
11330 player->show_envelope = element;
11332 ShowEnvelope(element - EL_ENVELOPE_1);
11335 else if (IS_DROPPABLE(element) ||
11336 IS_THROWABLE(element)) /* can be collected and dropped */
11340 if (element_info[element].collect_count == 0)
11341 player->inventory_infinite_element = element;
11343 for (i = 0; i < element_info[element].collect_count; i++)
11344 if (player->inventory_size < MAX_INVENTORY_SIZE)
11345 player->inventory_element[player->inventory_size++] = element;
11347 DrawGameValue_Dynamite(local_player->inventory_size);
11349 else if (element_info[element].collect_count > 0)
11351 local_player->gems_still_needed -=
11352 element_info[element].collect_count;
11353 if (local_player->gems_still_needed < 0)
11354 local_player->gems_still_needed = 0;
11356 DrawGameValue_Emeralds(local_player->gems_still_needed);
11359 RaiseScoreElement(element);
11360 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11363 CheckTriggeredElementChangeByPlayer(x, y, element,
11364 CE_OTHER_GETS_COLLECTED,
11365 player->index_bit, dig_side);
11368 if (mode == DF_SNAP)
11369 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11374 else if (IS_PUSHABLE(element))
11376 if (mode == DF_SNAP && element != EL_BD_ROCK)
11377 return MF_NO_ACTION;
11379 if (CAN_FALL(element) && dy)
11380 return MF_NO_ACTION;
11382 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11383 !(element == EL_SPRING && level.use_spring_bug))
11384 return MF_NO_ACTION;
11387 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11388 ((move_direction & MV_VERTICAL &&
11389 ((element_info[element].move_pattern & MV_LEFT &&
11390 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11391 (element_info[element].move_pattern & MV_RIGHT &&
11392 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11393 (move_direction & MV_HORIZONTAL &&
11394 ((element_info[element].move_pattern & MV_UP &&
11395 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11396 (element_info[element].move_pattern & MV_DOWN &&
11397 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11398 return MF_NO_ACTION;
11402 /* do not push elements already moving away faster than player */
11403 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11404 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11405 return MF_NO_ACTION;
11407 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11408 return MF_NO_ACTION;
11414 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11416 if (player->push_delay_value == -1 || !player_was_pushing)
11417 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11419 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11421 if (player->push_delay_value == -1)
11422 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11425 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11427 if (player->push_delay_value == -1 || !player_was_pushing)
11428 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11431 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11433 if (!player->is_pushing)
11434 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11438 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11439 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11440 !player_is_pushing))
11441 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11444 if (!player->is_pushing &&
11445 game.engine_version >= VERSION_IDENT(2,2,0,7))
11446 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11450 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11451 player->push_delay, player->push_delay_value,
11452 FrameCounter, game.engine_version,
11453 player_was_pushing, player->is_pushing,
11454 element, element_info[element].token_name,
11455 GET_NEW_PUSH_DELAY(element));
11458 player->is_pushing = TRUE;
11460 if (!(IN_LEV_FIELD(nextx, nexty) &&
11461 (IS_FREE(nextx, nexty) ||
11462 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11463 IS_SB_ELEMENT(element)))))
11464 return MF_NO_ACTION;
11466 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11467 return MF_NO_ACTION;
11469 #if USE_NEW_PUSH_DELAY
11472 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11473 printf("::: ALERT: %d, %d [%d / %d]\n",
11474 player->push_delay, player->push_delay2,
11475 FrameCounter, FrameCounter / 50);
11478 if (player->push_delay == -1) /* new pushing; restart delay */
11479 player->push_delay = 0;
11481 if (player->push_delay == 0) /* new pushing; restart delay */
11482 player->push_delay = FrameCounter;
11485 #if USE_NEW_PUSH_DELAY
11487 if ( (player->push_delay > 0) != (!xxx_fr) )
11488 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11489 player->push_delay,
11490 xxx_pdv2, player->push_delay2, player->push_delay_value,
11491 FrameCounter, FrameCounter / 50);
11495 if (player->push_delay > 0 &&
11496 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11497 element != EL_SPRING && element != EL_BALLOON)
11500 if (player->push_delay < player->push_delay_value &&
11501 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11502 element != EL_SPRING && element != EL_BALLOON)
11506 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11507 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11508 element != EL_SPRING && element != EL_BALLOON)
11511 /* make sure that there is no move delay before next try to push */
11512 #if USE_NEW_MOVE_DELAY
11513 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11514 player->move_delay = 0;
11516 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11517 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11520 return MF_NO_ACTION;
11524 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11527 if (IS_SB_ELEMENT(element))
11529 if (element == EL_SOKOBAN_FIELD_FULL)
11531 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11532 local_player->sokobanfields_still_needed++;
11535 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11537 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11538 local_player->sokobanfields_still_needed--;
11541 Feld[x][y] = EL_SOKOBAN_OBJECT;
11543 if (Back[x][y] == Back[nextx][nexty])
11544 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11545 else if (Back[x][y] != 0)
11546 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11549 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11552 if (local_player->sokobanfields_still_needed == 0 &&
11553 game.emulation == EMU_SOKOBAN)
11555 player->LevelSolved = player->GameOver = TRUE;
11556 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11560 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11562 InitMovingField(x, y, move_direction);
11563 GfxAction[x][y] = ACTION_PUSHING;
11565 if (mode == DF_SNAP)
11566 ContinueMoving(x, y);
11568 MovPos[x][y] = (dx != 0 ? dx : dy);
11570 Pushed[x][y] = TRUE;
11571 Pushed[nextx][nexty] = TRUE;
11573 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11574 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11576 player->push_delay_value = -1; /* get new value later */
11579 /* check for element change _after_ element has been pushed! */
11583 /* !!! TEST ONLY !!! */
11584 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11585 player->index_bit, dig_side);
11586 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11587 player->index_bit, dig_side);
11589 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11590 player->index_bit, dig_side);
11591 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11592 player->index_bit, dig_side);
11598 else if (IS_SWITCHABLE(element))
11600 if (PLAYER_SWITCHING(player, x, y))
11602 CheckTriggeredElementChangeByPlayer(x,y, element,
11603 CE_OTHER_GETS_PRESSED,
11604 player->index_bit, dig_side);
11609 player->is_switching = TRUE;
11610 player->switch_x = x;
11611 player->switch_y = y;
11613 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11615 if (element == EL_ROBOT_WHEEL)
11617 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11621 DrawLevelField(x, y);
11623 else if (element == EL_SP_TERMINAL)
11627 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11629 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11631 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11632 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11635 else if (IS_BELT_SWITCH(element))
11637 ToggleBeltSwitch(x, y);
11639 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11640 element == EL_SWITCHGATE_SWITCH_DOWN)
11642 ToggleSwitchgateSwitch(x, y);
11644 else if (element == EL_LIGHT_SWITCH ||
11645 element == EL_LIGHT_SWITCH_ACTIVE)
11647 ToggleLightSwitch(x, y);
11650 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11651 SND_LIGHT_SWITCH_ACTIVATING :
11652 SND_LIGHT_SWITCH_DEACTIVATING);
11655 else if (element == EL_TIMEGATE_SWITCH)
11657 ActivateTimegateSwitch(x, y);
11659 else if (element == EL_BALLOON_SWITCH_LEFT ||
11660 element == EL_BALLOON_SWITCH_RIGHT ||
11661 element == EL_BALLOON_SWITCH_UP ||
11662 element == EL_BALLOON_SWITCH_DOWN ||
11663 element == EL_BALLOON_SWITCH_ANY)
11665 if (element == EL_BALLOON_SWITCH_ANY)
11666 game.balloon_dir = move_direction;
11668 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11669 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11670 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11671 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11674 else if (element == EL_LAMP)
11676 Feld[x][y] = EL_LAMP_ACTIVE;
11677 local_player->lights_still_needed--;
11679 DrawLevelField(x, y);
11681 else if (element == EL_TIME_ORB_FULL)
11683 Feld[x][y] = EL_TIME_ORB_EMPTY;
11685 DrawGameValue_Time(TimeLeft);
11687 DrawLevelField(x, y);
11690 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11694 CheckTriggeredElementChangeByPlayer(x, y, element,
11695 CE_OTHER_IS_SWITCHING,
11696 player->index_bit, dig_side);
11698 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11699 player->index_bit, dig_side);
11705 if (!PLAYER_SWITCHING(player, x, y))
11707 player->is_switching = TRUE;
11708 player->switch_x = x;
11709 player->switch_y = y;
11712 /* !!! TEST ONLY !!! */
11713 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11714 player->index_bit, dig_side);
11715 CheckTriggeredElementChangeByPlayer(x, y, element,
11716 CE_OTHER_IS_SWITCHING,
11717 player->index_bit, dig_side);
11719 CheckTriggeredElementChangeByPlayer(x, y, element,
11720 CE_OTHER_IS_SWITCHING,
11721 player->index_bit, dig_side);
11722 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11723 player->index_bit, dig_side);
11728 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11729 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11730 player->index_bit, dig_side);
11731 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11732 player->index_bit, dig_side);
11734 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11735 player->index_bit, dig_side);
11736 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11737 player->index_bit, dig_side);
11741 return MF_NO_ACTION;
11744 #if USE_NEW_PUSH_DELAY
11745 player->push_delay = -1;
11747 player->push_delay = 0;
11750 if (Feld[x][y] != element) /* really digged/collected something */
11751 player->is_collecting = !player->is_digging;
11756 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11758 int jx = player->jx, jy = player->jy;
11759 int x = jx + dx, y = jy + dy;
11760 int snap_direction = (dx == -1 ? MV_LEFT :
11761 dx == +1 ? MV_RIGHT :
11763 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11766 if (player->MovPos != 0)
11769 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11773 if (!player->active || !IN_LEV_FIELD(x, y))
11781 if (player->MovPos == 0)
11782 player->is_pushing = FALSE;
11784 player->is_snapping = FALSE;
11786 if (player->MovPos == 0)
11788 player->is_moving = FALSE;
11789 player->is_digging = FALSE;
11790 player->is_collecting = FALSE;
11796 if (player->is_snapping)
11799 player->MovDir = snap_direction;
11802 if (player->MovPos == 0)
11805 player->is_moving = FALSE;
11806 player->is_digging = FALSE;
11807 player->is_collecting = FALSE;
11810 player->is_dropping = FALSE;
11812 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11815 player->is_snapping = TRUE;
11818 if (player->MovPos == 0)
11821 player->is_moving = FALSE;
11822 player->is_digging = FALSE;
11823 player->is_collecting = FALSE;
11827 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11828 DrawLevelField(player->last_jx, player->last_jy);
11831 DrawLevelField(x, y);
11840 boolean DropElement(struct PlayerInfo *player)
11842 int old_element, new_element;
11843 int dropx = player->jx, dropy = player->jy;
11844 int drop_direction = player->MovDir;
11846 int drop_side = drop_direction;
11848 static int trigger_sides[4] =
11850 CH_SIDE_LEFT, /* dropping left */
11851 CH_SIDE_RIGHT, /* dropping right */
11852 CH_SIDE_TOP, /* dropping up */
11853 CH_SIDE_BOTTOM, /* dropping down */
11855 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11857 int drop_element = (player->inventory_size > 0 ?
11858 player->inventory_element[player->inventory_size - 1] :
11859 player->inventory_infinite_element != EL_UNDEFINED ?
11860 player->inventory_infinite_element :
11861 player->dynabombs_left > 0 ?
11862 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11865 if (IS_THROWABLE(drop_element))
11867 dropx += GET_DX_FROM_DIR(drop_direction);
11868 dropy += GET_DY_FROM_DIR(drop_direction);
11870 if (!IN_LEV_FIELD(dropx, dropy))
11874 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11875 new_element = drop_element; /* default: no change when dropping */
11877 /* check if player is active, not moving and ready to drop */
11878 if (!player->active || player->MovPos || player->drop_delay > 0)
11881 /* check if player has anything that can be dropped */
11883 if (new_element == EL_UNDEFINED)
11886 if (player->inventory_size == 0 &&
11887 player->inventory_infinite_element == EL_UNDEFINED &&
11888 player->dynabombs_left == 0)
11892 /* check if anything can be dropped at the current position */
11893 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11896 /* collected custom elements can only be dropped on empty fields */
11898 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11901 if (player->inventory_size > 0 &&
11902 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11903 && old_element != EL_EMPTY)
11907 if (old_element != EL_EMPTY)
11908 Back[dropx][dropy] = old_element; /* store old element on this field */
11910 ResetGfxAnimation(dropx, dropy);
11911 ResetRandomAnimationValue(dropx, dropy);
11913 if (player->inventory_size > 0 ||
11914 player->inventory_infinite_element != EL_UNDEFINED)
11916 if (player->inventory_size > 0)
11918 player->inventory_size--;
11921 new_element = player->inventory_element[player->inventory_size];
11924 DrawGameValue_Dynamite(local_player->inventory_size);
11926 if (new_element == EL_DYNAMITE)
11927 new_element = EL_DYNAMITE_ACTIVE;
11928 else if (new_element == EL_SP_DISK_RED)
11929 new_element = EL_SP_DISK_RED_ACTIVE;
11932 Feld[dropx][dropy] = new_element;
11934 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11935 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11936 el2img(Feld[dropx][dropy]), 0);
11938 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11941 /* needed if previous element just changed to "empty" in the last frame */
11942 Changed[dropx][dropy] = 0; /* allow another change */
11946 /* !!! TEST ONLY !!! */
11947 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11948 player->index_bit, drop_side);
11949 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11950 CE_OTHER_GETS_DROPPED,
11951 player->index_bit, drop_side);
11953 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11954 CE_OTHER_GETS_DROPPED,
11955 player->index_bit, drop_side);
11956 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11957 player->index_bit, drop_side);
11960 TestIfElementTouchesCustomElement(dropx, dropy);
11962 else /* player is dropping a dyna bomb */
11964 player->dynabombs_left--;
11967 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
11970 Feld[dropx][dropy] = new_element;
11972 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11973 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11974 el2img(Feld[dropx][dropy]), 0);
11976 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11983 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11986 InitField_WithBug1(dropx, dropy, FALSE);
11988 InitField(dropx, dropy, FALSE);
11989 if (CAN_MOVE(Feld[dropx][dropy]))
11990 InitMovDir(dropx, dropy);
11994 new_element = Feld[dropx][dropy]; /* element might have changed */
11996 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11997 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12000 int move_stepsize = element_info[new_element].move_stepsize;
12002 int move_direction, nextx, nexty;
12004 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12005 MovDir[dropx][dropy] = drop_direction;
12007 move_direction = MovDir[dropx][dropy];
12008 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12009 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12012 Changed[dropx][dropy] = 0; /* allow another change */
12013 CheckCollision[dropx][dropy] = 2;
12016 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12019 WasJustMoving[dropx][dropy] = 3;
12022 InitMovingField(dropx, dropy, move_direction);
12023 ContinueMoving(dropx, dropy);
12028 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12031 Changed[dropx][dropy] = 0; /* allow another change */
12034 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12036 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12037 CE_HITTING_SOMETHING, move_direction);
12045 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12050 player->drop_delay = 8 + 8 + 8;
12054 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12059 player->is_dropping = TRUE;
12065 /* ------------------------------------------------------------------------- */
12066 /* game sound playing functions */
12067 /* ------------------------------------------------------------------------- */
12069 static int *loop_sound_frame = NULL;
12070 static int *loop_sound_volume = NULL;
12072 void InitPlayLevelSound()
12074 int num_sounds = getSoundListSize();
12076 checked_free(loop_sound_frame);
12077 checked_free(loop_sound_volume);
12079 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12080 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12083 static void PlayLevelSound(int x, int y, int nr)
12085 int sx = SCREENX(x), sy = SCREENY(y);
12086 int volume, stereo_position;
12087 int max_distance = 8;
12088 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12090 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12091 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12094 if (!IN_LEV_FIELD(x, y) ||
12095 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12096 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12099 volume = SOUND_MAX_VOLUME;
12101 if (!IN_SCR_FIELD(sx, sy))
12103 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12104 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12106 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12109 stereo_position = (SOUND_MAX_LEFT +
12110 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12111 (SCR_FIELDX + 2 * max_distance));
12113 if (IS_LOOP_SOUND(nr))
12115 /* This assures that quieter loop sounds do not overwrite louder ones,
12116 while restarting sound volume comparison with each new game frame. */
12118 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12121 loop_sound_volume[nr] = volume;
12122 loop_sound_frame[nr] = FrameCounter;
12125 PlaySoundExt(nr, volume, stereo_position, type);
12128 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12130 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12131 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12132 y < LEVELY(BY1) ? LEVELY(BY1) :
12133 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12137 static void PlayLevelSoundAction(int x, int y, int action)
12139 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12142 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12144 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12146 if (sound_effect != SND_UNDEFINED)
12147 PlayLevelSound(x, y, sound_effect);
12150 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12153 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12155 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12156 PlayLevelSound(x, y, sound_effect);
12159 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12161 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12163 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12164 PlayLevelSound(x, y, sound_effect);
12167 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12169 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12171 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12172 StopSound(sound_effect);
12175 static void PlayLevelMusic()
12177 if (levelset.music[level_nr] != MUS_UNDEFINED)
12178 PlayMusic(levelset.music[level_nr]); /* from config file */
12180 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12183 void RaiseScore(int value)
12185 local_player->score += value;
12187 DrawGameValue_Score(local_player->score);
12190 void RaiseScoreElement(int element)
12195 case EL_BD_DIAMOND:
12196 case EL_EMERALD_YELLOW:
12197 case EL_EMERALD_RED:
12198 case EL_EMERALD_PURPLE:
12199 case EL_SP_INFOTRON:
12200 RaiseScore(level.score[SC_EMERALD]);
12203 RaiseScore(level.score[SC_DIAMOND]);
12206 RaiseScore(level.score[SC_CRYSTAL]);
12209 RaiseScore(level.score[SC_PEARL]);
12212 case EL_BD_BUTTERFLY:
12213 case EL_SP_ELECTRON:
12214 RaiseScore(level.score[SC_BUG]);
12217 case EL_BD_FIREFLY:
12218 case EL_SP_SNIKSNAK:
12219 RaiseScore(level.score[SC_SPACESHIP]);
12222 case EL_DARK_YAMYAM:
12223 RaiseScore(level.score[SC_YAMYAM]);
12226 RaiseScore(level.score[SC_ROBOT]);
12229 RaiseScore(level.score[SC_PACMAN]);
12232 RaiseScore(level.score[SC_NUT]);
12235 case EL_SP_DISK_RED:
12236 case EL_DYNABOMB_INCREASE_NUMBER:
12237 case EL_DYNABOMB_INCREASE_SIZE:
12238 case EL_DYNABOMB_INCREASE_POWER:
12239 RaiseScore(level.score[SC_DYNAMITE]);
12241 case EL_SHIELD_NORMAL:
12242 case EL_SHIELD_DEADLY:
12243 RaiseScore(level.score[SC_SHIELD]);
12245 case EL_EXTRA_TIME:
12246 RaiseScore(level.score[SC_TIME_BONUS]);
12252 RaiseScore(level.score[SC_KEY]);
12255 RaiseScore(element_info[element].collect_score);
12260 void RequestQuitGame(boolean ask_if_really_quit)
12262 if (AllPlayersGone ||
12263 !ask_if_really_quit ||
12264 level_editor_test_game ||
12265 Request("Do you really want to quit the game ?",
12266 REQ_ASK | REQ_STAY_CLOSED))
12268 #if defined(NETWORK_AVALIABLE)
12269 if (options.network)
12270 SendToServer_StopPlaying();
12274 game_status = GAME_MODE_MAIN;
12282 if (tape.playing && tape.deactivate_display)
12283 TapeDeactivateDisplayOff(TRUE);
12286 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12289 if (tape.playing && tape.deactivate_display)
12290 TapeDeactivateDisplayOn();
12297 /* ---------- new game button stuff ---------------------------------------- */
12299 /* graphic position values for game buttons */
12300 #define GAME_BUTTON_XSIZE 30
12301 #define GAME_BUTTON_YSIZE 30
12302 #define GAME_BUTTON_XPOS 5
12303 #define GAME_BUTTON_YPOS 215
12304 #define SOUND_BUTTON_XPOS 5
12305 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12307 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12308 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12309 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12310 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12311 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12312 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12319 } gamebutton_info[NUM_GAME_BUTTONS] =
12322 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12327 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12328 GAME_CTRL_ID_PAUSE,
12332 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12337 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12338 SOUND_CTRL_ID_MUSIC,
12339 "background music on/off"
12342 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12343 SOUND_CTRL_ID_LOOPS,
12344 "sound loops on/off"
12347 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12348 SOUND_CTRL_ID_SIMPLE,
12349 "normal sounds on/off"
12353 void CreateGameButtons()
12357 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12359 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12360 struct GadgetInfo *gi;
12363 unsigned long event_mask;
12364 int gd_xoffset, gd_yoffset;
12365 int gd_x1, gd_x2, gd_y1, gd_y2;
12368 gd_xoffset = gamebutton_info[i].x;
12369 gd_yoffset = gamebutton_info[i].y;
12370 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12371 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12373 if (id == GAME_CTRL_ID_STOP ||
12374 id == GAME_CTRL_ID_PAUSE ||
12375 id == GAME_CTRL_ID_PLAY)
12377 button_type = GD_TYPE_NORMAL_BUTTON;
12379 event_mask = GD_EVENT_RELEASED;
12380 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12381 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12385 button_type = GD_TYPE_CHECK_BUTTON;
12387 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12388 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12389 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12390 event_mask = GD_EVENT_PRESSED;
12391 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12392 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12395 gi = CreateGadget(GDI_CUSTOM_ID, id,
12396 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12397 GDI_X, DX + gd_xoffset,
12398 GDI_Y, DY + gd_yoffset,
12399 GDI_WIDTH, GAME_BUTTON_XSIZE,
12400 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12401 GDI_TYPE, button_type,
12402 GDI_STATE, GD_BUTTON_UNPRESSED,
12403 GDI_CHECKED, checked,
12404 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12405 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12406 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12407 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12408 GDI_EVENT_MASK, event_mask,
12409 GDI_CALLBACK_ACTION, HandleGameButtons,
12413 Error(ERR_EXIT, "cannot create gadget");
12415 game_gadget[id] = gi;
12419 void FreeGameButtons()
12423 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12424 FreeGadget(game_gadget[i]);
12427 static void MapGameButtons()
12431 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12432 MapGadget(game_gadget[i]);
12435 void UnmapGameButtons()
12439 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12440 UnmapGadget(game_gadget[i]);
12443 static void HandleGameButtons(struct GadgetInfo *gi)
12445 int id = gi->custom_id;
12447 if (game_status != GAME_MODE_PLAYING)
12452 case GAME_CTRL_ID_STOP:
12453 RequestQuitGame(TRUE);
12456 case GAME_CTRL_ID_PAUSE:
12457 if (options.network)
12459 #if defined(NETWORK_AVALIABLE)
12461 SendToServer_ContinuePlaying();
12463 SendToServer_PausePlaying();
12467 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12470 case GAME_CTRL_ID_PLAY:
12473 #if defined(NETWORK_AVALIABLE)
12474 if (options.network)
12475 SendToServer_ContinuePlaying();
12479 tape.pausing = FALSE;
12480 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12485 case SOUND_CTRL_ID_MUSIC:
12486 if (setup.sound_music)
12488 setup.sound_music = FALSE;
12491 else if (audio.music_available)
12493 setup.sound = setup.sound_music = TRUE;
12495 SetAudioMode(setup.sound);
12501 case SOUND_CTRL_ID_LOOPS:
12502 if (setup.sound_loops)
12503 setup.sound_loops = FALSE;
12504 else if (audio.loops_available)
12506 setup.sound = setup.sound_loops = TRUE;
12507 SetAudioMode(setup.sound);
12511 case SOUND_CTRL_ID_SIMPLE:
12512 if (setup.sound_simple)
12513 setup.sound_simple = FALSE;
12514 else if (audio.sound_available)
12516 setup.sound = setup.sound_simple = TRUE;
12517 SetAudioMode(setup.sound);