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;
773 player->jx = player->last_jx = x;
774 player->jy = player->last_jy = y;
778 static void InitField(int x, int y, boolean init_game)
780 int element = Feld[x][y];
789 InitPlayerField(x, y, element, init_game);
792 case EL_SOKOBAN_FIELD_PLAYER:
793 element = Feld[x][y] = EL_PLAYER_1;
794 InitField(x, y, init_game);
796 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
797 InitField(x, y, init_game);
800 case EL_SOKOBAN_FIELD_EMPTY:
801 local_player->sokobanfields_still_needed++;
805 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
806 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
807 else if (x > 0 && Feld[x-1][y] == EL_ACID)
808 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
809 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
810 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
811 else if (y > 0 && Feld[x][y-1] == EL_ACID)
812 Feld[x][y] = EL_ACID_POOL_BOTTOM;
813 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
814 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
822 case EL_SPACESHIP_RIGHT:
823 case EL_SPACESHIP_UP:
824 case EL_SPACESHIP_LEFT:
825 case EL_SPACESHIP_DOWN:
827 case EL_BD_BUTTERFLY_RIGHT:
828 case EL_BD_BUTTERFLY_UP:
829 case EL_BD_BUTTERFLY_LEFT:
830 case EL_BD_BUTTERFLY_DOWN:
831 case EL_BD_BUTTERFLY:
832 case EL_BD_FIREFLY_RIGHT:
833 case EL_BD_FIREFLY_UP:
834 case EL_BD_FIREFLY_LEFT:
835 case EL_BD_FIREFLY_DOWN:
837 case EL_PACMAN_RIGHT:
861 if (y == lev_fieldy - 1)
863 Feld[x][y] = EL_AMOEBA_GROWING;
864 Store[x][y] = EL_AMOEBA_WET;
868 case EL_DYNAMITE_ACTIVE:
869 case EL_SP_DISK_RED_ACTIVE:
870 case EL_DYNABOMB_PLAYER_1_ACTIVE:
871 case EL_DYNABOMB_PLAYER_2_ACTIVE:
872 case EL_DYNABOMB_PLAYER_3_ACTIVE:
873 case EL_DYNABOMB_PLAYER_4_ACTIVE:
878 local_player->lights_still_needed++;
882 local_player->friends_still_needed++;
887 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
892 Feld[x][y] = EL_EMPTY;
897 case EL_EM_KEY_1_FILE:
898 Feld[x][y] = EL_EM_KEY_1;
900 case EL_EM_KEY_2_FILE:
901 Feld[x][y] = EL_EM_KEY_2;
903 case EL_EM_KEY_3_FILE:
904 Feld[x][y] = EL_EM_KEY_3;
906 case EL_EM_KEY_4_FILE:
907 Feld[x][y] = EL_EM_KEY_4;
911 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
912 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
913 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
914 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
915 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
916 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
917 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
918 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
919 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
920 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
921 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
922 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
925 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
926 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
927 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
929 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
931 game.belt_dir[belt_nr] = belt_dir;
932 game.belt_dir_nr[belt_nr] = belt_dir_nr;
934 else /* more than one switch -- set it like the first switch */
936 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
941 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
943 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
946 case EL_LIGHT_SWITCH_ACTIVE:
948 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
952 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
954 else if (IS_GROUP_ELEMENT(element))
956 struct ElementGroupInfo *group = element_info[element].group;
957 int last_anim_random_frame = gfx.anim_random_frame;
960 if (group->choice_mode == ANIM_RANDOM)
961 gfx.anim_random_frame = RND(group->num_elements_resolved);
963 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
964 group->choice_mode, 0,
967 if (group->choice_mode == ANIM_RANDOM)
968 gfx.anim_random_frame = last_anim_random_frame;
972 Feld[x][y] = group->element_resolved[element_pos];
974 InitField(x, y, init_game);
980 static inline void InitField_WithBug1(int x, int y, boolean init_game)
982 InitField(x, y, init_game);
984 /* not needed to call InitMovDir() -- already done by InitField()! */
985 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
986 CAN_MOVE(Feld[x][y]))
990 static inline void InitField_WithBug2(int x, int y, boolean init_game)
992 int old_element = Feld[x][y];
994 InitField(x, y, init_game);
996 /* not needed to call InitMovDir() -- already done by InitField()! */
997 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
998 CAN_MOVE(old_element) &&
999 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1002 /* this case is in fact a combination of not less than three bugs:
1003 first, it calls InitMovDir() for elements that can move, although this is
1004 already done by InitField(); then, it checks the element that was at this
1005 field _before_ the call to InitField() (which can change it); lastly, it
1006 was not called for "mole with direction" elements, which were treated as
1007 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1011 inline void DrawGameValue_Emeralds(int value)
1013 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1016 inline void DrawGameValue_Dynamite(int value)
1018 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1021 inline void DrawGameValue_Keys(struct PlayerInfo *player)
1025 for (i = 0; i < MAX_KEYS; i++)
1027 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1028 el2edimg(EL_KEY_1 + i));
1031 inline void DrawGameValue_Score(int value)
1033 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1036 inline void DrawGameValue_Time(int value)
1039 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1041 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1044 inline void DrawGameValue_Level(int value)
1047 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1050 /* misuse area for displaying emeralds to draw bigger level number */
1051 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1052 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1054 /* now copy it to the area for displaying level number */
1055 BlitBitmap(drawto, drawto,
1056 DX_EMERALDS, DY_EMERALDS + 1,
1057 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1058 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1059 DX_LEVEL - 1, DY_LEVEL + 1);
1061 /* restore the area for displaying emeralds */
1062 DrawGameValue_Emeralds(local_player->gems_still_needed);
1064 /* yes, this is all really ugly :-) */
1068 void DrawGameDoorValues()
1072 DrawGameValue_Level(level_nr);
1074 for (i = 0; i < MAX_PLAYERS; i++)
1075 DrawGameValue_Keys(&stored_player[i]);
1077 DrawGameValue_Emeralds(local_player->gems_still_needed);
1078 DrawGameValue_Dynamite(local_player->inventory_size);
1079 DrawGameValue_Score(local_player->score);
1080 DrawGameValue_Time(TimeLeft);
1083 static void resolve_group_element(int group_element, int recursion_depth)
1085 static int group_nr;
1086 static struct ElementGroupInfo *group;
1087 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1090 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1092 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1093 group_element - EL_GROUP_START + 1);
1095 /* replace element which caused too deep recursion by question mark */
1096 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1101 if (recursion_depth == 0) /* initialization */
1103 group = element_info[group_element].group;
1104 group_nr = group_element - EL_GROUP_START;
1106 group->num_elements_resolved = 0;
1107 group->choice_pos = 0;
1110 for (i = 0; i < actual_group->num_elements; i++)
1112 int element = actual_group->element[i];
1114 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1117 if (IS_GROUP_ELEMENT(element))
1118 resolve_group_element(element, recursion_depth + 1);
1121 group->element_resolved[group->num_elements_resolved++] = element;
1122 element_info[element].in_group[group_nr] = TRUE;
1127 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1129 printf("::: group %d: %d resolved elements\n",
1130 group_element - EL_GROUP_START, group->num_elements_resolved);
1131 for (i = 0; i < group->num_elements_resolved; i++)
1132 printf("::: - %d ['%s']\n", group->element_resolved[i],
1133 element_info[group->element_resolved[i]].token_name);
1140 =============================================================================
1142 -----------------------------------------------------------------------------
1143 initialize game engine due to level / tape version number
1144 =============================================================================
1147 static void InitGameEngine()
1151 /* set game engine from tape file when re-playing, else from level file */
1152 game.engine_version = (tape.playing ? tape.engine_version :
1153 level.game_version);
1155 /* dynamically adjust element properties according to game engine version */
1156 InitElementPropertiesEngine(game.engine_version);
1159 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1160 printf(" tape version == %06d [%s] [file: %06d]\n",
1161 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1163 printf(" => game.engine_version == %06d\n", game.engine_version);
1166 /* ---------- recursively resolve group elements ------------------------- */
1168 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1169 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1170 element_info[i].in_group[j] = FALSE;
1172 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1173 resolve_group_element(EL_GROUP_START + i, 0);
1175 /* ---------- initialize player's initial move delay --------------------- */
1177 /* dynamically adjust player properties according to game engine version */
1178 game.initial_move_delay =
1179 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1180 INITIAL_MOVE_DELAY_OFF);
1182 /* dynamically adjust player properties according to level information */
1183 game.initial_move_delay_value =
1184 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1186 /* ---------- initialize player's initial push delay --------------------- */
1188 /* dynamically adjust player properties according to game engine version */
1189 game.initial_push_delay_value =
1190 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1192 /* ---------- initialize changing elements ------------------------------- */
1194 /* initialize changing elements information */
1195 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1197 struct ElementInfo *ei = &element_info[i];
1199 /* this pointer might have been changed in the level editor */
1200 ei->change = &ei->change_page[0];
1202 if (!IS_CUSTOM_ELEMENT(i))
1204 ei->change->target_element = EL_EMPTY_SPACE;
1205 ei->change->delay_fixed = 0;
1206 ei->change->delay_random = 0;
1207 ei->change->delay_frames = 1;
1210 ei->change_events = CE_BITMASK_DEFAULT;
1211 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1213 ei->event_page_nr[j] = 0;
1214 ei->event_page[j] = &ei->change_page[0];
1218 /* add changing elements from pre-defined list */
1219 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1221 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1222 struct ElementInfo *ei = &element_info[ch_delay->element];
1224 ei->change->target_element = ch_delay->target_element;
1225 ei->change->delay_fixed = ch_delay->change_delay;
1227 ei->change->pre_change_function = ch_delay->pre_change_function;
1228 ei->change->change_function = ch_delay->change_function;
1229 ei->change->post_change_function = ch_delay->post_change_function;
1231 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1234 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1239 /* add change events from custom element configuration */
1240 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1242 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1244 for (j = 0; j < ei->num_change_pages; j++)
1246 if (!ei->change_page[j].can_change)
1249 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1251 /* only add event page for the first page found with this event */
1252 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1253 !(ei->change_events & CH_EVENT_BIT(k)))
1255 ei->change_events |= CH_EVENT_BIT(k);
1256 ei->event_page_nr[k] = j;
1257 ei->event_page[k] = &ei->change_page[j];
1265 /* add change events from custom element configuration */
1266 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1268 int element = EL_CUSTOM_START + i;
1270 /* only add custom elements that change after fixed/random frame delay */
1271 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1272 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1276 /* ---------- initialize run-time trigger player and element ------------- */
1278 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1280 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1282 for (j = 0; j < ei->num_change_pages; j++)
1284 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1285 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1289 /* ---------- initialize trigger events ---------------------------------- */
1291 /* initialize trigger events information */
1292 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1293 trigger_events[i] = EP_BITMASK_DEFAULT;
1296 /* add trigger events from element change event properties */
1297 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1299 struct ElementInfo *ei = &element_info[i];
1301 for (j = 0; j < ei->num_change_pages; j++)
1303 if (!ei->change_page[j].can_change)
1306 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1308 int trigger_element = ei->change_page[j].trigger_element;
1310 if (IS_GROUP_ELEMENT(trigger_element))
1312 struct ElementGroupInfo *group = element_info[trigger_element].group;
1314 for (k = 0; k < group->num_elements_resolved; k++)
1315 trigger_events[group->element_resolved[k]]
1316 |= ei->change_page[j].events;
1319 trigger_events[trigger_element] |= ei->change_page[j].events;
1324 /* add trigger events from element change event properties */
1325 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1326 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1327 trigger_events[element_info[i].change->trigger_element] |=
1328 element_info[i].change->events;
1331 /* ---------- initialize push delay -------------------------------------- */
1333 /* initialize push delay values to default */
1334 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1336 if (!IS_CUSTOM_ELEMENT(i))
1338 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1339 element_info[i].push_delay_random = game.default_push_delay_random;
1343 /* set push delay value for certain elements from pre-defined list */
1344 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1346 int e = push_delay_list[i].element;
1348 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1349 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1352 /* set push delay value for Supaplex elements for newer engine versions */
1353 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1355 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1357 if (IS_SP_ELEMENT(i))
1359 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1360 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1365 /* ---------- initialize move stepsize ----------------------------------- */
1367 /* initialize move stepsize values to default */
1368 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1369 if (!IS_CUSTOM_ELEMENT(i))
1370 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1372 /* set move stepsize value for certain elements from pre-defined list */
1373 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1375 int e = move_stepsize_list[i].element;
1377 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1381 /* ---------- initialize move dig/leave ---------------------------------- */
1383 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1385 element_info[i].can_leave_element = FALSE;
1386 element_info[i].can_leave_element_last = FALSE;
1390 /* ---------- initialize gem count --------------------------------------- */
1392 /* initialize gem count values for each element */
1393 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1394 if (!IS_CUSTOM_ELEMENT(i))
1395 element_info[i].collect_count = 0;
1397 /* add gem count values for all elements from pre-defined list */
1398 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1399 element_info[collect_count_list[i].element].collect_count =
1400 collect_count_list[i].count;
1402 /* ---------- initialize access direction -------------------------------- */
1404 /* initialize access direction values to default (access from every side) */
1405 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1406 if (!IS_CUSTOM_ELEMENT(i))
1407 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1409 /* set access direction value for certain elements from pre-defined list */
1410 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1411 element_info[access_direction_list[i].element].access_direction =
1412 access_direction_list[i].direction;
1417 =============================================================================
1419 -----------------------------------------------------------------------------
1420 initialize and start new game
1421 =============================================================================
1426 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1427 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1428 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1435 #if USE_NEW_AMOEBA_CODE
1436 printf("Using new amoeba code.\n");
1438 printf("Using old amoeba code.\n");
1443 /* don't play tapes over network */
1444 network_playing = (options.network && !tape.playing);
1446 for (i = 0; i < MAX_PLAYERS; i++)
1448 struct PlayerInfo *player = &stored_player[i];
1450 player->index_nr = i;
1451 player->index_bit = (1 << i);
1452 player->element_nr = EL_PLAYER_1 + i;
1454 player->present = FALSE;
1455 player->active = FALSE;
1458 player->effective_action = 0;
1459 player->programmed_action = 0;
1462 player->gems_still_needed = level.gems_needed;
1463 player->sokobanfields_still_needed = 0;
1464 player->lights_still_needed = 0;
1465 player->friends_still_needed = 0;
1467 for (j = 0; j < MAX_KEYS; j++)
1468 player->key[j] = FALSE;
1470 player->dynabomb_count = 0;
1471 player->dynabomb_size = 1;
1472 player->dynabombs_left = 0;
1473 player->dynabomb_xl = FALSE;
1475 player->MovDir = MV_NO_MOVING;
1478 player->GfxDir = MV_NO_MOVING;
1479 player->GfxAction = ACTION_DEFAULT;
1481 player->StepFrame = 0;
1483 player->use_murphy_graphic = FALSE;
1485 player->block_last_field = FALSE;
1486 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1488 player->actual_frame_counter = 0;
1490 player->step_counter = 0;
1492 player->last_move_dir = MV_NO_MOVING;
1494 player->is_waiting = FALSE;
1495 player->is_moving = FALSE;
1496 player->is_auto_moving = FALSE;
1497 player->is_digging = FALSE;
1498 player->is_snapping = FALSE;
1499 player->is_collecting = FALSE;
1500 player->is_pushing = FALSE;
1501 player->is_switching = FALSE;
1502 player->is_dropping = FALSE;
1504 player->is_bored = FALSE;
1505 player->is_sleeping = FALSE;
1507 player->frame_counter_bored = -1;
1508 player->frame_counter_sleeping = -1;
1510 player->anim_delay_counter = 0;
1511 player->post_delay_counter = 0;
1513 player->action_waiting = ACTION_DEFAULT;
1514 player->last_action_waiting = ACTION_DEFAULT;
1515 player->special_action_bored = ACTION_DEFAULT;
1516 player->special_action_sleeping = ACTION_DEFAULT;
1518 player->num_special_action_bored = 0;
1519 player->num_special_action_sleeping = 0;
1521 /* determine number of special actions for bored and sleeping animation */
1522 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1524 boolean found = FALSE;
1526 for (k = 0; k < NUM_DIRECTIONS; k++)
1527 if (el_act_dir2img(player->element_nr, j, k) !=
1528 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1532 player->num_special_action_bored++;
1536 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1538 boolean found = FALSE;
1540 for (k = 0; k < NUM_DIRECTIONS; k++)
1541 if (el_act_dir2img(player->element_nr, j, k) !=
1542 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1546 player->num_special_action_sleeping++;
1551 player->switch_x = -1;
1552 player->switch_y = -1;
1554 player->show_envelope = 0;
1556 player->move_delay = game.initial_move_delay;
1557 player->move_delay_value = game.initial_move_delay_value;
1559 player->move_delay_reset_counter = 0;
1561 player->push_delay = 0;
1562 player->push_delay_value = game.initial_push_delay_value;
1564 player->drop_delay = 0;
1566 player->last_jx = player->last_jy = 0;
1567 player->jx = player->jy = 0;
1569 player->shield_normal_time_left = 0;
1570 player->shield_deadly_time_left = 0;
1572 player->inventory_infinite_element = EL_UNDEFINED;
1573 player->inventory_size = 0;
1575 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1576 SnapField(player, 0, 0);
1578 player->LevelSolved = FALSE;
1579 player->GameOver = FALSE;
1582 network_player_action_received = FALSE;
1584 #if defined(PLATFORM_UNIX)
1585 /* initial null action */
1586 if (network_playing)
1587 SendToServer_MovePlayer(MV_NO_MOVING);
1595 TimeLeft = level.time;
1598 ScreenMovDir = MV_NO_MOVING;
1602 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1604 AllPlayersGone = FALSE;
1606 game.yamyam_content_nr = 0;
1607 game.magic_wall_active = FALSE;
1608 game.magic_wall_time_left = 0;
1609 game.light_time_left = 0;
1610 game.timegate_time_left = 0;
1611 game.switchgate_pos = 0;
1612 game.balloon_dir = MV_NO_MOVING;
1613 game.gravity = level.initial_gravity;
1614 game.explosions_delayed = TRUE;
1616 game.envelope_active = FALSE;
1618 for (i = 0; i < NUM_BELTS; i++)
1620 game.belt_dir[i] = MV_NO_MOVING;
1621 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1624 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1625 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1627 for (x = 0; x < lev_fieldx; x++)
1629 for (y = 0; y < lev_fieldy; y++)
1631 Feld[x][y] = level.field[x][y];
1632 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1633 ChangeDelay[x][y] = 0;
1634 ChangePage[x][y] = -1;
1635 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1637 WasJustMoving[x][y] = 0;
1638 WasJustFalling[x][y] = 0;
1639 CheckCollision[x][y] = 0;
1641 Pushed[x][y] = FALSE;
1643 Changed[x][y] = CE_BITMASK_DEFAULT;
1644 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1646 ExplodePhase[x][y] = 0;
1647 ExplodeDelay[x][y] = 0;
1648 ExplodeField[x][y] = EX_TYPE_NONE;
1650 RunnerVisit[x][y] = 0;
1651 PlayerVisit[x][y] = 0;
1654 GfxRandom[x][y] = INIT_GFX_RANDOM();
1655 GfxElement[x][y] = EL_UNDEFINED;
1656 GfxAction[x][y] = ACTION_DEFAULT;
1657 GfxDir[x][y] = MV_NO_MOVING;
1661 for (y = 0; y < lev_fieldy; y++)
1663 for (x = 0; x < lev_fieldx; x++)
1665 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1667 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1669 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1672 InitField(x, y, TRUE);
1678 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1679 emulate_sb ? EMU_SOKOBAN :
1680 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1682 /* initialize explosion and ignition delay */
1683 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1685 if (!IS_CUSTOM_ELEMENT(i))
1688 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1689 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1690 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1691 int last_phase = (num_phase + 1) * delay;
1692 int half_phase = (num_phase / 2) * delay;
1694 element_info[i].explosion_delay = last_phase - 1;
1695 element_info[i].ignition_delay = half_phase;
1698 if (i == EL_BLACK_ORB)
1699 element_info[i].ignition_delay = 0;
1701 if (i == EL_BLACK_ORB)
1702 element_info[i].ignition_delay = 1;
1707 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1708 element_info[i].explosion_delay = 1;
1710 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1711 element_info[i].ignition_delay = 1;
1715 /* correct non-moving belts to start moving left */
1716 for (i = 0; i < NUM_BELTS; i++)
1717 if (game.belt_dir[i] == MV_NO_MOVING)
1718 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1720 /* check if any connected player was not found in playfield */
1721 for (i = 0; i < MAX_PLAYERS; i++)
1723 struct PlayerInfo *player = &stored_player[i];
1725 if (player->connected && !player->present)
1727 for (j = 0; j < MAX_PLAYERS; j++)
1729 struct PlayerInfo *some_player = &stored_player[j];
1730 int jx = some_player->jx, jy = some_player->jy;
1732 /* assign first free player found that is present in the playfield */
1733 if (some_player->present && !some_player->connected)
1735 player->present = TRUE;
1736 player->active = TRUE;
1738 some_player->present = FALSE;
1739 some_player->active = FALSE;
1742 player->element_nr = some_player->element_nr;
1745 StorePlayer[jx][jy] = player->element_nr;
1746 player->jx = player->last_jx = jx;
1747 player->jy = player->last_jy = jy;
1757 /* when playing a tape, eliminate all players which do not participate */
1759 for (i = 0; i < MAX_PLAYERS; i++)
1761 if (stored_player[i].active && !tape.player_participates[i])
1763 struct PlayerInfo *player = &stored_player[i];
1764 int jx = player->jx, jy = player->jy;
1766 player->active = FALSE;
1767 StorePlayer[jx][jy] = 0;
1768 Feld[jx][jy] = EL_EMPTY;
1772 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1774 /* when in single player mode, eliminate all but the first active player */
1776 for (i = 0; i < MAX_PLAYERS; i++)
1778 if (stored_player[i].active)
1780 for (j = i + 1; j < MAX_PLAYERS; j++)
1782 if (stored_player[j].active)
1784 struct PlayerInfo *player = &stored_player[j];
1785 int jx = player->jx, jy = player->jy;
1787 player->active = FALSE;
1788 player->present = FALSE;
1790 StorePlayer[jx][jy] = 0;
1791 Feld[jx][jy] = EL_EMPTY;
1798 /* when recording the game, store which players take part in the game */
1801 for (i = 0; i < MAX_PLAYERS; i++)
1802 if (stored_player[i].active)
1803 tape.player_participates[i] = TRUE;
1808 for (i = 0; i < MAX_PLAYERS; i++)
1810 struct PlayerInfo *player = &stored_player[i];
1812 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1817 if (local_player == player)
1818 printf("Player %d is local player.\n", i+1);
1822 if (BorderElement == EL_EMPTY)
1825 SBX_Right = lev_fieldx - SCR_FIELDX;
1827 SBY_Lower = lev_fieldy - SCR_FIELDY;
1832 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1834 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1837 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1838 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1840 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1841 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1843 /* if local player not found, look for custom element that might create
1844 the player (make some assumptions about the right custom element) */
1845 if (!local_player->present)
1847 int start_x = 0, start_y = 0;
1848 int found_rating = 0;
1849 int found_element = EL_UNDEFINED;
1851 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1853 int element = Feld[x][y];
1858 if (!IS_CUSTOM_ELEMENT(element))
1861 if (CAN_CHANGE(element))
1863 for (i = 0; i < element_info[element].num_change_pages; i++)
1865 content = element_info[element].change_page[i].target_element;
1866 is_player = ELEM_IS_PLAYER(content);
1868 if (is_player && (found_rating < 3 || element < found_element))
1874 found_element = element;
1879 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1881 content = element_info[element].content[xx][yy];
1882 is_player = ELEM_IS_PLAYER(content);
1884 if (is_player && (found_rating < 2 || element < found_element))
1886 start_x = x + xx - 1;
1887 start_y = y + yy - 1;
1890 found_element = element;
1893 if (!CAN_CHANGE(element))
1896 for (i = 0; i < element_info[element].num_change_pages; i++)
1898 content= element_info[element].change_page[i].target_content[xx][yy];
1899 is_player = ELEM_IS_PLAYER(content);
1901 if (is_player && (found_rating < 1 || element < found_element))
1903 start_x = x + xx - 1;
1904 start_y = y + yy - 1;
1907 found_element = element;
1913 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1914 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1917 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1918 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1924 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1925 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1926 local_player->jx - MIDPOSX);
1928 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1929 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1930 local_player->jy - MIDPOSY);
1932 scroll_x = SBX_Left;
1933 scroll_y = SBY_Upper;
1934 if (local_player->jx >= SBX_Left + MIDPOSX)
1935 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1936 local_player->jx - MIDPOSX :
1938 if (local_player->jy >= SBY_Upper + MIDPOSY)
1939 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1940 local_player->jy - MIDPOSY :
1945 CloseDoor(DOOR_CLOSE_1);
1950 /* after drawing the level, correct some elements */
1951 if (game.timegate_time_left == 0)
1952 CloseAllOpenTimegates();
1954 if (setup.soft_scrolling)
1955 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1957 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1960 /* copy default game door content to main double buffer */
1961 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1962 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1964 DrawGameDoorValues();
1968 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1969 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1970 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1974 /* copy actual game door content to door double buffer for OpenDoor() */
1975 BlitBitmap(drawto, bitmap_db_door,
1976 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1978 OpenDoor(DOOR_OPEN_ALL);
1980 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1982 if (setup.sound_music)
1985 KeyboardAutoRepeatOffUnlessAutoplay();
1989 for (i = 0; i < MAX_PLAYERS; i++)
1990 printf("Player %d %sactive.\n",
1991 i + 1, (stored_player[i].active ? "" : "not "));
1995 printf("::: starting game [%d]\n", FrameCounter);
1999 void InitMovDir(int x, int y)
2001 int i, element = Feld[x][y];
2002 static int xy[4][2] =
2009 static int direction[3][4] =
2011 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2012 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2013 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2022 Feld[x][y] = EL_BUG;
2023 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2026 case EL_SPACESHIP_RIGHT:
2027 case EL_SPACESHIP_UP:
2028 case EL_SPACESHIP_LEFT:
2029 case EL_SPACESHIP_DOWN:
2030 Feld[x][y] = EL_SPACESHIP;
2031 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2034 case EL_BD_BUTTERFLY_RIGHT:
2035 case EL_BD_BUTTERFLY_UP:
2036 case EL_BD_BUTTERFLY_LEFT:
2037 case EL_BD_BUTTERFLY_DOWN:
2038 Feld[x][y] = EL_BD_BUTTERFLY;
2039 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2042 case EL_BD_FIREFLY_RIGHT:
2043 case EL_BD_FIREFLY_UP:
2044 case EL_BD_FIREFLY_LEFT:
2045 case EL_BD_FIREFLY_DOWN:
2046 Feld[x][y] = EL_BD_FIREFLY;
2047 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2050 case EL_PACMAN_RIGHT:
2052 case EL_PACMAN_LEFT:
2053 case EL_PACMAN_DOWN:
2054 Feld[x][y] = EL_PACMAN;
2055 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2058 case EL_SP_SNIKSNAK:
2059 MovDir[x][y] = MV_UP;
2062 case EL_SP_ELECTRON:
2063 MovDir[x][y] = MV_LEFT;
2070 Feld[x][y] = EL_MOLE;
2071 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2075 if (IS_CUSTOM_ELEMENT(element))
2077 struct ElementInfo *ei = &element_info[element];
2078 int move_direction_initial = ei->move_direction_initial;
2079 int move_pattern = ei->move_pattern;
2081 if (move_direction_initial == MV_START_PREVIOUS)
2083 if (MovDir[x][y] != MV_NO_MOVING)
2086 move_direction_initial = MV_START_AUTOMATIC;
2089 if (move_direction_initial == MV_START_RANDOM)
2090 MovDir[x][y] = 1 << RND(4);
2091 else if (move_direction_initial & MV_ANY_DIRECTION)
2092 MovDir[x][y] = move_direction_initial;
2093 else if (move_pattern == MV_ALL_DIRECTIONS ||
2094 move_pattern == MV_TURNING_LEFT ||
2095 move_pattern == MV_TURNING_RIGHT ||
2096 move_pattern == MV_TURNING_LEFT_RIGHT ||
2097 move_pattern == MV_TURNING_RIGHT_LEFT ||
2098 move_pattern == MV_TURNING_RANDOM)
2099 MovDir[x][y] = 1 << RND(4);
2100 else if (move_pattern == MV_HORIZONTAL)
2101 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2102 else if (move_pattern == MV_VERTICAL)
2103 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2104 else if (move_pattern & MV_ANY_DIRECTION)
2105 MovDir[x][y] = element_info[element].move_pattern;
2106 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2107 move_pattern == MV_ALONG_RIGHT_SIDE)
2110 /* use random direction as default start direction */
2111 if (game.engine_version >= VERSION_IDENT(3,1,0,2))
2112 MovDir[x][y] = 1 << RND(4);
2115 for (i = 0; i < NUM_DIRECTIONS; i++)
2117 int x1 = x + xy[i][0];
2118 int y1 = y + xy[i][1];
2120 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2122 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2123 MovDir[x][y] = direction[0][i];
2125 MovDir[x][y] = direction[1][i];
2134 MovDir[x][y] = 1 << RND(4);
2136 if (element != EL_BUG &&
2137 element != EL_SPACESHIP &&
2138 element != EL_BD_BUTTERFLY &&
2139 element != EL_BD_FIREFLY)
2142 for (i = 0; i < NUM_DIRECTIONS; i++)
2144 int x1 = x + xy[i][0];
2145 int y1 = y + xy[i][1];
2147 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2149 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2151 MovDir[x][y] = direction[0][i];
2154 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2155 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2157 MovDir[x][y] = direction[1][i];
2166 GfxDir[x][y] = MovDir[x][y];
2169 void InitAmoebaNr(int x, int y)
2172 int group_nr = AmoebeNachbarNr(x, y);
2176 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2178 if (AmoebaCnt[i] == 0)
2186 AmoebaNr[x][y] = group_nr;
2187 AmoebaCnt[group_nr]++;
2188 AmoebaCnt2[group_nr]++;
2194 boolean raise_level = FALSE;
2196 if (local_player->MovPos)
2200 if (tape.auto_play) /* tape might already be stopped here */
2201 tape.auto_play_level_solved = TRUE;
2203 if (tape.playing && tape.auto_play)
2204 tape.auto_play_level_solved = TRUE;
2207 local_player->LevelSolved = FALSE;
2209 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2213 if (!tape.playing && setup.sound_loops)
2214 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2215 SND_CTRL_PLAY_LOOP);
2217 while (TimeLeft > 0)
2219 if (!tape.playing && !setup.sound_loops)
2220 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2221 if (TimeLeft > 0 && !(TimeLeft % 10))
2222 RaiseScore(level.score[SC_TIME_BONUS]);
2223 if (TimeLeft > 100 && !(TimeLeft % 10))
2228 DrawGameValue_Time(TimeLeft);
2236 if (!tape.playing && setup.sound_loops)
2237 StopSound(SND_GAME_LEVELTIME_BONUS);
2239 else if (level.time == 0) /* level without time limit */
2241 if (!tape.playing && setup.sound_loops)
2242 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2243 SND_CTRL_PLAY_LOOP);
2245 while (TimePlayed < 999)
2247 if (!tape.playing && !setup.sound_loops)
2248 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2249 if (TimePlayed < 999 && !(TimePlayed % 10))
2250 RaiseScore(level.score[SC_TIME_BONUS]);
2251 if (TimePlayed < 900 && !(TimePlayed % 10))
2256 DrawGameValue_Time(TimePlayed);
2264 if (!tape.playing && setup.sound_loops)
2265 StopSound(SND_GAME_LEVELTIME_BONUS);
2268 /* close exit door after last player */
2269 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2270 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2272 int element = Feld[ExitX][ExitY];
2274 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2275 EL_SP_EXIT_CLOSING);
2277 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2280 /* Hero disappears */
2281 DrawLevelField(ExitX, ExitY);
2287 CloseDoor(DOOR_CLOSE_1);
2292 SaveTape(tape.level_nr); /* Ask to save tape */
2295 if (level_nr == leveldir_current->handicap_level)
2297 leveldir_current->handicap_level++;
2298 SaveLevelSetup_SeriesInfo();
2301 if (level_editor_test_game)
2302 local_player->score = -1; /* no highscore when playing from editor */
2303 else if (level_nr < leveldir_current->last_level)
2304 raise_level = TRUE; /* advance to next level */
2306 if ((hi_pos = NewHiScore()) >= 0)
2308 game_status = GAME_MODE_SCORES;
2309 DrawHallOfFame(hi_pos);
2318 game_status = GAME_MODE_MAIN;
2335 LoadScore(level_nr);
2337 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2338 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2341 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2343 if (local_player->score > highscore[k].Score)
2345 /* player has made it to the hall of fame */
2347 if (k < MAX_SCORE_ENTRIES - 1)
2349 int m = MAX_SCORE_ENTRIES - 1;
2352 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2353 if (!strcmp(setup.player_name, highscore[l].Name))
2355 if (m == k) /* player's new highscore overwrites his old one */
2359 for (l = m; l > k; l--)
2361 strcpy(highscore[l].Name, highscore[l - 1].Name);
2362 highscore[l].Score = highscore[l - 1].Score;
2369 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2370 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2371 highscore[k].Score = local_player->score;
2377 else if (!strncmp(setup.player_name, highscore[k].Name,
2378 MAX_PLAYER_NAME_LEN))
2379 break; /* player already there with a higher score */
2385 SaveScore(level_nr);
2390 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2392 if (player->GfxAction != action || player->GfxDir != dir)
2395 printf("Player frame reset! (%d => %d, %d => %d)\n",
2396 player->GfxAction, action, player->GfxDir, dir);
2399 player->GfxAction = action;
2400 player->GfxDir = dir;
2402 player->StepFrame = 0;
2406 static void ResetRandomAnimationValue(int x, int y)
2408 GfxRandom[x][y] = INIT_GFX_RANDOM();
2411 static void ResetGfxAnimation(int x, int y)
2414 GfxAction[x][y] = ACTION_DEFAULT;
2415 GfxDir[x][y] = MovDir[x][y];
2418 void InitMovingField(int x, int y, int direction)
2420 int element = Feld[x][y];
2421 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2422 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2426 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2427 ResetGfxAnimation(x, y);
2429 MovDir[newx][newy] = MovDir[x][y] = direction;
2430 GfxDir[x][y] = direction;
2432 if (Feld[newx][newy] == EL_EMPTY)
2433 Feld[newx][newy] = EL_BLOCKED;
2435 if (direction == MV_DOWN && CAN_FALL(element))
2436 GfxAction[x][y] = ACTION_FALLING;
2438 GfxAction[x][y] = ACTION_MOVING;
2440 GfxFrame[newx][newy] = GfxFrame[x][y];
2441 GfxRandom[newx][newy] = GfxRandom[x][y];
2442 GfxAction[newx][newy] = GfxAction[x][y];
2443 GfxDir[newx][newy] = GfxDir[x][y];
2446 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2448 int direction = MovDir[x][y];
2449 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2450 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2456 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2458 int oldx = x, oldy = y;
2459 int direction = MovDir[x][y];
2461 if (direction == MV_LEFT)
2463 else if (direction == MV_RIGHT)
2465 else if (direction == MV_UP)
2467 else if (direction == MV_DOWN)
2470 *comes_from_x = oldx;
2471 *comes_from_y = oldy;
2474 int MovingOrBlocked2Element(int x, int y)
2476 int element = Feld[x][y];
2478 if (element == EL_BLOCKED)
2482 Blocked2Moving(x, y, &oldx, &oldy);
2483 return Feld[oldx][oldy];
2489 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2491 /* like MovingOrBlocked2Element(), but if element is moving
2492 and (x,y) is the field the moving element is just leaving,
2493 return EL_BLOCKED instead of the element value */
2494 int element = Feld[x][y];
2496 if (IS_MOVING(x, y))
2498 if (element == EL_BLOCKED)
2502 Blocked2Moving(x, y, &oldx, &oldy);
2503 return Feld[oldx][oldy];
2512 static void RemoveField(int x, int y)
2514 Feld[x][y] = EL_EMPTY;
2521 ChangeDelay[x][y] = 0;
2522 ChangePage[x][y] = -1;
2523 Pushed[x][y] = FALSE;
2526 ExplodeField[x][y] = EX_TYPE_NONE;
2529 GfxElement[x][y] = EL_UNDEFINED;
2530 GfxAction[x][y] = ACTION_DEFAULT;
2531 GfxDir[x][y] = MV_NO_MOVING;
2534 void RemoveMovingField(int x, int y)
2536 int oldx = x, oldy = y, newx = x, newy = y;
2537 int element = Feld[x][y];
2538 int next_element = EL_UNDEFINED;
2540 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2543 if (IS_MOVING(x, y))
2545 Moving2Blocked(x, y, &newx, &newy);
2547 if (Feld[newx][newy] != EL_BLOCKED)
2550 if (Feld[newx][newy] != EL_BLOCKED)
2552 /* element is moving, but target field is not free (blocked), but
2553 already occupied by something different (example: acid pool);
2554 in this case, only remove the moving field, but not the target */
2556 RemoveField(oldx, oldy);
2558 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2560 DrawLevelField(oldx, oldy);
2566 else if (element == EL_BLOCKED)
2568 Blocked2Moving(x, y, &oldx, &oldy);
2569 if (!IS_MOVING(oldx, oldy))
2573 if (element == EL_BLOCKED &&
2574 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2575 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2576 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2577 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2578 next_element = get_next_element(Feld[oldx][oldy]);
2580 RemoveField(oldx, oldy);
2581 RemoveField(newx, newy);
2583 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2585 if (next_element != EL_UNDEFINED)
2586 Feld[oldx][oldy] = next_element;
2588 DrawLevelField(oldx, oldy);
2589 DrawLevelField(newx, newy);
2592 void DrawDynamite(int x, int y)
2594 int sx = SCREENX(x), sy = SCREENY(y);
2595 int graphic = el2img(Feld[x][y]);
2598 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2601 if (IS_WALKABLE_INSIDE(Back[x][y]))
2605 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2606 else if (Store[x][y])
2607 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2609 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2612 if (Back[x][y] || Store[x][y])
2613 DrawGraphicThruMask(sx, sy, graphic, frame);
2615 DrawGraphic(sx, sy, graphic, frame);
2617 if (game.emulation == EMU_SUPAPLEX)
2618 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2619 else if (Store[x][y])
2620 DrawGraphicThruMask(sx, sy, graphic, frame);
2622 DrawGraphic(sx, sy, graphic, frame);
2626 void CheckDynamite(int x, int y)
2628 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2632 if (MovDelay[x][y] != 0)
2635 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2642 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2644 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2645 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2646 StopSound(SND_DYNAMITE_ACTIVE);
2648 StopSound(SND_DYNABOMB_ACTIVE);
2654 void RelocatePlayer(int x, int y, int element_raw)
2656 int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
2657 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2658 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2659 boolean no_delay = (tape.warp_forward);
2660 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2661 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2664 if (player->GameOver) /* do not reanimate dead player */
2667 RemoveField(x, y); /* temporarily remove newly placed player */
2668 DrawLevelField(x, y);
2670 if (player->present)
2672 while (player->MovPos)
2674 ScrollPlayer(player, SCROLL_GO_ON);
2675 ScrollScreen(NULL, SCROLL_GO_ON);
2681 Delay(wait_delay_value);
2684 DrawPlayer(player); /* needed here only to cleanup last field */
2685 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2687 player->is_moving = FALSE;
2690 old_jx = player->jx;
2691 old_jy = player->jy;
2693 Feld[x][y] = element;
2694 InitPlayerField(x, y, element, TRUE);
2696 if (player != local_player) /* do not visually relocate other players */
2699 if (level.instant_relocation)
2702 int offset = (setup.scroll_delay ? 3 : 0);
2703 int jx = local_player->jx;
2704 int jy = local_player->jy;
2706 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2708 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2709 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2710 local_player->jx - MIDPOSX);
2712 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2713 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2714 local_player->jy - MIDPOSY);
2718 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2719 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2720 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2722 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2723 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2724 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2726 /* don't scroll over playfield boundaries */
2727 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2728 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2730 /* don't scroll over playfield boundaries */
2731 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2732 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2735 scroll_x += (local_player->jx - old_jx);
2736 scroll_y += (local_player->jy - old_jy);
2738 /* don't scroll over playfield boundaries */
2739 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2740 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2742 /* don't scroll over playfield boundaries */
2743 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2744 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2747 RedrawPlayfield(TRUE, 0,0,0,0);
2753 int offset = (setup.scroll_delay ? 3 : 0);
2754 int jx = local_player->jx;
2755 int jy = local_player->jy;
2757 int scroll_xx = -999, scroll_yy = -999;
2759 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2761 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2764 int fx = FX, fy = FY;
2766 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2767 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2768 local_player->jx - MIDPOSX);
2770 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2771 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2772 local_player->jy - MIDPOSY);
2774 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2775 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2778 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2781 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2788 fx += dx * TILEX / 2;
2789 fy += dy * TILEY / 2;
2791 ScrollLevel(dx, dy);
2794 /* scroll in two steps of half tile size to make things smoother */
2795 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2797 Delay(wait_delay_value);
2799 /* scroll second step to align at full tile size */
2801 Delay(wait_delay_value);
2804 int scroll_xx = -999, scroll_yy = -999;
2806 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2808 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2811 int fx = FX, fy = FY;
2813 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2814 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2815 local_player->jx - MIDPOSX);
2817 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2818 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2819 local_player->jy - MIDPOSY);
2821 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2822 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2825 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2828 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2835 fx += dx * TILEX / 2;
2836 fy += dy * TILEY / 2;
2838 ScrollLevel(dx, dy);
2841 /* scroll in two steps of half tile size to make things smoother */
2842 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2844 Delay(wait_delay_value);
2846 /* scroll second step to align at full tile size */
2848 Delay(wait_delay_value);
2854 void Explode(int ex, int ey, int phase, int mode)
2861 /* !!! eliminate this variable !!! */
2862 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2867 int last_phase = num_phase * delay;
2868 int half_phase = (num_phase / 2) * delay;
2869 int first_phase_after_start = EX_PHASE_START + 1;
2873 if (game.explosions_delayed)
2875 ExplodeField[ex][ey] = mode;
2879 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2881 int center_element = Feld[ex][ey];
2884 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
2888 /* --- This is only really needed (and now handled) in "Impact()". --- */
2889 /* do not explode moving elements that left the explode field in time */
2890 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2891 center_element == EL_EMPTY &&
2892 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
2896 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
2897 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2899 /* remove things displayed in background while burning dynamite */
2900 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2903 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2905 /* put moving element to center field (and let it explode there) */
2906 center_element = MovingOrBlocked2Element(ex, ey);
2907 RemoveMovingField(ex, ey);
2908 Feld[ex][ey] = center_element;
2914 last_phase = element_info[center_element].explosion_delay + 1;
2916 last_phase = element_info[center_element].explosion_delay;
2920 printf("::: %d -> %d\n", center_element, last_phase);
2924 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2926 int xx = x - ex + 1;
2927 int yy = y - ey + 1;
2932 if (!IN_LEV_FIELD(x, y) ||
2933 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
2934 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
2937 if (!IN_LEV_FIELD(x, y) ||
2938 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
2942 if (!IN_LEV_FIELD(x, y) ||
2943 ((mode != EX_TYPE_NORMAL ||
2944 center_element == EL_AMOEBA_TO_DIAMOND) &&
2945 (x != ex || y != ey)))
2949 element = Feld[x][y];
2951 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2953 element = MovingOrBlocked2Element(x, y);
2955 if (!IS_EXPLOSION_PROOF(element))
2956 RemoveMovingField(x, y);
2962 if (IS_EXPLOSION_PROOF(element))
2965 /* indestructible elements can only explode in center (but not flames) */
2966 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2967 element == EL_FLAMES)
2972 if ((IS_INDESTRUCTIBLE(element) &&
2973 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2974 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2975 element == EL_FLAMES)
2979 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2981 if (IS_ACTIVE_BOMB(element))
2983 /* re-activate things under the bomb like gate or penguin */
2984 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2991 /* save walkable background elements while explosion on same tile */
2993 if (IS_INDESTRUCTIBLE(element))
2994 Back[x][y] = element;
2996 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2997 Back[x][y] = element;
3000 /* ignite explodable elements reached by other explosion */
3001 if (element == EL_EXPLOSION)
3002 element = Store2[x][y];
3005 if (AmoebaNr[x][y] &&
3006 (element == EL_AMOEBA_FULL ||
3007 element == EL_BD_AMOEBA ||
3008 element == EL_AMOEBA_GROWING))
3010 AmoebaCnt[AmoebaNr[x][y]]--;
3011 AmoebaCnt2[AmoebaNr[x][y]]--;
3017 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3019 switch(StorePlayer[ex][ey])
3022 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3025 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3028 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3032 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3037 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3038 Store[x][y] = EL_EMPTY;
3040 if (game.emulation == EMU_SUPAPLEX)
3041 Store[x][y] = EL_EMPTY;
3044 else if (center_element == EL_MOLE)
3045 Store[x][y] = EL_EMERALD_RED;
3046 else if (center_element == EL_PENGUIN)
3047 Store[x][y] = EL_EMERALD_PURPLE;
3048 else if (center_element == EL_BUG)
3049 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3050 else if (center_element == EL_BD_BUTTERFLY)
3051 Store[x][y] = EL_BD_DIAMOND;
3052 else if (center_element == EL_SP_ELECTRON)
3053 Store[x][y] = EL_SP_INFOTRON;
3054 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3055 Store[x][y] = level.amoeba_content;
3056 else if (center_element == EL_YAMYAM)
3057 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3058 else if (IS_CUSTOM_ELEMENT(center_element) &&
3059 element_info[center_element].content[xx][yy] != EL_EMPTY)
3060 Store[x][y] = element_info[center_element].content[xx][yy];
3061 else if (element == EL_WALL_EMERALD)
3062 Store[x][y] = EL_EMERALD;
3063 else if (element == EL_WALL_DIAMOND)
3064 Store[x][y] = EL_DIAMOND;
3065 else if (element == EL_WALL_BD_DIAMOND)
3066 Store[x][y] = EL_BD_DIAMOND;
3067 else if (element == EL_WALL_EMERALD_YELLOW)
3068 Store[x][y] = EL_EMERALD_YELLOW;
3069 else if (element == EL_WALL_EMERALD_RED)
3070 Store[x][y] = EL_EMERALD_RED;
3071 else if (element == EL_WALL_EMERALD_PURPLE)
3072 Store[x][y] = EL_EMERALD_PURPLE;
3073 else if (element == EL_WALL_PEARL)
3074 Store[x][y] = EL_PEARL;
3075 else if (element == EL_WALL_CRYSTAL)
3076 Store[x][y] = EL_CRYSTAL;
3077 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3078 Store[x][y] = element_info[element].content[1][1];
3080 Store[x][y] = EL_EMPTY;
3082 if (x != ex || y != ey ||
3083 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_TYPE_BORDER)
3084 Store2[x][y] = element;
3087 if (AmoebaNr[x][y] &&
3088 (element == EL_AMOEBA_FULL ||
3089 element == EL_BD_AMOEBA ||
3090 element == EL_AMOEBA_GROWING))
3092 AmoebaCnt[AmoebaNr[x][y]]--;
3093 AmoebaCnt2[AmoebaNr[x][y]]--;
3099 MovDir[x][y] = MovPos[x][y] = 0;
3100 GfxDir[x][y] = MovDir[x][y];
3105 Feld[x][y] = EL_EXPLOSION;
3107 GfxElement[x][y] = center_element;
3109 GfxElement[x][y] = EL_UNDEFINED;
3112 ExplodePhase[x][y] = 1;
3114 ExplodeDelay[x][y] = last_phase;
3119 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3121 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3128 if (center_element == EL_YAMYAM)
3129 game.yamyam_content_nr =
3130 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3143 GfxFrame[x][y] = 0; /* restart explosion animation */
3147 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3151 last_phase = ExplodeDelay[x][y];
3154 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3158 /* activate this even in non-DEBUG version until cause for crash in
3159 getGraphicAnimationFrame() (see below) is found and eliminated */
3163 if (GfxElement[x][y] == EL_UNDEFINED)
3166 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3167 printf("Explode(): This should never happen!\n");
3170 GfxElement[x][y] = EL_EMPTY;
3176 border_element = Store2[x][y];
3177 if (IS_PLAYER(x, y))
3178 border_element = StorePlayer[x][y];
3181 printf("::: phase == %d\n", phase);
3184 if (phase == element_info[border_element].ignition_delay ||
3185 phase == last_phase)
3187 boolean border_explosion = FALSE;
3190 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3192 if (IS_PLAYER(x, y))
3195 KillHeroUnlessExplosionProtected(x, y);
3196 border_explosion = TRUE;
3199 if (phase == last_phase)
3200 printf("::: IS_PLAYER\n");
3203 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3205 Feld[x][y] = Store2[x][y];
3208 border_explosion = TRUE;
3211 if (phase == last_phase)
3212 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3215 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3217 AmoebeUmwandeln(x, y);
3219 border_explosion = TRUE;
3222 if (phase == last_phase)
3223 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3224 element_info[border_element].explosion_delay,
3225 element_info[border_element].ignition_delay,
3231 /* if an element just explodes due to another explosion (chain-reaction),
3232 do not immediately end the new explosion when it was the last frame of
3233 the explosion (as it would be done in the following "if"-statement!) */
3234 if (border_explosion && phase == last_phase)
3241 if (phase == first_phase_after_start)
3243 int element = Store2[x][y];
3245 if (element == EL_BLACK_ORB)
3247 Feld[x][y] = Store2[x][y];
3252 else if (phase == half_phase)
3254 int element = Store2[x][y];
3256 if (IS_PLAYER(x, y))
3257 KillHeroUnlessExplosionProtected(x, y);
3258 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3260 Feld[x][y] = Store2[x][y];
3264 else if (element == EL_AMOEBA_TO_DIAMOND)
3265 AmoebeUmwandeln(x, y);
3269 if (phase == last_phase)
3274 printf("::: done: phase == %d\n", phase);
3278 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3281 element = Feld[x][y] = Store[x][y];
3282 Store[x][y] = Store2[x][y] = 0;
3283 GfxElement[x][y] = EL_UNDEFINED;
3285 /* player can escape from explosions and might therefore be still alive */
3286 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3287 element <= EL_PLAYER_IS_EXPLODING_4)
3288 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3290 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3291 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3292 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3295 /* restore probably existing indestructible background element */
3296 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3297 element = Feld[x][y] = Back[x][y];
3300 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3301 GfxDir[x][y] = MV_NO_MOVING;
3302 ChangeDelay[x][y] = 0;
3303 ChangePage[x][y] = -1;
3306 InitField_WithBug2(x, y, FALSE);
3308 InitField(x, y, FALSE);
3310 /* !!! not needed !!! */
3312 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3313 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3316 if (CAN_MOVE(element))
3321 DrawLevelField(x, y);
3323 TestIfElementTouchesCustomElement(x, y);
3325 if (GFX_CRUMBLED(element))
3326 DrawLevelFieldCrumbledSandNeighbours(x, y);
3328 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3329 StorePlayer[x][y] = 0;
3331 if (ELEM_IS_PLAYER(element))
3332 RelocatePlayer(x, y, element);
3335 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3337 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3341 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3343 int stored = Store[x][y];
3344 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3345 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3349 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3351 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3355 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3359 printf("::: %d / %d [%d - %d]\n",
3360 GfxFrame[x][y], phase - delay, phase, delay);
3364 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3365 element_info[GfxElement[x][y]].token_name,
3370 DrawLevelFieldCrumbledSand(x, y);
3372 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3374 DrawLevelElement(x, y, Back[x][y]);
3375 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3377 else if (IS_WALKABLE_UNDER(Back[x][y]))
3379 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3380 DrawLevelElementThruMask(x, y, Back[x][y]);
3382 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3383 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3387 void DynaExplode(int ex, int ey)
3390 int dynabomb_element = Feld[ex][ey];
3391 int dynabomb_size = 1;
3392 boolean dynabomb_xl = FALSE;
3393 struct PlayerInfo *player;
3394 static int xy[4][2] =
3402 if (IS_ACTIVE_BOMB(dynabomb_element))
3404 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3405 dynabomb_size = player->dynabomb_size;
3406 dynabomb_xl = player->dynabomb_xl;
3407 player->dynabombs_left++;
3410 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3412 for (i = 0; i < NUM_DIRECTIONS; i++)
3414 for (j = 1; j <= dynabomb_size; j++)
3416 int x = ex + j * xy[i][0];
3417 int y = ey + j * xy[i][1];
3420 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3423 element = Feld[x][y];
3425 /* do not restart explosions of fields with active bombs */
3426 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3429 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3432 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3433 !CAN_GROW_INTO(element) && !dynabomb_xl)
3436 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3437 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3438 element != EL_SAND && !dynabomb_xl)
3445 void Bang(int x, int y)
3448 int element = MovingOrBlocked2Element(x, y);
3450 int element = Feld[x][y];
3454 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3456 if (IS_PLAYER(x, y))
3459 struct PlayerInfo *player = PLAYERINFO(x, y);
3461 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3462 player->element_nr);
3467 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3469 if (game.emulation == EMU_SUPAPLEX)
3470 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3472 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3477 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3485 case EL_BD_BUTTERFLY:
3488 case EL_DARK_YAMYAM:
3492 RaiseScoreElement(element);
3493 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3495 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3496 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3497 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3498 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3499 case EL_DYNABOMB_INCREASE_NUMBER:
3500 case EL_DYNABOMB_INCREASE_SIZE:
3501 case EL_DYNABOMB_INCREASE_POWER:
3506 case EL_LAMP_ACTIVE:
3508 case EL_AMOEBA_TO_DIAMOND:
3510 if (IS_PLAYER(x, y))
3511 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3513 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3516 if (CAN_EXPLODE_CROSS(element))
3518 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3522 else if (CAN_EXPLODE_1X1(element))
3523 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3525 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3529 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3532 void SplashAcid(int x, int y)
3535 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3536 (!IN_LEV_FIELD(x - 1, y - 2) ||
3537 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3538 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3540 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3541 (!IN_LEV_FIELD(x + 1, y - 2) ||
3542 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3543 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3545 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3547 /* input: position of element entering acid (obsolete) */
3549 int element = Feld[x][y];
3551 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3554 if (element != EL_ACID_SPLASH_LEFT &&
3555 element != EL_ACID_SPLASH_RIGHT)
3557 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3559 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3560 (!IN_LEV_FIELD(x - 1, y - 1) ||
3561 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3562 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3564 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3565 (!IN_LEV_FIELD(x + 1, y - 1) ||
3566 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3567 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3572 static void InitBeltMovement()
3574 static int belt_base_element[4] =
3576 EL_CONVEYOR_BELT_1_LEFT,
3577 EL_CONVEYOR_BELT_2_LEFT,
3578 EL_CONVEYOR_BELT_3_LEFT,
3579 EL_CONVEYOR_BELT_4_LEFT
3581 static int belt_base_active_element[4] =
3583 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3584 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3585 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3586 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3591 /* set frame order for belt animation graphic according to belt direction */
3592 for (i = 0; i < NUM_BELTS; i++)
3596 for (j = 0; j < NUM_BELT_PARTS; j++)
3598 int element = belt_base_active_element[belt_nr] + j;
3599 int graphic = el2img(element);
3601 if (game.belt_dir[i] == MV_LEFT)
3602 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3604 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3608 for (y = 0; y < lev_fieldy; y++)
3610 for (x = 0; x < lev_fieldx; x++)
3612 int element = Feld[x][y];
3614 for (i = 0; i < NUM_BELTS; i++)
3616 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3618 int e_belt_nr = getBeltNrFromBeltElement(element);
3621 if (e_belt_nr == belt_nr)
3623 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3625 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3633 static void ToggleBeltSwitch(int x, int y)
3635 static int belt_base_element[4] =
3637 EL_CONVEYOR_BELT_1_LEFT,
3638 EL_CONVEYOR_BELT_2_LEFT,
3639 EL_CONVEYOR_BELT_3_LEFT,
3640 EL_CONVEYOR_BELT_4_LEFT
3642 static int belt_base_active_element[4] =
3644 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3645 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3646 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3647 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3649 static int belt_base_switch_element[4] =
3651 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3652 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3653 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3654 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3656 static int belt_move_dir[4] =
3664 int element = Feld[x][y];
3665 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3666 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3667 int belt_dir = belt_move_dir[belt_dir_nr];
3670 if (!IS_BELT_SWITCH(element))
3673 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3674 game.belt_dir[belt_nr] = belt_dir;
3676 if (belt_dir_nr == 3)
3679 /* set frame order for belt animation graphic according to belt direction */
3680 for (i = 0; i < NUM_BELT_PARTS; i++)
3682 int element = belt_base_active_element[belt_nr] + i;
3683 int graphic = el2img(element);
3685 if (belt_dir == MV_LEFT)
3686 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3688 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3691 for (yy = 0; yy < lev_fieldy; yy++)
3693 for (xx = 0; xx < lev_fieldx; xx++)
3695 int element = Feld[xx][yy];
3697 if (IS_BELT_SWITCH(element))
3699 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3701 if (e_belt_nr == belt_nr)
3703 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3704 DrawLevelField(xx, yy);
3707 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3709 int e_belt_nr = getBeltNrFromBeltElement(element);
3711 if (e_belt_nr == belt_nr)
3713 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3715 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3716 DrawLevelField(xx, yy);
3719 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3721 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3723 if (e_belt_nr == belt_nr)
3725 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3727 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3728 DrawLevelField(xx, yy);
3735 static void ToggleSwitchgateSwitch(int x, int y)
3739 game.switchgate_pos = !game.switchgate_pos;
3741 for (yy = 0; yy < lev_fieldy; yy++)
3743 for (xx = 0; xx < lev_fieldx; xx++)
3745 int element = Feld[xx][yy];
3747 if (element == EL_SWITCHGATE_SWITCH_UP ||
3748 element == EL_SWITCHGATE_SWITCH_DOWN)
3750 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3751 DrawLevelField(xx, yy);
3753 else if (element == EL_SWITCHGATE_OPEN ||
3754 element == EL_SWITCHGATE_OPENING)
3756 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3758 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3760 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3763 else if (element == EL_SWITCHGATE_CLOSED ||
3764 element == EL_SWITCHGATE_CLOSING)
3766 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3768 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3770 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3777 static int getInvisibleActiveFromInvisibleElement(int element)
3779 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3780 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3781 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3785 static int getInvisibleFromInvisibleActiveElement(int element)
3787 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3788 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3789 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3793 static void RedrawAllLightSwitchesAndInvisibleElements()
3797 for (y = 0; y < lev_fieldy; y++)
3799 for (x = 0; x < lev_fieldx; x++)
3801 int element = Feld[x][y];
3803 if (element == EL_LIGHT_SWITCH &&
3804 game.light_time_left > 0)
3806 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3807 DrawLevelField(x, y);
3809 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3810 game.light_time_left == 0)
3812 Feld[x][y] = EL_LIGHT_SWITCH;
3813 DrawLevelField(x, y);
3815 else if (element == EL_INVISIBLE_STEELWALL ||
3816 element == EL_INVISIBLE_WALL ||
3817 element == EL_INVISIBLE_SAND)
3819 if (game.light_time_left > 0)
3820 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3822 DrawLevelField(x, y);
3824 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3825 element == EL_INVISIBLE_WALL_ACTIVE ||
3826 element == EL_INVISIBLE_SAND_ACTIVE)
3828 if (game.light_time_left == 0)
3829 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3831 DrawLevelField(x, y);
3837 static void ToggleLightSwitch(int x, int y)
3839 int element = Feld[x][y];
3841 game.light_time_left =
3842 (element == EL_LIGHT_SWITCH ?
3843 level.time_light * FRAMES_PER_SECOND : 0);
3845 RedrawAllLightSwitchesAndInvisibleElements();
3848 static void ActivateTimegateSwitch(int x, int y)
3852 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3854 for (yy = 0; yy < lev_fieldy; yy++)
3856 for (xx = 0; xx < lev_fieldx; xx++)
3858 int element = Feld[xx][yy];
3860 if (element == EL_TIMEGATE_CLOSED ||
3861 element == EL_TIMEGATE_CLOSING)
3863 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3864 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3868 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3870 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3871 DrawLevelField(xx, yy);
3878 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3881 inline static int getElementMoveStepsize(int x, int y)
3883 int element = Feld[x][y];
3884 int direction = MovDir[x][y];
3885 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3886 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3887 int horiz_move = (dx != 0);
3888 int sign = (horiz_move ? dx : dy);
3889 int step = sign * element_info[element].move_stepsize;
3891 /* special values for move stepsize for spring and things on conveyor belt */
3895 if (element == EL_SPRING)
3896 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3897 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3898 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3899 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3901 if (CAN_FALL(element) &&
3902 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3903 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3904 else if (element == EL_SPRING)
3905 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3912 void Impact(int x, int y)
3914 boolean lastline = (y == lev_fieldy-1);
3915 boolean object_hit = FALSE;
3916 boolean impact = (lastline || object_hit);
3917 int element = Feld[x][y];
3918 int smashed = EL_STEELWALL;
3920 if (!lastline) /* check if element below was hit */
3922 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3925 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3926 MovDir[x][y + 1] != MV_DOWN ||
3927 MovPos[x][y + 1] <= TILEY / 2));
3930 object_hit = !IS_FREE(x, y + 1);
3933 /* do not smash moving elements that left the smashed field in time */
3934 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3935 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3939 smashed = MovingOrBlocked2Element(x, y + 1);
3941 impact = (lastline || object_hit);
3944 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3946 SplashAcid(x, y + 1);
3950 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
3951 /* only reset graphic animation if graphic really changes after impact */
3953 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3955 ResetGfxAnimation(x, y);
3956 DrawLevelField(x, y);
3959 if (impact && CAN_EXPLODE_IMPACT(element))
3964 else if (impact && element == EL_PEARL)
3966 ResetGfxAnimation(x, y);
3968 Feld[x][y] = EL_PEARL_BREAKING;
3969 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3972 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
3974 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3979 if (impact && element == EL_AMOEBA_DROP)
3981 if (object_hit && IS_PLAYER(x, y + 1))
3982 KillHeroUnlessEnemyProtected(x, y + 1);
3983 else if (object_hit && smashed == EL_PENGUIN)
3987 Feld[x][y] = EL_AMOEBA_GROWING;
3988 Store[x][y] = EL_AMOEBA_WET;
3990 ResetRandomAnimationValue(x, y);
3995 if (object_hit) /* check which object was hit */
3997 if (CAN_PASS_MAGIC_WALL(element) &&
3998 (smashed == EL_MAGIC_WALL ||
3999 smashed == EL_BD_MAGIC_WALL))
4002 int activated_magic_wall =
4003 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4004 EL_BD_MAGIC_WALL_ACTIVE);
4006 /* activate magic wall / mill */
4007 for (yy = 0; yy < lev_fieldy; yy++)
4008 for (xx = 0; xx < lev_fieldx; xx++)
4009 if (Feld[xx][yy] == smashed)
4010 Feld[xx][yy] = activated_magic_wall;
4012 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4013 game.magic_wall_active = TRUE;
4015 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4016 SND_MAGIC_WALL_ACTIVATING :
4017 SND_BD_MAGIC_WALL_ACTIVATING));
4020 if (IS_PLAYER(x, y + 1))
4022 if (CAN_SMASH_PLAYER(element))
4024 KillHeroUnlessEnemyProtected(x, y + 1);
4028 else if (smashed == EL_PENGUIN)
4030 if (CAN_SMASH_PLAYER(element))
4036 else if (element == EL_BD_DIAMOND)
4038 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4044 else if (((element == EL_SP_INFOTRON ||
4045 element == EL_SP_ZONK) &&
4046 (smashed == EL_SP_SNIKSNAK ||
4047 smashed == EL_SP_ELECTRON ||
4048 smashed == EL_SP_DISK_ORANGE)) ||
4049 (element == EL_SP_INFOTRON &&
4050 smashed == EL_SP_DISK_YELLOW))
4056 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4062 else if (CAN_SMASH_EVERYTHING(element))
4064 if (IS_CLASSIC_ENEMY(smashed) ||
4065 CAN_EXPLODE_SMASHED(smashed))
4070 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4072 if (smashed == EL_LAMP ||
4073 smashed == EL_LAMP_ACTIVE)
4078 else if (smashed == EL_NUT)
4080 Feld[x][y + 1] = EL_NUT_BREAKING;
4081 PlayLevelSound(x, y, SND_NUT_BREAKING);
4082 RaiseScoreElement(EL_NUT);
4085 else if (smashed == EL_PEARL)
4087 ResetGfxAnimation(x, y);
4089 Feld[x][y + 1] = EL_PEARL_BREAKING;
4090 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4093 else if (smashed == EL_DIAMOND)
4095 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4096 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4099 else if (IS_BELT_SWITCH(smashed))
4101 ToggleBeltSwitch(x, y + 1);
4103 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4104 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4106 ToggleSwitchgateSwitch(x, y + 1);
4108 else if (smashed == EL_LIGHT_SWITCH ||
4109 smashed == EL_LIGHT_SWITCH_ACTIVE)
4111 ToggleLightSwitch(x, y + 1);
4116 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4119 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4122 /* !!! TEST ONLY !!! */
4123 CheckElementChangeBySide(x, y + 1, smashed, element,
4124 CE_SWITCHED, CH_SIDE_TOP);
4125 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4126 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4128 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4129 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4130 CheckElementChangeBySide(x, y + 1, smashed, element,
4131 CE_SWITCHED, CH_SIDE_TOP);
4137 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4142 /* play sound of magic wall / mill */
4144 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4145 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4147 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4148 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4149 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4150 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4155 /* play sound of object that hits the ground */
4156 if (lastline || object_hit)
4157 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4160 inline static void TurnRoundExt(int x, int y)
4172 { 0, 0 }, { 0, 0 }, { 0, 0 },
4177 int left, right, back;
4181 { MV_DOWN, MV_UP, MV_RIGHT },
4182 { MV_UP, MV_DOWN, MV_LEFT },
4184 { MV_LEFT, MV_RIGHT, MV_DOWN },
4188 { MV_RIGHT, MV_LEFT, MV_UP }
4191 int element = Feld[x][y];
4192 int move_pattern = element_info[element].move_pattern;
4194 int old_move_dir = MovDir[x][y];
4195 int left_dir = turn[old_move_dir].left;
4196 int right_dir = turn[old_move_dir].right;
4197 int back_dir = turn[old_move_dir].back;
4199 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4200 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4201 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4202 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4204 int left_x = x + left_dx, left_y = y + left_dy;
4205 int right_x = x + right_dx, right_y = y + right_dy;
4206 int move_x = x + move_dx, move_y = y + move_dy;
4210 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4212 TestIfBadThingTouchesOtherBadThing(x, y);
4214 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4215 MovDir[x][y] = right_dir;
4216 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4217 MovDir[x][y] = left_dir;
4219 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4221 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4225 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4226 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4228 TestIfBadThingTouchesOtherBadThing(x, y);
4230 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4231 MovDir[x][y] = left_dir;
4232 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4233 MovDir[x][y] = right_dir;
4235 if ((element == EL_SPACESHIP ||
4236 element == EL_SP_SNIKSNAK ||
4237 element == EL_SP_ELECTRON)
4238 && MovDir[x][y] != old_move_dir)
4240 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4244 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4246 TestIfBadThingTouchesOtherBadThing(x, y);
4248 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4249 MovDir[x][y] = left_dir;
4250 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4251 MovDir[x][y] = right_dir;
4253 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4255 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4258 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4260 TestIfBadThingTouchesOtherBadThing(x, y);
4262 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4263 MovDir[x][y] = left_dir;
4264 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4265 MovDir[x][y] = right_dir;
4267 if (MovDir[x][y] != old_move_dir)
4271 else if (element == EL_YAMYAM)
4273 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4274 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4276 if (can_turn_left && can_turn_right)
4277 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4278 else if (can_turn_left)
4279 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4280 else if (can_turn_right)
4281 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4283 MovDir[x][y] = back_dir;
4285 MovDelay[x][y] = 16 + 16 * RND(3);
4287 else if (element == EL_DARK_YAMYAM)
4289 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4291 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4294 if (can_turn_left && can_turn_right)
4295 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4296 else if (can_turn_left)
4297 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4298 else if (can_turn_right)
4299 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4301 MovDir[x][y] = back_dir;
4303 MovDelay[x][y] = 16 + 16 * RND(3);
4305 else if (element == EL_PACMAN)
4307 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4308 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
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] = 6 + RND(40);
4321 else if (element == EL_PIG)
4323 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4324 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4325 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4326 boolean should_turn_left, should_turn_right, should_move_on;
4328 int rnd = RND(rnd_value);
4330 should_turn_left = (can_turn_left &&
4332 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4333 y + back_dy + left_dy)));
4334 should_turn_right = (can_turn_right &&
4336 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4337 y + back_dy + right_dy)));
4338 should_move_on = (can_move_on &&
4341 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4342 y + move_dy + left_dy) ||
4343 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4344 y + move_dy + right_dy)));
4346 if (should_turn_left || should_turn_right || should_move_on)
4348 if (should_turn_left && should_turn_right && should_move_on)
4349 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4350 rnd < 2 * rnd_value / 3 ? right_dir :
4352 else if (should_turn_left && should_turn_right)
4353 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4354 else if (should_turn_left && should_move_on)
4355 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4356 else if (should_turn_right && should_move_on)
4357 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4358 else if (should_turn_left)
4359 MovDir[x][y] = left_dir;
4360 else if (should_turn_right)
4361 MovDir[x][y] = right_dir;
4362 else if (should_move_on)
4363 MovDir[x][y] = old_move_dir;
4365 else if (can_move_on && rnd > rnd_value / 8)
4366 MovDir[x][y] = old_move_dir;
4367 else if (can_turn_left && can_turn_right)
4368 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4369 else if (can_turn_left && rnd > rnd_value / 8)
4370 MovDir[x][y] = left_dir;
4371 else if (can_turn_right && rnd > rnd_value/8)
4372 MovDir[x][y] = right_dir;
4374 MovDir[x][y] = back_dir;
4376 xx = x + move_xy[MovDir[x][y]].x;
4377 yy = y + move_xy[MovDir[x][y]].y;
4379 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4380 MovDir[x][y] = old_move_dir;
4384 else if (element == EL_DRAGON)
4386 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4387 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4388 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4390 int rnd = RND(rnd_value);
4393 if (FrameCounter < 1 && x == 0 && y == 29)
4394 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4397 if (can_move_on && rnd > rnd_value / 8)
4398 MovDir[x][y] = old_move_dir;
4399 else if (can_turn_left && can_turn_right)
4400 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4401 else if (can_turn_left && rnd > rnd_value / 8)
4402 MovDir[x][y] = left_dir;
4403 else if (can_turn_right && rnd > rnd_value / 8)
4404 MovDir[x][y] = right_dir;
4406 MovDir[x][y] = back_dir;
4408 xx = x + move_xy[MovDir[x][y]].x;
4409 yy = y + move_xy[MovDir[x][y]].y;
4412 if (FrameCounter < 1 && x == 0 && y == 29)
4413 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4414 xx, yy, Feld[xx][yy],
4419 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4420 MovDir[x][y] = old_move_dir;
4422 if (!IS_FREE(xx, yy))
4423 MovDir[x][y] = old_move_dir;
4427 if (FrameCounter < 1 && x == 0 && y == 29)
4428 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4433 else if (element == EL_MOLE)
4435 boolean can_move_on =
4436 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4437 IS_AMOEBOID(Feld[move_x][move_y]) ||
4438 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4441 boolean can_turn_left =
4442 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4443 IS_AMOEBOID(Feld[left_x][left_y])));
4445 boolean can_turn_right =
4446 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4447 IS_AMOEBOID(Feld[right_x][right_y])));
4449 if (can_turn_left && can_turn_right)
4450 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4451 else if (can_turn_left)
4452 MovDir[x][y] = left_dir;
4454 MovDir[x][y] = right_dir;
4457 if (MovDir[x][y] != old_move_dir)
4460 else if (element == EL_BALLOON)
4462 MovDir[x][y] = game.balloon_dir;
4465 else if (element == EL_SPRING)
4468 if (MovDir[x][y] & MV_HORIZONTAL &&
4469 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4470 MovDir[x][y] = MV_NO_MOVING;
4472 if (MovDir[x][y] & MV_HORIZONTAL &&
4473 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4474 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4475 MovDir[x][y] = MV_NO_MOVING;
4480 else if (element == EL_ROBOT ||
4481 element == EL_SATELLITE ||
4482 element == EL_PENGUIN)
4484 int attr_x = -1, attr_y = -1;
4495 for (i = 0; i < MAX_PLAYERS; i++)
4497 struct PlayerInfo *player = &stored_player[i];
4498 int jx = player->jx, jy = player->jy;
4500 if (!player->active)
4504 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4513 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4514 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4515 game.engine_version < VERSION_IDENT(3,1,0,0)))
4517 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4524 if (element == EL_PENGUIN)
4527 static int xy[4][2] =
4535 for (i = 0; i < NUM_DIRECTIONS; i++)
4537 int ex = x + xy[i][0];
4538 int ey = y + xy[i][1];
4540 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4549 MovDir[x][y] = MV_NO_MOVING;
4551 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4552 else if (attr_x > x)
4553 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4555 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4556 else if (attr_y > y)
4557 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4559 if (element == EL_ROBOT)
4563 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4564 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4565 Moving2Blocked(x, y, &newx, &newy);
4567 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4568 MovDelay[x][y] = 8 + 8 * !RND(3);
4570 MovDelay[x][y] = 16;
4572 else if (element == EL_PENGUIN)
4578 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4580 boolean first_horiz = RND(2);
4581 int new_move_dir = MovDir[x][y];
4584 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4585 Moving2Blocked(x, y, &newx, &newy);
4587 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4591 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4592 Moving2Blocked(x, y, &newx, &newy);
4594 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4597 MovDir[x][y] = old_move_dir;
4601 else /* (element == EL_SATELLITE) */
4607 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4609 boolean first_horiz = RND(2);
4610 int new_move_dir = MovDir[x][y];
4613 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4614 Moving2Blocked(x, y, &newx, &newy);
4616 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4620 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4621 Moving2Blocked(x, y, &newx, &newy);
4623 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4626 MovDir[x][y] = old_move_dir;
4631 else if (move_pattern == MV_TURNING_LEFT ||
4632 move_pattern == MV_TURNING_RIGHT ||
4633 move_pattern == MV_TURNING_LEFT_RIGHT ||
4634 move_pattern == MV_TURNING_RIGHT_LEFT ||
4635 move_pattern == MV_TURNING_RANDOM ||
4636 move_pattern == MV_ALL_DIRECTIONS)
4638 boolean can_turn_left =
4639 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4640 boolean can_turn_right =
4641 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4643 if (move_pattern == MV_TURNING_LEFT)
4644 MovDir[x][y] = left_dir;
4645 else if (move_pattern == MV_TURNING_RIGHT)
4646 MovDir[x][y] = right_dir;
4647 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4648 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4649 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4650 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4651 else if (move_pattern == MV_TURNING_RANDOM)
4652 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4653 can_turn_right && !can_turn_left ? right_dir :
4654 RND(2) ? left_dir : right_dir);
4655 else if (can_turn_left && can_turn_right)
4656 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4657 else if (can_turn_left)
4658 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4659 else if (can_turn_right)
4660 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4662 MovDir[x][y] = back_dir;
4664 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4666 else if (move_pattern == MV_HORIZONTAL ||
4667 move_pattern == MV_VERTICAL)
4669 if (move_pattern & old_move_dir)
4670 MovDir[x][y] = back_dir;
4671 else if (move_pattern == MV_HORIZONTAL)
4672 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4673 else if (move_pattern == MV_VERTICAL)
4674 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4676 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4678 else if (move_pattern & MV_ANY_DIRECTION)
4680 MovDir[x][y] = move_pattern;
4681 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4683 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4685 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4686 MovDir[x][y] = left_dir;
4687 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4688 MovDir[x][y] = right_dir;
4690 if (MovDir[x][y] != old_move_dir)
4691 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4693 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4695 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4696 MovDir[x][y] = right_dir;
4697 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4698 MovDir[x][y] = left_dir;
4700 if (MovDir[x][y] != old_move_dir)
4701 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4703 else if (move_pattern == MV_TOWARDS_PLAYER ||
4704 move_pattern == MV_AWAY_FROM_PLAYER)
4706 int attr_x = -1, attr_y = -1;
4708 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4719 for (i = 0; i < MAX_PLAYERS; i++)
4721 struct PlayerInfo *player = &stored_player[i];
4722 int jx = player->jx, jy = player->jy;
4724 if (!player->active)
4728 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4736 MovDir[x][y] = MV_NO_MOVING;
4738 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4739 else if (attr_x > x)
4740 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4742 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4743 else if (attr_y > y)
4744 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4746 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4748 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4750 boolean first_horiz = RND(2);
4751 int new_move_dir = MovDir[x][y];
4754 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4755 Moving2Blocked(x, y, &newx, &newy);
4757 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4761 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4762 Moving2Blocked(x, y, &newx, &newy);
4764 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4767 MovDir[x][y] = old_move_dir;
4770 else if (move_pattern == MV_WHEN_PUSHED ||
4771 move_pattern == MV_WHEN_DROPPED)
4773 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4774 MovDir[x][y] = MV_NO_MOVING;
4778 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4780 static int test_xy[7][2] =
4790 static int test_dir[7] =
4800 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4801 int move_preference = -1000000; /* start with very low preference */
4802 int new_move_dir = MV_NO_MOVING;
4803 int start_test = RND(4);
4806 for (i = 0; i < NUM_DIRECTIONS; i++)
4808 int move_dir = test_dir[start_test + i];
4809 int move_dir_preference;
4811 xx = x + test_xy[start_test + i][0];
4812 yy = y + test_xy[start_test + i][1];
4814 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4815 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4817 new_move_dir = move_dir;
4822 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4825 move_dir_preference = -1 * RunnerVisit[xx][yy];
4826 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4827 move_dir_preference = PlayerVisit[xx][yy];
4829 if (move_dir_preference > move_preference)
4831 /* prefer field that has not been visited for the longest time */
4832 move_preference = move_dir_preference;
4833 new_move_dir = move_dir;
4835 else if (move_dir_preference == move_preference &&
4836 move_dir == old_move_dir)
4838 /* prefer last direction when all directions are preferred equally */
4839 move_preference = move_dir_preference;
4840 new_move_dir = move_dir;
4844 MovDir[x][y] = new_move_dir;
4845 if (old_move_dir != new_move_dir)
4850 static void TurnRound(int x, int y)
4852 int direction = MovDir[x][y];
4855 GfxDir[x][y] = MovDir[x][y];
4861 GfxDir[x][y] = MovDir[x][y];
4864 if (direction != MovDir[x][y])
4869 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4872 GfxAction[x][y] = ACTION_WAITING;
4876 static boolean JustBeingPushed(int x, int y)
4880 for (i = 0; i < MAX_PLAYERS; i++)
4882 struct PlayerInfo *player = &stored_player[i];
4884 if (player->active && player->is_pushing && player->MovPos)
4886 int next_jx = player->jx + (player->jx - player->last_jx);
4887 int next_jy = player->jy + (player->jy - player->last_jy);
4889 if (x == next_jx && y == next_jy)
4897 void StartMoving(int x, int y)
4900 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4902 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4903 int element = Feld[x][y];
4909 if (MovDelay[x][y] == 0)
4910 GfxAction[x][y] = ACTION_DEFAULT;
4912 /* !!! this should be handled more generic (not only for mole) !!! */
4913 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4914 GfxAction[x][y] = ACTION_DEFAULT;
4917 if (CAN_FALL(element) && y < lev_fieldy - 1)
4919 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4920 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4921 if (JustBeingPushed(x, y))
4924 if (element == EL_QUICKSAND_FULL)
4926 if (IS_FREE(x, y + 1))
4928 InitMovingField(x, y, MV_DOWN);
4929 started_moving = TRUE;
4931 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4932 Store[x][y] = EL_ROCK;
4934 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4936 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4939 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4941 if (!MovDelay[x][y])
4942 MovDelay[x][y] = TILEY + 1;
4951 Feld[x][y] = EL_QUICKSAND_EMPTY;
4952 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4953 Store[x][y + 1] = Store[x][y];
4956 PlayLevelSoundAction(x, y, ACTION_FILLING);
4958 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4962 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4963 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4965 InitMovingField(x, y, MV_DOWN);
4966 started_moving = TRUE;
4968 Feld[x][y] = EL_QUICKSAND_FILLING;
4969 Store[x][y] = element;
4971 PlayLevelSoundAction(x, y, ACTION_FILLING);
4973 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4976 else if (element == EL_MAGIC_WALL_FULL)
4978 if (IS_FREE(x, y + 1))
4980 InitMovingField(x, y, MV_DOWN);
4981 started_moving = TRUE;
4983 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4984 Store[x][y] = EL_CHANGED(Store[x][y]);
4986 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4988 if (!MovDelay[x][y])
4989 MovDelay[x][y] = TILEY/4 + 1;
4998 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4999 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5000 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5004 else if (element == EL_BD_MAGIC_WALL_FULL)
5006 if (IS_FREE(x, y + 1))
5008 InitMovingField(x, y, MV_DOWN);
5009 started_moving = TRUE;
5011 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5012 Store[x][y] = EL_CHANGED2(Store[x][y]);
5014 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5016 if (!MovDelay[x][y])
5017 MovDelay[x][y] = TILEY/4 + 1;
5026 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5027 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5028 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5032 else if (CAN_PASS_MAGIC_WALL(element) &&
5033 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5034 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5036 InitMovingField(x, y, MV_DOWN);
5037 started_moving = TRUE;
5040 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5041 EL_BD_MAGIC_WALL_FILLING);
5042 Store[x][y] = element;
5045 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5047 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5050 SplashAcid(x, y + 1);
5052 InitMovingField(x, y, MV_DOWN);
5053 started_moving = TRUE;
5055 Store[x][y] = EL_ACID;
5057 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5058 GfxAction[x][y + 1] = ACTION_ACTIVE;
5062 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5063 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5065 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5066 CAN_SMASH(element) && WasJustFalling[x][y] &&
5067 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5069 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5070 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5071 (Feld[x][y + 1] == EL_BLOCKED)))
5075 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5076 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5077 WasJustMoving[x][y] && !Pushed[x][y + 1])
5079 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5080 WasJustMoving[x][y])
5085 /* this is needed for a special case not covered by calling "Impact()"
5086 from "ContinueMoving()": if an element moves to a tile directly below
5087 another element which was just falling on that tile (which was empty
5088 in the previous frame), the falling element above would just stop
5089 instead of smashing the element below (in previous version, the above
5090 element was just checked for "moving" instead of "falling", resulting
5091 in incorrect smashes caused by horizontal movement of the above
5092 element; also, the case of the player being the element to smash was
5093 simply not covered here... :-/ ) */
5096 WasJustMoving[x][y] = 0;
5097 WasJustFalling[x][y] = 0;
5100 CheckCollision[x][y] = 0;
5104 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5106 if (MovDir[x][y] == MV_NO_MOVING)
5108 InitMovingField(x, y, MV_DOWN);
5109 started_moving = TRUE;
5112 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5114 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5115 MovDir[x][y] = MV_DOWN;
5117 InitMovingField(x, y, MV_DOWN);
5118 started_moving = TRUE;
5120 else if (element == EL_AMOEBA_DROP)
5122 Feld[x][y] = EL_AMOEBA_GROWING;
5123 Store[x][y] = EL_AMOEBA_WET;
5125 /* Store[x][y + 1] must be zero, because:
5126 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5129 #if OLD_GAME_BEHAVIOUR
5130 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5132 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5133 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5134 element != EL_DX_SUPABOMB)
5137 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5138 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5139 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5140 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5143 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5144 (IS_FREE(x - 1, y + 1) ||
5145 Feld[x - 1][y + 1] == EL_ACID));
5146 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5147 (IS_FREE(x + 1, y + 1) ||
5148 Feld[x + 1][y + 1] == EL_ACID));
5149 boolean can_fall_any = (can_fall_left || can_fall_right);
5150 boolean can_fall_both = (can_fall_left && can_fall_right);
5152 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5154 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5156 if (slippery_type == SLIPPERY_ONLY_LEFT)
5157 can_fall_right = FALSE;
5158 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5159 can_fall_left = FALSE;
5160 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5161 can_fall_right = FALSE;
5162 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5163 can_fall_left = FALSE;
5165 can_fall_any = (can_fall_left || can_fall_right);
5166 can_fall_both = (can_fall_left && can_fall_right);
5171 if (can_fall_both &&
5172 (game.emulation != EMU_BOULDERDASH &&
5173 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5174 can_fall_left = !(can_fall_right = RND(2));
5176 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5177 started_moving = TRUE;
5181 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5183 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5186 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5187 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5188 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5189 int belt_dir = game.belt_dir[belt_nr];
5191 if ((belt_dir == MV_LEFT && left_is_free) ||
5192 (belt_dir == MV_RIGHT && right_is_free))
5195 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5198 InitMovingField(x, y, belt_dir);
5199 started_moving = TRUE;
5202 Pushed[x][y] = TRUE;
5203 Pushed[nextx][y] = TRUE;
5206 GfxAction[x][y] = ACTION_DEFAULT;
5210 MovDir[x][y] = 0; /* if element was moving, stop it */
5215 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5217 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5219 if (CAN_MOVE(element) && !started_moving)
5222 int move_pattern = element_info[element].move_pattern;
5226 if (MovDir[x][y] == MV_NO_MOVING)
5228 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5229 x, y, element, element_info[element].token_name);
5230 printf("StartMoving(): This should never happen!\n");
5234 Moving2Blocked(x, y, &newx, &newy);
5237 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5240 if ((element == EL_SATELLITE ||
5241 element == EL_BALLOON ||
5242 element == EL_SPRING)
5243 && JustBeingPushed(x, y))
5250 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5251 CheckCollision[x][y] && IN_LEV_FIELD_AND_NOT_FREE(newx, newy))
5253 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5254 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5255 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5259 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5260 element, element_info[element].token_name,
5261 WasJustMoving[x][y],
5262 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5263 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5264 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5265 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5269 WasJustMoving[x][y] = 0;
5272 CheckCollision[x][y] = 0;
5274 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5277 if (Feld[x][y] != element) /* element has changed */
5279 element = Feld[x][y];
5280 move_pattern = element_info[element].move_pattern;
5282 if (!CAN_MOVE(element))
5286 if (Feld[x][y] != element) /* element has changed */
5294 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5295 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5297 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5299 Moving2Blocked(x, y, &newx, &newy);
5300 if (Feld[newx][newy] == EL_BLOCKED)
5301 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5307 if (FrameCounter < 1 && x == 0 && y == 29)
5308 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5311 if (!MovDelay[x][y]) /* start new movement phase */
5313 /* all objects that can change their move direction after each step
5314 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5316 if (element != EL_YAMYAM &&
5317 element != EL_DARK_YAMYAM &&
5318 element != EL_PACMAN &&
5319 !(move_pattern & MV_ANY_DIRECTION) &&
5320 move_pattern != MV_TURNING_LEFT &&
5321 move_pattern != MV_TURNING_RIGHT &&
5322 move_pattern != MV_TURNING_LEFT_RIGHT &&
5323 move_pattern != MV_TURNING_RIGHT_LEFT &&
5324 move_pattern != MV_TURNING_RANDOM)
5329 if (FrameCounter < 1 && x == 0 && y == 29)
5330 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5333 if (MovDelay[x][y] && (element == EL_BUG ||
5334 element == EL_SPACESHIP ||
5335 element == EL_SP_SNIKSNAK ||
5336 element == EL_SP_ELECTRON ||
5337 element == EL_MOLE))
5338 DrawLevelField(x, y);
5342 if (MovDelay[x][y]) /* wait some time before next movement */
5347 if (element == EL_YAMYAM)
5350 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5351 DrawLevelElementAnimation(x, y, element);
5355 if (MovDelay[x][y]) /* element still has to wait some time */
5358 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5359 ResetGfxAnimation(x, y);
5363 if (GfxAction[x][y] != ACTION_WAITING)
5364 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5366 GfxAction[x][y] = ACTION_WAITING;
5370 if (element == EL_ROBOT ||
5372 element == EL_PACMAN ||
5374 element == EL_YAMYAM ||
5375 element == EL_DARK_YAMYAM)
5378 DrawLevelElementAnimation(x, y, element);
5380 DrawLevelElementAnimationIfNeeded(x, y, element);
5382 PlayLevelSoundAction(x, y, ACTION_WAITING);
5384 else if (element == EL_SP_ELECTRON)
5385 DrawLevelElementAnimationIfNeeded(x, y, element);
5386 else if (element == EL_DRAGON)
5389 int dir = MovDir[x][y];
5390 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5391 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5392 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5393 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5394 dir == MV_UP ? IMG_FLAMES_1_UP :
5395 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5396 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5399 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5402 GfxAction[x][y] = ACTION_ATTACKING;
5404 if (IS_PLAYER(x, y))
5405 DrawPlayerField(x, y);
5407 DrawLevelField(x, y);
5409 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5411 for (i = 1; i <= 3; i++)
5413 int xx = x + i * dx;
5414 int yy = y + i * dy;
5415 int sx = SCREENX(xx);
5416 int sy = SCREENY(yy);
5417 int flame_graphic = graphic + (i - 1);
5419 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5424 int flamed = MovingOrBlocked2Element(xx, yy);
5428 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5430 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5431 RemoveMovingField(xx, yy);
5433 RemoveField(xx, yy);
5435 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5438 RemoveMovingField(xx, yy);
5442 if (ChangeDelay[xx][yy])
5443 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5444 Feld[xx][yy] == EL_BLOCKED));
5448 ChangeDelay[xx][yy] = 0;
5450 Feld[xx][yy] = EL_FLAMES;
5451 if (IN_SCR_FIELD(sx, sy))
5453 DrawLevelFieldCrumbledSand(xx, yy);
5454 DrawGraphic(sx, sy, flame_graphic, frame);
5459 if (Feld[xx][yy] == EL_FLAMES)
5460 Feld[xx][yy] = EL_EMPTY;
5461 DrawLevelField(xx, yy);
5466 if (MovDelay[x][y]) /* element still has to wait some time */
5468 PlayLevelSoundAction(x, y, ACTION_WAITING);
5474 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5475 for all other elements GfxAction will be set by InitMovingField() */
5476 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5477 GfxAction[x][y] = ACTION_MOVING;
5481 /* now make next step */
5483 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5485 if (DONT_COLLIDE_WITH(element) &&
5486 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5487 !PLAYER_ENEMY_PROTECTED(newx, newy))
5490 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5494 /* player killed by element which is deadly when colliding with */
5496 KillHero(PLAYERINFO(newx, newy));
5503 else if (CAN_MOVE_INTO_ACID(element) &&
5504 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5505 (MovDir[x][y] == MV_DOWN ||
5506 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5508 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5509 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5513 else if ((element == EL_PENGUIN ||
5514 element == EL_ROBOT ||
5515 element == EL_SATELLITE ||
5516 element == EL_BALLOON ||
5517 IS_CUSTOM_ELEMENT(element)) &&
5518 IN_LEV_FIELD(newx, newy) &&
5519 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5522 SplashAcid(newx, newy);
5523 Store[x][y] = EL_ACID;
5525 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5527 if (Feld[newx][newy] == EL_EXIT_OPEN)
5531 DrawLevelField(x, y);
5533 Feld[x][y] = EL_EMPTY;
5534 DrawLevelField(x, y);
5537 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5538 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5539 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5541 local_player->friends_still_needed--;
5542 if (!local_player->friends_still_needed &&
5543 !local_player->GameOver && AllPlayersGone)
5544 local_player->LevelSolved = local_player->GameOver = TRUE;
5548 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5550 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5551 DrawLevelField(newx, newy);
5553 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5555 else if (!IS_FREE(newx, newy))
5557 GfxAction[x][y] = ACTION_WAITING;
5559 if (IS_PLAYER(x, y))
5560 DrawPlayerField(x, y);
5562 DrawLevelField(x, y);
5567 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5569 if (IS_FOOD_PIG(Feld[newx][newy]))
5571 if (IS_MOVING(newx, newy))
5572 RemoveMovingField(newx, newy);
5575 Feld[newx][newy] = EL_EMPTY;
5576 DrawLevelField(newx, newy);
5579 PlayLevelSound(x, y, SND_PIG_DIGGING);
5581 else if (!IS_FREE(newx, newy))
5583 if (IS_PLAYER(x, y))
5584 DrawPlayerField(x, y);
5586 DrawLevelField(x, y);
5595 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5598 else if (IS_CUSTOM_ELEMENT(element) &&
5599 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5603 !IS_FREE(newx, newy)
5608 int new_element = Feld[newx][newy];
5611 printf("::: '%s' digs '%s' [%d]\n",
5612 element_info[element].token_name,
5613 element_info[Feld[newx][newy]].token_name,
5614 StorePlayer[newx][newy]);
5617 if (!IS_FREE(newx, newy))
5619 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5620 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5623 /* no element can dig solid indestructible elements */
5624 if (IS_INDESTRUCTIBLE(new_element) &&
5625 !IS_DIGGABLE(new_element) &&
5626 !IS_COLLECTIBLE(new_element))
5629 if (AmoebaNr[newx][newy] &&
5630 (new_element == EL_AMOEBA_FULL ||
5631 new_element == EL_BD_AMOEBA ||
5632 new_element == EL_AMOEBA_GROWING))
5634 AmoebaCnt[AmoebaNr[newx][newy]]--;
5635 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5638 if (IS_MOVING(newx, newy))
5639 RemoveMovingField(newx, newy);
5642 RemoveField(newx, newy);
5643 DrawLevelField(newx, newy);
5646 /* if digged element was about to explode, prevent the explosion */
5647 ExplodeField[newx][newy] = EX_TYPE_NONE;
5649 PlayLevelSoundAction(x, y, action);
5654 Store[newx][newy] = EL_EMPTY;
5655 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5656 Store[newx][newy] = element_info[element].move_leave_element;
5658 Store[newx][newy] = EL_EMPTY;
5659 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5660 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5661 Store[newx][newy] = element_info[element].move_leave_element;
5664 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5665 element_info[element].can_leave_element = TRUE;
5668 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5670 RunnerVisit[x][y] = FrameCounter;
5671 PlayerVisit[x][y] /= 8; /* expire player visit path */
5677 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5679 if (!IS_FREE(newx, newy))
5681 if (IS_PLAYER(x, y))
5682 DrawPlayerField(x, y);
5684 DrawLevelField(x, y);
5690 boolean wanna_flame = !RND(10);
5691 int dx = newx - x, dy = newy - y;
5692 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5693 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5694 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5695 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5696 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5697 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5700 IS_CLASSIC_ENEMY(element1) ||
5701 IS_CLASSIC_ENEMY(element2)) &&
5702 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5703 element1 != EL_FLAMES && element2 != EL_FLAMES)
5706 ResetGfxAnimation(x, y);
5707 GfxAction[x][y] = ACTION_ATTACKING;
5710 if (IS_PLAYER(x, y))
5711 DrawPlayerField(x, y);
5713 DrawLevelField(x, y);
5715 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5717 MovDelay[x][y] = 50;
5721 RemoveField(newx, newy);
5723 Feld[newx][newy] = EL_FLAMES;
5724 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5727 RemoveField(newx1, newy1);
5729 Feld[newx1][newy1] = EL_FLAMES;
5731 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5734 RemoveField(newx2, newy2);
5736 Feld[newx2][newy2] = EL_FLAMES;
5743 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5744 Feld[newx][newy] == EL_DIAMOND)
5746 if (IS_MOVING(newx, newy))
5747 RemoveMovingField(newx, newy);
5750 Feld[newx][newy] = EL_EMPTY;
5751 DrawLevelField(newx, newy);
5754 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5756 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5757 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5759 if (AmoebaNr[newx][newy])
5761 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5762 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5763 Feld[newx][newy] == EL_BD_AMOEBA)
5764 AmoebaCnt[AmoebaNr[newx][newy]]--;
5769 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5771 if (IS_MOVING(newx, newy))
5774 RemoveMovingField(newx, newy);
5778 Feld[newx][newy] = EL_EMPTY;
5779 DrawLevelField(newx, newy);
5782 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5784 else if ((element == EL_PACMAN || element == EL_MOLE)
5785 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5787 if (AmoebaNr[newx][newy])
5789 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5790 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5791 Feld[newx][newy] == EL_BD_AMOEBA)
5792 AmoebaCnt[AmoebaNr[newx][newy]]--;
5795 if (element == EL_MOLE)
5797 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5798 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5800 ResetGfxAnimation(x, y);
5801 GfxAction[x][y] = ACTION_DIGGING;
5802 DrawLevelField(x, y);
5804 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5806 return; /* wait for shrinking amoeba */
5808 else /* element == EL_PACMAN */
5810 Feld[newx][newy] = EL_EMPTY;
5811 DrawLevelField(newx, newy);
5812 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5815 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5816 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5817 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5819 /* wait for shrinking amoeba to completely disappear */
5822 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5824 /* object was running against a wall */
5828 if (move_pattern & MV_ANY_DIRECTION &&
5829 move_pattern == MovDir[x][y])
5831 int blocking_element =
5832 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5835 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
5836 element_info[element].token_name,
5837 element_info[blocking_element].token_name,
5841 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5844 element = Feld[x][y]; /* element might have changed */
5848 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5849 DrawLevelElementAnimation(x, y, element);
5851 if (element == EL_BUG ||
5852 element == EL_SPACESHIP ||
5853 element == EL_SP_SNIKSNAK)
5854 DrawLevelField(x, y);
5855 else if (element == EL_MOLE)
5856 DrawLevelField(x, y);
5857 else if (element == EL_BD_BUTTERFLY ||
5858 element == EL_BD_FIREFLY)
5859 DrawLevelElementAnimationIfNeeded(x, y, element);
5860 else if (element == EL_SATELLITE)
5861 DrawLevelElementAnimationIfNeeded(x, y, element);
5862 else if (element == EL_SP_ELECTRON)
5863 DrawLevelElementAnimationIfNeeded(x, y, element);
5866 if (DONT_TOUCH(element))
5867 TestIfBadThingTouchesHero(x, y);
5870 PlayLevelSoundAction(x, y, ACTION_WAITING);
5876 InitMovingField(x, y, MovDir[x][y]);
5878 PlayLevelSoundAction(x, y, ACTION_MOVING);
5882 ContinueMoving(x, y);
5885 void ContinueMoving(int x, int y)
5887 int element = Feld[x][y];
5888 int stored = Store[x][y];
5889 struct ElementInfo *ei = &element_info[element];
5890 int direction = MovDir[x][y];
5891 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5892 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5893 int newx = x + dx, newy = y + dy;
5895 int nextx = newx + dx, nexty = newy + dy;
5898 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5899 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5901 boolean pushed_by_player = Pushed[x][y];
5904 MovPos[x][y] += getElementMoveStepsize(x, y);
5907 if (pushed_by_player && IS_PLAYER(x, y))
5909 /* special case: moving object pushed by player */
5910 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5913 if (pushed_by_player) /* special case: moving object pushed by player */
5914 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5917 if (ABS(MovPos[x][y]) < TILEX)
5919 DrawLevelField(x, y);
5921 return; /* element is still moving */
5924 /* element reached destination field */
5926 Feld[x][y] = EL_EMPTY;
5927 Feld[newx][newy] = element;
5928 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5930 if (element == EL_MOLE)
5932 Feld[x][y] = EL_SAND;
5934 DrawLevelFieldCrumbledSandNeighbours(x, y);
5936 else if (element == EL_QUICKSAND_FILLING)
5938 element = Feld[newx][newy] = get_next_element(element);
5939 Store[newx][newy] = Store[x][y];
5941 else if (element == EL_QUICKSAND_EMPTYING)
5943 Feld[x][y] = get_next_element(element);
5944 element = Feld[newx][newy] = Store[x][y];
5946 else if (element == EL_MAGIC_WALL_FILLING)
5948 element = Feld[newx][newy] = get_next_element(element);
5949 if (!game.magic_wall_active)
5950 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5951 Store[newx][newy] = Store[x][y];
5953 else if (element == EL_MAGIC_WALL_EMPTYING)
5955 Feld[x][y] = get_next_element(element);
5956 if (!game.magic_wall_active)
5957 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5958 element = Feld[newx][newy] = Store[x][y];
5960 else if (element == EL_BD_MAGIC_WALL_FILLING)
5962 element = Feld[newx][newy] = get_next_element(element);
5963 if (!game.magic_wall_active)
5964 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5965 Store[newx][newy] = Store[x][y];
5967 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5969 Feld[x][y] = get_next_element(element);
5970 if (!game.magic_wall_active)
5971 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5972 element = Feld[newx][newy] = Store[x][y];
5974 else if (element == EL_AMOEBA_DROPPING)
5976 Feld[x][y] = get_next_element(element);
5977 element = Feld[newx][newy] = Store[x][y];
5979 else if (element == EL_SOKOBAN_OBJECT)
5982 Feld[x][y] = Back[x][y];
5984 if (Back[newx][newy])
5985 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5987 Back[x][y] = Back[newx][newy] = 0;
5989 else if (Store[x][y] == EL_ACID)
5991 element = Feld[newx][newy] = EL_ACID;
5994 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5995 ei->move_leave_element != EL_EMPTY &&
5996 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5997 Store[x][y] != EL_EMPTY))
5999 /* some elements can leave other elements behind after moving */
6001 Feld[x][y] = ei->move_leave_element;
6002 InitField(x, y, FALSE);
6004 if (GFX_CRUMBLED(Feld[x][y]))
6005 DrawLevelFieldCrumbledSandNeighbours(x, y);
6009 Store[x][y] = EL_EMPTY;
6010 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6011 MovDelay[newx][newy] = 0;
6013 if (CAN_CHANGE(element))
6015 /* copy element change control values to new field */
6016 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6017 ChangePage[newx][newy] = ChangePage[x][y];
6018 Changed[newx][newy] = Changed[x][y];
6019 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6022 ChangeDelay[x][y] = 0;
6023 ChangePage[x][y] = -1;
6024 Changed[x][y] = CE_BITMASK_DEFAULT;
6025 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6027 /* copy animation control values to new field */
6028 GfxFrame[newx][newy] = GfxFrame[x][y];
6029 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6030 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6031 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6033 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6035 ResetGfxAnimation(x, y); /* reset animation values for old field */
6038 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6039 ei->move_leave_element != EL_EMPTY &&
6040 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6041 stored != EL_EMPTY))
6043 /* some elements can leave other elements behind after moving */
6045 Feld[x][y] = ei->move_leave_element;
6046 InitField(x, y, FALSE);
6048 if (GFX_CRUMBLED(Feld[x][y]))
6049 DrawLevelFieldCrumbledSandNeighbours(x, y);
6054 /* some elements can leave other elements behind after moving */
6055 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6056 ei->move_leave_element != EL_EMPTY &&
6057 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6058 ei->can_leave_element_last))
6060 Feld[x][y] = ei->move_leave_element;
6061 InitField(x, y, FALSE);
6063 if (GFX_CRUMBLED(Feld[x][y]))
6064 DrawLevelFieldCrumbledSandNeighbours(x, y);
6067 ei->can_leave_element_last = ei->can_leave_element;
6068 ei->can_leave_element = FALSE;
6072 /* 2.1.1 (does not work correctly for spring) */
6073 if (!CAN_MOVE(element))
6074 MovDir[newx][newy] = 0;
6078 /* (does not work for falling objects that slide horizontally) */
6079 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6080 MovDir[newx][newy] = 0;
6083 if (!CAN_MOVE(element) ||
6084 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6085 MovDir[newx][newy] = 0;
6089 if (!CAN_MOVE(element) ||
6090 (CAN_FALL(element) && direction == MV_DOWN))
6091 GfxDir[x][y] = MovDir[newx][newy] = 0;
6093 if (!CAN_MOVE(element) ||
6094 (CAN_FALL(element) && direction == MV_DOWN &&
6095 (element == EL_SPRING ||
6096 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6097 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6098 GfxDir[x][y] = MovDir[newx][newy] = 0;
6104 DrawLevelField(x, y);
6105 DrawLevelField(newx, newy);
6107 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6109 /* prevent pushed element from moving on in pushed direction */
6110 if (pushed_by_player && CAN_MOVE(element) &&
6111 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6112 !(element_info[element].move_pattern & direction))
6113 TurnRound(newx, newy);
6116 /* prevent elements on conveyor belt from moving on in last direction */
6117 if (pushed_by_conveyor && CAN_FALL(element) &&
6118 direction & MV_HORIZONTAL)
6120 if (CAN_MOVE(element))
6121 InitMovDir(newx, newy);
6123 MovDir[newx][newy] = 0;
6127 if (!pushed_by_player)
6129 int nextx = newx + dx, nexty = newy + dy;
6130 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6132 WasJustMoving[newx][newy] = 3;
6134 if (CAN_FALL(element) && direction == MV_DOWN)
6135 WasJustFalling[newx][newy] = 3;
6137 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6138 CheckCollision[newx][newy] = 2;
6141 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6143 TestIfBadThingTouchesHero(newx, newy);
6144 TestIfBadThingTouchesFriend(newx, newy);
6146 if (!IS_CUSTOM_ELEMENT(element))
6147 TestIfBadThingTouchesOtherBadThing(newx, newy);
6149 else if (element == EL_PENGUIN)
6150 TestIfFriendTouchesBadThing(newx, newy);
6152 if (CAN_FALL(element) && direction == MV_DOWN &&
6153 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6157 if (pushed_by_player)
6159 static int trigger_sides[4] =
6161 CH_SIDE_RIGHT, /* moving left */
6162 CH_SIDE_LEFT, /* moving right */
6163 CH_SIDE_BOTTOM, /* moving up */
6164 CH_SIDE_TOP, /* moving down */
6166 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6167 struct PlayerInfo *player = PLAYERINFO(x, y);
6169 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6170 player->index_bit, dig_side);
6171 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6172 player->index_bit, dig_side);
6177 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6181 if (ChangePage[newx][newy] != -1) /* delayed change */
6182 ChangeElement(newx, newy, ChangePage[newx][newy]);
6187 TestIfElementHitsCustomElement(newx, newy, direction);
6191 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6193 int hitting_element = Feld[newx][newy];
6195 /* !!! fix side (direction) orientation here and elsewhere !!! */
6196 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6200 if (IN_LEV_FIELD(nextx, nexty))
6202 int opposite_direction = MV_DIR_OPPOSITE(direction);
6203 int hitting_side = direction;
6204 int touched_side = opposite_direction;
6205 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6206 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6207 MovDir[nextx][nexty] != direction ||
6208 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6214 CheckElementChangeBySide(nextx, nexty, touched_element,
6215 CE_HIT_BY_SOMETHING, opposite_direction);
6217 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6218 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6220 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6222 struct ElementChangeInfo *change =
6223 &element_info[hitting_element].change_page[i];
6225 if (change->can_change &&
6226 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6227 change->trigger_side & touched_side &&
6228 change->trigger_element == touched_element)
6230 CheckElementChangeByPage(newx, newy, hitting_element,
6231 touched_element, CE_OTHER_IS_HITTING,i);
6237 if (IS_CUSTOM_ELEMENT(touched_element) &&
6238 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6240 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6242 struct ElementChangeInfo *change =
6243 &element_info[touched_element].change_page[i];
6245 if (change->can_change &&
6246 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6247 change->trigger_side & hitting_side &&
6248 change->trigger_element == hitting_element)
6250 CheckElementChangeByPage(nextx, nexty, touched_element,
6251 hitting_element, CE_OTHER_GETS_HIT, i);
6262 TestIfPlayerTouchesCustomElement(newx, newy);
6263 TestIfElementTouchesCustomElement(newx, newy);
6266 int AmoebeNachbarNr(int ax, int ay)
6269 int element = Feld[ax][ay];
6271 static int xy[4][2] =
6279 for (i = 0; i < NUM_DIRECTIONS; i++)
6281 int x = ax + xy[i][0];
6282 int y = ay + xy[i][1];
6284 if (!IN_LEV_FIELD(x, y))
6287 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6288 group_nr = AmoebaNr[x][y];
6294 void AmoebenVereinigen(int ax, int ay)
6296 int i, x, y, xx, yy;
6297 int new_group_nr = AmoebaNr[ax][ay];
6298 static int xy[4][2] =
6306 if (new_group_nr == 0)
6309 for (i = 0; i < NUM_DIRECTIONS; i++)
6314 if (!IN_LEV_FIELD(x, y))
6317 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6318 Feld[x][y] == EL_BD_AMOEBA ||
6319 Feld[x][y] == EL_AMOEBA_DEAD) &&
6320 AmoebaNr[x][y] != new_group_nr)
6322 int old_group_nr = AmoebaNr[x][y];
6324 if (old_group_nr == 0)
6327 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6328 AmoebaCnt[old_group_nr] = 0;
6329 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6330 AmoebaCnt2[old_group_nr] = 0;
6332 for (yy = 0; yy < lev_fieldy; yy++)
6334 for (xx = 0; xx < lev_fieldx; xx++)
6336 if (AmoebaNr[xx][yy] == old_group_nr)
6337 AmoebaNr[xx][yy] = new_group_nr;
6344 void AmoebeUmwandeln(int ax, int ay)
6348 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6350 int group_nr = AmoebaNr[ax][ay];
6355 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6356 printf("AmoebeUmwandeln(): This should never happen!\n");
6361 for (y = 0; y < lev_fieldy; y++)
6363 for (x = 0; x < lev_fieldx; x++)
6365 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6368 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6372 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6373 SND_AMOEBA_TURNING_TO_GEM :
6374 SND_AMOEBA_TURNING_TO_ROCK));
6379 static int xy[4][2] =
6387 for (i = 0; i < NUM_DIRECTIONS; i++)
6392 if (!IN_LEV_FIELD(x, y))
6395 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6397 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6398 SND_AMOEBA_TURNING_TO_GEM :
6399 SND_AMOEBA_TURNING_TO_ROCK));
6406 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6409 int group_nr = AmoebaNr[ax][ay];
6410 boolean done = FALSE;
6415 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6416 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6421 for (y = 0; y < lev_fieldy; y++)
6423 for (x = 0; x < lev_fieldx; x++)
6425 if (AmoebaNr[x][y] == group_nr &&
6426 (Feld[x][y] == EL_AMOEBA_DEAD ||
6427 Feld[x][y] == EL_BD_AMOEBA ||
6428 Feld[x][y] == EL_AMOEBA_GROWING))
6431 Feld[x][y] = new_element;
6432 InitField(x, y, FALSE);
6433 DrawLevelField(x, y);
6440 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6441 SND_BD_AMOEBA_TURNING_TO_ROCK :
6442 SND_BD_AMOEBA_TURNING_TO_GEM));
6445 void AmoebeWaechst(int x, int y)
6447 static unsigned long sound_delay = 0;
6448 static unsigned long sound_delay_value = 0;
6450 if (!MovDelay[x][y]) /* start new growing cycle */
6454 if (DelayReached(&sound_delay, sound_delay_value))
6457 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6459 if (Store[x][y] == EL_BD_AMOEBA)
6460 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6462 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6464 sound_delay_value = 30;
6468 if (MovDelay[x][y]) /* wait some time before growing bigger */
6471 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6473 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6474 6 - MovDelay[x][y]);
6476 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6479 if (!MovDelay[x][y])
6481 Feld[x][y] = Store[x][y];
6483 DrawLevelField(x, y);
6488 void AmoebaDisappearing(int x, int y)
6490 static unsigned long sound_delay = 0;
6491 static unsigned long sound_delay_value = 0;
6493 if (!MovDelay[x][y]) /* start new shrinking cycle */
6497 if (DelayReached(&sound_delay, sound_delay_value))
6498 sound_delay_value = 30;
6501 if (MovDelay[x][y]) /* wait some time before shrinking */
6504 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6506 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6507 6 - MovDelay[x][y]);
6509 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6512 if (!MovDelay[x][y])
6514 Feld[x][y] = EL_EMPTY;
6515 DrawLevelField(x, y);
6517 /* don't let mole enter this field in this cycle;
6518 (give priority to objects falling to this field from above) */
6524 void AmoebeAbleger(int ax, int ay)
6527 int element = Feld[ax][ay];
6528 int graphic = el2img(element);
6529 int newax = ax, neway = ay;
6530 static int xy[4][2] =
6538 if (!level.amoeba_speed)
6540 Feld[ax][ay] = EL_AMOEBA_DEAD;
6541 DrawLevelField(ax, ay);
6545 if (IS_ANIMATED(graphic))
6546 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6548 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6549 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6551 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6554 if (MovDelay[ax][ay])
6558 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6561 int x = ax + xy[start][0];
6562 int y = ay + xy[start][1];
6564 if (!IN_LEV_FIELD(x, y))
6568 if (IS_FREE(x, y) ||
6569 CAN_GROW_INTO(Feld[x][y]) ||
6570 Feld[x][y] == EL_QUICKSAND_EMPTY)
6576 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6577 if (IS_FREE(x, y) ||
6578 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6585 if (newax == ax && neway == ay)
6588 else /* normal or "filled" (BD style) amoeba */
6591 boolean waiting_for_player = FALSE;
6593 for (i = 0; i < NUM_DIRECTIONS; i++)
6595 int j = (start + i) % 4;
6596 int x = ax + xy[j][0];
6597 int y = ay + xy[j][1];
6599 if (!IN_LEV_FIELD(x, y))
6603 if (IS_FREE(x, y) ||
6604 CAN_GROW_INTO(Feld[x][y]) ||
6605 Feld[x][y] == EL_QUICKSAND_EMPTY)
6612 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6613 if (IS_FREE(x, y) ||
6614 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6621 else if (IS_PLAYER(x, y))
6622 waiting_for_player = TRUE;
6625 if (newax == ax && neway == ay) /* amoeba cannot grow */
6628 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6630 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6633 Feld[ax][ay] = EL_AMOEBA_DEAD;
6634 DrawLevelField(ax, ay);
6635 AmoebaCnt[AmoebaNr[ax][ay]]--;
6637 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6639 if (element == EL_AMOEBA_FULL)
6640 AmoebeUmwandeln(ax, ay);
6641 else if (element == EL_BD_AMOEBA)
6642 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6647 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6649 /* amoeba gets larger by growing in some direction */
6651 int new_group_nr = AmoebaNr[ax][ay];
6654 if (new_group_nr == 0)
6656 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6657 printf("AmoebeAbleger(): This should never happen!\n");
6662 AmoebaNr[newax][neway] = new_group_nr;
6663 AmoebaCnt[new_group_nr]++;
6664 AmoebaCnt2[new_group_nr]++;
6666 /* if amoeba touches other amoeba(s) after growing, unify them */
6667 AmoebenVereinigen(newax, neway);
6669 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6671 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6677 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6678 (neway == lev_fieldy - 1 && newax != ax))
6680 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6681 Store[newax][neway] = element;
6683 else if (neway == ay)
6685 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6687 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6689 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6694 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6695 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6696 Store[ax][ay] = EL_AMOEBA_DROP;
6697 ContinueMoving(ax, ay);
6701 DrawLevelField(newax, neway);
6704 void Life(int ax, int ay)
6707 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6709 int element = Feld[ax][ay];
6710 int graphic = el2img(element);
6711 boolean changed = FALSE;
6713 if (IS_ANIMATED(graphic))
6714 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6719 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6720 MovDelay[ax][ay] = life_time;
6722 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6725 if (MovDelay[ax][ay])
6729 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6731 int xx = ax+x1, yy = ay+y1;
6734 if (!IN_LEV_FIELD(xx, yy))
6737 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6739 int x = xx+x2, y = yy+y2;
6741 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6744 if (((Feld[x][y] == element ||
6745 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6747 (IS_FREE(x, y) && Stop[x][y]))
6751 if (xx == ax && yy == ay) /* field in the middle */
6753 if (nachbarn < life[0] || nachbarn > life[1])
6755 Feld[xx][yy] = EL_EMPTY;
6757 DrawLevelField(xx, yy);
6758 Stop[xx][yy] = TRUE;
6763 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6764 { /* free border field */
6765 if (nachbarn >= life[2] && nachbarn <= life[3])
6767 Feld[xx][yy] = element;
6768 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6770 DrawLevelField(xx, yy);
6771 Stop[xx][yy] = TRUE;
6776 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6777 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6778 { /* free border field */
6779 if (nachbarn >= life[2] && nachbarn <= life[3])
6781 Feld[xx][yy] = element;
6782 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6784 DrawLevelField(xx, yy);
6785 Stop[xx][yy] = TRUE;
6793 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6794 SND_GAME_OF_LIFE_GROWING);
6797 static void InitRobotWheel(int x, int y)
6799 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6802 static void RunRobotWheel(int x, int y)
6804 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6807 static void StopRobotWheel(int x, int y)
6809 if (ZX == x && ZY == y)
6813 static void InitTimegateWheel(int x, int y)
6816 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6818 /* another brainless, "type style" bug ... :-( */
6819 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6823 static void RunTimegateWheel(int x, int y)
6825 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6828 void CheckExit(int x, int y)
6830 if (local_player->gems_still_needed > 0 ||
6831 local_player->sokobanfields_still_needed > 0 ||
6832 local_player->lights_still_needed > 0)
6834 int element = Feld[x][y];
6835 int graphic = el2img(element);
6837 if (IS_ANIMATED(graphic))
6838 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6843 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6846 Feld[x][y] = EL_EXIT_OPENING;
6848 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6851 void CheckExitSP(int x, int y)
6853 if (local_player->gems_still_needed > 0)
6855 int element = Feld[x][y];
6856 int graphic = el2img(element);
6858 if (IS_ANIMATED(graphic))
6859 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6864 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6867 Feld[x][y] = EL_SP_EXIT_OPENING;
6869 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6872 static void CloseAllOpenTimegates()
6876 for (y = 0; y < lev_fieldy; y++)
6878 for (x = 0; x < lev_fieldx; x++)
6880 int element = Feld[x][y];
6882 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6884 Feld[x][y] = EL_TIMEGATE_CLOSING;
6886 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6888 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6895 void EdelsteinFunkeln(int x, int y)
6897 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6900 if (Feld[x][y] == EL_BD_DIAMOND)
6903 if (MovDelay[x][y] == 0) /* next animation frame */
6904 MovDelay[x][y] = 11 * !SimpleRND(500);
6906 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6910 if (setup.direct_draw && MovDelay[x][y])
6911 SetDrawtoField(DRAW_BUFFERED);
6913 DrawLevelElementAnimation(x, y, Feld[x][y]);
6915 if (MovDelay[x][y] != 0)
6917 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6918 10 - MovDelay[x][y]);
6920 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6922 if (setup.direct_draw)
6926 dest_x = FX + SCREENX(x) * TILEX;
6927 dest_y = FY + SCREENY(y) * TILEY;
6929 BlitBitmap(drawto_field, window,
6930 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6931 SetDrawtoField(DRAW_DIRECT);
6937 void MauerWaechst(int x, int y)
6941 if (!MovDelay[x][y]) /* next animation frame */
6942 MovDelay[x][y] = 3 * delay;
6944 if (MovDelay[x][y]) /* wait some time before next frame */
6948 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6950 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6951 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6953 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6956 if (!MovDelay[x][y])
6958 if (MovDir[x][y] == MV_LEFT)
6960 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6961 DrawLevelField(x - 1, y);
6963 else if (MovDir[x][y] == MV_RIGHT)
6965 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6966 DrawLevelField(x + 1, y);
6968 else if (MovDir[x][y] == MV_UP)
6970 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6971 DrawLevelField(x, y - 1);
6975 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6976 DrawLevelField(x, y + 1);
6979 Feld[x][y] = Store[x][y];
6981 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6982 DrawLevelField(x, y);
6987 void MauerAbleger(int ax, int ay)
6989 int element = Feld[ax][ay];
6990 int graphic = el2img(element);
6991 boolean oben_frei = FALSE, unten_frei = FALSE;
6992 boolean links_frei = FALSE, rechts_frei = FALSE;
6993 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6994 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6995 boolean new_wall = FALSE;
6997 if (IS_ANIMATED(graphic))
6998 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7000 if (!MovDelay[ax][ay]) /* start building new wall */
7001 MovDelay[ax][ay] = 6;
7003 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7006 if (MovDelay[ax][ay])
7010 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7012 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7014 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7016 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7019 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7020 element == EL_EXPANDABLE_WALL_ANY)
7024 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7025 Store[ax][ay-1] = element;
7026 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7027 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7028 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7029 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7034 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7035 Store[ax][ay+1] = element;
7036 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7037 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7038 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7039 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7044 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7045 element == EL_EXPANDABLE_WALL_ANY ||
7046 element == EL_EXPANDABLE_WALL)
7050 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7051 Store[ax-1][ay] = element;
7052 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7053 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7054 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7055 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7061 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7062 Store[ax+1][ay] = element;
7063 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7064 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7065 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7066 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7071 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7072 DrawLevelField(ax, ay);
7074 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7076 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7077 unten_massiv = TRUE;
7078 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7079 links_massiv = TRUE;
7080 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7081 rechts_massiv = TRUE;
7083 if (((oben_massiv && unten_massiv) ||
7084 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7085 element == EL_EXPANDABLE_WALL) &&
7086 ((links_massiv && rechts_massiv) ||
7087 element == EL_EXPANDABLE_WALL_VERTICAL))
7088 Feld[ax][ay] = EL_WALL;
7092 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7094 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7098 void CheckForDragon(int x, int y)
7101 boolean dragon_found = FALSE;
7102 static int xy[4][2] =
7110 for (i = 0; i < NUM_DIRECTIONS; i++)
7112 for (j = 0; j < 4; j++)
7114 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7116 if (IN_LEV_FIELD(xx, yy) &&
7117 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7119 if (Feld[xx][yy] == EL_DRAGON)
7120 dragon_found = TRUE;
7129 for (i = 0; i < NUM_DIRECTIONS; i++)
7131 for (j = 0; j < 3; j++)
7133 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7135 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7137 Feld[xx][yy] = EL_EMPTY;
7138 DrawLevelField(xx, yy);
7147 static void InitBuggyBase(int x, int y)
7149 int element = Feld[x][y];
7150 int activating_delay = FRAMES_PER_SECOND / 4;
7153 (element == EL_SP_BUGGY_BASE ?
7154 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7155 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7157 element == EL_SP_BUGGY_BASE_ACTIVE ?
7158 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7161 static void WarnBuggyBase(int x, int y)
7164 static int xy[4][2] =
7172 for (i = 0; i < NUM_DIRECTIONS; i++)
7174 int xx = x + xy[i][0], yy = y + xy[i][1];
7176 if (IS_PLAYER(xx, yy))
7178 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7185 static void InitTrap(int x, int y)
7187 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7190 static void ActivateTrap(int x, int y)
7192 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7195 static void ChangeActiveTrap(int x, int y)
7197 int graphic = IMG_TRAP_ACTIVE;
7199 /* if new animation frame was drawn, correct crumbled sand border */
7200 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7201 DrawLevelFieldCrumbledSand(x, y);
7204 static void ChangeElementNowExt(int x, int y, int target_element)
7206 int previous_move_direction = MovDir[x][y];
7208 /* check if element under player changes from accessible to unaccessible
7209 (needed for special case of dropping element which then changes) */
7210 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7211 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7218 Feld[x][y] = target_element;
7220 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7222 ResetGfxAnimation(x, y);
7223 ResetRandomAnimationValue(x, y);
7225 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7226 MovDir[x][y] = previous_move_direction;
7229 InitField_WithBug1(x, y, FALSE);
7231 InitField(x, y, FALSE);
7232 if (CAN_MOVE(Feld[x][y]))
7236 DrawLevelField(x, y);
7238 if (GFX_CRUMBLED(Feld[x][y]))
7239 DrawLevelFieldCrumbledSandNeighbours(x, y);
7241 TestIfBadThingTouchesHero(x, y);
7242 TestIfPlayerTouchesCustomElement(x, y);
7243 TestIfElementTouchesCustomElement(x, y);
7245 if (ELEM_IS_PLAYER(target_element))
7246 RelocatePlayer(x, y, target_element);
7249 static boolean ChangeElementNow(int x, int y, int element, int page)
7251 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7254 /* always use default change event to prevent running into a loop */
7255 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7256 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7258 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7260 /* reset actual trigger element and player */
7261 change->actual_trigger_element = EL_EMPTY;
7262 change->actual_trigger_player = EL_PLAYER_1;
7265 /* do not change already changed elements with same change event */
7267 if (Changed[x][y] & ChangeEvent[x][y])
7274 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7276 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7278 if (change->explode)
7285 if (change->use_target_content)
7287 boolean complete_replace = TRUE;
7288 boolean can_replace[3][3];
7291 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7294 boolean is_diggable;
7295 boolean is_collectible;
7296 boolean is_removable;
7297 boolean is_destructible;
7298 int ex = x + xx - 1;
7299 int ey = y + yy - 1;
7300 int content_element = change->target_content[xx][yy];
7303 can_replace[xx][yy] = TRUE;
7305 if (ex == x && ey == y) /* do not check changing element itself */
7308 if (content_element == EL_EMPTY_SPACE)
7310 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7315 if (!IN_LEV_FIELD(ex, ey))
7317 can_replace[xx][yy] = FALSE;
7318 complete_replace = FALSE;
7325 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7326 e = MovingOrBlocked2Element(ex, ey);
7331 is_empty = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7332 IS_WALKABLE(content_element)));
7334 is_empty = (IS_FREE(ex, ey) || (IS_PLAYER(ex, ey) &&
7335 IS_WALKABLE(content_element)));
7337 is_diggable = (is_empty || IS_DIGGABLE(e));
7338 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7339 is_removable = (is_diggable || is_collectible);
7340 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7342 can_replace[xx][yy] =
7343 ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7344 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7345 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7346 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7347 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7349 if (!can_replace[xx][yy])
7350 complete_replace = FALSE;
7352 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7353 IS_WALKABLE(content_element)));
7355 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7357 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7360 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7361 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7362 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7364 can_replace[xx][yy] = FALSE;
7365 complete_replace = FALSE;
7370 if (!change->only_if_complete || complete_replace)
7372 boolean something_has_changed = FALSE;
7374 if (change->only_if_complete && change->use_random_replace &&
7375 RND(100) < change->random_percentage)
7378 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7380 int ex = x + xx - 1;
7381 int ey = y + yy - 1;
7382 int content_element;
7384 if (can_replace[xx][yy] && (!change->use_random_replace ||
7385 RND(100) < change->random_percentage))
7387 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7388 RemoveMovingField(ex, ey);
7390 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7392 content_element = change->target_content[xx][yy];
7393 target_element = GET_TARGET_ELEMENT(content_element, change);
7395 ChangeElementNowExt(ex, ey, target_element);
7397 something_has_changed = TRUE;
7399 /* for symmetry reasons, freeze newly created border elements */
7400 if (ex != x || ey != y)
7401 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7405 if (something_has_changed)
7406 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7411 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7413 ChangeElementNowExt(x, y, target_element);
7415 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7421 static void ChangeElement(int x, int y, int page)
7423 int element = MovingOrBlocked2Element(x, y);
7424 struct ElementInfo *ei = &element_info[element];
7425 struct ElementChangeInfo *change = &ei->change_page[page];
7428 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7431 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7432 x, y, element, element_info[element].token_name);
7433 printf("ChangeElement(): This should never happen!\n");
7438 /* this can happen with classic bombs on walkable, changing elements */
7439 if (!CAN_CHANGE(element))
7442 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7443 ChangeDelay[x][y] = 0;
7449 if (ChangeDelay[x][y] == 0) /* initialize element change */
7451 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7452 RND(change->delay_random * change->delay_frames)) + 1;
7454 ResetGfxAnimation(x, y);
7455 ResetRandomAnimationValue(x, y);
7457 if (change->pre_change_function)
7458 change->pre_change_function(x, y);
7461 ChangeDelay[x][y]--;
7463 if (ChangeDelay[x][y] != 0) /* continue element change */
7465 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7467 if (IS_ANIMATED(graphic))
7468 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7470 if (change->change_function)
7471 change->change_function(x, y);
7473 else /* finish element change */
7475 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7477 page = ChangePage[x][y];
7478 ChangePage[x][y] = -1;
7480 change = &ei->change_page[page];
7484 if (IS_MOVING(x, y) && !change->explode)
7486 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7489 ChangeDelay[x][y] = 1; /* try change after next move step */
7490 ChangePage[x][y] = page; /* remember page to use for change */
7495 if (ChangeElementNow(x, y, element, page))
7497 if (change->post_change_function)
7498 change->post_change_function(x, y);
7503 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7504 int trigger_element,
7511 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7513 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7516 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7518 int element = EL_CUSTOM_START + i;
7520 boolean change_element = FALSE;
7523 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7526 for (j = 0; j < element_info[element].num_change_pages; j++)
7528 struct ElementChangeInfo *change = &element_info[element].change_page[j];
7530 if (change->can_change &&
7531 change->events & CH_EVENT_BIT(trigger_event) &&
7532 change->trigger_side & trigger_side &&
7533 change->trigger_player & trigger_player &&
7534 change->trigger_page & trigger_page_bits &&
7535 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7538 if (!(change->events & CH_EVENT_BIT(trigger_event)))
7539 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7540 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7543 change_element = TRUE;
7546 change->actual_trigger_element = trigger_element;
7547 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7553 if (!change_element)
7556 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7559 if (x == lx && y == ly) /* do not change trigger element itself */
7563 if (Feld[x][y] == element)
7565 ChangeDelay[x][y] = 1;
7566 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7567 ChangeElement(x, y, page);
7575 static boolean CheckElementChangeExt(int x, int y,
7577 int trigger_element,
7583 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7586 if (Feld[x][y] == EL_BLOCKED)
7588 Blocked2Moving(x, y, &x, &y);
7589 element = Feld[x][y];
7593 if (Feld[x][y] != element) /* check if element has already changed */
7596 printf("::: %d ('%s') != %d ('%s') [%d]\n",
7597 Feld[x][y], element_info[Feld[x][y]].token_name,
7598 element, element_info[element].token_name,
7607 if (trigger_page < 0)
7609 boolean change_element = FALSE;
7612 for (i = 0; i < element_info[element].num_change_pages; i++)
7614 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7616 if (change->can_change &&
7617 change->events & CH_EVENT_BIT(trigger_event) &&
7618 change->trigger_side & trigger_side &&
7619 change->trigger_player & trigger_player)
7621 change_element = TRUE;
7624 change->actual_trigger_element = trigger_element;
7625 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7631 if (!change_element)
7636 struct ElementInfo *ei = &element_info[element];
7637 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7639 change->actual_trigger_element = trigger_element;
7640 change->actual_trigger_player = EL_PLAYER_1; /* unused */
7645 /* !!! this check misses pages with same event, but different side !!! */
7647 if (trigger_page < 0)
7648 trigger_page = element_info[element].event_page_nr[trigger_event];
7650 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7654 ChangeDelay[x][y] = 1;
7655 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7656 ChangeElement(x, y, trigger_page);
7661 static void PlayPlayerSound(struct PlayerInfo *player)
7663 int jx = player->jx, jy = player->jy;
7664 int element = player->element_nr;
7665 int last_action = player->last_action_waiting;
7666 int action = player->action_waiting;
7668 if (player->is_waiting)
7670 if (action != last_action)
7671 PlayLevelSoundElementAction(jx, jy, element, action);
7673 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7677 if (action != last_action)
7678 StopSound(element_info[element].sound[last_action]);
7680 if (last_action == ACTION_SLEEPING)
7681 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7685 static void PlayAllPlayersSound()
7689 for (i = 0; i < MAX_PLAYERS; i++)
7690 if (stored_player[i].active)
7691 PlayPlayerSound(&stored_player[i]);
7694 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7696 boolean last_waiting = player->is_waiting;
7697 int move_dir = player->MovDir;
7699 player->last_action_waiting = player->action_waiting;
7703 if (!last_waiting) /* not waiting -> waiting */
7705 player->is_waiting = TRUE;
7707 player->frame_counter_bored =
7709 game.player_boring_delay_fixed +
7710 SimpleRND(game.player_boring_delay_random);
7711 player->frame_counter_sleeping =
7713 game.player_sleeping_delay_fixed +
7714 SimpleRND(game.player_sleeping_delay_random);
7716 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7719 if (game.player_sleeping_delay_fixed +
7720 game.player_sleeping_delay_random > 0 &&
7721 player->anim_delay_counter == 0 &&
7722 player->post_delay_counter == 0 &&
7723 FrameCounter >= player->frame_counter_sleeping)
7724 player->is_sleeping = TRUE;
7725 else if (game.player_boring_delay_fixed +
7726 game.player_boring_delay_random > 0 &&
7727 FrameCounter >= player->frame_counter_bored)
7728 player->is_bored = TRUE;
7730 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7731 player->is_bored ? ACTION_BORING :
7734 if (player->is_sleeping)
7736 if (player->num_special_action_sleeping > 0)
7738 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7740 int last_special_action = player->special_action_sleeping;
7741 int num_special_action = player->num_special_action_sleeping;
7742 int special_action =
7743 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7744 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7745 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7746 last_special_action + 1 : ACTION_SLEEPING);
7747 int special_graphic =
7748 el_act_dir2img(player->element_nr, special_action, move_dir);
7750 player->anim_delay_counter =
7751 graphic_info[special_graphic].anim_delay_fixed +
7752 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7753 player->post_delay_counter =
7754 graphic_info[special_graphic].post_delay_fixed +
7755 SimpleRND(graphic_info[special_graphic].post_delay_random);
7757 player->special_action_sleeping = special_action;
7760 if (player->anim_delay_counter > 0)
7762 player->action_waiting = player->special_action_sleeping;
7763 player->anim_delay_counter--;
7765 else if (player->post_delay_counter > 0)
7767 player->post_delay_counter--;
7771 else if (player->is_bored)
7773 if (player->num_special_action_bored > 0)
7775 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7777 int special_action =
7778 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7779 int special_graphic =
7780 el_act_dir2img(player->element_nr, special_action, move_dir);
7782 player->anim_delay_counter =
7783 graphic_info[special_graphic].anim_delay_fixed +
7784 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7785 player->post_delay_counter =
7786 graphic_info[special_graphic].post_delay_fixed +
7787 SimpleRND(graphic_info[special_graphic].post_delay_random);
7789 player->special_action_bored = special_action;
7792 if (player->anim_delay_counter > 0)
7794 player->action_waiting = player->special_action_bored;
7795 player->anim_delay_counter--;
7797 else if (player->post_delay_counter > 0)
7799 player->post_delay_counter--;
7804 else if (last_waiting) /* waiting -> not waiting */
7806 player->is_waiting = FALSE;
7807 player->is_bored = FALSE;
7808 player->is_sleeping = FALSE;
7810 player->frame_counter_bored = -1;
7811 player->frame_counter_sleeping = -1;
7813 player->anim_delay_counter = 0;
7814 player->post_delay_counter = 0;
7816 player->action_waiting = ACTION_DEFAULT;
7818 player->special_action_bored = ACTION_DEFAULT;
7819 player->special_action_sleeping = ACTION_DEFAULT;
7824 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7827 static byte stored_player_action[MAX_PLAYERS];
7828 static int num_stored_actions = 0;
7830 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7831 int left = player_action & JOY_LEFT;
7832 int right = player_action & JOY_RIGHT;
7833 int up = player_action & JOY_UP;
7834 int down = player_action & JOY_DOWN;
7835 int button1 = player_action & JOY_BUTTON_1;
7836 int button2 = player_action & JOY_BUTTON_2;
7837 int dx = (left ? -1 : right ? 1 : 0);
7838 int dy = (up ? -1 : down ? 1 : 0);
7841 stored_player_action[player->index_nr] = 0;
7842 num_stored_actions++;
7846 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7849 if (!player->active || tape.pausing)
7853 printf("::: [%d %d %d %d] [%d %d]\n",
7854 left, right, up, down, button1, button2);
7860 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7865 if (player->MovPos == 0)
7866 CheckGravityMovement(player);
7869 snapped = SnapField(player, dx, dy);
7873 dropped = DropElement(player);
7875 moved = MovePlayer(player, dx, dy);
7878 if (tape.single_step && tape.recording && !tape.pausing)
7880 if (button1 || (dropped && !moved))
7882 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7883 SnapField(player, 0, 0); /* stop snapping */
7887 SetPlayerWaiting(player, FALSE);
7890 return player_action;
7892 stored_player_action[player->index_nr] = player_action;
7898 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7901 /* no actions for this player (no input at player's configured device) */
7903 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7904 SnapField(player, 0, 0);
7905 CheckGravityMovementWhenNotMoving(player);
7907 if (player->MovPos == 0)
7908 SetPlayerWaiting(player, TRUE);
7910 if (player->MovPos == 0) /* needed for tape.playing */
7911 player->is_moving = FALSE;
7913 player->is_dropping = FALSE;
7919 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7921 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7923 TapeRecordAction(stored_player_action);
7924 num_stored_actions = 0;
7931 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7933 static byte stored_player_action[MAX_PLAYERS];
7934 static int num_stored_actions = 0;
7935 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7936 int left = player_action & JOY_LEFT;
7937 int right = player_action & JOY_RIGHT;
7938 int up = player_action & JOY_UP;
7939 int down = player_action & JOY_DOWN;
7940 int button1 = player_action & JOY_BUTTON_1;
7941 int button2 = player_action & JOY_BUTTON_2;
7942 int dx = (left ? -1 : right ? 1 : 0);
7943 int dy = (up ? -1 : down ? 1 : 0);
7945 stored_player_action[player->index_nr] = 0;
7946 num_stored_actions++;
7948 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7950 if (!player->active || tape.pausing)
7955 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7958 snapped = SnapField(player, dx, dy);
7962 dropped = DropElement(player);
7964 moved = MovePlayer(player, dx, dy);
7967 if (tape.single_step && tape.recording && !tape.pausing)
7969 if (button1 || (dropped && !moved))
7971 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7972 SnapField(player, 0, 0); /* stop snapping */
7976 stored_player_action[player->index_nr] = player_action;
7980 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7982 /* no actions for this player (no input at player's configured device) */
7984 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7985 SnapField(player, 0, 0);
7986 CheckGravityMovementWhenNotMoving(player);
7988 if (player->MovPos == 0)
7989 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7991 if (player->MovPos == 0) /* needed for tape.playing */
7992 player->is_moving = FALSE;
7995 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7997 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7999 TapeRecordAction(stored_player_action);
8000 num_stored_actions = 0;
8007 static unsigned long action_delay = 0;
8008 unsigned long action_delay_value;
8009 int magic_wall_x = 0, magic_wall_y = 0;
8010 int i, x, y, element, graphic;
8011 byte *recorded_player_action;
8012 byte summarized_player_action = 0;
8014 byte tape_action[MAX_PLAYERS];
8017 if (game_status != GAME_MODE_PLAYING)
8020 action_delay_value =
8021 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8023 if (tape.playing && tape.warp_forward && !tape.pausing)
8024 action_delay_value = 0;
8026 /* ---------- main game synchronization point ---------- */
8028 WaitUntilDelayReached(&action_delay, action_delay_value);
8030 if (network_playing && !network_player_action_received)
8034 printf("DEBUG: try to get network player actions in time\n");
8038 #if defined(PLATFORM_UNIX)
8039 /* last chance to get network player actions without main loop delay */
8043 if (game_status != GAME_MODE_PLAYING)
8046 if (!network_player_action_received)
8050 printf("DEBUG: failed to get network player actions in time\n");
8061 printf("::: getting new tape action [%d]\n", FrameCounter);
8064 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8067 if (recorded_player_action == NULL && tape.pausing)
8072 printf("::: %d\n", stored_player[0].action);
8076 if (recorded_player_action != NULL)
8077 for (i = 0; i < MAX_PLAYERS; i++)
8078 stored_player[i].action = recorded_player_action[i];
8081 for (i = 0; i < MAX_PLAYERS; i++)
8083 summarized_player_action |= stored_player[i].action;
8085 if (!network_playing)
8086 stored_player[i].effective_action = stored_player[i].action;
8089 #if defined(PLATFORM_UNIX)
8090 if (network_playing)
8091 SendToServer_MovePlayer(summarized_player_action);
8094 if (!options.network && !setup.team_mode)
8095 local_player->effective_action = summarized_player_action;
8098 if (recorded_player_action != NULL)
8099 for (i = 0; i < MAX_PLAYERS; i++)
8100 stored_player[i].effective_action = recorded_player_action[i];
8104 for (i = 0; i < MAX_PLAYERS; i++)
8106 tape_action[i] = stored_player[i].effective_action;
8108 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8109 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8112 /* only save actions from input devices, but not programmed actions */
8114 TapeRecordAction(tape_action);
8117 for (i = 0; i < MAX_PLAYERS; i++)
8119 int actual_player_action = stored_player[i].effective_action;
8122 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8123 - rnd_equinox_tetrachloride 048
8124 - rnd_equinox_tetrachloride_ii 096
8125 - rnd_emanuel_schmieg 002
8126 - doctor_sloan_ww 001, 020
8128 if (stored_player[i].MovPos == 0)
8129 CheckGravityMovement(&stored_player[i]);
8133 /* overwrite programmed action with tape action */
8134 if (stored_player[i].programmed_action)
8135 actual_player_action = stored_player[i].programmed_action;
8139 if (stored_player[i].programmed_action)
8140 printf("::: %d\n", stored_player[i].programmed_action);
8143 if (recorded_player_action)
8146 if (stored_player[i].programmed_action &&
8147 stored_player[i].programmed_action != recorded_player_action[i])
8148 printf("::: %d: %d <-> %d\n", i,
8149 stored_player[i].programmed_action, recorded_player_action[i]);
8153 actual_player_action = recorded_player_action[i];
8158 /* overwrite tape action with programmed action */
8159 if (stored_player[i].programmed_action)
8160 actual_player_action = stored_player[i].programmed_action;
8165 printf("::: action: %d: %x [%d]\n",
8166 stored_player[i].MovPos, actual_player_action, FrameCounter);
8170 PlayerActions(&stored_player[i], actual_player_action);
8172 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8174 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8175 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8178 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8183 TapeRecordAction(tape_action);
8186 network_player_action_received = FALSE;
8188 ScrollScreen(NULL, SCROLL_GO_ON);
8194 for (i = 0; i < MAX_PLAYERS; i++)
8195 stored_player[i].Frame++;
8199 /* for downwards compatibility, the following code emulates a fixed bug that
8200 occured when pushing elements (causing elements that just made their last
8201 pushing step to already (if possible) make their first falling step in the
8202 same game frame, which is bad); this code is also needed to use the famous
8203 "spring push bug" which is used in older levels and might be wanted to be
8204 used also in newer levels, but in this case the buggy pushing code is only
8205 affecting the "spring" element and no other elements */
8208 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8210 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8213 for (i = 0; i < MAX_PLAYERS; i++)
8215 struct PlayerInfo *player = &stored_player[i];
8220 if (player->active && player->is_pushing && player->is_moving &&
8222 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8223 Feld[x][y] == EL_SPRING))
8225 if (player->active && player->is_pushing && player->is_moving &&
8229 ContinueMoving(x, y);
8231 /* continue moving after pushing (this is actually a bug) */
8232 if (!IS_MOVING(x, y))
8241 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8243 Changed[x][y] = CE_BITMASK_DEFAULT;
8244 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8247 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8249 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8250 printf("GameActions(): This should never happen!\n");
8252 ChangePage[x][y] = -1;
8257 if (WasJustMoving[x][y] > 0)
8258 WasJustMoving[x][y]--;
8259 if (WasJustFalling[x][y] > 0)
8260 WasJustFalling[x][y]--;
8261 if (CheckCollision[x][y] > 0)
8262 CheckCollision[x][y]--;
8267 /* reset finished pushing action (not done in ContinueMoving() to allow
8268 continous pushing animation for elements with zero push delay) */
8269 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8271 ResetGfxAnimation(x, y);
8272 DrawLevelField(x, y);
8277 if (IS_BLOCKED(x, y))
8281 Blocked2Moving(x, y, &oldx, &oldy);
8282 if (!IS_MOVING(oldx, oldy))
8284 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8285 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8286 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8287 printf("GameActions(): This should never happen!\n");
8293 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8295 element = Feld[x][y];
8297 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8299 graphic = el2img(element);
8305 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8307 element = graphic = 0;
8311 if (graphic_info[graphic].anim_global_sync)
8312 GfxFrame[x][y] = FrameCounter;
8314 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8315 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8316 ResetRandomAnimationValue(x, y);
8318 SetRandomAnimationValue(x, y);
8321 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8324 if (IS_INACTIVE(element))
8326 if (IS_ANIMATED(graphic))
8327 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8333 /* this may take place after moving, so 'element' may have changed */
8335 if (IS_CHANGING(x, y))
8337 if (IS_CHANGING(x, y) &&
8338 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8342 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8343 element_info[element].event_page_nr[CE_DELAY]);
8345 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8348 element = Feld[x][y];
8349 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8353 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8358 element = Feld[x][y];
8359 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8361 if (element == EL_MOLE)
8362 printf("::: %d, %d, %d [%d]\n",
8363 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8367 if (element == EL_YAMYAM)
8368 printf("::: %d, %d, %d\n",
8369 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8373 if (IS_ANIMATED(graphic) &&
8377 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8380 if (element == EL_BUG)
8381 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8385 if (element == EL_MOLE)
8386 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8390 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8391 EdelsteinFunkeln(x, y);
8393 else if ((element == EL_ACID ||
8394 element == EL_EXIT_OPEN ||
8395 element == EL_SP_EXIT_OPEN ||
8396 element == EL_SP_TERMINAL ||
8397 element == EL_SP_TERMINAL_ACTIVE ||
8398 element == EL_EXTRA_TIME ||
8399 element == EL_SHIELD_NORMAL ||
8400 element == EL_SHIELD_DEADLY) &&
8401 IS_ANIMATED(graphic))
8402 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8403 else if (IS_MOVING(x, y))
8404 ContinueMoving(x, y);
8405 else if (IS_ACTIVE_BOMB(element))
8406 CheckDynamite(x, y);
8408 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8409 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8411 else if (element == EL_AMOEBA_GROWING)
8412 AmoebeWaechst(x, y);
8413 else if (element == EL_AMOEBA_SHRINKING)
8414 AmoebaDisappearing(x, y);
8416 #if !USE_NEW_AMOEBA_CODE
8417 else if (IS_AMOEBALIVE(element))
8418 AmoebeAbleger(x, y);
8421 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8423 else if (element == EL_EXIT_CLOSED)
8425 else if (element == EL_SP_EXIT_CLOSED)
8427 else if (element == EL_EXPANDABLE_WALL_GROWING)
8429 else if (element == EL_EXPANDABLE_WALL ||
8430 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8431 element == EL_EXPANDABLE_WALL_VERTICAL ||
8432 element == EL_EXPANDABLE_WALL_ANY)
8434 else if (element == EL_FLAMES)
8435 CheckForDragon(x, y);
8437 else if (IS_AUTO_CHANGING(element))
8438 ChangeElement(x, y);
8440 else if (element == EL_EXPLOSION)
8441 ; /* drawing of correct explosion animation is handled separately */
8442 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8443 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8446 /* this may take place after moving, so 'element' may have changed */
8447 if (IS_AUTO_CHANGING(Feld[x][y]))
8448 ChangeElement(x, y);
8451 if (IS_BELT_ACTIVE(element))
8452 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8454 if (game.magic_wall_active)
8456 int jx = local_player->jx, jy = local_player->jy;
8458 /* play the element sound at the position nearest to the player */
8459 if ((element == EL_MAGIC_WALL_FULL ||
8460 element == EL_MAGIC_WALL_ACTIVE ||
8461 element == EL_MAGIC_WALL_EMPTYING ||
8462 element == EL_BD_MAGIC_WALL_FULL ||
8463 element == EL_BD_MAGIC_WALL_ACTIVE ||
8464 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8465 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8473 #if USE_NEW_AMOEBA_CODE
8474 /* new experimental amoeba growth stuff */
8476 if (!(FrameCounter % 8))
8479 static unsigned long random = 1684108901;
8481 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8484 x = (random >> 10) % lev_fieldx;
8485 y = (random >> 20) % lev_fieldy;
8487 x = RND(lev_fieldx);
8488 y = RND(lev_fieldy);
8490 element = Feld[x][y];
8493 if (!IS_PLAYER(x,y) &&
8494 (element == EL_EMPTY ||
8495 CAN_GROW_INTO(element) ||
8496 element == EL_QUICKSAND_EMPTY ||
8497 element == EL_ACID_SPLASH_LEFT ||
8498 element == EL_ACID_SPLASH_RIGHT))
8500 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8501 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8502 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8503 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8504 Feld[x][y] = EL_AMOEBA_DROP;
8507 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8508 if (!IS_PLAYER(x,y) &&
8509 (element == EL_EMPTY ||
8510 element == EL_SAND ||
8511 element == EL_QUICKSAND_EMPTY ||
8512 element == EL_ACID_SPLASH_LEFT ||
8513 element == EL_ACID_SPLASH_RIGHT))
8515 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8516 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8517 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8518 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8519 Feld[x][y] = EL_AMOEBA_DROP;
8523 random = random * 129 + 1;
8529 if (game.explosions_delayed)
8532 game.explosions_delayed = FALSE;
8534 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8536 element = Feld[x][y];
8538 if (ExplodeField[x][y])
8539 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8540 else if (element == EL_EXPLOSION)
8541 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8543 ExplodeField[x][y] = EX_TYPE_NONE;
8546 game.explosions_delayed = TRUE;
8549 if (game.magic_wall_active)
8551 if (!(game.magic_wall_time_left % 4))
8553 int element = Feld[magic_wall_x][magic_wall_y];
8555 if (element == EL_BD_MAGIC_WALL_FULL ||
8556 element == EL_BD_MAGIC_WALL_ACTIVE ||
8557 element == EL_BD_MAGIC_WALL_EMPTYING)
8558 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8560 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8563 if (game.magic_wall_time_left > 0)
8565 game.magic_wall_time_left--;
8566 if (!game.magic_wall_time_left)
8568 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8570 element = Feld[x][y];
8572 if (element == EL_MAGIC_WALL_ACTIVE ||
8573 element == EL_MAGIC_WALL_FULL)
8575 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8576 DrawLevelField(x, y);
8578 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8579 element == EL_BD_MAGIC_WALL_FULL)
8581 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8582 DrawLevelField(x, y);
8586 game.magic_wall_active = FALSE;
8591 if (game.light_time_left > 0)
8593 game.light_time_left--;
8595 if (game.light_time_left == 0)
8596 RedrawAllLightSwitchesAndInvisibleElements();
8599 if (game.timegate_time_left > 0)
8601 game.timegate_time_left--;
8603 if (game.timegate_time_left == 0)
8604 CloseAllOpenTimegates();
8607 for (i = 0; i < MAX_PLAYERS; i++)
8609 struct PlayerInfo *player = &stored_player[i];
8611 if (SHIELD_ON(player))
8613 if (player->shield_deadly_time_left)
8614 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8615 else if (player->shield_normal_time_left)
8616 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8620 if (TimeFrames >= FRAMES_PER_SECOND)
8625 if (!level.use_step_counter)
8629 for (i = 0; i < MAX_PLAYERS; i++)
8631 struct PlayerInfo *player = &stored_player[i];
8633 if (SHIELD_ON(player))
8635 player->shield_normal_time_left--;
8637 if (player->shield_deadly_time_left > 0)
8638 player->shield_deadly_time_left--;
8646 if (TimeLeft <= 10 && setup.time_limit)
8647 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8649 DrawGameValue_Time(TimeLeft);
8651 if (!TimeLeft && setup.time_limit)
8652 for (i = 0; i < MAX_PLAYERS; i++)
8653 KillHero(&stored_player[i]);
8655 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8656 DrawGameValue_Time(TimePlayed);
8659 if (tape.recording || tape.playing)
8660 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8664 PlayAllPlayersSound();
8666 if (options.debug) /* calculate frames per second */
8668 static unsigned long fps_counter = 0;
8669 static int fps_frames = 0;
8670 unsigned long fps_delay_ms = Counter() - fps_counter;
8674 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8676 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8679 fps_counter = Counter();
8682 redraw_mask |= REDRAW_FPS;
8686 if (stored_player[0].jx != stored_player[0].last_jx ||
8687 stored_player[0].jy != stored_player[0].last_jy)
8688 printf("::: %d, %d, %d, %d, %d\n",
8689 stored_player[0].MovDir,
8690 stored_player[0].MovPos,
8691 stored_player[0].GfxPos,
8692 stored_player[0].Frame,
8693 stored_player[0].StepFrame);
8700 for (i = 0; i < MAX_PLAYERS; i++)
8703 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8705 stored_player[i].Frame += move_frames;
8707 if (stored_player[i].MovPos != 0)
8708 stored_player[i].StepFrame += move_frames;
8710 if (stored_player[i].drop_delay > 0)
8711 stored_player[i].drop_delay--;
8716 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8718 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8720 local_player->show_envelope = 0;
8725 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8727 int min_x = x, min_y = y, max_x = x, max_y = y;
8730 for (i = 0; i < MAX_PLAYERS; i++)
8732 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8734 if (!stored_player[i].active || &stored_player[i] == player)
8737 min_x = MIN(min_x, jx);
8738 min_y = MIN(min_y, jy);
8739 max_x = MAX(max_x, jx);
8740 max_y = MAX(max_y, jy);
8743 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8746 static boolean AllPlayersInVisibleScreen()
8750 for (i = 0; i < MAX_PLAYERS; i++)
8752 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8754 if (!stored_player[i].active)
8757 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8764 void ScrollLevel(int dx, int dy)
8766 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8769 BlitBitmap(drawto_field, drawto_field,
8770 FX + TILEX * (dx == -1) - softscroll_offset,
8771 FY + TILEY * (dy == -1) - softscroll_offset,
8772 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8773 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8774 FX + TILEX * (dx == 1) - softscroll_offset,
8775 FY + TILEY * (dy == 1) - softscroll_offset);
8779 x = (dx == 1 ? BX1 : BX2);
8780 for (y = BY1; y <= BY2; y++)
8781 DrawScreenField(x, y);
8786 y = (dy == 1 ? BY1 : BY2);
8787 for (x = BX1; x <= BX2; x++)
8788 DrawScreenField(x, y);
8791 redraw_mask |= REDRAW_FIELD;
8795 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8797 int nextx = x + dx, nexty = y + dy;
8798 int element = Feld[x][y];
8801 element != EL_SP_PORT_LEFT &&
8802 element != EL_SP_GRAVITY_PORT_LEFT &&
8803 element != EL_SP_PORT_HORIZONTAL &&
8804 element != EL_SP_PORT_ANY) ||
8806 element != EL_SP_PORT_RIGHT &&
8807 element != EL_SP_GRAVITY_PORT_RIGHT &&
8808 element != EL_SP_PORT_HORIZONTAL &&
8809 element != EL_SP_PORT_ANY) ||
8811 element != EL_SP_PORT_UP &&
8812 element != EL_SP_GRAVITY_PORT_UP &&
8813 element != EL_SP_PORT_VERTICAL &&
8814 element != EL_SP_PORT_ANY) ||
8816 element != EL_SP_PORT_DOWN &&
8817 element != EL_SP_GRAVITY_PORT_DOWN &&
8818 element != EL_SP_PORT_VERTICAL &&
8819 element != EL_SP_PORT_ANY) ||
8820 !IN_LEV_FIELD(nextx, nexty) ||
8821 !IS_FREE(nextx, nexty))
8828 static boolean canFallDown(struct PlayerInfo *player)
8830 int jx = player->jx, jy = player->jy;
8832 return (IN_LEV_FIELD(jx, jy + 1) &&
8833 (IS_FREE(jx, jy + 1) ||
8834 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8835 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8836 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8839 static boolean canPassField(int x, int y, int move_dir)
8841 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8842 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8843 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8846 int element = Feld[x][y];
8848 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8849 !CAN_MOVE(element) &&
8850 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8851 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8852 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8855 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8857 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8858 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8859 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8863 int nextx = newx + dx;
8864 int nexty = newy + dy;
8868 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8869 (IS_DIGGABLE(Feld[newx][newy]) ||
8870 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8871 canPassField(newx, newy, move_dir)));
8873 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8874 (IS_DIGGABLE(Feld[newx][newy]) ||
8875 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8876 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
8877 !CAN_MOVE(Feld[newx][newy]) &&
8878 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8879 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8880 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
8884 static void CheckGravityMovement(struct PlayerInfo *player)
8886 if (game.gravity && !player->programmed_action)
8889 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8890 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8892 int move_dir_horizontal = player->action & MV_HORIZONTAL;
8893 int move_dir_vertical = player->action & MV_VERTICAL;
8897 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8899 boolean player_is_snapping = player->action & JOY_BUTTON_1;
8902 int jx = player->jx, jy = player->jy;
8904 boolean player_is_moving_to_valid_field =
8905 (!player_is_snapping &&
8906 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8907 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8911 (player->last_move_dir & MV_HORIZONTAL ?
8912 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8913 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8917 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8918 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8919 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8920 int new_jx = jx + dx, new_jy = jy + dy;
8921 int nextx = new_jx + dx, nexty = new_jy + dy;
8927 boolean player_can_fall_down = canFallDown(player);
8929 boolean player_can_fall_down =
8930 (IN_LEV_FIELD(jx, jy + 1) &&
8931 (IS_FREE(jx, jy + 1) ||
8932 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
8936 boolean player_can_fall_down =
8937 (IN_LEV_FIELD(jx, jy + 1) &&
8938 (IS_FREE(jx, jy + 1)));
8942 boolean player_is_moving_to_valid_field =
8945 !player_is_snapping &&
8949 IN_LEV_FIELD(new_jx, new_jy) &&
8950 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
8951 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8952 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
8953 IN_LEV_FIELD(nextx, nexty) &&
8954 element_info[Feld[nextx][nexty]].access_direction & move_dir))
8956 IN_LEV_FIELD(new_jx, new_jy) &&
8957 (Feld[new_jx][new_jy] == EL_SP_BASE ||
8958 Feld[new_jx][new_jy] == EL_SAND ||
8959 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8960 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
8961 /* !!! extend EL_SAND to anything diggable !!! */
8967 boolean player_is_standing_on_valid_field =
8968 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8969 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
8973 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
8974 player_can_fall_down,
8975 player_is_standing_on_valid_field,
8976 player_is_moving_to_valid_field,
8977 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
8978 player->effective_action,
8979 player->can_fall_into_acid);
8982 if (player_can_fall_down &&
8984 !player_is_standing_on_valid_field &&
8986 !player_is_moving_to_valid_field)
8989 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
8990 jx, jy, FrameCounter);
8993 player->programmed_action = MV_DOWN;
8998 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9001 return CheckGravityMovement(player);
9004 if (game.gravity && !player->programmed_action)
9006 int jx = player->jx, jy = player->jy;
9007 boolean field_under_player_is_free =
9008 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9009 boolean player_is_standing_on_valid_field =
9010 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9011 (IS_WALKABLE(Feld[jx][jy]) &&
9012 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9014 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9015 player->programmed_action = MV_DOWN;
9021 -----------------------------------------------------------------------------
9022 dx, dy: direction (non-diagonal) to try to move the player to
9023 real_dx, real_dy: direction as read from input device (can be diagonal)
9026 boolean MovePlayerOneStep(struct PlayerInfo *player,
9027 int dx, int dy, int real_dx, int real_dy)
9030 static int trigger_sides[4][2] =
9032 /* enter side leave side */
9033 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9034 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9035 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9036 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9038 int move_direction = (dx == -1 ? MV_LEFT :
9039 dx == +1 ? MV_RIGHT :
9041 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9042 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9043 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9045 int jx = player->jx, jy = player->jy;
9046 int new_jx = jx + dx, new_jy = jy + dy;
9050 if (!player->active || (!dx && !dy))
9051 return MF_NO_ACTION;
9053 player->MovDir = (dx < 0 ? MV_LEFT :
9056 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9058 if (!IN_LEV_FIELD(new_jx, new_jy))
9059 return MF_NO_ACTION;
9061 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9062 return MF_NO_ACTION;
9065 element = MovingOrBlocked2Element(new_jx, new_jy);
9067 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9070 if (DONT_RUN_INTO(element))
9072 if (element == EL_ACID && dx == 0 && dy == 1)
9074 SplashAcid(new_jx, new_jy);
9075 Feld[jx][jy] = EL_PLAYER_1;
9076 InitMovingField(jx, jy, MV_DOWN);
9077 Store[jx][jy] = EL_ACID;
9078 ContinueMoving(jx, jy);
9082 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9087 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9088 if (can_move != MF_MOVING)
9091 /* check if DigField() has caused relocation of the player */
9092 if (player->jx != jx || player->jy != jy)
9093 return MF_NO_ACTION;
9095 StorePlayer[jx][jy] = 0;
9096 player->last_jx = jx;
9097 player->last_jy = jy;
9098 player->jx = new_jx;
9099 player->jy = new_jy;
9100 StorePlayer[new_jx][new_jy] = player->element_nr;
9103 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9105 player->step_counter++;
9108 player->drop_delay = 0;
9111 PlayerVisit[jx][jy] = FrameCounter;
9113 ScrollPlayer(player, SCROLL_INIT);
9116 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9118 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9120 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9123 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9125 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9126 CE_OTHER_GETS_ENTERED, enter_side);
9127 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9128 CE_ENTERED_BY_PLAYER, enter_side);
9135 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9137 int jx = player->jx, jy = player->jy;
9138 int old_jx = jx, old_jy = jy;
9139 int moved = MF_NO_ACTION;
9142 if (!player->active)
9147 if (player->MovPos == 0)
9149 player->is_moving = FALSE;
9150 player->is_digging = FALSE;
9151 player->is_collecting = FALSE;
9152 player->is_snapping = FALSE;
9153 player->is_pushing = FALSE;
9159 if (!player->active || (!dx && !dy))
9164 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9170 if (!FrameReached(&player->move_delay, player->move_delay_value))
9173 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9174 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9180 /* store if player is automatically moved to next field */
9181 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9183 /* remove the last programmed player action */
9184 player->programmed_action = 0;
9188 /* should only happen if pre-1.2 tape recordings are played */
9189 /* this is only for backward compatibility */
9191 int original_move_delay_value = player->move_delay_value;
9194 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9198 /* scroll remaining steps with finest movement resolution */
9199 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9201 while (player->MovPos)
9203 ScrollPlayer(player, SCROLL_GO_ON);
9204 ScrollScreen(NULL, SCROLL_GO_ON);
9210 player->move_delay_value = original_move_delay_value;
9213 if (player->last_move_dir & MV_HORIZONTAL)
9215 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9216 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9220 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9221 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9227 if (moved & MF_MOVING && !ScreenMovPos &&
9228 (player == local_player || !options.network))
9230 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9231 int offset = (setup.scroll_delay ? 3 : 0);
9233 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9235 /* actual player has left the screen -- scroll in that direction */
9236 if (jx != old_jx) /* player has moved horizontally */
9237 scroll_x += (jx - old_jx);
9238 else /* player has moved vertically */
9239 scroll_y += (jy - old_jy);
9243 if (jx != old_jx) /* player has moved horizontally */
9245 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9246 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9247 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9249 /* don't scroll over playfield boundaries */
9250 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9251 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9253 /* don't scroll more than one field at a time */
9254 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9256 /* don't scroll against the player's moving direction */
9257 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9258 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9259 scroll_x = old_scroll_x;
9261 else /* player has moved vertically */
9263 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9264 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9265 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9267 /* don't scroll over playfield boundaries */
9268 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9269 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9271 /* don't scroll more than one field at a time */
9272 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9274 /* don't scroll against the player's moving direction */
9275 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9276 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9277 scroll_y = old_scroll_y;
9281 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9283 if (!options.network && !AllPlayersInVisibleScreen())
9285 scroll_x = old_scroll_x;
9286 scroll_y = old_scroll_y;
9290 ScrollScreen(player, SCROLL_INIT);
9291 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9298 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9300 if (!(moved & MF_MOVING) && !player->is_pushing)
9305 player->StepFrame = 0;
9307 if (moved & MF_MOVING)
9309 if (old_jx != jx && old_jy == jy)
9310 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9311 else if (old_jx == jx && old_jy != jy)
9312 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9314 DrawLevelField(jx, jy); /* for "crumbled sand" */
9316 player->last_move_dir = player->MovDir;
9317 player->is_moving = TRUE;
9319 player->is_snapping = FALSE;
9323 player->is_switching = FALSE;
9326 player->is_dropping = FALSE;
9330 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9333 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9336 static int trigger_sides[4][2] =
9338 /* enter side leave side */
9339 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9340 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9341 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9342 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9344 int move_direction = player->MovDir;
9345 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9346 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9349 /* !!! TEST ONLY !!! */
9350 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9351 CheckElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9353 player->index_bit, leave_side);
9355 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9357 player->index_bit, leave_side);
9359 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9360 CheckElementChangeByPlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9361 player->index_bit, enter_side);
9363 CheckTriggeredElementChangeByPlayer(jx, jy, Feld[jx][jy],
9364 CE_OTHER_GETS_ENTERED,
9365 player->index_bit, enter_side);
9375 CheckGravityMovementWhenNotMoving(player);
9378 player->last_move_dir = MV_NO_MOVING;
9380 player->is_moving = FALSE;
9383 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9385 TestIfHeroTouchesBadThing(jx, jy);
9386 TestIfPlayerTouchesCustomElement(jx, jy);
9389 if (!player->active)
9395 void ScrollPlayer(struct PlayerInfo *player, int mode)
9397 int jx = player->jx, jy = player->jy;
9398 int last_jx = player->last_jx, last_jy = player->last_jy;
9399 int move_stepsize = TILEX / player->move_delay_value;
9401 if (!player->active || !player->MovPos)
9404 if (mode == SCROLL_INIT)
9406 player->actual_frame_counter = FrameCounter;
9407 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9409 if (Feld[last_jx][last_jy] == EL_EMPTY)
9410 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9418 else if (!FrameReached(&player->actual_frame_counter, 1))
9421 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9422 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9424 if (!player->block_last_field &&
9425 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9426 Feld[last_jx][last_jy] = EL_EMPTY;
9428 /* before DrawPlayer() to draw correct player graphic for this case */
9429 if (player->MovPos == 0)
9430 CheckGravityMovement(player);
9433 DrawPlayer(player); /* needed here only to cleanup last field */
9436 if (player->MovPos == 0) /* player reached destination field */
9439 if (player->move_delay_reset_counter > 0)
9441 player->move_delay_reset_counter--;
9443 if (player->move_delay_reset_counter == 0)
9445 /* continue with normal speed after quickly moving through gate */
9446 HALVE_PLAYER_SPEED(player);
9448 /* be able to make the next move without delay */
9449 player->move_delay = 0;
9453 if (IS_PASSABLE(Feld[last_jx][last_jy]))
9455 /* continue with normal speed after quickly moving through gate */
9456 HALVE_PLAYER_SPEED(player);
9458 /* be able to make the next move without delay */
9459 player->move_delay = 0;
9463 if (player->block_last_field &&
9464 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9465 Feld[last_jx][last_jy] = EL_EMPTY;
9467 player->last_jx = jx;
9468 player->last_jy = jy;
9470 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9471 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9472 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9474 DrawPlayer(player); /* needed here only to cleanup last field */
9477 if (local_player->friends_still_needed == 0 ||
9478 IS_SP_ELEMENT(Feld[jx][jy]))
9479 player->LevelSolved = player->GameOver = TRUE;
9483 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9484 /* this breaks one level: "machine", level 000 */
9486 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9489 static int trigger_sides[4][2] =
9491 /* enter side leave side */
9492 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9493 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9494 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9495 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9497 int move_direction = player->MovDir;
9498 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9499 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9500 int old_jx = last_jx;
9501 int old_jy = last_jy;
9504 /* !!! TEST ONLY !!! */
9505 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9506 CheckElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9508 player->index_bit, leave_side);
9510 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9512 player->index_bit, leave_side);
9514 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9515 CheckElementChangeByPlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9516 player->index_bit, enter_side);
9518 CheckTriggeredElementChangeByPlayer(jx, jy, Feld[jx][jy],
9519 CE_OTHER_GETS_ENTERED,
9520 player->index_bit, enter_side);
9526 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9528 TestIfHeroTouchesBadThing(jx, jy);
9529 TestIfPlayerTouchesCustomElement(jx, jy);
9531 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9534 if (!player->active)
9538 if (level.use_step_counter)
9544 for (i = 0; i < MAX_PLAYERS; i++)
9546 struct PlayerInfo *player = &stored_player[i];
9548 if (SHIELD_ON(player))
9550 player->shield_normal_time_left--;
9552 if (player->shield_deadly_time_left > 0)
9553 player->shield_deadly_time_left--;
9561 if (TimeLeft <= 10 && setup.time_limit)
9562 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9564 DrawGameValue_Time(TimeLeft);
9566 if (!TimeLeft && setup.time_limit)
9567 for (i = 0; i < MAX_PLAYERS; i++)
9568 KillHero(&stored_player[i]);
9570 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9571 DrawGameValue_Time(TimePlayed);
9574 if (tape.single_step && tape.recording && !tape.pausing &&
9575 !player->programmed_action)
9576 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9580 void ScrollScreen(struct PlayerInfo *player, int mode)
9582 static unsigned long screen_frame_counter = 0;
9584 if (mode == SCROLL_INIT)
9586 /* set scrolling step size according to actual player's moving speed */
9587 ScrollStepSize = TILEX / player->move_delay_value;
9589 screen_frame_counter = FrameCounter;
9590 ScreenMovDir = player->MovDir;
9591 ScreenMovPos = player->MovPos;
9592 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9595 else if (!FrameReached(&screen_frame_counter, 1))
9600 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9601 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9602 redraw_mask |= REDRAW_FIELD;
9605 ScreenMovDir = MV_NO_MOVING;
9608 void TestIfPlayerTouchesCustomElement(int x, int y)
9610 static int xy[4][2] =
9617 static int trigger_sides[4][2] =
9619 /* center side border side */
9620 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9621 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9622 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9623 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9625 static int touch_dir[4] =
9632 int center_element = Feld[x][y]; /* should always be non-moving! */
9635 for (i = 0; i < NUM_DIRECTIONS; i++)
9637 int xx = x + xy[i][0];
9638 int yy = y + xy[i][1];
9639 int center_side = trigger_sides[i][0];
9640 int border_side = trigger_sides[i][1];
9643 if (!IN_LEV_FIELD(xx, yy))
9646 if (IS_PLAYER(x, y))
9648 struct PlayerInfo *player = PLAYERINFO(x, y);
9650 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9651 border_element = Feld[xx][yy]; /* may be moving! */
9652 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9653 border_element = Feld[xx][yy];
9654 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9655 border_element = MovingOrBlocked2Element(xx, yy);
9657 continue; /* center and border element do not touch */
9660 /* !!! TEST ONLY !!! */
9661 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9662 player->index_bit, border_side);
9663 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9664 CE_OTHER_GETS_TOUCHED,
9665 player->index_bit, border_side);
9667 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9668 CE_OTHER_GETS_TOUCHED,
9669 player->index_bit, border_side);
9670 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9671 player->index_bit, border_side);
9674 else if (IS_PLAYER(xx, yy))
9676 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9678 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9680 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9681 continue; /* center and border element do not touch */
9685 /* !!! TEST ONLY !!! */
9686 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9687 player->index_bit, center_side);
9688 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9689 CE_OTHER_GETS_TOUCHED,
9690 player->index_bit, center_side);
9692 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9693 CE_OTHER_GETS_TOUCHED,
9694 player->index_bit, center_side);
9695 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9696 player->index_bit, center_side);
9704 void TestIfElementTouchesCustomElement(int x, int y)
9706 static int xy[4][2] =
9713 static int trigger_sides[4][2] =
9715 /* center side border side */
9716 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9717 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9718 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9719 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9721 static int touch_dir[4] =
9728 boolean change_center_element = FALSE;
9729 int center_element_change_page = 0;
9730 int center_element = Feld[x][y]; /* should always be non-moving! */
9731 int border_trigger_element;
9734 for (i = 0; i < NUM_DIRECTIONS; i++)
9736 int xx = x + xy[i][0];
9737 int yy = y + xy[i][1];
9738 int center_side = trigger_sides[i][0];
9739 int border_side = trigger_sides[i][1];
9742 if (!IN_LEV_FIELD(xx, yy))
9745 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9746 border_element = Feld[xx][yy]; /* may be moving! */
9747 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9748 border_element = Feld[xx][yy];
9749 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9750 border_element = MovingOrBlocked2Element(xx, yy);
9752 continue; /* center and border element do not touch */
9754 /* check for change of center element (but change it only once) */
9755 if (IS_CUSTOM_ELEMENT(center_element) &&
9756 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9757 !change_center_element)
9759 for (j = 0; j < element_info[center_element].num_change_pages; j++)
9761 struct ElementChangeInfo *change =
9762 &element_info[center_element].change_page[j];
9764 if (change->can_change &&
9765 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9766 change->trigger_side & border_side &&
9768 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9770 change->trigger_element == border_element
9774 change_center_element = TRUE;
9775 center_element_change_page = j;
9776 border_trigger_element = border_element;
9783 /* check for change of border element */
9784 if (IS_CUSTOM_ELEMENT(border_element) &&
9785 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9787 for (j = 0; j < element_info[border_element].num_change_pages; j++)
9789 struct ElementChangeInfo *change =
9790 &element_info[border_element].change_page[j];
9792 if (change->can_change &&
9793 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9794 change->trigger_side & center_side &&
9796 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9798 change->trigger_element == center_element
9803 printf("::: border_element %d, %d\n", x, y);
9806 CheckElementChangeByPage(xx, yy, border_element, center_element,
9807 CE_OTHER_IS_TOUCHING, j);
9814 if (change_center_element)
9817 printf("::: center_element %d, %d\n", x, y);
9820 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
9821 CE_OTHER_IS_TOUCHING, center_element_change_page);
9825 void TestIfElementHitsCustomElement(int x, int y, int direction)
9827 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9828 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9829 int hitx = x + dx, hity = y + dy;
9830 int hitting_element = Feld[x][y];
9831 int touched_element;
9833 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9834 !IS_FREE(hitx, hity) &&
9835 (!IS_MOVING(hitx, hity) ||
9836 MovDir[hitx][hity] != direction ||
9837 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9840 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9844 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9848 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9849 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9851 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9852 CE_HITTING_SOMETHING, direction);
9854 if (IN_LEV_FIELD(hitx, hity))
9856 int opposite_direction = MV_DIR_OPPOSITE(direction);
9857 int hitting_side = direction;
9858 int touched_side = opposite_direction;
9860 int touched_element = MovingOrBlocked2Element(hitx, hity);
9863 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9864 MovDir[hitx][hity] != direction ||
9865 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9874 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9875 CE_HIT_BY_SOMETHING, opposite_direction);
9877 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9878 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
9880 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9882 struct ElementChangeInfo *change =
9883 &element_info[hitting_element].change_page[i];
9885 if (change->can_change &&
9886 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
9887 change->trigger_side & touched_side &&
9890 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9892 change->trigger_element == touched_element
9896 CheckElementChangeByPage(x, y, hitting_element, touched_element,
9897 CE_OTHER_IS_HITTING, i);
9903 if (IS_CUSTOM_ELEMENT(touched_element) &&
9904 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
9906 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9908 struct ElementChangeInfo *change =
9909 &element_info[touched_element].change_page[i];
9911 if (change->can_change &&
9912 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
9913 change->trigger_side & hitting_side &&
9915 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9917 change->trigger_element == hitting_element
9921 CheckElementChangeByPage(hitx, hity, touched_element,
9922 hitting_element, CE_OTHER_GETS_HIT, i);
9932 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9934 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9935 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9936 int hitx = x + dx, hity = y + dy;
9937 int hitting_element = Feld[x][y];
9938 int touched_element;
9940 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9941 !IS_FREE(hitx, hity) &&
9942 (!IS_MOVING(hitx, hity) ||
9943 MovDir[hitx][hity] != direction ||
9944 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9947 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9951 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9955 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9956 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9958 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9959 EP_CAN_SMASH_EVERYTHING, direction);
9961 if (IN_LEV_FIELD(hitx, hity))
9963 int opposite_direction = MV_DIR_OPPOSITE(direction);
9964 int hitting_side = direction;
9965 int touched_side = opposite_direction;
9967 int touched_element = MovingOrBlocked2Element(hitx, hity);
9970 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9971 MovDir[hitx][hity] != direction ||
9972 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9981 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9982 CE_SMASHED_BY_SOMETHING, opposite_direction);
9984 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9985 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
9987 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9989 struct ElementChangeInfo *change =
9990 &element_info[hitting_element].change_page[i];
9992 if (change->can_change &&
9993 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
9994 change->trigger_side & touched_side &&
9997 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9999 change->trigger_element == touched_element
10003 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10004 CE_OTHER_IS_SMASHING, i);
10010 if (IS_CUSTOM_ELEMENT(touched_element) &&
10011 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10013 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10015 struct ElementChangeInfo *change =
10016 &element_info[touched_element].change_page[i];
10018 if (change->can_change &&
10019 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10020 change->trigger_side & hitting_side &&
10022 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10024 change->trigger_element == hitting_element
10028 CheckElementChangeByPage(hitx, hity, touched_element,
10029 hitting_element, CE_OTHER_GETS_SMASHED,i);
10039 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10041 int i, kill_x = -1, kill_y = -1;
10042 int bad_element = -1;
10043 static int test_xy[4][2] =
10050 static int test_dir[4] =
10058 for (i = 0; i < NUM_DIRECTIONS; i++)
10060 int test_x, test_y, test_move_dir, test_element;
10062 test_x = good_x + test_xy[i][0];
10063 test_y = good_y + test_xy[i][1];
10065 if (!IN_LEV_FIELD(test_x, test_y))
10069 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10072 test_element = Feld[test_x][test_y];
10074 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10077 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10078 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10080 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10081 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10085 bad_element = test_element;
10091 if (kill_x != -1 || kill_y != -1)
10093 if (IS_PLAYER(good_x, good_y))
10095 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10098 if (player->shield_deadly_time_left > 0 &&
10099 !IS_INDESTRUCTIBLE(bad_element))
10100 Bang(kill_x, kill_y);
10101 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10104 if (player->shield_deadly_time_left > 0)
10105 Bang(kill_x, kill_y);
10106 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10111 Bang(good_x, good_y);
10115 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10117 int i, kill_x = -1, kill_y = -1;
10118 int bad_element = Feld[bad_x][bad_y];
10119 static int test_xy[4][2] =
10126 static int touch_dir[4] =
10128 MV_LEFT | MV_RIGHT,
10133 static int test_dir[4] =
10141 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10144 for (i = 0; i < NUM_DIRECTIONS; i++)
10146 int test_x, test_y, test_move_dir, test_element;
10148 test_x = bad_x + test_xy[i][0];
10149 test_y = bad_y + test_xy[i][1];
10150 if (!IN_LEV_FIELD(test_x, test_y))
10154 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10156 test_element = Feld[test_x][test_y];
10158 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10159 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10161 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10162 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10164 /* good thing is player or penguin that does not move away */
10165 if (IS_PLAYER(test_x, test_y))
10167 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10169 if (bad_element == EL_ROBOT && player->is_moving)
10170 continue; /* robot does not kill player if he is moving */
10172 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10174 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10175 continue; /* center and border element do not touch */
10182 else if (test_element == EL_PENGUIN)
10191 if (kill_x != -1 || kill_y != -1)
10193 if (IS_PLAYER(kill_x, kill_y))
10195 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10198 if (player->shield_deadly_time_left > 0 &&
10199 !IS_INDESTRUCTIBLE(bad_element))
10200 Bang(bad_x, bad_y);
10201 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10204 if (player->shield_deadly_time_left > 0)
10205 Bang(bad_x, bad_y);
10206 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10211 Bang(kill_x, kill_y);
10215 void TestIfHeroTouchesBadThing(int x, int y)
10217 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10220 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10222 TestIfGoodThingHitsBadThing(x, y, move_dir);
10225 void TestIfBadThingTouchesHero(int x, int y)
10227 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10230 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10232 TestIfBadThingHitsGoodThing(x, y, move_dir);
10235 void TestIfFriendTouchesBadThing(int x, int y)
10237 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10240 void TestIfBadThingTouchesFriend(int x, int y)
10242 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10245 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10247 int i, kill_x = bad_x, kill_y = bad_y;
10248 static int xy[4][2] =
10256 for (i = 0; i < NUM_DIRECTIONS; i++)
10260 x = bad_x + xy[i][0];
10261 y = bad_y + xy[i][1];
10262 if (!IN_LEV_FIELD(x, y))
10265 element = Feld[x][y];
10266 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10267 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10275 if (kill_x != bad_x || kill_y != bad_y)
10276 Bang(bad_x, bad_y);
10279 void KillHero(struct PlayerInfo *player)
10281 int jx = player->jx, jy = player->jy;
10283 if (!player->active)
10286 /* remove accessible field at the player's position */
10287 Feld[jx][jy] = EL_EMPTY;
10289 /* deactivate shield (else Bang()/Explode() would not work right) */
10290 player->shield_normal_time_left = 0;
10291 player->shield_deadly_time_left = 0;
10297 static void KillHeroUnlessEnemyProtected(int x, int y)
10299 if (!PLAYER_ENEMY_PROTECTED(x, y))
10300 KillHero(PLAYERINFO(x, y));
10303 static void KillHeroUnlessExplosionProtected(int x, int y)
10305 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10306 KillHero(PLAYERINFO(x, y));
10309 void BuryHero(struct PlayerInfo *player)
10311 int jx = player->jx, jy = player->jy;
10313 if (!player->active)
10317 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10319 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10321 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10323 player->GameOver = TRUE;
10324 RemoveHero(player);
10327 void RemoveHero(struct PlayerInfo *player)
10329 int jx = player->jx, jy = player->jy;
10330 int i, found = FALSE;
10332 player->present = FALSE;
10333 player->active = FALSE;
10335 if (!ExplodeField[jx][jy])
10336 StorePlayer[jx][jy] = 0;
10338 for (i = 0; i < MAX_PLAYERS; i++)
10339 if (stored_player[i].active)
10343 AllPlayersGone = TRUE;
10350 =============================================================================
10351 checkDiagonalPushing()
10352 -----------------------------------------------------------------------------
10353 check if diagonal input device direction results in pushing of object
10354 (by checking if the alternative direction is walkable, diggable, ...)
10355 =============================================================================
10358 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10359 int x, int y, int real_dx, int real_dy)
10361 int jx, jy, dx, dy, xx, yy;
10363 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10366 /* diagonal direction: check alternative direction */
10371 xx = jx + (dx == 0 ? real_dx : 0);
10372 yy = jy + (dy == 0 ? real_dy : 0);
10374 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10378 =============================================================================
10380 -----------------------------------------------------------------------------
10381 x, y: field next to player (non-diagonal) to try to dig to
10382 real_dx, real_dy: direction as read from input device (can be diagonal)
10383 =============================================================================
10386 int DigField(struct PlayerInfo *player,
10387 int oldx, int oldy, int x, int y,
10388 int real_dx, int real_dy, int mode)
10390 static int trigger_sides[4] =
10392 CH_SIDE_RIGHT, /* moving left */
10393 CH_SIDE_LEFT, /* moving right */
10394 CH_SIDE_BOTTOM, /* moving up */
10395 CH_SIDE_TOP, /* moving down */
10398 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10400 int jx = oldx, jy = oldy;
10401 int dx = x - jx, dy = y - jy;
10402 int nextx = x + dx, nexty = y + dy;
10403 int move_direction = (dx == -1 ? MV_LEFT :
10404 dx == +1 ? MV_RIGHT :
10406 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10407 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10408 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10409 int old_element = Feld[jx][jy];
10412 if (player->MovPos == 0)
10414 player->is_digging = FALSE;
10415 player->is_collecting = FALSE;
10418 if (player->MovPos == 0) /* last pushing move finished */
10419 player->is_pushing = FALSE;
10421 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10423 player->is_switching = FALSE;
10424 player->push_delay = 0;
10426 return MF_NO_ACTION;
10429 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10430 return MF_NO_ACTION;
10435 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10437 if (IS_TUBE(Feld[jx][jy]) ||
10438 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10442 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10443 int tube_leave_directions[][2] =
10445 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10446 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10447 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10448 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
10449 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
10450 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
10451 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
10452 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
10453 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
10454 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
10455 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
10456 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10459 while (tube_leave_directions[i][0] != tube_element)
10462 if (tube_leave_directions[i][0] == -1) /* should not happen */
10466 if (!(tube_leave_directions[i][1] & move_direction))
10467 return MF_NO_ACTION; /* tube has no opening in this direction */
10472 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10473 old_element = Back[jx][jy];
10477 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10478 return MF_NO_ACTION; /* field has no opening in this direction */
10480 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10481 return MF_NO_ACTION; /* field has no opening in this direction */
10483 element = Feld[x][y];
10485 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10486 game.engine_version >= VERSION_IDENT(2,2,0,0))
10487 return MF_NO_ACTION;
10492 case EL_SP_PORT_LEFT:
10493 case EL_SP_PORT_RIGHT:
10494 case EL_SP_PORT_UP:
10495 case EL_SP_PORT_DOWN:
10496 case EL_SP_PORT_HORIZONTAL:
10497 case EL_SP_PORT_VERTICAL:
10498 case EL_SP_PORT_ANY:
10499 case EL_SP_GRAVITY_PORT_LEFT:
10500 case EL_SP_GRAVITY_PORT_RIGHT:
10501 case EL_SP_GRAVITY_PORT_UP:
10502 case EL_SP_GRAVITY_PORT_DOWN:
10504 if (!canEnterSupaplexPort(x, y, dx, dy))
10505 return MF_NO_ACTION;
10508 element != EL_SP_PORT_LEFT &&
10509 element != EL_SP_GRAVITY_PORT_LEFT &&
10510 element != EL_SP_PORT_HORIZONTAL &&
10511 element != EL_SP_PORT_ANY) ||
10513 element != EL_SP_PORT_RIGHT &&
10514 element != EL_SP_GRAVITY_PORT_RIGHT &&
10515 element != EL_SP_PORT_HORIZONTAL &&
10516 element != EL_SP_PORT_ANY) ||
10518 element != EL_SP_PORT_UP &&
10519 element != EL_SP_GRAVITY_PORT_UP &&
10520 element != EL_SP_PORT_VERTICAL &&
10521 element != EL_SP_PORT_ANY) ||
10523 element != EL_SP_PORT_DOWN &&
10524 element != EL_SP_GRAVITY_PORT_DOWN &&
10525 element != EL_SP_PORT_VERTICAL &&
10526 element != EL_SP_PORT_ANY) ||
10527 !IN_LEV_FIELD(nextx, nexty) ||
10528 !IS_FREE(nextx, nexty))
10529 return MF_NO_ACTION;
10532 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10533 element == EL_SP_GRAVITY_PORT_RIGHT ||
10534 element == EL_SP_GRAVITY_PORT_UP ||
10535 element == EL_SP_GRAVITY_PORT_DOWN)
10536 game.gravity = !game.gravity;
10538 /* automatically move to the next field with double speed */
10539 player->programmed_action = move_direction;
10541 if (player->move_delay_reset_counter == 0)
10543 player->move_delay_reset_counter = 2; /* two double speed steps */
10545 DOUBLE_PLAYER_SPEED(player);
10548 player->move_delay_reset_counter = 2;
10550 DOUBLE_PLAYER_SPEED(player);
10554 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10557 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10563 case EL_TUBE_VERTICAL:
10564 case EL_TUBE_HORIZONTAL:
10565 case EL_TUBE_VERTICAL_LEFT:
10566 case EL_TUBE_VERTICAL_RIGHT:
10567 case EL_TUBE_HORIZONTAL_UP:
10568 case EL_TUBE_HORIZONTAL_DOWN:
10569 case EL_TUBE_LEFT_UP:
10570 case EL_TUBE_LEFT_DOWN:
10571 case EL_TUBE_RIGHT_UP:
10572 case EL_TUBE_RIGHT_DOWN:
10575 int tube_enter_directions[][2] =
10577 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10578 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10579 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10580 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
10581 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
10582 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
10583 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
10584 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
10585 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
10586 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
10587 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
10588 { -1, MV_NO_MOVING }
10591 while (tube_enter_directions[i][0] != element)
10594 if (tube_enter_directions[i][0] == -1) /* should not happen */
10598 if (!(tube_enter_directions[i][1] & move_direction))
10599 return MF_NO_ACTION; /* tube has no opening in this direction */
10601 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10609 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10611 if (IS_WALKABLE(element))
10614 int sound_action = ACTION_WALKING;
10617 if (!ACCESS_FROM(element, opposite_direction))
10618 return MF_NO_ACTION; /* field not accessible from this direction */
10622 if (element == EL_EMPTY_SPACE &&
10623 game.gravity && !player->is_auto_moving &&
10624 canFallDown(player) && move_direction != MV_DOWN)
10625 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10628 if (IS_GATE(element))
10630 if (!player->key[element - EL_GATE_1])
10631 return MF_NO_ACTION;
10633 else if (IS_GATE_GRAY(element))
10635 if (!player->key[element - EL_GATE_1_GRAY])
10636 return MF_NO_ACTION;
10638 else if (element == EL_EXIT_OPEN ||
10639 element == EL_SP_EXIT_OPEN ||
10640 element == EL_SP_EXIT_OPENING)
10642 sound_action = ACTION_PASSING; /* player is passing exit */
10644 else if (element == EL_EMPTY)
10646 sound_action = ACTION_MOVING; /* nothing to walk on */
10649 /* play sound from background or player, whatever is available */
10650 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
10651 PlayLevelSoundElementAction(x, y, element, sound_action);
10653 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10658 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
10660 else if (IS_PASSABLE(element))
10664 if (!canPassField(x, y, move_direction))
10665 return MF_NO_ACTION;
10670 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
10671 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
10672 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
10673 return MF_NO_ACTION;
10675 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10676 return MF_NO_ACTION;
10681 if (!ACCESS_FROM(element, opposite_direction))
10682 return MF_NO_ACTION; /* field not accessible from this direction */
10684 if (IS_CUSTOM_ELEMENT(element) &&
10685 !ACCESS_FROM(element, opposite_direction))
10686 return MF_NO_ACTION; /* field not accessible from this direction */
10690 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10691 return MF_NO_ACTION;
10696 if (IS_EM_GATE(element))
10698 if (!player->key[element - EL_EM_GATE_1])
10699 return MF_NO_ACTION;
10701 else if (IS_EM_GATE_GRAY(element))
10703 if (!player->key[element - EL_EM_GATE_1_GRAY])
10704 return MF_NO_ACTION;
10706 else if (IS_SP_PORT(element))
10708 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10709 element == EL_SP_GRAVITY_PORT_RIGHT ||
10710 element == EL_SP_GRAVITY_PORT_UP ||
10711 element == EL_SP_GRAVITY_PORT_DOWN)
10712 game.gravity = !game.gravity;
10715 /* automatically move to the next field with double speed */
10716 player->programmed_action = move_direction;
10718 if (player->move_delay_reset_counter == 0)
10720 player->move_delay_reset_counter = 2; /* two double speed steps */
10722 DOUBLE_PLAYER_SPEED(player);
10725 player->move_delay_reset_counter = 2;
10727 DOUBLE_PLAYER_SPEED(player);
10730 PlayLevelSoundAction(x, y, ACTION_PASSING);
10734 else if (IS_DIGGABLE(element))
10738 if (mode != DF_SNAP)
10741 GfxElement[x][y] = GFX_ELEMENT(element);
10744 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
10746 player->is_digging = TRUE;
10749 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10751 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
10752 player->index_bit, dig_side);
10755 if (mode == DF_SNAP)
10756 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10761 else if (IS_COLLECTIBLE(element))
10765 if (mode != DF_SNAP)
10767 GfxElement[x][y] = element;
10768 player->is_collecting = TRUE;
10771 if (element == EL_SPEED_PILL)
10772 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10773 else if (element == EL_EXTRA_TIME && level.time > 0)
10776 DrawGameValue_Time(TimeLeft);
10778 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10780 player->shield_normal_time_left += 10;
10781 if (element == EL_SHIELD_DEADLY)
10782 player->shield_deadly_time_left += 10;
10784 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10786 if (player->inventory_size < MAX_INVENTORY_SIZE)
10787 player->inventory_element[player->inventory_size++] = element;
10789 DrawGameValue_Dynamite(local_player->inventory_size);
10791 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10793 player->dynabomb_count++;
10794 player->dynabombs_left++;
10796 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10798 player->dynabomb_size++;
10800 else if (element == EL_DYNABOMB_INCREASE_POWER)
10802 player->dynabomb_xl = TRUE;
10804 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
10805 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
10807 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
10808 element - EL_KEY_1 : element - EL_EM_KEY_1);
10810 player->key[key_nr] = TRUE;
10812 DrawGameValue_Keys(player);
10814 redraw_mask |= REDRAW_DOOR_1;
10816 else if (IS_ENVELOPE(element))
10819 player->show_envelope = element;
10821 ShowEnvelope(element - EL_ENVELOPE_1);
10824 else if (IS_DROPPABLE(element) ||
10825 IS_THROWABLE(element)) /* can be collected and dropped */
10829 if (element_info[element].collect_count == 0)
10830 player->inventory_infinite_element = element;
10832 for (i = 0; i < element_info[element].collect_count; i++)
10833 if (player->inventory_size < MAX_INVENTORY_SIZE)
10834 player->inventory_element[player->inventory_size++] = element;
10836 DrawGameValue_Dynamite(local_player->inventory_size);
10838 else if (element_info[element].collect_count > 0)
10840 local_player->gems_still_needed -=
10841 element_info[element].collect_count;
10842 if (local_player->gems_still_needed < 0)
10843 local_player->gems_still_needed = 0;
10845 DrawGameValue_Emeralds(local_player->gems_still_needed);
10848 RaiseScoreElement(element);
10849 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10851 CheckTriggeredElementChangeByPlayer(x, y, element,
10852 CE_OTHER_GETS_COLLECTED,
10853 player->index_bit, dig_side);
10856 if (mode == DF_SNAP)
10857 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10862 else if (IS_PUSHABLE(element))
10864 if (mode == DF_SNAP && element != EL_BD_ROCK)
10865 return MF_NO_ACTION;
10867 if (CAN_FALL(element) && dy)
10868 return MF_NO_ACTION;
10870 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10871 !(element == EL_SPRING && level.use_spring_bug))
10872 return MF_NO_ACTION;
10875 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10876 ((move_direction & MV_VERTICAL &&
10877 ((element_info[element].move_pattern & MV_LEFT &&
10878 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10879 (element_info[element].move_pattern & MV_RIGHT &&
10880 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10881 (move_direction & MV_HORIZONTAL &&
10882 ((element_info[element].move_pattern & MV_UP &&
10883 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10884 (element_info[element].move_pattern & MV_DOWN &&
10885 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10886 return MF_NO_ACTION;
10890 /* do not push elements already moving away faster than player */
10891 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10892 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10893 return MF_NO_ACTION;
10895 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
10896 return MF_NO_ACTION;
10900 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10902 if (player->push_delay_value == -1)
10903 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10905 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10907 if (!player->is_pushing)
10908 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10912 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
10913 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
10914 !player_is_pushing))
10915 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10918 if (!player->is_pushing &&
10919 game.engine_version >= VERSION_IDENT(2,2,0,7))
10920 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10924 printf("::: push delay: %ld [%d, %d] [%d]\n",
10925 player->push_delay_value, FrameCounter, game.engine_version,
10926 player->is_pushing);
10929 player->is_pushing = TRUE;
10931 if (!(IN_LEV_FIELD(nextx, nexty) &&
10932 (IS_FREE(nextx, nexty) ||
10933 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10934 IS_SB_ELEMENT(element)))))
10935 return MF_NO_ACTION;
10937 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10938 return MF_NO_ACTION;
10940 if (player->push_delay == 0) /* new pushing; restart delay */
10941 player->push_delay = FrameCounter;
10943 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
10944 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10945 element != EL_SPRING && element != EL_BALLOON)
10947 /* make sure that there is no move delay before next try to push */
10948 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10949 player->move_delay = INITIAL_MOVE_DELAY_OFF;
10951 return MF_NO_ACTION;
10955 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
10958 if (IS_SB_ELEMENT(element))
10960 if (element == EL_SOKOBAN_FIELD_FULL)
10962 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10963 local_player->sokobanfields_still_needed++;
10966 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10968 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10969 local_player->sokobanfields_still_needed--;
10972 Feld[x][y] = EL_SOKOBAN_OBJECT;
10974 if (Back[x][y] == Back[nextx][nexty])
10975 PlayLevelSoundAction(x, y, ACTION_PUSHING);
10976 else if (Back[x][y] != 0)
10977 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10980 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10983 if (local_player->sokobanfields_still_needed == 0 &&
10984 game.emulation == EMU_SOKOBAN)
10986 player->LevelSolved = player->GameOver = TRUE;
10987 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10991 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10993 InitMovingField(x, y, move_direction);
10994 GfxAction[x][y] = ACTION_PUSHING;
10996 if (mode == DF_SNAP)
10997 ContinueMoving(x, y);
10999 MovPos[x][y] = (dx != 0 ? dx : dy);
11001 Pushed[x][y] = TRUE;
11002 Pushed[nextx][nexty] = TRUE;
11004 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11005 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11007 player->push_delay_value = -1; /* get new value later */
11010 /* check for element change _after_ element has been pushed! */
11014 /* !!! TEST ONLY !!! */
11015 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11016 player->index_bit, dig_side);
11017 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11018 player->index_bit, dig_side);
11020 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11021 player->index_bit, dig_side);
11022 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11023 player->index_bit, dig_side);
11029 else if (IS_SWITCHABLE(element))
11031 if (PLAYER_SWITCHING(player, x, y))
11034 player->is_switching = TRUE;
11035 player->switch_x = x;
11036 player->switch_y = y;
11038 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11040 if (element == EL_ROBOT_WHEEL)
11042 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11046 DrawLevelField(x, y);
11048 else if (element == EL_SP_TERMINAL)
11052 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11054 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11056 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11057 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11060 else if (IS_BELT_SWITCH(element))
11062 ToggleBeltSwitch(x, y);
11064 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11065 element == EL_SWITCHGATE_SWITCH_DOWN)
11067 ToggleSwitchgateSwitch(x, y);
11069 else if (element == EL_LIGHT_SWITCH ||
11070 element == EL_LIGHT_SWITCH_ACTIVE)
11072 ToggleLightSwitch(x, y);
11075 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11076 SND_LIGHT_SWITCH_ACTIVATING :
11077 SND_LIGHT_SWITCH_DEACTIVATING);
11080 else if (element == EL_TIMEGATE_SWITCH)
11082 ActivateTimegateSwitch(x, y);
11084 else if (element == EL_BALLOON_SWITCH_LEFT ||
11085 element == EL_BALLOON_SWITCH_RIGHT ||
11086 element == EL_BALLOON_SWITCH_UP ||
11087 element == EL_BALLOON_SWITCH_DOWN ||
11088 element == EL_BALLOON_SWITCH_ANY)
11090 if (element == EL_BALLOON_SWITCH_ANY)
11091 game.balloon_dir = move_direction;
11093 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11094 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11095 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11096 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11099 else if (element == EL_LAMP)
11101 Feld[x][y] = EL_LAMP_ACTIVE;
11102 local_player->lights_still_needed--;
11104 DrawLevelField(x, y);
11106 else if (element == EL_TIME_ORB_FULL)
11108 Feld[x][y] = EL_TIME_ORB_EMPTY;
11110 DrawGameValue_Time(TimeLeft);
11112 DrawLevelField(x, y);
11115 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11123 if (!PLAYER_SWITCHING(player, x, y))
11125 player->is_switching = TRUE;
11126 player->switch_x = x;
11127 player->switch_y = y;
11130 /* !!! TEST ONLY !!! */
11131 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11132 player->index_bit, dig_side);
11133 CheckTriggeredElementChangeByPlayer(x, y, element,
11134 CE_OTHER_IS_SWITCHING,
11135 player->index_bit, dig_side);
11137 CheckTriggeredElementChangeByPlayer(x, y, element,
11138 CE_OTHER_IS_SWITCHING,
11139 player->index_bit, dig_side);
11140 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11141 player->index_bit, dig_side);
11146 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11147 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11148 player->index_bit, dig_side);
11149 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11150 player->index_bit, dig_side);
11152 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11153 player->index_bit, dig_side);
11154 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11155 player->index_bit, dig_side);
11159 return MF_NO_ACTION;
11162 player->push_delay = 0;
11164 if (Feld[x][y] != element) /* really digged/collected something */
11165 player->is_collecting = !player->is_digging;
11170 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11172 int jx = player->jx, jy = player->jy;
11173 int x = jx + dx, y = jy + dy;
11174 int snap_direction = (dx == -1 ? MV_LEFT :
11175 dx == +1 ? MV_RIGHT :
11177 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11180 if (player->MovPos)
11183 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
11187 if (!player->active || !IN_LEV_FIELD(x, y))
11195 if (player->MovPos == 0)
11196 player->is_pushing = FALSE;
11198 player->is_snapping = FALSE;
11200 if (player->MovPos == 0)
11202 player->is_moving = FALSE;
11203 player->is_digging = FALSE;
11204 player->is_collecting = FALSE;
11210 if (player->is_snapping)
11213 player->MovDir = snap_direction;
11216 if (player->MovPos == 0)
11219 player->is_moving = FALSE;
11220 player->is_digging = FALSE;
11221 player->is_collecting = FALSE;
11224 player->is_dropping = FALSE;
11226 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11229 player->is_snapping = TRUE;
11232 if (player->MovPos == 0)
11235 player->is_moving = FALSE;
11236 player->is_digging = FALSE;
11237 player->is_collecting = FALSE;
11240 DrawLevelField(x, y);
11246 boolean DropElement(struct PlayerInfo *player)
11248 static int trigger_sides[4] =
11250 CH_SIDE_LEFT, /* dropping left */
11251 CH_SIDE_RIGHT, /* dropping right */
11252 CH_SIDE_TOP, /* dropping up */
11253 CH_SIDE_BOTTOM, /* dropping down */
11255 int old_element, new_element;
11256 int dropx = player->jx, dropy = player->jy;
11257 int drop_direction = player->MovDir;
11258 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11259 int drop_element = (player->inventory_size > 0 ?
11260 player->inventory_element[player->inventory_size - 1] :
11261 player->inventory_infinite_element != EL_UNDEFINED ?
11262 player->inventory_infinite_element :
11263 player->dynabombs_left > 0 ?
11264 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11267 if (IS_THROWABLE(drop_element))
11269 dropx += GET_DX_FROM_DIR(drop_direction);
11270 dropy += GET_DY_FROM_DIR(drop_direction);
11272 if (!IN_LEV_FIELD(dropx, dropy))
11276 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11277 new_element = drop_element; /* default: no change when dropping */
11279 /* check if player is active, not moving and ready to drop */
11280 if (!player->active || player->MovPos || player->drop_delay > 0)
11283 /* check if player has anything that can be dropped */
11285 if (new_element == EL_UNDEFINED)
11288 if (player->inventory_size == 0 &&
11289 player->inventory_infinite_element == EL_UNDEFINED &&
11290 player->dynabombs_left == 0)
11294 /* check if anything can be dropped at the current position */
11295 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11298 /* collected custom elements can only be dropped on empty fields */
11300 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11303 if (player->inventory_size > 0 &&
11304 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11305 && old_element != EL_EMPTY)
11309 if (old_element != EL_EMPTY)
11310 Back[dropx][dropy] = old_element; /* store old element on this field */
11312 ResetGfxAnimation(dropx, dropy);
11313 ResetRandomAnimationValue(dropx, dropy);
11315 if (player->inventory_size > 0 ||
11316 player->inventory_infinite_element != EL_UNDEFINED)
11318 if (player->inventory_size > 0)
11320 player->inventory_size--;
11323 new_element = player->inventory_element[player->inventory_size];
11326 DrawGameValue_Dynamite(local_player->inventory_size);
11328 if (new_element == EL_DYNAMITE)
11329 new_element = EL_DYNAMITE_ACTIVE;
11330 else if (new_element == EL_SP_DISK_RED)
11331 new_element = EL_SP_DISK_RED_ACTIVE;
11334 Feld[dropx][dropy] = new_element;
11336 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11337 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11338 el2img(Feld[dropx][dropy]), 0);
11340 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11343 /* needed if previous element just changed to "empty" in the last frame */
11344 Changed[dropx][dropy] = 0; /* allow another change */
11348 /* !!! TEST ONLY !!! */
11349 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11350 player->index_bit, drop_side);
11351 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11352 CE_OTHER_GETS_DROPPED,
11353 player->index_bit, drop_side);
11355 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11356 CE_OTHER_GETS_DROPPED,
11357 player->index_bit, drop_side);
11358 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11359 player->index_bit, drop_side);
11362 TestIfElementTouchesCustomElement(dropx, dropy);
11364 else /* player is dropping a dyna bomb */
11366 player->dynabombs_left--;
11369 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
11372 Feld[dropx][dropy] = new_element;
11374 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11375 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11376 el2img(Feld[dropx][dropy]), 0);
11378 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11385 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11388 InitField_WithBug1(dropx, dropy, FALSE);
11390 InitField(dropx, dropy, FALSE);
11391 if (CAN_MOVE(Feld[dropx][dropy]))
11392 InitMovDir(dropx, dropy);
11396 new_element = Feld[dropx][dropy]; /* element might have changed */
11398 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11399 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11402 int move_stepsize = element_info[new_element].move_stepsize;
11404 int move_direction, nextx, nexty;
11406 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11407 MovDir[dropx][dropy] = drop_direction;
11409 move_direction = MovDir[dropx][dropy];
11410 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11411 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11414 Changed[dropx][dropy] = 0; /* allow another change */
11415 CheckCollision[dropx][dropy] = 2;
11418 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
11421 WasJustMoving[dropx][dropy] = 3;
11424 InitMovingField(dropx, dropy, move_direction);
11425 ContinueMoving(dropx, dropy);
11432 Changed[dropx][dropy] = 0; /* allow another change */
11435 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
11437 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
11438 CE_HITTING_SOMETHING, move_direction);
11446 player->drop_delay = 2 * TILEX / move_stepsize + 1;
11451 player->drop_delay = 8 + 8 + 8;
11455 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11460 player->is_dropping = TRUE;
11466 /* ------------------------------------------------------------------------- */
11467 /* game sound playing functions */
11468 /* ------------------------------------------------------------------------- */
11470 static int *loop_sound_frame = NULL;
11471 static int *loop_sound_volume = NULL;
11473 void InitPlayLevelSound()
11475 int num_sounds = getSoundListSize();
11477 checked_free(loop_sound_frame);
11478 checked_free(loop_sound_volume);
11480 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11481 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11484 static void PlayLevelSound(int x, int y, int nr)
11486 int sx = SCREENX(x), sy = SCREENY(y);
11487 int volume, stereo_position;
11488 int max_distance = 8;
11489 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11491 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11492 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11495 if (!IN_LEV_FIELD(x, y) ||
11496 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11497 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11500 volume = SOUND_MAX_VOLUME;
11502 if (!IN_SCR_FIELD(sx, sy))
11504 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11505 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11507 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11510 stereo_position = (SOUND_MAX_LEFT +
11511 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11512 (SCR_FIELDX + 2 * max_distance));
11514 if (IS_LOOP_SOUND(nr))
11516 /* This assures that quieter loop sounds do not overwrite louder ones,
11517 while restarting sound volume comparison with each new game frame. */
11519 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11522 loop_sound_volume[nr] = volume;
11523 loop_sound_frame[nr] = FrameCounter;
11526 PlaySoundExt(nr, volume, stereo_position, type);
11529 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11531 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11532 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11533 y < LEVELY(BY1) ? LEVELY(BY1) :
11534 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11538 static void PlayLevelSoundAction(int x, int y, int action)
11540 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11543 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11545 int sound_effect = element_info[element].sound[action];
11547 if (sound_effect != SND_UNDEFINED)
11548 PlayLevelSound(x, y, sound_effect);
11551 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11554 int sound_effect = element_info[element].sound[action];
11556 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11557 PlayLevelSound(x, y, sound_effect);
11560 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11562 int sound_effect = element_info[Feld[x][y]].sound[action];
11564 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11565 PlayLevelSound(x, y, sound_effect);
11568 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11570 int sound_effect = element_info[Feld[x][y]].sound[action];
11572 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11573 StopSound(sound_effect);
11576 static void PlayLevelMusic()
11578 if (levelset.music[level_nr] != MUS_UNDEFINED)
11579 PlayMusic(levelset.music[level_nr]); /* from config file */
11581 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11584 void RaiseScore(int value)
11586 local_player->score += value;
11588 DrawGameValue_Score(local_player->score);
11591 void RaiseScoreElement(int element)
11596 case EL_BD_DIAMOND:
11597 case EL_EMERALD_YELLOW:
11598 case EL_EMERALD_RED:
11599 case EL_EMERALD_PURPLE:
11600 case EL_SP_INFOTRON:
11601 RaiseScore(level.score[SC_EMERALD]);
11604 RaiseScore(level.score[SC_DIAMOND]);
11607 RaiseScore(level.score[SC_CRYSTAL]);
11610 RaiseScore(level.score[SC_PEARL]);
11613 case EL_BD_BUTTERFLY:
11614 case EL_SP_ELECTRON:
11615 RaiseScore(level.score[SC_BUG]);
11618 case EL_BD_FIREFLY:
11619 case EL_SP_SNIKSNAK:
11620 RaiseScore(level.score[SC_SPACESHIP]);
11623 case EL_DARK_YAMYAM:
11624 RaiseScore(level.score[SC_YAMYAM]);
11627 RaiseScore(level.score[SC_ROBOT]);
11630 RaiseScore(level.score[SC_PACMAN]);
11633 RaiseScore(level.score[SC_NUT]);
11636 case EL_SP_DISK_RED:
11637 case EL_DYNABOMB_INCREASE_NUMBER:
11638 case EL_DYNABOMB_INCREASE_SIZE:
11639 case EL_DYNABOMB_INCREASE_POWER:
11640 RaiseScore(level.score[SC_DYNAMITE]);
11642 case EL_SHIELD_NORMAL:
11643 case EL_SHIELD_DEADLY:
11644 RaiseScore(level.score[SC_SHIELD]);
11646 case EL_EXTRA_TIME:
11647 RaiseScore(level.score[SC_TIME_BONUS]);
11653 RaiseScore(level.score[SC_KEY]);
11656 RaiseScore(element_info[element].collect_score);
11661 void RequestQuitGame(boolean ask_if_really_quit)
11663 if (AllPlayersGone ||
11664 !ask_if_really_quit ||
11665 level_editor_test_game ||
11666 Request("Do you really want to quit the game ?",
11667 REQ_ASK | REQ_STAY_CLOSED))
11669 #if defined(PLATFORM_UNIX)
11670 if (options.network)
11671 SendToServer_StopPlaying();
11675 game_status = GAME_MODE_MAIN;
11683 if (tape.playing && tape.deactivate_display)
11684 TapeDeactivateDisplayOff(TRUE);
11687 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11690 if (tape.playing && tape.deactivate_display)
11691 TapeDeactivateDisplayOn();
11698 /* ---------- new game button stuff ---------------------------------------- */
11700 /* graphic position values for game buttons */
11701 #define GAME_BUTTON_XSIZE 30
11702 #define GAME_BUTTON_YSIZE 30
11703 #define GAME_BUTTON_XPOS 5
11704 #define GAME_BUTTON_YPOS 215
11705 #define SOUND_BUTTON_XPOS 5
11706 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11708 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11709 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11710 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11711 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11712 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11713 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11720 } gamebutton_info[NUM_GAME_BUTTONS] =
11723 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11728 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11729 GAME_CTRL_ID_PAUSE,
11733 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11738 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11739 SOUND_CTRL_ID_MUSIC,
11740 "background music on/off"
11743 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11744 SOUND_CTRL_ID_LOOPS,
11745 "sound loops on/off"
11748 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11749 SOUND_CTRL_ID_SIMPLE,
11750 "normal sounds on/off"
11754 void CreateGameButtons()
11758 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11760 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11761 struct GadgetInfo *gi;
11764 unsigned long event_mask;
11765 int gd_xoffset, gd_yoffset;
11766 int gd_x1, gd_x2, gd_y1, gd_y2;
11769 gd_xoffset = gamebutton_info[i].x;
11770 gd_yoffset = gamebutton_info[i].y;
11771 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11772 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11774 if (id == GAME_CTRL_ID_STOP ||
11775 id == GAME_CTRL_ID_PAUSE ||
11776 id == GAME_CTRL_ID_PLAY)
11778 button_type = GD_TYPE_NORMAL_BUTTON;
11780 event_mask = GD_EVENT_RELEASED;
11781 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11782 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11786 button_type = GD_TYPE_CHECK_BUTTON;
11788 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11789 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11790 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11791 event_mask = GD_EVENT_PRESSED;
11792 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11793 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11796 gi = CreateGadget(GDI_CUSTOM_ID, id,
11797 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11798 GDI_X, DX + gd_xoffset,
11799 GDI_Y, DY + gd_yoffset,
11800 GDI_WIDTH, GAME_BUTTON_XSIZE,
11801 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11802 GDI_TYPE, button_type,
11803 GDI_STATE, GD_BUTTON_UNPRESSED,
11804 GDI_CHECKED, checked,
11805 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11806 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11807 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11808 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11809 GDI_EVENT_MASK, event_mask,
11810 GDI_CALLBACK_ACTION, HandleGameButtons,
11814 Error(ERR_EXIT, "cannot create gadget");
11816 game_gadget[id] = gi;
11820 void FreeGameButtons()
11824 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11825 FreeGadget(game_gadget[i]);
11828 static void MapGameButtons()
11832 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11833 MapGadget(game_gadget[i]);
11836 void UnmapGameButtons()
11840 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11841 UnmapGadget(game_gadget[i]);
11844 static void HandleGameButtons(struct GadgetInfo *gi)
11846 int id = gi->custom_id;
11848 if (game_status != GAME_MODE_PLAYING)
11853 case GAME_CTRL_ID_STOP:
11854 RequestQuitGame(TRUE);
11857 case GAME_CTRL_ID_PAUSE:
11858 if (options.network)
11860 #if defined(PLATFORM_UNIX)
11862 SendToServer_ContinuePlaying();
11864 SendToServer_PausePlaying();
11868 TapeTogglePause(TAPE_TOGGLE_MANUAL);
11871 case GAME_CTRL_ID_PLAY:
11874 #if defined(PLATFORM_UNIX)
11875 if (options.network)
11876 SendToServer_ContinuePlaying();
11880 tape.pausing = FALSE;
11881 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11886 case SOUND_CTRL_ID_MUSIC:
11887 if (setup.sound_music)
11889 setup.sound_music = FALSE;
11892 else if (audio.music_available)
11894 setup.sound = setup.sound_music = TRUE;
11896 SetAudioMode(setup.sound);
11902 case SOUND_CTRL_ID_LOOPS:
11903 if (setup.sound_loops)
11904 setup.sound_loops = FALSE;
11905 else if (audio.loops_available)
11907 setup.sound = setup.sound_loops = TRUE;
11908 SetAudioMode(setup.sound);
11912 case SOUND_CTRL_ID_SIMPLE:
11913 if (setup.sound_simple)
11914 setup.sound_simple = FALSE;
11915 else if (audio.sound_available)
11917 setup.sound = setup.sound_simple = TRUE;
11918 SetAudioMode(setup.sound);