1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
35 /* for MovePlayer() */
36 #define MF_NO_ACTION 0
40 /* for ScrollPlayer() */
42 #define SCROLL_GO_ON 1
45 #define EX_PHASE_START 0
46 #define EX_TYPE_NONE 0
47 #define EX_TYPE_NORMAL (1 << 0)
48 #define EX_TYPE_CENTER (1 << 1)
49 #define EX_TYPE_BORDER (1 << 2)
50 #define EX_TYPE_CROSS (1 << 3)
51 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
53 /* special positions in the game control window (relative to control window) */
56 #define XX_EMERALDS 29
57 #define YY_EMERALDS 54
58 #define XX_DYNAMITE 29
59 #define YY_DYNAMITE 89
68 /* special positions in the game control window (relative to main window) */
69 #define DX_LEVEL (DX + XX_LEVEL)
70 #define DY_LEVEL (DY + YY_LEVEL)
71 #define DX_EMERALDS (DX + XX_EMERALDS)
72 #define DY_EMERALDS (DY + YY_EMERALDS)
73 #define DX_DYNAMITE (DX + XX_DYNAMITE)
74 #define DY_DYNAMITE (DY + YY_DYNAMITE)
75 #define DX_KEYS (DX + XX_KEYS)
76 #define DY_KEYS (DY + YY_KEYS)
77 #define DX_SCORE (DX + XX_SCORE)
78 #define DY_SCORE (DY + YY_SCORE)
79 #define DX_TIME1 (DX + XX_TIME1)
80 #define DX_TIME2 (DX + XX_TIME2)
81 #define DY_TIME (DY + YY_TIME)
83 /* values for initial player move delay (initial delay counter value) */
84 #define INITIAL_MOVE_DELAY_OFF -1
85 #define INITIAL_MOVE_DELAY_ON 0
87 /* values for player movement speed (which is in fact a delay value) */
88 #define MOVE_DELAY_NORMAL_SPEED 8
89 #define MOVE_DELAY_HIGH_SPEED 4
91 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
92 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
93 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
94 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
96 /* values for other actions */
97 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
99 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
101 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
102 RND(element_info[e].push_delay_random))
103 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
104 RND(element_info[e].drop_delay_random))
105 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
106 RND(element_info[e].move_delay_random))
107 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
108 (element_info[e].move_delay_random))
110 #define GET_TARGET_ELEMENT(e, ch) \
111 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
112 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
114 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
115 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
118 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
119 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
120 (CAN_MOVE_INTO_ACID(e) && \
121 Feld[x][y] == EL_ACID) || \
124 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
125 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
126 (CAN_MOVE_INTO_ACID(e) && \
127 Feld[x][y] == EL_ACID) || \
130 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
131 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
133 (CAN_MOVE_INTO_ACID(e) && \
134 Feld[x][y] == EL_ACID) || \
135 (DONT_COLLIDE_WITH(e) && \
137 !PLAYER_ENEMY_PROTECTED(x, y))))
140 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
141 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
143 (DONT_COLLIDE_WITH(e) && \
145 !PLAYER_ENEMY_PROTECTED(x, y))))
148 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
149 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
152 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
153 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
155 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
156 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
160 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
163 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
164 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
168 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
169 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
171 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
172 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
174 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
175 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
177 #define PIG_CAN_ENTER_FIELD(e, x, y) \
178 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
180 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
181 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
182 IS_FOOD_PENGUIN(Feld[x][y])))
183 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
184 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
186 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
187 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
189 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
194 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
195 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
196 (CAN_MOVE_INTO_ACID(e) && \
197 Feld[x][y] == EL_ACID) || \
198 Feld[x][y] == EL_DIAMOND))
200 #define DARK_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 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
206 #define PACMAN_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_AMOEBOID(Feld[x][y])))
212 #define PIG_CAN_ENTER_FIELD(e, x, y) \
213 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
214 (CAN_MOVE_INTO_ACID(e) && \
215 Feld[x][y] == EL_ACID) || \
216 IS_FOOD_PIG(Feld[x][y])))
218 #define PENGUIN_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_PENGUIN(Feld[x][y]) || \
223 Feld[x][y] == EL_EXIT_OPEN))
225 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
226 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
227 (CAN_MOVE_INTO_ACID(e) && \
228 Feld[x][y] == EL_ACID)))
230 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
231 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
232 (CAN_MOVE_INTO_ACID(e) && \
233 Feld[x][y] == EL_ACID) || \
236 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
237 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
238 (CAN_MOVE_INTO_ACID(e) && \
239 Feld[x][y] == EL_ACID)))
243 #define GROUP_NR(e) ((e) - EL_GROUP_START)
244 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
245 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
246 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
248 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
249 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
252 #define CE_ENTER_FIELD_COND(e, x, y) \
253 (!IS_PLAYER(x, y) && \
254 (Feld[x][y] == EL_ACID || \
255 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
257 #define CE_ENTER_FIELD_COND(e, x, y) \
258 (!IS_PLAYER(x, y) && \
259 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
262 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
263 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
265 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
266 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
268 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
269 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
270 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
271 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
273 /* game button identifiers */
274 #define GAME_CTRL_ID_STOP 0
275 #define GAME_CTRL_ID_PAUSE 1
276 #define GAME_CTRL_ID_PLAY 2
277 #define SOUND_CTRL_ID_MUSIC 3
278 #define SOUND_CTRL_ID_LOOPS 4
279 #define SOUND_CTRL_ID_SIMPLE 5
281 #define NUM_GAME_BUTTONS 6
284 /* forward declaration for internal use */
286 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
287 static boolean MovePlayer(struct PlayerInfo *, int, int);
288 static void ScrollPlayer(struct PlayerInfo *, int);
289 static void ScrollScreen(struct PlayerInfo *, int);
291 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
293 static void InitBeltMovement(void);
294 static void CloseAllOpenTimegates(void);
295 static void CheckGravityMovement(struct PlayerInfo *);
296 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
297 static void KillHeroUnlessEnemyProtected(int, int);
298 static void KillHeroUnlessExplosionProtected(int, int);
300 static void TestIfPlayerTouchesCustomElement(int, int);
301 static void TestIfElementTouchesCustomElement(int, int);
302 static void TestIfElementHitsCustomElement(int, int, int);
304 static void TestIfElementSmashesCustomElement(int, int, int);
307 static void ChangeElement(int, int, int);
309 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
310 #define CheckTriggeredElementChange(x, y, e, ev) \
311 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
313 #define CheckTriggeredElementChangePlayer(x, y, e, ev, p, s) \
314 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
315 #define CheckTriggeredElementChangeSide(x, y, e, ev, s) \
316 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
317 #define CheckTriggeredElementChangePage(x, y, e, ev, p) \
318 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
321 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
322 #define CheckElementChange(x, y, e, te, ev) \
323 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
324 #define CheckElementChangePlayer(x, y, e, ev, p, s) \
325 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
326 #define CheckElementChangeSide(x, y, e, te, ev, s) \
327 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
328 #define CheckElementChangePage(x, y, e, te, ev, p) \
329 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
331 static void PlayLevelSound(int, int, int);
332 static void PlayLevelSoundNearest(int, int, int);
333 static void PlayLevelSoundAction(int, int, int);
334 static void PlayLevelSoundElementAction(int, int, int, int);
335 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
336 static void PlayLevelSoundActionIfLoop(int, int, int);
337 static void StopLevelSoundActionIfLoop(int, int, int);
338 static void PlayLevelMusic();
340 static void MapGameButtons();
341 static void HandleGameButtons(struct GadgetInfo *);
343 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
346 /* ------------------------------------------------------------------------- */
347 /* definition of elements that automatically change to other elements after */
348 /* a specified time, eventually calling a function when changing */
349 /* ------------------------------------------------------------------------- */
351 /* forward declaration for changer functions */
352 static void InitBuggyBase(int x, int y);
353 static void WarnBuggyBase(int x, int y);
355 static void InitTrap(int x, int y);
356 static void ActivateTrap(int x, int y);
357 static void ChangeActiveTrap(int x, int y);
359 static void InitRobotWheel(int x, int y);
360 static void RunRobotWheel(int x, int y);
361 static void StopRobotWheel(int x, int y);
363 static void InitTimegateWheel(int x, int y);
364 static void RunTimegateWheel(int x, int y);
366 struct ChangingElementInfo
371 void (*pre_change_function)(int x, int y);
372 void (*change_function)(int x, int y);
373 void (*post_change_function)(int x, int y);
376 static struct ChangingElementInfo change_delay_list[] =
427 EL_SWITCHGATE_OPENING,
435 EL_SWITCHGATE_CLOSING,
436 EL_SWITCHGATE_CLOSED,
468 EL_ACID_SPLASH_RIGHT,
477 EL_SP_BUGGY_BASE_ACTIVATING,
484 EL_SP_BUGGY_BASE_ACTIVATING,
485 EL_SP_BUGGY_BASE_ACTIVE,
492 EL_SP_BUGGY_BASE_ACTIVE,
516 EL_ROBOT_WHEEL_ACTIVE,
524 EL_TIMEGATE_SWITCH_ACTIVE,
545 int push_delay_fixed, push_delay_random;
550 { EL_BALLOON, 0, 0 },
552 { EL_SOKOBAN_OBJECT, 2, 0 },
553 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
554 { EL_SATELLITE, 2, 0 },
555 { EL_SP_DISK_YELLOW, 2, 0 },
557 { EL_UNDEFINED, 0, 0 },
565 move_stepsize_list[] =
567 { EL_AMOEBA_DROP, 2 },
568 { EL_AMOEBA_DROPPING, 2 },
569 { EL_QUICKSAND_FILLING, 1 },
570 { EL_QUICKSAND_EMPTYING, 1 },
571 { EL_MAGIC_WALL_FILLING, 2 },
572 { EL_BD_MAGIC_WALL_FILLING, 2 },
573 { EL_MAGIC_WALL_EMPTYING, 2 },
574 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
584 collect_count_list[] =
587 { EL_BD_DIAMOND, 1 },
588 { EL_EMERALD_YELLOW, 1 },
589 { EL_EMERALD_RED, 1 },
590 { EL_EMERALD_PURPLE, 1 },
592 { EL_SP_INFOTRON, 1 },
604 access_direction_list[] =
606 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
607 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
608 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
609 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
610 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
611 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
612 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
613 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
614 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
615 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
616 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
618 { EL_SP_PORT_LEFT, MV_RIGHT },
619 { EL_SP_PORT_RIGHT, MV_LEFT },
620 { EL_SP_PORT_UP, MV_DOWN },
621 { EL_SP_PORT_DOWN, MV_UP },
622 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
623 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
624 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
625 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
626 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
627 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
628 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
630 { EL_UNDEFINED, MV_NO_MOVING }
633 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
635 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
636 CH_EVENT_BIT(CE_DELAY))
637 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
638 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
639 IS_JUST_CHANGING(x, y))
641 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
644 void GetPlayerConfig()
646 if (!audio.sound_available)
647 setup.sound_simple = FALSE;
649 if (!audio.loops_available)
650 setup.sound_loops = FALSE;
652 if (!audio.music_available)
653 setup.sound_music = FALSE;
655 if (!video.fullscreen_available)
656 setup.fullscreen = FALSE;
658 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
660 SetAudioMode(setup.sound);
664 static int getBeltNrFromBeltElement(int element)
666 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
667 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
668 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
671 static int getBeltNrFromBeltActiveElement(int element)
673 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
674 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
675 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
678 static int getBeltNrFromBeltSwitchElement(int element)
680 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
681 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
682 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
685 static int getBeltDirNrFromBeltSwitchElement(int element)
687 static int belt_base_element[4] =
689 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
690 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
691 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
692 EL_CONVEYOR_BELT_4_SWITCH_LEFT
695 int belt_nr = getBeltNrFromBeltSwitchElement(element);
696 int belt_dir_nr = element - belt_base_element[belt_nr];
698 return (belt_dir_nr % 3);
701 static int getBeltDirFromBeltSwitchElement(int element)
703 static int belt_move_dir[3] =
710 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
712 return belt_move_dir[belt_dir_nr];
715 static void InitPlayerField(int x, int y, int element, boolean init_game)
717 if (element == EL_SP_MURPHY)
721 if (stored_player[0].present)
723 Feld[x][y] = EL_SP_MURPHY_CLONE;
729 stored_player[0].use_murphy_graphic = TRUE;
732 Feld[x][y] = EL_PLAYER_1;
738 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
739 int jx = player->jx, jy = player->jy;
741 player->present = TRUE;
743 player->block_last_field = (element == EL_SP_MURPHY ?
744 level.sp_block_last_field :
745 level.block_last_field);
747 if (!options.network || player->connected)
749 player->active = TRUE;
751 /* remove potentially duplicate players */
752 if (StorePlayer[jx][jy] == Feld[x][y])
753 StorePlayer[jx][jy] = 0;
755 StorePlayer[x][y] = Feld[x][y];
759 printf("Player %d activated.\n", player->element_nr);
760 printf("[Local player is %d and currently %s.]\n",
761 local_player->element_nr,
762 local_player->active ? "active" : "not active");
766 Feld[x][y] = EL_EMPTY;
767 player->jx = player->last_jx = x;
768 player->jy = player->last_jy = y;
772 static void InitField(int x, int y, boolean init_game)
774 int element = Feld[x][y];
783 InitPlayerField(x, y, element, init_game);
786 case EL_SOKOBAN_FIELD_PLAYER:
787 element = Feld[x][y] = EL_PLAYER_1;
788 InitField(x, y, init_game);
790 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
791 InitField(x, y, init_game);
794 case EL_SOKOBAN_FIELD_EMPTY:
795 local_player->sokobanfields_still_needed++;
799 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
800 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
801 else if (x > 0 && Feld[x-1][y] == EL_ACID)
802 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
803 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
804 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
805 else if (y > 0 && Feld[x][y-1] == EL_ACID)
806 Feld[x][y] = EL_ACID_POOL_BOTTOM;
807 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
808 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
816 case EL_SPACESHIP_RIGHT:
817 case EL_SPACESHIP_UP:
818 case EL_SPACESHIP_LEFT:
819 case EL_SPACESHIP_DOWN:
821 case EL_BD_BUTTERFLY_RIGHT:
822 case EL_BD_BUTTERFLY_UP:
823 case EL_BD_BUTTERFLY_LEFT:
824 case EL_BD_BUTTERFLY_DOWN:
825 case EL_BD_BUTTERFLY:
826 case EL_BD_FIREFLY_RIGHT:
827 case EL_BD_FIREFLY_UP:
828 case EL_BD_FIREFLY_LEFT:
829 case EL_BD_FIREFLY_DOWN:
831 case EL_PACMAN_RIGHT:
855 if (y == lev_fieldy - 1)
857 Feld[x][y] = EL_AMOEBA_GROWING;
858 Store[x][y] = EL_AMOEBA_WET;
862 case EL_DYNAMITE_ACTIVE:
863 case EL_SP_DISK_RED_ACTIVE:
864 case EL_DYNABOMB_PLAYER_1_ACTIVE:
865 case EL_DYNABOMB_PLAYER_2_ACTIVE:
866 case EL_DYNABOMB_PLAYER_3_ACTIVE:
867 case EL_DYNABOMB_PLAYER_4_ACTIVE:
872 local_player->lights_still_needed++;
876 local_player->friends_still_needed++;
881 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
886 Feld[x][y] = EL_EMPTY;
891 case EL_EM_KEY_1_FILE:
892 Feld[x][y] = EL_EM_KEY_1;
894 case EL_EM_KEY_2_FILE:
895 Feld[x][y] = EL_EM_KEY_2;
897 case EL_EM_KEY_3_FILE:
898 Feld[x][y] = EL_EM_KEY_3;
900 case EL_EM_KEY_4_FILE:
901 Feld[x][y] = EL_EM_KEY_4;
905 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
906 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
907 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
908 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
909 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
910 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
911 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
912 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
913 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
914 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
915 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
916 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
919 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
920 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
921 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
923 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
925 game.belt_dir[belt_nr] = belt_dir;
926 game.belt_dir_nr[belt_nr] = belt_dir_nr;
928 else /* more than one switch -- set it like the first switch */
930 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
935 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
937 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
940 case EL_LIGHT_SWITCH_ACTIVE:
942 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
946 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
948 else if (IS_GROUP_ELEMENT(element))
950 struct ElementGroupInfo *group = element_info[element].group;
951 int last_anim_random_frame = gfx.anim_random_frame;
954 if (group->choice_mode == ANIM_RANDOM)
955 gfx.anim_random_frame = RND(group->num_elements_resolved);
957 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
958 group->choice_mode, 0,
961 if (group->choice_mode == ANIM_RANDOM)
962 gfx.anim_random_frame = last_anim_random_frame;
966 Feld[x][y] = group->element_resolved[element_pos];
968 InitField(x, y, init_game);
974 static inline void InitField_WithBug1(int x, int y, boolean init_game)
976 InitField(x, y, init_game);
978 /* not needed to call InitMovDir() -- already done by InitField()! */
979 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
980 CAN_MOVE(Feld[x][y]))
984 static inline void InitField_WithBug2(int x, int y, boolean init_game)
986 int old_element = Feld[x][y];
988 InitField(x, y, init_game);
990 /* not needed to call InitMovDir() -- already done by InitField()! */
991 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
992 CAN_MOVE(old_element) &&
993 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
996 /* this case is in fact a combination of not less than three bugs:
997 first, it calls InitMovDir() for elements that can move, although this is
998 already done by InitField(); then, it checks the element that was at this
999 field _before_ the call to InitField() (which can change it)
1004 inline void DrawGameValue_Emeralds(int value)
1006 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1009 inline void DrawGameValue_Dynamite(int value)
1011 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1014 inline void DrawGameValue_Keys(struct PlayerInfo *player)
1018 for (i = 0; i < MAX_KEYS; i++)
1020 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1021 el2edimg(EL_KEY_1 + i));
1024 inline void DrawGameValue_Score(int value)
1026 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1029 inline void DrawGameValue_Time(int value)
1032 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1034 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1037 inline void DrawGameValue_Level(int value)
1040 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1043 /* misuse area for displaying emeralds to draw bigger level number */
1044 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1045 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1047 /* now copy it to the area for displaying level number */
1048 BlitBitmap(drawto, drawto,
1049 DX_EMERALDS, DY_EMERALDS + 1,
1050 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1051 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1052 DX_LEVEL - 1, DY_LEVEL + 1);
1054 /* restore the area for displaying emeralds */
1055 DrawGameValue_Emeralds(local_player->gems_still_needed);
1057 /* yes, this is all really ugly :-) */
1061 void DrawGameDoorValues()
1065 DrawGameValue_Level(level_nr);
1067 for (i = 0; i < MAX_PLAYERS; i++)
1068 DrawGameValue_Keys(&stored_player[i]);
1070 DrawGameValue_Emeralds(local_player->gems_still_needed);
1071 DrawGameValue_Dynamite(local_player->inventory_size);
1072 DrawGameValue_Score(local_player->score);
1073 DrawGameValue_Time(TimeLeft);
1076 static void resolve_group_element(int group_element, int recursion_depth)
1078 static int group_nr;
1079 static struct ElementGroupInfo *group;
1080 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1083 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1085 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1086 group_element - EL_GROUP_START + 1);
1088 /* replace element which caused too deep recursion by question mark */
1089 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1094 if (recursion_depth == 0) /* initialization */
1096 group = element_info[group_element].group;
1097 group_nr = group_element - EL_GROUP_START;
1099 group->num_elements_resolved = 0;
1100 group->choice_pos = 0;
1103 for (i = 0; i < actual_group->num_elements; i++)
1105 int element = actual_group->element[i];
1107 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1110 if (IS_GROUP_ELEMENT(element))
1111 resolve_group_element(element, recursion_depth + 1);
1114 group->element_resolved[group->num_elements_resolved++] = element;
1115 element_info[element].in_group[group_nr] = TRUE;
1120 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1122 printf("::: group %d: %d resolved elements\n",
1123 group_element - EL_GROUP_START, group->num_elements_resolved);
1124 for (i = 0; i < group->num_elements_resolved; i++)
1125 printf("::: - %d ['%s']\n", group->element_resolved[i],
1126 element_info[group->element_resolved[i]].token_name);
1133 =============================================================================
1135 -----------------------------------------------------------------------------
1136 initialize game engine due to level / tape version number
1137 =============================================================================
1140 static void InitGameEngine()
1144 /* set game engine from tape file when re-playing, else from level file */
1145 game.engine_version = (tape.playing ? tape.engine_version :
1146 level.game_version);
1148 /* dynamically adjust element properties according to game engine version */
1149 InitElementPropertiesEngine(game.engine_version);
1152 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1153 printf(" tape version == %06d [%s] [file: %06d]\n",
1154 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1156 printf(" => game.engine_version == %06d\n", game.engine_version);
1159 /* ---------- recursively resolve group elements ------------------------- */
1161 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1162 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1163 element_info[i].in_group[j] = FALSE;
1165 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1166 resolve_group_element(EL_GROUP_START + i, 0);
1168 /* ---------- initialize player's initial move delay --------------------- */
1170 /* dynamically adjust player properties according to game engine version */
1171 game.initial_move_delay =
1172 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1173 INITIAL_MOVE_DELAY_OFF);
1175 /* dynamically adjust player properties according to level information */
1176 game.initial_move_delay_value =
1177 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1179 /* ---------- initialize player's initial push delay --------------------- */
1181 /* dynamically adjust player properties according to game engine version */
1182 game.initial_push_delay_value =
1183 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1185 /* ---------- initialize changing elements ------------------------------- */
1187 /* initialize changing elements information */
1188 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1190 struct ElementInfo *ei = &element_info[i];
1192 /* this pointer might have been changed in the level editor */
1193 ei->change = &ei->change_page[0];
1195 if (!IS_CUSTOM_ELEMENT(i))
1197 ei->change->target_element = EL_EMPTY_SPACE;
1198 ei->change->delay_fixed = 0;
1199 ei->change->delay_random = 0;
1200 ei->change->delay_frames = 1;
1203 ei->change_events = CE_BITMASK_DEFAULT;
1204 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1206 ei->event_page_nr[j] = 0;
1207 ei->event_page[j] = &ei->change_page[0];
1211 /* add changing elements from pre-defined list */
1212 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1214 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1215 struct ElementInfo *ei = &element_info[ch_delay->element];
1217 ei->change->target_element = ch_delay->target_element;
1218 ei->change->delay_fixed = ch_delay->change_delay;
1220 ei->change->pre_change_function = ch_delay->pre_change_function;
1221 ei->change->change_function = ch_delay->change_function;
1222 ei->change->post_change_function = ch_delay->post_change_function;
1224 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1227 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1232 /* add change events from custom element configuration */
1233 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1235 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1237 for (j = 0; j < ei->num_change_pages; j++)
1239 if (!ei->change_page[j].can_change)
1242 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1244 /* only add event page for the first page found with this event */
1245 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1246 !(ei->change_events & CH_EVENT_BIT(k)))
1248 ei->change_events |= CH_EVENT_BIT(k);
1249 ei->event_page_nr[k] = j;
1250 ei->event_page[k] = &ei->change_page[j];
1258 /* add change events from custom element configuration */
1259 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1261 int element = EL_CUSTOM_START + i;
1263 /* only add custom elements that change after fixed/random frame delay */
1264 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1265 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1269 /* ---------- initialize run-time trigger player and element ------------- */
1271 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1273 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1275 for (j = 0; j < ei->num_change_pages; j++)
1277 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1278 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1282 /* ---------- initialize trigger events ---------------------------------- */
1284 /* initialize trigger events information */
1285 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1286 trigger_events[i] = EP_BITMASK_DEFAULT;
1289 /* add trigger events from element change event properties */
1290 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1292 struct ElementInfo *ei = &element_info[i];
1294 for (j = 0; j < ei->num_change_pages; j++)
1296 if (!ei->change_page[j].can_change)
1299 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1301 int trigger_element = ei->change_page[j].trigger_element;
1303 if (IS_GROUP_ELEMENT(trigger_element))
1305 struct ElementGroupInfo *group = element_info[trigger_element].group;
1307 for (k = 0; k < group->num_elements_resolved; k++)
1308 trigger_events[group->element_resolved[k]]
1309 |= ei->change_page[j].events;
1312 trigger_events[trigger_element] |= ei->change_page[j].events;
1317 /* add trigger events from element change event properties */
1318 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1319 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1320 trigger_events[element_info[i].change->trigger_element] |=
1321 element_info[i].change->events;
1324 /* ---------- initialize push delay -------------------------------------- */
1326 /* initialize push delay values to default */
1327 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1329 if (!IS_CUSTOM_ELEMENT(i))
1331 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1332 element_info[i].push_delay_random = game.default_push_delay_random;
1336 /* set push delay value for certain elements from pre-defined list */
1337 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1339 int e = push_delay_list[i].element;
1341 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1342 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1345 /* set push delay value for Supaplex elements for newer engine versions */
1346 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1348 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1350 if (IS_SP_ELEMENT(i))
1352 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1353 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1358 /* ---------- initialize move stepsize ----------------------------------- */
1360 /* initialize move stepsize values to default */
1361 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1362 if (!IS_CUSTOM_ELEMENT(i))
1363 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1365 /* set move stepsize value for certain elements from pre-defined list */
1366 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1368 int e = move_stepsize_list[i].element;
1370 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1374 /* ---------- initialize move dig/leave ---------------------------------- */
1376 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1378 element_info[i].can_leave_element = FALSE;
1379 element_info[i].can_leave_element_last = FALSE;
1383 /* ---------- initialize gem count --------------------------------------- */
1385 /* initialize gem count values for each element */
1386 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1387 if (!IS_CUSTOM_ELEMENT(i))
1388 element_info[i].collect_count = 0;
1390 /* add gem count values for all elements from pre-defined list */
1391 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1392 element_info[collect_count_list[i].element].collect_count =
1393 collect_count_list[i].count;
1395 /* ---------- initialize access direction -------------------------------- */
1397 /* initialize access direction values to default (access from every side) */
1398 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1399 if (!IS_CUSTOM_ELEMENT(i))
1400 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1402 /* set access direction value for certain elements from pre-defined list */
1403 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1404 element_info[access_direction_list[i].element].access_direction =
1405 access_direction_list[i].direction;
1410 =============================================================================
1412 -----------------------------------------------------------------------------
1413 initialize and start new game
1414 =============================================================================
1419 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1420 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1421 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1428 #if USE_NEW_AMOEBA_CODE
1429 printf("Using new amoeba code.\n");
1431 printf("Using old amoeba code.\n");
1436 /* don't play tapes over network */
1437 network_playing = (options.network && !tape.playing);
1439 for (i = 0; i < MAX_PLAYERS; i++)
1441 struct PlayerInfo *player = &stored_player[i];
1443 player->index_nr = i;
1444 player->index_bit = (1 << i);
1445 player->element_nr = EL_PLAYER_1 + i;
1447 player->present = FALSE;
1448 player->active = FALSE;
1451 player->effective_action = 0;
1452 player->programmed_action = 0;
1455 player->gems_still_needed = level.gems_needed;
1456 player->sokobanfields_still_needed = 0;
1457 player->lights_still_needed = 0;
1458 player->friends_still_needed = 0;
1460 for (j = 0; j < MAX_KEYS; j++)
1461 player->key[j] = FALSE;
1463 player->dynabomb_count = 0;
1464 player->dynabomb_size = 1;
1465 player->dynabombs_left = 0;
1466 player->dynabomb_xl = FALSE;
1468 player->MovDir = MV_NO_MOVING;
1471 player->GfxDir = MV_NO_MOVING;
1472 player->GfxAction = ACTION_DEFAULT;
1474 player->StepFrame = 0;
1476 player->use_murphy_graphic = FALSE;
1478 player->block_last_field = FALSE;
1479 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1481 player->actual_frame_counter = 0;
1483 player->step_counter = 0;
1485 player->last_move_dir = MV_NO_MOVING;
1487 player->is_waiting = FALSE;
1488 player->is_moving = FALSE;
1489 player->is_digging = FALSE;
1490 player->is_snapping = FALSE;
1491 player->is_collecting = FALSE;
1492 player->is_pushing = FALSE;
1493 player->is_switching = FALSE;
1494 player->is_dropping = FALSE;
1496 player->is_bored = FALSE;
1497 player->is_sleeping = FALSE;
1499 player->frame_counter_bored = -1;
1500 player->frame_counter_sleeping = -1;
1502 player->anim_delay_counter = 0;
1503 player->post_delay_counter = 0;
1505 player->action_waiting = ACTION_DEFAULT;
1506 player->last_action_waiting = ACTION_DEFAULT;
1507 player->special_action_bored = ACTION_DEFAULT;
1508 player->special_action_sleeping = ACTION_DEFAULT;
1510 player->num_special_action_bored = 0;
1511 player->num_special_action_sleeping = 0;
1513 /* determine number of special actions for bored and sleeping animation */
1514 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1516 boolean found = FALSE;
1518 for (k = 0; k < NUM_DIRECTIONS; k++)
1519 if (el_act_dir2img(player->element_nr, j, k) !=
1520 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1524 player->num_special_action_bored++;
1528 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1530 boolean found = FALSE;
1532 for (k = 0; k < NUM_DIRECTIONS; k++)
1533 if (el_act_dir2img(player->element_nr, j, k) !=
1534 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1538 player->num_special_action_sleeping++;
1543 player->switch_x = -1;
1544 player->switch_y = -1;
1546 player->show_envelope = 0;
1548 player->move_delay = game.initial_move_delay;
1549 player->move_delay_value = game.initial_move_delay_value;
1551 player->move_delay_reset_counter = 0;
1553 player->push_delay = 0;
1554 player->push_delay_value = game.initial_push_delay_value;
1556 player->drop_delay = 0;
1558 player->last_jx = player->last_jy = 0;
1559 player->jx = player->jy = 0;
1561 player->shield_normal_time_left = 0;
1562 player->shield_deadly_time_left = 0;
1564 player->inventory_infinite_element = EL_UNDEFINED;
1565 player->inventory_size = 0;
1567 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1568 SnapField(player, 0, 0);
1570 player->LevelSolved = FALSE;
1571 player->GameOver = FALSE;
1574 network_player_action_received = FALSE;
1576 #if defined(PLATFORM_UNIX)
1577 /* initial null action */
1578 if (network_playing)
1579 SendToServer_MovePlayer(MV_NO_MOVING);
1587 TimeLeft = level.time;
1590 ScreenMovDir = MV_NO_MOVING;
1594 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1596 AllPlayersGone = FALSE;
1598 game.yamyam_content_nr = 0;
1599 game.magic_wall_active = FALSE;
1600 game.magic_wall_time_left = 0;
1601 game.light_time_left = 0;
1602 game.timegate_time_left = 0;
1603 game.switchgate_pos = 0;
1604 game.balloon_dir = MV_NO_MOVING;
1605 game.gravity = level.initial_gravity;
1606 game.explosions_delayed = TRUE;
1608 game.envelope_active = FALSE;
1610 for (i = 0; i < NUM_BELTS; i++)
1612 game.belt_dir[i] = MV_NO_MOVING;
1613 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1616 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1617 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1619 for (x = 0; x < lev_fieldx; x++)
1621 for (y = 0; y < lev_fieldy; y++)
1623 Feld[x][y] = level.field[x][y];
1624 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1625 ChangeDelay[x][y] = 0;
1626 ChangePage[x][y] = -1;
1627 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1629 WasJustMoving[x][y] = 0;
1630 WasJustFalling[x][y] = 0;
1632 Pushed[x][y] = FALSE;
1634 Changed[x][y] = CE_BITMASK_DEFAULT;
1635 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1637 ExplodePhase[x][y] = 0;
1638 ExplodeDelay[x][y] = 0;
1639 ExplodeField[x][y] = EX_TYPE_NONE;
1641 RunnerVisit[x][y] = 0;
1642 PlayerVisit[x][y] = 0;
1645 GfxRandom[x][y] = INIT_GFX_RANDOM();
1646 GfxElement[x][y] = EL_UNDEFINED;
1647 GfxAction[x][y] = ACTION_DEFAULT;
1648 GfxDir[x][y] = MV_NO_MOVING;
1652 for (y = 0; y < lev_fieldy; y++)
1654 for (x = 0; x < lev_fieldx; x++)
1656 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1658 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1660 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1663 InitField(x, y, TRUE);
1669 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1670 emulate_sb ? EMU_SOKOBAN :
1671 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1673 /* initialize explosion and ignition delay */
1674 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1676 if (!IS_CUSTOM_ELEMENT(i))
1679 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1680 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1681 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1682 int last_phase = (num_phase + 1) * delay;
1683 int half_phase = (num_phase / 2) * delay;
1685 element_info[i].explosion_delay = last_phase - 1;
1686 element_info[i].ignition_delay = half_phase;
1689 if (i == EL_BLACK_ORB)
1690 element_info[i].ignition_delay = 0;
1692 if (i == EL_BLACK_ORB)
1693 element_info[i].ignition_delay = 1;
1698 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1699 element_info[i].explosion_delay = 1;
1701 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1702 element_info[i].ignition_delay = 1;
1706 /* correct non-moving belts to start moving left */
1707 for (i = 0; i < NUM_BELTS; i++)
1708 if (game.belt_dir[i] == MV_NO_MOVING)
1709 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1711 /* check if any connected player was not found in playfield */
1712 for (i = 0; i < MAX_PLAYERS; i++)
1714 struct PlayerInfo *player = &stored_player[i];
1716 if (player->connected && !player->present)
1718 for (j = 0; j < MAX_PLAYERS; j++)
1720 struct PlayerInfo *some_player = &stored_player[j];
1721 int jx = some_player->jx, jy = some_player->jy;
1723 /* assign first free player found that is present in the playfield */
1724 if (some_player->present && !some_player->connected)
1726 player->present = TRUE;
1727 player->active = TRUE;
1729 some_player->present = FALSE;
1730 some_player->active = FALSE;
1733 player->element_nr = some_player->element_nr;
1736 StorePlayer[jx][jy] = player->element_nr;
1737 player->jx = player->last_jx = jx;
1738 player->jy = player->last_jy = jy;
1748 /* when playing a tape, eliminate all players which do not participate */
1750 for (i = 0; i < MAX_PLAYERS; i++)
1752 if (stored_player[i].active && !tape.player_participates[i])
1754 struct PlayerInfo *player = &stored_player[i];
1755 int jx = player->jx, jy = player->jy;
1757 player->active = FALSE;
1758 StorePlayer[jx][jy] = 0;
1759 Feld[jx][jy] = EL_EMPTY;
1763 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1765 /* when in single player mode, eliminate all but the first active player */
1767 for (i = 0; i < MAX_PLAYERS; i++)
1769 if (stored_player[i].active)
1771 for (j = i + 1; j < MAX_PLAYERS; j++)
1773 if (stored_player[j].active)
1775 struct PlayerInfo *player = &stored_player[j];
1776 int jx = player->jx, jy = player->jy;
1778 player->active = FALSE;
1779 player->present = FALSE;
1781 StorePlayer[jx][jy] = 0;
1782 Feld[jx][jy] = EL_EMPTY;
1789 /* when recording the game, store which players take part in the game */
1792 for (i = 0; i < MAX_PLAYERS; i++)
1793 if (stored_player[i].active)
1794 tape.player_participates[i] = TRUE;
1799 for (i = 0; i < MAX_PLAYERS; i++)
1801 struct PlayerInfo *player = &stored_player[i];
1803 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1808 if (local_player == player)
1809 printf("Player %d is local player.\n", i+1);
1813 if (BorderElement == EL_EMPTY)
1816 SBX_Right = lev_fieldx - SCR_FIELDX;
1818 SBY_Lower = lev_fieldy - SCR_FIELDY;
1823 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1825 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1828 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1829 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1831 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1832 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1834 /* if local player not found, look for custom element that might create
1835 the player (make some assumptions about the right custom element) */
1836 if (!local_player->present)
1838 int start_x = 0, start_y = 0;
1839 int found_rating = 0;
1840 int found_element = EL_UNDEFINED;
1842 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1844 int element = Feld[x][y];
1849 if (!IS_CUSTOM_ELEMENT(element))
1852 if (CAN_CHANGE(element))
1854 for (i = 0; i < element_info[element].num_change_pages; i++)
1856 content = element_info[element].change_page[i].target_element;
1857 is_player = ELEM_IS_PLAYER(content);
1859 if (is_player && (found_rating < 3 || element < found_element))
1865 found_element = element;
1870 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1872 content = element_info[element].content[xx][yy];
1873 is_player = ELEM_IS_PLAYER(content);
1875 if (is_player && (found_rating < 2 || element < found_element))
1877 start_x = x + xx - 1;
1878 start_y = y + yy - 1;
1881 found_element = element;
1884 if (!CAN_CHANGE(element))
1887 for (i = 0; i < element_info[element].num_change_pages; i++)
1889 content= element_info[element].change_page[i].target_content[xx][yy];
1890 is_player = ELEM_IS_PLAYER(content);
1892 if (is_player && (found_rating < 1 || element < found_element))
1894 start_x = x + xx - 1;
1895 start_y = y + yy - 1;
1898 found_element = element;
1904 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1905 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1908 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1909 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1915 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1916 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1917 local_player->jx - MIDPOSX);
1919 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1920 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1921 local_player->jy - MIDPOSY);
1923 scroll_x = SBX_Left;
1924 scroll_y = SBY_Upper;
1925 if (local_player->jx >= SBX_Left + MIDPOSX)
1926 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1927 local_player->jx - MIDPOSX :
1929 if (local_player->jy >= SBY_Upper + MIDPOSY)
1930 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1931 local_player->jy - MIDPOSY :
1936 CloseDoor(DOOR_CLOSE_1);
1941 /* after drawing the level, correct some elements */
1942 if (game.timegate_time_left == 0)
1943 CloseAllOpenTimegates();
1945 if (setup.soft_scrolling)
1946 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1948 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1951 /* copy default game door content to main double buffer */
1952 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1953 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1955 DrawGameDoorValues();
1959 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1960 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1961 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1965 /* copy actual game door content to door double buffer for OpenDoor() */
1966 BlitBitmap(drawto, bitmap_db_door,
1967 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1969 OpenDoor(DOOR_OPEN_ALL);
1971 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1973 if (setup.sound_music)
1976 KeyboardAutoRepeatOffUnlessAutoplay();
1980 for (i = 0; i < MAX_PLAYERS; i++)
1981 printf("Player %d %sactive.\n",
1982 i + 1, (stored_player[i].active ? "" : "not "));
1986 printf("::: starting game [%d]\n", FrameCounter);
1990 void InitMovDir(int x, int y)
1992 int i, element = Feld[x][y];
1993 static int xy[4][2] =
2000 static int direction[3][4] =
2002 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2003 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2004 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2013 Feld[x][y] = EL_BUG;
2014 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2017 case EL_SPACESHIP_RIGHT:
2018 case EL_SPACESHIP_UP:
2019 case EL_SPACESHIP_LEFT:
2020 case EL_SPACESHIP_DOWN:
2021 Feld[x][y] = EL_SPACESHIP;
2022 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2025 case EL_BD_BUTTERFLY_RIGHT:
2026 case EL_BD_BUTTERFLY_UP:
2027 case EL_BD_BUTTERFLY_LEFT:
2028 case EL_BD_BUTTERFLY_DOWN:
2029 Feld[x][y] = EL_BD_BUTTERFLY;
2030 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2033 case EL_BD_FIREFLY_RIGHT:
2034 case EL_BD_FIREFLY_UP:
2035 case EL_BD_FIREFLY_LEFT:
2036 case EL_BD_FIREFLY_DOWN:
2037 Feld[x][y] = EL_BD_FIREFLY;
2038 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2041 case EL_PACMAN_RIGHT:
2043 case EL_PACMAN_LEFT:
2044 case EL_PACMAN_DOWN:
2045 Feld[x][y] = EL_PACMAN;
2046 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2049 case EL_SP_SNIKSNAK:
2050 MovDir[x][y] = MV_UP;
2053 case EL_SP_ELECTRON:
2054 MovDir[x][y] = MV_LEFT;
2061 Feld[x][y] = EL_MOLE;
2062 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2066 if (IS_CUSTOM_ELEMENT(element))
2068 struct ElementInfo *ei = &element_info[element];
2069 int move_direction_initial = ei->move_direction_initial;
2070 int move_pattern = ei->move_pattern;
2072 if (move_direction_initial == MV_START_PREVIOUS)
2074 if (MovDir[x][y] != MV_NO_MOVING)
2077 move_direction_initial = MV_START_AUTOMATIC;
2080 if (move_direction_initial == MV_START_RANDOM)
2081 MovDir[x][y] = 1 << RND(4);
2082 else if (move_direction_initial & MV_ANY_DIRECTION)
2083 MovDir[x][y] = move_direction_initial;
2084 else if (move_pattern == MV_ALL_DIRECTIONS ||
2085 move_pattern == MV_TURNING_LEFT ||
2086 move_pattern == MV_TURNING_RIGHT ||
2087 move_pattern == MV_TURNING_LEFT_RIGHT ||
2088 move_pattern == MV_TURNING_RIGHT_LEFT ||
2089 move_pattern == MV_TURNING_RANDOM)
2090 MovDir[x][y] = 1 << RND(4);
2091 else if (move_pattern == MV_HORIZONTAL)
2092 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2093 else if (move_pattern == MV_VERTICAL)
2094 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2095 else if (move_pattern & MV_ANY_DIRECTION)
2096 MovDir[x][y] = element_info[element].move_pattern;
2097 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2098 move_pattern == MV_ALONG_RIGHT_SIDE)
2101 /* use random direction as default start direction */
2102 if (game.engine_version >= VERSION_IDENT(3,1,0,2))
2103 MovDir[x][y] = 1 << RND(4);
2106 for (i = 0; i < NUM_DIRECTIONS; i++)
2108 int x1 = x + xy[i][0];
2109 int y1 = y + xy[i][1];
2111 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2113 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2114 MovDir[x][y] = direction[0][i];
2116 MovDir[x][y] = direction[1][i];
2125 MovDir[x][y] = 1 << RND(4);
2127 if (element != EL_BUG &&
2128 element != EL_SPACESHIP &&
2129 element != EL_BD_BUTTERFLY &&
2130 element != EL_BD_FIREFLY)
2133 for (i = 0; i < NUM_DIRECTIONS; i++)
2135 int x1 = x + xy[i][0];
2136 int y1 = y + xy[i][1];
2138 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2140 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2142 MovDir[x][y] = direction[0][i];
2145 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2146 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2148 MovDir[x][y] = direction[1][i];
2157 GfxDir[x][y] = MovDir[x][y];
2160 void InitAmoebaNr(int x, int y)
2163 int group_nr = AmoebeNachbarNr(x, y);
2167 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2169 if (AmoebaCnt[i] == 0)
2177 AmoebaNr[x][y] = group_nr;
2178 AmoebaCnt[group_nr]++;
2179 AmoebaCnt2[group_nr]++;
2185 boolean raise_level = FALSE;
2187 if (local_player->MovPos)
2191 if (tape.auto_play) /* tape might already be stopped here */
2192 tape.auto_play_level_solved = TRUE;
2194 if (tape.playing && tape.auto_play)
2195 tape.auto_play_level_solved = TRUE;
2198 local_player->LevelSolved = FALSE;
2200 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2204 if (!tape.playing && setup.sound_loops)
2205 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2206 SND_CTRL_PLAY_LOOP);
2208 while (TimeLeft > 0)
2210 if (!tape.playing && !setup.sound_loops)
2211 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2212 if (TimeLeft > 0 && !(TimeLeft % 10))
2213 RaiseScore(level.score[SC_TIME_BONUS]);
2214 if (TimeLeft > 100 && !(TimeLeft % 10))
2219 DrawGameValue_Time(TimeLeft);
2227 if (!tape.playing && setup.sound_loops)
2228 StopSound(SND_GAME_LEVELTIME_BONUS);
2230 else if (level.time == 0) /* level without time limit */
2232 if (!tape.playing && setup.sound_loops)
2233 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2234 SND_CTRL_PLAY_LOOP);
2236 while (TimePlayed < 999)
2238 if (!tape.playing && !setup.sound_loops)
2239 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2240 if (TimePlayed < 999 && !(TimePlayed % 10))
2241 RaiseScore(level.score[SC_TIME_BONUS]);
2242 if (TimePlayed < 900 && !(TimePlayed % 10))
2247 DrawGameValue_Time(TimePlayed);
2255 if (!tape.playing && setup.sound_loops)
2256 StopSound(SND_GAME_LEVELTIME_BONUS);
2259 /* close exit door after last player */
2260 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2261 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2263 int element = Feld[ExitX][ExitY];
2265 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2266 EL_SP_EXIT_CLOSING);
2268 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2271 /* Hero disappears */
2272 DrawLevelField(ExitX, ExitY);
2278 CloseDoor(DOOR_CLOSE_1);
2283 SaveTape(tape.level_nr); /* Ask to save tape */
2286 if (level_nr == leveldir_current->handicap_level)
2288 leveldir_current->handicap_level++;
2289 SaveLevelSetup_SeriesInfo();
2292 if (level_editor_test_game)
2293 local_player->score = -1; /* no highscore when playing from editor */
2294 else if (level_nr < leveldir_current->last_level)
2295 raise_level = TRUE; /* advance to next level */
2297 if ((hi_pos = NewHiScore()) >= 0)
2299 game_status = GAME_MODE_SCORES;
2300 DrawHallOfFame(hi_pos);
2309 game_status = GAME_MODE_MAIN;
2326 LoadScore(level_nr);
2328 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2329 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2332 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2334 if (local_player->score > highscore[k].Score)
2336 /* player has made it to the hall of fame */
2338 if (k < MAX_SCORE_ENTRIES - 1)
2340 int m = MAX_SCORE_ENTRIES - 1;
2343 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2344 if (!strcmp(setup.player_name, highscore[l].Name))
2346 if (m == k) /* player's new highscore overwrites his old one */
2350 for (l = m; l > k; l--)
2352 strcpy(highscore[l].Name, highscore[l - 1].Name);
2353 highscore[l].Score = highscore[l - 1].Score;
2360 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2361 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2362 highscore[k].Score = local_player->score;
2368 else if (!strncmp(setup.player_name, highscore[k].Name,
2369 MAX_PLAYER_NAME_LEN))
2370 break; /* player already there with a higher score */
2376 SaveScore(level_nr);
2381 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2383 if (player->GfxAction != action || player->GfxDir != dir)
2386 printf("Player frame reset! (%d => %d, %d => %d)\n",
2387 player->GfxAction, action, player->GfxDir, dir);
2390 player->GfxAction = action;
2391 player->GfxDir = dir;
2393 player->StepFrame = 0;
2397 static void ResetRandomAnimationValue(int x, int y)
2399 GfxRandom[x][y] = INIT_GFX_RANDOM();
2402 static void ResetGfxAnimation(int x, int y)
2405 GfxAction[x][y] = ACTION_DEFAULT;
2406 GfxDir[x][y] = MovDir[x][y];
2409 void InitMovingField(int x, int y, int direction)
2411 int element = Feld[x][y];
2412 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2413 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2417 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2418 ResetGfxAnimation(x, y);
2420 MovDir[newx][newy] = MovDir[x][y] = direction;
2421 GfxDir[x][y] = direction;
2423 if (Feld[newx][newy] == EL_EMPTY)
2424 Feld[newx][newy] = EL_BLOCKED;
2426 if (direction == MV_DOWN && CAN_FALL(element))
2427 GfxAction[x][y] = ACTION_FALLING;
2429 GfxAction[x][y] = ACTION_MOVING;
2431 GfxFrame[newx][newy] = GfxFrame[x][y];
2432 GfxRandom[newx][newy] = GfxRandom[x][y];
2433 GfxAction[newx][newy] = GfxAction[x][y];
2434 GfxDir[newx][newy] = GfxDir[x][y];
2437 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2439 int direction = MovDir[x][y];
2440 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2441 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2447 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2449 int oldx = x, oldy = y;
2450 int direction = MovDir[x][y];
2452 if (direction == MV_LEFT)
2454 else if (direction == MV_RIGHT)
2456 else if (direction == MV_UP)
2458 else if (direction == MV_DOWN)
2461 *comes_from_x = oldx;
2462 *comes_from_y = oldy;
2465 int MovingOrBlocked2Element(int x, int y)
2467 int element = Feld[x][y];
2469 if (element == EL_BLOCKED)
2473 Blocked2Moving(x, y, &oldx, &oldy);
2474 return Feld[oldx][oldy];
2480 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2482 /* like MovingOrBlocked2Element(), but if element is moving
2483 and (x,y) is the field the moving element is just leaving,
2484 return EL_BLOCKED instead of the element value */
2485 int element = Feld[x][y];
2487 if (IS_MOVING(x, y))
2489 if (element == EL_BLOCKED)
2493 Blocked2Moving(x, y, &oldx, &oldy);
2494 return Feld[oldx][oldy];
2503 static void RemoveField(int x, int y)
2505 Feld[x][y] = EL_EMPTY;
2512 ChangeDelay[x][y] = 0;
2513 ChangePage[x][y] = -1;
2514 Pushed[x][y] = FALSE;
2516 GfxElement[x][y] = EL_UNDEFINED;
2517 GfxAction[x][y] = ACTION_DEFAULT;
2518 GfxDir[x][y] = MV_NO_MOVING;
2521 void RemoveMovingField(int x, int y)
2523 int oldx = x, oldy = y, newx = x, newy = y;
2524 int element = Feld[x][y];
2525 int next_element = EL_UNDEFINED;
2527 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2530 if (IS_MOVING(x, y))
2532 Moving2Blocked(x, y, &newx, &newy);
2534 if (Feld[newx][newy] != EL_BLOCKED)
2537 if (Feld[newx][newy] != EL_BLOCKED)
2539 /* element is moving, but target field is not free (blocked), but
2540 already occupied by something different (example: acid pool);
2541 in this case, only remove the moving field, but not the target */
2543 RemoveField(oldx, oldy);
2545 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2547 DrawLevelField(oldx, oldy);
2553 else if (element == EL_BLOCKED)
2555 Blocked2Moving(x, y, &oldx, &oldy);
2556 if (!IS_MOVING(oldx, oldy))
2560 if (element == EL_BLOCKED &&
2561 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2562 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2563 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2564 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2565 next_element = get_next_element(Feld[oldx][oldy]);
2567 RemoveField(oldx, oldy);
2568 RemoveField(newx, newy);
2570 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2572 if (next_element != EL_UNDEFINED)
2573 Feld[oldx][oldy] = next_element;
2575 DrawLevelField(oldx, oldy);
2576 DrawLevelField(newx, newy);
2579 void DrawDynamite(int x, int y)
2581 int sx = SCREENX(x), sy = SCREENY(y);
2582 int graphic = el2img(Feld[x][y]);
2585 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2588 if (IS_WALKABLE_INSIDE(Back[x][y]))
2592 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2593 else if (Store[x][y])
2594 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2596 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2599 if (Back[x][y] || Store[x][y])
2600 DrawGraphicThruMask(sx, sy, graphic, frame);
2602 DrawGraphic(sx, sy, graphic, frame);
2604 if (game.emulation == EMU_SUPAPLEX)
2605 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2606 else if (Store[x][y])
2607 DrawGraphicThruMask(sx, sy, graphic, frame);
2609 DrawGraphic(sx, sy, graphic, frame);
2613 void CheckDynamite(int x, int y)
2615 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2619 if (MovDelay[x][y] != 0)
2622 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2629 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2631 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2632 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2633 StopSound(SND_DYNAMITE_ACTIVE);
2635 StopSound(SND_DYNABOMB_ACTIVE);
2641 void RelocatePlayer(int x, int y, int element_raw)
2643 int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
2644 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2645 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2646 boolean no_delay = (tape.warp_forward);
2647 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2648 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2651 if (player->GameOver) /* do not reanimate dead player */
2654 RemoveField(x, y); /* temporarily remove newly placed player */
2655 DrawLevelField(x, y);
2657 if (player->present)
2659 while (player->MovPos)
2661 ScrollPlayer(player, SCROLL_GO_ON);
2662 ScrollScreen(NULL, SCROLL_GO_ON);
2668 Delay(wait_delay_value);
2671 DrawPlayer(player); /* needed here only to cleanup last field */
2672 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2674 player->is_moving = FALSE;
2677 old_jx = player->jx;
2678 old_jy = player->jy;
2680 Feld[x][y] = element;
2681 InitPlayerField(x, y, element, TRUE);
2683 if (player != local_player) /* do not visually relocate other players */
2686 if (level.instant_relocation)
2689 int offset = (setup.scroll_delay ? 3 : 0);
2690 int jx = local_player->jx;
2691 int jy = local_player->jy;
2693 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2695 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2696 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2697 local_player->jx - MIDPOSX);
2699 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2700 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2701 local_player->jy - MIDPOSY);
2705 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2706 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2707 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2709 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2710 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2711 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2713 /* don't scroll over playfield boundaries */
2714 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2715 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2717 /* don't scroll over playfield boundaries */
2718 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2719 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2722 scroll_x += (local_player->jx - old_jx);
2723 scroll_y += (local_player->jy - old_jy);
2725 /* don't scroll over playfield boundaries */
2726 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2727 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2729 /* don't scroll over playfield boundaries */
2730 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2731 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2734 RedrawPlayfield(TRUE, 0,0,0,0);
2740 int offset = (setup.scroll_delay ? 3 : 0);
2741 int jx = local_player->jx;
2742 int jy = local_player->jy;
2744 int scroll_xx = -999, scroll_yy = -999;
2746 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2748 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2751 int fx = FX, fy = FY;
2753 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2754 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2755 local_player->jx - MIDPOSX);
2757 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2758 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2759 local_player->jy - MIDPOSY);
2761 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2762 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2765 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2768 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2775 fx += dx * TILEX / 2;
2776 fy += dy * TILEY / 2;
2778 ScrollLevel(dx, dy);
2781 /* scroll in two steps of half tile size to make things smoother */
2782 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2784 Delay(wait_delay_value);
2786 /* scroll second step to align at full tile size */
2788 Delay(wait_delay_value);
2791 int scroll_xx = -999, scroll_yy = -999;
2793 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2795 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2798 int fx = FX, fy = FY;
2800 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2801 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2802 local_player->jx - MIDPOSX);
2804 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2805 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2806 local_player->jy - MIDPOSY);
2808 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2809 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2812 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2815 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2822 fx += dx * TILEX / 2;
2823 fy += dy * TILEY / 2;
2825 ScrollLevel(dx, dy);
2828 /* scroll in two steps of half tile size to make things smoother */
2829 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2831 Delay(wait_delay_value);
2833 /* scroll second step to align at full tile size */
2835 Delay(wait_delay_value);
2841 void Explode(int ex, int ey, int phase, int mode)
2848 /* !!! eliminate this variable !!! */
2849 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2854 int last_phase = num_phase * delay;
2855 int half_phase = (num_phase / 2) * delay;
2856 int first_phase_after_start = EX_PHASE_START + 1;
2860 if (game.explosions_delayed)
2862 ExplodeField[ex][ey] = mode;
2866 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2868 int center_element = Feld[ex][ey];
2871 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
2875 /* --- This is only really needed (and now handled) in "Impact()". --- */
2876 /* do not explode moving elements that left the explode field in time */
2877 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2878 center_element == EL_EMPTY &&
2879 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
2883 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
2884 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2886 /* remove things displayed in background while burning dynamite */
2887 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2890 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2892 /* put moving element to center field (and let it explode there) */
2893 center_element = MovingOrBlocked2Element(ex, ey);
2894 RemoveMovingField(ex, ey);
2895 Feld[ex][ey] = center_element;
2901 last_phase = element_info[center_element].explosion_delay + 1;
2903 last_phase = element_info[center_element].explosion_delay;
2907 printf("::: %d -> %d\n", center_element, last_phase);
2911 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2913 int xx = x - ex + 1;
2914 int yy = y - ey + 1;
2919 if (!IN_LEV_FIELD(x, y) ||
2920 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
2921 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
2924 if (!IN_LEV_FIELD(x, y) ||
2925 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
2929 if (!IN_LEV_FIELD(x, y) ||
2930 ((mode != EX_TYPE_NORMAL ||
2931 center_element == EL_AMOEBA_TO_DIAMOND) &&
2932 (x != ex || y != ey)))
2936 element = Feld[x][y];
2938 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2940 element = MovingOrBlocked2Element(x, y);
2942 if (!IS_EXPLOSION_PROOF(element))
2943 RemoveMovingField(x, y);
2949 if (IS_EXPLOSION_PROOF(element))
2952 /* indestructible elements can only explode in center (but not flames) */
2953 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2954 element == EL_FLAMES)
2959 if ((IS_INDESTRUCTIBLE(element) &&
2960 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2961 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2962 element == EL_FLAMES)
2966 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2968 if (IS_ACTIVE_BOMB(element))
2970 /* re-activate things under the bomb like gate or penguin */
2971 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2978 /* save walkable background elements while explosion on same tile */
2980 if (IS_INDESTRUCTIBLE(element))
2981 Back[x][y] = element;
2983 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2984 Back[x][y] = element;
2987 /* ignite explodable elements reached by other explosion */
2988 if (element == EL_EXPLOSION)
2989 element = Store2[x][y];
2992 if (AmoebaNr[x][y] &&
2993 (element == EL_AMOEBA_FULL ||
2994 element == EL_BD_AMOEBA ||
2995 element == EL_AMOEBA_GROWING))
2997 AmoebaCnt[AmoebaNr[x][y]]--;
2998 AmoebaCnt2[AmoebaNr[x][y]]--;
3004 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3006 switch(StorePlayer[ex][ey])
3009 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3012 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3015 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3019 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3024 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3025 Store[x][y] = EL_EMPTY;
3027 if (game.emulation == EMU_SUPAPLEX)
3028 Store[x][y] = EL_EMPTY;
3031 else if (center_element == EL_MOLE)
3032 Store[x][y] = EL_EMERALD_RED;
3033 else if (center_element == EL_PENGUIN)
3034 Store[x][y] = EL_EMERALD_PURPLE;
3035 else if (center_element == EL_BUG)
3036 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3037 else if (center_element == EL_BD_BUTTERFLY)
3038 Store[x][y] = EL_BD_DIAMOND;
3039 else if (center_element == EL_SP_ELECTRON)
3040 Store[x][y] = EL_SP_INFOTRON;
3041 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3042 Store[x][y] = level.amoeba_content;
3043 else if (center_element == EL_YAMYAM)
3044 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3045 else if (IS_CUSTOM_ELEMENT(center_element) &&
3046 element_info[center_element].content[xx][yy] != EL_EMPTY)
3047 Store[x][y] = element_info[center_element].content[xx][yy];
3048 else if (element == EL_WALL_EMERALD)
3049 Store[x][y] = EL_EMERALD;
3050 else if (element == EL_WALL_DIAMOND)
3051 Store[x][y] = EL_DIAMOND;
3052 else if (element == EL_WALL_BD_DIAMOND)
3053 Store[x][y] = EL_BD_DIAMOND;
3054 else if (element == EL_WALL_EMERALD_YELLOW)
3055 Store[x][y] = EL_EMERALD_YELLOW;
3056 else if (element == EL_WALL_EMERALD_RED)
3057 Store[x][y] = EL_EMERALD_RED;
3058 else if (element == EL_WALL_EMERALD_PURPLE)
3059 Store[x][y] = EL_EMERALD_PURPLE;
3060 else if (element == EL_WALL_PEARL)
3061 Store[x][y] = EL_PEARL;
3062 else if (element == EL_WALL_CRYSTAL)
3063 Store[x][y] = EL_CRYSTAL;
3064 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3065 Store[x][y] = element_info[element].content[1][1];
3067 Store[x][y] = EL_EMPTY;
3069 if (x != ex || y != ey ||
3070 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_TYPE_BORDER)
3071 Store2[x][y] = element;
3074 if (AmoebaNr[x][y] &&
3075 (element == EL_AMOEBA_FULL ||
3076 element == EL_BD_AMOEBA ||
3077 element == EL_AMOEBA_GROWING))
3079 AmoebaCnt[AmoebaNr[x][y]]--;
3080 AmoebaCnt2[AmoebaNr[x][y]]--;
3086 MovDir[x][y] = MovPos[x][y] = 0;
3087 GfxDir[x][y] = MovDir[x][y];
3092 Feld[x][y] = EL_EXPLOSION;
3094 GfxElement[x][y] = center_element;
3096 GfxElement[x][y] = EL_UNDEFINED;
3099 ExplodePhase[x][y] = 1;
3101 ExplodeDelay[x][y] = last_phase;
3106 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3108 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3115 if (center_element == EL_YAMYAM)
3116 game.yamyam_content_nr =
3117 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3130 GfxFrame[x][y] = 0; /* restart explosion animation */
3134 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3138 last_phase = ExplodeDelay[x][y];
3141 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3145 /* activate this even in non-DEBUG version until cause for crash in
3146 getGraphicAnimationFrame() (see below) is found and eliminated */
3150 if (GfxElement[x][y] == EL_UNDEFINED)
3153 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3154 printf("Explode(): This should never happen!\n");
3157 GfxElement[x][y] = EL_EMPTY;
3163 border_element = Store2[x][y];
3164 if (IS_PLAYER(x, y))
3165 border_element = StorePlayer[x][y];
3168 printf("::: phase == %d\n", phase);
3171 if (phase == element_info[border_element].ignition_delay ||
3172 phase == last_phase)
3174 boolean border_explosion = FALSE;
3177 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3179 if (IS_PLAYER(x, y))
3182 KillHeroUnlessExplosionProtected(x, y);
3183 border_explosion = TRUE;
3186 if (phase == last_phase)
3187 printf("::: IS_PLAYER\n");
3190 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3192 Feld[x][y] = Store2[x][y];
3195 border_explosion = TRUE;
3198 if (phase == last_phase)
3199 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3202 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3204 AmoebeUmwandeln(x, y);
3206 border_explosion = TRUE;
3209 if (phase == last_phase)
3210 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3211 element_info[border_element].explosion_delay,
3212 element_info[border_element].ignition_delay,
3218 /* if an element just explodes due to another explosion (chain-reaction),
3219 do not immediately end the new explosion when it was the last frame of
3220 the explosion (as it would be done in the following "if"-statement!) */
3221 if (border_explosion && phase == last_phase)
3228 if (phase == first_phase_after_start)
3230 int element = Store2[x][y];
3232 if (element == EL_BLACK_ORB)
3234 Feld[x][y] = Store2[x][y];
3239 else if (phase == half_phase)
3241 int element = Store2[x][y];
3243 if (IS_PLAYER(x, y))
3244 KillHeroUnlessExplosionProtected(x, y);
3245 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3247 Feld[x][y] = Store2[x][y];
3251 else if (element == EL_AMOEBA_TO_DIAMOND)
3252 AmoebeUmwandeln(x, y);
3256 if (phase == last_phase)
3261 printf("::: done: phase == %d\n", phase);
3265 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3268 element = Feld[x][y] = Store[x][y];
3269 Store[x][y] = Store2[x][y] = 0;
3270 GfxElement[x][y] = EL_UNDEFINED;
3272 /* player can escape from explosions and might therefore be still alive */
3273 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3274 element <= EL_PLAYER_IS_EXPLODING_4)
3275 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3277 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3278 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3279 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3282 /* restore probably existing indestructible background element */
3283 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3284 element = Feld[x][y] = Back[x][y];
3287 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3288 GfxDir[x][y] = MV_NO_MOVING;
3289 ChangeDelay[x][y] = 0;
3290 ChangePage[x][y] = -1;
3293 InitField_WithBug2(x, y, FALSE);
3295 InitField(x, y, FALSE);
3297 /* !!! not needed !!! */
3299 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3300 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3303 if (CAN_MOVE(element))
3308 DrawLevelField(x, y);
3310 TestIfElementTouchesCustomElement(x, y);
3312 if (GFX_CRUMBLED(element))
3313 DrawLevelFieldCrumbledSandNeighbours(x, y);
3315 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3316 StorePlayer[x][y] = 0;
3318 if (ELEM_IS_PLAYER(element))
3319 RelocatePlayer(x, y, element);
3322 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3324 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3328 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3330 int stored = Store[x][y];
3331 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3332 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3336 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3338 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3342 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3346 printf("::: %d / %d [%d - %d]\n",
3347 GfxFrame[x][y], phase - delay, phase, delay);
3351 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3352 element_info[GfxElement[x][y]].token_name,
3357 DrawLevelFieldCrumbledSand(x, y);
3359 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3361 DrawLevelElement(x, y, Back[x][y]);
3362 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3364 else if (IS_WALKABLE_UNDER(Back[x][y]))
3366 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3367 DrawLevelElementThruMask(x, y, Back[x][y]);
3369 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3370 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3374 void DynaExplode(int ex, int ey)
3377 int dynabomb_element = Feld[ex][ey];
3378 int dynabomb_size = 1;
3379 boolean dynabomb_xl = FALSE;
3380 struct PlayerInfo *player;
3381 static int xy[4][2] =
3389 if (IS_ACTIVE_BOMB(dynabomb_element))
3391 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3392 dynabomb_size = player->dynabomb_size;
3393 dynabomb_xl = player->dynabomb_xl;
3394 player->dynabombs_left++;
3397 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3399 for (i = 0; i < NUM_DIRECTIONS; i++)
3401 for (j = 1; j <= dynabomb_size; j++)
3403 int x = ex + j * xy[i][0];
3404 int y = ey + j * xy[i][1];
3407 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3410 element = Feld[x][y];
3412 /* do not restart explosions of fields with active bombs */
3413 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3416 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3418 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3419 if (element != EL_EMPTY &&
3420 element != EL_SAND &&
3421 element != EL_EXPLOSION &&
3428 void Bang(int x, int y)
3431 int element = MovingOrBlocked2Element(x, y);
3433 int element = Feld[x][y];
3437 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3439 if (IS_PLAYER(x, y))
3442 struct PlayerInfo *player = PLAYERINFO(x, y);
3444 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3445 player->element_nr);
3450 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3452 if (game.emulation == EMU_SUPAPLEX)
3453 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3455 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3460 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3468 case EL_BD_BUTTERFLY:
3471 case EL_DARK_YAMYAM:
3475 RaiseScoreElement(element);
3476 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3478 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3479 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3480 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3481 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3482 case EL_DYNABOMB_INCREASE_NUMBER:
3483 case EL_DYNABOMB_INCREASE_SIZE:
3484 case EL_DYNABOMB_INCREASE_POWER:
3489 case EL_LAMP_ACTIVE:
3491 case EL_AMOEBA_TO_DIAMOND:
3493 if (IS_PLAYER(x, y))
3494 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3496 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3499 if (CAN_EXPLODE_CROSS(element))
3501 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3505 else if (CAN_EXPLODE_1X1(element))
3506 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3508 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3512 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3515 void SplashAcid(int x, int y)
3518 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3519 (!IN_LEV_FIELD(x - 1, y - 2) ||
3520 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3521 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3523 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3524 (!IN_LEV_FIELD(x + 1, y - 2) ||
3525 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3526 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3528 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3530 /* input: position of element entering acid (obsolete) */
3532 int element = Feld[x][y];
3534 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3537 if (element != EL_ACID_SPLASH_LEFT &&
3538 element != EL_ACID_SPLASH_RIGHT)
3540 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3542 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3543 (!IN_LEV_FIELD(x - 1, y - 1) ||
3544 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3545 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3547 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3548 (!IN_LEV_FIELD(x + 1, y - 1) ||
3549 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3550 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3555 static void InitBeltMovement()
3557 static int belt_base_element[4] =
3559 EL_CONVEYOR_BELT_1_LEFT,
3560 EL_CONVEYOR_BELT_2_LEFT,
3561 EL_CONVEYOR_BELT_3_LEFT,
3562 EL_CONVEYOR_BELT_4_LEFT
3564 static int belt_base_active_element[4] =
3566 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3567 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3568 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3569 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3574 /* set frame order for belt animation graphic according to belt direction */
3575 for (i = 0; i < NUM_BELTS; i++)
3579 for (j = 0; j < NUM_BELT_PARTS; j++)
3581 int element = belt_base_active_element[belt_nr] + j;
3582 int graphic = el2img(element);
3584 if (game.belt_dir[i] == MV_LEFT)
3585 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3587 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3591 for (y = 0; y < lev_fieldy; y++)
3593 for (x = 0; x < lev_fieldx; x++)
3595 int element = Feld[x][y];
3597 for (i = 0; i < NUM_BELTS; i++)
3599 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3601 int e_belt_nr = getBeltNrFromBeltElement(element);
3604 if (e_belt_nr == belt_nr)
3606 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3608 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3616 static void ToggleBeltSwitch(int x, int y)
3618 static int belt_base_element[4] =
3620 EL_CONVEYOR_BELT_1_LEFT,
3621 EL_CONVEYOR_BELT_2_LEFT,
3622 EL_CONVEYOR_BELT_3_LEFT,
3623 EL_CONVEYOR_BELT_4_LEFT
3625 static int belt_base_active_element[4] =
3627 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3628 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3629 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3630 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3632 static int belt_base_switch_element[4] =
3634 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3635 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3636 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3637 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3639 static int belt_move_dir[4] =
3647 int element = Feld[x][y];
3648 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3649 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3650 int belt_dir = belt_move_dir[belt_dir_nr];
3653 if (!IS_BELT_SWITCH(element))
3656 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3657 game.belt_dir[belt_nr] = belt_dir;
3659 if (belt_dir_nr == 3)
3662 /* set frame order for belt animation graphic according to belt direction */
3663 for (i = 0; i < NUM_BELT_PARTS; i++)
3665 int element = belt_base_active_element[belt_nr] + i;
3666 int graphic = el2img(element);
3668 if (belt_dir == MV_LEFT)
3669 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3671 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3674 for (yy = 0; yy < lev_fieldy; yy++)
3676 for (xx = 0; xx < lev_fieldx; xx++)
3678 int element = Feld[xx][yy];
3680 if (IS_BELT_SWITCH(element))
3682 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3684 if (e_belt_nr == belt_nr)
3686 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3687 DrawLevelField(xx, yy);
3690 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3692 int e_belt_nr = getBeltNrFromBeltElement(element);
3694 if (e_belt_nr == belt_nr)
3696 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3698 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3699 DrawLevelField(xx, yy);
3702 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3704 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3706 if (e_belt_nr == belt_nr)
3708 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3710 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3711 DrawLevelField(xx, yy);
3718 static void ToggleSwitchgateSwitch(int x, int y)
3722 game.switchgate_pos = !game.switchgate_pos;
3724 for (yy = 0; yy < lev_fieldy; yy++)
3726 for (xx = 0; xx < lev_fieldx; xx++)
3728 int element = Feld[xx][yy];
3730 if (element == EL_SWITCHGATE_SWITCH_UP ||
3731 element == EL_SWITCHGATE_SWITCH_DOWN)
3733 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3734 DrawLevelField(xx, yy);
3736 else if (element == EL_SWITCHGATE_OPEN ||
3737 element == EL_SWITCHGATE_OPENING)
3739 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3741 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3743 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3746 else if (element == EL_SWITCHGATE_CLOSED ||
3747 element == EL_SWITCHGATE_CLOSING)
3749 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3751 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3753 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3760 static int getInvisibleActiveFromInvisibleElement(int element)
3762 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3763 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3764 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3768 static int getInvisibleFromInvisibleActiveElement(int element)
3770 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3771 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3772 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3776 static void RedrawAllLightSwitchesAndInvisibleElements()
3780 for (y = 0; y < lev_fieldy; y++)
3782 for (x = 0; x < lev_fieldx; x++)
3784 int element = Feld[x][y];
3786 if (element == EL_LIGHT_SWITCH &&
3787 game.light_time_left > 0)
3789 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3790 DrawLevelField(x, y);
3792 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3793 game.light_time_left == 0)
3795 Feld[x][y] = EL_LIGHT_SWITCH;
3796 DrawLevelField(x, y);
3798 else if (element == EL_INVISIBLE_STEELWALL ||
3799 element == EL_INVISIBLE_WALL ||
3800 element == EL_INVISIBLE_SAND)
3802 if (game.light_time_left > 0)
3803 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3805 DrawLevelField(x, y);
3807 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3808 element == EL_INVISIBLE_WALL_ACTIVE ||
3809 element == EL_INVISIBLE_SAND_ACTIVE)
3811 if (game.light_time_left == 0)
3812 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3814 DrawLevelField(x, y);
3820 static void ToggleLightSwitch(int x, int y)
3822 int element = Feld[x][y];
3824 game.light_time_left =
3825 (element == EL_LIGHT_SWITCH ?
3826 level.time_light * FRAMES_PER_SECOND : 0);
3828 RedrawAllLightSwitchesAndInvisibleElements();
3831 static void ActivateTimegateSwitch(int x, int y)
3835 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3837 for (yy = 0; yy < lev_fieldy; yy++)
3839 for (xx = 0; xx < lev_fieldx; xx++)
3841 int element = Feld[xx][yy];
3843 if (element == EL_TIMEGATE_CLOSED ||
3844 element == EL_TIMEGATE_CLOSING)
3846 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3847 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3851 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3853 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3854 DrawLevelField(xx, yy);
3861 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3864 inline static int getElementMoveStepsize(int x, int y)
3866 int element = Feld[x][y];
3867 int direction = MovDir[x][y];
3868 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3869 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3870 int horiz_move = (dx != 0);
3871 int sign = (horiz_move ? dx : dy);
3872 int step = sign * element_info[element].move_stepsize;
3874 /* special values for move stepsize for spring and things on conveyor belt */
3878 if (element == EL_SPRING)
3879 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3880 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3881 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3882 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3884 if (CAN_FALL(element) &&
3885 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3886 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3887 else if (element == EL_SPRING)
3888 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3895 void Impact(int x, int y)
3897 boolean lastline = (y == lev_fieldy-1);
3898 boolean object_hit = FALSE;
3899 boolean impact = (lastline || object_hit);
3900 int element = Feld[x][y];
3901 int smashed = EL_STEELWALL;
3903 if (!lastline) /* check if element below was hit */
3905 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3908 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3909 MovDir[x][y + 1] != MV_DOWN ||
3910 MovPos[x][y + 1] <= TILEY / 2));
3913 object_hit = !IS_FREE(x, y + 1);
3916 /* do not smash moving elements that left the smashed field in time */
3917 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3918 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3922 smashed = MovingOrBlocked2Element(x, y + 1);
3924 impact = (lastline || object_hit);
3927 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3929 SplashAcid(x, y + 1);
3933 /* only reset graphic animation if graphic really changes after impact */
3935 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3937 ResetGfxAnimation(x, y);
3938 DrawLevelField(x, y);
3941 if (impact && CAN_EXPLODE_IMPACT(element))
3946 else if (impact && element == EL_PEARL)
3948 Feld[x][y] = EL_PEARL_BREAKING;
3949 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3952 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
3954 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3959 if (impact && element == EL_AMOEBA_DROP)
3961 if (object_hit && IS_PLAYER(x, y + 1))
3962 KillHeroUnlessEnemyProtected(x, y + 1);
3963 else if (object_hit && smashed == EL_PENGUIN)
3967 Feld[x][y] = EL_AMOEBA_GROWING;
3968 Store[x][y] = EL_AMOEBA_WET;
3970 ResetRandomAnimationValue(x, y);
3975 if (object_hit) /* check which object was hit */
3977 if (CAN_PASS_MAGIC_WALL(element) &&
3978 (smashed == EL_MAGIC_WALL ||
3979 smashed == EL_BD_MAGIC_WALL))
3982 int activated_magic_wall =
3983 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3984 EL_BD_MAGIC_WALL_ACTIVE);
3986 /* activate magic wall / mill */
3987 for (yy = 0; yy < lev_fieldy; yy++)
3988 for (xx = 0; xx < lev_fieldx; xx++)
3989 if (Feld[xx][yy] == smashed)
3990 Feld[xx][yy] = activated_magic_wall;
3992 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3993 game.magic_wall_active = TRUE;
3995 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3996 SND_MAGIC_WALL_ACTIVATING :
3997 SND_BD_MAGIC_WALL_ACTIVATING));
4000 if (IS_PLAYER(x, y + 1))
4002 if (CAN_SMASH_PLAYER(element))
4004 KillHeroUnlessEnemyProtected(x, y + 1);
4008 else if (smashed == EL_PENGUIN)
4010 if (CAN_SMASH_PLAYER(element))
4016 else if (element == EL_BD_DIAMOND)
4018 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4024 else if (((element == EL_SP_INFOTRON ||
4025 element == EL_SP_ZONK) &&
4026 (smashed == EL_SP_SNIKSNAK ||
4027 smashed == EL_SP_ELECTRON ||
4028 smashed == EL_SP_DISK_ORANGE)) ||
4029 (element == EL_SP_INFOTRON &&
4030 smashed == EL_SP_DISK_YELLOW))
4036 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4042 else if (CAN_SMASH_EVERYTHING(element))
4044 if (IS_CLASSIC_ENEMY(smashed) ||
4045 CAN_EXPLODE_SMASHED(smashed))
4050 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4052 if (smashed == EL_LAMP ||
4053 smashed == EL_LAMP_ACTIVE)
4058 else if (smashed == EL_NUT)
4060 Feld[x][y + 1] = EL_NUT_BREAKING;
4061 PlayLevelSound(x, y, SND_NUT_BREAKING);
4062 RaiseScoreElement(EL_NUT);
4065 else if (smashed == EL_PEARL)
4067 Feld[x][y + 1] = EL_PEARL_BREAKING;
4068 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4071 else if (smashed == EL_DIAMOND)
4073 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4074 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4077 else if (IS_BELT_SWITCH(smashed))
4079 ToggleBeltSwitch(x, y + 1);
4081 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4082 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4084 ToggleSwitchgateSwitch(x, y + 1);
4086 else if (smashed == EL_LIGHT_SWITCH ||
4087 smashed == EL_LIGHT_SWITCH_ACTIVE)
4089 ToggleLightSwitch(x, y + 1);
4094 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4097 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4099 CheckTriggeredElementChangeSide(x, y + 1, smashed,
4100 CE_OTHER_IS_SWITCHING, CH_SIDE_TOP);
4101 CheckElementChangeSide(x, y + 1, smashed, element,
4102 CE_SWITCHED, CH_SIDE_TOP);
4107 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4112 /* play sound of magic wall / mill */
4114 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4115 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4117 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4118 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4119 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4120 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4125 /* play sound of object that hits the ground */
4126 if (lastline || object_hit)
4127 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4130 inline static void TurnRoundExt(int x, int y)
4142 { 0, 0 }, { 0, 0 }, { 0, 0 },
4147 int left, right, back;
4151 { MV_DOWN, MV_UP, MV_RIGHT },
4152 { MV_UP, MV_DOWN, MV_LEFT },
4154 { MV_LEFT, MV_RIGHT, MV_DOWN },
4158 { MV_RIGHT, MV_LEFT, MV_UP }
4161 int element = Feld[x][y];
4162 int move_pattern = element_info[element].move_pattern;
4164 int old_move_dir = MovDir[x][y];
4165 int left_dir = turn[old_move_dir].left;
4166 int right_dir = turn[old_move_dir].right;
4167 int back_dir = turn[old_move_dir].back;
4169 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4170 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4171 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4172 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4174 int left_x = x + left_dx, left_y = y + left_dy;
4175 int right_x = x + right_dx, right_y = y + right_dy;
4176 int move_x = x + move_dx, move_y = y + move_dy;
4180 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4182 TestIfBadThingTouchesOtherBadThing(x, y);
4184 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4185 MovDir[x][y] = right_dir;
4186 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4187 MovDir[x][y] = left_dir;
4189 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4191 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4195 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4196 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4198 TestIfBadThingTouchesOtherBadThing(x, y);
4200 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4201 MovDir[x][y] = left_dir;
4202 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4203 MovDir[x][y] = right_dir;
4205 if ((element == EL_SPACESHIP ||
4206 element == EL_SP_SNIKSNAK ||
4207 element == EL_SP_ELECTRON)
4208 && MovDir[x][y] != old_move_dir)
4210 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4214 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4216 TestIfBadThingTouchesOtherBadThing(x, y);
4218 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4219 MovDir[x][y] = left_dir;
4220 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4221 MovDir[x][y] = right_dir;
4223 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4225 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4228 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4230 TestIfBadThingTouchesOtherBadThing(x, y);
4232 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4233 MovDir[x][y] = left_dir;
4234 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4235 MovDir[x][y] = right_dir;
4237 if (MovDir[x][y] != old_move_dir)
4241 else if (element == EL_YAMYAM)
4243 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4244 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4246 if (can_turn_left && can_turn_right)
4247 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4248 else if (can_turn_left)
4249 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4250 else if (can_turn_right)
4251 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4253 MovDir[x][y] = back_dir;
4255 MovDelay[x][y] = 16 + 16 * RND(3);
4257 else if (element == EL_DARK_YAMYAM)
4259 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4261 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4264 if (can_turn_left && can_turn_right)
4265 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4266 else if (can_turn_left)
4267 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4268 else if (can_turn_right)
4269 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4271 MovDir[x][y] = back_dir;
4273 MovDelay[x][y] = 16 + 16 * RND(3);
4275 else if (element == EL_PACMAN)
4277 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4278 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4280 if (can_turn_left && can_turn_right)
4281 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4282 else if (can_turn_left)
4283 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4284 else if (can_turn_right)
4285 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4287 MovDir[x][y] = back_dir;
4289 MovDelay[x][y] = 6 + RND(40);
4291 else if (element == EL_PIG)
4293 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4294 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4295 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4296 boolean should_turn_left, should_turn_right, should_move_on;
4298 int rnd = RND(rnd_value);
4300 should_turn_left = (can_turn_left &&
4302 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4303 y + back_dy + left_dy)));
4304 should_turn_right = (can_turn_right &&
4306 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4307 y + back_dy + right_dy)));
4308 should_move_on = (can_move_on &&
4311 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4312 y + move_dy + left_dy) ||
4313 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4314 y + move_dy + right_dy)));
4316 if (should_turn_left || should_turn_right || should_move_on)
4318 if (should_turn_left && should_turn_right && should_move_on)
4319 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4320 rnd < 2 * rnd_value / 3 ? right_dir :
4322 else if (should_turn_left && should_turn_right)
4323 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4324 else if (should_turn_left && should_move_on)
4325 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4326 else if (should_turn_right && should_move_on)
4327 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4328 else if (should_turn_left)
4329 MovDir[x][y] = left_dir;
4330 else if (should_turn_right)
4331 MovDir[x][y] = right_dir;
4332 else if (should_move_on)
4333 MovDir[x][y] = old_move_dir;
4335 else if (can_move_on && rnd > rnd_value / 8)
4336 MovDir[x][y] = old_move_dir;
4337 else if (can_turn_left && can_turn_right)
4338 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4339 else if (can_turn_left && rnd > rnd_value / 8)
4340 MovDir[x][y] = left_dir;
4341 else if (can_turn_right && rnd > rnd_value/8)
4342 MovDir[x][y] = right_dir;
4344 MovDir[x][y] = back_dir;
4346 xx = x + move_xy[MovDir[x][y]].x;
4347 yy = y + move_xy[MovDir[x][y]].y;
4349 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4350 MovDir[x][y] = old_move_dir;
4354 else if (element == EL_DRAGON)
4356 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4357 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4358 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4360 int rnd = RND(rnd_value);
4363 if (FrameCounter < 1 && x == 0 && y == 29)
4364 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4367 if (can_move_on && rnd > rnd_value / 8)
4368 MovDir[x][y] = old_move_dir;
4369 else if (can_turn_left && can_turn_right)
4370 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4371 else if (can_turn_left && rnd > rnd_value / 8)
4372 MovDir[x][y] = left_dir;
4373 else if (can_turn_right && rnd > rnd_value / 8)
4374 MovDir[x][y] = right_dir;
4376 MovDir[x][y] = back_dir;
4378 xx = x + move_xy[MovDir[x][y]].x;
4379 yy = y + move_xy[MovDir[x][y]].y;
4382 if (FrameCounter < 1 && x == 0 && y == 29)
4383 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4384 xx, yy, Feld[xx][yy],
4389 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4390 MovDir[x][y] = old_move_dir;
4392 if (!IS_FREE(xx, yy))
4393 MovDir[x][y] = old_move_dir;
4397 if (FrameCounter < 1 && x == 0 && y == 29)
4398 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4403 else if (element == EL_MOLE)
4405 boolean can_move_on =
4406 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4407 IS_AMOEBOID(Feld[move_x][move_y]) ||
4408 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4411 boolean can_turn_left =
4412 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4413 IS_AMOEBOID(Feld[left_x][left_y])));
4415 boolean can_turn_right =
4416 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4417 IS_AMOEBOID(Feld[right_x][right_y])));
4419 if (can_turn_left && can_turn_right)
4420 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4421 else if (can_turn_left)
4422 MovDir[x][y] = left_dir;
4424 MovDir[x][y] = right_dir;
4427 if (MovDir[x][y] != old_move_dir)
4430 else if (element == EL_BALLOON)
4432 MovDir[x][y] = game.balloon_dir;
4435 else if (element == EL_SPRING)
4438 if (MovDir[x][y] & MV_HORIZONTAL &&
4439 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4440 MovDir[x][y] = MV_NO_MOVING;
4442 if (MovDir[x][y] & MV_HORIZONTAL &&
4443 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4444 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4445 MovDir[x][y] = MV_NO_MOVING;
4450 else if (element == EL_ROBOT ||
4451 element == EL_SATELLITE ||
4452 element == EL_PENGUIN)
4454 int attr_x = -1, attr_y = -1;
4465 for (i = 0; i < MAX_PLAYERS; i++)
4467 struct PlayerInfo *player = &stored_player[i];
4468 int jx = player->jx, jy = player->jy;
4470 if (!player->active)
4474 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4482 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4488 if (element == EL_PENGUIN)
4491 static int xy[4][2] =
4499 for (i = 0; i < NUM_DIRECTIONS; i++)
4501 int ex = x + xy[i][0];
4502 int ey = y + xy[i][1];
4504 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4513 MovDir[x][y] = MV_NO_MOVING;
4515 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4516 else if (attr_x > x)
4517 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4519 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4520 else if (attr_y > y)
4521 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4523 if (element == EL_ROBOT)
4527 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4528 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4529 Moving2Blocked(x, y, &newx, &newy);
4531 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4532 MovDelay[x][y] = 8 + 8 * !RND(3);
4534 MovDelay[x][y] = 16;
4536 else if (element == EL_PENGUIN)
4542 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4544 boolean first_horiz = RND(2);
4545 int new_move_dir = MovDir[x][y];
4548 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4549 Moving2Blocked(x, y, &newx, &newy);
4551 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4555 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4556 Moving2Blocked(x, y, &newx, &newy);
4558 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4561 MovDir[x][y] = old_move_dir;
4565 else /* (element == EL_SATELLITE) */
4571 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4573 boolean first_horiz = RND(2);
4574 int new_move_dir = MovDir[x][y];
4577 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4578 Moving2Blocked(x, y, &newx, &newy);
4580 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4584 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4585 Moving2Blocked(x, y, &newx, &newy);
4587 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4590 MovDir[x][y] = old_move_dir;
4595 else if (move_pattern == MV_TURNING_LEFT ||
4596 move_pattern == MV_TURNING_RIGHT ||
4597 move_pattern == MV_TURNING_LEFT_RIGHT ||
4598 move_pattern == MV_TURNING_RIGHT_LEFT ||
4599 move_pattern == MV_TURNING_RANDOM ||
4600 move_pattern == MV_ALL_DIRECTIONS)
4602 boolean can_turn_left =
4603 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4604 boolean can_turn_right =
4605 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4607 if (move_pattern == MV_TURNING_LEFT)
4608 MovDir[x][y] = left_dir;
4609 else if (move_pattern == MV_TURNING_RIGHT)
4610 MovDir[x][y] = right_dir;
4611 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4612 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4613 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4614 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4615 else if (move_pattern == MV_TURNING_RANDOM)
4616 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4617 can_turn_right && !can_turn_left ? right_dir :
4618 RND(2) ? left_dir : right_dir);
4619 else if (can_turn_left && can_turn_right)
4620 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4621 else if (can_turn_left)
4622 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4623 else if (can_turn_right)
4624 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4626 MovDir[x][y] = back_dir;
4628 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4630 else if (move_pattern == MV_HORIZONTAL ||
4631 move_pattern == MV_VERTICAL)
4633 if (move_pattern & old_move_dir)
4634 MovDir[x][y] = back_dir;
4635 else if (move_pattern == MV_HORIZONTAL)
4636 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4637 else if (move_pattern == MV_VERTICAL)
4638 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4640 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4642 else if (move_pattern & MV_ANY_DIRECTION)
4644 MovDir[x][y] = move_pattern;
4645 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4647 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4649 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4650 MovDir[x][y] = left_dir;
4651 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4652 MovDir[x][y] = right_dir;
4654 if (MovDir[x][y] != old_move_dir)
4655 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4657 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4659 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4660 MovDir[x][y] = right_dir;
4661 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4662 MovDir[x][y] = left_dir;
4664 if (MovDir[x][y] != old_move_dir)
4665 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4667 else if (move_pattern == MV_TOWARDS_PLAYER ||
4668 move_pattern == MV_AWAY_FROM_PLAYER)
4670 int attr_x = -1, attr_y = -1;
4672 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4683 for (i = 0; i < MAX_PLAYERS; i++)
4685 struct PlayerInfo *player = &stored_player[i];
4686 int jx = player->jx, jy = player->jy;
4688 if (!player->active)
4692 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4700 MovDir[x][y] = MV_NO_MOVING;
4702 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4703 else if (attr_x > x)
4704 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4706 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4707 else if (attr_y > y)
4708 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4710 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4712 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4714 boolean first_horiz = RND(2);
4715 int new_move_dir = MovDir[x][y];
4718 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4719 Moving2Blocked(x, y, &newx, &newy);
4721 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4725 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4726 Moving2Blocked(x, y, &newx, &newy);
4728 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4731 MovDir[x][y] = old_move_dir;
4734 else if (move_pattern == MV_WHEN_PUSHED ||
4735 move_pattern == MV_WHEN_DROPPED)
4737 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4738 MovDir[x][y] = MV_NO_MOVING;
4742 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4744 static int test_xy[7][2] =
4754 static int test_dir[7] =
4764 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4765 int move_preference = -1000000; /* start with very low preference */
4766 int new_move_dir = MV_NO_MOVING;
4767 int start_test = RND(4);
4770 for (i = 0; i < NUM_DIRECTIONS; i++)
4772 int move_dir = test_dir[start_test + i];
4773 int move_dir_preference;
4775 xx = x + test_xy[start_test + i][0];
4776 yy = y + test_xy[start_test + i][1];
4778 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4779 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4781 new_move_dir = move_dir;
4786 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4789 move_dir_preference = -1 * RunnerVisit[xx][yy];
4790 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4791 move_dir_preference = PlayerVisit[xx][yy];
4793 if (move_dir_preference > move_preference)
4795 /* prefer field that has not been visited for the longest time */
4796 move_preference = move_dir_preference;
4797 new_move_dir = move_dir;
4799 else if (move_dir_preference == move_preference &&
4800 move_dir == old_move_dir)
4802 /* prefer last direction when all directions are preferred equally */
4803 move_preference = move_dir_preference;
4804 new_move_dir = move_dir;
4808 MovDir[x][y] = new_move_dir;
4809 if (old_move_dir != new_move_dir)
4814 static void TurnRound(int x, int y)
4816 int direction = MovDir[x][y];
4819 GfxDir[x][y] = MovDir[x][y];
4825 GfxDir[x][y] = MovDir[x][y];
4828 if (direction != MovDir[x][y])
4833 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4836 GfxAction[x][y] = ACTION_WAITING;
4840 static boolean JustBeingPushed(int x, int y)
4844 for (i = 0; i < MAX_PLAYERS; i++)
4846 struct PlayerInfo *player = &stored_player[i];
4848 if (player->active && player->is_pushing && player->MovPos)
4850 int next_jx = player->jx + (player->jx - player->last_jx);
4851 int next_jy = player->jy + (player->jy - player->last_jy);
4853 if (x == next_jx && y == next_jy)
4861 void StartMoving(int x, int y)
4864 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4866 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4867 int element = Feld[x][y];
4873 if (MovDelay[x][y] == 0)
4874 GfxAction[x][y] = ACTION_DEFAULT;
4876 /* !!! this should be handled more generic (not only for mole) !!! */
4877 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4878 GfxAction[x][y] = ACTION_DEFAULT;
4881 if (CAN_FALL(element) && y < lev_fieldy - 1)
4883 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4884 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4885 if (JustBeingPushed(x, y))
4888 if (element == EL_QUICKSAND_FULL)
4890 if (IS_FREE(x, y + 1))
4892 InitMovingField(x, y, MV_DOWN);
4893 started_moving = TRUE;
4895 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4896 Store[x][y] = EL_ROCK;
4898 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4900 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4903 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4905 if (!MovDelay[x][y])
4906 MovDelay[x][y] = TILEY + 1;
4915 Feld[x][y] = EL_QUICKSAND_EMPTY;
4916 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4917 Store[x][y + 1] = Store[x][y];
4920 PlayLevelSoundAction(x, y, ACTION_FILLING);
4922 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4926 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4927 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4929 InitMovingField(x, y, MV_DOWN);
4930 started_moving = TRUE;
4932 Feld[x][y] = EL_QUICKSAND_FILLING;
4933 Store[x][y] = element;
4935 PlayLevelSoundAction(x, y, ACTION_FILLING);
4937 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4940 else if (element == EL_MAGIC_WALL_FULL)
4942 if (IS_FREE(x, y + 1))
4944 InitMovingField(x, y, MV_DOWN);
4945 started_moving = TRUE;
4947 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4948 Store[x][y] = EL_CHANGED(Store[x][y]);
4950 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4952 if (!MovDelay[x][y])
4953 MovDelay[x][y] = TILEY/4 + 1;
4962 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4963 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4964 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4968 else if (element == EL_BD_MAGIC_WALL_FULL)
4970 if (IS_FREE(x, y + 1))
4972 InitMovingField(x, y, MV_DOWN);
4973 started_moving = TRUE;
4975 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4976 Store[x][y] = EL_CHANGED2(Store[x][y]);
4978 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4980 if (!MovDelay[x][y])
4981 MovDelay[x][y] = TILEY/4 + 1;
4990 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4991 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4992 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4996 else if (CAN_PASS_MAGIC_WALL(element) &&
4997 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4998 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5000 InitMovingField(x, y, MV_DOWN);
5001 started_moving = TRUE;
5004 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5005 EL_BD_MAGIC_WALL_FILLING);
5006 Store[x][y] = element;
5009 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5011 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5014 SplashAcid(x, y + 1);
5016 InitMovingField(x, y, MV_DOWN);
5017 started_moving = TRUE;
5019 Store[x][y] = EL_ACID;
5021 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5022 GfxAction[x][y + 1] = ACTION_ACTIVE;
5026 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
5027 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5028 (Feld[x][y + 1] == EL_BLOCKED)) ||
5029 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5030 CAN_SMASH(element) && WasJustFalling[x][y] &&
5031 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
5035 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5036 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5037 WasJustMoving[x][y] && !Pushed[x][y + 1])
5039 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5040 WasJustMoving[x][y])
5045 /* this is needed for a special case not covered by calling "Impact()"
5046 from "ContinueMoving()": if an element moves to a tile directly below
5047 another element which was just falling on that tile (which was empty
5048 in the previous frame), the falling element above would just stop
5049 instead of smashing the element below (in previous version, the above
5050 element was just checked for "moving" instead of "falling", resulting
5051 in incorrect smashes caused by horizontal movement of the above
5052 element; also, the case of the player being the element to smash was
5053 simply not covered here... :-/ ) */
5056 WasJustMoving[x][y] = 0;
5057 WasJustFalling[x][y] = 0;
5062 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5064 if (MovDir[x][y] == MV_NO_MOVING)
5066 InitMovingField(x, y, MV_DOWN);
5067 started_moving = TRUE;
5070 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5072 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5073 MovDir[x][y] = MV_DOWN;
5075 InitMovingField(x, y, MV_DOWN);
5076 started_moving = TRUE;
5078 else if (element == EL_AMOEBA_DROP)
5080 Feld[x][y] = EL_AMOEBA_GROWING;
5081 Store[x][y] = EL_AMOEBA_WET;
5083 /* Store[x][y + 1] must be zero, because:
5084 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5087 #if OLD_GAME_BEHAVIOUR
5088 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5090 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5091 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5092 element != EL_DX_SUPABOMB)
5095 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5096 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5097 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5098 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5101 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5102 (IS_FREE(x - 1, y + 1) ||
5103 Feld[x - 1][y + 1] == EL_ACID));
5104 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5105 (IS_FREE(x + 1, y + 1) ||
5106 Feld[x + 1][y + 1] == EL_ACID));
5107 boolean can_fall_any = (can_fall_left || can_fall_right);
5108 boolean can_fall_both = (can_fall_left && can_fall_right);
5110 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5112 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5114 if (slippery_type == SLIPPERY_ONLY_LEFT)
5115 can_fall_right = FALSE;
5116 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5117 can_fall_left = FALSE;
5118 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5119 can_fall_right = FALSE;
5120 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5121 can_fall_left = FALSE;
5123 can_fall_any = (can_fall_left || can_fall_right);
5124 can_fall_both = (can_fall_left && can_fall_right);
5129 if (can_fall_both &&
5130 (game.emulation != EMU_BOULDERDASH &&
5131 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5132 can_fall_left = !(can_fall_right = RND(2));
5134 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5135 started_moving = TRUE;
5139 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5141 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5144 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5145 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5146 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5147 int belt_dir = game.belt_dir[belt_nr];
5149 if ((belt_dir == MV_LEFT && left_is_free) ||
5150 (belt_dir == MV_RIGHT && right_is_free))
5153 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5156 InitMovingField(x, y, belt_dir);
5157 started_moving = TRUE;
5160 Pushed[x][y] = TRUE;
5161 Pushed[nextx][y] = TRUE;
5164 GfxAction[x][y] = ACTION_DEFAULT;
5168 MovDir[x][y] = 0; /* if element was moving, stop it */
5173 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5174 if (CAN_MOVE(element) && !started_moving)
5176 int move_pattern = element_info[element].move_pattern;
5179 Moving2Blocked(x, y, &newx, &newy);
5182 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5185 if ((element == EL_SATELLITE ||
5186 element == EL_BALLOON ||
5187 element == EL_SPRING)
5188 && JustBeingPushed(x, y))
5193 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5194 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5195 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5198 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5199 element, element_info[element].token_name,
5200 WasJustMoving[x][y],
5201 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5202 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5203 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5204 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5208 WasJustMoving[x][y] = 0;
5211 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5214 if (Feld[x][y] != element) /* element has changed */
5216 element = Feld[x][y];
5217 move_pattern = element_info[element].move_pattern;
5219 if (!CAN_MOVE(element))
5223 if (Feld[x][y] != element) /* element has changed */
5231 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5232 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5234 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5236 Moving2Blocked(x, y, &newx, &newy);
5237 if (Feld[newx][newy] == EL_BLOCKED)
5238 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5244 if (FrameCounter < 1 && x == 0 && y == 29)
5245 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5248 if (!MovDelay[x][y]) /* start new movement phase */
5250 /* all objects that can change their move direction after each step
5251 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5253 if (element != EL_YAMYAM &&
5254 element != EL_DARK_YAMYAM &&
5255 element != EL_PACMAN &&
5256 !(move_pattern & MV_ANY_DIRECTION) &&
5257 move_pattern != MV_TURNING_LEFT &&
5258 move_pattern != MV_TURNING_RIGHT &&
5259 move_pattern != MV_TURNING_LEFT_RIGHT &&
5260 move_pattern != MV_TURNING_RIGHT_LEFT &&
5261 move_pattern != MV_TURNING_RANDOM)
5266 if (FrameCounter < 1 && x == 0 && y == 29)
5267 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5270 if (MovDelay[x][y] && (element == EL_BUG ||
5271 element == EL_SPACESHIP ||
5272 element == EL_SP_SNIKSNAK ||
5273 element == EL_SP_ELECTRON ||
5274 element == EL_MOLE))
5275 DrawLevelField(x, y);
5279 if (MovDelay[x][y]) /* wait some time before next movement */
5284 if (element == EL_YAMYAM)
5287 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5288 DrawLevelElementAnimation(x, y, element);
5292 if (MovDelay[x][y]) /* element still has to wait some time */
5295 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5296 ResetGfxAnimation(x, y);
5300 if (GfxAction[x][y] != ACTION_WAITING)
5301 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5303 GfxAction[x][y] = ACTION_WAITING;
5307 if (element == EL_ROBOT ||
5309 element == EL_PACMAN ||
5311 element == EL_YAMYAM ||
5312 element == EL_DARK_YAMYAM)
5315 DrawLevelElementAnimation(x, y, element);
5317 DrawLevelElementAnimationIfNeeded(x, y, element);
5319 PlayLevelSoundAction(x, y, ACTION_WAITING);
5321 else if (element == EL_SP_ELECTRON)
5322 DrawLevelElementAnimationIfNeeded(x, y, element);
5323 else if (element == EL_DRAGON)
5326 int dir = MovDir[x][y];
5327 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5328 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5329 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5330 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5331 dir == MV_UP ? IMG_FLAMES_1_UP :
5332 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5333 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5336 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5339 GfxAction[x][y] = ACTION_ATTACKING;
5341 if (IS_PLAYER(x, y))
5342 DrawPlayerField(x, y);
5344 DrawLevelField(x, y);
5346 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5348 for (i = 1; i <= 3; i++)
5350 int xx = x + i * dx;
5351 int yy = y + i * dy;
5352 int sx = SCREENX(xx);
5353 int sy = SCREENY(yy);
5354 int flame_graphic = graphic + (i - 1);
5356 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5361 int flamed = MovingOrBlocked2Element(xx, yy);
5365 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5367 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5368 RemoveMovingField(xx, yy);
5370 RemoveField(xx, yy);
5372 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5375 RemoveMovingField(xx, yy);
5379 if (ChangeDelay[xx][yy])
5380 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5381 Feld[xx][yy] == EL_BLOCKED));
5385 ChangeDelay[xx][yy] = 0;
5387 Feld[xx][yy] = EL_FLAMES;
5388 if (IN_SCR_FIELD(sx, sy))
5390 DrawLevelFieldCrumbledSand(xx, yy);
5391 DrawGraphic(sx, sy, flame_graphic, frame);
5396 if (Feld[xx][yy] == EL_FLAMES)
5397 Feld[xx][yy] = EL_EMPTY;
5398 DrawLevelField(xx, yy);
5403 if (MovDelay[x][y]) /* element still has to wait some time */
5405 PlayLevelSoundAction(x, y, ACTION_WAITING);
5411 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5412 for all other elements GfxAction will be set by InitMovingField() */
5413 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5414 GfxAction[x][y] = ACTION_MOVING;
5418 /* now make next step */
5420 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5422 if (DONT_COLLIDE_WITH(element) &&
5423 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5424 !PLAYER_ENEMY_PROTECTED(newx, newy))
5427 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5431 /* player killed by element which is deadly when colliding with */
5433 KillHero(PLAYERINFO(newx, newy));
5440 else if (CAN_MOVE_INTO_ACID(element) &&
5441 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5442 (MovDir[x][y] == MV_DOWN ||
5443 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5445 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5446 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5450 else if ((element == EL_PENGUIN ||
5451 element == EL_ROBOT ||
5452 element == EL_SATELLITE ||
5453 element == EL_BALLOON ||
5454 IS_CUSTOM_ELEMENT(element)) &&
5455 IN_LEV_FIELD(newx, newy) &&
5456 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5459 SplashAcid(newx, newy);
5460 Store[x][y] = EL_ACID;
5462 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5464 if (Feld[newx][newy] == EL_EXIT_OPEN)
5468 DrawLevelField(x, y);
5470 Feld[x][y] = EL_EMPTY;
5471 DrawLevelField(x, y);
5474 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5475 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5476 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5478 local_player->friends_still_needed--;
5479 if (!local_player->friends_still_needed &&
5480 !local_player->GameOver && AllPlayersGone)
5481 local_player->LevelSolved = local_player->GameOver = TRUE;
5485 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5487 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5488 DrawLevelField(newx, newy);
5490 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5492 else if (!IS_FREE(newx, newy))
5494 GfxAction[x][y] = ACTION_WAITING;
5496 if (IS_PLAYER(x, y))
5497 DrawPlayerField(x, y);
5499 DrawLevelField(x, y);
5504 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5506 if (IS_FOOD_PIG(Feld[newx][newy]))
5508 if (IS_MOVING(newx, newy))
5509 RemoveMovingField(newx, newy);
5512 Feld[newx][newy] = EL_EMPTY;
5513 DrawLevelField(newx, newy);
5516 PlayLevelSound(x, y, SND_PIG_DIGGING);
5518 else if (!IS_FREE(newx, newy))
5520 if (IS_PLAYER(x, y))
5521 DrawPlayerField(x, y);
5523 DrawLevelField(x, y);
5532 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5535 else if (IS_CUSTOM_ELEMENT(element) &&
5536 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5540 !IS_FREE(newx, newy)
5545 int new_element = Feld[newx][newy];
5548 printf("::: '%s' digs '%s' [%d]\n",
5549 element_info[element].token_name,
5550 element_info[Feld[newx][newy]].token_name,
5551 StorePlayer[newx][newy]);
5554 if (!IS_FREE(newx, newy))
5556 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5557 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5560 /* no element can dig solid indestructible elements */
5561 if (IS_INDESTRUCTIBLE(new_element) &&
5562 !IS_DIGGABLE(new_element) &&
5563 !IS_COLLECTIBLE(new_element))
5566 if (AmoebaNr[newx][newy] &&
5567 (new_element == EL_AMOEBA_FULL ||
5568 new_element == EL_BD_AMOEBA ||
5569 new_element == EL_AMOEBA_GROWING))
5571 AmoebaCnt[AmoebaNr[newx][newy]]--;
5572 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5575 if (IS_MOVING(newx, newy))
5576 RemoveMovingField(newx, newy);
5579 RemoveField(newx, newy);
5580 DrawLevelField(newx, newy);
5583 PlayLevelSoundAction(x, y, action);
5588 Store[newx][newy] = EL_EMPTY;
5589 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5590 Store[newx][newy] = element_info[element].move_leave_element;
5592 Store[newx][newy] = EL_EMPTY;
5593 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5594 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5595 Store[newx][newy] = element_info[element].move_leave_element;
5598 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5599 element_info[element].can_leave_element = TRUE;
5602 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5604 RunnerVisit[x][y] = FrameCounter;
5605 PlayerVisit[x][y] /= 8; /* expire player visit path */
5611 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5613 if (!IS_FREE(newx, newy))
5615 if (IS_PLAYER(x, y))
5616 DrawPlayerField(x, y);
5618 DrawLevelField(x, y);
5624 boolean wanna_flame = !RND(10);
5625 int dx = newx - x, dy = newy - y;
5626 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5627 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5628 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5629 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5630 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5631 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5634 IS_CLASSIC_ENEMY(element1) ||
5635 IS_CLASSIC_ENEMY(element2)) &&
5636 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5637 element1 != EL_FLAMES && element2 != EL_FLAMES)
5640 ResetGfxAnimation(x, y);
5641 GfxAction[x][y] = ACTION_ATTACKING;
5644 if (IS_PLAYER(x, y))
5645 DrawPlayerField(x, y);
5647 DrawLevelField(x, y);
5649 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5651 MovDelay[x][y] = 50;
5655 RemoveField(newx, newy);
5657 Feld[newx][newy] = EL_FLAMES;
5658 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5661 RemoveField(newx1, newy1);
5663 Feld[newx1][newy1] = EL_FLAMES;
5665 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5668 RemoveField(newx2, newy2);
5670 Feld[newx2][newy2] = EL_FLAMES;
5677 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5678 Feld[newx][newy] == EL_DIAMOND)
5680 if (IS_MOVING(newx, newy))
5681 RemoveMovingField(newx, newy);
5684 Feld[newx][newy] = EL_EMPTY;
5685 DrawLevelField(newx, newy);
5688 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5690 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5691 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5693 if (AmoebaNr[newx][newy])
5695 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5696 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5697 Feld[newx][newy] == EL_BD_AMOEBA)
5698 AmoebaCnt[AmoebaNr[newx][newy]]--;
5703 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5705 if (IS_MOVING(newx, newy))
5708 RemoveMovingField(newx, newy);
5712 Feld[newx][newy] = EL_EMPTY;
5713 DrawLevelField(newx, newy);
5716 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5718 else if ((element == EL_PACMAN || element == EL_MOLE)
5719 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5721 if (AmoebaNr[newx][newy])
5723 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5724 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5725 Feld[newx][newy] == EL_BD_AMOEBA)
5726 AmoebaCnt[AmoebaNr[newx][newy]]--;
5729 if (element == EL_MOLE)
5731 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5732 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5734 ResetGfxAnimation(x, y);
5735 GfxAction[x][y] = ACTION_DIGGING;
5736 DrawLevelField(x, y);
5738 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5740 return; /* wait for shrinking amoeba */
5742 else /* element == EL_PACMAN */
5744 Feld[newx][newy] = EL_EMPTY;
5745 DrawLevelField(newx, newy);
5746 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5749 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5750 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5751 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5753 /* wait for shrinking amoeba to completely disappear */
5756 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5758 /* object was running against a wall */
5763 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5764 DrawLevelElementAnimation(x, y, element);
5766 if (element == EL_BUG ||
5767 element == EL_SPACESHIP ||
5768 element == EL_SP_SNIKSNAK)
5769 DrawLevelField(x, y);
5770 else if (element == EL_MOLE)
5771 DrawLevelField(x, y);
5772 else if (element == EL_BD_BUTTERFLY ||
5773 element == EL_BD_FIREFLY)
5774 DrawLevelElementAnimationIfNeeded(x, y, element);
5775 else if (element == EL_SATELLITE)
5776 DrawLevelElementAnimationIfNeeded(x, y, element);
5777 else if (element == EL_SP_ELECTRON)
5778 DrawLevelElementAnimationIfNeeded(x, y, element);
5781 if (DONT_TOUCH(element))
5782 TestIfBadThingTouchesHero(x, y);
5785 PlayLevelSoundAction(x, y, ACTION_WAITING);
5791 InitMovingField(x, y, MovDir[x][y]);
5793 PlayLevelSoundAction(x, y, ACTION_MOVING);
5797 ContinueMoving(x, y);
5800 void ContinueMoving(int x, int y)
5802 int element = Feld[x][y];
5803 struct ElementInfo *ei = &element_info[element];
5804 int direction = MovDir[x][y];
5805 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5806 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5807 int newx = x + dx, newy = y + dy;
5809 int nextx = newx + dx, nexty = newy + dy;
5812 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5813 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5815 boolean pushed_by_player = Pushed[x][y];
5818 MovPos[x][y] += getElementMoveStepsize(x, y);
5821 if (pushed_by_player && IS_PLAYER(x, y))
5823 /* special case: moving object pushed by player */
5824 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5827 if (pushed_by_player) /* special case: moving object pushed by player */
5828 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5831 if (ABS(MovPos[x][y]) < TILEX)
5833 DrawLevelField(x, y);
5835 return; /* element is still moving */
5838 /* element reached destination field */
5840 Feld[x][y] = EL_EMPTY;
5841 Feld[newx][newy] = element;
5842 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5844 if (element == EL_MOLE)
5846 Feld[x][y] = EL_SAND;
5848 DrawLevelFieldCrumbledSandNeighbours(x, y);
5850 else if (element == EL_QUICKSAND_FILLING)
5852 element = Feld[newx][newy] = get_next_element(element);
5853 Store[newx][newy] = Store[x][y];
5855 else if (element == EL_QUICKSAND_EMPTYING)
5857 Feld[x][y] = get_next_element(element);
5858 element = Feld[newx][newy] = Store[x][y];
5860 else if (element == EL_MAGIC_WALL_FILLING)
5862 element = Feld[newx][newy] = get_next_element(element);
5863 if (!game.magic_wall_active)
5864 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5865 Store[newx][newy] = Store[x][y];
5867 else if (element == EL_MAGIC_WALL_EMPTYING)
5869 Feld[x][y] = get_next_element(element);
5870 if (!game.magic_wall_active)
5871 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5872 element = Feld[newx][newy] = Store[x][y];
5874 else if (element == EL_BD_MAGIC_WALL_FILLING)
5876 element = Feld[newx][newy] = get_next_element(element);
5877 if (!game.magic_wall_active)
5878 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5879 Store[newx][newy] = Store[x][y];
5881 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5883 Feld[x][y] = get_next_element(element);
5884 if (!game.magic_wall_active)
5885 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5886 element = Feld[newx][newy] = Store[x][y];
5888 else if (element == EL_AMOEBA_DROPPING)
5890 Feld[x][y] = get_next_element(element);
5891 element = Feld[newx][newy] = Store[x][y];
5893 else if (element == EL_SOKOBAN_OBJECT)
5896 Feld[x][y] = Back[x][y];
5898 if (Back[newx][newy])
5899 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5901 Back[x][y] = Back[newx][newy] = 0;
5903 else if (Store[x][y] == EL_ACID)
5905 element = Feld[newx][newy] = EL_ACID;
5908 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5909 ei->move_leave_element != EL_EMPTY &&
5910 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5911 Store[x][y] != EL_EMPTY))
5913 /* some elements can leave other elements behind after moving */
5915 Feld[x][y] = ei->move_leave_element;
5916 InitField(x, y, FALSE);
5918 if (GFX_CRUMBLED(Feld[x][y]))
5919 DrawLevelFieldCrumbledSandNeighbours(x, y);
5923 Store[x][y] = EL_EMPTY;
5924 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5925 MovDelay[newx][newy] = 0;
5927 if (CAN_CHANGE(element))
5929 /* copy element change control values to new field */
5930 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5931 ChangePage[newx][newy] = ChangePage[x][y];
5932 Changed[newx][newy] = Changed[x][y];
5933 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5936 ChangeDelay[x][y] = 0;
5937 ChangePage[x][y] = -1;
5938 Changed[x][y] = CE_BITMASK_DEFAULT;
5939 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5941 /* copy animation control values to new field */
5942 GfxFrame[newx][newy] = GfxFrame[x][y];
5943 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5944 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5945 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5947 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5949 ResetGfxAnimation(x, y); /* reset animation values for old field */
5952 /* some elements can leave other elements behind after moving */
5953 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5954 ei->move_leave_element != EL_EMPTY &&
5955 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5956 ei->can_leave_element_last))
5958 Feld[x][y] = ei->move_leave_element;
5959 InitField(x, y, FALSE);
5961 if (GFX_CRUMBLED(Feld[x][y]))
5962 DrawLevelFieldCrumbledSandNeighbours(x, y);
5965 ei->can_leave_element_last = ei->can_leave_element;
5966 ei->can_leave_element = FALSE;
5970 /* 2.1.1 (does not work correctly for spring) */
5971 if (!CAN_MOVE(element))
5972 MovDir[newx][newy] = 0;
5976 /* (does not work for falling objects that slide horizontally) */
5977 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5978 MovDir[newx][newy] = 0;
5981 if (!CAN_MOVE(element) ||
5982 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5983 MovDir[newx][newy] = 0;
5987 if (!CAN_MOVE(element) ||
5988 (CAN_FALL(element) && direction == MV_DOWN))
5989 GfxDir[x][y] = MovDir[newx][newy] = 0;
5991 if (!CAN_MOVE(element) ||
5992 (CAN_FALL(element) && direction == MV_DOWN &&
5993 (element == EL_SPRING ||
5994 element_info[element].move_pattern == MV_WHEN_PUSHED ||
5995 element_info[element].move_pattern == MV_WHEN_DROPPED)))
5996 GfxDir[x][y] = MovDir[newx][newy] = 0;
6002 DrawLevelField(x, y);
6003 DrawLevelField(newx, newy);
6005 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6007 /* prevent pushed element from moving on in pushed direction */
6008 if (pushed_by_player && CAN_MOVE(element) &&
6009 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6010 !(element_info[element].move_pattern & direction))
6011 TurnRound(newx, newy);
6014 /* prevent elements on conveyor belt from moving on in last direction */
6015 if (pushed_by_conveyor && CAN_FALL(element) &&
6016 direction & MV_HORIZONTAL)
6017 MovDir[newx][newy] = 0;
6020 if (!pushed_by_player)
6022 WasJustMoving[newx][newy] = 3;
6024 if (CAN_FALL(element) && direction == MV_DOWN)
6025 WasJustFalling[newx][newy] = 3;
6028 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6030 TestIfBadThingTouchesHero(newx, newy);
6031 TestIfBadThingTouchesFriend(newx, newy);
6033 if (!IS_CUSTOM_ELEMENT(element))
6034 TestIfBadThingTouchesOtherBadThing(newx, newy);
6036 else if (element == EL_PENGUIN)
6037 TestIfFriendTouchesBadThing(newx, newy);
6039 if (CAN_FALL(element) && direction == MV_DOWN &&
6040 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6044 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6048 if (ChangePage[newx][newy] != -1) /* delayed change */
6049 ChangeElement(newx, newy, ChangePage[newx][newy]);
6054 TestIfElementHitsCustomElement(newx, newy, direction);
6058 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6060 int hitting_element = Feld[newx][newy];
6062 /* !!! fix side (direction) orientation here and elsewhere !!! */
6063 CheckElementChangeSide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6067 if (IN_LEV_FIELD(nextx, nexty))
6069 int opposite_direction = MV_DIR_OPPOSITE(direction);
6070 int hitting_side = direction;
6071 int touched_side = opposite_direction;
6072 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6073 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6074 MovDir[nextx][nexty] != direction ||
6075 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6081 CheckElementChangeSide(nextx, nexty, touched_element,
6082 CE_HIT_BY_SOMETHING, opposite_direction);
6084 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6085 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6087 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6089 struct ElementChangeInfo *change =
6090 &element_info[hitting_element].change_page[i];
6092 if (change->can_change &&
6093 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6094 change->trigger_side & touched_side &&
6095 change->trigger_element == touched_element)
6097 CheckElementChangePage(newx, newy, hitting_element,
6098 touched_element, CE_OTHER_IS_HITTING, i);
6104 if (IS_CUSTOM_ELEMENT(touched_element) &&
6105 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6107 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6109 struct ElementChangeInfo *change =
6110 &element_info[touched_element].change_page[i];
6112 if (change->can_change &&
6113 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6114 change->trigger_side & hitting_side &&
6115 change->trigger_element == hitting_element)
6117 CheckElementChangePage(nextx, nexty, touched_element,
6118 hitting_element, CE_OTHER_GETS_HIT, i);
6129 TestIfPlayerTouchesCustomElement(newx, newy);
6130 TestIfElementTouchesCustomElement(newx, newy);
6133 int AmoebeNachbarNr(int ax, int ay)
6136 int element = Feld[ax][ay];
6138 static int xy[4][2] =
6146 for (i = 0; i < NUM_DIRECTIONS; i++)
6148 int x = ax + xy[i][0];
6149 int y = ay + xy[i][1];
6151 if (!IN_LEV_FIELD(x, y))
6154 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6155 group_nr = AmoebaNr[x][y];
6161 void AmoebenVereinigen(int ax, int ay)
6163 int i, x, y, xx, yy;
6164 int new_group_nr = AmoebaNr[ax][ay];
6165 static int xy[4][2] =
6173 if (new_group_nr == 0)
6176 for (i = 0; i < NUM_DIRECTIONS; i++)
6181 if (!IN_LEV_FIELD(x, y))
6184 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6185 Feld[x][y] == EL_BD_AMOEBA ||
6186 Feld[x][y] == EL_AMOEBA_DEAD) &&
6187 AmoebaNr[x][y] != new_group_nr)
6189 int old_group_nr = AmoebaNr[x][y];
6191 if (old_group_nr == 0)
6194 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6195 AmoebaCnt[old_group_nr] = 0;
6196 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6197 AmoebaCnt2[old_group_nr] = 0;
6199 for (yy = 0; yy < lev_fieldy; yy++)
6201 for (xx = 0; xx < lev_fieldx; xx++)
6203 if (AmoebaNr[xx][yy] == old_group_nr)
6204 AmoebaNr[xx][yy] = new_group_nr;
6211 void AmoebeUmwandeln(int ax, int ay)
6215 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6217 int group_nr = AmoebaNr[ax][ay];
6222 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6223 printf("AmoebeUmwandeln(): This should never happen!\n");
6228 for (y = 0; y < lev_fieldy; y++)
6230 for (x = 0; x < lev_fieldx; x++)
6232 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6235 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6239 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6240 SND_AMOEBA_TURNING_TO_GEM :
6241 SND_AMOEBA_TURNING_TO_ROCK));
6246 static int xy[4][2] =
6254 for (i = 0; i < NUM_DIRECTIONS; i++)
6259 if (!IN_LEV_FIELD(x, y))
6262 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6264 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6265 SND_AMOEBA_TURNING_TO_GEM :
6266 SND_AMOEBA_TURNING_TO_ROCK));
6273 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6276 int group_nr = AmoebaNr[ax][ay];
6277 boolean done = FALSE;
6282 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6283 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6288 for (y = 0; y < lev_fieldy; y++)
6290 for (x = 0; x < lev_fieldx; x++)
6292 if (AmoebaNr[x][y] == group_nr &&
6293 (Feld[x][y] == EL_AMOEBA_DEAD ||
6294 Feld[x][y] == EL_BD_AMOEBA ||
6295 Feld[x][y] == EL_AMOEBA_GROWING))
6298 Feld[x][y] = new_element;
6299 InitField(x, y, FALSE);
6300 DrawLevelField(x, y);
6307 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6308 SND_BD_AMOEBA_TURNING_TO_ROCK :
6309 SND_BD_AMOEBA_TURNING_TO_GEM));
6312 void AmoebeWaechst(int x, int y)
6314 static unsigned long sound_delay = 0;
6315 static unsigned long sound_delay_value = 0;
6317 if (!MovDelay[x][y]) /* start new growing cycle */
6321 if (DelayReached(&sound_delay, sound_delay_value))
6324 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6326 if (Store[x][y] == EL_BD_AMOEBA)
6327 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6329 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6331 sound_delay_value = 30;
6335 if (MovDelay[x][y]) /* wait some time before growing bigger */
6338 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6340 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6341 6 - MovDelay[x][y]);
6343 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6346 if (!MovDelay[x][y])
6348 Feld[x][y] = Store[x][y];
6350 DrawLevelField(x, y);
6355 void AmoebaDisappearing(int x, int y)
6357 static unsigned long sound_delay = 0;
6358 static unsigned long sound_delay_value = 0;
6360 if (!MovDelay[x][y]) /* start new shrinking cycle */
6364 if (DelayReached(&sound_delay, sound_delay_value))
6365 sound_delay_value = 30;
6368 if (MovDelay[x][y]) /* wait some time before shrinking */
6371 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6373 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6374 6 - MovDelay[x][y]);
6376 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6379 if (!MovDelay[x][y])
6381 Feld[x][y] = EL_EMPTY;
6382 DrawLevelField(x, y);
6384 /* don't let mole enter this field in this cycle;
6385 (give priority to objects falling to this field from above) */
6391 void AmoebeAbleger(int ax, int ay)
6394 int element = Feld[ax][ay];
6395 int graphic = el2img(element);
6396 int newax = ax, neway = ay;
6397 static int xy[4][2] =
6405 if (!level.amoeba_speed)
6407 Feld[ax][ay] = EL_AMOEBA_DEAD;
6408 DrawLevelField(ax, ay);
6412 if (IS_ANIMATED(graphic))
6413 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6415 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6416 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6418 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6421 if (MovDelay[ax][ay])
6425 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6428 int x = ax + xy[start][0];
6429 int y = ay + xy[start][1];
6431 if (!IN_LEV_FIELD(x, y))
6434 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6435 if (IS_FREE(x, y) ||
6436 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6442 if (newax == ax && neway == ay)
6445 else /* normal or "filled" (BD style) amoeba */
6448 boolean waiting_for_player = FALSE;
6450 for (i = 0; i < NUM_DIRECTIONS; i++)
6452 int j = (start + i) % 4;
6453 int x = ax + xy[j][0];
6454 int y = ay + xy[j][1];
6456 if (!IN_LEV_FIELD(x, y))
6459 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6460 if (IS_FREE(x, y) ||
6461 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6467 else if (IS_PLAYER(x, y))
6468 waiting_for_player = TRUE;
6471 if (newax == ax && neway == ay) /* amoeba cannot grow */
6473 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6475 Feld[ax][ay] = EL_AMOEBA_DEAD;
6476 DrawLevelField(ax, ay);
6477 AmoebaCnt[AmoebaNr[ax][ay]]--;
6479 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6481 if (element == EL_AMOEBA_FULL)
6482 AmoebeUmwandeln(ax, ay);
6483 else if (element == EL_BD_AMOEBA)
6484 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6489 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6491 /* amoeba gets larger by growing in some direction */
6493 int new_group_nr = AmoebaNr[ax][ay];
6496 if (new_group_nr == 0)
6498 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6499 printf("AmoebeAbleger(): This should never happen!\n");
6504 AmoebaNr[newax][neway] = new_group_nr;
6505 AmoebaCnt[new_group_nr]++;
6506 AmoebaCnt2[new_group_nr]++;
6508 /* if amoeba touches other amoeba(s) after growing, unify them */
6509 AmoebenVereinigen(newax, neway);
6511 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6513 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6519 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6520 (neway == lev_fieldy - 1 && newax != ax))
6522 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6523 Store[newax][neway] = element;
6525 else if (neway == ay)
6527 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6529 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6531 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6536 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6537 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6538 Store[ax][ay] = EL_AMOEBA_DROP;
6539 ContinueMoving(ax, ay);
6543 DrawLevelField(newax, neway);
6546 void Life(int ax, int ay)
6549 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6551 int element = Feld[ax][ay];
6552 int graphic = el2img(element);
6553 boolean changed = FALSE;
6555 if (IS_ANIMATED(graphic))
6556 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6561 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6562 MovDelay[ax][ay] = life_time;
6564 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6567 if (MovDelay[ax][ay])
6571 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6573 int xx = ax+x1, yy = ay+y1;
6576 if (!IN_LEV_FIELD(xx, yy))
6579 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6581 int x = xx+x2, y = yy+y2;
6583 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6586 if (((Feld[x][y] == element ||
6587 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6589 (IS_FREE(x, y) && Stop[x][y]))
6593 if (xx == ax && yy == ay) /* field in the middle */
6595 if (nachbarn < life[0] || nachbarn > life[1])
6597 Feld[xx][yy] = EL_EMPTY;
6599 DrawLevelField(xx, yy);
6600 Stop[xx][yy] = TRUE;
6604 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6605 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6606 { /* free border field */
6607 if (nachbarn >= life[2] && nachbarn <= life[3])
6609 Feld[xx][yy] = element;
6610 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6612 DrawLevelField(xx, yy);
6613 Stop[xx][yy] = TRUE;
6620 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6621 SND_GAME_OF_LIFE_GROWING);
6624 static void InitRobotWheel(int x, int y)
6626 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6629 static void RunRobotWheel(int x, int y)
6631 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6634 static void StopRobotWheel(int x, int y)
6636 if (ZX == x && ZY == y)
6640 static void InitTimegateWheel(int x, int y)
6642 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6645 static void RunTimegateWheel(int x, int y)
6647 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6650 void CheckExit(int x, int y)
6652 if (local_player->gems_still_needed > 0 ||
6653 local_player->sokobanfields_still_needed > 0 ||
6654 local_player->lights_still_needed > 0)
6656 int element = Feld[x][y];
6657 int graphic = el2img(element);
6659 if (IS_ANIMATED(graphic))
6660 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6665 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6668 Feld[x][y] = EL_EXIT_OPENING;
6670 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6673 void CheckExitSP(int x, int y)
6675 if (local_player->gems_still_needed > 0)
6677 int element = Feld[x][y];
6678 int graphic = el2img(element);
6680 if (IS_ANIMATED(graphic))
6681 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6686 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6689 Feld[x][y] = EL_SP_EXIT_OPENING;
6691 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6694 static void CloseAllOpenTimegates()
6698 for (y = 0; y < lev_fieldy; y++)
6700 for (x = 0; x < lev_fieldx; x++)
6702 int element = Feld[x][y];
6704 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6706 Feld[x][y] = EL_TIMEGATE_CLOSING;
6708 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6710 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6717 void EdelsteinFunkeln(int x, int y)
6719 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6722 if (Feld[x][y] == EL_BD_DIAMOND)
6725 if (MovDelay[x][y] == 0) /* next animation frame */
6726 MovDelay[x][y] = 11 * !SimpleRND(500);
6728 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6732 if (setup.direct_draw && MovDelay[x][y])
6733 SetDrawtoField(DRAW_BUFFERED);
6735 DrawLevelElementAnimation(x, y, Feld[x][y]);
6737 if (MovDelay[x][y] != 0)
6739 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6740 10 - MovDelay[x][y]);
6742 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6744 if (setup.direct_draw)
6748 dest_x = FX + SCREENX(x) * TILEX;
6749 dest_y = FY + SCREENY(y) * TILEY;
6751 BlitBitmap(drawto_field, window,
6752 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6753 SetDrawtoField(DRAW_DIRECT);
6759 void MauerWaechst(int x, int y)
6763 if (!MovDelay[x][y]) /* next animation frame */
6764 MovDelay[x][y] = 3 * delay;
6766 if (MovDelay[x][y]) /* wait some time before next frame */
6770 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6772 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6773 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6775 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6778 if (!MovDelay[x][y])
6780 if (MovDir[x][y] == MV_LEFT)
6782 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6783 DrawLevelField(x - 1, y);
6785 else if (MovDir[x][y] == MV_RIGHT)
6787 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6788 DrawLevelField(x + 1, y);
6790 else if (MovDir[x][y] == MV_UP)
6792 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6793 DrawLevelField(x, y - 1);
6797 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6798 DrawLevelField(x, y + 1);
6801 Feld[x][y] = Store[x][y];
6803 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6804 DrawLevelField(x, y);
6809 void MauerAbleger(int ax, int ay)
6811 int element = Feld[ax][ay];
6812 int graphic = el2img(element);
6813 boolean oben_frei = FALSE, unten_frei = FALSE;
6814 boolean links_frei = FALSE, rechts_frei = FALSE;
6815 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6816 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6817 boolean new_wall = FALSE;
6819 if (IS_ANIMATED(graphic))
6820 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6822 if (!MovDelay[ax][ay]) /* start building new wall */
6823 MovDelay[ax][ay] = 6;
6825 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6828 if (MovDelay[ax][ay])
6832 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6834 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6836 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6838 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6841 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6842 element == EL_EXPANDABLE_WALL_ANY)
6846 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6847 Store[ax][ay-1] = element;
6848 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6849 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6850 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6851 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6856 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6857 Store[ax][ay+1] = element;
6858 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6859 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6860 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6861 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6866 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6867 element == EL_EXPANDABLE_WALL_ANY ||
6868 element == EL_EXPANDABLE_WALL)
6872 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6873 Store[ax-1][ay] = element;
6874 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6875 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6876 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6877 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6883 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6884 Store[ax+1][ay] = element;
6885 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6886 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6887 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6888 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6893 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6894 DrawLevelField(ax, ay);
6896 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6898 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6899 unten_massiv = TRUE;
6900 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6901 links_massiv = TRUE;
6902 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6903 rechts_massiv = TRUE;
6905 if (((oben_massiv && unten_massiv) ||
6906 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6907 element == EL_EXPANDABLE_WALL) &&
6908 ((links_massiv && rechts_massiv) ||
6909 element == EL_EXPANDABLE_WALL_VERTICAL))
6910 Feld[ax][ay] = EL_WALL;
6914 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6916 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6920 void CheckForDragon(int x, int y)
6923 boolean dragon_found = FALSE;
6924 static int xy[4][2] =
6932 for (i = 0; i < NUM_DIRECTIONS; i++)
6934 for (j = 0; j < 4; j++)
6936 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6938 if (IN_LEV_FIELD(xx, yy) &&
6939 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6941 if (Feld[xx][yy] == EL_DRAGON)
6942 dragon_found = TRUE;
6951 for (i = 0; i < NUM_DIRECTIONS; i++)
6953 for (j = 0; j < 3; j++)
6955 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6957 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6959 Feld[xx][yy] = EL_EMPTY;
6960 DrawLevelField(xx, yy);
6969 static void InitBuggyBase(int x, int y)
6971 int element = Feld[x][y];
6972 int activating_delay = FRAMES_PER_SECOND / 4;
6975 (element == EL_SP_BUGGY_BASE ?
6976 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6977 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6979 element == EL_SP_BUGGY_BASE_ACTIVE ?
6980 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6983 static void WarnBuggyBase(int x, int y)
6986 static int xy[4][2] =
6994 for (i = 0; i < NUM_DIRECTIONS; i++)
6996 int xx = x + xy[i][0], yy = y + xy[i][1];
6998 if (IS_PLAYER(xx, yy))
7000 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7007 static void InitTrap(int x, int y)
7009 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7012 static void ActivateTrap(int x, int y)
7014 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7017 static void ChangeActiveTrap(int x, int y)
7019 int graphic = IMG_TRAP_ACTIVE;
7021 /* if new animation frame was drawn, correct crumbled sand border */
7022 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7023 DrawLevelFieldCrumbledSand(x, y);
7026 static void ChangeElementNowExt(int x, int y, int target_element)
7028 int previous_move_direction = MovDir[x][y];
7030 /* check if element under player changes from accessible to unaccessible
7031 (needed for special case of dropping element which then changes) */
7032 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7033 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7040 Feld[x][y] = target_element;
7042 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7044 ResetGfxAnimation(x, y);
7045 ResetRandomAnimationValue(x, y);
7047 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7048 MovDir[x][y] = previous_move_direction;
7051 InitField_WithBug1(x, y, FALSE);
7053 InitField(x, y, FALSE);
7054 if (CAN_MOVE(Feld[x][y]))
7058 DrawLevelField(x, y);
7060 if (GFX_CRUMBLED(Feld[x][y]))
7061 DrawLevelFieldCrumbledSandNeighbours(x, y);
7063 TestIfBadThingTouchesHero(x, y);
7064 TestIfPlayerTouchesCustomElement(x, y);
7065 TestIfElementTouchesCustomElement(x, y);
7067 if (ELEM_IS_PLAYER(target_element))
7068 RelocatePlayer(x, y, target_element);
7071 static boolean ChangeElementNow(int x, int y, int element, int page)
7073 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7076 /* always use default change event to prevent running into a loop */
7077 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7078 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7080 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7082 /* reset actual trigger element and player */
7083 change->actual_trigger_element = EL_EMPTY;
7084 change->actual_trigger_player = EL_PLAYER_1;
7087 /* do not change already changed elements with same change event */
7089 if (Changed[x][y] & ChangeEvent[x][y])
7096 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7098 CheckTriggeredElementChangePage(x,y, Feld[x][y], CE_OTHER_IS_CHANGING, page);
7100 if (change->explode)
7107 if (change->use_target_content)
7109 boolean complete_replace = TRUE;
7110 boolean can_replace[3][3];
7113 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7116 boolean is_diggable;
7117 boolean is_destructible;
7118 int ex = x + xx - 1;
7119 int ey = y + yy - 1;
7120 int content_element = change->target_content[xx][yy];
7123 can_replace[xx][yy] = TRUE;
7125 if (ex == x && ey == y) /* do not check changing element itself */
7128 if (content_element == EL_EMPTY_SPACE)
7130 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7135 if (!IN_LEV_FIELD(ex, ey))
7137 can_replace[xx][yy] = FALSE;
7138 complete_replace = FALSE;
7145 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7146 e = MovingOrBlocked2Element(ex, ey);
7149 is_empty = (IS_FREE(ex, ey) || (IS_PLAYER(ex, ey) &&
7150 IS_WALKABLE(content_element)));
7151 is_diggable = (is_empty || IS_DIGGABLE(e));
7152 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7154 can_replace[xx][yy] =
7155 ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7156 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7157 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7159 if (!can_replace[xx][yy])
7160 complete_replace = FALSE;
7162 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7163 IS_WALKABLE(content_element)));
7165 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7167 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7170 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7171 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7172 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7174 can_replace[xx][yy] = FALSE;
7175 complete_replace = FALSE;
7180 if (!change->only_if_complete || complete_replace)
7182 boolean something_has_changed = FALSE;
7184 if (change->only_if_complete && change->use_random_replace &&
7185 RND(100) < change->random_percentage)
7188 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7190 int ex = x + xx - 1;
7191 int ey = y + yy - 1;
7192 int content_element;
7194 if (can_replace[xx][yy] && (!change->use_random_replace ||
7195 RND(100) < change->random_percentage))
7197 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7198 RemoveMovingField(ex, ey);
7200 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7202 content_element = change->target_content[xx][yy];
7203 target_element = GET_TARGET_ELEMENT(content_element, change);
7205 ChangeElementNowExt(ex, ey, target_element);
7207 something_has_changed = TRUE;
7209 /* for symmetry reasons, freeze newly created border elements */
7210 if (ex != x || ey != y)
7211 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7215 if (something_has_changed)
7216 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7221 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7223 ChangeElementNowExt(x, y, target_element);
7225 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7231 static void ChangeElement(int x, int y, int page)
7233 int element = MovingOrBlocked2Element(x, y);
7234 struct ElementInfo *ei = &element_info[element];
7235 struct ElementChangeInfo *change = &ei->change_page[page];
7238 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7241 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7242 x, y, element, element_info[element].token_name);
7243 printf("ChangeElement(): This should never happen!\n");
7248 /* this can happen with classic bombs on walkable, changing elements */
7249 if (!CAN_CHANGE(element))
7252 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7253 ChangeDelay[x][y] = 0;
7259 if (ChangeDelay[x][y] == 0) /* initialize element change */
7261 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7262 RND(change->delay_random * change->delay_frames)) + 1;
7264 ResetGfxAnimation(x, y);
7265 ResetRandomAnimationValue(x, y);
7267 if (change->pre_change_function)
7268 change->pre_change_function(x, y);
7271 ChangeDelay[x][y]--;
7273 if (ChangeDelay[x][y] != 0) /* continue element change */
7275 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7277 if (IS_ANIMATED(graphic))
7278 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7280 if (change->change_function)
7281 change->change_function(x, y);
7283 else /* finish element change */
7285 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7287 page = ChangePage[x][y];
7288 ChangePage[x][y] = -1;
7290 change = &ei->change_page[page];
7294 if (IS_MOVING(x, y) && !change->explode)
7296 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7299 ChangeDelay[x][y] = 1; /* try change after next move step */
7300 ChangePage[x][y] = page; /* remember page to use for change */
7305 if (ChangeElementNow(x, y, element, page))
7307 if (change->post_change_function)
7308 change->post_change_function(x, y);
7313 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7314 int trigger_element,
7321 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7323 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7326 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7328 int element = EL_CUSTOM_START + i;
7330 boolean change_element = FALSE;
7333 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7336 for (j = 0; j < element_info[element].num_change_pages; j++)
7338 struct ElementChangeInfo *change = &element_info[element].change_page[j];
7340 if (change->can_change &&
7341 change->events & CH_EVENT_BIT(trigger_event) &&
7342 change->trigger_side & trigger_side &&
7343 change->trigger_player & trigger_player &&
7344 change->trigger_page & trigger_page_bits &&
7345 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7348 if (!(change->events & CH_EVENT_BIT(trigger_event)))
7349 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7350 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7353 change_element = TRUE;
7356 change->actual_trigger_element = trigger_element;
7357 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7363 if (!change_element)
7366 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7369 if (x == lx && y == ly) /* do not change trigger element itself */
7373 if (Feld[x][y] == element)
7375 ChangeDelay[x][y] = 1;
7376 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7377 ChangeElement(x, y, page);
7385 static boolean CheckElementChangeExt(int x, int y,
7387 int trigger_element,
7393 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7396 if (Feld[x][y] == EL_BLOCKED)
7398 Blocked2Moving(x, y, &x, &y);
7399 element = Feld[x][y];
7403 if (Feld[x][y] != element) /* check if element has already changed */
7406 printf("::: %d ('%s') != %d ('%s') [%d]\n",
7407 Feld[x][y], element_info[Feld[x][y]].token_name,
7408 element, element_info[element].token_name,
7417 if (trigger_page < 0)
7419 boolean change_element = FALSE;
7422 for (i = 0; i < element_info[element].num_change_pages; i++)
7424 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7426 if (change->can_change &&
7427 change->events & CH_EVENT_BIT(trigger_event) &&
7428 change->trigger_side & trigger_side &&
7429 change->trigger_player & trigger_player)
7431 change_element = TRUE;
7434 change->actual_trigger_element = trigger_element;
7435 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7441 if (!change_element)
7446 struct ElementInfo *ei = &element_info[element];
7447 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7449 change->actual_trigger_element = trigger_element;
7450 change->actual_trigger_player = EL_PLAYER_1; /* unused */
7455 /* !!! this check misses pages with same event, but different side !!! */
7457 if (trigger_page < 0)
7458 trigger_page = element_info[element].event_page_nr[trigger_event];
7460 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7464 ChangeDelay[x][y] = 1;
7465 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7466 ChangeElement(x, y, trigger_page);
7471 static void PlayPlayerSound(struct PlayerInfo *player)
7473 int jx = player->jx, jy = player->jy;
7474 int element = player->element_nr;
7475 int last_action = player->last_action_waiting;
7476 int action = player->action_waiting;
7478 if (player->is_waiting)
7480 if (action != last_action)
7481 PlayLevelSoundElementAction(jx, jy, element, action);
7483 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7487 if (action != last_action)
7488 StopSound(element_info[element].sound[last_action]);
7490 if (last_action == ACTION_SLEEPING)
7491 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7495 static void PlayAllPlayersSound()
7499 for (i = 0; i < MAX_PLAYERS; i++)
7500 if (stored_player[i].active)
7501 PlayPlayerSound(&stored_player[i]);
7504 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7506 boolean last_waiting = player->is_waiting;
7507 int move_dir = player->MovDir;
7509 player->last_action_waiting = player->action_waiting;
7513 if (!last_waiting) /* not waiting -> waiting */
7515 player->is_waiting = TRUE;
7517 player->frame_counter_bored =
7519 game.player_boring_delay_fixed +
7520 SimpleRND(game.player_boring_delay_random);
7521 player->frame_counter_sleeping =
7523 game.player_sleeping_delay_fixed +
7524 SimpleRND(game.player_sleeping_delay_random);
7526 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7529 if (game.player_sleeping_delay_fixed +
7530 game.player_sleeping_delay_random > 0 &&
7531 player->anim_delay_counter == 0 &&
7532 player->post_delay_counter == 0 &&
7533 FrameCounter >= player->frame_counter_sleeping)
7534 player->is_sleeping = TRUE;
7535 else if (game.player_boring_delay_fixed +
7536 game.player_boring_delay_random > 0 &&
7537 FrameCounter >= player->frame_counter_bored)
7538 player->is_bored = TRUE;
7540 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7541 player->is_bored ? ACTION_BORING :
7544 if (player->is_sleeping)
7546 if (player->num_special_action_sleeping > 0)
7548 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7550 int last_special_action = player->special_action_sleeping;
7551 int num_special_action = player->num_special_action_sleeping;
7552 int special_action =
7553 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7554 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7555 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7556 last_special_action + 1 : ACTION_SLEEPING);
7557 int special_graphic =
7558 el_act_dir2img(player->element_nr, special_action, move_dir);
7560 player->anim_delay_counter =
7561 graphic_info[special_graphic].anim_delay_fixed +
7562 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7563 player->post_delay_counter =
7564 graphic_info[special_graphic].post_delay_fixed +
7565 SimpleRND(graphic_info[special_graphic].post_delay_random);
7567 player->special_action_sleeping = special_action;
7570 if (player->anim_delay_counter > 0)
7572 player->action_waiting = player->special_action_sleeping;
7573 player->anim_delay_counter--;
7575 else if (player->post_delay_counter > 0)
7577 player->post_delay_counter--;
7581 else if (player->is_bored)
7583 if (player->num_special_action_bored > 0)
7585 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7587 int special_action =
7588 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7589 int special_graphic =
7590 el_act_dir2img(player->element_nr, special_action, move_dir);
7592 player->anim_delay_counter =
7593 graphic_info[special_graphic].anim_delay_fixed +
7594 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7595 player->post_delay_counter =
7596 graphic_info[special_graphic].post_delay_fixed +
7597 SimpleRND(graphic_info[special_graphic].post_delay_random);
7599 player->special_action_bored = special_action;
7602 if (player->anim_delay_counter > 0)
7604 player->action_waiting = player->special_action_bored;
7605 player->anim_delay_counter--;
7607 else if (player->post_delay_counter > 0)
7609 player->post_delay_counter--;
7614 else if (last_waiting) /* waiting -> not waiting */
7616 player->is_waiting = FALSE;
7617 player->is_bored = FALSE;
7618 player->is_sleeping = FALSE;
7620 player->frame_counter_bored = -1;
7621 player->frame_counter_sleeping = -1;
7623 player->anim_delay_counter = 0;
7624 player->post_delay_counter = 0;
7626 player->action_waiting = ACTION_DEFAULT;
7628 player->special_action_bored = ACTION_DEFAULT;
7629 player->special_action_sleeping = ACTION_DEFAULT;
7634 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7637 static byte stored_player_action[MAX_PLAYERS];
7638 static int num_stored_actions = 0;
7640 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7641 int left = player_action & JOY_LEFT;
7642 int right = player_action & JOY_RIGHT;
7643 int up = player_action & JOY_UP;
7644 int down = player_action & JOY_DOWN;
7645 int button1 = player_action & JOY_BUTTON_1;
7646 int button2 = player_action & JOY_BUTTON_2;
7647 int dx = (left ? -1 : right ? 1 : 0);
7648 int dy = (up ? -1 : down ? 1 : 0);
7651 stored_player_action[player->index_nr] = 0;
7652 num_stored_actions++;
7656 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7659 if (!player->active || tape.pausing)
7663 printf("::: [%d %d %d %d] [%d %d]\n",
7664 left, right, up, down, button1, button2);
7670 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7675 if (player->MovPos == 0)
7676 CheckGravityMovement(player);
7679 snapped = SnapField(player, dx, dy);
7683 dropped = DropElement(player);
7685 moved = MovePlayer(player, dx, dy);
7688 if (tape.single_step && tape.recording && !tape.pausing)
7690 if (button1 || (dropped && !moved))
7692 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7693 SnapField(player, 0, 0); /* stop snapping */
7697 SetPlayerWaiting(player, FALSE);
7700 return player_action;
7702 stored_player_action[player->index_nr] = player_action;
7708 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7711 /* no actions for this player (no input at player's configured device) */
7713 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7714 SnapField(player, 0, 0);
7715 CheckGravityMovementWhenNotMoving(player);
7717 if (player->MovPos == 0)
7718 SetPlayerWaiting(player, TRUE);
7720 if (player->MovPos == 0) /* needed for tape.playing */
7721 player->is_moving = FALSE;
7723 player->is_dropping = FALSE;
7729 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7731 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7733 TapeRecordAction(stored_player_action);
7734 num_stored_actions = 0;
7741 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7743 static byte stored_player_action[MAX_PLAYERS];
7744 static int num_stored_actions = 0;
7745 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7746 int left = player_action & JOY_LEFT;
7747 int right = player_action & JOY_RIGHT;
7748 int up = player_action & JOY_UP;
7749 int down = player_action & JOY_DOWN;
7750 int button1 = player_action & JOY_BUTTON_1;
7751 int button2 = player_action & JOY_BUTTON_2;
7752 int dx = (left ? -1 : right ? 1 : 0);
7753 int dy = (up ? -1 : down ? 1 : 0);
7755 stored_player_action[player->index_nr] = 0;
7756 num_stored_actions++;
7758 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7760 if (!player->active || tape.pausing)
7765 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7768 snapped = SnapField(player, dx, dy);
7772 dropped = DropElement(player);
7774 moved = MovePlayer(player, dx, dy);
7777 if (tape.single_step && tape.recording && !tape.pausing)
7779 if (button1 || (dropped && !moved))
7781 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7782 SnapField(player, 0, 0); /* stop snapping */
7786 stored_player_action[player->index_nr] = player_action;
7790 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7792 /* no actions for this player (no input at player's configured device) */
7794 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7795 SnapField(player, 0, 0);
7796 CheckGravityMovementWhenNotMoving(player);
7798 if (player->MovPos == 0)
7799 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7801 if (player->MovPos == 0) /* needed for tape.playing */
7802 player->is_moving = FALSE;
7805 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7807 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7809 TapeRecordAction(stored_player_action);
7810 num_stored_actions = 0;
7817 static unsigned long action_delay = 0;
7818 unsigned long action_delay_value;
7819 int magic_wall_x = 0, magic_wall_y = 0;
7820 int i, x, y, element, graphic;
7821 byte *recorded_player_action;
7822 byte summarized_player_action = 0;
7824 byte tape_action[MAX_PLAYERS];
7827 if (game_status != GAME_MODE_PLAYING)
7830 action_delay_value =
7831 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7833 if (tape.playing && tape.warp_forward && !tape.pausing)
7834 action_delay_value = 0;
7836 /* ---------- main game synchronization point ---------- */
7838 WaitUntilDelayReached(&action_delay, action_delay_value);
7840 if (network_playing && !network_player_action_received)
7844 printf("DEBUG: try to get network player actions in time\n");
7848 #if defined(PLATFORM_UNIX)
7849 /* last chance to get network player actions without main loop delay */
7853 if (game_status != GAME_MODE_PLAYING)
7856 if (!network_player_action_received)
7860 printf("DEBUG: failed to get network player actions in time\n");
7871 printf("::: getting new tape action [%d]\n", FrameCounter);
7874 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7877 if (recorded_player_action == NULL && tape.pausing)
7882 printf("::: %d\n", stored_player[0].action);
7886 if (recorded_player_action != NULL)
7887 for (i = 0; i < MAX_PLAYERS; i++)
7888 stored_player[i].action = recorded_player_action[i];
7891 for (i = 0; i < MAX_PLAYERS; i++)
7893 summarized_player_action |= stored_player[i].action;
7895 if (!network_playing)
7896 stored_player[i].effective_action = stored_player[i].action;
7899 #if defined(PLATFORM_UNIX)
7900 if (network_playing)
7901 SendToServer_MovePlayer(summarized_player_action);
7904 if (!options.network && !setup.team_mode)
7905 local_player->effective_action = summarized_player_action;
7908 if (recorded_player_action != NULL)
7909 for (i = 0; i < MAX_PLAYERS; i++)
7910 stored_player[i].effective_action = recorded_player_action[i];
7914 for (i = 0; i < MAX_PLAYERS; i++)
7916 tape_action[i] = stored_player[i].effective_action;
7918 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7919 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7922 /* only save actions from input devices, but not programmed actions */
7924 TapeRecordAction(tape_action);
7927 for (i = 0; i < MAX_PLAYERS; i++)
7929 int actual_player_action = stored_player[i].effective_action;
7932 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
7933 - rnd_equinox_tetrachloride 048
7934 - rnd_equinox_tetrachloride_ii 096
7935 - rnd_emanuel_schmieg 002
7936 - doctor_sloan_ww 001, 020
7938 if (stored_player[i].MovPos == 0)
7939 CheckGravityMovement(&stored_player[i]);
7943 /* overwrite programmed action with tape action */
7944 if (stored_player[i].programmed_action)
7945 actual_player_action = stored_player[i].programmed_action;
7949 if (stored_player[i].programmed_action)
7950 printf("::: %d\n", stored_player[i].programmed_action);
7953 if (recorded_player_action)
7956 if (stored_player[i].programmed_action &&
7957 stored_player[i].programmed_action != recorded_player_action[i])
7958 printf("::: %d: %d <-> %d\n", i,
7959 stored_player[i].programmed_action, recorded_player_action[i]);
7963 actual_player_action = recorded_player_action[i];
7968 /* overwrite tape action with programmed action */
7969 if (stored_player[i].programmed_action)
7970 actual_player_action = stored_player[i].programmed_action;
7975 printf("::: action: %d: %x [%d]\n",
7976 stored_player[i].MovPos, actual_player_action, FrameCounter);
7980 PlayerActions(&stored_player[i], actual_player_action);
7982 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7984 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7985 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7988 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7993 TapeRecordAction(tape_action);
7996 network_player_action_received = FALSE;
7998 ScrollScreen(NULL, SCROLL_GO_ON);
8004 for (i = 0; i < MAX_PLAYERS; i++)
8005 stored_player[i].Frame++;
8009 /* for downwards compatibility, the following code emulates a fixed bug that
8010 occured when pushing elements (causing elements that just made their last
8011 pushing step to already (if possible) make their first falling step in the
8012 same game frame, which is bad); this code is also needed to use the famous
8013 "spring push bug" which is used in older levels and might be wanted to be
8014 used also in newer levels, but in this case the buggy pushing code is only
8015 affecting the "spring" element and no other elements */
8018 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8020 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8023 for (i = 0; i < MAX_PLAYERS; i++)
8025 struct PlayerInfo *player = &stored_player[i];
8030 if (player->active && player->is_pushing && player->is_moving &&
8032 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8033 Feld[x][y] == EL_SPRING))
8035 if (player->active && player->is_pushing && player->is_moving &&
8039 ContinueMoving(x, y);
8041 /* continue moving after pushing (this is actually a bug) */
8042 if (!IS_MOVING(x, y))
8051 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8053 Changed[x][y] = CE_BITMASK_DEFAULT;
8054 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8057 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8059 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8060 printf("GameActions(): This should never happen!\n");
8062 ChangePage[x][y] = -1;
8067 if (WasJustMoving[x][y] > 0)
8068 WasJustMoving[x][y]--;
8069 if (WasJustFalling[x][y] > 0)
8070 WasJustFalling[x][y]--;
8075 /* reset finished pushing action (not done in ContinueMoving() to allow
8076 continous pushing animation for elements with zero push delay) */
8077 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8079 ResetGfxAnimation(x, y);
8080 DrawLevelField(x, y);
8085 if (IS_BLOCKED(x, y))
8089 Blocked2Moving(x, y, &oldx, &oldy);
8090 if (!IS_MOVING(oldx, oldy))
8092 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8093 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8094 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8095 printf("GameActions(): This should never happen!\n");
8101 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8103 element = Feld[x][y];
8105 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8107 graphic = el2img(element);
8113 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8115 element = graphic = 0;
8119 if (graphic_info[graphic].anim_global_sync)
8120 GfxFrame[x][y] = FrameCounter;
8122 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8123 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8124 ResetRandomAnimationValue(x, y);
8126 SetRandomAnimationValue(x, y);
8129 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8132 if (IS_INACTIVE(element))
8134 if (IS_ANIMATED(graphic))
8135 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8141 /* this may take place after moving, so 'element' may have changed */
8143 if (IS_CHANGING(x, y))
8145 if (IS_CHANGING(x, y) &&
8146 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8150 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8151 element_info[element].event_page_nr[CE_DELAY]);
8153 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8156 element = Feld[x][y];
8157 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8161 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8166 element = Feld[x][y];
8167 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8169 if (element == EL_MOLE)
8170 printf("::: %d, %d, %d [%d]\n",
8171 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8175 if (element == EL_YAMYAM)
8176 printf("::: %d, %d, %d\n",
8177 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8181 if (IS_ANIMATED(graphic) &&
8185 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8188 if (element == EL_BUG)
8189 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8193 if (element == EL_MOLE)
8194 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8198 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8199 EdelsteinFunkeln(x, y);
8201 else if ((element == EL_ACID ||
8202 element == EL_EXIT_OPEN ||
8203 element == EL_SP_EXIT_OPEN ||
8204 element == EL_SP_TERMINAL ||
8205 element == EL_SP_TERMINAL_ACTIVE ||
8206 element == EL_EXTRA_TIME ||
8207 element == EL_SHIELD_NORMAL ||
8208 element == EL_SHIELD_DEADLY) &&
8209 IS_ANIMATED(graphic))
8210 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8211 else if (IS_MOVING(x, y))
8212 ContinueMoving(x, y);
8213 else if (IS_ACTIVE_BOMB(element))
8214 CheckDynamite(x, y);
8216 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8217 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8219 else if (element == EL_AMOEBA_GROWING)
8220 AmoebeWaechst(x, y);
8221 else if (element == EL_AMOEBA_SHRINKING)
8222 AmoebaDisappearing(x, y);
8224 #if !USE_NEW_AMOEBA_CODE
8225 else if (IS_AMOEBALIVE(element))
8226 AmoebeAbleger(x, y);
8229 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8231 else if (element == EL_EXIT_CLOSED)
8233 else if (element == EL_SP_EXIT_CLOSED)
8235 else if (element == EL_EXPANDABLE_WALL_GROWING)
8237 else if (element == EL_EXPANDABLE_WALL ||
8238 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8239 element == EL_EXPANDABLE_WALL_VERTICAL ||
8240 element == EL_EXPANDABLE_WALL_ANY)
8242 else if (element == EL_FLAMES)
8243 CheckForDragon(x, y);
8245 else if (IS_AUTO_CHANGING(element))
8246 ChangeElement(x, y);
8248 else if (element == EL_EXPLOSION)
8249 ; /* drawing of correct explosion animation is handled separately */
8250 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8251 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8254 /* this may take place after moving, so 'element' may have changed */
8255 if (IS_AUTO_CHANGING(Feld[x][y]))
8256 ChangeElement(x, y);
8259 if (IS_BELT_ACTIVE(element))
8260 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8262 if (game.magic_wall_active)
8264 int jx = local_player->jx, jy = local_player->jy;
8266 /* play the element sound at the position nearest to the player */
8267 if ((element == EL_MAGIC_WALL_FULL ||
8268 element == EL_MAGIC_WALL_ACTIVE ||
8269 element == EL_MAGIC_WALL_EMPTYING ||
8270 element == EL_BD_MAGIC_WALL_FULL ||
8271 element == EL_BD_MAGIC_WALL_ACTIVE ||
8272 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8273 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8281 #if USE_NEW_AMOEBA_CODE
8282 /* new experimental amoeba growth stuff */
8284 if (!(FrameCounter % 8))
8287 static unsigned long random = 1684108901;
8289 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8292 x = (random >> 10) % lev_fieldx;
8293 y = (random >> 20) % lev_fieldy;
8295 x = RND(lev_fieldx);
8296 y = RND(lev_fieldy);
8298 element = Feld[x][y];
8300 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8301 if (!IS_PLAYER(x,y) &&
8302 (element == EL_EMPTY ||
8303 element == EL_SAND ||
8304 element == EL_QUICKSAND_EMPTY ||
8305 element == EL_ACID_SPLASH_LEFT ||
8306 element == EL_ACID_SPLASH_RIGHT))
8308 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8309 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8310 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8311 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8312 Feld[x][y] = EL_AMOEBA_DROP;
8315 random = random * 129 + 1;
8321 if (game.explosions_delayed)
8324 game.explosions_delayed = FALSE;
8326 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8328 element = Feld[x][y];
8330 if (ExplodeField[x][y])
8331 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8332 else if (element == EL_EXPLOSION)
8333 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8335 ExplodeField[x][y] = EX_TYPE_NONE;
8338 game.explosions_delayed = TRUE;
8341 if (game.magic_wall_active)
8343 if (!(game.magic_wall_time_left % 4))
8345 int element = Feld[magic_wall_x][magic_wall_y];
8347 if (element == EL_BD_MAGIC_WALL_FULL ||
8348 element == EL_BD_MAGIC_WALL_ACTIVE ||
8349 element == EL_BD_MAGIC_WALL_EMPTYING)
8350 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8352 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8355 if (game.magic_wall_time_left > 0)
8357 game.magic_wall_time_left--;
8358 if (!game.magic_wall_time_left)
8360 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8362 element = Feld[x][y];
8364 if (element == EL_MAGIC_WALL_ACTIVE ||
8365 element == EL_MAGIC_WALL_FULL)
8367 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8368 DrawLevelField(x, y);
8370 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8371 element == EL_BD_MAGIC_WALL_FULL)
8373 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8374 DrawLevelField(x, y);
8378 game.magic_wall_active = FALSE;
8383 if (game.light_time_left > 0)
8385 game.light_time_left--;
8387 if (game.light_time_left == 0)
8388 RedrawAllLightSwitchesAndInvisibleElements();
8391 if (game.timegate_time_left > 0)
8393 game.timegate_time_left--;
8395 if (game.timegate_time_left == 0)
8396 CloseAllOpenTimegates();
8399 for (i = 0; i < MAX_PLAYERS; i++)
8401 struct PlayerInfo *player = &stored_player[i];
8403 if (SHIELD_ON(player))
8405 if (player->shield_deadly_time_left)
8406 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8407 else if (player->shield_normal_time_left)
8408 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8412 if (TimeFrames >= FRAMES_PER_SECOND)
8417 if (!level.use_step_counter)
8421 for (i = 0; i < MAX_PLAYERS; i++)
8423 struct PlayerInfo *player = &stored_player[i];
8425 if (SHIELD_ON(player))
8427 player->shield_normal_time_left--;
8429 if (player->shield_deadly_time_left > 0)
8430 player->shield_deadly_time_left--;
8438 if (TimeLeft <= 10 && setup.time_limit)
8439 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8441 DrawGameValue_Time(TimeLeft);
8443 if (!TimeLeft && setup.time_limit)
8444 for (i = 0; i < MAX_PLAYERS; i++)
8445 KillHero(&stored_player[i]);
8447 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8448 DrawGameValue_Time(TimePlayed);
8451 if (tape.recording || tape.playing)
8452 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8456 PlayAllPlayersSound();
8458 if (options.debug) /* calculate frames per second */
8460 static unsigned long fps_counter = 0;
8461 static int fps_frames = 0;
8462 unsigned long fps_delay_ms = Counter() - fps_counter;
8466 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8468 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8471 fps_counter = Counter();
8474 redraw_mask |= REDRAW_FPS;
8478 if (stored_player[0].jx != stored_player[0].last_jx ||
8479 stored_player[0].jy != stored_player[0].last_jy)
8480 printf("::: %d, %d, %d, %d, %d\n",
8481 stored_player[0].MovDir,
8482 stored_player[0].MovPos,
8483 stored_player[0].GfxPos,
8484 stored_player[0].Frame,
8485 stored_player[0].StepFrame);
8492 for (i = 0; i < MAX_PLAYERS; i++)
8495 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8497 stored_player[i].Frame += move_frames;
8499 if (stored_player[i].MovPos != 0)
8500 stored_player[i].StepFrame += move_frames;
8502 if (stored_player[i].drop_delay > 0)
8503 stored_player[i].drop_delay--;
8508 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8510 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8512 local_player->show_envelope = 0;
8517 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8519 int min_x = x, min_y = y, max_x = x, max_y = y;
8522 for (i = 0; i < MAX_PLAYERS; i++)
8524 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8526 if (!stored_player[i].active || &stored_player[i] == player)
8529 min_x = MIN(min_x, jx);
8530 min_y = MIN(min_y, jy);
8531 max_x = MAX(max_x, jx);
8532 max_y = MAX(max_y, jy);
8535 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8538 static boolean AllPlayersInVisibleScreen()
8542 for (i = 0; i < MAX_PLAYERS; i++)
8544 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8546 if (!stored_player[i].active)
8549 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8556 void ScrollLevel(int dx, int dy)
8558 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8561 BlitBitmap(drawto_field, drawto_field,
8562 FX + TILEX * (dx == -1) - softscroll_offset,
8563 FY + TILEY * (dy == -1) - softscroll_offset,
8564 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8565 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8566 FX + TILEX * (dx == 1) - softscroll_offset,
8567 FY + TILEY * (dy == 1) - softscroll_offset);
8571 x = (dx == 1 ? BX1 : BX2);
8572 for (y = BY1; y <= BY2; y++)
8573 DrawScreenField(x, y);
8578 y = (dy == 1 ? BY1 : BY2);
8579 for (x = BX1; x <= BX2; x++)
8580 DrawScreenField(x, y);
8583 redraw_mask |= REDRAW_FIELD;
8587 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8589 int nextx = x + dx, nexty = y + dy;
8590 int element = Feld[x][y];
8593 element != EL_SP_PORT_LEFT &&
8594 element != EL_SP_GRAVITY_PORT_LEFT &&
8595 element != EL_SP_PORT_HORIZONTAL &&
8596 element != EL_SP_PORT_ANY) ||
8598 element != EL_SP_PORT_RIGHT &&
8599 element != EL_SP_GRAVITY_PORT_RIGHT &&
8600 element != EL_SP_PORT_HORIZONTAL &&
8601 element != EL_SP_PORT_ANY) ||
8603 element != EL_SP_PORT_UP &&
8604 element != EL_SP_GRAVITY_PORT_UP &&
8605 element != EL_SP_PORT_VERTICAL &&
8606 element != EL_SP_PORT_ANY) ||
8608 element != EL_SP_PORT_DOWN &&
8609 element != EL_SP_GRAVITY_PORT_DOWN &&
8610 element != EL_SP_PORT_VERTICAL &&
8611 element != EL_SP_PORT_ANY) ||
8612 !IN_LEV_FIELD(nextx, nexty) ||
8613 !IS_FREE(nextx, nexty))
8620 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8622 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8623 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8624 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8627 int nextx = newx + dx;
8628 int nexty = newy + dy;
8629 boolean next_field_must_be_free = TRUE;
8631 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8632 (IS_DIGGABLE(Feld[newx][newy]) ||
8633 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8634 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
8635 !CAN_MOVE(Feld[newx][newy]) &&
8636 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8637 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8638 !(next_field_must_be_free && !IS_FREE(nextx, nexty)))));
8641 static void CheckGravityMovement(struct PlayerInfo *player)
8643 if (game.gravity && !player->programmed_action)
8646 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8647 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8649 int move_dir_horizontal = player->action & MV_HORIZONTAL;
8650 int move_dir_vertical = player->action & MV_VERTICAL;
8653 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8655 boolean player_is_snapping = player->action & JOY_BUTTON_1;
8658 int jx = player->jx, jy = player->jy;
8661 boolean player_is_moving_to_valid_field =
8662 (!player_is_snapping &&
8663 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8664 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8668 (player->last_move_dir & MV_HORIZONTAL ?
8669 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8670 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8674 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8675 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8676 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8677 int new_jx = jx + dx, new_jy = jy + dy;
8678 int nextx = new_jx + dx, nexty = new_jy + dy;
8682 boolean player_can_fall_down =
8683 (IN_LEV_FIELD(jx, jy + 1) &&
8684 (IS_FREE(jx, jy + 1) ||
8685 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
8687 boolean player_can_fall_down =
8688 (IN_LEV_FIELD(jx, jy + 1) &&
8689 (IS_FREE(jx, jy + 1)));
8692 boolean player_is_moving_to_valid_field =
8695 !player_is_snapping &&
8699 IN_LEV_FIELD(new_jx, new_jy) &&
8700 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
8701 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8702 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
8703 IN_LEV_FIELD(nextx, nexty) &&
8704 element_info[Feld[nextx][nexty]].access_direction & move_dir))
8706 IN_LEV_FIELD(new_jx, new_jy) &&
8707 (Feld[new_jx][new_jy] == EL_SP_BASE ||
8708 Feld[new_jx][new_jy] == EL_SAND ||
8709 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8710 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
8711 /* !!! extend EL_SAND to anything diggable !!! */
8716 boolean player_is_standing_on_valid_field =
8717 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8718 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
8721 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
8722 player_can_fall_down,
8723 player_is_standing_on_valid_field,
8724 player_is_moving_to_valid_field,
8725 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
8726 player->effective_action,
8727 player->can_fall_into_acid);
8730 if (player_can_fall_down &&
8731 !player_is_standing_on_valid_field &&
8732 !player_is_moving_to_valid_field)
8735 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
8736 jx, jy, FrameCounter);
8739 player->programmed_action = MV_DOWN;
8744 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8747 return CheckGravityMovement(player);
8750 if (game.gravity && !player->programmed_action)
8752 int jx = player->jx, jy = player->jy;
8753 boolean field_under_player_is_free =
8754 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8755 boolean player_is_standing_on_valid_field =
8756 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8757 (IS_WALKABLE(Feld[jx][jy]) &&
8758 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8760 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8761 player->programmed_action = MV_DOWN;
8767 -----------------------------------------------------------------------------
8768 dx, dy: direction (non-diagonal) to try to move the player to
8769 real_dx, real_dy: direction as read from input device (can be diagonal)
8772 boolean MovePlayerOneStep(struct PlayerInfo *player,
8773 int dx, int dy, int real_dx, int real_dy)
8776 static int trigger_sides[4][2] =
8778 /* enter side leave side */
8779 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8780 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8781 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8782 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8784 int move_direction = (dx == -1 ? MV_LEFT :
8785 dx == +1 ? MV_RIGHT :
8787 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8788 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8789 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8791 int jx = player->jx, jy = player->jy;
8792 int new_jx = jx + dx, new_jy = jy + dy;
8796 if (!player->active || (!dx && !dy))
8797 return MF_NO_ACTION;
8799 player->MovDir = (dx < 0 ? MV_LEFT :
8802 dy > 0 ? MV_DOWN : MV_NO_MOVING);
8804 if (!IN_LEV_FIELD(new_jx, new_jy))
8805 return MF_NO_ACTION;
8807 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8808 return MF_NO_ACTION;
8811 element = MovingOrBlocked2Element(new_jx, new_jy);
8813 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8816 if (DONT_RUN_INTO(element))
8818 if (element == EL_ACID && dx == 0 && dy == 1)
8820 SplashAcid(new_jx, new_jy);
8821 Feld[jx][jy] = EL_PLAYER_1;
8822 InitMovingField(jx, jy, MV_DOWN);
8823 Store[jx][jy] = EL_ACID;
8824 ContinueMoving(jx, jy);
8828 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8833 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8834 if (can_move != MF_MOVING)
8837 /* check if DigField() has caused relocation of the player */
8838 if (player->jx != jx || player->jy != jy)
8839 return MF_NO_ACTION;
8841 StorePlayer[jx][jy] = 0;
8842 player->last_jx = jx;
8843 player->last_jy = jy;
8844 player->jx = new_jx;
8845 player->jy = new_jy;
8846 StorePlayer[new_jx][new_jy] = player->element_nr;
8849 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8851 player->step_counter++;
8854 player->drop_delay = 0;
8857 PlayerVisit[jx][jy] = FrameCounter;
8859 ScrollPlayer(player, SCROLL_INIT);
8862 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8864 CheckTriggeredElementChangeSide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
8866 CheckElementChangeSide(jx, jy, Feld[jx][jy], CE_LEFT_BY_PLAYER,leave_side);
8869 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
8871 CheckTriggeredElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8872 CE_OTHER_GETS_ENTERED, enter_side);
8873 CheckElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8874 CE_ENTERED_BY_PLAYER, enter_side);
8881 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8883 int jx = player->jx, jy = player->jy;
8884 int old_jx = jx, old_jy = jy;
8885 int moved = MF_NO_ACTION;
8888 if (!player->active)
8893 if (player->MovPos == 0)
8895 player->is_moving = FALSE;
8896 player->is_digging = FALSE;
8897 player->is_collecting = FALSE;
8898 player->is_snapping = FALSE;
8899 player->is_pushing = FALSE;
8905 if (!player->active || (!dx && !dy))
8910 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8916 if (!FrameReached(&player->move_delay, player->move_delay_value))
8919 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8920 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
8926 /* remove the last programmed player action */
8927 player->programmed_action = 0;
8931 /* should only happen if pre-1.2 tape recordings are played */
8932 /* this is only for backward compatibility */
8934 int original_move_delay_value = player->move_delay_value;
8937 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8941 /* scroll remaining steps with finest movement resolution */
8942 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8944 while (player->MovPos)
8946 ScrollPlayer(player, SCROLL_GO_ON);
8947 ScrollScreen(NULL, SCROLL_GO_ON);
8953 player->move_delay_value = original_move_delay_value;
8956 if (player->last_move_dir & MV_HORIZONTAL)
8958 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8959 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8963 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8964 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8970 if (moved & MF_MOVING && !ScreenMovPos &&
8971 (player == local_player || !options.network))
8973 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8974 int offset = (setup.scroll_delay ? 3 : 0);
8976 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8978 /* actual player has left the screen -- scroll in that direction */
8979 if (jx != old_jx) /* player has moved horizontally */
8980 scroll_x += (jx - old_jx);
8981 else /* player has moved vertically */
8982 scroll_y += (jy - old_jy);
8986 if (jx != old_jx) /* player has moved horizontally */
8988 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8989 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8990 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8992 /* don't scroll over playfield boundaries */
8993 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8994 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8996 /* don't scroll more than one field at a time */
8997 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8999 /* don't scroll against the player's moving direction */
9000 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9001 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9002 scroll_x = old_scroll_x;
9004 else /* player has moved vertically */
9006 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9007 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9008 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9010 /* don't scroll over playfield boundaries */
9011 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9012 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9014 /* don't scroll more than one field at a time */
9015 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9017 /* don't scroll against the player's moving direction */
9018 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9019 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9020 scroll_y = old_scroll_y;
9024 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9026 if (!options.network && !AllPlayersInVisibleScreen())
9028 scroll_x = old_scroll_x;
9029 scroll_y = old_scroll_y;
9033 ScrollScreen(player, SCROLL_INIT);
9034 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9041 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9043 if (!(moved & MF_MOVING) && !player->is_pushing)
9048 player->StepFrame = 0;
9050 if (moved & MF_MOVING)
9052 if (old_jx != jx && old_jy == jy)
9053 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9054 else if (old_jx == jx && old_jy != jy)
9055 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9057 DrawLevelField(jx, jy); /* for "crumbled sand" */
9059 player->last_move_dir = player->MovDir;
9060 player->is_moving = TRUE;
9062 player->is_snapping = FALSE;
9066 player->is_switching = FALSE;
9069 player->is_dropping = FALSE;
9073 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9075 static int trigger_sides[4][2] =
9077 /* enter side leave side */
9078 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9079 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9080 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9081 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9083 int move_direction = player->MovDir;
9084 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9085 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9088 CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9090 player->index_bit, leave_side);
9092 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9093 CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9095 player->index_bit, leave_side);
9097 CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
9098 CE_OTHER_GETS_ENTERED,
9099 player->index_bit, enter_side);
9101 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9102 CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9103 player->index_bit, enter_side);
9113 CheckGravityMovementWhenNotMoving(player);
9116 player->last_move_dir = MV_NO_MOVING;
9118 player->is_moving = FALSE;
9121 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9123 TestIfHeroTouchesBadThing(jx, jy);
9124 TestIfPlayerTouchesCustomElement(jx, jy);
9127 if (!player->active)
9133 void ScrollPlayer(struct PlayerInfo *player, int mode)
9135 int jx = player->jx, jy = player->jy;
9136 int last_jx = player->last_jx, last_jy = player->last_jy;
9137 int move_stepsize = TILEX / player->move_delay_value;
9139 if (!player->active || !player->MovPos)
9142 if (mode == SCROLL_INIT)
9144 player->actual_frame_counter = FrameCounter;
9145 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9147 if (Feld[last_jx][last_jy] == EL_EMPTY)
9148 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9156 else if (!FrameReached(&player->actual_frame_counter, 1))
9159 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9160 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9162 if (!player->block_last_field &&
9163 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9164 Feld[last_jx][last_jy] = EL_EMPTY;
9166 /* before DrawPlayer() to draw correct player graphic for this case */
9167 if (player->MovPos == 0)
9168 CheckGravityMovement(player);
9171 DrawPlayer(player); /* needed here only to cleanup last field */
9174 if (player->MovPos == 0) /* player reached destination field */
9177 if (player->move_delay_reset_counter > 0)
9179 player->move_delay_reset_counter--;
9181 if (player->move_delay_reset_counter == 0)
9183 /* continue with normal speed after quickly moving through gate */
9184 HALVE_PLAYER_SPEED(player);
9186 /* be able to make the next move without delay */
9187 player->move_delay = 0;
9191 if (IS_PASSABLE(Feld[last_jx][last_jy]))
9193 /* continue with normal speed after quickly moving through gate */
9194 HALVE_PLAYER_SPEED(player);
9196 /* be able to make the next move without delay */
9197 player->move_delay = 0;
9201 if (player->block_last_field &&
9202 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9203 Feld[last_jx][last_jy] = EL_EMPTY;
9205 player->last_jx = jx;
9206 player->last_jy = jy;
9208 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9209 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9210 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9212 DrawPlayer(player); /* needed here only to cleanup last field */
9215 if (local_player->friends_still_needed == 0 ||
9216 IS_SP_ELEMENT(Feld[jx][jy]))
9217 player->LevelSolved = player->GameOver = TRUE;
9221 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9222 /* this breaks one level: "machine", level 000 */
9224 static int trigger_sides[4][2] =
9226 /* enter side leave side */
9227 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9228 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9229 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9230 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9232 int move_direction = player->MovDir;
9233 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9234 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9235 int old_jx = last_jx;
9236 int old_jy = last_jy;
9239 CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9241 player->index_bit, leave_side);
9243 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9244 CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9246 player->index_bit, leave_side);
9248 CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
9249 CE_OTHER_GETS_ENTERED,
9250 player->index_bit, enter_side);
9252 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9253 CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9254 player->index_bit, enter_side);
9260 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9262 TestIfHeroTouchesBadThing(jx, jy);
9263 TestIfPlayerTouchesCustomElement(jx, jy);
9265 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9268 if (!player->active)
9272 if (level.use_step_counter)
9278 for (i = 0; i < MAX_PLAYERS; i++)
9280 struct PlayerInfo *player = &stored_player[i];
9282 if (SHIELD_ON(player))
9284 player->shield_normal_time_left--;
9286 if (player->shield_deadly_time_left > 0)
9287 player->shield_deadly_time_left--;
9295 if (TimeLeft <= 10 && setup.time_limit)
9296 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9298 DrawGameValue_Time(TimeLeft);
9300 if (!TimeLeft && setup.time_limit)
9301 for (i = 0; i < MAX_PLAYERS; i++)
9302 KillHero(&stored_player[i]);
9304 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9305 DrawGameValue_Time(TimePlayed);
9308 if (tape.single_step && tape.recording && !tape.pausing &&
9309 !player->programmed_action)
9310 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9314 void ScrollScreen(struct PlayerInfo *player, int mode)
9316 static unsigned long screen_frame_counter = 0;
9318 if (mode == SCROLL_INIT)
9320 /* set scrolling step size according to actual player's moving speed */
9321 ScrollStepSize = TILEX / player->move_delay_value;
9323 screen_frame_counter = FrameCounter;
9324 ScreenMovDir = player->MovDir;
9325 ScreenMovPos = player->MovPos;
9326 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9329 else if (!FrameReached(&screen_frame_counter, 1))
9334 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9335 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9336 redraw_mask |= REDRAW_FIELD;
9339 ScreenMovDir = MV_NO_MOVING;
9342 void TestIfPlayerTouchesCustomElement(int x, int y)
9344 static int xy[4][2] =
9351 static int trigger_sides[4][2] =
9353 /* center side border side */
9354 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9355 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9356 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9357 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9359 static int touch_dir[4] =
9366 int center_element = Feld[x][y]; /* should always be non-moving! */
9369 for (i = 0; i < NUM_DIRECTIONS; i++)
9371 int xx = x + xy[i][0];
9372 int yy = y + xy[i][1];
9373 int center_side = trigger_sides[i][0];
9374 int border_side = trigger_sides[i][1];
9377 if (!IN_LEV_FIELD(xx, yy))
9380 if (IS_PLAYER(x, y))
9382 struct PlayerInfo *player = PLAYERINFO(x, y);
9384 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9385 border_element = Feld[xx][yy]; /* may be moving! */
9386 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9387 border_element = Feld[xx][yy];
9388 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9389 border_element = MovingOrBlocked2Element(xx, yy);
9391 continue; /* center and border element do not touch */
9393 CheckTriggeredElementChangePlayer(xx, yy, border_element,
9394 CE_OTHER_GETS_TOUCHED,
9395 player->index_bit, border_side);
9396 CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9397 player->index_bit, border_side);
9399 else if (IS_PLAYER(xx, yy))
9401 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9403 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9405 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9406 continue; /* center and border element do not touch */
9409 CheckTriggeredElementChangePlayer(x, y, center_element,
9410 CE_OTHER_GETS_TOUCHED,
9411 player->index_bit, center_side);
9412 CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9413 player->index_bit, center_side);
9420 void TestIfElementTouchesCustomElement(int x, int y)
9422 static int xy[4][2] =
9429 static int trigger_sides[4][2] =
9431 /* center side border side */
9432 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9433 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9434 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9435 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9437 static int touch_dir[4] =
9444 boolean change_center_element = FALSE;
9445 int center_element_change_page = 0;
9446 int center_element = Feld[x][y]; /* should always be non-moving! */
9447 int border_trigger_element;
9450 for (i = 0; i < NUM_DIRECTIONS; i++)
9452 int xx = x + xy[i][0];
9453 int yy = y + xy[i][1];
9454 int center_side = trigger_sides[i][0];
9455 int border_side = trigger_sides[i][1];
9458 if (!IN_LEV_FIELD(xx, yy))
9461 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9462 border_element = Feld[xx][yy]; /* may be moving! */
9463 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9464 border_element = Feld[xx][yy];
9465 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9466 border_element = MovingOrBlocked2Element(xx, yy);
9468 continue; /* center and border element do not touch */
9470 /* check for change of center element (but change it only once) */
9471 if (IS_CUSTOM_ELEMENT(center_element) &&
9472 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9473 !change_center_element)
9475 for (j = 0; j < element_info[center_element].num_change_pages; j++)
9477 struct ElementChangeInfo *change =
9478 &element_info[center_element].change_page[j];
9480 if (change->can_change &&
9481 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9482 change->trigger_side & border_side &&
9484 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9486 change->trigger_element == border_element
9490 change_center_element = TRUE;
9491 center_element_change_page = j;
9492 border_trigger_element = border_element;
9499 /* check for change of border element */
9500 if (IS_CUSTOM_ELEMENT(border_element) &&
9501 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9503 for (j = 0; j < element_info[border_element].num_change_pages; j++)
9505 struct ElementChangeInfo *change =
9506 &element_info[border_element].change_page[j];
9508 if (change->can_change &&
9509 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9510 change->trigger_side & center_side &&
9512 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9514 change->trigger_element == center_element
9519 printf("::: border_element %d, %d\n", x, y);
9522 CheckElementChangePage(xx, yy, border_element, center_element,
9523 CE_OTHER_IS_TOUCHING, j);
9530 if (change_center_element)
9533 printf("::: center_element %d, %d\n", x, y);
9536 CheckElementChangePage(x, y, center_element, border_trigger_element,
9537 CE_OTHER_IS_TOUCHING, center_element_change_page);
9541 void TestIfElementHitsCustomElement(int x, int y, int direction)
9543 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9544 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9545 int hitx = x + dx, hity = y + dy;
9546 int hitting_element = Feld[x][y];
9547 int touched_element;
9549 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9550 !IS_FREE(hitx, hity) &&
9551 (!IS_MOVING(hitx, hity) ||
9552 MovDir[hitx][hity] != direction ||
9553 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9556 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9560 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9564 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9565 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9567 CheckElementChangeSide(x, y, hitting_element, touched_element,
9568 CE_HITTING_SOMETHING, direction);
9570 if (IN_LEV_FIELD(hitx, hity))
9572 int opposite_direction = MV_DIR_OPPOSITE(direction);
9573 int hitting_side = direction;
9574 int touched_side = opposite_direction;
9576 int touched_element = MovingOrBlocked2Element(hitx, hity);
9579 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9580 MovDir[hitx][hity] != direction ||
9581 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9590 CheckElementChangeSide(hitx, hity, touched_element, hitting_element,
9591 CE_HIT_BY_SOMETHING, opposite_direction);
9593 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9594 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
9596 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9598 struct ElementChangeInfo *change =
9599 &element_info[hitting_element].change_page[i];
9601 if (change->can_change &&
9602 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
9603 change->trigger_side & touched_side &&
9606 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9608 change->trigger_element == touched_element
9612 CheckElementChangePage(x, y, hitting_element, touched_element,
9613 CE_OTHER_IS_HITTING, i);
9619 if (IS_CUSTOM_ELEMENT(touched_element) &&
9620 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
9622 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9624 struct ElementChangeInfo *change =
9625 &element_info[touched_element].change_page[i];
9627 if (change->can_change &&
9628 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
9629 change->trigger_side & hitting_side &&
9631 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9633 change->trigger_element == hitting_element
9637 CheckElementChangePage(hitx, hity, touched_element,
9638 hitting_element, CE_OTHER_GETS_HIT, i);
9648 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9650 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9651 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9652 int hitx = x + dx, hity = y + dy;
9653 int hitting_element = Feld[x][y];
9654 int touched_element;
9656 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9657 !IS_FREE(hitx, hity) &&
9658 (!IS_MOVING(hitx, hity) ||
9659 MovDir[hitx][hity] != direction ||
9660 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9663 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9667 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9671 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9672 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9674 CheckElementChangeSide(x, y, hitting_element, touched_element,
9675 EP_CAN_SMASH_EVERYTHING, direction);
9677 if (IN_LEV_FIELD(hitx, hity))
9679 int opposite_direction = MV_DIR_OPPOSITE(direction);
9680 int hitting_side = direction;
9681 int touched_side = opposite_direction;
9683 int touched_element = MovingOrBlocked2Element(hitx, hity);
9686 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9687 MovDir[hitx][hity] != direction ||
9688 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9697 CheckElementChangeSide(hitx, hity, touched_element, hitting_element,
9698 CE_SMASHED_BY_SOMETHING, opposite_direction);
9700 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9701 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
9703 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9705 struct ElementChangeInfo *change =
9706 &element_info[hitting_element].change_page[i];
9708 if (change->can_change &&
9709 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
9710 change->trigger_side & touched_side &&
9713 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9715 change->trigger_element == touched_element
9719 CheckElementChangePage(x, y, hitting_element, touched_element,
9720 CE_OTHER_IS_SMASHING, i);
9726 if (IS_CUSTOM_ELEMENT(touched_element) &&
9727 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
9729 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9731 struct ElementChangeInfo *change =
9732 &element_info[touched_element].change_page[i];
9734 if (change->can_change &&
9735 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
9736 change->trigger_side & hitting_side &&
9738 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9740 change->trigger_element == hitting_element
9744 CheckElementChangePage(hitx, hity, touched_element,
9745 hitting_element, CE_OTHER_GETS_SMASHED, i);
9755 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9757 int i, kill_x = -1, kill_y = -1;
9758 static int test_xy[4][2] =
9765 static int test_dir[4] =
9773 for (i = 0; i < NUM_DIRECTIONS; i++)
9775 int test_x, test_y, test_move_dir, test_element;
9777 test_x = good_x + test_xy[i][0];
9778 test_y = good_y + test_xy[i][1];
9779 if (!IN_LEV_FIELD(test_x, test_y))
9783 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9786 test_element = Feld[test_x][test_y];
9788 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9791 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9792 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9794 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9795 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9803 if (kill_x != -1 || kill_y != -1)
9805 if (IS_PLAYER(good_x, good_y))
9807 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9809 if (player->shield_deadly_time_left > 0)
9810 Bang(kill_x, kill_y);
9811 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9815 Bang(good_x, good_y);
9819 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9821 int i, kill_x = -1, kill_y = -1;
9822 int bad_element = Feld[bad_x][bad_y];
9823 static int test_xy[4][2] =
9830 static int touch_dir[4] =
9837 static int test_dir[4] =
9845 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9848 for (i = 0; i < NUM_DIRECTIONS; i++)
9850 int test_x, test_y, test_move_dir, test_element;
9852 test_x = bad_x + test_xy[i][0];
9853 test_y = bad_y + test_xy[i][1];
9854 if (!IN_LEV_FIELD(test_x, test_y))
9858 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9860 test_element = Feld[test_x][test_y];
9862 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9863 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9865 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9866 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9868 /* good thing is player or penguin that does not move away */
9869 if (IS_PLAYER(test_x, test_y))
9871 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9873 if (bad_element == EL_ROBOT && player->is_moving)
9874 continue; /* robot does not kill player if he is moving */
9876 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9878 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9879 continue; /* center and border element do not touch */
9886 else if (test_element == EL_PENGUIN)
9895 if (kill_x != -1 || kill_y != -1)
9897 if (IS_PLAYER(kill_x, kill_y))
9899 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9901 if (player->shield_deadly_time_left > 0)
9903 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9907 Bang(kill_x, kill_y);
9911 void TestIfHeroTouchesBadThing(int x, int y)
9913 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9916 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
9918 TestIfGoodThingHitsBadThing(x, y, move_dir);
9921 void TestIfBadThingTouchesHero(int x, int y)
9923 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9926 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
9928 TestIfBadThingHitsGoodThing(x, y, move_dir);
9931 void TestIfFriendTouchesBadThing(int x, int y)
9933 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9936 void TestIfBadThingTouchesFriend(int x, int y)
9938 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9941 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9943 int i, kill_x = bad_x, kill_y = bad_y;
9944 static int xy[4][2] =
9952 for (i = 0; i < NUM_DIRECTIONS; i++)
9956 x = bad_x + xy[i][0];
9957 y = bad_y + xy[i][1];
9958 if (!IN_LEV_FIELD(x, y))
9961 element = Feld[x][y];
9962 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9963 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9971 if (kill_x != bad_x || kill_y != bad_y)
9975 void KillHero(struct PlayerInfo *player)
9977 int jx = player->jx, jy = player->jy;
9979 if (!player->active)
9982 /* remove accessible field at the player's position */
9983 Feld[jx][jy] = EL_EMPTY;
9985 /* deactivate shield (else Bang()/Explode() would not work right) */
9986 player->shield_normal_time_left = 0;
9987 player->shield_deadly_time_left = 0;
9993 static void KillHeroUnlessEnemyProtected(int x, int y)
9995 if (!PLAYER_ENEMY_PROTECTED(x, y))
9996 KillHero(PLAYERINFO(x, y));
9999 static void KillHeroUnlessExplosionProtected(int x, int y)
10001 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10002 KillHero(PLAYERINFO(x, y));
10005 void BuryHero(struct PlayerInfo *player)
10007 int jx = player->jx, jy = player->jy;
10009 if (!player->active)
10013 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10015 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10017 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10019 player->GameOver = TRUE;
10020 RemoveHero(player);
10023 void RemoveHero(struct PlayerInfo *player)
10025 int jx = player->jx, jy = player->jy;
10026 int i, found = FALSE;
10028 player->present = FALSE;
10029 player->active = FALSE;
10031 if (!ExplodeField[jx][jy])
10032 StorePlayer[jx][jy] = 0;
10034 for (i = 0; i < MAX_PLAYERS; i++)
10035 if (stored_player[i].active)
10039 AllPlayersGone = TRUE;
10046 =============================================================================
10047 checkDiagonalPushing()
10048 -----------------------------------------------------------------------------
10049 check if diagonal input device direction results in pushing of object
10050 (by checking if the alternative direction is walkable, diggable, ...)
10051 =============================================================================
10054 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10055 int x, int y, int real_dx, int real_dy)
10057 int jx, jy, dx, dy, xx, yy;
10059 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10062 /* diagonal direction: check alternative direction */
10067 xx = jx + (dx == 0 ? real_dx : 0);
10068 yy = jy + (dy == 0 ? real_dy : 0);
10070 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10074 =============================================================================
10076 -----------------------------------------------------------------------------
10077 x, y: field next to player (non-diagonal) to try to dig to
10078 real_dx, real_dy: direction as read from input device (can be diagonal)
10079 =============================================================================
10082 int DigField(struct PlayerInfo *player,
10083 int oldx, int oldy, int x, int y,
10084 int real_dx, int real_dy, int mode)
10086 static int trigger_sides[4] =
10088 CH_SIDE_RIGHT, /* moving left */
10089 CH_SIDE_LEFT, /* moving right */
10090 CH_SIDE_BOTTOM, /* moving up */
10091 CH_SIDE_TOP, /* moving down */
10094 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10096 int jx = oldx, jy = oldy;
10097 int dx = x - jx, dy = y - jy;
10098 int nextx = x + dx, nexty = y + dy;
10099 int move_direction = (dx == -1 ? MV_LEFT :
10100 dx == +1 ? MV_RIGHT :
10102 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10103 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10104 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10105 int old_element = Feld[jx][jy];
10108 if (player->MovPos == 0)
10110 player->is_digging = FALSE;
10111 player->is_collecting = FALSE;
10114 if (player->MovPos == 0) /* last pushing move finished */
10115 player->is_pushing = FALSE;
10117 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10119 player->is_switching = FALSE;
10120 player->push_delay = 0;
10122 return MF_NO_ACTION;
10125 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10126 return MF_NO_ACTION;
10131 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10133 if (IS_TUBE(Feld[jx][jy]) ||
10134 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10138 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10139 int tube_leave_directions[][2] =
10141 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10142 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10143 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10144 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
10145 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
10146 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
10147 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
10148 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
10149 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
10150 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
10151 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
10152 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10155 while (tube_leave_directions[i][0] != tube_element)
10158 if (tube_leave_directions[i][0] == -1) /* should not happen */
10162 if (!(tube_leave_directions[i][1] & move_direction))
10163 return MF_NO_ACTION; /* tube has no opening in this direction */
10168 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10169 old_element = Back[jx][jy];
10173 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10174 return MF_NO_ACTION; /* field has no opening in this direction */
10176 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10177 return MF_NO_ACTION; /* field has no opening in this direction */
10179 element = Feld[x][y];
10181 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10182 game.engine_version >= VERSION_IDENT(2,2,0,0))
10183 return MF_NO_ACTION;
10188 case EL_SP_PORT_LEFT:
10189 case EL_SP_PORT_RIGHT:
10190 case EL_SP_PORT_UP:
10191 case EL_SP_PORT_DOWN:
10192 case EL_SP_PORT_HORIZONTAL:
10193 case EL_SP_PORT_VERTICAL:
10194 case EL_SP_PORT_ANY:
10195 case EL_SP_GRAVITY_PORT_LEFT:
10196 case EL_SP_GRAVITY_PORT_RIGHT:
10197 case EL_SP_GRAVITY_PORT_UP:
10198 case EL_SP_GRAVITY_PORT_DOWN:
10200 if (!canEnterSupaplexPort(x, y, dx, dy))
10201 return MF_NO_ACTION;
10204 element != EL_SP_PORT_LEFT &&
10205 element != EL_SP_GRAVITY_PORT_LEFT &&
10206 element != EL_SP_PORT_HORIZONTAL &&
10207 element != EL_SP_PORT_ANY) ||
10209 element != EL_SP_PORT_RIGHT &&
10210 element != EL_SP_GRAVITY_PORT_RIGHT &&
10211 element != EL_SP_PORT_HORIZONTAL &&
10212 element != EL_SP_PORT_ANY) ||
10214 element != EL_SP_PORT_UP &&
10215 element != EL_SP_GRAVITY_PORT_UP &&
10216 element != EL_SP_PORT_VERTICAL &&
10217 element != EL_SP_PORT_ANY) ||
10219 element != EL_SP_PORT_DOWN &&
10220 element != EL_SP_GRAVITY_PORT_DOWN &&
10221 element != EL_SP_PORT_VERTICAL &&
10222 element != EL_SP_PORT_ANY) ||
10223 !IN_LEV_FIELD(nextx, nexty) ||
10224 !IS_FREE(nextx, nexty))
10225 return MF_NO_ACTION;
10228 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10229 element == EL_SP_GRAVITY_PORT_RIGHT ||
10230 element == EL_SP_GRAVITY_PORT_UP ||
10231 element == EL_SP_GRAVITY_PORT_DOWN)
10232 game.gravity = !game.gravity;
10234 /* automatically move to the next field with double speed */
10235 player->programmed_action = move_direction;
10237 if (player->move_delay_reset_counter == 0)
10239 player->move_delay_reset_counter = 2; /* two double speed steps */
10241 DOUBLE_PLAYER_SPEED(player);
10244 player->move_delay_reset_counter = 2;
10246 DOUBLE_PLAYER_SPEED(player);
10250 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10253 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10259 case EL_TUBE_VERTICAL:
10260 case EL_TUBE_HORIZONTAL:
10261 case EL_TUBE_VERTICAL_LEFT:
10262 case EL_TUBE_VERTICAL_RIGHT:
10263 case EL_TUBE_HORIZONTAL_UP:
10264 case EL_TUBE_HORIZONTAL_DOWN:
10265 case EL_TUBE_LEFT_UP:
10266 case EL_TUBE_LEFT_DOWN:
10267 case EL_TUBE_RIGHT_UP:
10268 case EL_TUBE_RIGHT_DOWN:
10271 int tube_enter_directions[][2] =
10273 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10274 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10275 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10276 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
10277 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
10278 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
10279 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
10280 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
10281 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
10282 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
10283 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
10284 { -1, MV_NO_MOVING }
10287 while (tube_enter_directions[i][0] != element)
10290 if (tube_enter_directions[i][0] == -1) /* should not happen */
10294 if (!(tube_enter_directions[i][1] & move_direction))
10295 return MF_NO_ACTION; /* tube has no opening in this direction */
10297 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10304 if (IS_WALKABLE(element))
10306 int sound_action = ACTION_WALKING;
10308 if (!ACCESS_FROM(element, opposite_direction))
10309 return MF_NO_ACTION; /* field not accessible from this direction */
10311 if (element >= EL_GATE_1 && element <= EL_GATE_4)
10313 if (!player->key[element - EL_GATE_1])
10314 return MF_NO_ACTION;
10316 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
10318 if (!player->key[element - EL_GATE_1_GRAY])
10319 return MF_NO_ACTION;
10321 else if (element == EL_EXIT_OPEN ||
10322 element == EL_SP_EXIT_OPEN ||
10323 element == EL_SP_EXIT_OPENING)
10325 sound_action = ACTION_PASSING; /* player is passing exit */
10327 else if (element == EL_EMPTY)
10329 sound_action = ACTION_MOVING; /* nothing to walk on */
10332 /* play sound from background or player, whatever is available */
10333 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
10334 PlayLevelSoundElementAction(x, y, element, sound_action);
10336 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10340 else if (IS_PASSABLE(element))
10342 boolean next_field_must_be_free = TRUE;
10345 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
10346 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
10347 (next_field_must_be_free && !IS_FREE(nextx, nexty)))
10348 return MF_NO_ACTION;
10350 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10351 return MF_NO_ACTION;
10355 if (!ACCESS_FROM(element, opposite_direction))
10356 return MF_NO_ACTION; /* field not accessible from this direction */
10358 if (IS_CUSTOM_ELEMENT(element) &&
10359 !ACCESS_FROM(element, opposite_direction))
10360 return MF_NO_ACTION; /* field not accessible from this direction */
10364 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10365 return MF_NO_ACTION;
10368 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
10370 if (!player->key[element - EL_EM_GATE_1])
10371 return MF_NO_ACTION;
10373 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
10375 if (!player->key[element - EL_EM_GATE_1_GRAY])
10376 return MF_NO_ACTION;
10378 else if (IS_SP_PORT(element))
10380 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10381 element == EL_SP_GRAVITY_PORT_RIGHT ||
10382 element == EL_SP_GRAVITY_PORT_UP ||
10383 element == EL_SP_GRAVITY_PORT_DOWN)
10384 game.gravity = !game.gravity;
10387 /* automatically move to the next field with double speed */
10388 player->programmed_action = move_direction;
10390 if (player->move_delay_reset_counter == 0)
10392 player->move_delay_reset_counter = 2; /* two double speed steps */
10394 DOUBLE_PLAYER_SPEED(player);
10397 player->move_delay_reset_counter = 2;
10399 DOUBLE_PLAYER_SPEED(player);
10402 PlayLevelSoundAction(x, y, ACTION_PASSING);
10406 else if (IS_DIGGABLE(element))
10410 if (mode != DF_SNAP)
10413 GfxElement[x][y] = GFX_ELEMENT(element);
10416 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
10418 player->is_digging = TRUE;
10421 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10423 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED,
10424 player->index_bit, dig_side);
10427 if (mode == DF_SNAP)
10428 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10433 else if (IS_COLLECTIBLE(element))
10437 if (mode != DF_SNAP)
10439 GfxElement[x][y] = element;
10440 player->is_collecting = TRUE;
10443 if (element == EL_SPEED_PILL)
10444 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10445 else if (element == EL_EXTRA_TIME && level.time > 0)
10448 DrawGameValue_Time(TimeLeft);
10450 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10452 player->shield_normal_time_left += 10;
10453 if (element == EL_SHIELD_DEADLY)
10454 player->shield_deadly_time_left += 10;
10456 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10458 if (player->inventory_size < MAX_INVENTORY_SIZE)
10459 player->inventory_element[player->inventory_size++] = element;
10461 DrawGameValue_Dynamite(local_player->inventory_size);
10463 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10465 player->dynabomb_count++;
10466 player->dynabombs_left++;
10468 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10470 player->dynabomb_size++;
10472 else if (element == EL_DYNABOMB_INCREASE_POWER)
10474 player->dynabomb_xl = TRUE;
10476 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
10477 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
10479 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
10480 element - EL_KEY_1 : element - EL_EM_KEY_1);
10482 player->key[key_nr] = TRUE;
10484 DrawGameValue_Keys(player);
10486 redraw_mask |= REDRAW_DOOR_1;
10488 else if (IS_ENVELOPE(element))
10491 player->show_envelope = element;
10493 ShowEnvelope(element - EL_ENVELOPE_1);
10496 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
10500 if (element_info[element].collect_count == 0)
10501 player->inventory_infinite_element = element;
10503 for (i = 0; i < element_info[element].collect_count; i++)
10504 if (player->inventory_size < MAX_INVENTORY_SIZE)
10505 player->inventory_element[player->inventory_size++] = element;
10507 DrawGameValue_Dynamite(local_player->inventory_size);
10509 else if (element_info[element].collect_count > 0)
10511 local_player->gems_still_needed -=
10512 element_info[element].collect_count;
10513 if (local_player->gems_still_needed < 0)
10514 local_player->gems_still_needed = 0;
10516 DrawGameValue_Emeralds(local_player->gems_still_needed);
10519 RaiseScoreElement(element);
10520 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10522 CheckTriggeredElementChangePlayer(x, y, element,
10523 CE_OTHER_GETS_COLLECTED,
10524 player->index_bit, dig_side);
10527 if (mode == DF_SNAP)
10528 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10533 else if (IS_PUSHABLE(element))
10535 if (mode == DF_SNAP && element != EL_BD_ROCK)
10536 return MF_NO_ACTION;
10538 if (CAN_FALL(element) && dy)
10539 return MF_NO_ACTION;
10541 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10542 !(element == EL_SPRING && level.use_spring_bug))
10543 return MF_NO_ACTION;
10546 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10547 ((move_direction & MV_VERTICAL &&
10548 ((element_info[element].move_pattern & MV_LEFT &&
10549 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10550 (element_info[element].move_pattern & MV_RIGHT &&
10551 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10552 (move_direction & MV_HORIZONTAL &&
10553 ((element_info[element].move_pattern & MV_UP &&
10554 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10555 (element_info[element].move_pattern & MV_DOWN &&
10556 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10557 return MF_NO_ACTION;
10561 /* do not push elements already moving away faster than player */
10562 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10563 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10564 return MF_NO_ACTION;
10566 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
10567 return MF_NO_ACTION;
10571 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10573 if (player->push_delay_value == -1)
10574 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10576 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10578 if (!player->is_pushing)
10579 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10583 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
10584 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
10585 !player_is_pushing))
10586 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10589 if (!player->is_pushing &&
10590 game.engine_version >= VERSION_IDENT(2,2,0,7))
10591 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10595 printf("::: push delay: %ld [%d, %d] [%d]\n",
10596 player->push_delay_value, FrameCounter, game.engine_version,
10597 player->is_pushing);
10600 player->is_pushing = TRUE;
10602 if (!(IN_LEV_FIELD(nextx, nexty) &&
10603 (IS_FREE(nextx, nexty) ||
10604 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10605 IS_SB_ELEMENT(element)))))
10606 return MF_NO_ACTION;
10608 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10609 return MF_NO_ACTION;
10611 if (player->push_delay == 0) /* new pushing; restart delay */
10612 player->push_delay = FrameCounter;
10614 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
10615 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10616 element != EL_SPRING && element != EL_BALLOON)
10618 /* make sure that there is no move delay before next try to push */
10619 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10620 player->move_delay = INITIAL_MOVE_DELAY_OFF;
10622 return MF_NO_ACTION;
10626 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
10629 if (IS_SB_ELEMENT(element))
10631 if (element == EL_SOKOBAN_FIELD_FULL)
10633 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10634 local_player->sokobanfields_still_needed++;
10637 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10639 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10640 local_player->sokobanfields_still_needed--;
10643 Feld[x][y] = EL_SOKOBAN_OBJECT;
10645 if (Back[x][y] == Back[nextx][nexty])
10646 PlayLevelSoundAction(x, y, ACTION_PUSHING);
10647 else if (Back[x][y] != 0)
10648 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10651 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10654 if (local_player->sokobanfields_still_needed == 0 &&
10655 game.emulation == EMU_SOKOBAN)
10657 player->LevelSolved = player->GameOver = TRUE;
10658 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10662 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10664 InitMovingField(x, y, move_direction);
10665 GfxAction[x][y] = ACTION_PUSHING;
10667 if (mode == DF_SNAP)
10668 ContinueMoving(x, y);
10670 MovPos[x][y] = (dx != 0 ? dx : dy);
10672 Pushed[x][y] = TRUE;
10673 Pushed[nextx][nexty] = TRUE;
10675 if (game.engine_version < VERSION_IDENT(2,2,0,7))
10676 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10678 player->push_delay_value = -1; /* get new value later */
10680 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED,
10681 player->index_bit, dig_side);
10682 CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10683 player->index_bit, dig_side);
10687 else if (IS_SWITCHABLE(element))
10689 if (PLAYER_SWITCHING(player, x, y))
10692 player->is_switching = TRUE;
10693 player->switch_x = x;
10694 player->switch_y = y;
10696 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10698 if (element == EL_ROBOT_WHEEL)
10700 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10704 DrawLevelField(x, y);
10706 else if (element == EL_SP_TERMINAL)
10710 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
10712 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10714 else if (Feld[xx][yy] == EL_SP_TERMINAL)
10715 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10718 else if (IS_BELT_SWITCH(element))
10720 ToggleBeltSwitch(x, y);
10722 else if (element == EL_SWITCHGATE_SWITCH_UP ||
10723 element == EL_SWITCHGATE_SWITCH_DOWN)
10725 ToggleSwitchgateSwitch(x, y);
10727 else if (element == EL_LIGHT_SWITCH ||
10728 element == EL_LIGHT_SWITCH_ACTIVE)
10730 ToggleLightSwitch(x, y);
10733 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
10734 SND_LIGHT_SWITCH_ACTIVATING :
10735 SND_LIGHT_SWITCH_DEACTIVATING);
10738 else if (element == EL_TIMEGATE_SWITCH)
10740 ActivateTimegateSwitch(x, y);
10742 else if (element == EL_BALLOON_SWITCH_LEFT ||
10743 element == EL_BALLOON_SWITCH_RIGHT ||
10744 element == EL_BALLOON_SWITCH_UP ||
10745 element == EL_BALLOON_SWITCH_DOWN ||
10746 element == EL_BALLOON_SWITCH_ANY)
10748 if (element == EL_BALLOON_SWITCH_ANY)
10749 game.balloon_dir = move_direction;
10751 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10752 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10753 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10754 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10757 else if (element == EL_LAMP)
10759 Feld[x][y] = EL_LAMP_ACTIVE;
10760 local_player->lights_still_needed--;
10762 DrawLevelField(x, y);
10764 else if (element == EL_TIME_ORB_FULL)
10766 Feld[x][y] = EL_TIME_ORB_EMPTY;
10768 DrawGameValue_Time(TimeLeft);
10770 DrawLevelField(x, y);
10773 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
10781 if (!PLAYER_SWITCHING(player, x, y))
10783 player->is_switching = TRUE;
10784 player->switch_x = x;
10785 player->switch_y = y;
10787 CheckTriggeredElementChangePlayer(x, y, element,
10788 CE_OTHER_IS_SWITCHING,
10789 player->index_bit, dig_side);
10790 CheckElementChangePlayer(x, y, element, CE_SWITCHED,
10791 player->index_bit, dig_side);
10794 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED,
10795 player->index_bit, dig_side);
10796 CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10797 player->index_bit, dig_side);
10800 return MF_NO_ACTION;
10803 player->push_delay = 0;
10805 if (Feld[x][y] != element) /* really digged/collected something */
10806 player->is_collecting = !player->is_digging;
10811 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10813 int jx = player->jx, jy = player->jy;
10814 int x = jx + dx, y = jy + dy;
10815 int snap_direction = (dx == -1 ? MV_LEFT :
10816 dx == +1 ? MV_RIGHT :
10818 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10821 if (player->MovPos)
10824 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
10828 if (!player->active || !IN_LEV_FIELD(x, y))
10836 if (player->MovPos == 0)
10837 player->is_pushing = FALSE;
10839 player->is_snapping = FALSE;
10841 if (player->MovPos == 0)
10843 player->is_moving = FALSE;
10844 player->is_digging = FALSE;
10845 player->is_collecting = FALSE;
10851 if (player->is_snapping)
10854 player->MovDir = snap_direction;
10857 if (player->MovPos == 0)
10860 player->is_moving = FALSE;
10861 player->is_digging = FALSE;
10862 player->is_collecting = FALSE;
10865 player->is_dropping = FALSE;
10867 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10870 player->is_snapping = TRUE;
10873 if (player->MovPos == 0)
10876 player->is_moving = FALSE;
10877 player->is_digging = FALSE;
10878 player->is_collecting = FALSE;
10881 DrawLevelField(x, y);
10887 boolean DropElement(struct PlayerInfo *player)
10889 static int trigger_sides[4] =
10891 CH_SIDE_LEFT, /* dropping left */
10892 CH_SIDE_RIGHT, /* dropping right */
10893 CH_SIDE_TOP, /* dropping up */
10894 CH_SIDE_BOTTOM, /* dropping down */
10896 int jx = player->jx, jy = player->jy;
10897 int drop_direction = player->MovDir;
10898 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
10899 int old_element = Feld[jx][jy];
10900 int drop_element = (player->inventory_size > 0 ?
10901 player->inventory_element[player->inventory_size - 1] :
10902 player->inventory_infinite_element != EL_UNDEFINED ?
10903 player->inventory_infinite_element :
10904 player->dynabombs_left > 0 ?
10905 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10907 int new_element = drop_element; /* default: element does not change */
10909 /* check if player is active, not moving and ready to drop */
10910 if (!player->active || player->MovPos || player->drop_delay > 0)
10913 /* check if player has anything that can be dropped */
10915 if (new_element == EL_UNDEFINED)
10918 if (player->inventory_size == 0 &&
10919 player->inventory_infinite_element == EL_UNDEFINED &&
10920 player->dynabombs_left == 0)
10924 /* check if anything can be dropped at the current position */
10925 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10928 /* collected custom elements can only be dropped on empty fields */
10930 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10933 if (player->inventory_size > 0 &&
10934 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
10935 && old_element != EL_EMPTY)
10939 if (old_element != EL_EMPTY)
10940 Back[jx][jy] = old_element; /* store old element on this field */
10942 ResetGfxAnimation(jx, jy);
10943 ResetRandomAnimationValue(jx, jy);
10945 if (player->inventory_size > 0 ||
10946 player->inventory_infinite_element != EL_UNDEFINED)
10948 if (player->inventory_size > 0)
10950 player->inventory_size--;
10953 new_element = player->inventory_element[player->inventory_size];
10956 DrawGameValue_Dynamite(local_player->inventory_size);
10958 if (new_element == EL_DYNAMITE)
10959 new_element = EL_DYNAMITE_ACTIVE;
10960 else if (new_element == EL_SP_DISK_RED)
10961 new_element = EL_SP_DISK_RED_ACTIVE;
10964 Feld[jx][jy] = new_element;
10966 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10967 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10969 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10972 /* needed if previous element just changed to "empty" in the last frame */
10973 Changed[jx][jy] = 0; /* allow another change */
10976 CheckTriggeredElementChangePlayer(jx, jy, new_element,
10977 CE_OTHER_GETS_DROPPED,
10978 player->index_bit, drop_side);
10979 CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
10980 player->index_bit, drop_side);
10982 TestIfElementTouchesCustomElement(jx, jy);
10984 else /* player is dropping a dyna bomb */
10986 player->dynabombs_left--;
10989 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
10992 Feld[jx][jy] = new_element;
10994 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10995 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10997 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
11004 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
11007 InitField_WithBug1(jx, jy, FALSE);
11009 InitField(jx, jy, FALSE);
11010 if (CAN_MOVE(Feld[jx][jy]))
11011 InitMovDir(jx, jy);
11015 new_element = Feld[jx][jy]; /* element might have changed */
11017 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11018 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11021 int move_stepsize = element_info[new_element].move_stepsize;
11023 int direction, dx, dy, nextx, nexty;
11025 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11026 MovDir[jx][jy] = player->MovDir;
11028 direction = MovDir[jx][jy];
11029 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11030 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11034 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
11037 WasJustMoving[jx][jy] = 3;
11039 InitMovingField(jx, jy, direction);
11040 ContinueMoving(jx, jy);
11045 Changed[jx][jy] = 0; /* allow another change */
11048 TestIfElementHitsCustomElement(jx, jy, direction);
11050 CheckElementChangeSide(jx, jy, new_element, touched_element,
11051 CE_HITTING_SOMETHING, direction);
11056 player->drop_delay = 2 * TILEX / move_stepsize + 1;
11061 player->drop_delay = 8 + 8 + 8;
11065 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11070 player->is_dropping = TRUE;
11076 /* ------------------------------------------------------------------------- */
11077 /* game sound playing functions */
11078 /* ------------------------------------------------------------------------- */
11080 static int *loop_sound_frame = NULL;
11081 static int *loop_sound_volume = NULL;
11083 void InitPlayLevelSound()
11085 int num_sounds = getSoundListSize();
11087 checked_free(loop_sound_frame);
11088 checked_free(loop_sound_volume);
11090 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11091 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11094 static void PlayLevelSound(int x, int y, int nr)
11096 int sx = SCREENX(x), sy = SCREENY(y);
11097 int volume, stereo_position;
11098 int max_distance = 8;
11099 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11101 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11102 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11105 if (!IN_LEV_FIELD(x, y) ||
11106 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11107 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11110 volume = SOUND_MAX_VOLUME;
11112 if (!IN_SCR_FIELD(sx, sy))
11114 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11115 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11117 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11120 stereo_position = (SOUND_MAX_LEFT +
11121 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11122 (SCR_FIELDX + 2 * max_distance));
11124 if (IS_LOOP_SOUND(nr))
11126 /* This assures that quieter loop sounds do not overwrite louder ones,
11127 while restarting sound volume comparison with each new game frame. */
11129 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11132 loop_sound_volume[nr] = volume;
11133 loop_sound_frame[nr] = FrameCounter;
11136 PlaySoundExt(nr, volume, stereo_position, type);
11139 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11141 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11142 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11143 y < LEVELY(BY1) ? LEVELY(BY1) :
11144 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11148 static void PlayLevelSoundAction(int x, int y, int action)
11150 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11153 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11155 int sound_effect = element_info[element].sound[action];
11157 if (sound_effect != SND_UNDEFINED)
11158 PlayLevelSound(x, y, sound_effect);
11161 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11164 int sound_effect = element_info[element].sound[action];
11166 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11167 PlayLevelSound(x, y, sound_effect);
11170 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11172 int sound_effect = element_info[Feld[x][y]].sound[action];
11174 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11175 PlayLevelSound(x, y, sound_effect);
11178 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11180 int sound_effect = element_info[Feld[x][y]].sound[action];
11182 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11183 StopSound(sound_effect);
11186 static void PlayLevelMusic()
11188 if (levelset.music[level_nr] != MUS_UNDEFINED)
11189 PlayMusic(levelset.music[level_nr]); /* from config file */
11191 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11194 void RaiseScore(int value)
11196 local_player->score += value;
11198 DrawGameValue_Score(local_player->score);
11201 void RaiseScoreElement(int element)
11206 case EL_BD_DIAMOND:
11207 case EL_EMERALD_YELLOW:
11208 case EL_EMERALD_RED:
11209 case EL_EMERALD_PURPLE:
11210 case EL_SP_INFOTRON:
11211 RaiseScore(level.score[SC_EMERALD]);
11214 RaiseScore(level.score[SC_DIAMOND]);
11217 RaiseScore(level.score[SC_CRYSTAL]);
11220 RaiseScore(level.score[SC_PEARL]);
11223 case EL_BD_BUTTERFLY:
11224 case EL_SP_ELECTRON:
11225 RaiseScore(level.score[SC_BUG]);
11228 case EL_BD_FIREFLY:
11229 case EL_SP_SNIKSNAK:
11230 RaiseScore(level.score[SC_SPACESHIP]);
11233 case EL_DARK_YAMYAM:
11234 RaiseScore(level.score[SC_YAMYAM]);
11237 RaiseScore(level.score[SC_ROBOT]);
11240 RaiseScore(level.score[SC_PACMAN]);
11243 RaiseScore(level.score[SC_NUT]);
11246 case EL_SP_DISK_RED:
11247 case EL_DYNABOMB_INCREASE_NUMBER:
11248 case EL_DYNABOMB_INCREASE_SIZE:
11249 case EL_DYNABOMB_INCREASE_POWER:
11250 RaiseScore(level.score[SC_DYNAMITE]);
11252 case EL_SHIELD_NORMAL:
11253 case EL_SHIELD_DEADLY:
11254 RaiseScore(level.score[SC_SHIELD]);
11256 case EL_EXTRA_TIME:
11257 RaiseScore(level.score[SC_TIME_BONUS]);
11263 RaiseScore(level.score[SC_KEY]);
11266 RaiseScore(element_info[element].collect_score);
11271 void RequestQuitGame(boolean ask_if_really_quit)
11273 if (AllPlayersGone ||
11274 !ask_if_really_quit ||
11275 level_editor_test_game ||
11276 Request("Do you really want to quit the game ?",
11277 REQ_ASK | REQ_STAY_CLOSED))
11279 #if defined(PLATFORM_UNIX)
11280 if (options.network)
11281 SendToServer_StopPlaying();
11285 game_status = GAME_MODE_MAIN;
11293 if (tape.playing && tape.deactivate_display)
11294 TapeDeactivateDisplayOff(TRUE);
11297 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11300 if (tape.playing && tape.deactivate_display)
11301 TapeDeactivateDisplayOn();
11308 /* ---------- new game button stuff ---------------------------------------- */
11310 /* graphic position values for game buttons */
11311 #define GAME_BUTTON_XSIZE 30
11312 #define GAME_BUTTON_YSIZE 30
11313 #define GAME_BUTTON_XPOS 5
11314 #define GAME_BUTTON_YPOS 215
11315 #define SOUND_BUTTON_XPOS 5
11316 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11318 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11319 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11320 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11321 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11322 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11323 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11330 } gamebutton_info[NUM_GAME_BUTTONS] =
11333 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11338 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11339 GAME_CTRL_ID_PAUSE,
11343 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11348 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11349 SOUND_CTRL_ID_MUSIC,
11350 "background music on/off"
11353 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11354 SOUND_CTRL_ID_LOOPS,
11355 "sound loops on/off"
11358 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11359 SOUND_CTRL_ID_SIMPLE,
11360 "normal sounds on/off"
11364 void CreateGameButtons()
11368 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11370 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11371 struct GadgetInfo *gi;
11374 unsigned long event_mask;
11375 int gd_xoffset, gd_yoffset;
11376 int gd_x1, gd_x2, gd_y1, gd_y2;
11379 gd_xoffset = gamebutton_info[i].x;
11380 gd_yoffset = gamebutton_info[i].y;
11381 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11382 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11384 if (id == GAME_CTRL_ID_STOP ||
11385 id == GAME_CTRL_ID_PAUSE ||
11386 id == GAME_CTRL_ID_PLAY)
11388 button_type = GD_TYPE_NORMAL_BUTTON;
11390 event_mask = GD_EVENT_RELEASED;
11391 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11392 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11396 button_type = GD_TYPE_CHECK_BUTTON;
11398 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11399 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11400 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11401 event_mask = GD_EVENT_PRESSED;
11402 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11403 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11406 gi = CreateGadget(GDI_CUSTOM_ID, id,
11407 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11408 GDI_X, DX + gd_xoffset,
11409 GDI_Y, DY + gd_yoffset,
11410 GDI_WIDTH, GAME_BUTTON_XSIZE,
11411 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11412 GDI_TYPE, button_type,
11413 GDI_STATE, GD_BUTTON_UNPRESSED,
11414 GDI_CHECKED, checked,
11415 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11416 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11417 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11418 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11419 GDI_EVENT_MASK, event_mask,
11420 GDI_CALLBACK_ACTION, HandleGameButtons,
11424 Error(ERR_EXIT, "cannot create gadget");
11426 game_gadget[id] = gi;
11430 void FreeGameButtons()
11434 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11435 FreeGadget(game_gadget[i]);
11438 static void MapGameButtons()
11442 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11443 MapGadget(game_gadget[i]);
11446 void UnmapGameButtons()
11450 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11451 UnmapGadget(game_gadget[i]);
11454 static void HandleGameButtons(struct GadgetInfo *gi)
11456 int id = gi->custom_id;
11458 if (game_status != GAME_MODE_PLAYING)
11463 case GAME_CTRL_ID_STOP:
11464 RequestQuitGame(TRUE);
11467 case GAME_CTRL_ID_PAUSE:
11468 if (options.network)
11470 #if defined(PLATFORM_UNIX)
11472 SendToServer_ContinuePlaying();
11474 SendToServer_PausePlaying();
11478 TapeTogglePause(TAPE_TOGGLE_MANUAL);
11481 case GAME_CTRL_ID_PLAY:
11484 #if defined(PLATFORM_UNIX)
11485 if (options.network)
11486 SendToServer_ContinuePlaying();
11490 tape.pausing = FALSE;
11491 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11496 case SOUND_CTRL_ID_MUSIC:
11497 if (setup.sound_music)
11499 setup.sound_music = FALSE;
11502 else if (audio.music_available)
11504 setup.sound = setup.sound_music = TRUE;
11506 SetAudioMode(setup.sound);
11512 case SOUND_CTRL_ID_LOOPS:
11513 if (setup.sound_loops)
11514 setup.sound_loops = FALSE;
11515 else if (audio.loops_available)
11517 setup.sound = setup.sound_loops = TRUE;
11518 SetAudioMode(setup.sound);
11522 case SOUND_CTRL_ID_SIMPLE:
11523 if (setup.sound_simple)
11524 setup.sound_simple = FALSE;
11525 else if (audio.sound_available)
11527 setup.sound = setup.sound_simple = TRUE;
11528 SetAudioMode(setup.sound);