1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
35 /* for MovePlayer() */
36 #define MF_NO_ACTION 0
40 /* for ScrollPlayer() */
42 #define SCROLL_GO_ON 1
45 #define EX_PHASE_START 0
46 #define EX_TYPE_NONE 0
47 #define EX_TYPE_NORMAL (1 << 0)
48 #define EX_TYPE_CENTER (1 << 1)
49 #define EX_TYPE_BORDER (1 << 2)
50 #define EX_TYPE_CROSS (1 << 3)
51 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
53 /* special positions in the game control window (relative to control window) */
56 #define XX_EMERALDS 29
57 #define YY_EMERALDS 54
58 #define XX_DYNAMITE 29
59 #define YY_DYNAMITE 89
68 /* special positions in the game control window (relative to main window) */
69 #define DX_LEVEL (DX + XX_LEVEL)
70 #define DY_LEVEL (DY + YY_LEVEL)
71 #define DX_EMERALDS (DX + XX_EMERALDS)
72 #define DY_EMERALDS (DY + YY_EMERALDS)
73 #define DX_DYNAMITE (DX + XX_DYNAMITE)
74 #define DY_DYNAMITE (DY + YY_DYNAMITE)
75 #define DX_KEYS (DX + XX_KEYS)
76 #define DY_KEYS (DY + YY_KEYS)
77 #define DX_SCORE (DX + XX_SCORE)
78 #define DY_SCORE (DY + YY_SCORE)
79 #define DX_TIME1 (DX + XX_TIME1)
80 #define DX_TIME2 (DX + XX_TIME2)
81 #define DY_TIME (DY + YY_TIME)
83 /* values for initial player move delay (initial delay counter value) */
84 #define INITIAL_MOVE_DELAY_OFF -1
85 #define INITIAL_MOVE_DELAY_ON 0
87 /* values for player movement speed (which is in fact a delay value) */
88 #define MOVE_DELAY_NORMAL_SPEED 8
89 #define MOVE_DELAY_HIGH_SPEED 4
91 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
92 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
93 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
94 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
96 /* values for other actions */
97 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
99 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
100 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
102 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
104 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
105 RND(element_info[e].push_delay_random))
106 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
107 RND(element_info[e].drop_delay_random))
108 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
109 RND(element_info[e].move_delay_random))
110 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
111 (element_info[e].move_delay_random))
113 #define GET_TARGET_ELEMENT(e, ch) \
114 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
115 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
117 #define CAN_GROW_INTO(e) \
118 (e == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
120 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
121 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
124 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
125 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
126 (CAN_MOVE_INTO_ACID(e) && \
127 Feld[x][y] == EL_ACID) || \
130 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
131 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
132 (CAN_MOVE_INTO_ACID(e) && \
133 Feld[x][y] == EL_ACID) || \
136 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
137 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
139 (CAN_MOVE_INTO_ACID(e) && \
140 Feld[x][y] == EL_ACID) || \
141 (DONT_COLLIDE_WITH(e) && \
143 !PLAYER_ENEMY_PROTECTED(x, y))))
146 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
147 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
149 (DONT_COLLIDE_WITH(e) && \
151 !PLAYER_ENEMY_PROTECTED(x, y))))
154 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
155 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
158 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
159 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
161 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
162 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
166 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
169 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
170 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
174 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
175 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
177 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
178 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
180 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
181 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
183 #define PIG_CAN_ENTER_FIELD(e, x, y) \
184 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
186 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
187 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
188 IS_FOOD_PENGUIN(Feld[x][y])))
189 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
192 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
193 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
195 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
196 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
200 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
201 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
202 (CAN_MOVE_INTO_ACID(e) && \
203 Feld[x][y] == EL_ACID) || \
204 Feld[x][y] == EL_DIAMOND))
206 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
207 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
208 (CAN_MOVE_INTO_ACID(e) && \
209 Feld[x][y] == EL_ACID) || \
210 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
212 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
213 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
214 (CAN_MOVE_INTO_ACID(e) && \
215 Feld[x][y] == EL_ACID) || \
216 IS_AMOEBOID(Feld[x][y])))
218 #define PIG_CAN_ENTER_FIELD(e, x, y) \
219 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
220 (CAN_MOVE_INTO_ACID(e) && \
221 Feld[x][y] == EL_ACID) || \
222 IS_FOOD_PIG(Feld[x][y])))
224 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
225 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
226 (CAN_MOVE_INTO_ACID(e) && \
227 Feld[x][y] == EL_ACID) || \
228 IS_FOOD_PENGUIN(Feld[x][y]) || \
229 Feld[x][y] == EL_EXIT_OPEN))
231 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
232 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
233 (CAN_MOVE_INTO_ACID(e) && \
234 Feld[x][y] == EL_ACID)))
236 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
237 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
238 (CAN_MOVE_INTO_ACID(e) && \
239 Feld[x][y] == EL_ACID) || \
242 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
243 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
244 (CAN_MOVE_INTO_ACID(e) && \
245 Feld[x][y] == EL_ACID)))
249 #define GROUP_NR(e) ((e) - EL_GROUP_START)
250 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
251 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
252 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
254 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
255 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
258 #define CE_ENTER_FIELD_COND(e, x, y) \
259 (!IS_PLAYER(x, y) && \
260 (Feld[x][y] == EL_ACID || \
261 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
263 #define CE_ENTER_FIELD_COND(e, x, y) \
264 (!IS_PLAYER(x, y) && \
265 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
268 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
269 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
271 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
272 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
274 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
275 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
276 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
277 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
279 /* game button identifiers */
280 #define GAME_CTRL_ID_STOP 0
281 #define GAME_CTRL_ID_PAUSE 1
282 #define GAME_CTRL_ID_PLAY 2
283 #define SOUND_CTRL_ID_MUSIC 3
284 #define SOUND_CTRL_ID_LOOPS 4
285 #define SOUND_CTRL_ID_SIMPLE 5
287 #define NUM_GAME_BUTTONS 6
290 /* forward declaration for internal use */
292 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
293 static boolean MovePlayer(struct PlayerInfo *, int, int);
294 static void ScrollPlayer(struct PlayerInfo *, int);
295 static void ScrollScreen(struct PlayerInfo *, int);
297 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
299 static void InitBeltMovement(void);
300 static void CloseAllOpenTimegates(void);
301 static void CheckGravityMovement(struct PlayerInfo *);
302 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
303 static void KillHeroUnlessEnemyProtected(int, int);
304 static void KillHeroUnlessExplosionProtected(int, int);
306 static void TestIfPlayerTouchesCustomElement(int, int);
307 static void TestIfElementTouchesCustomElement(int, int);
308 static void TestIfElementHitsCustomElement(int, int, int);
310 static void TestIfElementSmashesCustomElement(int, int, int);
313 static void ChangeElement(int, int, int);
315 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
316 #define CheckTriggeredElementChange(x, y, e, ev) \
317 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
319 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
320 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
321 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
322 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
323 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
324 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
327 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
328 #define CheckElementChange(x, y, e, te, ev) \
329 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
330 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
331 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
332 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
333 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
334 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
335 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
337 static void PlayLevelSound(int, int, int);
338 static void PlayLevelSoundNearest(int, int, int);
339 static void PlayLevelSoundAction(int, int, int);
340 static void PlayLevelSoundElementAction(int, int, int, int);
341 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
342 static void PlayLevelSoundActionIfLoop(int, int, int);
343 static void StopLevelSoundActionIfLoop(int, int, int);
344 static void PlayLevelMusic();
346 static void MapGameButtons();
347 static void HandleGameButtons(struct GadgetInfo *);
349 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
352 /* ------------------------------------------------------------------------- */
353 /* definition of elements that automatically change to other elements after */
354 /* a specified time, eventually calling a function when changing */
355 /* ------------------------------------------------------------------------- */
357 /* forward declaration for changer functions */
358 static void InitBuggyBase(int x, int y);
359 static void WarnBuggyBase(int x, int y);
361 static void InitTrap(int x, int y);
362 static void ActivateTrap(int x, int y);
363 static void ChangeActiveTrap(int x, int y);
365 static void InitRobotWheel(int x, int y);
366 static void RunRobotWheel(int x, int y);
367 static void StopRobotWheel(int x, int y);
369 static void InitTimegateWheel(int x, int y);
370 static void RunTimegateWheel(int x, int y);
372 struct ChangingElementInfo
377 void (*pre_change_function)(int x, int y);
378 void (*change_function)(int x, int y);
379 void (*post_change_function)(int x, int y);
382 static struct ChangingElementInfo change_delay_list[] =
433 EL_SWITCHGATE_OPENING,
441 EL_SWITCHGATE_CLOSING,
442 EL_SWITCHGATE_CLOSED,
474 EL_ACID_SPLASH_RIGHT,
483 EL_SP_BUGGY_BASE_ACTIVATING,
490 EL_SP_BUGGY_BASE_ACTIVATING,
491 EL_SP_BUGGY_BASE_ACTIVE,
498 EL_SP_BUGGY_BASE_ACTIVE,
522 EL_ROBOT_WHEEL_ACTIVE,
530 EL_TIMEGATE_SWITCH_ACTIVE,
551 int push_delay_fixed, push_delay_random;
556 { EL_BALLOON, 0, 0 },
558 { EL_SOKOBAN_OBJECT, 2, 0 },
559 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
560 { EL_SATELLITE, 2, 0 },
561 { EL_SP_DISK_YELLOW, 2, 0 },
563 { EL_UNDEFINED, 0, 0 },
571 move_stepsize_list[] =
573 { EL_AMOEBA_DROP, 2 },
574 { EL_AMOEBA_DROPPING, 2 },
575 { EL_QUICKSAND_FILLING, 1 },
576 { EL_QUICKSAND_EMPTYING, 1 },
577 { EL_MAGIC_WALL_FILLING, 2 },
578 { EL_BD_MAGIC_WALL_FILLING, 2 },
579 { EL_MAGIC_WALL_EMPTYING, 2 },
580 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
590 collect_count_list[] =
593 { EL_BD_DIAMOND, 1 },
594 { EL_EMERALD_YELLOW, 1 },
595 { EL_EMERALD_RED, 1 },
596 { EL_EMERALD_PURPLE, 1 },
598 { EL_SP_INFOTRON, 1 },
610 access_direction_list[] =
612 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
613 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
614 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
615 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
616 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
617 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
618 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
619 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
620 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
621 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
622 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
624 { EL_SP_PORT_LEFT, MV_RIGHT },
625 { EL_SP_PORT_RIGHT, MV_LEFT },
626 { EL_SP_PORT_UP, MV_DOWN },
627 { EL_SP_PORT_DOWN, MV_UP },
628 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
629 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
630 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
631 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
632 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
633 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
634 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
636 { EL_UNDEFINED, MV_NO_MOVING }
639 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
641 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
642 CH_EVENT_BIT(CE_DELAY))
643 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
644 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
645 IS_JUST_CHANGING(x, y))
647 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
650 void GetPlayerConfig()
652 if (!audio.sound_available)
653 setup.sound_simple = FALSE;
655 if (!audio.loops_available)
656 setup.sound_loops = FALSE;
658 if (!audio.music_available)
659 setup.sound_music = FALSE;
661 if (!video.fullscreen_available)
662 setup.fullscreen = FALSE;
664 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
666 SetAudioMode(setup.sound);
670 static int getBeltNrFromBeltElement(int element)
672 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
673 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
674 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
677 static int getBeltNrFromBeltActiveElement(int element)
679 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
680 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
681 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
684 static int getBeltNrFromBeltSwitchElement(int element)
686 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
687 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
688 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
691 static int getBeltDirNrFromBeltSwitchElement(int element)
693 static int belt_base_element[4] =
695 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
696 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
697 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
698 EL_CONVEYOR_BELT_4_SWITCH_LEFT
701 int belt_nr = getBeltNrFromBeltSwitchElement(element);
702 int belt_dir_nr = element - belt_base_element[belt_nr];
704 return (belt_dir_nr % 3);
707 static int getBeltDirFromBeltSwitchElement(int element)
709 static int belt_move_dir[3] =
716 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
718 return belt_move_dir[belt_dir_nr];
721 static void InitPlayerField(int x, int y, int element, boolean init_game)
723 if (element == EL_SP_MURPHY)
727 if (stored_player[0].present)
729 Feld[x][y] = EL_SP_MURPHY_CLONE;
735 stored_player[0].use_murphy_graphic = TRUE;
738 Feld[x][y] = EL_PLAYER_1;
744 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
745 int jx = player->jx, jy = player->jy;
747 player->present = TRUE;
749 player->block_last_field = (element == EL_SP_MURPHY ?
750 level.sp_block_last_field :
751 level.block_last_field);
753 if (!options.network || player->connected)
755 player->active = TRUE;
757 /* remove potentially duplicate players */
758 if (StorePlayer[jx][jy] == Feld[x][y])
759 StorePlayer[jx][jy] = 0;
761 StorePlayer[x][y] = Feld[x][y];
765 printf("Player %d activated.\n", player->element_nr);
766 printf("[Local player is %d and currently %s.]\n",
767 local_player->element_nr,
768 local_player->active ? "active" : "not active");
772 Feld[x][y] = EL_EMPTY;
774 player->jx = player->last_jx = x;
775 player->jy = player->last_jy = y;
779 static void InitField(int x, int y, boolean init_game)
781 int element = Feld[x][y];
790 InitPlayerField(x, y, element, init_game);
793 case EL_SOKOBAN_FIELD_PLAYER:
794 element = Feld[x][y] = EL_PLAYER_1;
795 InitField(x, y, init_game);
797 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
798 InitField(x, y, init_game);
801 case EL_SOKOBAN_FIELD_EMPTY:
802 local_player->sokobanfields_still_needed++;
806 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
807 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
808 else if (x > 0 && Feld[x-1][y] == EL_ACID)
809 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
810 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
811 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
812 else if (y > 0 && Feld[x][y-1] == EL_ACID)
813 Feld[x][y] = EL_ACID_POOL_BOTTOM;
814 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
815 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
823 case EL_SPACESHIP_RIGHT:
824 case EL_SPACESHIP_UP:
825 case EL_SPACESHIP_LEFT:
826 case EL_SPACESHIP_DOWN:
828 case EL_BD_BUTTERFLY_RIGHT:
829 case EL_BD_BUTTERFLY_UP:
830 case EL_BD_BUTTERFLY_LEFT:
831 case EL_BD_BUTTERFLY_DOWN:
832 case EL_BD_BUTTERFLY:
833 case EL_BD_FIREFLY_RIGHT:
834 case EL_BD_FIREFLY_UP:
835 case EL_BD_FIREFLY_LEFT:
836 case EL_BD_FIREFLY_DOWN:
838 case EL_PACMAN_RIGHT:
862 if (y == lev_fieldy - 1)
864 Feld[x][y] = EL_AMOEBA_GROWING;
865 Store[x][y] = EL_AMOEBA_WET;
869 case EL_DYNAMITE_ACTIVE:
870 case EL_SP_DISK_RED_ACTIVE:
871 case EL_DYNABOMB_PLAYER_1_ACTIVE:
872 case EL_DYNABOMB_PLAYER_2_ACTIVE:
873 case EL_DYNABOMB_PLAYER_3_ACTIVE:
874 case EL_DYNABOMB_PLAYER_4_ACTIVE:
879 local_player->lights_still_needed++;
883 local_player->friends_still_needed++;
888 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
893 Feld[x][y] = EL_EMPTY;
898 case EL_EM_KEY_1_FILE:
899 Feld[x][y] = EL_EM_KEY_1;
901 case EL_EM_KEY_2_FILE:
902 Feld[x][y] = EL_EM_KEY_2;
904 case EL_EM_KEY_3_FILE:
905 Feld[x][y] = EL_EM_KEY_3;
907 case EL_EM_KEY_4_FILE:
908 Feld[x][y] = EL_EM_KEY_4;
912 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
913 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
914 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
915 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
916 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
917 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
918 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
919 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
920 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
921 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
922 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
923 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
926 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
927 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
928 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
930 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
932 game.belt_dir[belt_nr] = belt_dir;
933 game.belt_dir_nr[belt_nr] = belt_dir_nr;
935 else /* more than one switch -- set it like the first switch */
937 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
942 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
944 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
947 case EL_LIGHT_SWITCH_ACTIVE:
949 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
953 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
955 else if (IS_GROUP_ELEMENT(element))
957 struct ElementGroupInfo *group = element_info[element].group;
958 int last_anim_random_frame = gfx.anim_random_frame;
961 if (group->choice_mode == ANIM_RANDOM)
962 gfx.anim_random_frame = RND(group->num_elements_resolved);
964 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
965 group->choice_mode, 0,
968 if (group->choice_mode == ANIM_RANDOM)
969 gfx.anim_random_frame = last_anim_random_frame;
973 Feld[x][y] = group->element_resolved[element_pos];
975 InitField(x, y, init_game);
981 static inline void InitField_WithBug1(int x, int y, boolean init_game)
983 InitField(x, y, init_game);
985 /* not needed to call InitMovDir() -- already done by InitField()! */
986 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
987 CAN_MOVE(Feld[x][y]))
991 static inline void InitField_WithBug2(int x, int y, boolean init_game)
993 int old_element = Feld[x][y];
995 InitField(x, y, init_game);
997 /* not needed to call InitMovDir() -- already done by InitField()! */
998 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
999 CAN_MOVE(old_element) &&
1000 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1003 /* this case is in fact a combination of not less than three bugs:
1004 first, it calls InitMovDir() for elements that can move, although this is
1005 already done by InitField(); then, it checks the element that was at this
1006 field _before_ the call to InitField() (which can change it); lastly, it
1007 was not called for "mole with direction" elements, which were treated as
1008 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1012 inline void DrawGameValue_Emeralds(int value)
1014 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1017 inline void DrawGameValue_Dynamite(int value)
1019 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1022 inline void DrawGameValue_Keys(struct PlayerInfo *player)
1026 for (i = 0; i < MAX_KEYS; i++)
1028 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1029 el2edimg(EL_KEY_1 + i));
1032 inline void DrawGameValue_Score(int value)
1034 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1037 inline void DrawGameValue_Time(int value)
1040 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1042 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1045 inline void DrawGameValue_Level(int value)
1048 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1051 /* misuse area for displaying emeralds to draw bigger level number */
1052 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1053 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1055 /* now copy it to the area for displaying level number */
1056 BlitBitmap(drawto, drawto,
1057 DX_EMERALDS, DY_EMERALDS + 1,
1058 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1059 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1060 DX_LEVEL - 1, DY_LEVEL + 1);
1062 /* restore the area for displaying emeralds */
1063 DrawGameValue_Emeralds(local_player->gems_still_needed);
1065 /* yes, this is all really ugly :-) */
1069 void DrawGameDoorValues()
1073 DrawGameValue_Level(level_nr);
1075 for (i = 0; i < MAX_PLAYERS; i++)
1076 DrawGameValue_Keys(&stored_player[i]);
1078 DrawGameValue_Emeralds(local_player->gems_still_needed);
1079 DrawGameValue_Dynamite(local_player->inventory_size);
1080 DrawGameValue_Score(local_player->score);
1081 DrawGameValue_Time(TimeLeft);
1084 static void resolve_group_element(int group_element, int recursion_depth)
1086 static int group_nr;
1087 static struct ElementGroupInfo *group;
1088 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1091 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1093 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1094 group_element - EL_GROUP_START + 1);
1096 /* replace element which caused too deep recursion by question mark */
1097 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1102 if (recursion_depth == 0) /* initialization */
1104 group = element_info[group_element].group;
1105 group_nr = group_element - EL_GROUP_START;
1107 group->num_elements_resolved = 0;
1108 group->choice_pos = 0;
1111 for (i = 0; i < actual_group->num_elements; i++)
1113 int element = actual_group->element[i];
1115 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1118 if (IS_GROUP_ELEMENT(element))
1119 resolve_group_element(element, recursion_depth + 1);
1122 group->element_resolved[group->num_elements_resolved++] = element;
1123 element_info[element].in_group[group_nr] = TRUE;
1128 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1130 printf("::: group %d: %d resolved elements\n",
1131 group_element - EL_GROUP_START, group->num_elements_resolved);
1132 for (i = 0; i < group->num_elements_resolved; i++)
1133 printf("::: - %d ['%s']\n", group->element_resolved[i],
1134 element_info[group->element_resolved[i]].token_name);
1141 =============================================================================
1143 -----------------------------------------------------------------------------
1144 initialize game engine due to level / tape version number
1145 =============================================================================
1148 static void InitGameEngine()
1152 /* set game engine from tape file when re-playing, else from level file */
1153 game.engine_version = (tape.playing ? tape.engine_version :
1154 level.game_version);
1156 /* dynamically adjust element properties according to game engine version */
1157 InitElementPropertiesEngine(game.engine_version);
1160 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1161 printf(" tape version == %06d [%s] [file: %06d]\n",
1162 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1164 printf(" => game.engine_version == %06d\n", game.engine_version);
1167 /* ---------- recursively resolve group elements ------------------------- */
1169 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1170 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1171 element_info[i].in_group[j] = FALSE;
1173 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1174 resolve_group_element(EL_GROUP_START + i, 0);
1176 /* ---------- initialize player's initial move delay --------------------- */
1178 /* dynamically adjust player properties according to game engine version */
1179 game.initial_move_delay =
1180 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1181 INITIAL_MOVE_DELAY_OFF);
1183 /* dynamically adjust player properties according to level information */
1184 game.initial_move_delay_value =
1185 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1187 /* ---------- initialize player's initial push delay --------------------- */
1189 /* dynamically adjust player properties according to game engine version */
1190 game.initial_push_delay_value =
1191 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1193 /* ---------- initialize changing elements ------------------------------- */
1195 /* initialize changing elements information */
1196 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1198 struct ElementInfo *ei = &element_info[i];
1200 /* this pointer might have been changed in the level editor */
1201 ei->change = &ei->change_page[0];
1203 if (!IS_CUSTOM_ELEMENT(i))
1205 ei->change->target_element = EL_EMPTY_SPACE;
1206 ei->change->delay_fixed = 0;
1207 ei->change->delay_random = 0;
1208 ei->change->delay_frames = 1;
1211 ei->change_events = CE_BITMASK_DEFAULT;
1212 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1214 ei->event_page_nr[j] = 0;
1215 ei->event_page[j] = &ei->change_page[0];
1219 /* add changing elements from pre-defined list */
1220 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1222 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1223 struct ElementInfo *ei = &element_info[ch_delay->element];
1225 ei->change->target_element = ch_delay->target_element;
1226 ei->change->delay_fixed = ch_delay->change_delay;
1228 ei->change->pre_change_function = ch_delay->pre_change_function;
1229 ei->change->change_function = ch_delay->change_function;
1230 ei->change->post_change_function = ch_delay->post_change_function;
1232 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1235 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1240 /* add change events from custom element configuration */
1241 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1243 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1245 for (j = 0; j < ei->num_change_pages; j++)
1247 if (!ei->change_page[j].can_change)
1250 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1252 /* only add event page for the first page found with this event */
1253 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1254 !(ei->change_events & CH_EVENT_BIT(k)))
1256 ei->change_events |= CH_EVENT_BIT(k);
1257 ei->event_page_nr[k] = j;
1258 ei->event_page[k] = &ei->change_page[j];
1266 /* add change events from custom element configuration */
1267 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1269 int element = EL_CUSTOM_START + i;
1271 /* only add custom elements that change after fixed/random frame delay */
1272 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1273 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1277 /* ---------- initialize run-time trigger player and element ------------- */
1279 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1281 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1283 for (j = 0; j < ei->num_change_pages; j++)
1285 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1286 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1290 /* ---------- initialize trigger events ---------------------------------- */
1292 /* initialize trigger events information */
1293 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1294 trigger_events[i] = EP_BITMASK_DEFAULT;
1297 /* add trigger events from element change event properties */
1298 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1300 struct ElementInfo *ei = &element_info[i];
1302 for (j = 0; j < ei->num_change_pages; j++)
1304 if (!ei->change_page[j].can_change)
1307 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1309 int trigger_element = ei->change_page[j].trigger_element;
1311 if (IS_GROUP_ELEMENT(trigger_element))
1313 struct ElementGroupInfo *group = element_info[trigger_element].group;
1315 for (k = 0; k < group->num_elements_resolved; k++)
1316 trigger_events[group->element_resolved[k]]
1317 |= ei->change_page[j].events;
1320 trigger_events[trigger_element] |= ei->change_page[j].events;
1325 /* add trigger events from element change event properties */
1326 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1327 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1328 trigger_events[element_info[i].change->trigger_element] |=
1329 element_info[i].change->events;
1332 /* ---------- initialize push delay -------------------------------------- */
1334 /* initialize push delay values to default */
1335 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1337 if (!IS_CUSTOM_ELEMENT(i))
1339 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1340 element_info[i].push_delay_random = game.default_push_delay_random;
1344 /* set push delay value for certain elements from pre-defined list */
1345 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1347 int e = push_delay_list[i].element;
1349 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1350 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1353 /* set push delay value for Supaplex elements for newer engine versions */
1354 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1356 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1358 if (IS_SP_ELEMENT(i))
1360 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1361 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1366 /* ---------- initialize move stepsize ----------------------------------- */
1368 /* initialize move stepsize values to default */
1369 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1370 if (!IS_CUSTOM_ELEMENT(i))
1371 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1373 /* set move stepsize value for certain elements from pre-defined list */
1374 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1376 int e = move_stepsize_list[i].element;
1378 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1382 /* ---------- initialize move dig/leave ---------------------------------- */
1384 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1386 element_info[i].can_leave_element = FALSE;
1387 element_info[i].can_leave_element_last = FALSE;
1391 /* ---------- initialize gem count --------------------------------------- */
1393 /* initialize gem count values for each element */
1394 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1395 if (!IS_CUSTOM_ELEMENT(i))
1396 element_info[i].collect_count = 0;
1398 /* add gem count values for all elements from pre-defined list */
1399 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1400 element_info[collect_count_list[i].element].collect_count =
1401 collect_count_list[i].count;
1403 /* ---------- initialize access direction -------------------------------- */
1405 /* initialize access direction values to default (access from every side) */
1406 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1407 if (!IS_CUSTOM_ELEMENT(i))
1408 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1410 /* set access direction value for certain elements from pre-defined list */
1411 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1412 element_info[access_direction_list[i].element].access_direction =
1413 access_direction_list[i].direction;
1418 =============================================================================
1420 -----------------------------------------------------------------------------
1421 initialize and start new game
1422 =============================================================================
1427 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1428 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1429 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1436 #if USE_NEW_AMOEBA_CODE
1437 printf("Using new amoeba code.\n");
1439 printf("Using old amoeba code.\n");
1444 /* don't play tapes over network */
1445 network_playing = (options.network && !tape.playing);
1447 for (i = 0; i < MAX_PLAYERS; i++)
1449 struct PlayerInfo *player = &stored_player[i];
1451 player->index_nr = i;
1452 player->index_bit = (1 << i);
1453 player->element_nr = EL_PLAYER_1 + i;
1455 player->present = FALSE;
1456 player->active = FALSE;
1459 player->effective_action = 0;
1460 player->programmed_action = 0;
1463 player->gems_still_needed = level.gems_needed;
1464 player->sokobanfields_still_needed = 0;
1465 player->lights_still_needed = 0;
1466 player->friends_still_needed = 0;
1468 for (j = 0; j < MAX_KEYS; j++)
1469 player->key[j] = FALSE;
1471 player->dynabomb_count = 0;
1472 player->dynabomb_size = 1;
1473 player->dynabombs_left = 0;
1474 player->dynabomb_xl = FALSE;
1476 player->MovDir = MV_NO_MOVING;
1479 player->GfxDir = MV_NO_MOVING;
1480 player->GfxAction = ACTION_DEFAULT;
1482 player->StepFrame = 0;
1484 player->use_murphy_graphic = FALSE;
1486 player->block_last_field = FALSE;
1487 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1489 player->actual_frame_counter = 0;
1491 player->step_counter = 0;
1493 player->last_move_dir = MV_NO_MOVING;
1495 player->is_waiting = FALSE;
1496 player->is_moving = FALSE;
1497 player->is_auto_moving = FALSE;
1498 player->is_digging = FALSE;
1499 player->is_snapping = FALSE;
1500 player->is_collecting = FALSE;
1501 player->is_pushing = FALSE;
1502 player->is_switching = FALSE;
1503 player->is_dropping = FALSE;
1505 player->is_bored = FALSE;
1506 player->is_sleeping = FALSE;
1508 player->frame_counter_bored = -1;
1509 player->frame_counter_sleeping = -1;
1511 player->anim_delay_counter = 0;
1512 player->post_delay_counter = 0;
1514 player->action_waiting = ACTION_DEFAULT;
1515 player->last_action_waiting = ACTION_DEFAULT;
1516 player->special_action_bored = ACTION_DEFAULT;
1517 player->special_action_sleeping = ACTION_DEFAULT;
1519 player->num_special_action_bored = 0;
1520 player->num_special_action_sleeping = 0;
1522 /* determine number of special actions for bored and sleeping animation */
1523 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1525 boolean found = FALSE;
1527 for (k = 0; k < NUM_DIRECTIONS; k++)
1528 if (el_act_dir2img(player->element_nr, j, k) !=
1529 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1533 player->num_special_action_bored++;
1537 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1539 boolean found = FALSE;
1541 for (k = 0; k < NUM_DIRECTIONS; k++)
1542 if (el_act_dir2img(player->element_nr, j, k) !=
1543 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1547 player->num_special_action_sleeping++;
1552 player->switch_x = -1;
1553 player->switch_y = -1;
1555 player->show_envelope = 0;
1557 player->move_delay = game.initial_move_delay;
1558 player->move_delay_value = game.initial_move_delay_value;
1560 player->move_delay_reset_counter = 0;
1562 player->push_delay = 0;
1563 player->push_delay_value = game.initial_push_delay_value;
1565 player->drop_delay = 0;
1567 player->last_jx = player->last_jy = 0;
1568 player->jx = player->jy = 0;
1570 player->shield_normal_time_left = 0;
1571 player->shield_deadly_time_left = 0;
1573 player->inventory_infinite_element = EL_UNDEFINED;
1574 player->inventory_size = 0;
1576 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1577 SnapField(player, 0, 0);
1579 player->LevelSolved = FALSE;
1580 player->GameOver = FALSE;
1583 network_player_action_received = FALSE;
1585 #if defined(PLATFORM_UNIX)
1586 /* initial null action */
1587 if (network_playing)
1588 SendToServer_MovePlayer(MV_NO_MOVING);
1596 TimeLeft = level.time;
1599 ScreenMovDir = MV_NO_MOVING;
1603 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1605 AllPlayersGone = FALSE;
1607 game.yamyam_content_nr = 0;
1608 game.magic_wall_active = FALSE;
1609 game.magic_wall_time_left = 0;
1610 game.light_time_left = 0;
1611 game.timegate_time_left = 0;
1612 game.switchgate_pos = 0;
1613 game.balloon_dir = MV_NO_MOVING;
1614 game.gravity = level.initial_gravity;
1615 game.explosions_delayed = TRUE;
1617 game.envelope_active = FALSE;
1619 for (i = 0; i < NUM_BELTS; i++)
1621 game.belt_dir[i] = MV_NO_MOVING;
1622 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1625 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1626 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1628 for (x = 0; x < lev_fieldx; x++)
1630 for (y = 0; y < lev_fieldy; y++)
1632 Feld[x][y] = level.field[x][y];
1633 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1634 ChangeDelay[x][y] = 0;
1635 ChangePage[x][y] = -1;
1636 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1638 WasJustMoving[x][y] = 0;
1639 WasJustFalling[x][y] = 0;
1640 CheckCollision[x][y] = 0;
1642 Pushed[x][y] = FALSE;
1644 Changed[x][y] = CE_BITMASK_DEFAULT;
1645 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1647 ExplodePhase[x][y] = 0;
1648 ExplodeDelay[x][y] = 0;
1649 ExplodeField[x][y] = EX_TYPE_NONE;
1651 RunnerVisit[x][y] = 0;
1652 PlayerVisit[x][y] = 0;
1655 GfxRandom[x][y] = INIT_GFX_RANDOM();
1656 GfxElement[x][y] = EL_UNDEFINED;
1657 GfxAction[x][y] = ACTION_DEFAULT;
1658 GfxDir[x][y] = MV_NO_MOVING;
1662 for (y = 0; y < lev_fieldy; y++)
1664 for (x = 0; x < lev_fieldx; x++)
1666 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1668 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1670 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1673 InitField(x, y, TRUE);
1679 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1680 emulate_sb ? EMU_SOKOBAN :
1681 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1683 /* initialize explosion and ignition delay */
1684 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1686 if (!IS_CUSTOM_ELEMENT(i))
1689 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1690 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1691 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1692 int last_phase = (num_phase + 1) * delay;
1693 int half_phase = (num_phase / 2) * delay;
1695 element_info[i].explosion_delay = last_phase - 1;
1696 element_info[i].ignition_delay = half_phase;
1699 if (i == EL_BLACK_ORB)
1700 element_info[i].ignition_delay = 0;
1702 if (i == EL_BLACK_ORB)
1703 element_info[i].ignition_delay = 1;
1708 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1709 element_info[i].explosion_delay = 1;
1711 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1712 element_info[i].ignition_delay = 1;
1716 /* correct non-moving belts to start moving left */
1717 for (i = 0; i < NUM_BELTS; i++)
1718 if (game.belt_dir[i] == MV_NO_MOVING)
1719 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1721 /* check if any connected player was not found in playfield */
1722 for (i = 0; i < MAX_PLAYERS; i++)
1724 struct PlayerInfo *player = &stored_player[i];
1726 if (player->connected && !player->present)
1728 for (j = 0; j < MAX_PLAYERS; j++)
1730 struct PlayerInfo *some_player = &stored_player[j];
1731 int jx = some_player->jx, jy = some_player->jy;
1733 /* assign first free player found that is present in the playfield */
1734 if (some_player->present && !some_player->connected)
1736 player->present = TRUE;
1737 player->active = TRUE;
1739 some_player->present = FALSE;
1740 some_player->active = FALSE;
1743 player->element_nr = some_player->element_nr;
1746 StorePlayer[jx][jy] = player->element_nr;
1747 player->jx = player->last_jx = jx;
1748 player->jy = player->last_jy = jy;
1758 /* when playing a tape, eliminate all players which do not participate */
1760 for (i = 0; i < MAX_PLAYERS; i++)
1762 if (stored_player[i].active && !tape.player_participates[i])
1764 struct PlayerInfo *player = &stored_player[i];
1765 int jx = player->jx, jy = player->jy;
1767 player->active = FALSE;
1768 StorePlayer[jx][jy] = 0;
1769 Feld[jx][jy] = EL_EMPTY;
1773 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1775 /* when in single player mode, eliminate all but the first active player */
1777 for (i = 0; i < MAX_PLAYERS; i++)
1779 if (stored_player[i].active)
1781 for (j = i + 1; j < MAX_PLAYERS; j++)
1783 if (stored_player[j].active)
1785 struct PlayerInfo *player = &stored_player[j];
1786 int jx = player->jx, jy = player->jy;
1788 player->active = FALSE;
1789 player->present = FALSE;
1791 StorePlayer[jx][jy] = 0;
1792 Feld[jx][jy] = EL_EMPTY;
1799 /* when recording the game, store which players take part in the game */
1802 for (i = 0; i < MAX_PLAYERS; i++)
1803 if (stored_player[i].active)
1804 tape.player_participates[i] = TRUE;
1809 for (i = 0; i < MAX_PLAYERS; i++)
1811 struct PlayerInfo *player = &stored_player[i];
1813 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1818 if (local_player == player)
1819 printf("Player %d is local player.\n", i+1);
1823 if (BorderElement == EL_EMPTY)
1826 SBX_Right = lev_fieldx - SCR_FIELDX;
1828 SBY_Lower = lev_fieldy - SCR_FIELDY;
1833 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1835 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1838 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1839 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1841 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1842 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1844 /* if local player not found, look for custom element that might create
1845 the player (make some assumptions about the right custom element) */
1846 if (!local_player->present)
1848 int start_x = 0, start_y = 0;
1849 int found_rating = 0;
1850 int found_element = EL_UNDEFINED;
1852 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1854 int element = Feld[x][y];
1859 if (!IS_CUSTOM_ELEMENT(element))
1862 if (CAN_CHANGE(element))
1864 for (i = 0; i < element_info[element].num_change_pages; i++)
1866 content = element_info[element].change_page[i].target_element;
1867 is_player = ELEM_IS_PLAYER(content);
1869 if (is_player && (found_rating < 3 || element < found_element))
1875 found_element = element;
1880 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1882 content = element_info[element].content[xx][yy];
1883 is_player = ELEM_IS_PLAYER(content);
1885 if (is_player && (found_rating < 2 || element < found_element))
1887 start_x = x + xx - 1;
1888 start_y = y + yy - 1;
1891 found_element = element;
1894 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_content[xx][yy];
1900 is_player = ELEM_IS_PLAYER(content);
1902 if (is_player && (found_rating < 1 || element < found_element))
1904 start_x = x + xx - 1;
1905 start_y = y + yy - 1;
1908 found_element = element;
1914 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1915 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1918 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1919 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1925 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1926 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1927 local_player->jx - MIDPOSX);
1929 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1930 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1931 local_player->jy - MIDPOSY);
1933 scroll_x = SBX_Left;
1934 scroll_y = SBY_Upper;
1935 if (local_player->jx >= SBX_Left + MIDPOSX)
1936 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1937 local_player->jx - MIDPOSX :
1939 if (local_player->jy >= SBY_Upper + MIDPOSY)
1940 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1941 local_player->jy - MIDPOSY :
1946 CloseDoor(DOOR_CLOSE_1);
1951 /* after drawing the level, correct some elements */
1952 if (game.timegate_time_left == 0)
1953 CloseAllOpenTimegates();
1955 if (setup.soft_scrolling)
1956 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1958 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1961 /* copy default game door content to main double buffer */
1962 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1963 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1965 DrawGameDoorValues();
1969 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1970 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1971 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1975 /* copy actual game door content to door double buffer for OpenDoor() */
1976 BlitBitmap(drawto, bitmap_db_door,
1977 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1979 OpenDoor(DOOR_OPEN_ALL);
1981 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1983 if (setup.sound_music)
1986 KeyboardAutoRepeatOffUnlessAutoplay();
1990 for (i = 0; i < MAX_PLAYERS; i++)
1991 printf("Player %d %sactive.\n",
1992 i + 1, (stored_player[i].active ? "" : "not "));
1996 printf("::: starting game [%d]\n", FrameCounter);
2000 void InitMovDir(int x, int y)
2002 int i, element = Feld[x][y];
2003 static int xy[4][2] =
2010 static int direction[3][4] =
2012 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2013 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2014 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2023 Feld[x][y] = EL_BUG;
2024 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2027 case EL_SPACESHIP_RIGHT:
2028 case EL_SPACESHIP_UP:
2029 case EL_SPACESHIP_LEFT:
2030 case EL_SPACESHIP_DOWN:
2031 Feld[x][y] = EL_SPACESHIP;
2032 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2035 case EL_BD_BUTTERFLY_RIGHT:
2036 case EL_BD_BUTTERFLY_UP:
2037 case EL_BD_BUTTERFLY_LEFT:
2038 case EL_BD_BUTTERFLY_DOWN:
2039 Feld[x][y] = EL_BD_BUTTERFLY;
2040 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2043 case EL_BD_FIREFLY_RIGHT:
2044 case EL_BD_FIREFLY_UP:
2045 case EL_BD_FIREFLY_LEFT:
2046 case EL_BD_FIREFLY_DOWN:
2047 Feld[x][y] = EL_BD_FIREFLY;
2048 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2051 case EL_PACMAN_RIGHT:
2053 case EL_PACMAN_LEFT:
2054 case EL_PACMAN_DOWN:
2055 Feld[x][y] = EL_PACMAN;
2056 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2059 case EL_SP_SNIKSNAK:
2060 MovDir[x][y] = MV_UP;
2063 case EL_SP_ELECTRON:
2064 MovDir[x][y] = MV_LEFT;
2071 Feld[x][y] = EL_MOLE;
2072 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2076 if (IS_CUSTOM_ELEMENT(element))
2078 struct ElementInfo *ei = &element_info[element];
2079 int move_direction_initial = ei->move_direction_initial;
2080 int move_pattern = ei->move_pattern;
2082 if (move_direction_initial == MV_START_PREVIOUS)
2084 if (MovDir[x][y] != MV_NO_MOVING)
2087 move_direction_initial = MV_START_AUTOMATIC;
2090 if (move_direction_initial == MV_START_RANDOM)
2091 MovDir[x][y] = 1 << RND(4);
2092 else if (move_direction_initial & MV_ANY_DIRECTION)
2093 MovDir[x][y] = move_direction_initial;
2094 else if (move_pattern == MV_ALL_DIRECTIONS ||
2095 move_pattern == MV_TURNING_LEFT ||
2096 move_pattern == MV_TURNING_RIGHT ||
2097 move_pattern == MV_TURNING_LEFT_RIGHT ||
2098 move_pattern == MV_TURNING_RIGHT_LEFT ||
2099 move_pattern == MV_TURNING_RANDOM)
2100 MovDir[x][y] = 1 << RND(4);
2101 else if (move_pattern == MV_HORIZONTAL)
2102 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2103 else if (move_pattern == MV_VERTICAL)
2104 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2105 else if (move_pattern & MV_ANY_DIRECTION)
2106 MovDir[x][y] = element_info[element].move_pattern;
2107 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2108 move_pattern == MV_ALONG_RIGHT_SIDE)
2111 /* use random direction as default start direction */
2112 if (game.engine_version >= VERSION_IDENT(3,1,0,2))
2113 MovDir[x][y] = 1 << RND(4);
2116 for (i = 0; i < NUM_DIRECTIONS; i++)
2118 int x1 = x + xy[i][0];
2119 int y1 = y + xy[i][1];
2121 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2123 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2124 MovDir[x][y] = direction[0][i];
2126 MovDir[x][y] = direction[1][i];
2135 MovDir[x][y] = 1 << RND(4);
2137 if (element != EL_BUG &&
2138 element != EL_SPACESHIP &&
2139 element != EL_BD_BUTTERFLY &&
2140 element != EL_BD_FIREFLY)
2143 for (i = 0; i < NUM_DIRECTIONS; i++)
2145 int x1 = x + xy[i][0];
2146 int y1 = y + xy[i][1];
2148 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2150 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2152 MovDir[x][y] = direction[0][i];
2155 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2156 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2158 MovDir[x][y] = direction[1][i];
2167 GfxDir[x][y] = MovDir[x][y];
2170 void InitAmoebaNr(int x, int y)
2173 int group_nr = AmoebeNachbarNr(x, y);
2177 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2179 if (AmoebaCnt[i] == 0)
2187 AmoebaNr[x][y] = group_nr;
2188 AmoebaCnt[group_nr]++;
2189 AmoebaCnt2[group_nr]++;
2195 boolean raise_level = FALSE;
2197 if (local_player->MovPos)
2201 if (tape.auto_play) /* tape might already be stopped here */
2202 tape.auto_play_level_solved = TRUE;
2204 if (tape.playing && tape.auto_play)
2205 tape.auto_play_level_solved = TRUE;
2208 local_player->LevelSolved = FALSE;
2210 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2214 if (!tape.playing && setup.sound_loops)
2215 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2216 SND_CTRL_PLAY_LOOP);
2218 while (TimeLeft > 0)
2220 if (!tape.playing && !setup.sound_loops)
2221 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2222 if (TimeLeft > 0 && !(TimeLeft % 10))
2223 RaiseScore(level.score[SC_TIME_BONUS]);
2224 if (TimeLeft > 100 && !(TimeLeft % 10))
2229 DrawGameValue_Time(TimeLeft);
2237 if (!tape.playing && setup.sound_loops)
2238 StopSound(SND_GAME_LEVELTIME_BONUS);
2240 else if (level.time == 0) /* level without time limit */
2242 if (!tape.playing && setup.sound_loops)
2243 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2244 SND_CTRL_PLAY_LOOP);
2246 while (TimePlayed < 999)
2248 if (!tape.playing && !setup.sound_loops)
2249 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2250 if (TimePlayed < 999 && !(TimePlayed % 10))
2251 RaiseScore(level.score[SC_TIME_BONUS]);
2252 if (TimePlayed < 900 && !(TimePlayed % 10))
2257 DrawGameValue_Time(TimePlayed);
2265 if (!tape.playing && setup.sound_loops)
2266 StopSound(SND_GAME_LEVELTIME_BONUS);
2269 /* close exit door after last player */
2270 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2271 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2273 int element = Feld[ExitX][ExitY];
2275 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2276 EL_SP_EXIT_CLOSING);
2278 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2281 /* Hero disappears */
2282 DrawLevelField(ExitX, ExitY);
2288 CloseDoor(DOOR_CLOSE_1);
2293 SaveTape(tape.level_nr); /* Ask to save tape */
2296 if (level_nr == leveldir_current->handicap_level)
2298 leveldir_current->handicap_level++;
2299 SaveLevelSetup_SeriesInfo();
2302 if (level_editor_test_game)
2303 local_player->score = -1; /* no highscore when playing from editor */
2304 else if (level_nr < leveldir_current->last_level)
2305 raise_level = TRUE; /* advance to next level */
2307 if ((hi_pos = NewHiScore()) >= 0)
2309 game_status = GAME_MODE_SCORES;
2310 DrawHallOfFame(hi_pos);
2319 game_status = GAME_MODE_MAIN;
2336 LoadScore(level_nr);
2338 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2339 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2342 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2344 if (local_player->score > highscore[k].Score)
2346 /* player has made it to the hall of fame */
2348 if (k < MAX_SCORE_ENTRIES - 1)
2350 int m = MAX_SCORE_ENTRIES - 1;
2353 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2354 if (!strcmp(setup.player_name, highscore[l].Name))
2356 if (m == k) /* player's new highscore overwrites his old one */
2360 for (l = m; l > k; l--)
2362 strcpy(highscore[l].Name, highscore[l - 1].Name);
2363 highscore[l].Score = highscore[l - 1].Score;
2370 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2371 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2372 highscore[k].Score = local_player->score;
2378 else if (!strncmp(setup.player_name, highscore[k].Name,
2379 MAX_PLAYER_NAME_LEN))
2380 break; /* player already there with a higher score */
2386 SaveScore(level_nr);
2391 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2393 if (player->GfxAction != action || player->GfxDir != dir)
2396 printf("Player frame reset! (%d => %d, %d => %d)\n",
2397 player->GfxAction, action, player->GfxDir, dir);
2400 player->GfxAction = action;
2401 player->GfxDir = dir;
2403 player->StepFrame = 0;
2407 static void ResetRandomAnimationValue(int x, int y)
2409 GfxRandom[x][y] = INIT_GFX_RANDOM();
2412 static void ResetGfxAnimation(int x, int y)
2415 GfxAction[x][y] = ACTION_DEFAULT;
2416 GfxDir[x][y] = MovDir[x][y];
2419 void InitMovingField(int x, int y, int direction)
2421 int element = Feld[x][y];
2422 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2423 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2427 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2428 ResetGfxAnimation(x, y);
2430 MovDir[newx][newy] = MovDir[x][y] = direction;
2431 GfxDir[x][y] = direction;
2433 if (Feld[newx][newy] == EL_EMPTY)
2434 Feld[newx][newy] = EL_BLOCKED;
2436 if (direction == MV_DOWN && CAN_FALL(element))
2437 GfxAction[x][y] = ACTION_FALLING;
2439 GfxAction[x][y] = ACTION_MOVING;
2441 GfxFrame[newx][newy] = GfxFrame[x][y];
2442 GfxRandom[newx][newy] = GfxRandom[x][y];
2443 GfxAction[newx][newy] = GfxAction[x][y];
2444 GfxDir[newx][newy] = GfxDir[x][y];
2447 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2449 int direction = MovDir[x][y];
2450 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2451 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2457 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2459 int oldx = x, oldy = y;
2460 int direction = MovDir[x][y];
2462 if (direction == MV_LEFT)
2464 else if (direction == MV_RIGHT)
2466 else if (direction == MV_UP)
2468 else if (direction == MV_DOWN)
2471 *comes_from_x = oldx;
2472 *comes_from_y = oldy;
2475 int MovingOrBlocked2Element(int x, int y)
2477 int element = Feld[x][y];
2479 if (element == EL_BLOCKED)
2483 Blocked2Moving(x, y, &oldx, &oldy);
2484 return Feld[oldx][oldy];
2490 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2492 /* like MovingOrBlocked2Element(), but if element is moving
2493 and (x,y) is the field the moving element is just leaving,
2494 return EL_BLOCKED instead of the element value */
2495 int element = Feld[x][y];
2497 if (IS_MOVING(x, y))
2499 if (element == EL_BLOCKED)
2503 Blocked2Moving(x, y, &oldx, &oldy);
2504 return Feld[oldx][oldy];
2513 static void RemoveField(int x, int y)
2515 Feld[x][y] = EL_EMPTY;
2522 ChangeDelay[x][y] = 0;
2523 ChangePage[x][y] = -1;
2524 Pushed[x][y] = FALSE;
2527 ExplodeField[x][y] = EX_TYPE_NONE;
2530 GfxElement[x][y] = EL_UNDEFINED;
2531 GfxAction[x][y] = ACTION_DEFAULT;
2532 GfxDir[x][y] = MV_NO_MOVING;
2535 void RemoveMovingField(int x, int y)
2537 int oldx = x, oldy = y, newx = x, newy = y;
2538 int element = Feld[x][y];
2539 int next_element = EL_UNDEFINED;
2541 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2544 if (IS_MOVING(x, y))
2546 Moving2Blocked(x, y, &newx, &newy);
2548 if (Feld[newx][newy] != EL_BLOCKED)
2551 if (Feld[newx][newy] != EL_BLOCKED)
2553 /* element is moving, but target field is not free (blocked), but
2554 already occupied by something different (example: acid pool);
2555 in this case, only remove the moving field, but not the target */
2557 RemoveField(oldx, oldy);
2559 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2561 DrawLevelField(oldx, oldy);
2567 else if (element == EL_BLOCKED)
2569 Blocked2Moving(x, y, &oldx, &oldy);
2570 if (!IS_MOVING(oldx, oldy))
2574 if (element == EL_BLOCKED &&
2575 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2576 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2577 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2578 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2579 next_element = get_next_element(Feld[oldx][oldy]);
2581 RemoveField(oldx, oldy);
2582 RemoveField(newx, newy);
2584 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2586 if (next_element != EL_UNDEFINED)
2587 Feld[oldx][oldy] = next_element;
2589 DrawLevelField(oldx, oldy);
2590 DrawLevelField(newx, newy);
2593 void DrawDynamite(int x, int y)
2595 int sx = SCREENX(x), sy = SCREENY(y);
2596 int graphic = el2img(Feld[x][y]);
2599 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2602 if (IS_WALKABLE_INSIDE(Back[x][y]))
2606 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2607 else if (Store[x][y])
2608 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2610 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2613 if (Back[x][y] || Store[x][y])
2614 DrawGraphicThruMask(sx, sy, graphic, frame);
2616 DrawGraphic(sx, sy, graphic, frame);
2618 if (game.emulation == EMU_SUPAPLEX)
2619 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2620 else if (Store[x][y])
2621 DrawGraphicThruMask(sx, sy, graphic, frame);
2623 DrawGraphic(sx, sy, graphic, frame);
2627 void CheckDynamite(int x, int y)
2629 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2633 if (MovDelay[x][y] != 0)
2636 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2643 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2645 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2646 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2647 StopSound(SND_DYNAMITE_ACTIVE);
2649 StopSound(SND_DYNABOMB_ACTIVE);
2655 void RelocatePlayer(int x, int y, int element_raw)
2657 int old_element = Feld[x][y];
2658 int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
2659 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2660 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2661 boolean no_delay = (tape.warp_forward);
2662 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2663 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2666 if (player->GameOver) /* do not reanimate dead player */
2669 if (ELEM_IS_PLAYER(old_element)) /* player already set */
2671 RemoveField(x, y); /* temporarily remove newly placed player */
2672 DrawLevelField(x, y);
2675 if (player->present)
2677 while (player->MovPos)
2679 ScrollPlayer(player, SCROLL_GO_ON);
2680 ScrollScreen(NULL, SCROLL_GO_ON);
2686 Delay(wait_delay_value);
2689 DrawPlayer(player); /* needed here only to cleanup last field */
2690 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2692 player->is_moving = FALSE;
2695 old_jx = player->jx;
2696 old_jy = player->jy;
2698 Feld[x][y] = element;
2699 InitPlayerField(x, y, element, TRUE);
2701 if (!ELEM_IS_PLAYER(old_element)) /* player set on walkable element */
2703 Feld[x][y] = old_element;
2704 InitField(x, y, FALSE);
2707 if (player != local_player) /* do not visually relocate other players */
2710 if (level.instant_relocation)
2713 int offset = (setup.scroll_delay ? 3 : 0);
2714 int jx = local_player->jx;
2715 int jy = local_player->jy;
2717 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2719 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2720 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2721 local_player->jx - MIDPOSX);
2723 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2724 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2725 local_player->jy - MIDPOSY);
2729 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2730 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2731 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2733 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2734 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2735 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2737 /* don't scroll over playfield boundaries */
2738 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2739 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2741 /* don't scroll over playfield boundaries */
2742 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2743 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2746 scroll_x += (local_player->jx - old_jx);
2747 scroll_y += (local_player->jy - old_jy);
2749 /* don't scroll over playfield boundaries */
2750 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2751 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2753 /* don't scroll over playfield boundaries */
2754 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2755 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2758 RedrawPlayfield(TRUE, 0,0,0,0);
2764 int offset = (setup.scroll_delay ? 3 : 0);
2765 int jx = local_player->jx;
2766 int jy = local_player->jy;
2768 int scroll_xx = -999, scroll_yy = -999;
2770 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2772 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2775 int fx = FX, fy = FY;
2777 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2778 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2779 local_player->jx - MIDPOSX);
2781 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2782 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2783 local_player->jy - MIDPOSY);
2785 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2786 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2789 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2792 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2799 fx += dx * TILEX / 2;
2800 fy += dy * TILEY / 2;
2802 ScrollLevel(dx, dy);
2805 /* scroll in two steps of half tile size to make things smoother */
2806 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2808 Delay(wait_delay_value);
2810 /* scroll second step to align at full tile size */
2812 Delay(wait_delay_value);
2815 int scroll_xx = -999, scroll_yy = -999;
2817 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2819 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2822 int fx = FX, fy = FY;
2824 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2825 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2826 local_player->jx - MIDPOSX);
2828 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2829 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2830 local_player->jy - MIDPOSY);
2832 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2833 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2836 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2839 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2846 fx += dx * TILEX / 2;
2847 fy += dy * TILEY / 2;
2849 ScrollLevel(dx, dy);
2852 /* scroll in two steps of half tile size to make things smoother */
2853 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2855 Delay(wait_delay_value);
2857 /* scroll second step to align at full tile size */
2859 Delay(wait_delay_value);
2865 TestIfHeroTouchesBadThing(x, y);
2866 TestIfPlayerTouchesCustomElement(x, y);
2870 void Explode(int ex, int ey, int phase, int mode)
2877 /* !!! eliminate this variable !!! */
2878 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2883 int last_phase = num_phase * delay;
2884 int half_phase = (num_phase / 2) * delay;
2885 int first_phase_after_start = EX_PHASE_START + 1;
2889 if (game.explosions_delayed)
2891 ExplodeField[ex][ey] = mode;
2895 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2897 int center_element = Feld[ex][ey];
2900 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
2904 /* --- This is only really needed (and now handled) in "Impact()". --- */
2905 /* do not explode moving elements that left the explode field in time */
2906 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2907 center_element == EL_EMPTY &&
2908 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
2912 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
2913 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2915 /* remove things displayed in background while burning dynamite */
2916 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2919 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2921 /* put moving element to center field (and let it explode there) */
2922 center_element = MovingOrBlocked2Element(ex, ey);
2923 RemoveMovingField(ex, ey);
2924 Feld[ex][ey] = center_element;
2930 last_phase = element_info[center_element].explosion_delay + 1;
2932 last_phase = element_info[center_element].explosion_delay;
2936 printf("::: %d -> %d\n", center_element, last_phase);
2940 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2942 int xx = x - ex + 1;
2943 int yy = y - ey + 1;
2948 if (!IN_LEV_FIELD(x, y) ||
2949 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
2950 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
2953 if (!IN_LEV_FIELD(x, y) ||
2954 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
2958 if (!IN_LEV_FIELD(x, y) ||
2959 ((mode != EX_TYPE_NORMAL ||
2960 center_element == EL_AMOEBA_TO_DIAMOND) &&
2961 (x != ex || y != ey)))
2965 element = Feld[x][y];
2967 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2969 element = MovingOrBlocked2Element(x, y);
2971 if (!IS_EXPLOSION_PROOF(element))
2972 RemoveMovingField(x, y);
2978 if (IS_EXPLOSION_PROOF(element))
2981 /* indestructible elements can only explode in center (but not flames) */
2982 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2983 element == EL_FLAMES)
2988 if ((IS_INDESTRUCTIBLE(element) &&
2989 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2990 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2991 element == EL_FLAMES)
2995 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2997 if (IS_ACTIVE_BOMB(element))
2999 /* re-activate things under the bomb like gate or penguin */
3000 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3007 /* save walkable background elements while explosion on same tile */
3009 if (IS_INDESTRUCTIBLE(element))
3010 Back[x][y] = element;
3012 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3013 Back[x][y] = element;
3016 /* ignite explodable elements reached by other explosion */
3017 if (element == EL_EXPLOSION)
3018 element = Store2[x][y];
3021 if (AmoebaNr[x][y] &&
3022 (element == EL_AMOEBA_FULL ||
3023 element == EL_BD_AMOEBA ||
3024 element == EL_AMOEBA_GROWING))
3026 AmoebaCnt[AmoebaNr[x][y]]--;
3027 AmoebaCnt2[AmoebaNr[x][y]]--;
3033 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3035 switch(StorePlayer[ex][ey])
3038 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3041 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3044 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3048 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3053 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3054 Store[x][y] = EL_EMPTY;
3056 if (game.emulation == EMU_SUPAPLEX)
3057 Store[x][y] = EL_EMPTY;
3060 else if (center_element == EL_MOLE)
3061 Store[x][y] = EL_EMERALD_RED;
3062 else if (center_element == EL_PENGUIN)
3063 Store[x][y] = EL_EMERALD_PURPLE;
3064 else if (center_element == EL_BUG)
3065 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3066 else if (center_element == EL_BD_BUTTERFLY)
3067 Store[x][y] = EL_BD_DIAMOND;
3068 else if (center_element == EL_SP_ELECTRON)
3069 Store[x][y] = EL_SP_INFOTRON;
3070 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3071 Store[x][y] = level.amoeba_content;
3072 else if (center_element == EL_YAMYAM)
3073 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3074 else if (IS_CUSTOM_ELEMENT(center_element) &&
3075 element_info[center_element].content[xx][yy] != EL_EMPTY)
3076 Store[x][y] = element_info[center_element].content[xx][yy];
3077 else if (element == EL_WALL_EMERALD)
3078 Store[x][y] = EL_EMERALD;
3079 else if (element == EL_WALL_DIAMOND)
3080 Store[x][y] = EL_DIAMOND;
3081 else if (element == EL_WALL_BD_DIAMOND)
3082 Store[x][y] = EL_BD_DIAMOND;
3083 else if (element == EL_WALL_EMERALD_YELLOW)
3084 Store[x][y] = EL_EMERALD_YELLOW;
3085 else if (element == EL_WALL_EMERALD_RED)
3086 Store[x][y] = EL_EMERALD_RED;
3087 else if (element == EL_WALL_EMERALD_PURPLE)
3088 Store[x][y] = EL_EMERALD_PURPLE;
3089 else if (element == EL_WALL_PEARL)
3090 Store[x][y] = EL_PEARL;
3091 else if (element == EL_WALL_CRYSTAL)
3092 Store[x][y] = EL_CRYSTAL;
3093 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3094 Store[x][y] = element_info[element].content[1][1];
3096 Store[x][y] = EL_EMPTY;
3098 if (x != ex || y != ey ||
3099 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_TYPE_BORDER)
3100 Store2[x][y] = element;
3103 if (AmoebaNr[x][y] &&
3104 (element == EL_AMOEBA_FULL ||
3105 element == EL_BD_AMOEBA ||
3106 element == EL_AMOEBA_GROWING))
3108 AmoebaCnt[AmoebaNr[x][y]]--;
3109 AmoebaCnt2[AmoebaNr[x][y]]--;
3115 MovDir[x][y] = MovPos[x][y] = 0;
3116 GfxDir[x][y] = MovDir[x][y];
3121 Feld[x][y] = EL_EXPLOSION;
3123 GfxElement[x][y] = center_element;
3125 GfxElement[x][y] = EL_UNDEFINED;
3128 ExplodePhase[x][y] = 1;
3130 ExplodeDelay[x][y] = last_phase;
3135 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3137 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3144 if (center_element == EL_YAMYAM)
3145 game.yamyam_content_nr =
3146 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3159 GfxFrame[x][y] = 0; /* restart explosion animation */
3163 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3167 last_phase = ExplodeDelay[x][y];
3170 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3174 /* activate this even in non-DEBUG version until cause for crash in
3175 getGraphicAnimationFrame() (see below) is found and eliminated */
3179 if (GfxElement[x][y] == EL_UNDEFINED)
3182 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3183 printf("Explode(): This should never happen!\n");
3186 GfxElement[x][y] = EL_EMPTY;
3192 border_element = Store2[x][y];
3193 if (IS_PLAYER(x, y))
3194 border_element = StorePlayer[x][y];
3197 printf("::: phase == %d\n", phase);
3200 if (phase == element_info[border_element].ignition_delay ||
3201 phase == last_phase)
3203 boolean border_explosion = FALSE;
3206 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3208 if (IS_PLAYER(x, y))
3211 KillHeroUnlessExplosionProtected(x, y);
3212 border_explosion = TRUE;
3215 if (phase == last_phase)
3216 printf("::: IS_PLAYER\n");
3219 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3221 Feld[x][y] = Store2[x][y];
3224 border_explosion = TRUE;
3227 if (phase == last_phase)
3228 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3231 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3233 AmoebeUmwandeln(x, y);
3235 border_explosion = TRUE;
3238 if (phase == last_phase)
3239 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3240 element_info[border_element].explosion_delay,
3241 element_info[border_element].ignition_delay,
3247 /* if an element just explodes due to another explosion (chain-reaction),
3248 do not immediately end the new explosion when it was the last frame of
3249 the explosion (as it would be done in the following "if"-statement!) */
3250 if (border_explosion && phase == last_phase)
3257 if (phase == first_phase_after_start)
3259 int element = Store2[x][y];
3261 if (element == EL_BLACK_ORB)
3263 Feld[x][y] = Store2[x][y];
3268 else if (phase == half_phase)
3270 int element = Store2[x][y];
3272 if (IS_PLAYER(x, y))
3273 KillHeroUnlessExplosionProtected(x, y);
3274 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3276 Feld[x][y] = Store2[x][y];
3280 else if (element == EL_AMOEBA_TO_DIAMOND)
3281 AmoebeUmwandeln(x, y);
3285 if (phase == last_phase)
3290 printf("::: done: phase == %d\n", phase);
3294 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3297 element = Feld[x][y] = Store[x][y];
3298 Store[x][y] = Store2[x][y] = 0;
3299 GfxElement[x][y] = EL_UNDEFINED;
3301 /* player can escape from explosions and might therefore be still alive */
3302 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3303 element <= EL_PLAYER_IS_EXPLODING_4)
3304 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3306 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3307 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3308 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3311 /* restore probably existing indestructible background element */
3312 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3313 element = Feld[x][y] = Back[x][y];
3316 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3317 GfxDir[x][y] = MV_NO_MOVING;
3318 ChangeDelay[x][y] = 0;
3319 ChangePage[x][y] = -1;
3322 InitField_WithBug2(x, y, FALSE);
3324 InitField(x, y, FALSE);
3326 /* !!! not needed !!! */
3328 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3329 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3332 if (CAN_MOVE(element))
3337 DrawLevelField(x, y);
3339 TestIfElementTouchesCustomElement(x, y);
3341 if (GFX_CRUMBLED(element))
3342 DrawLevelFieldCrumbledSandNeighbours(x, y);
3344 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3345 StorePlayer[x][y] = 0;
3347 if (ELEM_IS_PLAYER(element))
3348 RelocatePlayer(x, y, element);
3351 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3353 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3357 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3359 int stored = Store[x][y];
3360 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3361 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3365 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3367 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3371 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3375 printf("::: %d / %d [%d - %d]\n",
3376 GfxFrame[x][y], phase - delay, phase, delay);
3380 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3381 element_info[GfxElement[x][y]].token_name,
3386 DrawLevelFieldCrumbledSand(x, y);
3388 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3390 DrawLevelElement(x, y, Back[x][y]);
3391 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3393 else if (IS_WALKABLE_UNDER(Back[x][y]))
3395 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3396 DrawLevelElementThruMask(x, y, Back[x][y]);
3398 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3399 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3403 void DynaExplode(int ex, int ey)
3406 int dynabomb_element = Feld[ex][ey];
3407 int dynabomb_size = 1;
3408 boolean dynabomb_xl = FALSE;
3409 struct PlayerInfo *player;
3410 static int xy[4][2] =
3418 if (IS_ACTIVE_BOMB(dynabomb_element))
3420 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3421 dynabomb_size = player->dynabomb_size;
3422 dynabomb_xl = player->dynabomb_xl;
3423 player->dynabombs_left++;
3426 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3428 for (i = 0; i < NUM_DIRECTIONS; i++)
3430 for (j = 1; j <= dynabomb_size; j++)
3432 int x = ex + j * xy[i][0];
3433 int y = ey + j * xy[i][1];
3436 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3439 element = Feld[x][y];
3441 /* do not restart explosions of fields with active bombs */
3442 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3445 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3448 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3449 !CAN_GROW_INTO(element) && !dynabomb_xl)
3452 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3453 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3454 element != EL_SAND && !dynabomb_xl)
3461 void Bang(int x, int y)
3464 int element = MovingOrBlocked2Element(x, y);
3466 int element = Feld[x][y];
3470 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3472 if (IS_PLAYER(x, y))
3475 struct PlayerInfo *player = PLAYERINFO(x, y);
3477 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3478 player->element_nr);
3483 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3485 if (game.emulation == EMU_SUPAPLEX)
3486 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3488 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3493 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3501 case EL_BD_BUTTERFLY:
3504 case EL_DARK_YAMYAM:
3508 RaiseScoreElement(element);
3509 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3511 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3512 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3513 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3514 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3515 case EL_DYNABOMB_INCREASE_NUMBER:
3516 case EL_DYNABOMB_INCREASE_SIZE:
3517 case EL_DYNABOMB_INCREASE_POWER:
3522 case EL_LAMP_ACTIVE:
3524 case EL_AMOEBA_TO_DIAMOND:
3526 if (IS_PLAYER(x, y))
3527 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3529 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3532 if (CAN_EXPLODE_CROSS(element))
3534 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3538 else if (CAN_EXPLODE_1X1(element))
3539 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3541 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3545 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3548 void SplashAcid(int x, int y)
3551 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3552 (!IN_LEV_FIELD(x - 1, y - 2) ||
3553 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3554 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3556 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3557 (!IN_LEV_FIELD(x + 1, y - 2) ||
3558 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3559 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3561 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3563 /* input: position of element entering acid (obsolete) */
3565 int element = Feld[x][y];
3567 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3570 if (element != EL_ACID_SPLASH_LEFT &&
3571 element != EL_ACID_SPLASH_RIGHT)
3573 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3575 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3576 (!IN_LEV_FIELD(x - 1, y - 1) ||
3577 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3578 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3580 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3581 (!IN_LEV_FIELD(x + 1, y - 1) ||
3582 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3583 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3588 static void InitBeltMovement()
3590 static int belt_base_element[4] =
3592 EL_CONVEYOR_BELT_1_LEFT,
3593 EL_CONVEYOR_BELT_2_LEFT,
3594 EL_CONVEYOR_BELT_3_LEFT,
3595 EL_CONVEYOR_BELT_4_LEFT
3597 static int belt_base_active_element[4] =
3599 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3600 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3601 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3602 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3607 /* set frame order for belt animation graphic according to belt direction */
3608 for (i = 0; i < NUM_BELTS; i++)
3612 for (j = 0; j < NUM_BELT_PARTS; j++)
3614 int element = belt_base_active_element[belt_nr] + j;
3615 int graphic = el2img(element);
3617 if (game.belt_dir[i] == MV_LEFT)
3618 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3620 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3624 for (y = 0; y < lev_fieldy; y++)
3626 for (x = 0; x < lev_fieldx; x++)
3628 int element = Feld[x][y];
3630 for (i = 0; i < NUM_BELTS; i++)
3632 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3634 int e_belt_nr = getBeltNrFromBeltElement(element);
3637 if (e_belt_nr == belt_nr)
3639 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3641 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3649 static void ToggleBeltSwitch(int x, int y)
3651 static int belt_base_element[4] =
3653 EL_CONVEYOR_BELT_1_LEFT,
3654 EL_CONVEYOR_BELT_2_LEFT,
3655 EL_CONVEYOR_BELT_3_LEFT,
3656 EL_CONVEYOR_BELT_4_LEFT
3658 static int belt_base_active_element[4] =
3660 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3661 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3662 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3663 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3665 static int belt_base_switch_element[4] =
3667 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3668 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3669 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3670 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3672 static int belt_move_dir[4] =
3680 int element = Feld[x][y];
3681 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3682 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3683 int belt_dir = belt_move_dir[belt_dir_nr];
3686 if (!IS_BELT_SWITCH(element))
3689 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3690 game.belt_dir[belt_nr] = belt_dir;
3692 if (belt_dir_nr == 3)
3695 /* set frame order for belt animation graphic according to belt direction */
3696 for (i = 0; i < NUM_BELT_PARTS; i++)
3698 int element = belt_base_active_element[belt_nr] + i;
3699 int graphic = el2img(element);
3701 if (belt_dir == MV_LEFT)
3702 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3704 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3707 for (yy = 0; yy < lev_fieldy; yy++)
3709 for (xx = 0; xx < lev_fieldx; xx++)
3711 int element = Feld[xx][yy];
3713 if (IS_BELT_SWITCH(element))
3715 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3717 if (e_belt_nr == belt_nr)
3719 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3720 DrawLevelField(xx, yy);
3723 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3725 int e_belt_nr = getBeltNrFromBeltElement(element);
3727 if (e_belt_nr == belt_nr)
3729 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3731 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3732 DrawLevelField(xx, yy);
3735 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3737 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3739 if (e_belt_nr == belt_nr)
3741 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3743 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3744 DrawLevelField(xx, yy);
3751 static void ToggleSwitchgateSwitch(int x, int y)
3755 game.switchgate_pos = !game.switchgate_pos;
3757 for (yy = 0; yy < lev_fieldy; yy++)
3759 for (xx = 0; xx < lev_fieldx; xx++)
3761 int element = Feld[xx][yy];
3763 if (element == EL_SWITCHGATE_SWITCH_UP ||
3764 element == EL_SWITCHGATE_SWITCH_DOWN)
3766 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3767 DrawLevelField(xx, yy);
3769 else if (element == EL_SWITCHGATE_OPEN ||
3770 element == EL_SWITCHGATE_OPENING)
3772 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3774 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3776 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3779 else if (element == EL_SWITCHGATE_CLOSED ||
3780 element == EL_SWITCHGATE_CLOSING)
3782 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3784 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3786 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3793 static int getInvisibleActiveFromInvisibleElement(int element)
3795 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3796 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3797 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3801 static int getInvisibleFromInvisibleActiveElement(int element)
3803 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3804 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3805 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3809 static void RedrawAllLightSwitchesAndInvisibleElements()
3813 for (y = 0; y < lev_fieldy; y++)
3815 for (x = 0; x < lev_fieldx; x++)
3817 int element = Feld[x][y];
3819 if (element == EL_LIGHT_SWITCH &&
3820 game.light_time_left > 0)
3822 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3823 DrawLevelField(x, y);
3825 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3826 game.light_time_left == 0)
3828 Feld[x][y] = EL_LIGHT_SWITCH;
3829 DrawLevelField(x, y);
3831 else if (element == EL_INVISIBLE_STEELWALL ||
3832 element == EL_INVISIBLE_WALL ||
3833 element == EL_INVISIBLE_SAND)
3835 if (game.light_time_left > 0)
3836 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3838 DrawLevelField(x, y);
3840 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3841 element == EL_INVISIBLE_WALL_ACTIVE ||
3842 element == EL_INVISIBLE_SAND_ACTIVE)
3844 if (game.light_time_left == 0)
3845 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3847 DrawLevelField(x, y);
3853 static void ToggleLightSwitch(int x, int y)
3855 int element = Feld[x][y];
3857 game.light_time_left =
3858 (element == EL_LIGHT_SWITCH ?
3859 level.time_light * FRAMES_PER_SECOND : 0);
3861 RedrawAllLightSwitchesAndInvisibleElements();
3864 static void ActivateTimegateSwitch(int x, int y)
3868 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3870 for (yy = 0; yy < lev_fieldy; yy++)
3872 for (xx = 0; xx < lev_fieldx; xx++)
3874 int element = Feld[xx][yy];
3876 if (element == EL_TIMEGATE_CLOSED ||
3877 element == EL_TIMEGATE_CLOSING)
3879 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3880 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3884 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3886 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3887 DrawLevelField(xx, yy);
3894 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3897 inline static int getElementMoveStepsize(int x, int y)
3899 int element = Feld[x][y];
3900 int direction = MovDir[x][y];
3901 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3902 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3903 int horiz_move = (dx != 0);
3904 int sign = (horiz_move ? dx : dy);
3905 int step = sign * element_info[element].move_stepsize;
3907 /* special values for move stepsize for spring and things on conveyor belt */
3911 if (element == EL_SPRING)
3912 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3913 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3914 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3915 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3917 if (CAN_FALL(element) &&
3918 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3919 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3920 else if (element == EL_SPRING)
3921 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3928 void Impact(int x, int y)
3930 boolean lastline = (y == lev_fieldy-1);
3931 boolean object_hit = FALSE;
3932 boolean impact = (lastline || object_hit);
3933 int element = Feld[x][y];
3934 int smashed = EL_STEELWALL;
3936 if (!lastline) /* check if element below was hit */
3938 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3941 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3942 MovDir[x][y + 1] != MV_DOWN ||
3943 MovPos[x][y + 1] <= TILEY / 2));
3946 object_hit = !IS_FREE(x, y + 1);
3949 /* do not smash moving elements that left the smashed field in time */
3950 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3951 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3955 smashed = MovingOrBlocked2Element(x, y + 1);
3957 impact = (lastline || object_hit);
3960 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3962 SplashAcid(x, y + 1);
3966 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
3967 /* only reset graphic animation if graphic really changes after impact */
3969 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3971 ResetGfxAnimation(x, y);
3972 DrawLevelField(x, y);
3975 if (impact && CAN_EXPLODE_IMPACT(element))
3980 else if (impact && element == EL_PEARL)
3982 ResetGfxAnimation(x, y);
3984 Feld[x][y] = EL_PEARL_BREAKING;
3985 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3988 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
3990 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3995 if (impact && element == EL_AMOEBA_DROP)
3997 if (object_hit && IS_PLAYER(x, y + 1))
3998 KillHeroUnlessEnemyProtected(x, y + 1);
3999 else if (object_hit && smashed == EL_PENGUIN)
4003 Feld[x][y] = EL_AMOEBA_GROWING;
4004 Store[x][y] = EL_AMOEBA_WET;
4006 ResetRandomAnimationValue(x, y);
4011 if (object_hit) /* check which object was hit */
4013 if (CAN_PASS_MAGIC_WALL(element) &&
4014 (smashed == EL_MAGIC_WALL ||
4015 smashed == EL_BD_MAGIC_WALL))
4018 int activated_magic_wall =
4019 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4020 EL_BD_MAGIC_WALL_ACTIVE);
4022 /* activate magic wall / mill */
4023 for (yy = 0; yy < lev_fieldy; yy++)
4024 for (xx = 0; xx < lev_fieldx; xx++)
4025 if (Feld[xx][yy] == smashed)
4026 Feld[xx][yy] = activated_magic_wall;
4028 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4029 game.magic_wall_active = TRUE;
4031 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4032 SND_MAGIC_WALL_ACTIVATING :
4033 SND_BD_MAGIC_WALL_ACTIVATING));
4036 if (IS_PLAYER(x, y + 1))
4038 if (CAN_SMASH_PLAYER(element))
4040 KillHeroUnlessEnemyProtected(x, y + 1);
4044 else if (smashed == EL_PENGUIN)
4046 if (CAN_SMASH_PLAYER(element))
4052 else if (element == EL_BD_DIAMOND)
4054 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4060 else if (((element == EL_SP_INFOTRON ||
4061 element == EL_SP_ZONK) &&
4062 (smashed == EL_SP_SNIKSNAK ||
4063 smashed == EL_SP_ELECTRON ||
4064 smashed == EL_SP_DISK_ORANGE)) ||
4065 (element == EL_SP_INFOTRON &&
4066 smashed == EL_SP_DISK_YELLOW))
4072 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4078 else if (CAN_SMASH_EVERYTHING(element))
4080 if (IS_CLASSIC_ENEMY(smashed) ||
4081 CAN_EXPLODE_SMASHED(smashed))
4086 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4088 if (smashed == EL_LAMP ||
4089 smashed == EL_LAMP_ACTIVE)
4094 else if (smashed == EL_NUT)
4096 Feld[x][y + 1] = EL_NUT_BREAKING;
4097 PlayLevelSound(x, y, SND_NUT_BREAKING);
4098 RaiseScoreElement(EL_NUT);
4101 else if (smashed == EL_PEARL)
4103 ResetGfxAnimation(x, y);
4105 Feld[x][y + 1] = EL_PEARL_BREAKING;
4106 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4109 else if (smashed == EL_DIAMOND)
4111 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4112 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4115 else if (IS_BELT_SWITCH(smashed))
4117 ToggleBeltSwitch(x, y + 1);
4119 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4120 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4122 ToggleSwitchgateSwitch(x, y + 1);
4124 else if (smashed == EL_LIGHT_SWITCH ||
4125 smashed == EL_LIGHT_SWITCH_ACTIVE)
4127 ToggleLightSwitch(x, y + 1);
4132 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4135 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4138 /* !!! TEST ONLY !!! */
4139 CheckElementChangeBySide(x, y + 1, smashed, element,
4140 CE_SWITCHED, CH_SIDE_TOP);
4141 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4142 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4144 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4145 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4146 CheckElementChangeBySide(x, y + 1, smashed, element,
4147 CE_SWITCHED, CH_SIDE_TOP);
4153 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4158 /* play sound of magic wall / mill */
4160 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4161 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4163 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4164 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4165 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4166 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4171 /* play sound of object that hits the ground */
4172 if (lastline || object_hit)
4173 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4176 inline static void TurnRoundExt(int x, int y)
4188 { 0, 0 }, { 0, 0 }, { 0, 0 },
4193 int left, right, back;
4197 { MV_DOWN, MV_UP, MV_RIGHT },
4198 { MV_UP, MV_DOWN, MV_LEFT },
4200 { MV_LEFT, MV_RIGHT, MV_DOWN },
4204 { MV_RIGHT, MV_LEFT, MV_UP }
4207 int element = Feld[x][y];
4208 int move_pattern = element_info[element].move_pattern;
4210 int old_move_dir = MovDir[x][y];
4211 int left_dir = turn[old_move_dir].left;
4212 int right_dir = turn[old_move_dir].right;
4213 int back_dir = turn[old_move_dir].back;
4215 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4216 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4217 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4218 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4220 int left_x = x + left_dx, left_y = y + left_dy;
4221 int right_x = x + right_dx, right_y = y + right_dy;
4222 int move_x = x + move_dx, move_y = y + move_dy;
4226 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4228 TestIfBadThingTouchesOtherBadThing(x, y);
4230 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4231 MovDir[x][y] = right_dir;
4232 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4233 MovDir[x][y] = left_dir;
4235 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4237 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4241 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4242 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4244 TestIfBadThingTouchesOtherBadThing(x, y);
4246 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4247 MovDir[x][y] = left_dir;
4248 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4249 MovDir[x][y] = right_dir;
4251 if ((element == EL_SPACESHIP ||
4252 element == EL_SP_SNIKSNAK ||
4253 element == EL_SP_ELECTRON)
4254 && MovDir[x][y] != old_move_dir)
4256 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4260 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4262 TestIfBadThingTouchesOtherBadThing(x, y);
4264 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4265 MovDir[x][y] = left_dir;
4266 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4267 MovDir[x][y] = right_dir;
4269 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4271 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4274 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4276 TestIfBadThingTouchesOtherBadThing(x, y);
4278 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4279 MovDir[x][y] = left_dir;
4280 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4281 MovDir[x][y] = right_dir;
4283 if (MovDir[x][y] != old_move_dir)
4287 else if (element == EL_YAMYAM)
4289 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4290 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4292 if (can_turn_left && can_turn_right)
4293 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4294 else if (can_turn_left)
4295 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4296 else if (can_turn_right)
4297 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4299 MovDir[x][y] = back_dir;
4301 MovDelay[x][y] = 16 + 16 * RND(3);
4303 else if (element == EL_DARK_YAMYAM)
4305 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4307 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4310 if (can_turn_left && can_turn_right)
4311 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4312 else if (can_turn_left)
4313 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4314 else if (can_turn_right)
4315 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4317 MovDir[x][y] = back_dir;
4319 MovDelay[x][y] = 16 + 16 * RND(3);
4321 else if (element == EL_PACMAN)
4323 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4324 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4326 if (can_turn_left && can_turn_right)
4327 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4328 else if (can_turn_left)
4329 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4330 else if (can_turn_right)
4331 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4333 MovDir[x][y] = back_dir;
4335 MovDelay[x][y] = 6 + RND(40);
4337 else if (element == EL_PIG)
4339 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4340 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4341 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4342 boolean should_turn_left, should_turn_right, should_move_on;
4344 int rnd = RND(rnd_value);
4346 should_turn_left = (can_turn_left &&
4348 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4349 y + back_dy + left_dy)));
4350 should_turn_right = (can_turn_right &&
4352 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4353 y + back_dy + right_dy)));
4354 should_move_on = (can_move_on &&
4357 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4358 y + move_dy + left_dy) ||
4359 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4360 y + move_dy + right_dy)));
4362 if (should_turn_left || should_turn_right || should_move_on)
4364 if (should_turn_left && should_turn_right && should_move_on)
4365 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4366 rnd < 2 * rnd_value / 3 ? right_dir :
4368 else if (should_turn_left && should_turn_right)
4369 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4370 else if (should_turn_left && should_move_on)
4371 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4372 else if (should_turn_right && should_move_on)
4373 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4374 else if (should_turn_left)
4375 MovDir[x][y] = left_dir;
4376 else if (should_turn_right)
4377 MovDir[x][y] = right_dir;
4378 else if (should_move_on)
4379 MovDir[x][y] = old_move_dir;
4381 else if (can_move_on && rnd > rnd_value / 8)
4382 MovDir[x][y] = old_move_dir;
4383 else if (can_turn_left && can_turn_right)
4384 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4385 else if (can_turn_left && rnd > rnd_value / 8)
4386 MovDir[x][y] = left_dir;
4387 else if (can_turn_right && rnd > rnd_value/8)
4388 MovDir[x][y] = right_dir;
4390 MovDir[x][y] = back_dir;
4392 xx = x + move_xy[MovDir[x][y]].x;
4393 yy = y + move_xy[MovDir[x][y]].y;
4395 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4396 MovDir[x][y] = old_move_dir;
4400 else if (element == EL_DRAGON)
4402 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4403 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4404 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4406 int rnd = RND(rnd_value);
4409 if (FrameCounter < 1 && x == 0 && y == 29)
4410 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4413 if (can_move_on && rnd > rnd_value / 8)
4414 MovDir[x][y] = old_move_dir;
4415 else if (can_turn_left && can_turn_right)
4416 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4417 else if (can_turn_left && rnd > rnd_value / 8)
4418 MovDir[x][y] = left_dir;
4419 else if (can_turn_right && rnd > rnd_value / 8)
4420 MovDir[x][y] = right_dir;
4422 MovDir[x][y] = back_dir;
4424 xx = x + move_xy[MovDir[x][y]].x;
4425 yy = y + move_xy[MovDir[x][y]].y;
4428 if (FrameCounter < 1 && x == 0 && y == 29)
4429 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4430 xx, yy, Feld[xx][yy],
4435 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4436 MovDir[x][y] = old_move_dir;
4438 if (!IS_FREE(xx, yy))
4439 MovDir[x][y] = old_move_dir;
4443 if (FrameCounter < 1 && x == 0 && y == 29)
4444 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4449 else if (element == EL_MOLE)
4451 boolean can_move_on =
4452 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4453 IS_AMOEBOID(Feld[move_x][move_y]) ||
4454 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4457 boolean can_turn_left =
4458 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4459 IS_AMOEBOID(Feld[left_x][left_y])));
4461 boolean can_turn_right =
4462 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4463 IS_AMOEBOID(Feld[right_x][right_y])));
4465 if (can_turn_left && can_turn_right)
4466 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4467 else if (can_turn_left)
4468 MovDir[x][y] = left_dir;
4470 MovDir[x][y] = right_dir;
4473 if (MovDir[x][y] != old_move_dir)
4476 else if (element == EL_BALLOON)
4478 MovDir[x][y] = game.balloon_dir;
4481 else if (element == EL_SPRING)
4484 if (MovDir[x][y] & MV_HORIZONTAL &&
4485 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4486 MovDir[x][y] = MV_NO_MOVING;
4488 if (MovDir[x][y] & MV_HORIZONTAL &&
4489 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4490 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4491 MovDir[x][y] = MV_NO_MOVING;
4496 else if (element == EL_ROBOT ||
4497 element == EL_SATELLITE ||
4498 element == EL_PENGUIN)
4500 int attr_x = -1, attr_y = -1;
4511 for (i = 0; i < MAX_PLAYERS; i++)
4513 struct PlayerInfo *player = &stored_player[i];
4514 int jx = player->jx, jy = player->jy;
4516 if (!player->active)
4520 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4529 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4530 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4531 game.engine_version < VERSION_IDENT(3,1,0,0)))
4533 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4540 if (element == EL_PENGUIN)
4543 static int xy[4][2] =
4551 for (i = 0; i < NUM_DIRECTIONS; i++)
4553 int ex = x + xy[i][0];
4554 int ey = y + xy[i][1];
4556 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4565 MovDir[x][y] = MV_NO_MOVING;
4567 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4568 else if (attr_x > x)
4569 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4571 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4572 else if (attr_y > y)
4573 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4575 if (element == EL_ROBOT)
4579 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4580 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4581 Moving2Blocked(x, y, &newx, &newy);
4583 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4584 MovDelay[x][y] = 8 + 8 * !RND(3);
4586 MovDelay[x][y] = 16;
4588 else if (element == EL_PENGUIN)
4594 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4596 boolean first_horiz = RND(2);
4597 int new_move_dir = MovDir[x][y];
4600 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4601 Moving2Blocked(x, y, &newx, &newy);
4603 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4607 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4608 Moving2Blocked(x, y, &newx, &newy);
4610 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4613 MovDir[x][y] = old_move_dir;
4617 else /* (element == EL_SATELLITE) */
4623 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4625 boolean first_horiz = RND(2);
4626 int new_move_dir = MovDir[x][y];
4629 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4630 Moving2Blocked(x, y, &newx, &newy);
4632 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4636 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4637 Moving2Blocked(x, y, &newx, &newy);
4639 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4642 MovDir[x][y] = old_move_dir;
4647 else if (move_pattern == MV_TURNING_LEFT ||
4648 move_pattern == MV_TURNING_RIGHT ||
4649 move_pattern == MV_TURNING_LEFT_RIGHT ||
4650 move_pattern == MV_TURNING_RIGHT_LEFT ||
4651 move_pattern == MV_TURNING_RANDOM ||
4652 move_pattern == MV_ALL_DIRECTIONS)
4654 boolean can_turn_left =
4655 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4656 boolean can_turn_right =
4657 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4659 if (move_pattern == MV_TURNING_LEFT)
4660 MovDir[x][y] = left_dir;
4661 else if (move_pattern == MV_TURNING_RIGHT)
4662 MovDir[x][y] = right_dir;
4663 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4664 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4665 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4666 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4667 else if (move_pattern == MV_TURNING_RANDOM)
4668 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4669 can_turn_right && !can_turn_left ? right_dir :
4670 RND(2) ? left_dir : right_dir);
4671 else if (can_turn_left && can_turn_right)
4672 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4673 else if (can_turn_left)
4674 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4675 else if (can_turn_right)
4676 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4678 MovDir[x][y] = back_dir;
4680 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4682 else if (move_pattern == MV_HORIZONTAL ||
4683 move_pattern == MV_VERTICAL)
4685 if (move_pattern & old_move_dir)
4686 MovDir[x][y] = back_dir;
4687 else if (move_pattern == MV_HORIZONTAL)
4688 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4689 else if (move_pattern == MV_VERTICAL)
4690 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4692 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4694 else if (move_pattern & MV_ANY_DIRECTION)
4696 MovDir[x][y] = move_pattern;
4697 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4699 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4701 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4702 MovDir[x][y] = left_dir;
4703 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4704 MovDir[x][y] = right_dir;
4706 if (MovDir[x][y] != old_move_dir)
4707 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4709 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4711 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4712 MovDir[x][y] = right_dir;
4713 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4714 MovDir[x][y] = left_dir;
4716 if (MovDir[x][y] != old_move_dir)
4717 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4719 else if (move_pattern == MV_TOWARDS_PLAYER ||
4720 move_pattern == MV_AWAY_FROM_PLAYER)
4722 int attr_x = -1, attr_y = -1;
4724 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4735 for (i = 0; i < MAX_PLAYERS; i++)
4737 struct PlayerInfo *player = &stored_player[i];
4738 int jx = player->jx, jy = player->jy;
4740 if (!player->active)
4744 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4752 MovDir[x][y] = MV_NO_MOVING;
4754 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4755 else if (attr_x > x)
4756 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4758 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4759 else if (attr_y > y)
4760 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4762 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4764 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4766 boolean first_horiz = RND(2);
4767 int new_move_dir = MovDir[x][y];
4770 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4771 Moving2Blocked(x, y, &newx, &newy);
4773 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4777 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4778 Moving2Blocked(x, y, &newx, &newy);
4780 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4783 MovDir[x][y] = old_move_dir;
4786 else if (move_pattern == MV_WHEN_PUSHED ||
4787 move_pattern == MV_WHEN_DROPPED)
4789 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4790 MovDir[x][y] = MV_NO_MOVING;
4794 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4796 static int test_xy[7][2] =
4806 static int test_dir[7] =
4816 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4817 int move_preference = -1000000; /* start with very low preference */
4818 int new_move_dir = MV_NO_MOVING;
4819 int start_test = RND(4);
4822 for (i = 0; i < NUM_DIRECTIONS; i++)
4824 int move_dir = test_dir[start_test + i];
4825 int move_dir_preference;
4827 xx = x + test_xy[start_test + i][0];
4828 yy = y + test_xy[start_test + i][1];
4830 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4831 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4833 new_move_dir = move_dir;
4838 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4841 move_dir_preference = -1 * RunnerVisit[xx][yy];
4842 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4843 move_dir_preference = PlayerVisit[xx][yy];
4845 if (move_dir_preference > move_preference)
4847 /* prefer field that has not been visited for the longest time */
4848 move_preference = move_dir_preference;
4849 new_move_dir = move_dir;
4851 else if (move_dir_preference == move_preference &&
4852 move_dir == old_move_dir)
4854 /* prefer last direction when all directions are preferred equally */
4855 move_preference = move_dir_preference;
4856 new_move_dir = move_dir;
4860 MovDir[x][y] = new_move_dir;
4861 if (old_move_dir != new_move_dir)
4866 static void TurnRound(int x, int y)
4868 int direction = MovDir[x][y];
4871 GfxDir[x][y] = MovDir[x][y];
4877 GfxDir[x][y] = MovDir[x][y];
4880 if (direction != MovDir[x][y])
4885 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4888 GfxAction[x][y] = ACTION_WAITING;
4892 static boolean JustBeingPushed(int x, int y)
4896 for (i = 0; i < MAX_PLAYERS; i++)
4898 struct PlayerInfo *player = &stored_player[i];
4900 if (player->active && player->is_pushing && player->MovPos)
4902 int next_jx = player->jx + (player->jx - player->last_jx);
4903 int next_jy = player->jy + (player->jy - player->last_jy);
4905 if (x == next_jx && y == next_jy)
4913 void StartMoving(int x, int y)
4916 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4918 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4919 int element = Feld[x][y];
4925 if (MovDelay[x][y] == 0)
4926 GfxAction[x][y] = ACTION_DEFAULT;
4928 /* !!! this should be handled more generic (not only for mole) !!! */
4929 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4930 GfxAction[x][y] = ACTION_DEFAULT;
4933 if (CAN_FALL(element) && y < lev_fieldy - 1)
4935 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4936 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4937 if (JustBeingPushed(x, y))
4940 if (element == EL_QUICKSAND_FULL)
4942 if (IS_FREE(x, y + 1))
4944 InitMovingField(x, y, MV_DOWN);
4945 started_moving = TRUE;
4947 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4948 Store[x][y] = EL_ROCK;
4950 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4952 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4955 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4957 if (!MovDelay[x][y])
4958 MovDelay[x][y] = TILEY + 1;
4967 Feld[x][y] = EL_QUICKSAND_EMPTY;
4968 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4969 Store[x][y + 1] = Store[x][y];
4972 PlayLevelSoundAction(x, y, ACTION_FILLING);
4974 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4978 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4979 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4981 InitMovingField(x, y, MV_DOWN);
4982 started_moving = TRUE;
4984 Feld[x][y] = EL_QUICKSAND_FILLING;
4985 Store[x][y] = element;
4987 PlayLevelSoundAction(x, y, ACTION_FILLING);
4989 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4992 else if (element == EL_MAGIC_WALL_FULL)
4994 if (IS_FREE(x, y + 1))
4996 InitMovingField(x, y, MV_DOWN);
4997 started_moving = TRUE;
4999 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5000 Store[x][y] = EL_CHANGED(Store[x][y]);
5002 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5004 if (!MovDelay[x][y])
5005 MovDelay[x][y] = TILEY/4 + 1;
5014 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5015 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5016 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5020 else if (element == EL_BD_MAGIC_WALL_FULL)
5022 if (IS_FREE(x, y + 1))
5024 InitMovingField(x, y, MV_DOWN);
5025 started_moving = TRUE;
5027 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5028 Store[x][y] = EL_CHANGED2(Store[x][y]);
5030 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5032 if (!MovDelay[x][y])
5033 MovDelay[x][y] = TILEY/4 + 1;
5042 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5043 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5044 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5048 else if (CAN_PASS_MAGIC_WALL(element) &&
5049 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5050 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5052 InitMovingField(x, y, MV_DOWN);
5053 started_moving = TRUE;
5056 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5057 EL_BD_MAGIC_WALL_FILLING);
5058 Store[x][y] = element;
5061 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5063 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5066 SplashAcid(x, y + 1);
5068 InitMovingField(x, y, MV_DOWN);
5069 started_moving = TRUE;
5071 Store[x][y] = EL_ACID;
5073 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5074 GfxAction[x][y + 1] = ACTION_ACTIVE;
5078 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5079 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5081 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5082 CAN_SMASH(element) && WasJustFalling[x][y] &&
5083 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5085 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5086 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5087 (Feld[x][y + 1] == EL_BLOCKED)))
5091 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5092 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5093 WasJustMoving[x][y] && !Pushed[x][y + 1])
5095 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5096 WasJustMoving[x][y])
5101 /* this is needed for a special case not covered by calling "Impact()"
5102 from "ContinueMoving()": if an element moves to a tile directly below
5103 another element which was just falling on that tile (which was empty
5104 in the previous frame), the falling element above would just stop
5105 instead of smashing the element below (in previous version, the above
5106 element was just checked for "moving" instead of "falling", resulting
5107 in incorrect smashes caused by horizontal movement of the above
5108 element; also, the case of the player being the element to smash was
5109 simply not covered here... :-/ ) */
5112 WasJustMoving[x][y] = 0;
5113 WasJustFalling[x][y] = 0;
5116 CheckCollision[x][y] = 0;
5120 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5122 if (MovDir[x][y] == MV_NO_MOVING)
5124 InitMovingField(x, y, MV_DOWN);
5125 started_moving = TRUE;
5128 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5130 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5131 MovDir[x][y] = MV_DOWN;
5133 InitMovingField(x, y, MV_DOWN);
5134 started_moving = TRUE;
5136 else if (element == EL_AMOEBA_DROP)
5138 Feld[x][y] = EL_AMOEBA_GROWING;
5139 Store[x][y] = EL_AMOEBA_WET;
5141 /* Store[x][y + 1] must be zero, because:
5142 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5145 #if OLD_GAME_BEHAVIOUR
5146 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5148 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5149 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5150 element != EL_DX_SUPABOMB)
5153 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5154 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5155 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5156 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5159 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5160 (IS_FREE(x - 1, y + 1) ||
5161 Feld[x - 1][y + 1] == EL_ACID));
5162 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5163 (IS_FREE(x + 1, y + 1) ||
5164 Feld[x + 1][y + 1] == EL_ACID));
5165 boolean can_fall_any = (can_fall_left || can_fall_right);
5166 boolean can_fall_both = (can_fall_left && can_fall_right);
5168 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5170 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5172 if (slippery_type == SLIPPERY_ONLY_LEFT)
5173 can_fall_right = FALSE;
5174 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5175 can_fall_left = FALSE;
5176 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5177 can_fall_right = FALSE;
5178 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5179 can_fall_left = FALSE;
5181 can_fall_any = (can_fall_left || can_fall_right);
5182 can_fall_both = (can_fall_left && can_fall_right);
5187 if (can_fall_both &&
5188 (game.emulation != EMU_BOULDERDASH &&
5189 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5190 can_fall_left = !(can_fall_right = RND(2));
5192 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5193 started_moving = TRUE;
5197 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5199 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5202 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5203 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5204 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5205 int belt_dir = game.belt_dir[belt_nr];
5207 if ((belt_dir == MV_LEFT && left_is_free) ||
5208 (belt_dir == MV_RIGHT && right_is_free))
5211 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5214 InitMovingField(x, y, belt_dir);
5215 started_moving = TRUE;
5218 Pushed[x][y] = TRUE;
5219 Pushed[nextx][y] = TRUE;
5222 GfxAction[x][y] = ACTION_DEFAULT;
5226 MovDir[x][y] = 0; /* if element was moving, stop it */
5231 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5233 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5235 if (CAN_MOVE(element) && !started_moving)
5238 int move_pattern = element_info[element].move_pattern;
5243 if (MovDir[x][y] == MV_NO_MOVING)
5245 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5246 x, y, element, element_info[element].token_name);
5247 printf("StartMoving(): This should never happen!\n");
5252 Moving2Blocked(x, y, &newx, &newy);
5255 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5258 if ((element == EL_SATELLITE ||
5259 element == EL_BALLOON ||
5260 element == EL_SPRING)
5261 && JustBeingPushed(x, y))
5268 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5269 CheckCollision[x][y] && IN_LEV_FIELD_AND_NOT_FREE(newx, newy))
5271 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5272 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5273 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5277 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5278 element, element_info[element].token_name,
5279 WasJustMoving[x][y],
5280 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5281 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5282 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5283 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5287 WasJustMoving[x][y] = 0;
5290 CheckCollision[x][y] = 0;
5292 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5295 if (Feld[x][y] != element) /* element has changed */
5297 element = Feld[x][y];
5298 move_pattern = element_info[element].move_pattern;
5300 if (!CAN_MOVE(element))
5304 if (Feld[x][y] != element) /* element has changed */
5312 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5313 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5315 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5317 Moving2Blocked(x, y, &newx, &newy);
5318 if (Feld[newx][newy] == EL_BLOCKED)
5319 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5325 if (FrameCounter < 1 && x == 0 && y == 29)
5326 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5329 if (!MovDelay[x][y]) /* start new movement phase */
5331 /* all objects that can change their move direction after each step
5332 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5334 if (element != EL_YAMYAM &&
5335 element != EL_DARK_YAMYAM &&
5336 element != EL_PACMAN &&
5337 !(move_pattern & MV_ANY_DIRECTION) &&
5338 move_pattern != MV_TURNING_LEFT &&
5339 move_pattern != MV_TURNING_RIGHT &&
5340 move_pattern != MV_TURNING_LEFT_RIGHT &&
5341 move_pattern != MV_TURNING_RIGHT_LEFT &&
5342 move_pattern != MV_TURNING_RANDOM)
5347 if (FrameCounter < 1 && x == 0 && y == 29)
5348 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5351 if (MovDelay[x][y] && (element == EL_BUG ||
5352 element == EL_SPACESHIP ||
5353 element == EL_SP_SNIKSNAK ||
5354 element == EL_SP_ELECTRON ||
5355 element == EL_MOLE))
5356 DrawLevelField(x, y);
5360 if (MovDelay[x][y]) /* wait some time before next movement */
5365 if (element == EL_YAMYAM)
5368 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5369 DrawLevelElementAnimation(x, y, element);
5373 if (MovDelay[x][y]) /* element still has to wait some time */
5376 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5377 ResetGfxAnimation(x, y);
5381 if (GfxAction[x][y] != ACTION_WAITING)
5382 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5384 GfxAction[x][y] = ACTION_WAITING;
5388 if (element == EL_ROBOT ||
5390 element == EL_PACMAN ||
5392 element == EL_YAMYAM ||
5393 element == EL_DARK_YAMYAM)
5396 DrawLevelElementAnimation(x, y, element);
5398 DrawLevelElementAnimationIfNeeded(x, y, element);
5400 PlayLevelSoundAction(x, y, ACTION_WAITING);
5402 else if (element == EL_SP_ELECTRON)
5403 DrawLevelElementAnimationIfNeeded(x, y, element);
5404 else if (element == EL_DRAGON)
5407 int dir = MovDir[x][y];
5408 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5409 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5410 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5411 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5412 dir == MV_UP ? IMG_FLAMES_1_UP :
5413 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5414 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5417 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5420 GfxAction[x][y] = ACTION_ATTACKING;
5422 if (IS_PLAYER(x, y))
5423 DrawPlayerField(x, y);
5425 DrawLevelField(x, y);
5427 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5429 for (i = 1; i <= 3; i++)
5431 int xx = x + i * dx;
5432 int yy = y + i * dy;
5433 int sx = SCREENX(xx);
5434 int sy = SCREENY(yy);
5435 int flame_graphic = graphic + (i - 1);
5437 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5442 int flamed = MovingOrBlocked2Element(xx, yy);
5446 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5448 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5449 RemoveMovingField(xx, yy);
5451 RemoveField(xx, yy);
5453 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5456 RemoveMovingField(xx, yy);
5460 if (ChangeDelay[xx][yy])
5461 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5462 Feld[xx][yy] == EL_BLOCKED));
5466 ChangeDelay[xx][yy] = 0;
5468 Feld[xx][yy] = EL_FLAMES;
5469 if (IN_SCR_FIELD(sx, sy))
5471 DrawLevelFieldCrumbledSand(xx, yy);
5472 DrawGraphic(sx, sy, flame_graphic, frame);
5477 if (Feld[xx][yy] == EL_FLAMES)
5478 Feld[xx][yy] = EL_EMPTY;
5479 DrawLevelField(xx, yy);
5484 if (MovDelay[x][y]) /* element still has to wait some time */
5486 PlayLevelSoundAction(x, y, ACTION_WAITING);
5492 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5493 for all other elements GfxAction will be set by InitMovingField() */
5494 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5495 GfxAction[x][y] = ACTION_MOVING;
5499 /* now make next step */
5501 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5503 if (DONT_COLLIDE_WITH(element) &&
5504 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5505 !PLAYER_ENEMY_PROTECTED(newx, newy))
5508 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5512 /* player killed by element which is deadly when colliding with */
5514 KillHero(PLAYERINFO(newx, newy));
5521 else if (CAN_MOVE_INTO_ACID(element) &&
5522 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5523 (MovDir[x][y] == MV_DOWN ||
5524 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5526 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5527 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5531 else if ((element == EL_PENGUIN ||
5532 element == EL_ROBOT ||
5533 element == EL_SATELLITE ||
5534 element == EL_BALLOON ||
5535 IS_CUSTOM_ELEMENT(element)) &&
5536 IN_LEV_FIELD(newx, newy) &&
5537 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5540 SplashAcid(newx, newy);
5541 Store[x][y] = EL_ACID;
5543 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5545 if (Feld[newx][newy] == EL_EXIT_OPEN)
5549 DrawLevelField(x, y);
5551 Feld[x][y] = EL_EMPTY;
5552 DrawLevelField(x, y);
5555 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5556 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5557 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5559 local_player->friends_still_needed--;
5560 if (!local_player->friends_still_needed &&
5561 !local_player->GameOver && AllPlayersGone)
5562 local_player->LevelSolved = local_player->GameOver = TRUE;
5566 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5568 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5569 DrawLevelField(newx, newy);
5571 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5573 else if (!IS_FREE(newx, newy))
5575 GfxAction[x][y] = ACTION_WAITING;
5577 if (IS_PLAYER(x, y))
5578 DrawPlayerField(x, y);
5580 DrawLevelField(x, y);
5585 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5587 if (IS_FOOD_PIG(Feld[newx][newy]))
5589 if (IS_MOVING(newx, newy))
5590 RemoveMovingField(newx, newy);
5593 Feld[newx][newy] = EL_EMPTY;
5594 DrawLevelField(newx, newy);
5597 PlayLevelSound(x, y, SND_PIG_DIGGING);
5599 else if (!IS_FREE(newx, newy))
5601 if (IS_PLAYER(x, y))
5602 DrawPlayerField(x, y);
5604 DrawLevelField(x, y);
5613 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5616 else if (IS_CUSTOM_ELEMENT(element) &&
5617 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5621 !IS_FREE(newx, newy)
5626 int new_element = Feld[newx][newy];
5629 printf("::: '%s' digs '%s' [%d]\n",
5630 element_info[element].token_name,
5631 element_info[Feld[newx][newy]].token_name,
5632 StorePlayer[newx][newy]);
5635 if (!IS_FREE(newx, newy))
5637 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5638 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5641 /* no element can dig solid indestructible elements */
5642 if (IS_INDESTRUCTIBLE(new_element) &&
5643 !IS_DIGGABLE(new_element) &&
5644 !IS_COLLECTIBLE(new_element))
5647 if (AmoebaNr[newx][newy] &&
5648 (new_element == EL_AMOEBA_FULL ||
5649 new_element == EL_BD_AMOEBA ||
5650 new_element == EL_AMOEBA_GROWING))
5652 AmoebaCnt[AmoebaNr[newx][newy]]--;
5653 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5656 if (IS_MOVING(newx, newy))
5657 RemoveMovingField(newx, newy);
5660 RemoveField(newx, newy);
5661 DrawLevelField(newx, newy);
5664 /* if digged element was about to explode, prevent the explosion */
5665 ExplodeField[newx][newy] = EX_TYPE_NONE;
5667 PlayLevelSoundAction(x, y, action);
5672 Store[newx][newy] = EL_EMPTY;
5673 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5674 Store[newx][newy] = element_info[element].move_leave_element;
5676 Store[newx][newy] = EL_EMPTY;
5677 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5678 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5679 Store[newx][newy] = element_info[element].move_leave_element;
5682 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5683 element_info[element].can_leave_element = TRUE;
5686 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5688 RunnerVisit[x][y] = FrameCounter;
5689 PlayerVisit[x][y] /= 8; /* expire player visit path */
5695 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5697 if (!IS_FREE(newx, newy))
5699 if (IS_PLAYER(x, y))
5700 DrawPlayerField(x, y);
5702 DrawLevelField(x, y);
5708 boolean wanna_flame = !RND(10);
5709 int dx = newx - x, dy = newy - y;
5710 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5711 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5712 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5713 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5714 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5715 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5718 IS_CLASSIC_ENEMY(element1) ||
5719 IS_CLASSIC_ENEMY(element2)) &&
5720 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5721 element1 != EL_FLAMES && element2 != EL_FLAMES)
5724 ResetGfxAnimation(x, y);
5725 GfxAction[x][y] = ACTION_ATTACKING;
5728 if (IS_PLAYER(x, y))
5729 DrawPlayerField(x, y);
5731 DrawLevelField(x, y);
5733 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5735 MovDelay[x][y] = 50;
5739 RemoveField(newx, newy);
5741 Feld[newx][newy] = EL_FLAMES;
5742 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5745 RemoveField(newx1, newy1);
5747 Feld[newx1][newy1] = EL_FLAMES;
5749 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5752 RemoveField(newx2, newy2);
5754 Feld[newx2][newy2] = EL_FLAMES;
5761 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5762 Feld[newx][newy] == EL_DIAMOND)
5764 if (IS_MOVING(newx, newy))
5765 RemoveMovingField(newx, newy);
5768 Feld[newx][newy] = EL_EMPTY;
5769 DrawLevelField(newx, newy);
5772 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5774 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5775 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5777 if (AmoebaNr[newx][newy])
5779 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5780 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5781 Feld[newx][newy] == EL_BD_AMOEBA)
5782 AmoebaCnt[AmoebaNr[newx][newy]]--;
5787 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5789 if (IS_MOVING(newx, newy))
5792 RemoveMovingField(newx, newy);
5796 Feld[newx][newy] = EL_EMPTY;
5797 DrawLevelField(newx, newy);
5800 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5802 else if ((element == EL_PACMAN || element == EL_MOLE)
5803 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5805 if (AmoebaNr[newx][newy])
5807 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5808 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5809 Feld[newx][newy] == EL_BD_AMOEBA)
5810 AmoebaCnt[AmoebaNr[newx][newy]]--;
5813 if (element == EL_MOLE)
5815 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5816 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5818 ResetGfxAnimation(x, y);
5819 GfxAction[x][y] = ACTION_DIGGING;
5820 DrawLevelField(x, y);
5822 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5824 return; /* wait for shrinking amoeba */
5826 else /* element == EL_PACMAN */
5828 Feld[newx][newy] = EL_EMPTY;
5829 DrawLevelField(newx, newy);
5830 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5833 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5834 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5835 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5837 /* wait for shrinking amoeba to completely disappear */
5840 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5842 /* object was running against a wall */
5847 if (move_pattern & MV_ANY_DIRECTION &&
5848 move_pattern == MovDir[x][y])
5850 int blocking_element =
5851 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5854 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
5855 element_info[element].token_name,
5856 element_info[blocking_element].token_name,
5860 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5863 element = Feld[x][y]; /* element might have changed */
5868 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5869 DrawLevelElementAnimation(x, y, element);
5871 if (element == EL_BUG ||
5872 element == EL_SPACESHIP ||
5873 element == EL_SP_SNIKSNAK)
5874 DrawLevelField(x, y);
5875 else if (element == EL_MOLE)
5876 DrawLevelField(x, y);
5877 else if (element == EL_BD_BUTTERFLY ||
5878 element == EL_BD_FIREFLY)
5879 DrawLevelElementAnimationIfNeeded(x, y, element);
5880 else if (element == EL_SATELLITE)
5881 DrawLevelElementAnimationIfNeeded(x, y, element);
5882 else if (element == EL_SP_ELECTRON)
5883 DrawLevelElementAnimationIfNeeded(x, y, element);
5886 if (DONT_TOUCH(element))
5887 TestIfBadThingTouchesHero(x, y);
5890 PlayLevelSoundAction(x, y, ACTION_WAITING);
5896 InitMovingField(x, y, MovDir[x][y]);
5898 PlayLevelSoundAction(x, y, ACTION_MOVING);
5902 ContinueMoving(x, y);
5905 void ContinueMoving(int x, int y)
5907 int element = Feld[x][y];
5908 int stored = Store[x][y];
5909 struct ElementInfo *ei = &element_info[element];
5910 int direction = MovDir[x][y];
5911 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5912 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5913 int newx = x + dx, newy = y + dy;
5915 int nextx = newx + dx, nexty = newy + dy;
5918 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5919 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5921 boolean pushed_by_player = Pushed[x][y];
5924 MovPos[x][y] += getElementMoveStepsize(x, y);
5927 if (pushed_by_player && IS_PLAYER(x, y))
5929 /* special case: moving object pushed by player */
5930 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5933 if (pushed_by_player) /* special case: moving object pushed by player */
5934 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5937 if (ABS(MovPos[x][y]) < TILEX)
5939 DrawLevelField(x, y);
5941 return; /* element is still moving */
5944 /* element reached destination field */
5946 Feld[x][y] = EL_EMPTY;
5947 Feld[newx][newy] = element;
5948 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5951 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
5953 element = Feld[newx][newy] = EL_ACID;
5956 else if (element == EL_MOLE)
5958 Feld[x][y] = EL_SAND;
5960 DrawLevelFieldCrumbledSandNeighbours(x, y);
5962 else if (element == EL_QUICKSAND_FILLING)
5964 element = Feld[newx][newy] = get_next_element(element);
5965 Store[newx][newy] = Store[x][y];
5967 else if (element == EL_QUICKSAND_EMPTYING)
5969 Feld[x][y] = get_next_element(element);
5970 element = Feld[newx][newy] = Store[x][y];
5972 else if (element == EL_MAGIC_WALL_FILLING)
5974 element = Feld[newx][newy] = get_next_element(element);
5975 if (!game.magic_wall_active)
5976 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5977 Store[newx][newy] = Store[x][y];
5979 else if (element == EL_MAGIC_WALL_EMPTYING)
5981 Feld[x][y] = get_next_element(element);
5982 if (!game.magic_wall_active)
5983 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5984 element = Feld[newx][newy] = Store[x][y];
5986 else if (element == EL_BD_MAGIC_WALL_FILLING)
5988 element = Feld[newx][newy] = get_next_element(element);
5989 if (!game.magic_wall_active)
5990 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5991 Store[newx][newy] = Store[x][y];
5993 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5995 Feld[x][y] = get_next_element(element);
5996 if (!game.magic_wall_active)
5997 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5998 element = Feld[newx][newy] = Store[x][y];
6000 else if (element == EL_AMOEBA_DROPPING)
6002 Feld[x][y] = get_next_element(element);
6003 element = Feld[newx][newy] = Store[x][y];
6005 else if (element == EL_SOKOBAN_OBJECT)
6008 Feld[x][y] = Back[x][y];
6010 if (Back[newx][newy])
6011 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6013 Back[x][y] = Back[newx][newy] = 0;
6016 else if (Store[x][y] == EL_ACID)
6018 element = Feld[newx][newy] = EL_ACID;
6022 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6023 ei->move_leave_element != EL_EMPTY &&
6024 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6025 Store[x][y] != EL_EMPTY))
6027 /* some elements can leave other elements behind after moving */
6029 Feld[x][y] = ei->move_leave_element;
6030 InitField(x, y, FALSE);
6032 if (GFX_CRUMBLED(Feld[x][y]))
6033 DrawLevelFieldCrumbledSandNeighbours(x, y);
6037 Store[x][y] = EL_EMPTY;
6038 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6039 MovDelay[newx][newy] = 0;
6041 if (CAN_CHANGE(element))
6043 /* copy element change control values to new field */
6044 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6045 ChangePage[newx][newy] = ChangePage[x][y];
6046 Changed[newx][newy] = Changed[x][y];
6047 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6050 ChangeDelay[x][y] = 0;
6051 ChangePage[x][y] = -1;
6052 Changed[x][y] = CE_BITMASK_DEFAULT;
6053 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6055 /* copy animation control values to new field */
6056 GfxFrame[newx][newy] = GfxFrame[x][y];
6057 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6058 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6059 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6061 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6063 ResetGfxAnimation(x, y); /* reset animation values for old field */
6066 /* some elements can leave other elements behind after moving */
6068 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6069 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6070 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6072 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6073 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6077 int move_leave_element = ei->move_leave_element;
6079 Feld[x][y] = move_leave_element;
6080 InitField(x, y, FALSE);
6082 if (GFX_CRUMBLED(Feld[x][y]))
6083 DrawLevelFieldCrumbledSandNeighbours(x, y);
6085 if (ELEM_IS_PLAYER(move_leave_element))
6086 RelocatePlayer(x, y, move_leave_element);
6091 /* some elements can leave other elements behind after moving */
6092 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6093 ei->move_leave_element != EL_EMPTY &&
6094 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6095 ei->can_leave_element_last))
6097 Feld[x][y] = ei->move_leave_element;
6098 InitField(x, y, FALSE);
6100 if (GFX_CRUMBLED(Feld[x][y]))
6101 DrawLevelFieldCrumbledSandNeighbours(x, y);
6104 ei->can_leave_element_last = ei->can_leave_element;
6105 ei->can_leave_element = FALSE;
6109 /* 2.1.1 (does not work correctly for spring) */
6110 if (!CAN_MOVE(element))
6111 MovDir[newx][newy] = 0;
6115 /* (does not work for falling objects that slide horizontally) */
6116 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6117 MovDir[newx][newy] = 0;
6120 if (!CAN_MOVE(element) ||
6121 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6122 MovDir[newx][newy] = 0;
6126 if (!CAN_MOVE(element) ||
6127 (CAN_FALL(element) && direction == MV_DOWN))
6128 GfxDir[x][y] = MovDir[newx][newy] = 0;
6130 if (!CAN_MOVE(element) ||
6131 (CAN_FALL(element) && direction == MV_DOWN &&
6132 (element == EL_SPRING ||
6133 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6134 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6135 GfxDir[x][y] = MovDir[newx][newy] = 0;
6141 DrawLevelField(x, y);
6142 DrawLevelField(newx, newy);
6144 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6146 /* prevent pushed element from moving on in pushed direction */
6147 if (pushed_by_player && CAN_MOVE(element) &&
6148 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6149 !(element_info[element].move_pattern & direction))
6150 TurnRound(newx, newy);
6153 /* prevent elements on conveyor belt from moving on in last direction */
6154 if (pushed_by_conveyor && CAN_FALL(element) &&
6155 direction & MV_HORIZONTAL)
6158 if (CAN_MOVE(element))
6159 InitMovDir(newx, newy);
6161 MovDir[newx][newy] = 0;
6163 MovDir[newx][newy] = 0;
6168 if (!pushed_by_player)
6170 int nextx = newx + dx, nexty = newy + dy;
6171 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6173 WasJustMoving[newx][newy] = 3;
6175 if (CAN_FALL(element) && direction == MV_DOWN)
6176 WasJustFalling[newx][newy] = 3;
6178 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6179 CheckCollision[newx][newy] = 2;
6182 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6184 TestIfBadThingTouchesHero(newx, newy);
6185 TestIfBadThingTouchesFriend(newx, newy);
6187 if (!IS_CUSTOM_ELEMENT(element))
6188 TestIfBadThingTouchesOtherBadThing(newx, newy);
6190 else if (element == EL_PENGUIN)
6191 TestIfFriendTouchesBadThing(newx, newy);
6193 if (CAN_FALL(element) && direction == MV_DOWN &&
6194 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6198 if (pushed_by_player)
6200 static int trigger_sides[4] =
6202 CH_SIDE_RIGHT, /* moving left */
6203 CH_SIDE_LEFT, /* moving right */
6204 CH_SIDE_BOTTOM, /* moving up */
6205 CH_SIDE_TOP, /* moving down */
6207 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6208 struct PlayerInfo *player = PLAYERINFO(x, y);
6210 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6211 player->index_bit, dig_side);
6212 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6213 player->index_bit, dig_side);
6218 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6222 if (ChangePage[newx][newy] != -1) /* delayed change */
6223 ChangeElement(newx, newy, ChangePage[newx][newy]);
6228 TestIfElementHitsCustomElement(newx, newy, direction);
6232 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6234 int hitting_element = Feld[newx][newy];
6236 /* !!! fix side (direction) orientation here and elsewhere !!! */
6237 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6241 if (IN_LEV_FIELD(nextx, nexty))
6243 int opposite_direction = MV_DIR_OPPOSITE(direction);
6244 int hitting_side = direction;
6245 int touched_side = opposite_direction;
6246 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6247 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6248 MovDir[nextx][nexty] != direction ||
6249 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6255 CheckElementChangeBySide(nextx, nexty, touched_element,
6256 CE_HIT_BY_SOMETHING, opposite_direction);
6258 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6259 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6261 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6263 struct ElementChangeInfo *change =
6264 &element_info[hitting_element].change_page[i];
6266 if (change->can_change &&
6267 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6268 change->trigger_side & touched_side &&
6269 change->trigger_element == touched_element)
6271 CheckElementChangeByPage(newx, newy, hitting_element,
6272 touched_element, CE_OTHER_IS_HITTING,i);
6278 if (IS_CUSTOM_ELEMENT(touched_element) &&
6279 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6281 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6283 struct ElementChangeInfo *change =
6284 &element_info[touched_element].change_page[i];
6286 if (change->can_change &&
6287 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6288 change->trigger_side & hitting_side &&
6289 change->trigger_element == hitting_element)
6291 CheckElementChangeByPage(nextx, nexty, touched_element,
6292 hitting_element, CE_OTHER_GETS_HIT, i);
6303 TestIfPlayerTouchesCustomElement(newx, newy);
6304 TestIfElementTouchesCustomElement(newx, newy);
6307 int AmoebeNachbarNr(int ax, int ay)
6310 int element = Feld[ax][ay];
6312 static int xy[4][2] =
6320 for (i = 0; i < NUM_DIRECTIONS; i++)
6322 int x = ax + xy[i][0];
6323 int y = ay + xy[i][1];
6325 if (!IN_LEV_FIELD(x, y))
6328 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6329 group_nr = AmoebaNr[x][y];
6335 void AmoebenVereinigen(int ax, int ay)
6337 int i, x, y, xx, yy;
6338 int new_group_nr = AmoebaNr[ax][ay];
6339 static int xy[4][2] =
6347 if (new_group_nr == 0)
6350 for (i = 0; i < NUM_DIRECTIONS; i++)
6355 if (!IN_LEV_FIELD(x, y))
6358 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6359 Feld[x][y] == EL_BD_AMOEBA ||
6360 Feld[x][y] == EL_AMOEBA_DEAD) &&
6361 AmoebaNr[x][y] != new_group_nr)
6363 int old_group_nr = AmoebaNr[x][y];
6365 if (old_group_nr == 0)
6368 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6369 AmoebaCnt[old_group_nr] = 0;
6370 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6371 AmoebaCnt2[old_group_nr] = 0;
6373 for (yy = 0; yy < lev_fieldy; yy++)
6375 for (xx = 0; xx < lev_fieldx; xx++)
6377 if (AmoebaNr[xx][yy] == old_group_nr)
6378 AmoebaNr[xx][yy] = new_group_nr;
6385 void AmoebeUmwandeln(int ax, int ay)
6389 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6391 int group_nr = AmoebaNr[ax][ay];
6396 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6397 printf("AmoebeUmwandeln(): This should never happen!\n");
6402 for (y = 0; y < lev_fieldy; y++)
6404 for (x = 0; x < lev_fieldx; x++)
6406 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6409 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6413 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6414 SND_AMOEBA_TURNING_TO_GEM :
6415 SND_AMOEBA_TURNING_TO_ROCK));
6420 static int xy[4][2] =
6428 for (i = 0; i < NUM_DIRECTIONS; i++)
6433 if (!IN_LEV_FIELD(x, y))
6436 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6438 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6439 SND_AMOEBA_TURNING_TO_GEM :
6440 SND_AMOEBA_TURNING_TO_ROCK));
6447 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6450 int group_nr = AmoebaNr[ax][ay];
6451 boolean done = FALSE;
6456 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6457 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6462 for (y = 0; y < lev_fieldy; y++)
6464 for (x = 0; x < lev_fieldx; x++)
6466 if (AmoebaNr[x][y] == group_nr &&
6467 (Feld[x][y] == EL_AMOEBA_DEAD ||
6468 Feld[x][y] == EL_BD_AMOEBA ||
6469 Feld[x][y] == EL_AMOEBA_GROWING))
6472 Feld[x][y] = new_element;
6473 InitField(x, y, FALSE);
6474 DrawLevelField(x, y);
6481 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6482 SND_BD_AMOEBA_TURNING_TO_ROCK :
6483 SND_BD_AMOEBA_TURNING_TO_GEM));
6486 void AmoebeWaechst(int x, int y)
6488 static unsigned long sound_delay = 0;
6489 static unsigned long sound_delay_value = 0;
6491 if (!MovDelay[x][y]) /* start new growing cycle */
6495 if (DelayReached(&sound_delay, sound_delay_value))
6498 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6500 if (Store[x][y] == EL_BD_AMOEBA)
6501 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6503 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6505 sound_delay_value = 30;
6509 if (MovDelay[x][y]) /* wait some time before growing bigger */
6512 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6514 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6515 6 - MovDelay[x][y]);
6517 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6520 if (!MovDelay[x][y])
6522 Feld[x][y] = Store[x][y];
6524 DrawLevelField(x, y);
6529 void AmoebaDisappearing(int x, int y)
6531 static unsigned long sound_delay = 0;
6532 static unsigned long sound_delay_value = 0;
6534 if (!MovDelay[x][y]) /* start new shrinking cycle */
6538 if (DelayReached(&sound_delay, sound_delay_value))
6539 sound_delay_value = 30;
6542 if (MovDelay[x][y]) /* wait some time before shrinking */
6545 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6547 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6548 6 - MovDelay[x][y]);
6550 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6553 if (!MovDelay[x][y])
6555 Feld[x][y] = EL_EMPTY;
6556 DrawLevelField(x, y);
6558 /* don't let mole enter this field in this cycle;
6559 (give priority to objects falling to this field from above) */
6565 void AmoebeAbleger(int ax, int ay)
6568 int element = Feld[ax][ay];
6569 int graphic = el2img(element);
6570 int newax = ax, neway = ay;
6571 static int xy[4][2] =
6579 if (!level.amoeba_speed)
6581 Feld[ax][ay] = EL_AMOEBA_DEAD;
6582 DrawLevelField(ax, ay);
6586 if (IS_ANIMATED(graphic))
6587 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6589 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6590 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6592 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6595 if (MovDelay[ax][ay])
6599 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6602 int x = ax + xy[start][0];
6603 int y = ay + xy[start][1];
6605 if (!IN_LEV_FIELD(x, y))
6609 if (IS_FREE(x, y) ||
6610 CAN_GROW_INTO(Feld[x][y]) ||
6611 Feld[x][y] == EL_QUICKSAND_EMPTY)
6617 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6618 if (IS_FREE(x, y) ||
6619 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6626 if (newax == ax && neway == ay)
6629 else /* normal or "filled" (BD style) amoeba */
6632 boolean waiting_for_player = FALSE;
6634 for (i = 0; i < NUM_DIRECTIONS; i++)
6636 int j = (start + i) % 4;
6637 int x = ax + xy[j][0];
6638 int y = ay + xy[j][1];
6640 if (!IN_LEV_FIELD(x, y))
6644 if (IS_FREE(x, y) ||
6645 CAN_GROW_INTO(Feld[x][y]) ||
6646 Feld[x][y] == EL_QUICKSAND_EMPTY)
6653 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6654 if (IS_FREE(x, y) ||
6655 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6662 else if (IS_PLAYER(x, y))
6663 waiting_for_player = TRUE;
6666 if (newax == ax && neway == ay) /* amoeba cannot grow */
6669 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6671 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6674 Feld[ax][ay] = EL_AMOEBA_DEAD;
6675 DrawLevelField(ax, ay);
6676 AmoebaCnt[AmoebaNr[ax][ay]]--;
6678 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6680 if (element == EL_AMOEBA_FULL)
6681 AmoebeUmwandeln(ax, ay);
6682 else if (element == EL_BD_AMOEBA)
6683 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6688 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6690 /* amoeba gets larger by growing in some direction */
6692 int new_group_nr = AmoebaNr[ax][ay];
6695 if (new_group_nr == 0)
6697 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6698 printf("AmoebeAbleger(): This should never happen!\n");
6703 AmoebaNr[newax][neway] = new_group_nr;
6704 AmoebaCnt[new_group_nr]++;
6705 AmoebaCnt2[new_group_nr]++;
6707 /* if amoeba touches other amoeba(s) after growing, unify them */
6708 AmoebenVereinigen(newax, neway);
6710 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6712 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6718 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6719 (neway == lev_fieldy - 1 && newax != ax))
6721 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6722 Store[newax][neway] = element;
6724 else if (neway == ay)
6726 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6728 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6730 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6735 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6736 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6737 Store[ax][ay] = EL_AMOEBA_DROP;
6738 ContinueMoving(ax, ay);
6742 DrawLevelField(newax, neway);
6745 void Life(int ax, int ay)
6748 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6750 int element = Feld[ax][ay];
6751 int graphic = el2img(element);
6752 boolean changed = FALSE;
6754 if (IS_ANIMATED(graphic))
6755 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6760 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6761 MovDelay[ax][ay] = life_time;
6763 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6766 if (MovDelay[ax][ay])
6770 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6772 int xx = ax+x1, yy = ay+y1;
6775 if (!IN_LEV_FIELD(xx, yy))
6778 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6780 int x = xx+x2, y = yy+y2;
6782 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6785 if (((Feld[x][y] == element ||
6786 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6788 (IS_FREE(x, y) && Stop[x][y]))
6792 if (xx == ax && yy == ay) /* field in the middle */
6794 if (nachbarn < life[0] || nachbarn > life[1])
6796 Feld[xx][yy] = EL_EMPTY;
6798 DrawLevelField(xx, yy);
6799 Stop[xx][yy] = TRUE;
6804 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6805 { /* free border field */
6806 if (nachbarn >= life[2] && nachbarn <= life[3])
6808 Feld[xx][yy] = element;
6809 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6811 DrawLevelField(xx, yy);
6812 Stop[xx][yy] = TRUE;
6817 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6818 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6819 { /* free border field */
6820 if (nachbarn >= life[2] && nachbarn <= life[3])
6822 Feld[xx][yy] = element;
6823 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6825 DrawLevelField(xx, yy);
6826 Stop[xx][yy] = TRUE;
6834 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6835 SND_GAME_OF_LIFE_GROWING);
6838 static void InitRobotWheel(int x, int y)
6840 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6843 static void RunRobotWheel(int x, int y)
6845 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6848 static void StopRobotWheel(int x, int y)
6850 if (ZX == x && ZY == y)
6854 static void InitTimegateWheel(int x, int y)
6857 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6859 /* another brainless, "type style" bug ... :-( */
6860 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6864 static void RunTimegateWheel(int x, int y)
6866 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6869 void CheckExit(int x, int y)
6871 if (local_player->gems_still_needed > 0 ||
6872 local_player->sokobanfields_still_needed > 0 ||
6873 local_player->lights_still_needed > 0)
6875 int element = Feld[x][y];
6876 int graphic = el2img(element);
6878 if (IS_ANIMATED(graphic))
6879 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6884 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6887 Feld[x][y] = EL_EXIT_OPENING;
6889 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6892 void CheckExitSP(int x, int y)
6894 if (local_player->gems_still_needed > 0)
6896 int element = Feld[x][y];
6897 int graphic = el2img(element);
6899 if (IS_ANIMATED(graphic))
6900 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6905 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6908 Feld[x][y] = EL_SP_EXIT_OPENING;
6910 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6913 static void CloseAllOpenTimegates()
6917 for (y = 0; y < lev_fieldy; y++)
6919 for (x = 0; x < lev_fieldx; x++)
6921 int element = Feld[x][y];
6923 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6925 Feld[x][y] = EL_TIMEGATE_CLOSING;
6927 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6929 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6936 void EdelsteinFunkeln(int x, int y)
6938 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6941 if (Feld[x][y] == EL_BD_DIAMOND)
6944 if (MovDelay[x][y] == 0) /* next animation frame */
6945 MovDelay[x][y] = 11 * !SimpleRND(500);
6947 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6951 if (setup.direct_draw && MovDelay[x][y])
6952 SetDrawtoField(DRAW_BUFFERED);
6954 DrawLevelElementAnimation(x, y, Feld[x][y]);
6956 if (MovDelay[x][y] != 0)
6958 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6959 10 - MovDelay[x][y]);
6961 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6963 if (setup.direct_draw)
6967 dest_x = FX + SCREENX(x) * TILEX;
6968 dest_y = FY + SCREENY(y) * TILEY;
6970 BlitBitmap(drawto_field, window,
6971 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6972 SetDrawtoField(DRAW_DIRECT);
6978 void MauerWaechst(int x, int y)
6982 if (!MovDelay[x][y]) /* next animation frame */
6983 MovDelay[x][y] = 3 * delay;
6985 if (MovDelay[x][y]) /* wait some time before next frame */
6989 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6991 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6992 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6994 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6997 if (!MovDelay[x][y])
6999 if (MovDir[x][y] == MV_LEFT)
7001 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7002 DrawLevelField(x - 1, y);
7004 else if (MovDir[x][y] == MV_RIGHT)
7006 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7007 DrawLevelField(x + 1, y);
7009 else if (MovDir[x][y] == MV_UP)
7011 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7012 DrawLevelField(x, y - 1);
7016 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7017 DrawLevelField(x, y + 1);
7020 Feld[x][y] = Store[x][y];
7022 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7023 DrawLevelField(x, y);
7028 void MauerAbleger(int ax, int ay)
7030 int element = Feld[ax][ay];
7031 int graphic = el2img(element);
7032 boolean oben_frei = FALSE, unten_frei = FALSE;
7033 boolean links_frei = FALSE, rechts_frei = FALSE;
7034 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7035 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7036 boolean new_wall = FALSE;
7038 if (IS_ANIMATED(graphic))
7039 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7041 if (!MovDelay[ax][ay]) /* start building new wall */
7042 MovDelay[ax][ay] = 6;
7044 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7047 if (MovDelay[ax][ay])
7051 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7053 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7055 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7057 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7060 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7061 element == EL_EXPANDABLE_WALL_ANY)
7065 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7066 Store[ax][ay-1] = element;
7067 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7068 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7069 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7070 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7075 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7076 Store[ax][ay+1] = element;
7077 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7078 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7079 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7080 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7085 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7086 element == EL_EXPANDABLE_WALL_ANY ||
7087 element == EL_EXPANDABLE_WALL)
7091 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7092 Store[ax-1][ay] = element;
7093 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7094 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7095 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7096 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7102 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7103 Store[ax+1][ay] = element;
7104 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7105 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7106 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7107 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7112 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7113 DrawLevelField(ax, ay);
7115 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7117 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7118 unten_massiv = TRUE;
7119 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7120 links_massiv = TRUE;
7121 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7122 rechts_massiv = TRUE;
7124 if (((oben_massiv && unten_massiv) ||
7125 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7126 element == EL_EXPANDABLE_WALL) &&
7127 ((links_massiv && rechts_massiv) ||
7128 element == EL_EXPANDABLE_WALL_VERTICAL))
7129 Feld[ax][ay] = EL_WALL;
7133 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7135 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7139 void CheckForDragon(int x, int y)
7142 boolean dragon_found = FALSE;
7143 static int xy[4][2] =
7151 for (i = 0; i < NUM_DIRECTIONS; i++)
7153 for (j = 0; j < 4; j++)
7155 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7157 if (IN_LEV_FIELD(xx, yy) &&
7158 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7160 if (Feld[xx][yy] == EL_DRAGON)
7161 dragon_found = TRUE;
7170 for (i = 0; i < NUM_DIRECTIONS; i++)
7172 for (j = 0; j < 3; j++)
7174 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7176 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7178 Feld[xx][yy] = EL_EMPTY;
7179 DrawLevelField(xx, yy);
7188 static void InitBuggyBase(int x, int y)
7190 int element = Feld[x][y];
7191 int activating_delay = FRAMES_PER_SECOND / 4;
7194 (element == EL_SP_BUGGY_BASE ?
7195 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7196 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7198 element == EL_SP_BUGGY_BASE_ACTIVE ?
7199 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7202 static void WarnBuggyBase(int x, int y)
7205 static int xy[4][2] =
7213 for (i = 0; i < NUM_DIRECTIONS; i++)
7215 int xx = x + xy[i][0], yy = y + xy[i][1];
7217 if (IS_PLAYER(xx, yy))
7219 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7226 static void InitTrap(int x, int y)
7228 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7231 static void ActivateTrap(int x, int y)
7233 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7236 static void ChangeActiveTrap(int x, int y)
7238 int graphic = IMG_TRAP_ACTIVE;
7240 /* if new animation frame was drawn, correct crumbled sand border */
7241 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7242 DrawLevelFieldCrumbledSand(x, y);
7245 static void ChangeElementNowExt(int x, int y, int target_element)
7247 int previous_move_direction = MovDir[x][y];
7248 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7249 IS_WALKABLE(Feld[x][y]) &&
7252 /* check if element under player changes from accessible to unaccessible
7253 (needed for special case of dropping element which then changes) */
7254 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7255 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7266 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7267 RemoveMovingField(x, y);
7271 Feld[x][y] = target_element;
7274 Feld[x][y] = target_element;
7277 ResetGfxAnimation(x, y);
7278 ResetRandomAnimationValue(x, y);
7280 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7281 MovDir[x][y] = previous_move_direction;
7284 InitField_WithBug1(x, y, FALSE);
7286 InitField(x, y, FALSE);
7287 if (CAN_MOVE(Feld[x][y]))
7291 DrawLevelField(x, y);
7293 if (GFX_CRUMBLED(Feld[x][y]))
7294 DrawLevelFieldCrumbledSandNeighbours(x, y);
7297 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7300 TestIfBadThingTouchesHero(x, y);
7301 TestIfPlayerTouchesCustomElement(x, y);
7302 TestIfElementTouchesCustomElement(x, y);
7305 if (ELEM_IS_PLAYER(target_element))
7306 RelocatePlayer(x, y, target_element);
7309 TestIfBadThingTouchesHero(x, y);
7310 TestIfPlayerTouchesCustomElement(x, y);
7311 TestIfElementTouchesCustomElement(x, y);
7315 static boolean ChangeElementNow(int x, int y, int element, int page)
7317 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7320 /* always use default change event to prevent running into a loop */
7321 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7322 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7324 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7326 /* reset actual trigger element and player */
7327 change->actual_trigger_element = EL_EMPTY;
7328 change->actual_trigger_player = EL_PLAYER_1;
7331 /* do not change already changed elements with same change event */
7333 if (Changed[x][y] & ChangeEvent[x][y])
7340 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7342 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7344 if (change->explode)
7351 if (change->use_target_content)
7353 boolean complete_replace = TRUE;
7354 boolean can_replace[3][3];
7357 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7360 boolean is_diggable;
7361 boolean is_collectible;
7362 boolean is_removable;
7363 boolean is_destructible;
7364 int ex = x + xx - 1;
7365 int ey = y + yy - 1;
7366 int content_element = change->target_content[xx][yy];
7369 can_replace[xx][yy] = TRUE;
7371 if (ex == x && ey == y) /* do not check changing element itself */
7374 if (content_element == EL_EMPTY_SPACE)
7376 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7381 if (!IN_LEV_FIELD(ex, ey))
7383 can_replace[xx][yy] = FALSE;
7384 complete_replace = FALSE;
7391 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7392 e = MovingOrBlocked2Element(ex, ey);
7397 is_empty = (IS_FREE(ex, ey) ||
7398 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7399 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7400 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7402 is_empty = (IS_FREE(ex, ey) || (IS_PLAYER(ex, ey) &&
7403 IS_WALKABLE(content_element)));
7405 is_diggable = (is_empty || IS_DIGGABLE(e));
7406 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7407 is_removable = (is_diggable || is_collectible);
7408 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7410 can_replace[xx][yy] =
7411 ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7412 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7413 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7414 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7415 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7417 if (!can_replace[xx][yy])
7418 complete_replace = FALSE;
7420 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7421 IS_WALKABLE(content_element)));
7423 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7425 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7428 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7429 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7430 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7432 can_replace[xx][yy] = FALSE;
7433 complete_replace = FALSE;
7438 if (!change->only_if_complete || complete_replace)
7440 boolean something_has_changed = FALSE;
7442 if (change->only_if_complete && change->use_random_replace &&
7443 RND(100) < change->random_percentage)
7446 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7448 int ex = x + xx - 1;
7449 int ey = y + yy - 1;
7450 int content_element;
7452 if (can_replace[xx][yy] && (!change->use_random_replace ||
7453 RND(100) < change->random_percentage))
7455 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7456 RemoveMovingField(ex, ey);
7458 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7460 content_element = change->target_content[xx][yy];
7461 target_element = GET_TARGET_ELEMENT(content_element, change);
7463 ChangeElementNowExt(ex, ey, target_element);
7465 something_has_changed = TRUE;
7467 /* for symmetry reasons, freeze newly created border elements */
7468 if (ex != x || ey != y)
7469 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7473 if (something_has_changed)
7474 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7479 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7481 ChangeElementNowExt(x, y, target_element);
7483 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7489 static void ChangeElement(int x, int y, int page)
7491 int element = MovingOrBlocked2Element(x, y);
7492 struct ElementInfo *ei = &element_info[element];
7493 struct ElementChangeInfo *change = &ei->change_page[page];
7496 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7499 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7500 x, y, element, element_info[element].token_name);
7501 printf("ChangeElement(): This should never happen!\n");
7506 /* this can happen with classic bombs on walkable, changing elements */
7507 if (!CAN_CHANGE(element))
7510 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7511 ChangeDelay[x][y] = 0;
7517 if (ChangeDelay[x][y] == 0) /* initialize element change */
7519 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7520 RND(change->delay_random * change->delay_frames)) + 1;
7522 ResetGfxAnimation(x, y);
7523 ResetRandomAnimationValue(x, y);
7525 if (change->pre_change_function)
7526 change->pre_change_function(x, y);
7529 ChangeDelay[x][y]--;
7531 if (ChangeDelay[x][y] != 0) /* continue element change */
7533 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7535 if (IS_ANIMATED(graphic))
7536 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7538 if (change->change_function)
7539 change->change_function(x, y);
7541 else /* finish element change */
7543 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7545 page = ChangePage[x][y];
7546 ChangePage[x][y] = -1;
7548 change = &ei->change_page[page];
7552 if (IS_MOVING(x, y) && !change->explode)
7554 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7557 ChangeDelay[x][y] = 1; /* try change after next move step */
7558 ChangePage[x][y] = page; /* remember page to use for change */
7563 if (ChangeElementNow(x, y, element, page))
7565 if (change->post_change_function)
7566 change->post_change_function(x, y);
7571 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7572 int trigger_element,
7579 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7581 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7584 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7586 int element = EL_CUSTOM_START + i;
7588 boolean change_element = FALSE;
7591 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7594 for (j = 0; j < element_info[element].num_change_pages; j++)
7596 struct ElementChangeInfo *change = &element_info[element].change_page[j];
7598 if (change->can_change &&
7599 change->events & CH_EVENT_BIT(trigger_event) &&
7600 change->trigger_side & trigger_side &&
7601 change->trigger_player & trigger_player &&
7602 change->trigger_page & trigger_page_bits &&
7603 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7606 if (!(change->events & CH_EVENT_BIT(trigger_event)))
7607 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7608 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7611 change_element = TRUE;
7614 change->actual_trigger_element = trigger_element;
7615 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7621 if (!change_element)
7624 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7627 if (x == lx && y == ly) /* do not change trigger element itself */
7631 if (Feld[x][y] == element)
7633 ChangeDelay[x][y] = 1;
7634 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7635 ChangeElement(x, y, page);
7643 static boolean CheckElementChangeExt(int x, int y,
7645 int trigger_element,
7651 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7654 if (Feld[x][y] == EL_BLOCKED)
7656 Blocked2Moving(x, y, &x, &y);
7657 element = Feld[x][y];
7661 if (Feld[x][y] != element) /* check if element has already changed */
7664 printf("::: %d ('%s') != %d ('%s') [%d]\n",
7665 Feld[x][y], element_info[Feld[x][y]].token_name,
7666 element, element_info[element].token_name,
7675 if (trigger_page < 0)
7677 boolean change_element = FALSE;
7680 for (i = 0; i < element_info[element].num_change_pages; i++)
7682 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7684 if (change->can_change &&
7685 change->events & CH_EVENT_BIT(trigger_event) &&
7686 change->trigger_side & trigger_side &&
7687 change->trigger_player & trigger_player)
7689 change_element = TRUE;
7692 change->actual_trigger_element = trigger_element;
7693 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7699 if (!change_element)
7704 struct ElementInfo *ei = &element_info[element];
7705 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7707 change->actual_trigger_element = trigger_element;
7708 change->actual_trigger_player = EL_PLAYER_1; /* unused */
7713 /* !!! this check misses pages with same event, but different side !!! */
7715 if (trigger_page < 0)
7716 trigger_page = element_info[element].event_page_nr[trigger_event];
7718 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7722 ChangeDelay[x][y] = 1;
7723 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7724 ChangeElement(x, y, trigger_page);
7729 static void PlayPlayerSound(struct PlayerInfo *player)
7731 int jx = player->jx, jy = player->jy;
7732 int element = player->element_nr;
7733 int last_action = player->last_action_waiting;
7734 int action = player->action_waiting;
7736 if (player->is_waiting)
7738 if (action != last_action)
7739 PlayLevelSoundElementAction(jx, jy, element, action);
7741 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7745 if (action != last_action)
7746 StopSound(element_info[element].sound[last_action]);
7748 if (last_action == ACTION_SLEEPING)
7749 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7753 static void PlayAllPlayersSound()
7757 for (i = 0; i < MAX_PLAYERS; i++)
7758 if (stored_player[i].active)
7759 PlayPlayerSound(&stored_player[i]);
7762 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7764 boolean last_waiting = player->is_waiting;
7765 int move_dir = player->MovDir;
7767 player->last_action_waiting = player->action_waiting;
7771 if (!last_waiting) /* not waiting -> waiting */
7773 player->is_waiting = TRUE;
7775 player->frame_counter_bored =
7777 game.player_boring_delay_fixed +
7778 SimpleRND(game.player_boring_delay_random);
7779 player->frame_counter_sleeping =
7781 game.player_sleeping_delay_fixed +
7782 SimpleRND(game.player_sleeping_delay_random);
7784 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7787 if (game.player_sleeping_delay_fixed +
7788 game.player_sleeping_delay_random > 0 &&
7789 player->anim_delay_counter == 0 &&
7790 player->post_delay_counter == 0 &&
7791 FrameCounter >= player->frame_counter_sleeping)
7792 player->is_sleeping = TRUE;
7793 else if (game.player_boring_delay_fixed +
7794 game.player_boring_delay_random > 0 &&
7795 FrameCounter >= player->frame_counter_bored)
7796 player->is_bored = TRUE;
7798 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7799 player->is_bored ? ACTION_BORING :
7802 if (player->is_sleeping)
7804 if (player->num_special_action_sleeping > 0)
7806 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7808 int last_special_action = player->special_action_sleeping;
7809 int num_special_action = player->num_special_action_sleeping;
7810 int special_action =
7811 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7812 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7813 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7814 last_special_action + 1 : ACTION_SLEEPING);
7815 int special_graphic =
7816 el_act_dir2img(player->element_nr, special_action, move_dir);
7818 player->anim_delay_counter =
7819 graphic_info[special_graphic].anim_delay_fixed +
7820 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7821 player->post_delay_counter =
7822 graphic_info[special_graphic].post_delay_fixed +
7823 SimpleRND(graphic_info[special_graphic].post_delay_random);
7825 player->special_action_sleeping = special_action;
7828 if (player->anim_delay_counter > 0)
7830 player->action_waiting = player->special_action_sleeping;
7831 player->anim_delay_counter--;
7833 else if (player->post_delay_counter > 0)
7835 player->post_delay_counter--;
7839 else if (player->is_bored)
7841 if (player->num_special_action_bored > 0)
7843 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7845 int special_action =
7846 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7847 int special_graphic =
7848 el_act_dir2img(player->element_nr, special_action, move_dir);
7850 player->anim_delay_counter =
7851 graphic_info[special_graphic].anim_delay_fixed +
7852 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7853 player->post_delay_counter =
7854 graphic_info[special_graphic].post_delay_fixed +
7855 SimpleRND(graphic_info[special_graphic].post_delay_random);
7857 player->special_action_bored = special_action;
7860 if (player->anim_delay_counter > 0)
7862 player->action_waiting = player->special_action_bored;
7863 player->anim_delay_counter--;
7865 else if (player->post_delay_counter > 0)
7867 player->post_delay_counter--;
7872 else if (last_waiting) /* waiting -> not waiting */
7874 player->is_waiting = FALSE;
7875 player->is_bored = FALSE;
7876 player->is_sleeping = FALSE;
7878 player->frame_counter_bored = -1;
7879 player->frame_counter_sleeping = -1;
7881 player->anim_delay_counter = 0;
7882 player->post_delay_counter = 0;
7884 player->action_waiting = ACTION_DEFAULT;
7886 player->special_action_bored = ACTION_DEFAULT;
7887 player->special_action_sleeping = ACTION_DEFAULT;
7892 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7895 static byte stored_player_action[MAX_PLAYERS];
7896 static int num_stored_actions = 0;
7898 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7899 int left = player_action & JOY_LEFT;
7900 int right = player_action & JOY_RIGHT;
7901 int up = player_action & JOY_UP;
7902 int down = player_action & JOY_DOWN;
7903 int button1 = player_action & JOY_BUTTON_1;
7904 int button2 = player_action & JOY_BUTTON_2;
7905 int dx = (left ? -1 : right ? 1 : 0);
7906 int dy = (up ? -1 : down ? 1 : 0);
7909 stored_player_action[player->index_nr] = 0;
7910 num_stored_actions++;
7914 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7917 if (!player->active || tape.pausing)
7921 printf("::: [%d %d %d %d] [%d %d]\n",
7922 left, right, up, down, button1, button2);
7928 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7933 if (player->MovPos == 0)
7934 CheckGravityMovement(player);
7937 snapped = SnapField(player, dx, dy);
7941 dropped = DropElement(player);
7943 moved = MovePlayer(player, dx, dy);
7946 if (tape.single_step && tape.recording && !tape.pausing)
7948 if (button1 || (dropped && !moved))
7950 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7951 SnapField(player, 0, 0); /* stop snapping */
7955 SetPlayerWaiting(player, FALSE);
7958 return player_action;
7960 stored_player_action[player->index_nr] = player_action;
7966 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7969 /* no actions for this player (no input at player's configured device) */
7971 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7972 SnapField(player, 0, 0);
7973 CheckGravityMovementWhenNotMoving(player);
7975 if (player->MovPos == 0)
7976 SetPlayerWaiting(player, TRUE);
7978 if (player->MovPos == 0) /* needed for tape.playing */
7979 player->is_moving = FALSE;
7981 player->is_dropping = FALSE;
7987 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7989 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7991 TapeRecordAction(stored_player_action);
7992 num_stored_actions = 0;
7999 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8001 static byte stored_player_action[MAX_PLAYERS];
8002 static int num_stored_actions = 0;
8003 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8004 int left = player_action & JOY_LEFT;
8005 int right = player_action & JOY_RIGHT;
8006 int up = player_action & JOY_UP;
8007 int down = player_action & JOY_DOWN;
8008 int button1 = player_action & JOY_BUTTON_1;
8009 int button2 = player_action & JOY_BUTTON_2;
8010 int dx = (left ? -1 : right ? 1 : 0);
8011 int dy = (up ? -1 : down ? 1 : 0);
8013 stored_player_action[player->index_nr] = 0;
8014 num_stored_actions++;
8016 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8018 if (!player->active || tape.pausing)
8023 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8026 snapped = SnapField(player, dx, dy);
8030 dropped = DropElement(player);
8032 moved = MovePlayer(player, dx, dy);
8035 if (tape.single_step && tape.recording && !tape.pausing)
8037 if (button1 || (dropped && !moved))
8039 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8040 SnapField(player, 0, 0); /* stop snapping */
8044 stored_player_action[player->index_nr] = player_action;
8048 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8050 /* no actions for this player (no input at player's configured device) */
8052 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8053 SnapField(player, 0, 0);
8054 CheckGravityMovementWhenNotMoving(player);
8056 if (player->MovPos == 0)
8057 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8059 if (player->MovPos == 0) /* needed for tape.playing */
8060 player->is_moving = FALSE;
8063 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8065 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8067 TapeRecordAction(stored_player_action);
8068 num_stored_actions = 0;
8075 static unsigned long action_delay = 0;
8076 unsigned long action_delay_value;
8077 int magic_wall_x = 0, magic_wall_y = 0;
8078 int i, x, y, element, graphic;
8079 byte *recorded_player_action;
8080 byte summarized_player_action = 0;
8082 byte tape_action[MAX_PLAYERS];
8085 if (game_status != GAME_MODE_PLAYING)
8088 action_delay_value =
8089 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8091 if (tape.playing && tape.warp_forward && !tape.pausing)
8092 action_delay_value = 0;
8094 /* ---------- main game synchronization point ---------- */
8096 WaitUntilDelayReached(&action_delay, action_delay_value);
8098 if (network_playing && !network_player_action_received)
8102 printf("DEBUG: try to get network player actions in time\n");
8106 #if defined(PLATFORM_UNIX)
8107 /* last chance to get network player actions without main loop delay */
8111 if (game_status != GAME_MODE_PLAYING)
8114 if (!network_player_action_received)
8118 printf("DEBUG: failed to get network player actions in time\n");
8129 printf("::: getting new tape action [%d]\n", FrameCounter);
8132 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8135 if (recorded_player_action == NULL && tape.pausing)
8140 printf("::: %d\n", stored_player[0].action);
8144 if (recorded_player_action != NULL)
8145 for (i = 0; i < MAX_PLAYERS; i++)
8146 stored_player[i].action = recorded_player_action[i];
8149 for (i = 0; i < MAX_PLAYERS; i++)
8151 summarized_player_action |= stored_player[i].action;
8153 if (!network_playing)
8154 stored_player[i].effective_action = stored_player[i].action;
8157 #if defined(PLATFORM_UNIX)
8158 if (network_playing)
8159 SendToServer_MovePlayer(summarized_player_action);
8162 if (!options.network && !setup.team_mode)
8163 local_player->effective_action = summarized_player_action;
8166 if (recorded_player_action != NULL)
8167 for (i = 0; i < MAX_PLAYERS; i++)
8168 stored_player[i].effective_action = recorded_player_action[i];
8172 for (i = 0; i < MAX_PLAYERS; i++)
8174 tape_action[i] = stored_player[i].effective_action;
8176 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8177 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8180 /* only save actions from input devices, but not programmed actions */
8182 TapeRecordAction(tape_action);
8185 for (i = 0; i < MAX_PLAYERS; i++)
8187 int actual_player_action = stored_player[i].effective_action;
8190 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8191 - rnd_equinox_tetrachloride 048
8192 - rnd_equinox_tetrachloride_ii 096
8193 - rnd_emanuel_schmieg 002
8194 - doctor_sloan_ww 001, 020
8196 if (stored_player[i].MovPos == 0)
8197 CheckGravityMovement(&stored_player[i]);
8201 /* overwrite programmed action with tape action */
8202 if (stored_player[i].programmed_action)
8203 actual_player_action = stored_player[i].programmed_action;
8207 if (stored_player[i].programmed_action)
8208 printf("::: %d\n", stored_player[i].programmed_action);
8211 if (recorded_player_action)
8214 if (stored_player[i].programmed_action &&
8215 stored_player[i].programmed_action != recorded_player_action[i])
8216 printf("::: %d: %d <-> %d\n", i,
8217 stored_player[i].programmed_action, recorded_player_action[i]);
8221 actual_player_action = recorded_player_action[i];
8226 /* overwrite tape action with programmed action */
8227 if (stored_player[i].programmed_action)
8228 actual_player_action = stored_player[i].programmed_action;
8233 printf("::: action: %d: %x [%d]\n",
8234 stored_player[i].MovPos, actual_player_action, FrameCounter);
8238 PlayerActions(&stored_player[i], actual_player_action);
8240 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8242 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8243 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8246 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8251 TapeRecordAction(tape_action);
8254 network_player_action_received = FALSE;
8256 ScrollScreen(NULL, SCROLL_GO_ON);
8262 for (i = 0; i < MAX_PLAYERS; i++)
8263 stored_player[i].Frame++;
8267 /* for downwards compatibility, the following code emulates a fixed bug that
8268 occured when pushing elements (causing elements that just made their last
8269 pushing step to already (if possible) make their first falling step in the
8270 same game frame, which is bad); this code is also needed to use the famous
8271 "spring push bug" which is used in older levels and might be wanted to be
8272 used also in newer levels, but in this case the buggy pushing code is only
8273 affecting the "spring" element and no other elements */
8276 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8278 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8281 for (i = 0; i < MAX_PLAYERS; i++)
8283 struct PlayerInfo *player = &stored_player[i];
8288 if (player->active && player->is_pushing && player->is_moving &&
8290 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8291 Feld[x][y] == EL_SPRING))
8293 if (player->active && player->is_pushing && player->is_moving &&
8297 ContinueMoving(x, y);
8299 /* continue moving after pushing (this is actually a bug) */
8300 if (!IS_MOVING(x, y))
8309 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8311 Changed[x][y] = CE_BITMASK_DEFAULT;
8312 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8315 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8317 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8318 printf("GameActions(): This should never happen!\n");
8320 ChangePage[x][y] = -1;
8325 if (WasJustMoving[x][y] > 0)
8326 WasJustMoving[x][y]--;
8327 if (WasJustFalling[x][y] > 0)
8328 WasJustFalling[x][y]--;
8329 if (CheckCollision[x][y] > 0)
8330 CheckCollision[x][y]--;
8335 /* reset finished pushing action (not done in ContinueMoving() to allow
8336 continous pushing animation for elements with zero push delay) */
8337 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8339 ResetGfxAnimation(x, y);
8340 DrawLevelField(x, y);
8345 if (IS_BLOCKED(x, y))
8349 Blocked2Moving(x, y, &oldx, &oldy);
8350 if (!IS_MOVING(oldx, oldy))
8352 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8353 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8354 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8355 printf("GameActions(): This should never happen!\n");
8361 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8363 element = Feld[x][y];
8365 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8367 graphic = el2img(element);
8373 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8375 element = graphic = 0;
8379 if (graphic_info[graphic].anim_global_sync)
8380 GfxFrame[x][y] = FrameCounter;
8382 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8383 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8384 ResetRandomAnimationValue(x, y);
8386 SetRandomAnimationValue(x, y);
8389 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8392 if (IS_INACTIVE(element))
8394 if (IS_ANIMATED(graphic))
8395 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8401 /* this may take place after moving, so 'element' may have changed */
8403 if (IS_CHANGING(x, y))
8405 if (IS_CHANGING(x, y) &&
8406 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8410 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8411 element_info[element].event_page_nr[CE_DELAY]);
8413 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8416 element = Feld[x][y];
8417 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8421 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8426 element = Feld[x][y];
8427 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8429 if (element == EL_MOLE)
8430 printf("::: %d, %d, %d [%d]\n",
8431 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8435 if (element == EL_YAMYAM)
8436 printf("::: %d, %d, %d\n",
8437 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8441 if (IS_ANIMATED(graphic) &&
8445 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8448 if (element == EL_BUG)
8449 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8453 if (element == EL_MOLE)
8454 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8458 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8459 EdelsteinFunkeln(x, y);
8461 else if ((element == EL_ACID ||
8462 element == EL_EXIT_OPEN ||
8463 element == EL_SP_EXIT_OPEN ||
8464 element == EL_SP_TERMINAL ||
8465 element == EL_SP_TERMINAL_ACTIVE ||
8466 element == EL_EXTRA_TIME ||
8467 element == EL_SHIELD_NORMAL ||
8468 element == EL_SHIELD_DEADLY) &&
8469 IS_ANIMATED(graphic))
8470 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8471 else if (IS_MOVING(x, y))
8472 ContinueMoving(x, y);
8473 else if (IS_ACTIVE_BOMB(element))
8474 CheckDynamite(x, y);
8476 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8477 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8479 else if (element == EL_AMOEBA_GROWING)
8480 AmoebeWaechst(x, y);
8481 else if (element == EL_AMOEBA_SHRINKING)
8482 AmoebaDisappearing(x, y);
8484 #if !USE_NEW_AMOEBA_CODE
8485 else if (IS_AMOEBALIVE(element))
8486 AmoebeAbleger(x, y);
8489 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8491 else if (element == EL_EXIT_CLOSED)
8493 else if (element == EL_SP_EXIT_CLOSED)
8495 else if (element == EL_EXPANDABLE_WALL_GROWING)
8497 else if (element == EL_EXPANDABLE_WALL ||
8498 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8499 element == EL_EXPANDABLE_WALL_VERTICAL ||
8500 element == EL_EXPANDABLE_WALL_ANY)
8502 else if (element == EL_FLAMES)
8503 CheckForDragon(x, y);
8505 else if (IS_AUTO_CHANGING(element))
8506 ChangeElement(x, y);
8508 else if (element == EL_EXPLOSION)
8509 ; /* drawing of correct explosion animation is handled separately */
8510 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8511 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8514 /* this may take place after moving, so 'element' may have changed */
8515 if (IS_AUTO_CHANGING(Feld[x][y]))
8516 ChangeElement(x, y);
8519 if (IS_BELT_ACTIVE(element))
8520 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8522 if (game.magic_wall_active)
8524 int jx = local_player->jx, jy = local_player->jy;
8526 /* play the element sound at the position nearest to the player */
8527 if ((element == EL_MAGIC_WALL_FULL ||
8528 element == EL_MAGIC_WALL_ACTIVE ||
8529 element == EL_MAGIC_WALL_EMPTYING ||
8530 element == EL_BD_MAGIC_WALL_FULL ||
8531 element == EL_BD_MAGIC_WALL_ACTIVE ||
8532 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8533 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8541 #if USE_NEW_AMOEBA_CODE
8542 /* new experimental amoeba growth stuff */
8544 if (!(FrameCounter % 8))
8547 static unsigned long random = 1684108901;
8549 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8552 x = (random >> 10) % lev_fieldx;
8553 y = (random >> 20) % lev_fieldy;
8555 x = RND(lev_fieldx);
8556 y = RND(lev_fieldy);
8558 element = Feld[x][y];
8561 if (!IS_PLAYER(x,y) &&
8562 (element == EL_EMPTY ||
8563 CAN_GROW_INTO(element) ||
8564 element == EL_QUICKSAND_EMPTY ||
8565 element == EL_ACID_SPLASH_LEFT ||
8566 element == EL_ACID_SPLASH_RIGHT))
8568 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8569 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8570 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8571 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8572 Feld[x][y] = EL_AMOEBA_DROP;
8575 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8576 if (!IS_PLAYER(x,y) &&
8577 (element == EL_EMPTY ||
8578 element == EL_SAND ||
8579 element == EL_QUICKSAND_EMPTY ||
8580 element == EL_ACID_SPLASH_LEFT ||
8581 element == EL_ACID_SPLASH_RIGHT))
8583 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8584 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8585 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8586 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8587 Feld[x][y] = EL_AMOEBA_DROP;
8591 random = random * 129 + 1;
8597 if (game.explosions_delayed)
8600 game.explosions_delayed = FALSE;
8602 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8604 element = Feld[x][y];
8606 if (ExplodeField[x][y])
8607 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8608 else if (element == EL_EXPLOSION)
8609 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8611 ExplodeField[x][y] = EX_TYPE_NONE;
8614 game.explosions_delayed = TRUE;
8617 if (game.magic_wall_active)
8619 if (!(game.magic_wall_time_left % 4))
8621 int element = Feld[magic_wall_x][magic_wall_y];
8623 if (element == EL_BD_MAGIC_WALL_FULL ||
8624 element == EL_BD_MAGIC_WALL_ACTIVE ||
8625 element == EL_BD_MAGIC_WALL_EMPTYING)
8626 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8628 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8631 if (game.magic_wall_time_left > 0)
8633 game.magic_wall_time_left--;
8634 if (!game.magic_wall_time_left)
8636 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8638 element = Feld[x][y];
8640 if (element == EL_MAGIC_WALL_ACTIVE ||
8641 element == EL_MAGIC_WALL_FULL)
8643 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8644 DrawLevelField(x, y);
8646 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8647 element == EL_BD_MAGIC_WALL_FULL)
8649 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8650 DrawLevelField(x, y);
8654 game.magic_wall_active = FALSE;
8659 if (game.light_time_left > 0)
8661 game.light_time_left--;
8663 if (game.light_time_left == 0)
8664 RedrawAllLightSwitchesAndInvisibleElements();
8667 if (game.timegate_time_left > 0)
8669 game.timegate_time_left--;
8671 if (game.timegate_time_left == 0)
8672 CloseAllOpenTimegates();
8675 for (i = 0; i < MAX_PLAYERS; i++)
8677 struct PlayerInfo *player = &stored_player[i];
8679 if (SHIELD_ON(player))
8681 if (player->shield_deadly_time_left)
8682 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8683 else if (player->shield_normal_time_left)
8684 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8688 if (TimeFrames >= FRAMES_PER_SECOND)
8693 if (!level.use_step_counter)
8697 for (i = 0; i < MAX_PLAYERS; i++)
8699 struct PlayerInfo *player = &stored_player[i];
8701 if (SHIELD_ON(player))
8703 player->shield_normal_time_left--;
8705 if (player->shield_deadly_time_left > 0)
8706 player->shield_deadly_time_left--;
8714 if (TimeLeft <= 10 && setup.time_limit)
8715 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8717 DrawGameValue_Time(TimeLeft);
8719 if (!TimeLeft && setup.time_limit)
8720 for (i = 0; i < MAX_PLAYERS; i++)
8721 KillHero(&stored_player[i]);
8723 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8724 DrawGameValue_Time(TimePlayed);
8727 if (tape.recording || tape.playing)
8728 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8732 PlayAllPlayersSound();
8734 if (options.debug) /* calculate frames per second */
8736 static unsigned long fps_counter = 0;
8737 static int fps_frames = 0;
8738 unsigned long fps_delay_ms = Counter() - fps_counter;
8742 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8744 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8747 fps_counter = Counter();
8750 redraw_mask |= REDRAW_FPS;
8754 if (stored_player[0].jx != stored_player[0].last_jx ||
8755 stored_player[0].jy != stored_player[0].last_jy)
8756 printf("::: %d, %d, %d, %d, %d\n",
8757 stored_player[0].MovDir,
8758 stored_player[0].MovPos,
8759 stored_player[0].GfxPos,
8760 stored_player[0].Frame,
8761 stored_player[0].StepFrame);
8768 for (i = 0; i < MAX_PLAYERS; i++)
8771 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8773 stored_player[i].Frame += move_frames;
8775 if (stored_player[i].MovPos != 0)
8776 stored_player[i].StepFrame += move_frames;
8778 if (stored_player[i].drop_delay > 0)
8779 stored_player[i].drop_delay--;
8784 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8786 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8788 local_player->show_envelope = 0;
8793 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8795 int min_x = x, min_y = y, max_x = x, max_y = y;
8798 for (i = 0; i < MAX_PLAYERS; i++)
8800 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8802 if (!stored_player[i].active || &stored_player[i] == player)
8805 min_x = MIN(min_x, jx);
8806 min_y = MIN(min_y, jy);
8807 max_x = MAX(max_x, jx);
8808 max_y = MAX(max_y, jy);
8811 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8814 static boolean AllPlayersInVisibleScreen()
8818 for (i = 0; i < MAX_PLAYERS; i++)
8820 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8822 if (!stored_player[i].active)
8825 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8832 void ScrollLevel(int dx, int dy)
8834 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8837 BlitBitmap(drawto_field, drawto_field,
8838 FX + TILEX * (dx == -1) - softscroll_offset,
8839 FY + TILEY * (dy == -1) - softscroll_offset,
8840 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8841 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8842 FX + TILEX * (dx == 1) - softscroll_offset,
8843 FY + TILEY * (dy == 1) - softscroll_offset);
8847 x = (dx == 1 ? BX1 : BX2);
8848 for (y = BY1; y <= BY2; y++)
8849 DrawScreenField(x, y);
8854 y = (dy == 1 ? BY1 : BY2);
8855 for (x = BX1; x <= BX2; x++)
8856 DrawScreenField(x, y);
8859 redraw_mask |= REDRAW_FIELD;
8863 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8865 int nextx = x + dx, nexty = y + dy;
8866 int element = Feld[x][y];
8869 element != EL_SP_PORT_LEFT &&
8870 element != EL_SP_GRAVITY_PORT_LEFT &&
8871 element != EL_SP_PORT_HORIZONTAL &&
8872 element != EL_SP_PORT_ANY) ||
8874 element != EL_SP_PORT_RIGHT &&
8875 element != EL_SP_GRAVITY_PORT_RIGHT &&
8876 element != EL_SP_PORT_HORIZONTAL &&
8877 element != EL_SP_PORT_ANY) ||
8879 element != EL_SP_PORT_UP &&
8880 element != EL_SP_GRAVITY_PORT_UP &&
8881 element != EL_SP_PORT_VERTICAL &&
8882 element != EL_SP_PORT_ANY) ||
8884 element != EL_SP_PORT_DOWN &&
8885 element != EL_SP_GRAVITY_PORT_DOWN &&
8886 element != EL_SP_PORT_VERTICAL &&
8887 element != EL_SP_PORT_ANY) ||
8888 !IN_LEV_FIELD(nextx, nexty) ||
8889 !IS_FREE(nextx, nexty))
8896 static boolean canFallDown(struct PlayerInfo *player)
8898 int jx = player->jx, jy = player->jy;
8900 return (IN_LEV_FIELD(jx, jy + 1) &&
8901 (IS_FREE(jx, jy + 1) ||
8902 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8903 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8904 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8907 static boolean canPassField(int x, int y, int move_dir)
8909 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8910 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8911 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8914 int element = Feld[x][y];
8916 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8917 !CAN_MOVE(element) &&
8918 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8919 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8920 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8923 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8925 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8926 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8927 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8931 int nextx = newx + dx;
8932 int nexty = newy + dy;
8936 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8937 (IS_DIGGABLE(Feld[newx][newy]) ||
8938 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8939 canPassField(newx, newy, move_dir)));
8941 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8942 (IS_DIGGABLE(Feld[newx][newy]) ||
8943 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8944 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
8945 !CAN_MOVE(Feld[newx][newy]) &&
8946 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8947 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8948 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
8952 static void CheckGravityMovement(struct PlayerInfo *player)
8954 if (game.gravity && !player->programmed_action)
8957 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8958 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8960 int move_dir_horizontal = player->action & MV_HORIZONTAL;
8961 int move_dir_vertical = player->action & MV_VERTICAL;
8965 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8967 boolean player_is_snapping = player->action & JOY_BUTTON_1;
8970 int jx = player->jx, jy = player->jy;
8972 boolean player_is_moving_to_valid_field =
8973 (!player_is_snapping &&
8974 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8975 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8979 (player->last_move_dir & MV_HORIZONTAL ?
8980 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8981 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8985 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8986 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8987 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8988 int new_jx = jx + dx, new_jy = jy + dy;
8989 int nextx = new_jx + dx, nexty = new_jy + dy;
8995 boolean player_can_fall_down = canFallDown(player);
8997 boolean player_can_fall_down =
8998 (IN_LEV_FIELD(jx, jy + 1) &&
8999 (IS_FREE(jx, jy + 1) ||
9000 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9004 boolean player_can_fall_down =
9005 (IN_LEV_FIELD(jx, jy + 1) &&
9006 (IS_FREE(jx, jy + 1)));
9010 boolean player_is_moving_to_valid_field =
9013 !player_is_snapping &&
9017 IN_LEV_FIELD(new_jx, new_jy) &&
9018 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9019 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9020 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9021 IN_LEV_FIELD(nextx, nexty) &&
9022 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9024 IN_LEV_FIELD(new_jx, new_jy) &&
9025 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9026 Feld[new_jx][new_jy] == EL_SAND ||
9027 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9028 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9029 /* !!! extend EL_SAND to anything diggable !!! */
9035 boolean player_is_standing_on_valid_field =
9036 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9037 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9041 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9042 player_can_fall_down,
9043 player_is_standing_on_valid_field,
9044 player_is_moving_to_valid_field,
9045 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9046 player->effective_action,
9047 player->can_fall_into_acid);
9050 if (player_can_fall_down &&
9052 !player_is_standing_on_valid_field &&
9054 !player_is_moving_to_valid_field)
9057 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9058 jx, jy, FrameCounter);
9061 player->programmed_action = MV_DOWN;
9066 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9069 return CheckGravityMovement(player);
9072 if (game.gravity && !player->programmed_action)
9074 int jx = player->jx, jy = player->jy;
9075 boolean field_under_player_is_free =
9076 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9077 boolean player_is_standing_on_valid_field =
9078 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9079 (IS_WALKABLE(Feld[jx][jy]) &&
9080 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9082 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9083 player->programmed_action = MV_DOWN;
9089 -----------------------------------------------------------------------------
9090 dx, dy: direction (non-diagonal) to try to move the player to
9091 real_dx, real_dy: direction as read from input device (can be diagonal)
9094 boolean MovePlayerOneStep(struct PlayerInfo *player,
9095 int dx, int dy, int real_dx, int real_dy)
9098 static int trigger_sides[4][2] =
9100 /* enter side leave side */
9101 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9102 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9103 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9104 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9106 int move_direction = (dx == -1 ? MV_LEFT :
9107 dx == +1 ? MV_RIGHT :
9109 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9110 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9111 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9113 int jx = player->jx, jy = player->jy;
9114 int new_jx = jx + dx, new_jy = jy + dy;
9118 if (!player->active || (!dx && !dy))
9119 return MF_NO_ACTION;
9121 player->MovDir = (dx < 0 ? MV_LEFT :
9124 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9126 if (!IN_LEV_FIELD(new_jx, new_jy))
9127 return MF_NO_ACTION;
9129 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9130 return MF_NO_ACTION;
9133 element = MovingOrBlocked2Element(new_jx, new_jy);
9135 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9138 if (DONT_RUN_INTO(element))
9140 if (element == EL_ACID && dx == 0 && dy == 1)
9142 SplashAcid(new_jx, new_jy);
9143 Feld[jx][jy] = EL_PLAYER_1;
9144 InitMovingField(jx, jy, MV_DOWN);
9145 Store[jx][jy] = EL_ACID;
9146 ContinueMoving(jx, jy);
9150 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9155 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9156 if (can_move != MF_MOVING)
9159 /* check if DigField() has caused relocation of the player */
9160 if (player->jx != jx || player->jy != jy)
9161 return MF_NO_ACTION;
9163 StorePlayer[jx][jy] = 0;
9164 player->last_jx = jx;
9165 player->last_jy = jy;
9166 player->jx = new_jx;
9167 player->jy = new_jy;
9168 StorePlayer[new_jx][new_jy] = player->element_nr;
9171 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9173 player->step_counter++;
9176 player->drop_delay = 0;
9179 PlayerVisit[jx][jy] = FrameCounter;
9181 ScrollPlayer(player, SCROLL_INIT);
9184 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9186 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9188 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9191 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9193 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9194 CE_OTHER_GETS_ENTERED, enter_side);
9195 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9196 CE_ENTERED_BY_PLAYER, enter_side);
9203 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9205 int jx = player->jx, jy = player->jy;
9206 int old_jx = jx, old_jy = jy;
9207 int moved = MF_NO_ACTION;
9210 if (!player->active)
9215 if (player->MovPos == 0)
9217 player->is_moving = FALSE;
9218 player->is_digging = FALSE;
9219 player->is_collecting = FALSE;
9220 player->is_snapping = FALSE;
9221 player->is_pushing = FALSE;
9227 if (!player->active || (!dx && !dy))
9232 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9238 if (!FrameReached(&player->move_delay, player->move_delay_value))
9241 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9242 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9248 /* store if player is automatically moved to next field */
9249 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9251 /* remove the last programmed player action */
9252 player->programmed_action = 0;
9256 /* should only happen if pre-1.2 tape recordings are played */
9257 /* this is only for backward compatibility */
9259 int original_move_delay_value = player->move_delay_value;
9262 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9266 /* scroll remaining steps with finest movement resolution */
9267 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9269 while (player->MovPos)
9271 ScrollPlayer(player, SCROLL_GO_ON);
9272 ScrollScreen(NULL, SCROLL_GO_ON);
9278 player->move_delay_value = original_move_delay_value;
9281 if (player->last_move_dir & MV_HORIZONTAL)
9283 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9284 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9288 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9289 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9295 if (moved & MF_MOVING && !ScreenMovPos &&
9296 (player == local_player || !options.network))
9298 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9299 int offset = (setup.scroll_delay ? 3 : 0);
9301 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9303 /* actual player has left the screen -- scroll in that direction */
9304 if (jx != old_jx) /* player has moved horizontally */
9305 scroll_x += (jx - old_jx);
9306 else /* player has moved vertically */
9307 scroll_y += (jy - old_jy);
9311 if (jx != old_jx) /* player has moved horizontally */
9313 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9314 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9315 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9317 /* don't scroll over playfield boundaries */
9318 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9319 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9321 /* don't scroll more than one field at a time */
9322 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9324 /* don't scroll against the player's moving direction */
9325 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9326 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9327 scroll_x = old_scroll_x;
9329 else /* player has moved vertically */
9331 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9332 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9333 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9335 /* don't scroll over playfield boundaries */
9336 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9337 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9339 /* don't scroll more than one field at a time */
9340 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9342 /* don't scroll against the player's moving direction */
9343 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9344 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9345 scroll_y = old_scroll_y;
9349 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9351 if (!options.network && !AllPlayersInVisibleScreen())
9353 scroll_x = old_scroll_x;
9354 scroll_y = old_scroll_y;
9358 ScrollScreen(player, SCROLL_INIT);
9359 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9366 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9368 if (!(moved & MF_MOVING) && !player->is_pushing)
9373 player->StepFrame = 0;
9375 if (moved & MF_MOVING)
9377 if (old_jx != jx && old_jy == jy)
9378 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9379 else if (old_jx == jx && old_jy != jy)
9380 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9382 DrawLevelField(jx, jy); /* for "crumbled sand" */
9384 player->last_move_dir = player->MovDir;
9385 player->is_moving = TRUE;
9387 player->is_snapping = FALSE;
9391 player->is_switching = FALSE;
9394 player->is_dropping = FALSE;
9398 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9401 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9404 static int trigger_sides[4][2] =
9406 /* enter side leave side */
9407 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9408 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9409 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9410 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9412 int move_direction = player->MovDir;
9413 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9414 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9417 /* !!! TEST ONLY !!! */
9418 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9419 CheckElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9421 player->index_bit, leave_side);
9423 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9425 player->index_bit, leave_side);
9427 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9428 CheckElementChangeByPlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9429 player->index_bit, enter_side);
9431 CheckTriggeredElementChangeByPlayer(jx, jy, Feld[jx][jy],
9432 CE_OTHER_GETS_ENTERED,
9433 player->index_bit, enter_side);
9443 CheckGravityMovementWhenNotMoving(player);
9446 player->last_move_dir = MV_NO_MOVING;
9448 player->is_moving = FALSE;
9451 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9453 TestIfHeroTouchesBadThing(jx, jy);
9454 TestIfPlayerTouchesCustomElement(jx, jy);
9457 if (!player->active)
9463 void ScrollPlayer(struct PlayerInfo *player, int mode)
9465 int jx = player->jx, jy = player->jy;
9466 int last_jx = player->last_jx, last_jy = player->last_jy;
9467 int move_stepsize = TILEX / player->move_delay_value;
9469 if (!player->active || !player->MovPos)
9472 if (mode == SCROLL_INIT)
9474 player->actual_frame_counter = FrameCounter;
9475 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9477 if (Feld[last_jx][last_jy] == EL_EMPTY)
9478 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9486 else if (!FrameReached(&player->actual_frame_counter, 1))
9489 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9490 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9492 if (!player->block_last_field &&
9493 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9494 Feld[last_jx][last_jy] = EL_EMPTY;
9496 /* before DrawPlayer() to draw correct player graphic for this case */
9497 if (player->MovPos == 0)
9498 CheckGravityMovement(player);
9501 DrawPlayer(player); /* needed here only to cleanup last field */
9504 if (player->MovPos == 0) /* player reached destination field */
9507 if (player->move_delay_reset_counter > 0)
9509 player->move_delay_reset_counter--;
9511 if (player->move_delay_reset_counter == 0)
9513 /* continue with normal speed after quickly moving through gate */
9514 HALVE_PLAYER_SPEED(player);
9516 /* be able to make the next move without delay */
9517 player->move_delay = 0;
9521 if (IS_PASSABLE(Feld[last_jx][last_jy]))
9523 /* continue with normal speed after quickly moving through gate */
9524 HALVE_PLAYER_SPEED(player);
9526 /* be able to make the next move without delay */
9527 player->move_delay = 0;
9531 if (player->block_last_field &&
9532 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9533 Feld[last_jx][last_jy] = EL_EMPTY;
9535 player->last_jx = jx;
9536 player->last_jy = jy;
9538 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9539 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9540 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9542 DrawPlayer(player); /* needed here only to cleanup last field */
9545 if (local_player->friends_still_needed == 0 ||
9546 IS_SP_ELEMENT(Feld[jx][jy]))
9547 player->LevelSolved = player->GameOver = TRUE;
9551 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9552 /* this breaks one level: "machine", level 000 */
9554 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9557 static int trigger_sides[4][2] =
9559 /* enter side leave side */
9560 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9561 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9562 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9563 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9565 int move_direction = player->MovDir;
9566 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9567 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9568 int old_jx = last_jx;
9569 int old_jy = last_jy;
9572 /* !!! TEST ONLY !!! */
9573 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9574 CheckElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9576 player->index_bit, leave_side);
9578 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9580 player->index_bit, leave_side);
9582 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9583 CheckElementChangeByPlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9584 player->index_bit, enter_side);
9586 CheckTriggeredElementChangeByPlayer(jx, jy, Feld[jx][jy],
9587 CE_OTHER_GETS_ENTERED,
9588 player->index_bit, enter_side);
9594 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9596 TestIfHeroTouchesBadThing(jx, jy);
9597 TestIfPlayerTouchesCustomElement(jx, jy);
9599 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9602 if (!player->active)
9606 if (level.use_step_counter)
9612 for (i = 0; i < MAX_PLAYERS; i++)
9614 struct PlayerInfo *player = &stored_player[i];
9616 if (SHIELD_ON(player))
9618 player->shield_normal_time_left--;
9620 if (player->shield_deadly_time_left > 0)
9621 player->shield_deadly_time_left--;
9629 if (TimeLeft <= 10 && setup.time_limit)
9630 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9632 DrawGameValue_Time(TimeLeft);
9634 if (!TimeLeft && setup.time_limit)
9635 for (i = 0; i < MAX_PLAYERS; i++)
9636 KillHero(&stored_player[i]);
9638 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9639 DrawGameValue_Time(TimePlayed);
9642 if (tape.single_step && tape.recording && !tape.pausing &&
9643 !player->programmed_action)
9644 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9648 void ScrollScreen(struct PlayerInfo *player, int mode)
9650 static unsigned long screen_frame_counter = 0;
9652 if (mode == SCROLL_INIT)
9654 /* set scrolling step size according to actual player's moving speed */
9655 ScrollStepSize = TILEX / player->move_delay_value;
9657 screen_frame_counter = FrameCounter;
9658 ScreenMovDir = player->MovDir;
9659 ScreenMovPos = player->MovPos;
9660 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9663 else if (!FrameReached(&screen_frame_counter, 1))
9668 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9669 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9670 redraw_mask |= REDRAW_FIELD;
9673 ScreenMovDir = MV_NO_MOVING;
9676 void TestIfPlayerTouchesCustomElement(int x, int y)
9678 static int xy[4][2] =
9685 static int trigger_sides[4][2] =
9687 /* center side border side */
9688 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9689 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9690 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9691 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9693 static int touch_dir[4] =
9700 int center_element = Feld[x][y]; /* should always be non-moving! */
9703 for (i = 0; i < NUM_DIRECTIONS; i++)
9705 int xx = x + xy[i][0];
9706 int yy = y + xy[i][1];
9707 int center_side = trigger_sides[i][0];
9708 int border_side = trigger_sides[i][1];
9711 if (!IN_LEV_FIELD(xx, yy))
9714 if (IS_PLAYER(x, y))
9716 struct PlayerInfo *player = PLAYERINFO(x, y);
9718 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9719 border_element = Feld[xx][yy]; /* may be moving! */
9720 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9721 border_element = Feld[xx][yy];
9722 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9723 border_element = MovingOrBlocked2Element(xx, yy);
9725 continue; /* center and border element do not touch */
9728 /* !!! TEST ONLY !!! */
9729 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9730 player->index_bit, border_side);
9731 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9732 CE_OTHER_GETS_TOUCHED,
9733 player->index_bit, border_side);
9735 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9736 CE_OTHER_GETS_TOUCHED,
9737 player->index_bit, border_side);
9738 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9739 player->index_bit, border_side);
9742 else if (IS_PLAYER(xx, yy))
9744 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9746 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9748 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9749 continue; /* center and border element do not touch */
9753 /* !!! TEST ONLY !!! */
9754 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9755 player->index_bit, center_side);
9756 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9757 CE_OTHER_GETS_TOUCHED,
9758 player->index_bit, center_side);
9760 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9761 CE_OTHER_GETS_TOUCHED,
9762 player->index_bit, center_side);
9763 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9764 player->index_bit, center_side);
9772 void TestIfElementTouchesCustomElement(int x, int y)
9774 static int xy[4][2] =
9781 static int trigger_sides[4][2] =
9783 /* center side border side */
9784 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9785 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9786 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9787 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9789 static int touch_dir[4] =
9796 boolean change_center_element = FALSE;
9797 int center_element_change_page = 0;
9798 int center_element = Feld[x][y]; /* should always be non-moving! */
9799 int border_trigger_element;
9802 for (i = 0; i < NUM_DIRECTIONS; i++)
9804 int xx = x + xy[i][0];
9805 int yy = y + xy[i][1];
9806 int center_side = trigger_sides[i][0];
9807 int border_side = trigger_sides[i][1];
9810 if (!IN_LEV_FIELD(xx, yy))
9813 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9814 border_element = Feld[xx][yy]; /* may be moving! */
9815 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9816 border_element = Feld[xx][yy];
9817 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9818 border_element = MovingOrBlocked2Element(xx, yy);
9820 continue; /* center and border element do not touch */
9822 /* check for change of center element (but change it only once) */
9823 if (IS_CUSTOM_ELEMENT(center_element) &&
9824 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9825 !change_center_element)
9827 for (j = 0; j < element_info[center_element].num_change_pages; j++)
9829 struct ElementChangeInfo *change =
9830 &element_info[center_element].change_page[j];
9832 if (change->can_change &&
9833 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9834 change->trigger_side & border_side &&
9836 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9838 change->trigger_element == border_element
9842 change_center_element = TRUE;
9843 center_element_change_page = j;
9844 border_trigger_element = border_element;
9851 /* check for change of border element */
9852 if (IS_CUSTOM_ELEMENT(border_element) &&
9853 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9855 for (j = 0; j < element_info[border_element].num_change_pages; j++)
9857 struct ElementChangeInfo *change =
9858 &element_info[border_element].change_page[j];
9860 if (change->can_change &&
9861 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9862 change->trigger_side & center_side &&
9864 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9866 change->trigger_element == center_element
9871 printf("::: border_element %d, %d\n", x, y);
9874 CheckElementChangeByPage(xx, yy, border_element, center_element,
9875 CE_OTHER_IS_TOUCHING, j);
9882 if (change_center_element)
9885 printf("::: center_element %d, %d\n", x, y);
9888 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
9889 CE_OTHER_IS_TOUCHING, center_element_change_page);
9893 void TestIfElementHitsCustomElement(int x, int y, int direction)
9895 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9896 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9897 int hitx = x + dx, hity = y + dy;
9898 int hitting_element = Feld[x][y];
9899 int touched_element;
9901 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9902 !IS_FREE(hitx, hity) &&
9903 (!IS_MOVING(hitx, hity) ||
9904 MovDir[hitx][hity] != direction ||
9905 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9908 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9912 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9916 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9917 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9919 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9920 CE_HITTING_SOMETHING, direction);
9922 if (IN_LEV_FIELD(hitx, hity))
9924 int opposite_direction = MV_DIR_OPPOSITE(direction);
9925 int hitting_side = direction;
9926 int touched_side = opposite_direction;
9928 int touched_element = MovingOrBlocked2Element(hitx, hity);
9931 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9932 MovDir[hitx][hity] != direction ||
9933 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9942 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9943 CE_HIT_BY_SOMETHING, opposite_direction);
9945 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9946 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
9948 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9950 struct ElementChangeInfo *change =
9951 &element_info[hitting_element].change_page[i];
9953 if (change->can_change &&
9954 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
9955 change->trigger_side & touched_side &&
9958 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9960 change->trigger_element == touched_element
9964 CheckElementChangeByPage(x, y, hitting_element, touched_element,
9965 CE_OTHER_IS_HITTING, i);
9971 if (IS_CUSTOM_ELEMENT(touched_element) &&
9972 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
9974 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9976 struct ElementChangeInfo *change =
9977 &element_info[touched_element].change_page[i];
9979 if (change->can_change &&
9980 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
9981 change->trigger_side & hitting_side &&
9983 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9985 change->trigger_element == hitting_element
9989 CheckElementChangeByPage(hitx, hity, touched_element,
9990 hitting_element, CE_OTHER_GETS_HIT, i);
10000 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10002 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10003 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10004 int hitx = x + dx, hity = y + dy;
10005 int hitting_element = Feld[x][y];
10006 int touched_element;
10008 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10009 !IS_FREE(hitx, hity) &&
10010 (!IS_MOVING(hitx, hity) ||
10011 MovDir[hitx][hity] != direction ||
10012 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10015 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10019 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10023 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10024 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10026 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10027 EP_CAN_SMASH_EVERYTHING, direction);
10029 if (IN_LEV_FIELD(hitx, hity))
10031 int opposite_direction = MV_DIR_OPPOSITE(direction);
10032 int hitting_side = direction;
10033 int touched_side = opposite_direction;
10035 int touched_element = MovingOrBlocked2Element(hitx, hity);
10038 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10039 MovDir[hitx][hity] != direction ||
10040 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10049 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10050 CE_SMASHED_BY_SOMETHING, opposite_direction);
10052 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10053 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10055 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10057 struct ElementChangeInfo *change =
10058 &element_info[hitting_element].change_page[i];
10060 if (change->can_change &&
10061 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10062 change->trigger_side & touched_side &&
10065 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10067 change->trigger_element == touched_element
10071 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10072 CE_OTHER_IS_SMASHING, i);
10078 if (IS_CUSTOM_ELEMENT(touched_element) &&
10079 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10081 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10083 struct ElementChangeInfo *change =
10084 &element_info[touched_element].change_page[i];
10086 if (change->can_change &&
10087 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10088 change->trigger_side & hitting_side &&
10090 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10092 change->trigger_element == hitting_element
10096 CheckElementChangeByPage(hitx, hity, touched_element,
10097 hitting_element, CE_OTHER_GETS_SMASHED,i);
10107 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10109 int i, kill_x = -1, kill_y = -1;
10110 int bad_element = -1;
10111 static int test_xy[4][2] =
10118 static int test_dir[4] =
10126 for (i = 0; i < NUM_DIRECTIONS; i++)
10128 int test_x, test_y, test_move_dir, test_element;
10130 test_x = good_x + test_xy[i][0];
10131 test_y = good_y + test_xy[i][1];
10133 if (!IN_LEV_FIELD(test_x, test_y))
10137 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10140 test_element = Feld[test_x][test_y];
10142 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10145 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10146 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10148 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10149 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10153 bad_element = test_element;
10159 if (kill_x != -1 || kill_y != -1)
10161 if (IS_PLAYER(good_x, good_y))
10163 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10166 if (player->shield_deadly_time_left > 0 &&
10167 !IS_INDESTRUCTIBLE(bad_element))
10168 Bang(kill_x, kill_y);
10169 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10172 if (player->shield_deadly_time_left > 0)
10173 Bang(kill_x, kill_y);
10174 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10179 Bang(good_x, good_y);
10183 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10185 int i, kill_x = -1, kill_y = -1;
10186 int bad_element = Feld[bad_x][bad_y];
10187 static int test_xy[4][2] =
10194 static int touch_dir[4] =
10196 MV_LEFT | MV_RIGHT,
10201 static int test_dir[4] =
10209 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10212 for (i = 0; i < NUM_DIRECTIONS; i++)
10214 int test_x, test_y, test_move_dir, test_element;
10216 test_x = bad_x + test_xy[i][0];
10217 test_y = bad_y + test_xy[i][1];
10218 if (!IN_LEV_FIELD(test_x, test_y))
10222 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10224 test_element = Feld[test_x][test_y];
10226 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10227 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10229 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10230 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10232 /* good thing is player or penguin that does not move away */
10233 if (IS_PLAYER(test_x, test_y))
10235 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10237 if (bad_element == EL_ROBOT && player->is_moving)
10238 continue; /* robot does not kill player if he is moving */
10240 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10242 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10243 continue; /* center and border element do not touch */
10250 else if (test_element == EL_PENGUIN)
10259 if (kill_x != -1 || kill_y != -1)
10261 if (IS_PLAYER(kill_x, kill_y))
10263 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10266 if (player->shield_deadly_time_left > 0 &&
10267 !IS_INDESTRUCTIBLE(bad_element))
10268 Bang(bad_x, bad_y);
10269 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10272 if (player->shield_deadly_time_left > 0)
10273 Bang(bad_x, bad_y);
10274 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10279 Bang(kill_x, kill_y);
10283 void TestIfHeroTouchesBadThing(int x, int y)
10285 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10288 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10290 TestIfGoodThingHitsBadThing(x, y, move_dir);
10293 void TestIfBadThingTouchesHero(int x, int y)
10295 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10298 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10300 TestIfBadThingHitsGoodThing(x, y, move_dir);
10303 void TestIfFriendTouchesBadThing(int x, int y)
10305 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10308 void TestIfBadThingTouchesFriend(int x, int y)
10310 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10313 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10315 int i, kill_x = bad_x, kill_y = bad_y;
10316 static int xy[4][2] =
10324 for (i = 0; i < NUM_DIRECTIONS; i++)
10328 x = bad_x + xy[i][0];
10329 y = bad_y + xy[i][1];
10330 if (!IN_LEV_FIELD(x, y))
10333 element = Feld[x][y];
10334 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10335 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10343 if (kill_x != bad_x || kill_y != bad_y)
10344 Bang(bad_x, bad_y);
10347 void KillHero(struct PlayerInfo *player)
10349 int jx = player->jx, jy = player->jy;
10351 if (!player->active)
10354 /* remove accessible field at the player's position */
10355 Feld[jx][jy] = EL_EMPTY;
10357 /* deactivate shield (else Bang()/Explode() would not work right) */
10358 player->shield_normal_time_left = 0;
10359 player->shield_deadly_time_left = 0;
10365 static void KillHeroUnlessEnemyProtected(int x, int y)
10367 if (!PLAYER_ENEMY_PROTECTED(x, y))
10368 KillHero(PLAYERINFO(x, y));
10371 static void KillHeroUnlessExplosionProtected(int x, int y)
10373 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10374 KillHero(PLAYERINFO(x, y));
10377 void BuryHero(struct PlayerInfo *player)
10379 int jx = player->jx, jy = player->jy;
10381 if (!player->active)
10385 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10387 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10389 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10391 player->GameOver = TRUE;
10392 RemoveHero(player);
10395 void RemoveHero(struct PlayerInfo *player)
10397 int jx = player->jx, jy = player->jy;
10398 int i, found = FALSE;
10400 player->present = FALSE;
10401 player->active = FALSE;
10403 if (!ExplodeField[jx][jy])
10404 StorePlayer[jx][jy] = 0;
10406 for (i = 0; i < MAX_PLAYERS; i++)
10407 if (stored_player[i].active)
10411 AllPlayersGone = TRUE;
10418 =============================================================================
10419 checkDiagonalPushing()
10420 -----------------------------------------------------------------------------
10421 check if diagonal input device direction results in pushing of object
10422 (by checking if the alternative direction is walkable, diggable, ...)
10423 =============================================================================
10426 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10427 int x, int y, int real_dx, int real_dy)
10429 int jx, jy, dx, dy, xx, yy;
10431 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10434 /* diagonal direction: check alternative direction */
10439 xx = jx + (dx == 0 ? real_dx : 0);
10440 yy = jy + (dy == 0 ? real_dy : 0);
10442 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10446 =============================================================================
10448 -----------------------------------------------------------------------------
10449 x, y: field next to player (non-diagonal) to try to dig to
10450 real_dx, real_dy: direction as read from input device (can be diagonal)
10451 =============================================================================
10454 int DigField(struct PlayerInfo *player,
10455 int oldx, int oldy, int x, int y,
10456 int real_dx, int real_dy, int mode)
10458 static int trigger_sides[4] =
10460 CH_SIDE_RIGHT, /* moving left */
10461 CH_SIDE_LEFT, /* moving right */
10462 CH_SIDE_BOTTOM, /* moving up */
10463 CH_SIDE_TOP, /* moving down */
10466 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10468 int jx = oldx, jy = oldy;
10469 int dx = x - jx, dy = y - jy;
10470 int nextx = x + dx, nexty = y + dy;
10471 int move_direction = (dx == -1 ? MV_LEFT :
10472 dx == +1 ? MV_RIGHT :
10474 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10475 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10476 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10477 int old_element = Feld[jx][jy];
10480 if (player->MovPos == 0)
10482 player->is_digging = FALSE;
10483 player->is_collecting = FALSE;
10486 if (player->MovPos == 0) /* last pushing move finished */
10487 player->is_pushing = FALSE;
10489 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10491 player->is_switching = FALSE;
10492 player->push_delay = 0;
10494 return MF_NO_ACTION;
10497 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10498 return MF_NO_ACTION;
10503 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10505 if (IS_TUBE(Feld[jx][jy]) ||
10506 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10510 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10511 int tube_leave_directions[][2] =
10513 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10514 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10515 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10516 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
10517 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
10518 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
10519 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
10520 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
10521 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
10522 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
10523 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
10524 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10527 while (tube_leave_directions[i][0] != tube_element)
10530 if (tube_leave_directions[i][0] == -1) /* should not happen */
10534 if (!(tube_leave_directions[i][1] & move_direction))
10535 return MF_NO_ACTION; /* tube has no opening in this direction */
10540 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10541 old_element = Back[jx][jy];
10545 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10546 return MF_NO_ACTION; /* field has no opening in this direction */
10548 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10549 return MF_NO_ACTION; /* field has no opening in this direction */
10551 element = Feld[x][y];
10553 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10554 game.engine_version >= VERSION_IDENT(2,2,0,0))
10555 return MF_NO_ACTION;
10558 if (game.gravity && !player->is_auto_moving &&
10559 canFallDown(player) && move_direction != MV_DOWN &&
10560 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10561 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10565 if (element == EL_EMPTY_SPACE &&
10566 game.gravity && !player->is_auto_moving &&
10567 canFallDown(player) && move_direction != MV_DOWN)
10568 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10574 case EL_SP_PORT_LEFT:
10575 case EL_SP_PORT_RIGHT:
10576 case EL_SP_PORT_UP:
10577 case EL_SP_PORT_DOWN:
10578 case EL_SP_PORT_HORIZONTAL:
10579 case EL_SP_PORT_VERTICAL:
10580 case EL_SP_PORT_ANY:
10581 case EL_SP_GRAVITY_PORT_LEFT:
10582 case EL_SP_GRAVITY_PORT_RIGHT:
10583 case EL_SP_GRAVITY_PORT_UP:
10584 case EL_SP_GRAVITY_PORT_DOWN:
10586 if (!canEnterSupaplexPort(x, y, dx, dy))
10587 return MF_NO_ACTION;
10590 element != EL_SP_PORT_LEFT &&
10591 element != EL_SP_GRAVITY_PORT_LEFT &&
10592 element != EL_SP_PORT_HORIZONTAL &&
10593 element != EL_SP_PORT_ANY) ||
10595 element != EL_SP_PORT_RIGHT &&
10596 element != EL_SP_GRAVITY_PORT_RIGHT &&
10597 element != EL_SP_PORT_HORIZONTAL &&
10598 element != EL_SP_PORT_ANY) ||
10600 element != EL_SP_PORT_UP &&
10601 element != EL_SP_GRAVITY_PORT_UP &&
10602 element != EL_SP_PORT_VERTICAL &&
10603 element != EL_SP_PORT_ANY) ||
10605 element != EL_SP_PORT_DOWN &&
10606 element != EL_SP_GRAVITY_PORT_DOWN &&
10607 element != EL_SP_PORT_VERTICAL &&
10608 element != EL_SP_PORT_ANY) ||
10609 !IN_LEV_FIELD(nextx, nexty) ||
10610 !IS_FREE(nextx, nexty))
10611 return MF_NO_ACTION;
10614 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10615 element == EL_SP_GRAVITY_PORT_RIGHT ||
10616 element == EL_SP_GRAVITY_PORT_UP ||
10617 element == EL_SP_GRAVITY_PORT_DOWN)
10618 game.gravity = !game.gravity;
10620 /* automatically move to the next field with double speed */
10621 player->programmed_action = move_direction;
10623 if (player->move_delay_reset_counter == 0)
10625 player->move_delay_reset_counter = 2; /* two double speed steps */
10627 DOUBLE_PLAYER_SPEED(player);
10630 player->move_delay_reset_counter = 2;
10632 DOUBLE_PLAYER_SPEED(player);
10636 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10639 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10645 case EL_TUBE_VERTICAL:
10646 case EL_TUBE_HORIZONTAL:
10647 case EL_TUBE_VERTICAL_LEFT:
10648 case EL_TUBE_VERTICAL_RIGHT:
10649 case EL_TUBE_HORIZONTAL_UP:
10650 case EL_TUBE_HORIZONTAL_DOWN:
10651 case EL_TUBE_LEFT_UP:
10652 case EL_TUBE_LEFT_DOWN:
10653 case EL_TUBE_RIGHT_UP:
10654 case EL_TUBE_RIGHT_DOWN:
10657 int tube_enter_directions[][2] =
10659 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10660 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10661 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10662 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
10663 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
10664 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
10665 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
10666 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
10667 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
10668 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
10669 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
10670 { -1, MV_NO_MOVING }
10673 while (tube_enter_directions[i][0] != element)
10676 if (tube_enter_directions[i][0] == -1) /* should not happen */
10680 if (!(tube_enter_directions[i][1] & move_direction))
10681 return MF_NO_ACTION; /* tube has no opening in this direction */
10683 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10691 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10693 if (IS_WALKABLE(element))
10696 int sound_action = ACTION_WALKING;
10699 if (!ACCESS_FROM(element, opposite_direction))
10700 return MF_NO_ACTION; /* field not accessible from this direction */
10704 if (element == EL_EMPTY_SPACE &&
10705 game.gravity && !player->is_auto_moving &&
10706 canFallDown(player) && move_direction != MV_DOWN)
10707 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10710 if (IS_GATE(element))
10712 if (!player->key[element - EL_GATE_1])
10713 return MF_NO_ACTION;
10715 else if (IS_GATE_GRAY(element))
10717 if (!player->key[element - EL_GATE_1_GRAY])
10718 return MF_NO_ACTION;
10720 else if (element == EL_EXIT_OPEN ||
10721 element == EL_SP_EXIT_OPEN ||
10722 element == EL_SP_EXIT_OPENING)
10724 sound_action = ACTION_PASSING; /* player is passing exit */
10726 else if (element == EL_EMPTY)
10728 sound_action = ACTION_MOVING; /* nothing to walk on */
10731 /* play sound from background or player, whatever is available */
10732 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
10733 PlayLevelSoundElementAction(x, y, element, sound_action);
10735 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10740 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
10742 else if (IS_PASSABLE(element))
10746 if (!canPassField(x, y, move_direction))
10747 return MF_NO_ACTION;
10752 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
10753 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
10754 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
10755 return MF_NO_ACTION;
10757 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10758 return MF_NO_ACTION;
10763 if (!ACCESS_FROM(element, opposite_direction))
10764 return MF_NO_ACTION; /* field not accessible from this direction */
10766 if (IS_CUSTOM_ELEMENT(element) &&
10767 !ACCESS_FROM(element, opposite_direction))
10768 return MF_NO_ACTION; /* field not accessible from this direction */
10772 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10773 return MF_NO_ACTION;
10778 if (IS_EM_GATE(element))
10780 if (!player->key[element - EL_EM_GATE_1])
10781 return MF_NO_ACTION;
10783 else if (IS_EM_GATE_GRAY(element))
10785 if (!player->key[element - EL_EM_GATE_1_GRAY])
10786 return MF_NO_ACTION;
10788 else if (IS_SP_PORT(element))
10790 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10791 element == EL_SP_GRAVITY_PORT_RIGHT ||
10792 element == EL_SP_GRAVITY_PORT_UP ||
10793 element == EL_SP_GRAVITY_PORT_DOWN)
10794 game.gravity = !game.gravity;
10797 /* automatically move to the next field with double speed */
10798 player->programmed_action = move_direction;
10800 if (player->move_delay_reset_counter == 0)
10802 player->move_delay_reset_counter = 2; /* two double speed steps */
10804 DOUBLE_PLAYER_SPEED(player);
10807 player->move_delay_reset_counter = 2;
10809 DOUBLE_PLAYER_SPEED(player);
10812 PlayLevelSoundAction(x, y, ACTION_PASSING);
10816 else if (IS_DIGGABLE(element))
10820 if (mode != DF_SNAP)
10823 GfxElement[x][y] = GFX_ELEMENT(element);
10826 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
10828 player->is_digging = TRUE;
10831 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10833 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
10834 player->index_bit, dig_side);
10837 if (mode == DF_SNAP)
10838 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10843 else if (IS_COLLECTIBLE(element))
10847 if (mode != DF_SNAP)
10849 GfxElement[x][y] = element;
10850 player->is_collecting = TRUE;
10853 if (element == EL_SPEED_PILL)
10854 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10855 else if (element == EL_EXTRA_TIME && level.time > 0)
10858 DrawGameValue_Time(TimeLeft);
10860 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10862 player->shield_normal_time_left += 10;
10863 if (element == EL_SHIELD_DEADLY)
10864 player->shield_deadly_time_left += 10;
10866 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10868 if (player->inventory_size < MAX_INVENTORY_SIZE)
10869 player->inventory_element[player->inventory_size++] = element;
10871 DrawGameValue_Dynamite(local_player->inventory_size);
10873 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10875 player->dynabomb_count++;
10876 player->dynabombs_left++;
10878 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10880 player->dynabomb_size++;
10882 else if (element == EL_DYNABOMB_INCREASE_POWER)
10884 player->dynabomb_xl = TRUE;
10886 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
10887 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
10889 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
10890 element - EL_KEY_1 : element - EL_EM_KEY_1);
10892 player->key[key_nr] = TRUE;
10894 DrawGameValue_Keys(player);
10896 redraw_mask |= REDRAW_DOOR_1;
10898 else if (IS_ENVELOPE(element))
10901 player->show_envelope = element;
10903 ShowEnvelope(element - EL_ENVELOPE_1);
10906 else if (IS_DROPPABLE(element) ||
10907 IS_THROWABLE(element)) /* can be collected and dropped */
10911 if (element_info[element].collect_count == 0)
10912 player->inventory_infinite_element = element;
10914 for (i = 0; i < element_info[element].collect_count; i++)
10915 if (player->inventory_size < MAX_INVENTORY_SIZE)
10916 player->inventory_element[player->inventory_size++] = element;
10918 DrawGameValue_Dynamite(local_player->inventory_size);
10920 else if (element_info[element].collect_count > 0)
10922 local_player->gems_still_needed -=
10923 element_info[element].collect_count;
10924 if (local_player->gems_still_needed < 0)
10925 local_player->gems_still_needed = 0;
10927 DrawGameValue_Emeralds(local_player->gems_still_needed);
10930 RaiseScoreElement(element);
10931 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10933 CheckTriggeredElementChangeByPlayer(x, y, element,
10934 CE_OTHER_GETS_COLLECTED,
10935 player->index_bit, dig_side);
10938 if (mode == DF_SNAP)
10939 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10944 else if (IS_PUSHABLE(element))
10946 if (mode == DF_SNAP && element != EL_BD_ROCK)
10947 return MF_NO_ACTION;
10949 if (CAN_FALL(element) && dy)
10950 return MF_NO_ACTION;
10952 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10953 !(element == EL_SPRING && level.use_spring_bug))
10954 return MF_NO_ACTION;
10957 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10958 ((move_direction & MV_VERTICAL &&
10959 ((element_info[element].move_pattern & MV_LEFT &&
10960 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10961 (element_info[element].move_pattern & MV_RIGHT &&
10962 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10963 (move_direction & MV_HORIZONTAL &&
10964 ((element_info[element].move_pattern & MV_UP &&
10965 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10966 (element_info[element].move_pattern & MV_DOWN &&
10967 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10968 return MF_NO_ACTION;
10972 /* do not push elements already moving away faster than player */
10973 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10974 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10975 return MF_NO_ACTION;
10977 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
10978 return MF_NO_ACTION;
10982 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10984 if (player->push_delay_value == -1)
10985 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10987 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10989 if (!player->is_pushing)
10990 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10994 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
10995 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
10996 !player_is_pushing))
10997 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11000 if (!player->is_pushing &&
11001 game.engine_version >= VERSION_IDENT(2,2,0,7))
11002 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11006 printf("::: push delay: %ld [%d, %d] [%d]\n",
11007 player->push_delay_value, FrameCounter, game.engine_version,
11008 player->is_pushing);
11011 player->is_pushing = TRUE;
11013 if (!(IN_LEV_FIELD(nextx, nexty) &&
11014 (IS_FREE(nextx, nexty) ||
11015 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11016 IS_SB_ELEMENT(element)))))
11017 return MF_NO_ACTION;
11019 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11020 return MF_NO_ACTION;
11022 if (player->push_delay == 0) /* new pushing; restart delay */
11023 player->push_delay = FrameCounter;
11025 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11026 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11027 element != EL_SPRING && element != EL_BALLOON)
11029 /* make sure that there is no move delay before next try to push */
11030 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11031 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11033 return MF_NO_ACTION;
11037 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11040 if (IS_SB_ELEMENT(element))
11042 if (element == EL_SOKOBAN_FIELD_FULL)
11044 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11045 local_player->sokobanfields_still_needed++;
11048 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11050 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11051 local_player->sokobanfields_still_needed--;
11054 Feld[x][y] = EL_SOKOBAN_OBJECT;
11056 if (Back[x][y] == Back[nextx][nexty])
11057 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11058 else if (Back[x][y] != 0)
11059 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11062 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11065 if (local_player->sokobanfields_still_needed == 0 &&
11066 game.emulation == EMU_SOKOBAN)
11068 player->LevelSolved = player->GameOver = TRUE;
11069 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11073 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11075 InitMovingField(x, y, move_direction);
11076 GfxAction[x][y] = ACTION_PUSHING;
11078 if (mode == DF_SNAP)
11079 ContinueMoving(x, y);
11081 MovPos[x][y] = (dx != 0 ? dx : dy);
11083 Pushed[x][y] = TRUE;
11084 Pushed[nextx][nexty] = TRUE;
11086 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11087 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11089 player->push_delay_value = -1; /* get new value later */
11092 /* check for element change _after_ element has been pushed! */
11096 /* !!! TEST ONLY !!! */
11097 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11098 player->index_bit, dig_side);
11099 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11100 player->index_bit, dig_side);
11102 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11103 player->index_bit, dig_side);
11104 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11105 player->index_bit, dig_side);
11111 else if (IS_SWITCHABLE(element))
11113 if (PLAYER_SWITCHING(player, x, y))
11116 player->is_switching = TRUE;
11117 player->switch_x = x;
11118 player->switch_y = y;
11120 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11122 if (element == EL_ROBOT_WHEEL)
11124 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11128 DrawLevelField(x, y);
11130 else if (element == EL_SP_TERMINAL)
11134 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11136 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11138 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11139 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11142 else if (IS_BELT_SWITCH(element))
11144 ToggleBeltSwitch(x, y);
11146 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11147 element == EL_SWITCHGATE_SWITCH_DOWN)
11149 ToggleSwitchgateSwitch(x, y);
11151 else if (element == EL_LIGHT_SWITCH ||
11152 element == EL_LIGHT_SWITCH_ACTIVE)
11154 ToggleLightSwitch(x, y);
11157 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11158 SND_LIGHT_SWITCH_ACTIVATING :
11159 SND_LIGHT_SWITCH_DEACTIVATING);
11162 else if (element == EL_TIMEGATE_SWITCH)
11164 ActivateTimegateSwitch(x, y);
11166 else if (element == EL_BALLOON_SWITCH_LEFT ||
11167 element == EL_BALLOON_SWITCH_RIGHT ||
11168 element == EL_BALLOON_SWITCH_UP ||
11169 element == EL_BALLOON_SWITCH_DOWN ||
11170 element == EL_BALLOON_SWITCH_ANY)
11172 if (element == EL_BALLOON_SWITCH_ANY)
11173 game.balloon_dir = move_direction;
11175 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11176 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11177 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11178 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11181 else if (element == EL_LAMP)
11183 Feld[x][y] = EL_LAMP_ACTIVE;
11184 local_player->lights_still_needed--;
11186 DrawLevelField(x, y);
11188 else if (element == EL_TIME_ORB_FULL)
11190 Feld[x][y] = EL_TIME_ORB_EMPTY;
11192 DrawGameValue_Time(TimeLeft);
11194 DrawLevelField(x, y);
11197 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11205 if (!PLAYER_SWITCHING(player, x, y))
11207 player->is_switching = TRUE;
11208 player->switch_x = x;
11209 player->switch_y = y;
11212 /* !!! TEST ONLY !!! */
11213 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11214 player->index_bit, dig_side);
11215 CheckTriggeredElementChangeByPlayer(x, y, element,
11216 CE_OTHER_IS_SWITCHING,
11217 player->index_bit, dig_side);
11219 CheckTriggeredElementChangeByPlayer(x, y, element,
11220 CE_OTHER_IS_SWITCHING,
11221 player->index_bit, dig_side);
11222 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11223 player->index_bit, dig_side);
11228 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11229 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11230 player->index_bit, dig_side);
11231 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11232 player->index_bit, dig_side);
11234 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11235 player->index_bit, dig_side);
11236 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11237 player->index_bit, dig_side);
11241 return MF_NO_ACTION;
11244 player->push_delay = 0;
11246 if (Feld[x][y] != element) /* really digged/collected something */
11247 player->is_collecting = !player->is_digging;
11252 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11254 int jx = player->jx, jy = player->jy;
11255 int x = jx + dx, y = jy + dy;
11256 int snap_direction = (dx == -1 ? MV_LEFT :
11257 dx == +1 ? MV_RIGHT :
11259 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11262 if (player->MovPos != 0)
11265 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11269 if (!player->active || !IN_LEV_FIELD(x, y))
11277 if (player->MovPos == 0)
11278 player->is_pushing = FALSE;
11280 player->is_snapping = FALSE;
11282 if (player->MovPos == 0)
11284 player->is_moving = FALSE;
11285 player->is_digging = FALSE;
11286 player->is_collecting = FALSE;
11292 if (player->is_snapping)
11295 player->MovDir = snap_direction;
11298 if (player->MovPos == 0)
11301 player->is_moving = FALSE;
11302 player->is_digging = FALSE;
11303 player->is_collecting = FALSE;
11306 player->is_dropping = FALSE;
11308 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11311 player->is_snapping = TRUE;
11314 if (player->MovPos == 0)
11317 player->is_moving = FALSE;
11318 player->is_digging = FALSE;
11319 player->is_collecting = FALSE;
11323 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11324 DrawLevelField(player->last_jx, player->last_jy);
11327 DrawLevelField(x, y);
11336 boolean DropElement(struct PlayerInfo *player)
11338 static int trigger_sides[4] =
11340 CH_SIDE_LEFT, /* dropping left */
11341 CH_SIDE_RIGHT, /* dropping right */
11342 CH_SIDE_TOP, /* dropping up */
11343 CH_SIDE_BOTTOM, /* dropping down */
11345 int old_element, new_element;
11346 int dropx = player->jx, dropy = player->jy;
11347 int drop_direction = player->MovDir;
11348 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11349 int drop_element = (player->inventory_size > 0 ?
11350 player->inventory_element[player->inventory_size - 1] :
11351 player->inventory_infinite_element != EL_UNDEFINED ?
11352 player->inventory_infinite_element :
11353 player->dynabombs_left > 0 ?
11354 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11357 if (IS_THROWABLE(drop_element))
11359 dropx += GET_DX_FROM_DIR(drop_direction);
11360 dropy += GET_DY_FROM_DIR(drop_direction);
11362 if (!IN_LEV_FIELD(dropx, dropy))
11366 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11367 new_element = drop_element; /* default: no change when dropping */
11369 /* check if player is active, not moving and ready to drop */
11370 if (!player->active || player->MovPos || player->drop_delay > 0)
11373 /* check if player has anything that can be dropped */
11375 if (new_element == EL_UNDEFINED)
11378 if (player->inventory_size == 0 &&
11379 player->inventory_infinite_element == EL_UNDEFINED &&
11380 player->dynabombs_left == 0)
11384 /* check if anything can be dropped at the current position */
11385 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11388 /* collected custom elements can only be dropped on empty fields */
11390 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11393 if (player->inventory_size > 0 &&
11394 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11395 && old_element != EL_EMPTY)
11399 if (old_element != EL_EMPTY)
11400 Back[dropx][dropy] = old_element; /* store old element on this field */
11402 ResetGfxAnimation(dropx, dropy);
11403 ResetRandomAnimationValue(dropx, dropy);
11405 if (player->inventory_size > 0 ||
11406 player->inventory_infinite_element != EL_UNDEFINED)
11408 if (player->inventory_size > 0)
11410 player->inventory_size--;
11413 new_element = player->inventory_element[player->inventory_size];
11416 DrawGameValue_Dynamite(local_player->inventory_size);
11418 if (new_element == EL_DYNAMITE)
11419 new_element = EL_DYNAMITE_ACTIVE;
11420 else if (new_element == EL_SP_DISK_RED)
11421 new_element = EL_SP_DISK_RED_ACTIVE;
11424 Feld[dropx][dropy] = new_element;
11426 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11427 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11428 el2img(Feld[dropx][dropy]), 0);
11430 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11433 /* needed if previous element just changed to "empty" in the last frame */
11434 Changed[dropx][dropy] = 0; /* allow another change */
11438 /* !!! TEST ONLY !!! */
11439 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11440 player->index_bit, drop_side);
11441 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11442 CE_OTHER_GETS_DROPPED,
11443 player->index_bit, drop_side);
11445 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11446 CE_OTHER_GETS_DROPPED,
11447 player->index_bit, drop_side);
11448 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11449 player->index_bit, drop_side);
11452 TestIfElementTouchesCustomElement(dropx, dropy);
11454 else /* player is dropping a dyna bomb */
11456 player->dynabombs_left--;
11459 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
11462 Feld[dropx][dropy] = new_element;
11464 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11465 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11466 el2img(Feld[dropx][dropy]), 0);
11468 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11475 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11478 InitField_WithBug1(dropx, dropy, FALSE);
11480 InitField(dropx, dropy, FALSE);
11481 if (CAN_MOVE(Feld[dropx][dropy]))
11482 InitMovDir(dropx, dropy);
11486 new_element = Feld[dropx][dropy]; /* element might have changed */
11488 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11489 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11492 int move_stepsize = element_info[new_element].move_stepsize;
11494 int move_direction, nextx, nexty;
11496 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11497 MovDir[dropx][dropy] = drop_direction;
11499 move_direction = MovDir[dropx][dropy];
11500 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11501 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11504 Changed[dropx][dropy] = 0; /* allow another change */
11505 CheckCollision[dropx][dropy] = 2;
11508 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
11511 WasJustMoving[dropx][dropy] = 3;
11514 InitMovingField(dropx, dropy, move_direction);
11515 ContinueMoving(dropx, dropy);
11522 Changed[dropx][dropy] = 0; /* allow another change */
11525 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
11527 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
11528 CE_HITTING_SOMETHING, move_direction);
11536 player->drop_delay = 2 * TILEX / move_stepsize + 1;
11541 player->drop_delay = 8 + 8 + 8;
11545 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11550 player->is_dropping = TRUE;
11556 /* ------------------------------------------------------------------------- */
11557 /* game sound playing functions */
11558 /* ------------------------------------------------------------------------- */
11560 static int *loop_sound_frame = NULL;
11561 static int *loop_sound_volume = NULL;
11563 void InitPlayLevelSound()
11565 int num_sounds = getSoundListSize();
11567 checked_free(loop_sound_frame);
11568 checked_free(loop_sound_volume);
11570 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11571 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11574 static void PlayLevelSound(int x, int y, int nr)
11576 int sx = SCREENX(x), sy = SCREENY(y);
11577 int volume, stereo_position;
11578 int max_distance = 8;
11579 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11581 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11582 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11585 if (!IN_LEV_FIELD(x, y) ||
11586 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11587 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11590 volume = SOUND_MAX_VOLUME;
11592 if (!IN_SCR_FIELD(sx, sy))
11594 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11595 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11597 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11600 stereo_position = (SOUND_MAX_LEFT +
11601 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11602 (SCR_FIELDX + 2 * max_distance));
11604 if (IS_LOOP_SOUND(nr))
11606 /* This assures that quieter loop sounds do not overwrite louder ones,
11607 while restarting sound volume comparison with each new game frame. */
11609 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11612 loop_sound_volume[nr] = volume;
11613 loop_sound_frame[nr] = FrameCounter;
11616 PlaySoundExt(nr, volume, stereo_position, type);
11619 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11621 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11622 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11623 y < LEVELY(BY1) ? LEVELY(BY1) :
11624 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11628 static void PlayLevelSoundAction(int x, int y, int action)
11630 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11633 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11635 int sound_effect = element_info[element].sound[action];
11637 if (sound_effect != SND_UNDEFINED)
11638 PlayLevelSound(x, y, sound_effect);
11641 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11644 int sound_effect = element_info[element].sound[action];
11646 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11647 PlayLevelSound(x, y, sound_effect);
11650 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11652 int sound_effect = element_info[Feld[x][y]].sound[action];
11654 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11655 PlayLevelSound(x, y, sound_effect);
11658 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11660 int sound_effect = element_info[Feld[x][y]].sound[action];
11662 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11663 StopSound(sound_effect);
11666 static void PlayLevelMusic()
11668 if (levelset.music[level_nr] != MUS_UNDEFINED)
11669 PlayMusic(levelset.music[level_nr]); /* from config file */
11671 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11674 void RaiseScore(int value)
11676 local_player->score += value;
11678 DrawGameValue_Score(local_player->score);
11681 void RaiseScoreElement(int element)
11686 case EL_BD_DIAMOND:
11687 case EL_EMERALD_YELLOW:
11688 case EL_EMERALD_RED:
11689 case EL_EMERALD_PURPLE:
11690 case EL_SP_INFOTRON:
11691 RaiseScore(level.score[SC_EMERALD]);
11694 RaiseScore(level.score[SC_DIAMOND]);
11697 RaiseScore(level.score[SC_CRYSTAL]);
11700 RaiseScore(level.score[SC_PEARL]);
11703 case EL_BD_BUTTERFLY:
11704 case EL_SP_ELECTRON:
11705 RaiseScore(level.score[SC_BUG]);
11708 case EL_BD_FIREFLY:
11709 case EL_SP_SNIKSNAK:
11710 RaiseScore(level.score[SC_SPACESHIP]);
11713 case EL_DARK_YAMYAM:
11714 RaiseScore(level.score[SC_YAMYAM]);
11717 RaiseScore(level.score[SC_ROBOT]);
11720 RaiseScore(level.score[SC_PACMAN]);
11723 RaiseScore(level.score[SC_NUT]);
11726 case EL_SP_DISK_RED:
11727 case EL_DYNABOMB_INCREASE_NUMBER:
11728 case EL_DYNABOMB_INCREASE_SIZE:
11729 case EL_DYNABOMB_INCREASE_POWER:
11730 RaiseScore(level.score[SC_DYNAMITE]);
11732 case EL_SHIELD_NORMAL:
11733 case EL_SHIELD_DEADLY:
11734 RaiseScore(level.score[SC_SHIELD]);
11736 case EL_EXTRA_TIME:
11737 RaiseScore(level.score[SC_TIME_BONUS]);
11743 RaiseScore(level.score[SC_KEY]);
11746 RaiseScore(element_info[element].collect_score);
11751 void RequestQuitGame(boolean ask_if_really_quit)
11753 if (AllPlayersGone ||
11754 !ask_if_really_quit ||
11755 level_editor_test_game ||
11756 Request("Do you really want to quit the game ?",
11757 REQ_ASK | REQ_STAY_CLOSED))
11759 #if defined(PLATFORM_UNIX)
11760 if (options.network)
11761 SendToServer_StopPlaying();
11765 game_status = GAME_MODE_MAIN;
11773 if (tape.playing && tape.deactivate_display)
11774 TapeDeactivateDisplayOff(TRUE);
11777 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11780 if (tape.playing && tape.deactivate_display)
11781 TapeDeactivateDisplayOn();
11788 /* ---------- new game button stuff ---------------------------------------- */
11790 /* graphic position values for game buttons */
11791 #define GAME_BUTTON_XSIZE 30
11792 #define GAME_BUTTON_YSIZE 30
11793 #define GAME_BUTTON_XPOS 5
11794 #define GAME_BUTTON_YPOS 215
11795 #define SOUND_BUTTON_XPOS 5
11796 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11798 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11799 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11800 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11801 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11802 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11803 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11810 } gamebutton_info[NUM_GAME_BUTTONS] =
11813 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11818 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11819 GAME_CTRL_ID_PAUSE,
11823 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11828 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11829 SOUND_CTRL_ID_MUSIC,
11830 "background music on/off"
11833 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11834 SOUND_CTRL_ID_LOOPS,
11835 "sound loops on/off"
11838 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11839 SOUND_CTRL_ID_SIMPLE,
11840 "normal sounds on/off"
11844 void CreateGameButtons()
11848 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11850 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11851 struct GadgetInfo *gi;
11854 unsigned long event_mask;
11855 int gd_xoffset, gd_yoffset;
11856 int gd_x1, gd_x2, gd_y1, gd_y2;
11859 gd_xoffset = gamebutton_info[i].x;
11860 gd_yoffset = gamebutton_info[i].y;
11861 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11862 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11864 if (id == GAME_CTRL_ID_STOP ||
11865 id == GAME_CTRL_ID_PAUSE ||
11866 id == GAME_CTRL_ID_PLAY)
11868 button_type = GD_TYPE_NORMAL_BUTTON;
11870 event_mask = GD_EVENT_RELEASED;
11871 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11872 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11876 button_type = GD_TYPE_CHECK_BUTTON;
11878 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11879 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11880 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11881 event_mask = GD_EVENT_PRESSED;
11882 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11883 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11886 gi = CreateGadget(GDI_CUSTOM_ID, id,
11887 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11888 GDI_X, DX + gd_xoffset,
11889 GDI_Y, DY + gd_yoffset,
11890 GDI_WIDTH, GAME_BUTTON_XSIZE,
11891 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11892 GDI_TYPE, button_type,
11893 GDI_STATE, GD_BUTTON_UNPRESSED,
11894 GDI_CHECKED, checked,
11895 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11896 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11897 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11898 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11899 GDI_EVENT_MASK, event_mask,
11900 GDI_CALLBACK_ACTION, HandleGameButtons,
11904 Error(ERR_EXIT, "cannot create gadget");
11906 game_gadget[id] = gi;
11910 void FreeGameButtons()
11914 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11915 FreeGadget(game_gadget[i]);
11918 static void MapGameButtons()
11922 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11923 MapGadget(game_gadget[i]);
11926 void UnmapGameButtons()
11930 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11931 UnmapGadget(game_gadget[i]);
11934 static void HandleGameButtons(struct GadgetInfo *gi)
11936 int id = gi->custom_id;
11938 if (game_status != GAME_MODE_PLAYING)
11943 case GAME_CTRL_ID_STOP:
11944 RequestQuitGame(TRUE);
11947 case GAME_CTRL_ID_PAUSE:
11948 if (options.network)
11950 #if defined(PLATFORM_UNIX)
11952 SendToServer_ContinuePlaying();
11954 SendToServer_PausePlaying();
11958 TapeTogglePause(TAPE_TOGGLE_MANUAL);
11961 case GAME_CTRL_ID_PLAY:
11964 #if defined(PLATFORM_UNIX)
11965 if (options.network)
11966 SendToServer_ContinuePlaying();
11970 tape.pausing = FALSE;
11971 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11976 case SOUND_CTRL_ID_MUSIC:
11977 if (setup.sound_music)
11979 setup.sound_music = FALSE;
11982 else if (audio.music_available)
11984 setup.sound = setup.sound_music = TRUE;
11986 SetAudioMode(setup.sound);
11992 case SOUND_CTRL_ID_LOOPS:
11993 if (setup.sound_loops)
11994 setup.sound_loops = FALSE;
11995 else if (audio.loops_available)
11997 setup.sound = setup.sound_loops = TRUE;
11998 SetAudioMode(setup.sound);
12002 case SOUND_CTRL_ID_SIMPLE:
12003 if (setup.sound_simple)
12004 setup.sound_simple = FALSE;
12005 else if (audio.sound_available)
12007 setup.sound = setup.sound_simple = TRUE;
12008 SetAudioMode(setup.sound);