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_NO_EXPLOSION 0
51 /* special positions in the game control window (relative to control window) */
54 #define XX_EMERALDS 29
55 #define YY_EMERALDS 54
56 #define XX_DYNAMITE 29
57 #define YY_DYNAMITE 89
66 /* special positions in the game control window (relative to main window) */
67 #define DX_LEVEL (DX + XX_LEVEL)
68 #define DY_LEVEL (DY + YY_LEVEL)
69 #define DX_EMERALDS (DX + XX_EMERALDS)
70 #define DY_EMERALDS (DY + YY_EMERALDS)
71 #define DX_DYNAMITE (DX + XX_DYNAMITE)
72 #define DY_DYNAMITE (DY + YY_DYNAMITE)
73 #define DX_KEYS (DX + XX_KEYS)
74 #define DY_KEYS (DY + YY_KEYS)
75 #define DX_SCORE (DX + XX_SCORE)
76 #define DY_SCORE (DY + YY_SCORE)
77 #define DX_TIME1 (DX + XX_TIME1)
78 #define DX_TIME2 (DX + XX_TIME2)
79 #define DY_TIME (DY + YY_TIME)
81 /* values for initial player move delay (initial delay counter value) */
82 #define INITIAL_MOVE_DELAY_OFF -1
83 #define INITIAL_MOVE_DELAY_ON 0
85 /* values for player movement speed (which is in fact a delay value) */
86 #define MOVE_DELAY_NORMAL_SPEED 8
87 #define MOVE_DELAY_HIGH_SPEED 4
89 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
90 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
91 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
92 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
94 /* values for other actions */
95 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
97 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
99 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
100 RND(element_info[e].push_delay_random))
101 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
102 RND(element_info[e].move_delay_random))
103 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
104 (element_info[e].move_delay_random))
106 #define GET_TARGET_ELEMENT(e, ch) \
107 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
108 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
110 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
111 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
114 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
115 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
116 (CAN_MOVE_INTO_ACID(e) && \
117 Feld[x][y] == EL_ACID) || \
120 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
121 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
122 (CAN_MOVE_INTO_ACID(e) && \
123 Feld[x][y] == EL_ACID) || \
126 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
127 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
129 (CAN_MOVE_INTO_ACID(e) && \
130 Feld[x][y] == EL_ACID) || \
131 (DONT_COLLIDE_WITH(e) && \
133 !PLAYER_ENEMY_PROTECTED(x, y))))
136 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
137 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
139 (DONT_COLLIDE_WITH(e) && \
141 !PLAYER_ENEMY_PROTECTED(x, y))))
144 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
145 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
148 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
149 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
151 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
152 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
156 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
159 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
160 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
164 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
165 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
167 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
168 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
170 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
171 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
173 #define PIG_CAN_ENTER_FIELD(e, x, y) \
174 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
176 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
177 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
178 IS_FOOD_PENGUIN(Feld[x][y])))
179 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
180 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
182 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
183 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
185 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
186 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
190 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
191 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
192 (CAN_MOVE_INTO_ACID(e) && \
193 Feld[x][y] == EL_ACID) || \
194 Feld[x][y] == EL_DIAMOND))
196 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
197 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
198 (CAN_MOVE_INTO_ACID(e) && \
199 Feld[x][y] == EL_ACID) || \
200 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
202 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
203 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
204 (CAN_MOVE_INTO_ACID(e) && \
205 Feld[x][y] == EL_ACID) || \
206 IS_AMOEBOID(Feld[x][y])))
208 #define PIG_CAN_ENTER_FIELD(e, x, y) \
209 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
210 (CAN_MOVE_INTO_ACID(e) && \
211 Feld[x][y] == EL_ACID) || \
212 IS_FOOD_PIG(Feld[x][y])))
214 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
215 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
216 (CAN_MOVE_INTO_ACID(e) && \
217 Feld[x][y] == EL_ACID) || \
218 IS_FOOD_PENGUIN(Feld[x][y]) || \
219 Feld[x][y] == EL_EXIT_OPEN))
221 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
222 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
223 (CAN_MOVE_INTO_ACID(e) && \
224 Feld[x][y] == EL_ACID)))
226 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
227 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
228 (CAN_MOVE_INTO_ACID(e) && \
229 Feld[x][y] == EL_ACID) || \
232 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
233 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
234 (CAN_MOVE_INTO_ACID(e) && \
235 Feld[x][y] == EL_ACID)))
239 #define GROUP_NR(e) ((e) - EL_GROUP_START)
240 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
241 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
242 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
244 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
245 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
248 #define CE_ENTER_FIELD_COND(e, x, y) \
249 (!IS_PLAYER(x, y) && \
250 (Feld[x][y] == EL_ACID || \
251 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
253 #define CE_ENTER_FIELD_COND(e, x, y) \
254 (!IS_PLAYER(x, y) && \
255 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
258 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
259 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
261 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
262 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
264 /* game button identifiers */
265 #define GAME_CTRL_ID_STOP 0
266 #define GAME_CTRL_ID_PAUSE 1
267 #define GAME_CTRL_ID_PLAY 2
268 #define SOUND_CTRL_ID_MUSIC 3
269 #define SOUND_CTRL_ID_LOOPS 4
270 #define SOUND_CTRL_ID_SIMPLE 5
272 #define NUM_GAME_BUTTONS 6
275 /* forward declaration for internal use */
277 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
278 static boolean MovePlayer(struct PlayerInfo *, int, int);
279 static void ScrollPlayer(struct PlayerInfo *, int);
280 static void ScrollScreen(struct PlayerInfo *, int);
282 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
284 static void InitBeltMovement(void);
285 static void CloseAllOpenTimegates(void);
286 static void CheckGravityMovement(struct PlayerInfo *);
287 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
288 static void KillHeroUnlessEnemyProtected(int, int);
289 static void KillHeroUnlessExplosionProtected(int, int);
291 static void TestIfPlayerTouchesCustomElement(int, int);
292 static void TestIfElementTouchesCustomElement(int, int);
293 static void TestIfElementHitsCustomElement(int, int, int);
295 static void TestIfElementSmashesCustomElement(int, int, int);
298 static void ChangeElement(int, int, int);
300 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
301 #define CheckTriggeredElementChange(x, y, e, ev) \
302 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
304 #define CheckTriggeredElementChangePlayer(x, y, e, ev, p, s) \
305 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
306 #define CheckTriggeredElementChangeSide(x, y, e, ev, s) \
307 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
308 #define CheckTriggeredElementChangePage(x, y, e, ev, p) \
309 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
312 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
313 #define CheckElementChange(x, y, e, te, ev) \
314 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
315 #define CheckElementChangePlayer(x, y, e, ev, p, s) \
316 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
317 #define CheckElementChangeSide(x, y, e, te, ev, s) \
318 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
319 #define CheckElementChangePage(x, y, e, te, ev, p) \
320 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
322 static void PlayLevelSound(int, int, int);
323 static void PlayLevelSoundNearest(int, int, int);
324 static void PlayLevelSoundAction(int, int, int);
325 static void PlayLevelSoundElementAction(int, int, int, int);
326 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
327 static void PlayLevelSoundActionIfLoop(int, int, int);
328 static void StopLevelSoundActionIfLoop(int, int, int);
329 static void PlayLevelMusic();
331 static void MapGameButtons();
332 static void HandleGameButtons(struct GadgetInfo *);
334 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
337 /* ------------------------------------------------------------------------- */
338 /* definition of elements that automatically change to other elements after */
339 /* a specified time, eventually calling a function when changing */
340 /* ------------------------------------------------------------------------- */
342 /* forward declaration for changer functions */
343 static void InitBuggyBase(int x, int y);
344 static void WarnBuggyBase(int x, int y);
346 static void InitTrap(int x, int y);
347 static void ActivateTrap(int x, int y);
348 static void ChangeActiveTrap(int x, int y);
350 static void InitRobotWheel(int x, int y);
351 static void RunRobotWheel(int x, int y);
352 static void StopRobotWheel(int x, int y);
354 static void InitTimegateWheel(int x, int y);
355 static void RunTimegateWheel(int x, int y);
357 struct ChangingElementInfo
362 void (*pre_change_function)(int x, int y);
363 void (*change_function)(int x, int y);
364 void (*post_change_function)(int x, int y);
367 static struct ChangingElementInfo change_delay_list[] =
418 EL_SWITCHGATE_OPENING,
426 EL_SWITCHGATE_CLOSING,
427 EL_SWITCHGATE_CLOSED,
459 EL_ACID_SPLASH_RIGHT,
468 EL_SP_BUGGY_BASE_ACTIVATING,
475 EL_SP_BUGGY_BASE_ACTIVATING,
476 EL_SP_BUGGY_BASE_ACTIVE,
483 EL_SP_BUGGY_BASE_ACTIVE,
507 EL_ROBOT_WHEEL_ACTIVE,
515 EL_TIMEGATE_SWITCH_ACTIVE,
536 int push_delay_fixed, push_delay_random;
541 { EL_BALLOON, 0, 0 },
543 { EL_SOKOBAN_OBJECT, 2, 0 },
544 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
545 { EL_SATELLITE, 2, 0 },
546 { EL_SP_DISK_YELLOW, 2, 0 },
548 { EL_UNDEFINED, 0, 0 },
556 move_stepsize_list[] =
558 { EL_AMOEBA_DROP, 2 },
559 { EL_AMOEBA_DROPPING, 2 },
560 { EL_QUICKSAND_FILLING, 1 },
561 { EL_QUICKSAND_EMPTYING, 1 },
562 { EL_MAGIC_WALL_FILLING, 2 },
563 { EL_BD_MAGIC_WALL_FILLING, 2 },
564 { EL_MAGIC_WALL_EMPTYING, 2 },
565 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
575 collect_count_list[] =
578 { EL_BD_DIAMOND, 1 },
579 { EL_EMERALD_YELLOW, 1 },
580 { EL_EMERALD_RED, 1 },
581 { EL_EMERALD_PURPLE, 1 },
583 { EL_SP_INFOTRON, 1 },
597 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
598 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
599 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
600 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
601 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
602 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
603 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
604 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
605 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
606 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
607 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
612 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
614 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
615 CH_EVENT_BIT(CE_DELAY))
616 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
617 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
618 IS_JUST_CHANGING(x, y))
620 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
623 void GetPlayerConfig()
625 if (!audio.sound_available)
626 setup.sound_simple = FALSE;
628 if (!audio.loops_available)
629 setup.sound_loops = FALSE;
631 if (!audio.music_available)
632 setup.sound_music = FALSE;
634 if (!video.fullscreen_available)
635 setup.fullscreen = FALSE;
637 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
639 SetAudioMode(setup.sound);
643 static int getBeltNrFromBeltElement(int element)
645 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
646 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
647 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
650 static int getBeltNrFromBeltActiveElement(int element)
652 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
653 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
654 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
657 static int getBeltNrFromBeltSwitchElement(int element)
659 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
660 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
661 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
664 static int getBeltDirNrFromBeltSwitchElement(int element)
666 static int belt_base_element[4] =
668 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
669 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
670 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
671 EL_CONVEYOR_BELT_4_SWITCH_LEFT
674 int belt_nr = getBeltNrFromBeltSwitchElement(element);
675 int belt_dir_nr = element - belt_base_element[belt_nr];
677 return (belt_dir_nr % 3);
680 static int getBeltDirFromBeltSwitchElement(int element)
682 static int belt_move_dir[3] =
689 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
691 return belt_move_dir[belt_dir_nr];
694 static void InitPlayerField(int x, int y, int element, boolean init_game)
696 if (element == EL_SP_MURPHY)
700 if (stored_player[0].present)
702 Feld[x][y] = EL_SP_MURPHY_CLONE;
708 stored_player[0].use_murphy_graphic = TRUE;
711 Feld[x][y] = EL_PLAYER_1;
717 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
718 int jx = player->jx, jy = player->jy;
720 player->present = TRUE;
722 player->block_last_field = (element == EL_SP_MURPHY ?
723 level.sp_block_last_field :
724 level.block_last_field);
726 if (!options.network || player->connected)
728 player->active = TRUE;
730 /* remove potentially duplicate players */
731 if (StorePlayer[jx][jy] == Feld[x][y])
732 StorePlayer[jx][jy] = 0;
734 StorePlayer[x][y] = Feld[x][y];
738 printf("Player %d activated.\n", player->element_nr);
739 printf("[Local player is %d and currently %s.]\n",
740 local_player->element_nr,
741 local_player->active ? "active" : "not active");
745 Feld[x][y] = EL_EMPTY;
746 player->jx = player->last_jx = x;
747 player->jy = player->last_jy = y;
751 static void InitField(int x, int y, boolean init_game)
753 int element = Feld[x][y];
762 InitPlayerField(x, y, element, init_game);
765 case EL_SOKOBAN_FIELD_PLAYER:
766 element = Feld[x][y] = EL_PLAYER_1;
767 InitField(x, y, init_game);
769 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
770 InitField(x, y, init_game);
773 case EL_SOKOBAN_FIELD_EMPTY:
774 local_player->sokobanfields_still_needed++;
778 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
779 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
780 else if (x > 0 && Feld[x-1][y] == EL_ACID)
781 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
782 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
783 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
784 else if (y > 0 && Feld[x][y-1] == EL_ACID)
785 Feld[x][y] = EL_ACID_POOL_BOTTOM;
786 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
787 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
795 case EL_SPACESHIP_RIGHT:
796 case EL_SPACESHIP_UP:
797 case EL_SPACESHIP_LEFT:
798 case EL_SPACESHIP_DOWN:
800 case EL_BD_BUTTERFLY_RIGHT:
801 case EL_BD_BUTTERFLY_UP:
802 case EL_BD_BUTTERFLY_LEFT:
803 case EL_BD_BUTTERFLY_DOWN:
804 case EL_BD_BUTTERFLY:
805 case EL_BD_FIREFLY_RIGHT:
806 case EL_BD_FIREFLY_UP:
807 case EL_BD_FIREFLY_LEFT:
808 case EL_BD_FIREFLY_DOWN:
810 case EL_PACMAN_RIGHT:
834 if (y == lev_fieldy - 1)
836 Feld[x][y] = EL_AMOEBA_GROWING;
837 Store[x][y] = EL_AMOEBA_WET;
841 case EL_DYNAMITE_ACTIVE:
842 case EL_SP_DISK_RED_ACTIVE:
843 case EL_DYNABOMB_PLAYER_1_ACTIVE:
844 case EL_DYNABOMB_PLAYER_2_ACTIVE:
845 case EL_DYNABOMB_PLAYER_3_ACTIVE:
846 case EL_DYNABOMB_PLAYER_4_ACTIVE:
851 local_player->lights_still_needed++;
855 local_player->friends_still_needed++;
860 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
865 Feld[x][y] = EL_EMPTY;
870 case EL_EM_KEY_1_FILE:
871 Feld[x][y] = EL_EM_KEY_1;
873 case EL_EM_KEY_2_FILE:
874 Feld[x][y] = EL_EM_KEY_2;
876 case EL_EM_KEY_3_FILE:
877 Feld[x][y] = EL_EM_KEY_3;
879 case EL_EM_KEY_4_FILE:
880 Feld[x][y] = EL_EM_KEY_4;
884 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
885 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
886 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
887 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
888 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
889 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
890 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
891 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
892 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
893 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
894 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
895 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
898 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
899 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
900 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
902 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
904 game.belt_dir[belt_nr] = belt_dir;
905 game.belt_dir_nr[belt_nr] = belt_dir_nr;
907 else /* more than one switch -- set it like the first switch */
909 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
914 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
916 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
919 case EL_LIGHT_SWITCH_ACTIVE:
921 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
925 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
927 else if (IS_GROUP_ELEMENT(element))
929 struct ElementGroupInfo *group = element_info[element].group;
930 int last_anim_random_frame = gfx.anim_random_frame;
933 if (group->choice_mode == ANIM_RANDOM)
934 gfx.anim_random_frame = RND(group->num_elements_resolved);
936 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
937 group->choice_mode, 0,
940 if (group->choice_mode == ANIM_RANDOM)
941 gfx.anim_random_frame = last_anim_random_frame;
945 Feld[x][y] = group->element_resolved[element_pos];
947 InitField(x, y, init_game);
953 static inline void InitField_WithBug1(int x, int y, boolean init_game)
955 InitField(x, y, init_game);
957 /* not needed to call InitMovDir() -- already done by InitField()! */
958 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
959 CAN_MOVE(Feld[x][y]))
963 static inline void InitField_WithBug2(int x, int y, boolean init_game)
965 int old_element = Feld[x][y];
967 InitField(x, y, init_game);
969 /* not needed to call InitMovDir() -- already done by InitField()! */
970 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
971 CAN_MOVE(old_element) &&
972 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
975 /* this case is in fact a combination of not less than three bugs:
976 first, it calls InitMovDir() for elements that can move, although this is
977 already done by InitField(); then, it checks the element that was at this
978 field _before_ the call to InitField() (which can change it)
983 inline void DrawGameValue_Emeralds(int value)
985 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
988 inline void DrawGameValue_Dynamite(int value)
990 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
993 inline void DrawGameValue_Keys(struct PlayerInfo *player)
997 for (i = 0; i < MAX_KEYS; i++)
999 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1000 el2edimg(EL_KEY_1 + i));
1003 inline void DrawGameValue_Score(int value)
1005 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1008 inline void DrawGameValue_Time(int value)
1011 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1013 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1016 inline void DrawGameValue_Level(int value)
1019 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1022 /* misuse area for displaying emeralds to draw bigger level number */
1023 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1024 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1026 /* now copy it to the area for displaying level number */
1027 BlitBitmap(drawto, drawto,
1028 DX_EMERALDS, DY_EMERALDS + 1,
1029 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1030 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1031 DX_LEVEL - 1, DY_LEVEL + 1);
1033 /* restore the area for displaying emeralds */
1034 DrawGameValue_Emeralds(local_player->gems_still_needed);
1036 /* yes, this is all really ugly :-) */
1040 void DrawGameDoorValues()
1044 DrawGameValue_Level(level_nr);
1046 for (i = 0; i < MAX_PLAYERS; i++)
1047 DrawGameValue_Keys(&stored_player[i]);
1049 DrawGameValue_Emeralds(local_player->gems_still_needed);
1050 DrawGameValue_Dynamite(local_player->inventory_size);
1051 DrawGameValue_Score(local_player->score);
1052 DrawGameValue_Time(TimeLeft);
1055 static void resolve_group_element(int group_element, int recursion_depth)
1057 static int group_nr;
1058 static struct ElementGroupInfo *group;
1059 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1062 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1064 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1065 group_element - EL_GROUP_START + 1);
1067 /* replace element which caused too deep recursion by question mark */
1068 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1073 if (recursion_depth == 0) /* initialization */
1075 group = element_info[group_element].group;
1076 group_nr = group_element - EL_GROUP_START;
1078 group->num_elements_resolved = 0;
1079 group->choice_pos = 0;
1082 for (i = 0; i < actual_group->num_elements; i++)
1084 int element = actual_group->element[i];
1086 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1089 if (IS_GROUP_ELEMENT(element))
1090 resolve_group_element(element, recursion_depth + 1);
1093 group->element_resolved[group->num_elements_resolved++] = element;
1094 element_info[element].in_group[group_nr] = TRUE;
1099 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1101 printf("::: group %d: %d resolved elements\n",
1102 group_element - EL_GROUP_START, group->num_elements_resolved);
1103 for (i = 0; i < group->num_elements_resolved; i++)
1104 printf("::: - %d ['%s']\n", group->element_resolved[i],
1105 element_info[group->element_resolved[i]].token_name);
1112 =============================================================================
1114 -----------------------------------------------------------------------------
1115 initialize game engine due to level / tape version number
1116 =============================================================================
1119 static void InitGameEngine()
1123 /* set game engine from tape file when re-playing, else from level file */
1124 game.engine_version = (tape.playing ? tape.engine_version :
1125 level.game_version);
1127 /* dynamically adjust element properties according to game engine version */
1128 InitElementPropertiesEngine(game.engine_version);
1131 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1132 printf(" tape version == %06d [%s] [file: %06d]\n",
1133 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1135 printf(" => game.engine_version == %06d\n", game.engine_version);
1138 /* ---------- recursively resolve group elements ------------------------- */
1140 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1141 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1142 element_info[i].in_group[j] = FALSE;
1144 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1145 resolve_group_element(EL_GROUP_START + i, 0);
1147 /* ---------- initialize player's initial move delay --------------------- */
1149 /* dynamically adjust player properties according to game engine version */
1150 game.initial_move_delay =
1151 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1152 INITIAL_MOVE_DELAY_OFF);
1154 /* dynamically adjust player properties according to level information */
1155 game.initial_move_delay_value =
1156 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1158 /* ---------- initialize player's initial push delay --------------------- */
1160 /* dynamically adjust player properties according to game engine version */
1161 game.initial_push_delay_value =
1162 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1164 /* ---------- initialize changing elements ------------------------------- */
1166 /* initialize changing elements information */
1167 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1169 struct ElementInfo *ei = &element_info[i];
1171 /* this pointer might have been changed in the level editor */
1172 ei->change = &ei->change_page[0];
1174 if (!IS_CUSTOM_ELEMENT(i))
1176 ei->change->target_element = EL_EMPTY_SPACE;
1177 ei->change->delay_fixed = 0;
1178 ei->change->delay_random = 0;
1179 ei->change->delay_frames = 1;
1182 ei->change_events = CE_BITMASK_DEFAULT;
1183 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1185 ei->event_page_nr[j] = 0;
1186 ei->event_page[j] = &ei->change_page[0];
1190 /* add changing elements from pre-defined list */
1191 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1193 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1194 struct ElementInfo *ei = &element_info[ch_delay->element];
1196 ei->change->target_element = ch_delay->target_element;
1197 ei->change->delay_fixed = ch_delay->change_delay;
1199 ei->change->pre_change_function = ch_delay->pre_change_function;
1200 ei->change->change_function = ch_delay->change_function;
1201 ei->change->post_change_function = ch_delay->post_change_function;
1203 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1206 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1211 /* add change events from custom element configuration */
1212 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1214 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1216 for (j = 0; j < ei->num_change_pages; j++)
1218 if (!ei->change_page[j].can_change)
1221 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1223 /* only add event page for the first page found with this event */
1224 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1225 !(ei->change_events & CH_EVENT_BIT(k)))
1227 ei->change_events |= CH_EVENT_BIT(k);
1228 ei->event_page_nr[k] = j;
1229 ei->event_page[k] = &ei->change_page[j];
1237 /* add change events from custom element configuration */
1238 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1240 int element = EL_CUSTOM_START + i;
1242 /* only add custom elements that change after fixed/random frame delay */
1243 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1244 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1248 /* ---------- initialize run-time trigger player and element ------------- */
1250 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1252 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1254 for (j = 0; j < ei->num_change_pages; j++)
1256 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1257 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1261 /* ---------- initialize trigger events ---------------------------------- */
1263 /* initialize trigger events information */
1264 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1265 trigger_events[i] = EP_BITMASK_DEFAULT;
1268 /* add trigger events from element change event properties */
1269 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1271 struct ElementInfo *ei = &element_info[i];
1273 for (j = 0; j < ei->num_change_pages; j++)
1275 if (!ei->change_page[j].can_change)
1278 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1280 int trigger_element = ei->change_page[j].trigger_element;
1282 if (IS_GROUP_ELEMENT(trigger_element))
1284 struct ElementGroupInfo *group = element_info[trigger_element].group;
1286 for (k = 0; k < group->num_elements_resolved; k++)
1287 trigger_events[group->element_resolved[k]]
1288 |= ei->change_page[j].events;
1291 trigger_events[trigger_element] |= ei->change_page[j].events;
1296 /* add trigger events from element change event properties */
1297 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1298 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1299 trigger_events[element_info[i].change->trigger_element] |=
1300 element_info[i].change->events;
1303 /* ---------- initialize push delay -------------------------------------- */
1305 /* initialize push delay values to default */
1306 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1308 if (!IS_CUSTOM_ELEMENT(i))
1310 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1311 element_info[i].push_delay_random = game.default_push_delay_random;
1315 /* set push delay value for certain elements from pre-defined list */
1316 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1318 int e = push_delay_list[i].element;
1320 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1321 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1324 /* set push delay value for Supaplex elements for newer engine versions */
1325 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1327 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1329 if (IS_SP_ELEMENT(i))
1331 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1332 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1337 /* ---------- initialize move stepsize ----------------------------------- */
1339 /* initialize move stepsize values to default */
1340 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1341 if (!IS_CUSTOM_ELEMENT(i))
1342 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1344 /* set move stepsize value for certain elements from pre-defined list */
1345 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1347 int e = move_stepsize_list[i].element;
1349 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1352 /* ---------- initialize move dig/leave ---------------------------------- */
1354 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1356 element_info[i].can_leave_element = FALSE;
1357 element_info[i].can_leave_element_last = FALSE;
1360 /* ---------- initialize gem count --------------------------------------- */
1362 /* initialize gem count values for each element */
1363 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1364 if (!IS_CUSTOM_ELEMENT(i))
1365 element_info[i].collect_count = 0;
1367 /* add gem count values for all elements from pre-defined list */
1368 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1369 element_info[collect_count_list[i].element].collect_count =
1370 collect_count_list[i].count;
1372 /* ---------- initialize access direction -------------------------------- */
1374 /* initialize access direction values to default */
1375 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1376 if (!IS_CUSTOM_ELEMENT(i))
1377 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1379 /* set access direction value for certain elements from pre-defined list */
1380 for (i = 0; tube_access[i].element != EL_UNDEFINED; i++)
1381 element_info[tube_access[i].element].access_direction =
1382 tube_access[i].direction;
1387 =============================================================================
1389 -----------------------------------------------------------------------------
1390 initialize and start new game
1391 =============================================================================
1396 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1397 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1398 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1405 #if USE_NEW_AMOEBA_CODE
1406 printf("Using new amoeba code.\n");
1408 printf("Using old amoeba code.\n");
1413 /* don't play tapes over network */
1414 network_playing = (options.network && !tape.playing);
1416 for (i = 0; i < MAX_PLAYERS; i++)
1418 struct PlayerInfo *player = &stored_player[i];
1420 player->index_nr = i;
1421 player->index_bit = (1 << i);
1422 player->element_nr = EL_PLAYER_1 + i;
1424 player->present = FALSE;
1425 player->active = FALSE;
1428 player->effective_action = 0;
1429 player->programmed_action = 0;
1432 player->gems_still_needed = level.gems_needed;
1433 player->sokobanfields_still_needed = 0;
1434 player->lights_still_needed = 0;
1435 player->friends_still_needed = 0;
1437 for (j = 0; j < MAX_KEYS; j++)
1438 player->key[j] = FALSE;
1440 player->dynabomb_count = 0;
1441 player->dynabomb_size = 1;
1442 player->dynabombs_left = 0;
1443 player->dynabomb_xl = FALSE;
1445 player->MovDir = MV_NO_MOVING;
1448 player->GfxDir = MV_NO_MOVING;
1449 player->GfxAction = ACTION_DEFAULT;
1451 player->StepFrame = 0;
1453 player->use_murphy_graphic = FALSE;
1455 player->block_last_field = FALSE;
1456 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1458 player->actual_frame_counter = 0;
1460 player->step_counter = 0;
1462 player->last_move_dir = MV_NO_MOVING;
1464 player->is_waiting = FALSE;
1465 player->is_moving = FALSE;
1466 player->is_digging = FALSE;
1467 player->is_snapping = FALSE;
1468 player->is_collecting = FALSE;
1469 player->is_pushing = FALSE;
1470 player->is_switching = FALSE;
1471 player->is_dropping = FALSE;
1473 player->is_bored = FALSE;
1474 player->is_sleeping = FALSE;
1476 player->frame_counter_bored = -1;
1477 player->frame_counter_sleeping = -1;
1479 player->anim_delay_counter = 0;
1480 player->post_delay_counter = 0;
1482 player->action_waiting = ACTION_DEFAULT;
1483 player->last_action_waiting = ACTION_DEFAULT;
1484 player->special_action_bored = ACTION_DEFAULT;
1485 player->special_action_sleeping = ACTION_DEFAULT;
1487 player->num_special_action_bored = 0;
1488 player->num_special_action_sleeping = 0;
1490 /* determine number of special actions for bored and sleeping animation */
1491 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1493 boolean found = FALSE;
1495 for (k = 0; k < NUM_DIRECTIONS; k++)
1496 if (el_act_dir2img(player->element_nr, j, k) !=
1497 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1501 player->num_special_action_bored++;
1505 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1507 boolean found = FALSE;
1509 for (k = 0; k < NUM_DIRECTIONS; k++)
1510 if (el_act_dir2img(player->element_nr, j, k) !=
1511 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1515 player->num_special_action_sleeping++;
1520 player->switch_x = -1;
1521 player->switch_y = -1;
1523 player->show_envelope = 0;
1525 player->move_delay = game.initial_move_delay;
1526 player->move_delay_value = game.initial_move_delay_value;
1528 player->move_delay_reset_counter = 0;
1530 player->push_delay = 0;
1531 player->push_delay_value = game.initial_push_delay_value;
1533 player->drop_delay = 0;
1535 player->last_jx = player->last_jy = 0;
1536 player->jx = player->jy = 0;
1538 player->shield_normal_time_left = 0;
1539 player->shield_deadly_time_left = 0;
1541 player->inventory_infinite_element = EL_UNDEFINED;
1542 player->inventory_size = 0;
1544 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1545 SnapField(player, 0, 0);
1547 player->LevelSolved = FALSE;
1548 player->GameOver = FALSE;
1551 network_player_action_received = FALSE;
1553 #if defined(PLATFORM_UNIX)
1554 /* initial null action */
1555 if (network_playing)
1556 SendToServer_MovePlayer(MV_NO_MOVING);
1564 TimeLeft = level.time;
1567 ScreenMovDir = MV_NO_MOVING;
1571 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1573 AllPlayersGone = FALSE;
1575 game.yamyam_content_nr = 0;
1576 game.magic_wall_active = FALSE;
1577 game.magic_wall_time_left = 0;
1578 game.light_time_left = 0;
1579 game.timegate_time_left = 0;
1580 game.switchgate_pos = 0;
1581 game.balloon_dir = MV_NO_MOVING;
1582 game.gravity = level.initial_gravity;
1583 game.explosions_delayed = TRUE;
1585 game.envelope_active = FALSE;
1587 for (i = 0; i < NUM_BELTS; i++)
1589 game.belt_dir[i] = MV_NO_MOVING;
1590 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1593 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1594 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1596 for (x = 0; x < lev_fieldx; x++)
1598 for (y = 0; y < lev_fieldy; y++)
1600 Feld[x][y] = level.field[x][y];
1601 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1602 ChangeDelay[x][y] = 0;
1603 ChangePage[x][y] = -1;
1604 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1606 WasJustMoving[x][y] = 0;
1607 WasJustFalling[x][y] = 0;
1609 Pushed[x][y] = FALSE;
1611 Changed[x][y] = CE_BITMASK_DEFAULT;
1612 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1614 ExplodePhase[x][y] = 0;
1615 ExplodeDelay[x][y] = 0;
1616 ExplodeField[x][y] = EX_NO_EXPLOSION;
1618 RunnerVisit[x][y] = 0;
1619 PlayerVisit[x][y] = 0;
1622 GfxRandom[x][y] = INIT_GFX_RANDOM();
1623 GfxElement[x][y] = EL_UNDEFINED;
1624 GfxAction[x][y] = ACTION_DEFAULT;
1625 GfxDir[x][y] = MV_NO_MOVING;
1629 for (y = 0; y < lev_fieldy; y++)
1631 for (x = 0; x < lev_fieldx; x++)
1633 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1635 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1637 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1640 InitField(x, y, TRUE);
1646 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1647 emulate_sb ? EMU_SOKOBAN :
1648 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1650 /* initialize explosion and ignition delay */
1651 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1653 if (!IS_CUSTOM_ELEMENT(i))
1656 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1657 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1658 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1659 int last_phase = (num_phase + 1) * delay;
1660 int half_phase = (num_phase / 2) * delay;
1662 element_info[i].explosion_delay = last_phase - 1;
1663 element_info[i].ignition_delay = half_phase;
1666 if (i == EL_BLACK_ORB)
1667 element_info[i].ignition_delay = 0;
1669 if (i == EL_BLACK_ORB)
1670 element_info[i].ignition_delay = 1;
1675 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1676 element_info[i].explosion_delay = 1;
1678 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1679 element_info[i].ignition_delay = 1;
1683 /* correct non-moving belts to start moving left */
1684 for (i = 0; i < NUM_BELTS; i++)
1685 if (game.belt_dir[i] == MV_NO_MOVING)
1686 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1688 /* check if any connected player was not found in playfield */
1689 for (i = 0; i < MAX_PLAYERS; i++)
1691 struct PlayerInfo *player = &stored_player[i];
1693 if (player->connected && !player->present)
1695 for (j = 0; j < MAX_PLAYERS; j++)
1697 struct PlayerInfo *some_player = &stored_player[j];
1698 int jx = some_player->jx, jy = some_player->jy;
1700 /* assign first free player found that is present in the playfield */
1701 if (some_player->present && !some_player->connected)
1703 player->present = TRUE;
1704 player->active = TRUE;
1706 some_player->present = FALSE;
1707 some_player->active = FALSE;
1710 player->element_nr = some_player->element_nr;
1713 StorePlayer[jx][jy] = player->element_nr;
1714 player->jx = player->last_jx = jx;
1715 player->jy = player->last_jy = jy;
1725 /* when playing a tape, eliminate all players which do not participate */
1727 for (i = 0; i < MAX_PLAYERS; i++)
1729 if (stored_player[i].active && !tape.player_participates[i])
1731 struct PlayerInfo *player = &stored_player[i];
1732 int jx = player->jx, jy = player->jy;
1734 player->active = FALSE;
1735 StorePlayer[jx][jy] = 0;
1736 Feld[jx][jy] = EL_EMPTY;
1740 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1742 /* when in single player mode, eliminate all but the first active player */
1744 for (i = 0; i < MAX_PLAYERS; i++)
1746 if (stored_player[i].active)
1748 for (j = i + 1; j < MAX_PLAYERS; j++)
1750 if (stored_player[j].active)
1752 struct PlayerInfo *player = &stored_player[j];
1753 int jx = player->jx, jy = player->jy;
1755 player->active = FALSE;
1756 player->present = FALSE;
1758 StorePlayer[jx][jy] = 0;
1759 Feld[jx][jy] = EL_EMPTY;
1766 /* when recording the game, store which players take part in the game */
1769 for (i = 0; i < MAX_PLAYERS; i++)
1770 if (stored_player[i].active)
1771 tape.player_participates[i] = TRUE;
1776 for (i = 0; i < MAX_PLAYERS; i++)
1778 struct PlayerInfo *player = &stored_player[i];
1780 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1785 if (local_player == player)
1786 printf("Player %d is local player.\n", i+1);
1790 if (BorderElement == EL_EMPTY)
1793 SBX_Right = lev_fieldx - SCR_FIELDX;
1795 SBY_Lower = lev_fieldy - SCR_FIELDY;
1800 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1802 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1805 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1806 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1808 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1809 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1811 /* if local player not found, look for custom element that might create
1812 the player (make some assumptions about the right custom element) */
1813 if (!local_player->present)
1815 int start_x = 0, start_y = 0;
1816 int found_rating = 0;
1817 int found_element = EL_UNDEFINED;
1819 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1821 int element = Feld[x][y];
1826 if (!IS_CUSTOM_ELEMENT(element))
1829 if (CAN_CHANGE(element))
1831 for (i = 0; i < element_info[element].num_change_pages; i++)
1833 content = element_info[element].change_page[i].target_element;
1834 is_player = ELEM_IS_PLAYER(content);
1836 if (is_player && (found_rating < 3 || element < found_element))
1842 found_element = element;
1847 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1849 content = element_info[element].content[xx][yy];
1850 is_player = ELEM_IS_PLAYER(content);
1852 if (is_player && (found_rating < 2 || element < found_element))
1854 start_x = x + xx - 1;
1855 start_y = y + yy - 1;
1858 found_element = element;
1861 if (!CAN_CHANGE(element))
1864 for (i = 0; i < element_info[element].num_change_pages; i++)
1866 content= element_info[element].change_page[i].target_content[xx][yy];
1867 is_player = ELEM_IS_PLAYER(content);
1869 if (is_player && (found_rating < 1 || element < found_element))
1871 start_x = x + xx - 1;
1872 start_y = y + yy - 1;
1875 found_element = element;
1881 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1882 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1885 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1886 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1892 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1893 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1894 local_player->jx - MIDPOSX);
1896 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1897 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1898 local_player->jy - MIDPOSY);
1900 scroll_x = SBX_Left;
1901 scroll_y = SBY_Upper;
1902 if (local_player->jx >= SBX_Left + MIDPOSX)
1903 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1904 local_player->jx - MIDPOSX :
1906 if (local_player->jy >= SBY_Upper + MIDPOSY)
1907 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1908 local_player->jy - MIDPOSY :
1913 CloseDoor(DOOR_CLOSE_1);
1918 /* after drawing the level, correct some elements */
1919 if (game.timegate_time_left == 0)
1920 CloseAllOpenTimegates();
1922 if (setup.soft_scrolling)
1923 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1925 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1928 /* copy default game door content to main double buffer */
1929 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1930 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1932 DrawGameDoorValues();
1936 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1937 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1938 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1942 /* copy actual game door content to door double buffer for OpenDoor() */
1943 BlitBitmap(drawto, bitmap_db_door,
1944 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1946 OpenDoor(DOOR_OPEN_ALL);
1948 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1950 if (setup.sound_music)
1953 KeyboardAutoRepeatOffUnlessAutoplay();
1957 for (i = 0; i < MAX_PLAYERS; i++)
1958 printf("Player %d %sactive.\n",
1959 i + 1, (stored_player[i].active ? "" : "not "));
1963 printf("::: starting game [%d]\n", FrameCounter);
1967 void InitMovDir(int x, int y)
1969 int i, element = Feld[x][y];
1970 static int xy[4][2] =
1977 static int direction[3][4] =
1979 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1980 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1981 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1990 Feld[x][y] = EL_BUG;
1991 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1994 case EL_SPACESHIP_RIGHT:
1995 case EL_SPACESHIP_UP:
1996 case EL_SPACESHIP_LEFT:
1997 case EL_SPACESHIP_DOWN:
1998 Feld[x][y] = EL_SPACESHIP;
1999 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2002 case EL_BD_BUTTERFLY_RIGHT:
2003 case EL_BD_BUTTERFLY_UP:
2004 case EL_BD_BUTTERFLY_LEFT:
2005 case EL_BD_BUTTERFLY_DOWN:
2006 Feld[x][y] = EL_BD_BUTTERFLY;
2007 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2010 case EL_BD_FIREFLY_RIGHT:
2011 case EL_BD_FIREFLY_UP:
2012 case EL_BD_FIREFLY_LEFT:
2013 case EL_BD_FIREFLY_DOWN:
2014 Feld[x][y] = EL_BD_FIREFLY;
2015 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2018 case EL_PACMAN_RIGHT:
2020 case EL_PACMAN_LEFT:
2021 case EL_PACMAN_DOWN:
2022 Feld[x][y] = EL_PACMAN;
2023 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2026 case EL_SP_SNIKSNAK:
2027 MovDir[x][y] = MV_UP;
2030 case EL_SP_ELECTRON:
2031 MovDir[x][y] = MV_LEFT;
2038 Feld[x][y] = EL_MOLE;
2039 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2043 if (IS_CUSTOM_ELEMENT(element))
2045 struct ElementInfo *ei = &element_info[element];
2046 int move_direction_initial = ei->move_direction_initial;
2047 int move_pattern = ei->move_pattern;
2049 if (move_direction_initial == MV_START_PREVIOUS)
2051 if (MovDir[x][y] != MV_NO_MOVING)
2054 move_direction_initial = MV_START_AUTOMATIC;
2057 if (move_direction_initial == MV_START_RANDOM)
2058 MovDir[x][y] = 1 << RND(4);
2059 else if (move_direction_initial & MV_ANY_DIRECTION)
2060 MovDir[x][y] = move_direction_initial;
2061 else if (move_pattern == MV_ALL_DIRECTIONS ||
2062 move_pattern == MV_TURNING_LEFT ||
2063 move_pattern == MV_TURNING_RIGHT ||
2064 move_pattern == MV_TURNING_LEFT_RIGHT ||
2065 move_pattern == MV_TURNING_RIGHT_LEFT ||
2066 move_pattern == MV_TURNING_RANDOM)
2067 MovDir[x][y] = 1 << RND(4);
2068 else if (move_pattern == MV_HORIZONTAL)
2069 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2070 else if (move_pattern == MV_VERTICAL)
2071 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2072 else if (move_pattern & MV_ANY_DIRECTION)
2073 MovDir[x][y] = element_info[element].move_pattern;
2074 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2075 move_pattern == MV_ALONG_RIGHT_SIDE)
2077 for (i = 0; i < NUM_DIRECTIONS; i++)
2079 int x1 = x + xy[i][0];
2080 int y1 = y + xy[i][1];
2082 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2084 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2085 MovDir[x][y] = direction[0][i];
2087 MovDir[x][y] = direction[1][i];
2096 MovDir[x][y] = 1 << RND(4);
2098 if (element != EL_BUG &&
2099 element != EL_SPACESHIP &&
2100 element != EL_BD_BUTTERFLY &&
2101 element != EL_BD_FIREFLY)
2104 for (i = 0; i < NUM_DIRECTIONS; i++)
2106 int x1 = x + xy[i][0];
2107 int y1 = y + xy[i][1];
2109 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2111 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2113 MovDir[x][y] = direction[0][i];
2116 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2117 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2119 MovDir[x][y] = direction[1][i];
2128 GfxDir[x][y] = MovDir[x][y];
2131 void InitAmoebaNr(int x, int y)
2134 int group_nr = AmoebeNachbarNr(x, y);
2138 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2140 if (AmoebaCnt[i] == 0)
2148 AmoebaNr[x][y] = group_nr;
2149 AmoebaCnt[group_nr]++;
2150 AmoebaCnt2[group_nr]++;
2156 boolean raise_level = FALSE;
2158 if (local_player->MovPos)
2162 if (tape.auto_play) /* tape might already be stopped here */
2163 tape.auto_play_level_solved = TRUE;
2165 if (tape.playing && tape.auto_play)
2166 tape.auto_play_level_solved = TRUE;
2169 local_player->LevelSolved = FALSE;
2171 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2175 if (!tape.playing && setup.sound_loops)
2176 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2177 SND_CTRL_PLAY_LOOP);
2179 while (TimeLeft > 0)
2181 if (!tape.playing && !setup.sound_loops)
2182 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2183 if (TimeLeft > 0 && !(TimeLeft % 10))
2184 RaiseScore(level.score[SC_TIME_BONUS]);
2185 if (TimeLeft > 100 && !(TimeLeft % 10))
2190 DrawGameValue_Time(TimeLeft);
2198 if (!tape.playing && setup.sound_loops)
2199 StopSound(SND_GAME_LEVELTIME_BONUS);
2201 else if (level.time == 0) /* level without time limit */
2203 if (!tape.playing && setup.sound_loops)
2204 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2205 SND_CTRL_PLAY_LOOP);
2207 while (TimePlayed < 999)
2209 if (!tape.playing && !setup.sound_loops)
2210 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2211 if (TimePlayed < 999 && !(TimePlayed % 10))
2212 RaiseScore(level.score[SC_TIME_BONUS]);
2213 if (TimePlayed < 900 && !(TimePlayed % 10))
2218 DrawGameValue_Time(TimePlayed);
2226 if (!tape.playing && setup.sound_loops)
2227 StopSound(SND_GAME_LEVELTIME_BONUS);
2230 /* close exit door after last player */
2231 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2232 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2234 int element = Feld[ExitX][ExitY];
2236 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2237 EL_SP_EXIT_CLOSING);
2239 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2242 /* Hero disappears */
2243 DrawLevelField(ExitX, ExitY);
2249 CloseDoor(DOOR_CLOSE_1);
2254 SaveTape(tape.level_nr); /* Ask to save tape */
2257 if (level_nr == leveldir_current->handicap_level)
2259 leveldir_current->handicap_level++;
2260 SaveLevelSetup_SeriesInfo();
2263 if (level_editor_test_game)
2264 local_player->score = -1; /* no highscore when playing from editor */
2265 else if (level_nr < leveldir_current->last_level)
2266 raise_level = TRUE; /* advance to next level */
2268 if ((hi_pos = NewHiScore()) >= 0)
2270 game_status = GAME_MODE_SCORES;
2271 DrawHallOfFame(hi_pos);
2280 game_status = GAME_MODE_MAIN;
2297 LoadScore(level_nr);
2299 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2300 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2303 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2305 if (local_player->score > highscore[k].Score)
2307 /* player has made it to the hall of fame */
2309 if (k < MAX_SCORE_ENTRIES - 1)
2311 int m = MAX_SCORE_ENTRIES - 1;
2314 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2315 if (!strcmp(setup.player_name, highscore[l].Name))
2317 if (m == k) /* player's new highscore overwrites his old one */
2321 for (l = m; l > k; l--)
2323 strcpy(highscore[l].Name, highscore[l - 1].Name);
2324 highscore[l].Score = highscore[l - 1].Score;
2331 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2332 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2333 highscore[k].Score = local_player->score;
2339 else if (!strncmp(setup.player_name, highscore[k].Name,
2340 MAX_PLAYER_NAME_LEN))
2341 break; /* player already there with a higher score */
2347 SaveScore(level_nr);
2352 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2354 if (player->GfxAction != action || player->GfxDir != dir)
2357 printf("Player frame reset! (%d => %d, %d => %d)\n",
2358 player->GfxAction, action, player->GfxDir, dir);
2361 player->GfxAction = action;
2362 player->GfxDir = dir;
2364 player->StepFrame = 0;
2368 static void ResetRandomAnimationValue(int x, int y)
2370 GfxRandom[x][y] = INIT_GFX_RANDOM();
2373 static void ResetGfxAnimation(int x, int y)
2376 GfxAction[x][y] = ACTION_DEFAULT;
2377 GfxDir[x][y] = MovDir[x][y];
2380 void InitMovingField(int x, int y, int direction)
2382 int element = Feld[x][y];
2383 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2384 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2388 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2389 ResetGfxAnimation(x, y);
2391 MovDir[newx][newy] = MovDir[x][y] = direction;
2392 GfxDir[x][y] = direction;
2394 if (Feld[newx][newy] == EL_EMPTY)
2395 Feld[newx][newy] = EL_BLOCKED;
2397 if (direction == MV_DOWN && CAN_FALL(element))
2398 GfxAction[x][y] = ACTION_FALLING;
2400 GfxAction[x][y] = ACTION_MOVING;
2402 GfxFrame[newx][newy] = GfxFrame[x][y];
2403 GfxRandom[newx][newy] = GfxRandom[x][y];
2404 GfxAction[newx][newy] = GfxAction[x][y];
2405 GfxDir[newx][newy] = GfxDir[x][y];
2408 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2410 int direction = MovDir[x][y];
2411 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2412 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2418 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2420 int oldx = x, oldy = y;
2421 int direction = MovDir[x][y];
2423 if (direction == MV_LEFT)
2425 else if (direction == MV_RIGHT)
2427 else if (direction == MV_UP)
2429 else if (direction == MV_DOWN)
2432 *comes_from_x = oldx;
2433 *comes_from_y = oldy;
2436 int MovingOrBlocked2Element(int x, int y)
2438 int element = Feld[x][y];
2440 if (element == EL_BLOCKED)
2444 Blocked2Moving(x, y, &oldx, &oldy);
2445 return Feld[oldx][oldy];
2451 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2453 /* like MovingOrBlocked2Element(), but if element is moving
2454 and (x,y) is the field the moving element is just leaving,
2455 return EL_BLOCKED instead of the element value */
2456 int element = Feld[x][y];
2458 if (IS_MOVING(x, y))
2460 if (element == EL_BLOCKED)
2464 Blocked2Moving(x, y, &oldx, &oldy);
2465 return Feld[oldx][oldy];
2474 static void RemoveField(int x, int y)
2476 Feld[x][y] = EL_EMPTY;
2483 ChangeDelay[x][y] = 0;
2484 ChangePage[x][y] = -1;
2485 Pushed[x][y] = FALSE;
2487 GfxElement[x][y] = EL_UNDEFINED;
2488 GfxAction[x][y] = ACTION_DEFAULT;
2489 GfxDir[x][y] = MV_NO_MOVING;
2492 void RemoveMovingField(int x, int y)
2494 int oldx = x, oldy = y, newx = x, newy = y;
2495 int element = Feld[x][y];
2496 int next_element = EL_UNDEFINED;
2498 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2501 if (IS_MOVING(x, y))
2503 Moving2Blocked(x, y, &newx, &newy);
2505 if (Feld[newx][newy] != EL_BLOCKED)
2508 if (Feld[newx][newy] != EL_BLOCKED)
2510 /* element is moving, but target field is not free (blocked), but
2511 already occupied by something different (example: acid pool);
2512 in this case, only remove the moving field, but not the target */
2514 RemoveField(oldx, oldy);
2516 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2518 DrawLevelField(oldx, oldy);
2524 else if (element == EL_BLOCKED)
2526 Blocked2Moving(x, y, &oldx, &oldy);
2527 if (!IS_MOVING(oldx, oldy))
2531 if (element == EL_BLOCKED &&
2532 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2533 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2534 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2535 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2536 next_element = get_next_element(Feld[oldx][oldy]);
2538 RemoveField(oldx, oldy);
2539 RemoveField(newx, newy);
2541 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2543 if (next_element != EL_UNDEFINED)
2544 Feld[oldx][oldy] = next_element;
2546 DrawLevelField(oldx, oldy);
2547 DrawLevelField(newx, newy);
2550 void DrawDynamite(int x, int y)
2552 int sx = SCREENX(x), sy = SCREENY(y);
2553 int graphic = el2img(Feld[x][y]);
2556 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2559 if (IS_WALKABLE_INSIDE(Back[x][y]))
2563 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2564 else if (Store[x][y])
2565 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2567 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2570 if (Back[x][y] || Store[x][y])
2571 DrawGraphicThruMask(sx, sy, graphic, frame);
2573 DrawGraphic(sx, sy, graphic, frame);
2575 if (game.emulation == EMU_SUPAPLEX)
2576 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2577 else if (Store[x][y])
2578 DrawGraphicThruMask(sx, sy, graphic, frame);
2580 DrawGraphic(sx, sy, graphic, frame);
2584 void CheckDynamite(int x, int y)
2586 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2590 if (MovDelay[x][y] != 0)
2593 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2600 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2602 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2603 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2604 StopSound(SND_DYNAMITE_ACTIVE);
2606 StopSound(SND_DYNABOMB_ACTIVE);
2612 void RelocatePlayer(int x, int y, int element_raw)
2614 int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
2615 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2616 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2617 boolean no_delay = (tape.index_search);
2618 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2619 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2622 if (player->GameOver) /* do not reanimate dead player */
2625 RemoveField(x, y); /* temporarily remove newly placed player */
2626 DrawLevelField(x, y);
2628 if (player->present)
2630 while (player->MovPos)
2632 ScrollPlayer(player, SCROLL_GO_ON);
2633 ScrollScreen(NULL, SCROLL_GO_ON);
2639 Delay(wait_delay_value);
2642 DrawPlayer(player); /* needed here only to cleanup last field */
2643 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2645 player->is_moving = FALSE;
2648 old_jx = player->jx;
2649 old_jy = player->jy;
2651 Feld[x][y] = element;
2652 InitPlayerField(x, y, element, TRUE);
2654 if (player != local_player) /* do not visually relocate other players */
2657 if (level.instant_relocation)
2660 int offset = (setup.scroll_delay ? 3 : 0);
2661 int jx = local_player->jx;
2662 int jy = local_player->jy;
2664 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2666 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2667 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2668 local_player->jx - MIDPOSX);
2670 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2671 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2672 local_player->jy - MIDPOSY);
2676 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2677 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2678 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2680 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2681 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2682 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2684 /* don't scroll over playfield boundaries */
2685 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2686 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2688 /* don't scroll over playfield boundaries */
2689 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2690 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2693 scroll_x += (local_player->jx - old_jx);
2694 scroll_y += (local_player->jy - old_jy);
2696 /* don't scroll over playfield boundaries */
2697 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2698 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2700 /* don't scroll over playfield boundaries */
2701 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2702 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2705 RedrawPlayfield(TRUE, 0,0,0,0);
2711 int offset = (setup.scroll_delay ? 3 : 0);
2712 int jx = local_player->jx;
2713 int jy = local_player->jy;
2715 int scroll_xx = -999, scroll_yy = -999;
2717 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2719 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2722 int fx = FX, fy = FY;
2724 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2725 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2726 local_player->jx - MIDPOSX);
2728 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2729 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2730 local_player->jy - MIDPOSY);
2732 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2733 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2736 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2739 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2746 fx += dx * TILEX / 2;
2747 fy += dy * TILEY / 2;
2749 ScrollLevel(dx, dy);
2752 /* scroll in two steps of half tile size to make things smoother */
2753 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2755 Delay(wait_delay_value);
2757 /* scroll second step to align at full tile size */
2759 Delay(wait_delay_value);
2762 int scroll_xx = -999, scroll_yy = -999;
2764 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2766 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2769 int fx = FX, fy = FY;
2771 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2772 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2773 local_player->jx - MIDPOSX);
2775 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2776 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2777 local_player->jy - MIDPOSY);
2779 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2780 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2783 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2786 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2793 fx += dx * TILEX / 2;
2794 fy += dy * TILEY / 2;
2796 ScrollLevel(dx, dy);
2799 /* scroll in two steps of half tile size to make things smoother */
2800 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2802 Delay(wait_delay_value);
2804 /* scroll second step to align at full tile size */
2806 Delay(wait_delay_value);
2812 void Explode(int ex, int ey, int phase, int mode)
2819 /* !!! eliminate this variable !!! */
2820 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2825 int last_phase = num_phase * delay;
2826 int half_phase = (num_phase / 2) * delay;
2827 int first_phase_after_start = EX_PHASE_START + 1;
2831 if (game.explosions_delayed)
2833 ExplodeField[ex][ey] = mode;
2837 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2839 int center_element = Feld[ex][ey];
2842 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
2846 /* --- This is only really needed (and now handled) in "Impact()". --- */
2847 /* do not explode moving elements that left the explode field in time */
2848 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2849 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2853 if (mode == EX_NORMAL || mode == EX_CENTER)
2854 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2856 /* remove things displayed in background while burning dynamite */
2857 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2860 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2862 /* put moving element to center field (and let it explode there) */
2863 center_element = MovingOrBlocked2Element(ex, ey);
2864 RemoveMovingField(ex, ey);
2865 Feld[ex][ey] = center_element;
2871 last_phase = element_info[center_element].explosion_delay + 1;
2873 last_phase = element_info[center_element].explosion_delay;
2877 printf("::: %d -> %d\n", center_element, last_phase);
2881 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2883 int xx = x - ex + 1;
2884 int yy = y - ey + 1;
2888 if (!IN_LEV_FIELD(x, y) || (mode != EX_NORMAL && (x != ex || y != ey)))
2891 if (!IN_LEV_FIELD(x, y) ||
2892 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2893 (x != ex || y != ey)))
2897 element = Feld[x][y];
2899 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2901 element = MovingOrBlocked2Element(x, y);
2903 if (!IS_EXPLOSION_PROOF(element))
2904 RemoveMovingField(x, y);
2910 if (IS_EXPLOSION_PROOF(element))
2913 /* indestructible elements can only explode in center (but not flames) */
2914 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2915 element == EL_FLAMES)
2920 if ((IS_INDESTRUCTIBLE(element) &&
2921 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2922 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2923 element == EL_FLAMES)
2927 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2929 if (IS_ACTIVE_BOMB(element))
2931 /* re-activate things under the bomb like gate or penguin */
2932 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2939 /* save walkable background elements while explosion on same tile */
2941 if (IS_INDESTRUCTIBLE(element))
2942 Back[x][y] = element;
2944 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2945 Back[x][y] = element;
2948 /* ignite explodable elements reached by other explosion */
2949 if (element == EL_EXPLOSION)
2950 element = Store2[x][y];
2953 if (AmoebaNr[x][y] &&
2954 (element == EL_AMOEBA_FULL ||
2955 element == EL_BD_AMOEBA ||
2956 element == EL_AMOEBA_GROWING))
2958 AmoebaCnt[AmoebaNr[x][y]]--;
2959 AmoebaCnt2[AmoebaNr[x][y]]--;
2965 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2967 switch(StorePlayer[ex][ey])
2970 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2973 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2976 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2980 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2985 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
2986 Store[x][y] = EL_EMPTY;
2988 if (game.emulation == EMU_SUPAPLEX)
2989 Store[x][y] = EL_EMPTY;
2992 else if (center_element == EL_MOLE)
2993 Store[x][y] = EL_EMERALD_RED;
2994 else if (center_element == EL_PENGUIN)
2995 Store[x][y] = EL_EMERALD_PURPLE;
2996 else if (center_element == EL_BUG)
2997 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2998 else if (center_element == EL_BD_BUTTERFLY)
2999 Store[x][y] = EL_BD_DIAMOND;
3000 else if (center_element == EL_SP_ELECTRON)
3001 Store[x][y] = EL_SP_INFOTRON;
3002 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3003 Store[x][y] = level.amoeba_content;
3004 else if (center_element == EL_YAMYAM)
3005 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3006 else if (IS_CUSTOM_ELEMENT(center_element) &&
3007 element_info[center_element].content[xx][yy] != EL_EMPTY)
3008 Store[x][y] = element_info[center_element].content[xx][yy];
3009 else if (element == EL_WALL_EMERALD)
3010 Store[x][y] = EL_EMERALD;
3011 else if (element == EL_WALL_DIAMOND)
3012 Store[x][y] = EL_DIAMOND;
3013 else if (element == EL_WALL_BD_DIAMOND)
3014 Store[x][y] = EL_BD_DIAMOND;
3015 else if (element == EL_WALL_EMERALD_YELLOW)
3016 Store[x][y] = EL_EMERALD_YELLOW;
3017 else if (element == EL_WALL_EMERALD_RED)
3018 Store[x][y] = EL_EMERALD_RED;
3019 else if (element == EL_WALL_EMERALD_PURPLE)
3020 Store[x][y] = EL_EMERALD_PURPLE;
3021 else if (element == EL_WALL_PEARL)
3022 Store[x][y] = EL_PEARL;
3023 else if (element == EL_WALL_CRYSTAL)
3024 Store[x][y] = EL_CRYSTAL;
3025 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3026 Store[x][y] = element_info[element].content[1][1];
3028 Store[x][y] = EL_EMPTY;
3030 if (x != ex || y != ey ||
3031 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
3032 Store2[x][y] = element;
3035 if (AmoebaNr[x][y] &&
3036 (element == EL_AMOEBA_FULL ||
3037 element == EL_BD_AMOEBA ||
3038 element == EL_AMOEBA_GROWING))
3040 AmoebaCnt[AmoebaNr[x][y]]--;
3041 AmoebaCnt2[AmoebaNr[x][y]]--;
3047 MovDir[x][y] = MovPos[x][y] = 0;
3048 GfxDir[x][y] = MovDir[x][y];
3053 Feld[x][y] = EL_EXPLOSION;
3055 GfxElement[x][y] = center_element;
3057 GfxElement[x][y] = EL_UNDEFINED;
3060 ExplodePhase[x][y] = 1;
3062 ExplodeDelay[x][y] = last_phase;
3067 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3069 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3076 if (center_element == EL_YAMYAM)
3077 game.yamyam_content_nr =
3078 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3091 GfxFrame[x][y] = 0; /* restart explosion animation */
3095 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3099 last_phase = ExplodeDelay[x][y];
3102 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3106 /* activate this even in non-DEBUG version until cause for crash in
3107 getGraphicAnimationFrame() (see below) is found and eliminated */
3111 if (GfxElement[x][y] == EL_UNDEFINED)
3114 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3115 printf("Explode(): This should never happen!\n");
3118 GfxElement[x][y] = EL_EMPTY;
3124 border_element = Store2[x][y];
3125 if (IS_PLAYER(x, y))
3126 border_element = StorePlayer[x][y];
3129 printf("::: phase == %d\n", phase);
3132 if (phase == element_info[border_element].ignition_delay ||
3133 phase == last_phase)
3135 boolean border_explosion = FALSE;
3138 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3140 if (IS_PLAYER(x, y))
3143 KillHeroUnlessExplosionProtected(x, y);
3144 border_explosion = TRUE;
3147 if (phase == last_phase)
3148 printf("::: IS_PLAYER\n");
3151 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3153 Feld[x][y] = Store2[x][y];
3156 border_explosion = TRUE;
3159 if (phase == last_phase)
3160 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3163 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3165 AmoebeUmwandeln(x, y);
3167 border_explosion = TRUE;
3170 if (phase == last_phase)
3171 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3172 element_info[border_element].explosion_delay,
3173 element_info[border_element].ignition_delay,
3179 /* if an element just explodes due to another explosion (chain-reaction),
3180 do not immediately end the new explosion when it was the last frame of
3181 the explosion (as it would be done in the following "if"-statement!) */
3182 if (border_explosion && phase == last_phase)
3189 if (phase == first_phase_after_start)
3191 int element = Store2[x][y];
3193 if (element == EL_BLACK_ORB)
3195 Feld[x][y] = Store2[x][y];
3200 else if (phase == half_phase)
3202 int element = Store2[x][y];
3204 if (IS_PLAYER(x, y))
3205 KillHeroUnlessExplosionProtected(x, y);
3206 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3208 Feld[x][y] = Store2[x][y];
3212 else if (element == EL_AMOEBA_TO_DIAMOND)
3213 AmoebeUmwandeln(x, y);
3217 if (phase == last_phase)
3222 printf("::: done: phase == %d\n", phase);
3226 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3229 element = Feld[x][y] = Store[x][y];
3230 Store[x][y] = Store2[x][y] = 0;
3231 GfxElement[x][y] = EL_UNDEFINED;
3233 /* player can escape from explosions and might therefore be still alive */
3234 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3235 element <= EL_PLAYER_IS_EXPLODING_4)
3236 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3238 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3239 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3240 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3243 /* restore probably existing indestructible background element */
3244 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3245 element = Feld[x][y] = Back[x][y];
3248 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3249 GfxDir[x][y] = MV_NO_MOVING;
3250 ChangeDelay[x][y] = 0;
3251 ChangePage[x][y] = -1;
3254 InitField_WithBug2(x, y, FALSE);
3256 InitField(x, y, FALSE);
3258 /* !!! not needed !!! */
3260 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3261 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3264 if (CAN_MOVE(element))
3269 DrawLevelField(x, y);
3271 TestIfElementTouchesCustomElement(x, y);
3273 if (GFX_CRUMBLED(element))
3274 DrawLevelFieldCrumbledSandNeighbours(x, y);
3276 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3277 StorePlayer[x][y] = 0;
3279 if (ELEM_IS_PLAYER(element))
3280 RelocatePlayer(x, y, element);
3283 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3285 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3289 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3291 int stored = Store[x][y];
3292 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3293 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3297 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3299 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3303 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3307 printf("::: %d / %d [%d - %d]\n",
3308 GfxFrame[x][y], phase - delay, phase, delay);
3312 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3313 element_info[GfxElement[x][y]].token_name,
3318 DrawLevelFieldCrumbledSand(x, y);
3320 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3322 DrawLevelElement(x, y, Back[x][y]);
3323 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3325 else if (IS_WALKABLE_UNDER(Back[x][y]))
3327 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3328 DrawLevelElementThruMask(x, y, Back[x][y]);
3330 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3331 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3335 void DynaExplode(int ex, int ey)
3338 int dynabomb_element = Feld[ex][ey];
3339 int dynabomb_size = 1;
3340 boolean dynabomb_xl = FALSE;
3341 struct PlayerInfo *player;
3342 static int xy[4][2] =
3350 if (IS_ACTIVE_BOMB(dynabomb_element))
3352 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3353 dynabomb_size = player->dynabomb_size;
3354 dynabomb_xl = player->dynabomb_xl;
3355 player->dynabombs_left++;
3358 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
3360 for (i = 0; i < NUM_DIRECTIONS; i++)
3362 for (j = 1; j <= dynabomb_size; j++)
3364 int x = ex + j * xy[i][0];
3365 int y = ey + j * xy[i][1];
3368 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3371 element = Feld[x][y];
3373 /* do not restart explosions of fields with active bombs */
3374 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3377 Explode(x, y, EX_PHASE_START, EX_BORDER);
3379 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3380 if (element != EL_EMPTY &&
3381 element != EL_SAND &&
3382 element != EL_EXPLOSION &&
3389 void Bang(int x, int y)
3392 int element = MovingOrBlocked2Element(x, y);
3394 int element = Feld[x][y];
3398 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3400 if (IS_PLAYER(x, y))
3403 struct PlayerInfo *player = PLAYERINFO(x, y);
3405 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3406 player->element_nr);
3411 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3413 if (game.emulation == EMU_SUPAPLEX)
3414 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3416 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3421 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3429 case EL_BD_BUTTERFLY:
3432 case EL_DARK_YAMYAM:
3436 RaiseScoreElement(element);
3437 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3439 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3440 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3441 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3442 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3443 case EL_DYNABOMB_INCREASE_NUMBER:
3444 case EL_DYNABOMB_INCREASE_SIZE:
3445 case EL_DYNABOMB_INCREASE_POWER:
3450 case EL_LAMP_ACTIVE:
3452 case EL_AMOEBA_TO_DIAMOND:
3454 if (IS_PLAYER(x, y))
3455 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3457 Explode(x, y, EX_PHASE_START, EX_CENTER);
3460 if (CAN_EXPLODE_DYNA(element))
3462 else if (CAN_EXPLODE_1X1(element))
3463 Explode(x, y, EX_PHASE_START, EX_CENTER);
3465 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3469 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3472 void SplashAcid(int x, int y)
3475 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3476 (!IN_LEV_FIELD(x - 1, y - 2) ||
3477 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3478 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3480 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3481 (!IN_LEV_FIELD(x + 1, y - 2) ||
3482 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3483 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3485 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3487 /* input: position of element entering acid (obsolete) */
3489 int element = Feld[x][y];
3491 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3494 if (element != EL_ACID_SPLASH_LEFT &&
3495 element != EL_ACID_SPLASH_RIGHT)
3497 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3499 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3500 (!IN_LEV_FIELD(x - 1, y - 1) ||
3501 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3502 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3504 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3505 (!IN_LEV_FIELD(x + 1, y - 1) ||
3506 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3507 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3512 static void InitBeltMovement()
3514 static int belt_base_element[4] =
3516 EL_CONVEYOR_BELT_1_LEFT,
3517 EL_CONVEYOR_BELT_2_LEFT,
3518 EL_CONVEYOR_BELT_3_LEFT,
3519 EL_CONVEYOR_BELT_4_LEFT
3521 static int belt_base_active_element[4] =
3523 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3524 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3525 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3526 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3531 /* set frame order for belt animation graphic according to belt direction */
3532 for (i = 0; i < NUM_BELTS; i++)
3536 for (j = 0; j < NUM_BELT_PARTS; j++)
3538 int element = belt_base_active_element[belt_nr] + j;
3539 int graphic = el2img(element);
3541 if (game.belt_dir[i] == MV_LEFT)
3542 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3544 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3548 for (y = 0; y < lev_fieldy; y++)
3550 for (x = 0; x < lev_fieldx; x++)
3552 int element = Feld[x][y];
3554 for (i = 0; i < NUM_BELTS; i++)
3556 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3558 int e_belt_nr = getBeltNrFromBeltElement(element);
3561 if (e_belt_nr == belt_nr)
3563 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3565 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3573 static void ToggleBeltSwitch(int x, int y)
3575 static int belt_base_element[4] =
3577 EL_CONVEYOR_BELT_1_LEFT,
3578 EL_CONVEYOR_BELT_2_LEFT,
3579 EL_CONVEYOR_BELT_3_LEFT,
3580 EL_CONVEYOR_BELT_4_LEFT
3582 static int belt_base_active_element[4] =
3584 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3585 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3586 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3587 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3589 static int belt_base_switch_element[4] =
3591 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3592 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3593 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3594 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3596 static int belt_move_dir[4] =
3604 int element = Feld[x][y];
3605 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3606 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3607 int belt_dir = belt_move_dir[belt_dir_nr];
3610 if (!IS_BELT_SWITCH(element))
3613 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3614 game.belt_dir[belt_nr] = belt_dir;
3616 if (belt_dir_nr == 3)
3619 /* set frame order for belt animation graphic according to belt direction */
3620 for (i = 0; i < NUM_BELT_PARTS; i++)
3622 int element = belt_base_active_element[belt_nr] + i;
3623 int graphic = el2img(element);
3625 if (belt_dir == MV_LEFT)
3626 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3628 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3631 for (yy = 0; yy < lev_fieldy; yy++)
3633 for (xx = 0; xx < lev_fieldx; xx++)
3635 int element = Feld[xx][yy];
3637 if (IS_BELT_SWITCH(element))
3639 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3641 if (e_belt_nr == belt_nr)
3643 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3644 DrawLevelField(xx, yy);
3647 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3649 int e_belt_nr = getBeltNrFromBeltElement(element);
3651 if (e_belt_nr == belt_nr)
3653 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3655 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3656 DrawLevelField(xx, yy);
3659 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3661 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3663 if (e_belt_nr == belt_nr)
3665 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3667 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3668 DrawLevelField(xx, yy);
3675 static void ToggleSwitchgateSwitch(int x, int y)
3679 game.switchgate_pos = !game.switchgate_pos;
3681 for (yy = 0; yy < lev_fieldy; yy++)
3683 for (xx = 0; xx < lev_fieldx; xx++)
3685 int element = Feld[xx][yy];
3687 if (element == EL_SWITCHGATE_SWITCH_UP ||
3688 element == EL_SWITCHGATE_SWITCH_DOWN)
3690 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3691 DrawLevelField(xx, yy);
3693 else if (element == EL_SWITCHGATE_OPEN ||
3694 element == EL_SWITCHGATE_OPENING)
3696 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3698 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3700 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3703 else if (element == EL_SWITCHGATE_CLOSED ||
3704 element == EL_SWITCHGATE_CLOSING)
3706 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3708 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3710 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3717 static int getInvisibleActiveFromInvisibleElement(int element)
3719 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3720 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3721 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3725 static int getInvisibleFromInvisibleActiveElement(int element)
3727 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3728 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3729 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3733 static void RedrawAllLightSwitchesAndInvisibleElements()
3737 for (y = 0; y < lev_fieldy; y++)
3739 for (x = 0; x < lev_fieldx; x++)
3741 int element = Feld[x][y];
3743 if (element == EL_LIGHT_SWITCH &&
3744 game.light_time_left > 0)
3746 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3747 DrawLevelField(x, y);
3749 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3750 game.light_time_left == 0)
3752 Feld[x][y] = EL_LIGHT_SWITCH;
3753 DrawLevelField(x, y);
3755 else if (element == EL_INVISIBLE_STEELWALL ||
3756 element == EL_INVISIBLE_WALL ||
3757 element == EL_INVISIBLE_SAND)
3759 if (game.light_time_left > 0)
3760 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3762 DrawLevelField(x, y);
3764 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3765 element == EL_INVISIBLE_WALL_ACTIVE ||
3766 element == EL_INVISIBLE_SAND_ACTIVE)
3768 if (game.light_time_left == 0)
3769 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3771 DrawLevelField(x, y);
3777 static void ToggleLightSwitch(int x, int y)
3779 int element = Feld[x][y];
3781 game.light_time_left =
3782 (element == EL_LIGHT_SWITCH ?
3783 level.time_light * FRAMES_PER_SECOND : 0);
3785 RedrawAllLightSwitchesAndInvisibleElements();
3788 static void ActivateTimegateSwitch(int x, int y)
3792 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3794 for (yy = 0; yy < lev_fieldy; yy++)
3796 for (xx = 0; xx < lev_fieldx; xx++)
3798 int element = Feld[xx][yy];
3800 if (element == EL_TIMEGATE_CLOSED ||
3801 element == EL_TIMEGATE_CLOSING)
3803 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3804 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3808 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3810 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3811 DrawLevelField(xx, yy);
3818 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3821 inline static int getElementMoveStepsize(int x, int y)
3823 int element = Feld[x][y];
3824 int direction = MovDir[x][y];
3825 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3826 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3827 int horiz_move = (dx != 0);
3828 int sign = (horiz_move ? dx : dy);
3829 int step = sign * element_info[element].move_stepsize;
3831 /* special values for move stepsize for spring and things on conveyor belt */
3835 if (element == EL_SPRING)
3836 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3837 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3838 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3839 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3841 if (CAN_FALL(element) &&
3842 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3843 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3844 else if (element == EL_SPRING)
3845 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3852 void Impact(int x, int y)
3854 boolean lastline = (y == lev_fieldy-1);
3855 boolean object_hit = FALSE;
3856 boolean impact = (lastline || object_hit);
3857 int element = Feld[x][y];
3858 int smashed = EL_STEELWALL;
3860 if (!lastline) /* check if element below was hit */
3862 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3865 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3866 MovDir[x][y + 1] != MV_DOWN ||
3867 MovPos[x][y + 1] <= TILEY / 2));
3870 object_hit = !IS_FREE(x, y + 1);
3873 /* do not smash moving elements that left the smashed field in time */
3874 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3875 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3879 smashed = MovingOrBlocked2Element(x, y + 1);
3881 impact = (lastline || object_hit);
3884 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3886 SplashAcid(x, y + 1);
3890 /* only reset graphic animation if graphic really changes after impact */
3892 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3894 ResetGfxAnimation(x, y);
3895 DrawLevelField(x, y);
3898 if (impact && CAN_EXPLODE_IMPACT(element))
3903 else if (impact && element == EL_PEARL)
3905 Feld[x][y] = EL_PEARL_BREAKING;
3906 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3909 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
3911 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3916 if (impact && element == EL_AMOEBA_DROP)
3918 if (object_hit && IS_PLAYER(x, y + 1))
3919 KillHeroUnlessEnemyProtected(x, y + 1);
3920 else if (object_hit && smashed == EL_PENGUIN)
3924 Feld[x][y] = EL_AMOEBA_GROWING;
3925 Store[x][y] = EL_AMOEBA_WET;
3927 ResetRandomAnimationValue(x, y);
3932 if (object_hit) /* check which object was hit */
3934 if (CAN_PASS_MAGIC_WALL(element) &&
3935 (smashed == EL_MAGIC_WALL ||
3936 smashed == EL_BD_MAGIC_WALL))
3939 int activated_magic_wall =
3940 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3941 EL_BD_MAGIC_WALL_ACTIVE);
3943 /* activate magic wall / mill */
3944 for (yy = 0; yy < lev_fieldy; yy++)
3945 for (xx = 0; xx < lev_fieldx; xx++)
3946 if (Feld[xx][yy] == smashed)
3947 Feld[xx][yy] = activated_magic_wall;
3949 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3950 game.magic_wall_active = TRUE;
3952 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3953 SND_MAGIC_WALL_ACTIVATING :
3954 SND_BD_MAGIC_WALL_ACTIVATING));
3957 if (IS_PLAYER(x, y + 1))
3959 if (CAN_SMASH_PLAYER(element))
3961 KillHeroUnlessEnemyProtected(x, y + 1);
3965 else if (smashed == EL_PENGUIN)
3967 if (CAN_SMASH_PLAYER(element))
3973 else if (element == EL_BD_DIAMOND)
3975 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3981 else if (((element == EL_SP_INFOTRON ||
3982 element == EL_SP_ZONK) &&
3983 (smashed == EL_SP_SNIKSNAK ||
3984 smashed == EL_SP_ELECTRON ||
3985 smashed == EL_SP_DISK_ORANGE)) ||
3986 (element == EL_SP_INFOTRON &&
3987 smashed == EL_SP_DISK_YELLOW))
3993 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3999 else if (CAN_SMASH_EVERYTHING(element))
4001 if (IS_CLASSIC_ENEMY(smashed) ||
4002 CAN_EXPLODE_SMASHED(smashed))
4007 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4009 if (smashed == EL_LAMP ||
4010 smashed == EL_LAMP_ACTIVE)
4015 else if (smashed == EL_NUT)
4017 Feld[x][y + 1] = EL_NUT_BREAKING;
4018 PlayLevelSound(x, y, SND_NUT_BREAKING);
4019 RaiseScoreElement(EL_NUT);
4022 else if (smashed == EL_PEARL)
4024 Feld[x][y + 1] = EL_PEARL_BREAKING;
4025 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4028 else if (smashed == EL_DIAMOND)
4030 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4031 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4034 else if (IS_BELT_SWITCH(smashed))
4036 ToggleBeltSwitch(x, y + 1);
4038 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4039 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4041 ToggleSwitchgateSwitch(x, y + 1);
4043 else if (smashed == EL_LIGHT_SWITCH ||
4044 smashed == EL_LIGHT_SWITCH_ACTIVE)
4046 ToggleLightSwitch(x, y + 1);
4051 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4054 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4056 CheckTriggeredElementChangeSide(x, y + 1, smashed,
4057 CE_OTHER_IS_SWITCHING, CH_SIDE_TOP);
4058 CheckElementChangeSide(x, y + 1, smashed, element,
4059 CE_SWITCHED, CH_SIDE_TOP);
4064 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4069 /* play sound of magic wall / mill */
4071 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4072 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4074 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4075 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4076 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4077 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4082 /* play sound of object that hits the ground */
4083 if (lastline || object_hit)
4084 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4087 inline static void TurnRoundExt(int x, int y)
4099 { 0, 0 }, { 0, 0 }, { 0, 0 },
4104 int left, right, back;
4108 { MV_DOWN, MV_UP, MV_RIGHT },
4109 { MV_UP, MV_DOWN, MV_LEFT },
4111 { MV_LEFT, MV_RIGHT, MV_DOWN },
4115 { MV_RIGHT, MV_LEFT, MV_UP }
4118 int element = Feld[x][y];
4119 int move_pattern = element_info[element].move_pattern;
4121 int old_move_dir = MovDir[x][y];
4122 int left_dir = turn[old_move_dir].left;
4123 int right_dir = turn[old_move_dir].right;
4124 int back_dir = turn[old_move_dir].back;
4126 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4127 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4128 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4129 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4131 int left_x = x + left_dx, left_y = y + left_dy;
4132 int right_x = x + right_dx, right_y = y + right_dy;
4133 int move_x = x + move_dx, move_y = y + move_dy;
4137 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4139 TestIfBadThingTouchesOtherBadThing(x, y);
4141 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4142 MovDir[x][y] = right_dir;
4143 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4144 MovDir[x][y] = left_dir;
4146 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4148 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4152 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4153 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4155 TestIfBadThingTouchesOtherBadThing(x, y);
4157 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4158 MovDir[x][y] = left_dir;
4159 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4160 MovDir[x][y] = right_dir;
4162 if ((element == EL_SPACESHIP ||
4163 element == EL_SP_SNIKSNAK ||
4164 element == EL_SP_ELECTRON)
4165 && MovDir[x][y] != old_move_dir)
4167 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4171 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4173 TestIfBadThingTouchesOtherBadThing(x, y);
4175 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4176 MovDir[x][y] = left_dir;
4177 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4178 MovDir[x][y] = right_dir;
4180 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4182 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4185 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4187 TestIfBadThingTouchesOtherBadThing(x, y);
4189 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4190 MovDir[x][y] = left_dir;
4191 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4192 MovDir[x][y] = right_dir;
4194 if (MovDir[x][y] != old_move_dir)
4198 else if (element == EL_YAMYAM)
4200 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4201 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4203 if (can_turn_left && can_turn_right)
4204 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4205 else if (can_turn_left)
4206 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4207 else if (can_turn_right)
4208 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4210 MovDir[x][y] = back_dir;
4212 MovDelay[x][y] = 16 + 16 * RND(3);
4214 else if (element == EL_DARK_YAMYAM)
4216 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4218 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4221 if (can_turn_left && can_turn_right)
4222 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4223 else if (can_turn_left)
4224 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4225 else if (can_turn_right)
4226 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4228 MovDir[x][y] = back_dir;
4230 MovDelay[x][y] = 16 + 16 * RND(3);
4232 else if (element == EL_PACMAN)
4234 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4235 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4237 if (can_turn_left && can_turn_right)
4238 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4239 else if (can_turn_left)
4240 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4241 else if (can_turn_right)
4242 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4244 MovDir[x][y] = back_dir;
4246 MovDelay[x][y] = 6 + RND(40);
4248 else if (element == EL_PIG)
4250 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4251 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4252 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4253 boolean should_turn_left, should_turn_right, should_move_on;
4255 int rnd = RND(rnd_value);
4257 should_turn_left = (can_turn_left &&
4259 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4260 y + back_dy + left_dy)));
4261 should_turn_right = (can_turn_right &&
4263 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4264 y + back_dy + right_dy)));
4265 should_move_on = (can_move_on &&
4268 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4269 y + move_dy + left_dy) ||
4270 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4271 y + move_dy + right_dy)));
4273 if (should_turn_left || should_turn_right || should_move_on)
4275 if (should_turn_left && should_turn_right && should_move_on)
4276 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4277 rnd < 2 * rnd_value / 3 ? right_dir :
4279 else if (should_turn_left && should_turn_right)
4280 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4281 else if (should_turn_left && should_move_on)
4282 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4283 else if (should_turn_right && should_move_on)
4284 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4285 else if (should_turn_left)
4286 MovDir[x][y] = left_dir;
4287 else if (should_turn_right)
4288 MovDir[x][y] = right_dir;
4289 else if (should_move_on)
4290 MovDir[x][y] = old_move_dir;
4292 else if (can_move_on && rnd > rnd_value / 8)
4293 MovDir[x][y] = old_move_dir;
4294 else if (can_turn_left && can_turn_right)
4295 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4296 else if (can_turn_left && rnd > rnd_value / 8)
4297 MovDir[x][y] = left_dir;
4298 else if (can_turn_right && rnd > rnd_value/8)
4299 MovDir[x][y] = right_dir;
4301 MovDir[x][y] = back_dir;
4303 xx = x + move_xy[MovDir[x][y]].x;
4304 yy = y + move_xy[MovDir[x][y]].y;
4306 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4307 MovDir[x][y] = old_move_dir;
4311 else if (element == EL_DRAGON)
4313 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4314 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4315 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4317 int rnd = RND(rnd_value);
4320 if (FrameCounter < 1 && x == 0 && y == 29)
4321 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4324 if (can_move_on && rnd > rnd_value / 8)
4325 MovDir[x][y] = old_move_dir;
4326 else if (can_turn_left && can_turn_right)
4327 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4328 else if (can_turn_left && rnd > rnd_value / 8)
4329 MovDir[x][y] = left_dir;
4330 else if (can_turn_right && rnd > rnd_value / 8)
4331 MovDir[x][y] = right_dir;
4333 MovDir[x][y] = back_dir;
4335 xx = x + move_xy[MovDir[x][y]].x;
4336 yy = y + move_xy[MovDir[x][y]].y;
4339 if (FrameCounter < 1 && x == 0 && y == 29)
4340 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4341 xx, yy, Feld[xx][yy],
4346 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4347 MovDir[x][y] = old_move_dir;
4349 if (!IS_FREE(xx, yy))
4350 MovDir[x][y] = old_move_dir;
4354 if (FrameCounter < 1 && x == 0 && y == 29)
4355 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4360 else if (element == EL_MOLE)
4362 boolean can_move_on =
4363 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4364 IS_AMOEBOID(Feld[move_x][move_y]) ||
4365 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4368 boolean can_turn_left =
4369 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4370 IS_AMOEBOID(Feld[left_x][left_y])));
4372 boolean can_turn_right =
4373 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4374 IS_AMOEBOID(Feld[right_x][right_y])));
4376 if (can_turn_left && can_turn_right)
4377 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4378 else if (can_turn_left)
4379 MovDir[x][y] = left_dir;
4381 MovDir[x][y] = right_dir;
4384 if (MovDir[x][y] != old_move_dir)
4387 else if (element == EL_BALLOON)
4389 MovDir[x][y] = game.balloon_dir;
4392 else if (element == EL_SPRING)
4395 if (MovDir[x][y] & MV_HORIZONTAL &&
4396 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4397 MovDir[x][y] = MV_NO_MOVING;
4399 if (MovDir[x][y] & MV_HORIZONTAL &&
4400 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4401 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4402 MovDir[x][y] = MV_NO_MOVING;
4407 else if (element == EL_ROBOT ||
4408 element == EL_SATELLITE ||
4409 element == EL_PENGUIN)
4411 int attr_x = -1, attr_y = -1;
4422 for (i = 0; i < MAX_PLAYERS; i++)
4424 struct PlayerInfo *player = &stored_player[i];
4425 int jx = player->jx, jy = player->jy;
4427 if (!player->active)
4431 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4439 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4445 if (element == EL_PENGUIN)
4448 static int xy[4][2] =
4456 for (i = 0; i < NUM_DIRECTIONS; i++)
4458 int ex = x + xy[i][0];
4459 int ey = y + xy[i][1];
4461 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4470 MovDir[x][y] = MV_NO_MOVING;
4472 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4473 else if (attr_x > x)
4474 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4476 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4477 else if (attr_y > y)
4478 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4480 if (element == EL_ROBOT)
4484 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4485 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4486 Moving2Blocked(x, y, &newx, &newy);
4488 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4489 MovDelay[x][y] = 8 + 8 * !RND(3);
4491 MovDelay[x][y] = 16;
4493 else if (element == EL_PENGUIN)
4499 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4501 boolean first_horiz = RND(2);
4502 int new_move_dir = MovDir[x][y];
4505 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4506 Moving2Blocked(x, y, &newx, &newy);
4508 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4512 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4513 Moving2Blocked(x, y, &newx, &newy);
4515 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4518 MovDir[x][y] = old_move_dir;
4522 else /* (element == EL_SATELLITE) */
4528 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4530 boolean first_horiz = RND(2);
4531 int new_move_dir = MovDir[x][y];
4534 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4535 Moving2Blocked(x, y, &newx, &newy);
4537 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4541 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4542 Moving2Blocked(x, y, &newx, &newy);
4544 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4547 MovDir[x][y] = old_move_dir;
4552 else if (move_pattern == MV_TURNING_LEFT ||
4553 move_pattern == MV_TURNING_RIGHT ||
4554 move_pattern == MV_TURNING_LEFT_RIGHT ||
4555 move_pattern == MV_TURNING_RIGHT_LEFT ||
4556 move_pattern == MV_TURNING_RANDOM ||
4557 move_pattern == MV_ALL_DIRECTIONS)
4559 boolean can_turn_left =
4560 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4561 boolean can_turn_right =
4562 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4564 if (move_pattern == MV_TURNING_LEFT)
4565 MovDir[x][y] = left_dir;
4566 else if (move_pattern == MV_TURNING_RIGHT)
4567 MovDir[x][y] = right_dir;
4568 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4569 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4570 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4571 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4572 else if (move_pattern == MV_TURNING_RANDOM)
4573 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4574 can_turn_right && !can_turn_left ? right_dir :
4575 RND(2) ? left_dir : right_dir);
4576 else if (can_turn_left && can_turn_right)
4577 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4578 else if (can_turn_left)
4579 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4580 else if (can_turn_right)
4581 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4583 MovDir[x][y] = back_dir;
4585 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4587 else if (move_pattern == MV_HORIZONTAL ||
4588 move_pattern == MV_VERTICAL)
4590 if (move_pattern & old_move_dir)
4591 MovDir[x][y] = back_dir;
4592 else if (move_pattern == MV_HORIZONTAL)
4593 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4594 else if (move_pattern == MV_VERTICAL)
4595 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4597 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4599 else if (move_pattern & MV_ANY_DIRECTION)
4601 MovDir[x][y] = move_pattern;
4602 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4604 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4606 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4607 MovDir[x][y] = left_dir;
4608 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4609 MovDir[x][y] = right_dir;
4611 if (MovDir[x][y] != old_move_dir)
4612 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4614 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4616 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4617 MovDir[x][y] = right_dir;
4618 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4619 MovDir[x][y] = left_dir;
4621 if (MovDir[x][y] != old_move_dir)
4622 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4624 else if (move_pattern == MV_TOWARDS_PLAYER ||
4625 move_pattern == MV_AWAY_FROM_PLAYER)
4627 int attr_x = -1, attr_y = -1;
4629 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4640 for (i = 0; i < MAX_PLAYERS; i++)
4642 struct PlayerInfo *player = &stored_player[i];
4643 int jx = player->jx, jy = player->jy;
4645 if (!player->active)
4649 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4657 MovDir[x][y] = MV_NO_MOVING;
4659 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4660 else if (attr_x > x)
4661 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4663 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4664 else if (attr_y > y)
4665 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4667 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4669 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4671 boolean first_horiz = RND(2);
4672 int new_move_dir = MovDir[x][y];
4675 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4676 Moving2Blocked(x, y, &newx, &newy);
4678 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4682 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4683 Moving2Blocked(x, y, &newx, &newy);
4685 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4688 MovDir[x][y] = old_move_dir;
4691 else if (move_pattern == MV_WHEN_PUSHED ||
4692 move_pattern == MV_WHEN_DROPPED)
4694 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4695 MovDir[x][y] = MV_NO_MOVING;
4699 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4701 static int test_xy[7][2] =
4711 static int test_dir[7] =
4721 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4722 int move_preference = -1000000; /* start with very low preference */
4723 int new_move_dir = MV_NO_MOVING;
4724 int start_test = RND(4);
4727 for (i = 0; i < NUM_DIRECTIONS; i++)
4729 int move_dir = test_dir[start_test + i];
4730 int move_dir_preference;
4732 xx = x + test_xy[start_test + i][0];
4733 yy = y + test_xy[start_test + i][1];
4735 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4736 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4738 new_move_dir = move_dir;
4743 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4746 move_dir_preference = -1 * RunnerVisit[xx][yy];
4747 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4748 move_dir_preference = PlayerVisit[xx][yy];
4750 if (move_dir_preference > move_preference)
4752 /* prefer field that has not been visited for the longest time */
4753 move_preference = move_dir_preference;
4754 new_move_dir = move_dir;
4756 else if (move_dir_preference == move_preference &&
4757 move_dir == old_move_dir)
4759 /* prefer last direction when all directions are preferred equally */
4760 move_preference = move_dir_preference;
4761 new_move_dir = move_dir;
4765 MovDir[x][y] = new_move_dir;
4766 if (old_move_dir != new_move_dir)
4771 static void TurnRound(int x, int y)
4773 int direction = MovDir[x][y];
4776 GfxDir[x][y] = MovDir[x][y];
4782 GfxDir[x][y] = MovDir[x][y];
4785 if (direction != MovDir[x][y])
4790 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4793 GfxAction[x][y] = ACTION_WAITING;
4797 static boolean JustBeingPushed(int x, int y)
4801 for (i = 0; i < MAX_PLAYERS; i++)
4803 struct PlayerInfo *player = &stored_player[i];
4805 if (player->active && player->is_pushing && player->MovPos)
4807 int next_jx = player->jx + (player->jx - player->last_jx);
4808 int next_jy = player->jy + (player->jy - player->last_jy);
4810 if (x == next_jx && y == next_jy)
4818 void StartMoving(int x, int y)
4821 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4823 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4824 int element = Feld[x][y];
4830 if (MovDelay[x][y] == 0)
4831 GfxAction[x][y] = ACTION_DEFAULT;
4833 /* !!! this should be handled more generic (not only for mole) !!! */
4834 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4835 GfxAction[x][y] = ACTION_DEFAULT;
4838 if (CAN_FALL(element) && y < lev_fieldy - 1)
4840 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4841 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4842 if (JustBeingPushed(x, y))
4845 if (element == EL_QUICKSAND_FULL)
4847 if (IS_FREE(x, y + 1))
4849 InitMovingField(x, y, MV_DOWN);
4850 started_moving = TRUE;
4852 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4853 Store[x][y] = EL_ROCK;
4855 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4857 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4860 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4862 if (!MovDelay[x][y])
4863 MovDelay[x][y] = TILEY + 1;
4872 Feld[x][y] = EL_QUICKSAND_EMPTY;
4873 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4874 Store[x][y + 1] = Store[x][y];
4877 PlayLevelSoundAction(x, y, ACTION_FILLING);
4879 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4883 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4884 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4886 InitMovingField(x, y, MV_DOWN);
4887 started_moving = TRUE;
4889 Feld[x][y] = EL_QUICKSAND_FILLING;
4890 Store[x][y] = element;
4892 PlayLevelSoundAction(x, y, ACTION_FILLING);
4894 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4897 else if (element == EL_MAGIC_WALL_FULL)
4899 if (IS_FREE(x, y + 1))
4901 InitMovingField(x, y, MV_DOWN);
4902 started_moving = TRUE;
4904 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4905 Store[x][y] = EL_CHANGED(Store[x][y]);
4907 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4909 if (!MovDelay[x][y])
4910 MovDelay[x][y] = TILEY/4 + 1;
4919 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4920 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4921 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4925 else if (element == EL_BD_MAGIC_WALL_FULL)
4927 if (IS_FREE(x, y + 1))
4929 InitMovingField(x, y, MV_DOWN);
4930 started_moving = TRUE;
4932 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4933 Store[x][y] = EL_CHANGED2(Store[x][y]);
4935 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4937 if (!MovDelay[x][y])
4938 MovDelay[x][y] = TILEY/4 + 1;
4947 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4948 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4949 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4953 else if (CAN_PASS_MAGIC_WALL(element) &&
4954 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4955 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4957 InitMovingField(x, y, MV_DOWN);
4958 started_moving = TRUE;
4961 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4962 EL_BD_MAGIC_WALL_FILLING);
4963 Store[x][y] = element;
4966 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4968 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4971 SplashAcid(x, y + 1);
4973 InitMovingField(x, y, MV_DOWN);
4974 started_moving = TRUE;
4976 Store[x][y] = EL_ACID;
4978 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4979 GfxAction[x][y + 1] = ACTION_ACTIVE;
4983 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4984 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4985 (Feld[x][y + 1] == EL_BLOCKED)) ||
4986 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4987 CAN_SMASH(element) && WasJustFalling[x][y] &&
4988 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4992 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4993 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4994 WasJustMoving[x][y] && !Pushed[x][y + 1])
4996 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4997 WasJustMoving[x][y])
5002 /* this is needed for a special case not covered by calling "Impact()"
5003 from "ContinueMoving()": if an element moves to a tile directly below
5004 another element which was just falling on that tile (which was empty
5005 in the previous frame), the falling element above would just stop
5006 instead of smashing the element below (in previous version, the above
5007 element was just checked for "moving" instead of "falling", resulting
5008 in incorrect smashes caused by horizontal movement of the above
5009 element; also, the case of the player being the element to smash was
5010 simply not covered here... :-/ ) */
5013 WasJustMoving[x][y] = 0;
5014 WasJustFalling[x][y] = 0;
5019 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5021 if (MovDir[x][y] == MV_NO_MOVING)
5023 InitMovingField(x, y, MV_DOWN);
5024 started_moving = TRUE;
5027 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5029 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5030 MovDir[x][y] = MV_DOWN;
5032 InitMovingField(x, y, MV_DOWN);
5033 started_moving = TRUE;
5035 else if (element == EL_AMOEBA_DROP)
5037 Feld[x][y] = EL_AMOEBA_GROWING;
5038 Store[x][y] = EL_AMOEBA_WET;
5040 /* Store[x][y + 1] must be zero, because:
5041 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5044 #if OLD_GAME_BEHAVIOUR
5045 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5047 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5048 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5049 element != EL_DX_SUPABOMB)
5052 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5053 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5054 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5055 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5058 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5059 (IS_FREE(x - 1, y + 1) ||
5060 Feld[x - 1][y + 1] == EL_ACID));
5061 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5062 (IS_FREE(x + 1, y + 1) ||
5063 Feld[x + 1][y + 1] == EL_ACID));
5064 boolean can_fall_any = (can_fall_left || can_fall_right);
5065 boolean can_fall_both = (can_fall_left && can_fall_right);
5067 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5069 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5071 if (slippery_type == SLIPPERY_ONLY_LEFT)
5072 can_fall_right = FALSE;
5073 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5074 can_fall_left = FALSE;
5075 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5076 can_fall_right = FALSE;
5077 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5078 can_fall_left = FALSE;
5080 can_fall_any = (can_fall_left || can_fall_right);
5081 can_fall_both = (can_fall_left && can_fall_right);
5086 if (can_fall_both &&
5087 (game.emulation != EMU_BOULDERDASH &&
5088 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5089 can_fall_left = !(can_fall_right = RND(2));
5091 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5092 started_moving = TRUE;
5096 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5098 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5101 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5102 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5103 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5104 int belt_dir = game.belt_dir[belt_nr];
5106 if ((belt_dir == MV_LEFT && left_is_free) ||
5107 (belt_dir == MV_RIGHT && right_is_free))
5110 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5113 InitMovingField(x, y, belt_dir);
5114 started_moving = TRUE;
5117 Pushed[x][y] = TRUE;
5118 Pushed[nextx][y] = TRUE;
5121 GfxAction[x][y] = ACTION_DEFAULT;
5125 MovDir[x][y] = 0; /* if element was moving, stop it */
5130 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5131 if (CAN_MOVE(element) && !started_moving)
5133 int move_pattern = element_info[element].move_pattern;
5136 Moving2Blocked(x, y, &newx, &newy);
5139 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5142 if ((element == EL_SATELLITE ||
5143 element == EL_BALLOON ||
5144 element == EL_SPRING)
5145 && JustBeingPushed(x, y))
5150 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5151 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5152 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5155 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5156 element, element_info[element].token_name,
5157 WasJustMoving[x][y],
5158 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5159 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5160 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5161 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5165 WasJustMoving[x][y] = 0;
5168 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5171 if (Feld[x][y] != element) /* element has changed */
5173 element = Feld[x][y];
5174 move_pattern = element_info[element].move_pattern;
5176 if (!CAN_MOVE(element))
5180 if (Feld[x][y] != element) /* element has changed */
5188 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5189 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5191 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5193 Moving2Blocked(x, y, &newx, &newy);
5194 if (Feld[newx][newy] == EL_BLOCKED)
5195 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5201 if (FrameCounter < 1 && x == 0 && y == 29)
5202 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5205 if (!MovDelay[x][y]) /* start new movement phase */
5207 /* all objects that can change their move direction after each step
5208 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5210 if (element != EL_YAMYAM &&
5211 element != EL_DARK_YAMYAM &&
5212 element != EL_PACMAN &&
5213 !(move_pattern & MV_ANY_DIRECTION) &&
5214 move_pattern != MV_TURNING_LEFT &&
5215 move_pattern != MV_TURNING_RIGHT &&
5216 move_pattern != MV_TURNING_LEFT_RIGHT &&
5217 move_pattern != MV_TURNING_RIGHT_LEFT &&
5218 move_pattern != MV_TURNING_RANDOM)
5223 if (FrameCounter < 1 && x == 0 && y == 29)
5224 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5227 if (MovDelay[x][y] && (element == EL_BUG ||
5228 element == EL_SPACESHIP ||
5229 element == EL_SP_SNIKSNAK ||
5230 element == EL_SP_ELECTRON ||
5231 element == EL_MOLE))
5232 DrawLevelField(x, y);
5236 if (MovDelay[x][y]) /* wait some time before next movement */
5241 if (element == EL_YAMYAM)
5244 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5245 DrawLevelElementAnimation(x, y, element);
5249 if (MovDelay[x][y]) /* element still has to wait some time */
5252 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5253 ResetGfxAnimation(x, y);
5257 if (GfxAction[x][y] != ACTION_WAITING)
5258 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5260 GfxAction[x][y] = ACTION_WAITING;
5264 if (element == EL_ROBOT ||
5266 element == EL_PACMAN ||
5268 element == EL_YAMYAM ||
5269 element == EL_DARK_YAMYAM)
5272 DrawLevelElementAnimation(x, y, element);
5274 DrawLevelElementAnimationIfNeeded(x, y, element);
5276 PlayLevelSoundAction(x, y, ACTION_WAITING);
5278 else if (element == EL_SP_ELECTRON)
5279 DrawLevelElementAnimationIfNeeded(x, y, element);
5280 else if (element == EL_DRAGON)
5283 int dir = MovDir[x][y];
5284 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5285 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5286 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5287 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5288 dir == MV_UP ? IMG_FLAMES_1_UP :
5289 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5290 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5293 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5296 GfxAction[x][y] = ACTION_ATTACKING;
5298 if (IS_PLAYER(x, y))
5299 DrawPlayerField(x, y);
5301 DrawLevelField(x, y);
5303 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5305 for (i = 1; i <= 3; i++)
5307 int xx = x + i * dx;
5308 int yy = y + i * dy;
5309 int sx = SCREENX(xx);
5310 int sy = SCREENY(yy);
5311 int flame_graphic = graphic + (i - 1);
5313 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5318 int flamed = MovingOrBlocked2Element(xx, yy);
5320 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5323 RemoveMovingField(xx, yy);
5325 Feld[xx][yy] = EL_FLAMES;
5326 if (IN_SCR_FIELD(sx, sy))
5328 DrawLevelFieldCrumbledSand(xx, yy);
5329 DrawGraphic(sx, sy, flame_graphic, frame);
5334 if (Feld[xx][yy] == EL_FLAMES)
5335 Feld[xx][yy] = EL_EMPTY;
5336 DrawLevelField(xx, yy);
5341 if (MovDelay[x][y]) /* element still has to wait some time */
5343 PlayLevelSoundAction(x, y, ACTION_WAITING);
5349 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5350 for all other elements GfxAction will be set by InitMovingField() */
5351 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5352 GfxAction[x][y] = ACTION_MOVING;
5356 /* now make next step */
5358 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5360 if (DONT_COLLIDE_WITH(element) &&
5361 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5362 !PLAYER_ENEMY_PROTECTED(newx, newy))
5365 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5369 /* player killed by element which is deadly when colliding with */
5371 KillHero(PLAYERINFO(newx, newy));
5378 else if (CAN_MOVE_INTO_ACID(element) &&
5379 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5380 (MovDir[x][y] == MV_DOWN ||
5381 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5383 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5384 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5388 else if ((element == EL_PENGUIN ||
5389 element == EL_ROBOT ||
5390 element == EL_SATELLITE ||
5391 element == EL_BALLOON ||
5392 IS_CUSTOM_ELEMENT(element)) &&
5393 IN_LEV_FIELD(newx, newy) &&
5394 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5397 SplashAcid(newx, newy);
5398 Store[x][y] = EL_ACID;
5400 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5402 if (Feld[newx][newy] == EL_EXIT_OPEN)
5406 DrawLevelField(x, y);
5408 Feld[x][y] = EL_EMPTY;
5409 DrawLevelField(x, y);
5412 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5413 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5414 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5416 local_player->friends_still_needed--;
5417 if (!local_player->friends_still_needed &&
5418 !local_player->GameOver && AllPlayersGone)
5419 local_player->LevelSolved = local_player->GameOver = TRUE;
5423 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5425 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5426 DrawLevelField(newx, newy);
5428 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5430 else if (!IS_FREE(newx, newy))
5432 GfxAction[x][y] = ACTION_WAITING;
5434 if (IS_PLAYER(x, y))
5435 DrawPlayerField(x, y);
5437 DrawLevelField(x, y);
5442 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5444 if (IS_FOOD_PIG(Feld[newx][newy]))
5446 if (IS_MOVING(newx, newy))
5447 RemoveMovingField(newx, newy);
5450 Feld[newx][newy] = EL_EMPTY;
5451 DrawLevelField(newx, newy);
5454 PlayLevelSound(x, y, SND_PIG_DIGGING);
5456 else if (!IS_FREE(newx, newy))
5458 if (IS_PLAYER(x, y))
5459 DrawPlayerField(x, y);
5461 DrawLevelField(x, y);
5470 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5473 else if (IS_CUSTOM_ELEMENT(element) &&
5474 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5478 !IS_FREE(newx, newy)
5483 int new_element = Feld[newx][newy];
5486 printf("::: '%s' digs '%s' [%d]\n",
5487 element_info[element].token_name,
5488 element_info[Feld[newx][newy]].token_name,
5489 StorePlayer[newx][newy]);
5492 if (!IS_FREE(newx, newy))
5494 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5495 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5498 /* no element can dig solid indestructible elements */
5499 if (IS_INDESTRUCTIBLE(new_element) &&
5500 !IS_DIGGABLE(new_element) &&
5501 !IS_COLLECTIBLE(new_element))
5504 if (AmoebaNr[newx][newy] &&
5505 (new_element == EL_AMOEBA_FULL ||
5506 new_element == EL_BD_AMOEBA ||
5507 new_element == EL_AMOEBA_GROWING))
5509 AmoebaCnt[AmoebaNr[newx][newy]]--;
5510 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5513 if (IS_MOVING(newx, newy))
5514 RemoveMovingField(newx, newy);
5517 RemoveField(newx, newy);
5518 DrawLevelField(newx, newy);
5521 PlayLevelSoundAction(x, y, action);
5524 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5525 element_info[element].can_leave_element = TRUE;
5527 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5529 RunnerVisit[x][y] = FrameCounter;
5530 PlayerVisit[x][y] /= 8; /* expire player visit path */
5536 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5538 if (!IS_FREE(newx, newy))
5540 if (IS_PLAYER(x, y))
5541 DrawPlayerField(x, y);
5543 DrawLevelField(x, y);
5549 boolean wanna_flame = !RND(10);
5550 int dx = newx - x, dy = newy - y;
5551 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5552 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5553 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5554 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5555 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5556 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5559 IS_CLASSIC_ENEMY(element1) ||
5560 IS_CLASSIC_ENEMY(element2)) &&
5561 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5562 element1 != EL_FLAMES && element2 != EL_FLAMES)
5565 ResetGfxAnimation(x, y);
5566 GfxAction[x][y] = ACTION_ATTACKING;
5569 if (IS_PLAYER(x, y))
5570 DrawPlayerField(x, y);
5572 DrawLevelField(x, y);
5574 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5576 MovDelay[x][y] = 50;
5578 Feld[newx][newy] = EL_FLAMES;
5579 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5580 Feld[newx1][newy1] = EL_FLAMES;
5581 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5582 Feld[newx2][newy2] = EL_FLAMES;
5588 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5589 Feld[newx][newy] == EL_DIAMOND)
5591 if (IS_MOVING(newx, newy))
5592 RemoveMovingField(newx, newy);
5595 Feld[newx][newy] = EL_EMPTY;
5596 DrawLevelField(newx, newy);
5599 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5601 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5602 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5604 if (AmoebaNr[newx][newy])
5606 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5607 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5608 Feld[newx][newy] == EL_BD_AMOEBA)
5609 AmoebaCnt[AmoebaNr[newx][newy]]--;
5614 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5616 if (IS_MOVING(newx, newy))
5619 RemoveMovingField(newx, newy);
5623 Feld[newx][newy] = EL_EMPTY;
5624 DrawLevelField(newx, newy);
5627 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5629 else if ((element == EL_PACMAN || element == EL_MOLE)
5630 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5632 if (AmoebaNr[newx][newy])
5634 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5635 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5636 Feld[newx][newy] == EL_BD_AMOEBA)
5637 AmoebaCnt[AmoebaNr[newx][newy]]--;
5640 if (element == EL_MOLE)
5642 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5643 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5645 ResetGfxAnimation(x, y);
5646 GfxAction[x][y] = ACTION_DIGGING;
5647 DrawLevelField(x, y);
5649 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5651 return; /* wait for shrinking amoeba */
5653 else /* element == EL_PACMAN */
5655 Feld[newx][newy] = EL_EMPTY;
5656 DrawLevelField(newx, newy);
5657 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5660 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5661 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5662 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5664 /* wait for shrinking amoeba to completely disappear */
5667 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5669 /* object was running against a wall */
5674 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5675 DrawLevelElementAnimation(x, y, element);
5677 if (element == EL_BUG ||
5678 element == EL_SPACESHIP ||
5679 element == EL_SP_SNIKSNAK)
5680 DrawLevelField(x, y);
5681 else if (element == EL_MOLE)
5682 DrawLevelField(x, y);
5683 else if (element == EL_BD_BUTTERFLY ||
5684 element == EL_BD_FIREFLY)
5685 DrawLevelElementAnimationIfNeeded(x, y, element);
5686 else if (element == EL_SATELLITE)
5687 DrawLevelElementAnimationIfNeeded(x, y, element);
5688 else if (element == EL_SP_ELECTRON)
5689 DrawLevelElementAnimationIfNeeded(x, y, element);
5692 if (DONT_TOUCH(element))
5693 TestIfBadThingTouchesHero(x, y);
5696 PlayLevelSoundAction(x, y, ACTION_WAITING);
5702 InitMovingField(x, y, MovDir[x][y]);
5704 PlayLevelSoundAction(x, y, ACTION_MOVING);
5708 ContinueMoving(x, y);
5711 void ContinueMoving(int x, int y)
5713 int element = Feld[x][y];
5714 struct ElementInfo *ei = &element_info[element];
5715 int direction = MovDir[x][y];
5716 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5717 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5718 int newx = x + dx, newy = y + dy;
5720 int nextx = newx + dx, nexty = newy + dy;
5723 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5724 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5726 boolean pushed_by_player = Pushed[x][y];
5729 MovPos[x][y] += getElementMoveStepsize(x, y);
5732 if (pushed_by_player && IS_PLAYER(x, y))
5734 /* special case: moving object pushed by player */
5735 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5738 if (pushed_by_player) /* special case: moving object pushed by player */
5739 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5742 if (ABS(MovPos[x][y]) < TILEX)
5744 DrawLevelField(x, y);
5746 return; /* element is still moving */
5749 /* element reached destination field */
5751 Feld[x][y] = EL_EMPTY;
5752 Feld[newx][newy] = element;
5753 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5755 if (element == EL_MOLE)
5757 Feld[x][y] = EL_SAND;
5759 DrawLevelFieldCrumbledSandNeighbours(x, y);
5761 else if (element == EL_QUICKSAND_FILLING)
5763 element = Feld[newx][newy] = get_next_element(element);
5764 Store[newx][newy] = Store[x][y];
5766 else if (element == EL_QUICKSAND_EMPTYING)
5768 Feld[x][y] = get_next_element(element);
5769 element = Feld[newx][newy] = Store[x][y];
5771 else if (element == EL_MAGIC_WALL_FILLING)
5773 element = Feld[newx][newy] = get_next_element(element);
5774 if (!game.magic_wall_active)
5775 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5776 Store[newx][newy] = Store[x][y];
5778 else if (element == EL_MAGIC_WALL_EMPTYING)
5780 Feld[x][y] = get_next_element(element);
5781 if (!game.magic_wall_active)
5782 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5783 element = Feld[newx][newy] = Store[x][y];
5785 else if (element == EL_BD_MAGIC_WALL_FILLING)
5787 element = Feld[newx][newy] = get_next_element(element);
5788 if (!game.magic_wall_active)
5789 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5790 Store[newx][newy] = Store[x][y];
5792 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5794 Feld[x][y] = get_next_element(element);
5795 if (!game.magic_wall_active)
5796 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5797 element = Feld[newx][newy] = Store[x][y];
5799 else if (element == EL_AMOEBA_DROPPING)
5801 Feld[x][y] = get_next_element(element);
5802 element = Feld[newx][newy] = Store[x][y];
5804 else if (element == EL_SOKOBAN_OBJECT)
5807 Feld[x][y] = Back[x][y];
5809 if (Back[newx][newy])
5810 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5812 Back[x][y] = Back[newx][newy] = 0;
5814 else if (Store[x][y] == EL_ACID)
5816 element = Feld[newx][newy] = EL_ACID;
5820 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5821 MovDelay[newx][newy] = 0;
5823 if (CAN_CHANGE(element))
5825 /* copy element change control values to new field */
5826 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5827 ChangePage[newx][newy] = ChangePage[x][y];
5828 Changed[newx][newy] = Changed[x][y];
5829 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5832 ChangeDelay[x][y] = 0;
5833 ChangePage[x][y] = -1;
5834 Changed[x][y] = CE_BITMASK_DEFAULT;
5835 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5837 /* copy animation control values to new field */
5838 GfxFrame[newx][newy] = GfxFrame[x][y];
5839 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5840 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5841 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5843 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5845 ResetGfxAnimation(x, y); /* reset animation values for old field */
5848 /* some elements can leave other elements behind after moving */
5849 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5850 ei->move_leave_element != EL_EMPTY &&
5851 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5852 ei->can_leave_element_last))
5854 Feld[x][y] = ei->move_leave_element;
5855 InitField(x, y, FALSE);
5857 if (GFX_CRUMBLED(Feld[x][y]))
5858 DrawLevelFieldCrumbledSandNeighbours(x, y);
5861 ei->can_leave_element_last = ei->can_leave_element;
5862 ei->can_leave_element = FALSE;
5866 /* 2.1.1 (does not work correctly for spring) */
5867 if (!CAN_MOVE(element))
5868 MovDir[newx][newy] = 0;
5872 /* (does not work for falling objects that slide horizontally) */
5873 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5874 MovDir[newx][newy] = 0;
5877 if (!CAN_MOVE(element) ||
5878 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5879 MovDir[newx][newy] = 0;
5882 if (!CAN_MOVE(element) ||
5883 (CAN_FALL(element) && direction == MV_DOWN))
5884 GfxDir[x][y] = MovDir[newx][newy] = 0;
5889 DrawLevelField(x, y);
5890 DrawLevelField(newx, newy);
5892 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5894 /* prevent pushed element from moving on in pushed direction */
5895 if (pushed_by_player && CAN_MOVE(element) &&
5896 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5897 !(element_info[element].move_pattern & direction))
5898 TurnRound(newx, newy);
5901 /* prevent elements on conveyor belt from moving on in last direction */
5902 if (pushed_by_conveyor && CAN_FALL(element) &&
5903 direction & MV_HORIZONTAL)
5904 MovDir[newx][newy] = 0;
5907 if (!pushed_by_player)
5909 WasJustMoving[newx][newy] = 3;
5911 if (CAN_FALL(element) && direction == MV_DOWN)
5912 WasJustFalling[newx][newy] = 3;
5915 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5917 TestIfBadThingTouchesHero(newx, newy);
5918 TestIfBadThingTouchesFriend(newx, newy);
5920 if (!IS_CUSTOM_ELEMENT(element))
5921 TestIfBadThingTouchesOtherBadThing(newx, newy);
5923 else if (element == EL_PENGUIN)
5924 TestIfFriendTouchesBadThing(newx, newy);
5926 if (CAN_FALL(element) && direction == MV_DOWN &&
5927 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5931 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5935 if (ChangePage[newx][newy] != -1) /* delayed change */
5936 ChangeElement(newx, newy, ChangePage[newx][newy]);
5941 TestIfElementHitsCustomElement(newx, newy, direction);
5945 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5947 int hitting_element = Feld[newx][newy];
5949 /* !!! fix side (direction) orientation here and elsewhere !!! */
5950 CheckElementChangeSide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
5954 if (IN_LEV_FIELD(nextx, nexty))
5956 int opposite_direction = MV_DIR_OPPOSITE(direction);
5957 int hitting_side = direction;
5958 int touched_side = opposite_direction;
5959 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5960 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5961 MovDir[nextx][nexty] != direction ||
5962 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5968 CheckElementChangeSide(nextx, nexty, touched_element,
5969 CE_HIT_BY_SOMETHING, opposite_direction);
5971 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5972 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5974 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5976 struct ElementChangeInfo *change =
5977 &element_info[hitting_element].change_page[i];
5979 if (change->can_change &&
5980 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5981 change->trigger_side & touched_side &&
5982 change->trigger_element == touched_element)
5984 CheckElementChangePage(newx, newy, hitting_element,
5985 touched_element, CE_OTHER_IS_HITTING, i);
5991 if (IS_CUSTOM_ELEMENT(touched_element) &&
5992 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5994 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5996 struct ElementChangeInfo *change =
5997 &element_info[touched_element].change_page[i];
5999 if (change->can_change &&
6000 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6001 change->trigger_side & hitting_side &&
6002 change->trigger_element == hitting_element)
6004 CheckElementChangePage(nextx, nexty, touched_element,
6005 hitting_element, CE_OTHER_GETS_HIT, i);
6016 TestIfPlayerTouchesCustomElement(newx, newy);
6017 TestIfElementTouchesCustomElement(newx, newy);
6020 int AmoebeNachbarNr(int ax, int ay)
6023 int element = Feld[ax][ay];
6025 static int xy[4][2] =
6033 for (i = 0; i < NUM_DIRECTIONS; i++)
6035 int x = ax + xy[i][0];
6036 int y = ay + xy[i][1];
6038 if (!IN_LEV_FIELD(x, y))
6041 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6042 group_nr = AmoebaNr[x][y];
6048 void AmoebenVereinigen(int ax, int ay)
6050 int i, x, y, xx, yy;
6051 int new_group_nr = AmoebaNr[ax][ay];
6052 static int xy[4][2] =
6060 if (new_group_nr == 0)
6063 for (i = 0; i < NUM_DIRECTIONS; i++)
6068 if (!IN_LEV_FIELD(x, y))
6071 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6072 Feld[x][y] == EL_BD_AMOEBA ||
6073 Feld[x][y] == EL_AMOEBA_DEAD) &&
6074 AmoebaNr[x][y] != new_group_nr)
6076 int old_group_nr = AmoebaNr[x][y];
6078 if (old_group_nr == 0)
6081 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6082 AmoebaCnt[old_group_nr] = 0;
6083 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6084 AmoebaCnt2[old_group_nr] = 0;
6086 for (yy = 0; yy < lev_fieldy; yy++)
6088 for (xx = 0; xx < lev_fieldx; xx++)
6090 if (AmoebaNr[xx][yy] == old_group_nr)
6091 AmoebaNr[xx][yy] = new_group_nr;
6098 void AmoebeUmwandeln(int ax, int ay)
6102 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6104 int group_nr = AmoebaNr[ax][ay];
6109 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6110 printf("AmoebeUmwandeln(): This should never happen!\n");
6115 for (y = 0; y < lev_fieldy; y++)
6117 for (x = 0; x < lev_fieldx; x++)
6119 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6122 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6126 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6127 SND_AMOEBA_TURNING_TO_GEM :
6128 SND_AMOEBA_TURNING_TO_ROCK));
6133 static int xy[4][2] =
6141 for (i = 0; i < NUM_DIRECTIONS; i++)
6146 if (!IN_LEV_FIELD(x, y))
6149 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6151 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6152 SND_AMOEBA_TURNING_TO_GEM :
6153 SND_AMOEBA_TURNING_TO_ROCK));
6160 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6163 int group_nr = AmoebaNr[ax][ay];
6164 boolean done = FALSE;
6169 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6170 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6175 for (y = 0; y < lev_fieldy; y++)
6177 for (x = 0; x < lev_fieldx; x++)
6179 if (AmoebaNr[x][y] == group_nr &&
6180 (Feld[x][y] == EL_AMOEBA_DEAD ||
6181 Feld[x][y] == EL_BD_AMOEBA ||
6182 Feld[x][y] == EL_AMOEBA_GROWING))
6185 Feld[x][y] = new_element;
6186 InitField(x, y, FALSE);
6187 DrawLevelField(x, y);
6194 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6195 SND_BD_AMOEBA_TURNING_TO_ROCK :
6196 SND_BD_AMOEBA_TURNING_TO_GEM));
6199 void AmoebeWaechst(int x, int y)
6201 static unsigned long sound_delay = 0;
6202 static unsigned long sound_delay_value = 0;
6204 if (!MovDelay[x][y]) /* start new growing cycle */
6208 if (DelayReached(&sound_delay, sound_delay_value))
6211 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6213 if (Store[x][y] == EL_BD_AMOEBA)
6214 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6216 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6218 sound_delay_value = 30;
6222 if (MovDelay[x][y]) /* wait some time before growing bigger */
6225 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6227 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6228 6 - MovDelay[x][y]);
6230 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6233 if (!MovDelay[x][y])
6235 Feld[x][y] = Store[x][y];
6237 DrawLevelField(x, y);
6242 void AmoebaDisappearing(int x, int y)
6244 static unsigned long sound_delay = 0;
6245 static unsigned long sound_delay_value = 0;
6247 if (!MovDelay[x][y]) /* start new shrinking cycle */
6251 if (DelayReached(&sound_delay, sound_delay_value))
6252 sound_delay_value = 30;
6255 if (MovDelay[x][y]) /* wait some time before shrinking */
6258 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6260 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6261 6 - MovDelay[x][y]);
6263 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6266 if (!MovDelay[x][y])
6268 Feld[x][y] = EL_EMPTY;
6269 DrawLevelField(x, y);
6271 /* don't let mole enter this field in this cycle;
6272 (give priority to objects falling to this field from above) */
6278 void AmoebeAbleger(int ax, int ay)
6281 int element = Feld[ax][ay];
6282 int graphic = el2img(element);
6283 int newax = ax, neway = ay;
6284 static int xy[4][2] =
6292 if (!level.amoeba_speed)
6294 Feld[ax][ay] = EL_AMOEBA_DEAD;
6295 DrawLevelField(ax, ay);
6299 if (IS_ANIMATED(graphic))
6300 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6302 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6303 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6305 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6308 if (MovDelay[ax][ay])
6312 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6315 int x = ax + xy[start][0];
6316 int y = ay + xy[start][1];
6318 if (!IN_LEV_FIELD(x, y))
6321 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6322 if (IS_FREE(x, y) ||
6323 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6329 if (newax == ax && neway == ay)
6332 else /* normal or "filled" (BD style) amoeba */
6335 boolean waiting_for_player = FALSE;
6337 for (i = 0; i < NUM_DIRECTIONS; i++)
6339 int j = (start + i) % 4;
6340 int x = ax + xy[j][0];
6341 int y = ay + xy[j][1];
6343 if (!IN_LEV_FIELD(x, y))
6346 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6347 if (IS_FREE(x, y) ||
6348 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6354 else if (IS_PLAYER(x, y))
6355 waiting_for_player = TRUE;
6358 if (newax == ax && neway == ay) /* amoeba cannot grow */
6360 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6362 Feld[ax][ay] = EL_AMOEBA_DEAD;
6363 DrawLevelField(ax, ay);
6364 AmoebaCnt[AmoebaNr[ax][ay]]--;
6366 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6368 if (element == EL_AMOEBA_FULL)
6369 AmoebeUmwandeln(ax, ay);
6370 else if (element == EL_BD_AMOEBA)
6371 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6376 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6378 /* amoeba gets larger by growing in some direction */
6380 int new_group_nr = AmoebaNr[ax][ay];
6383 if (new_group_nr == 0)
6385 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6386 printf("AmoebeAbleger(): This should never happen!\n");
6391 AmoebaNr[newax][neway] = new_group_nr;
6392 AmoebaCnt[new_group_nr]++;
6393 AmoebaCnt2[new_group_nr]++;
6395 /* if amoeba touches other amoeba(s) after growing, unify them */
6396 AmoebenVereinigen(newax, neway);
6398 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6400 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6406 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6407 (neway == lev_fieldy - 1 && newax != ax))
6409 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6410 Store[newax][neway] = element;
6412 else if (neway == ay)
6414 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6416 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6418 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6423 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6424 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6425 Store[ax][ay] = EL_AMOEBA_DROP;
6426 ContinueMoving(ax, ay);
6430 DrawLevelField(newax, neway);
6433 void Life(int ax, int ay)
6436 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6438 int element = Feld[ax][ay];
6439 int graphic = el2img(element);
6440 boolean changed = FALSE;
6442 if (IS_ANIMATED(graphic))
6443 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6448 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6449 MovDelay[ax][ay] = life_time;
6451 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6454 if (MovDelay[ax][ay])
6458 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6460 int xx = ax+x1, yy = ay+y1;
6463 if (!IN_LEV_FIELD(xx, yy))
6466 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6468 int x = xx+x2, y = yy+y2;
6470 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6473 if (((Feld[x][y] == element ||
6474 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6476 (IS_FREE(x, y) && Stop[x][y]))
6480 if (xx == ax && yy == ay) /* field in the middle */
6482 if (nachbarn < life[0] || nachbarn > life[1])
6484 Feld[xx][yy] = EL_EMPTY;
6486 DrawLevelField(xx, yy);
6487 Stop[xx][yy] = TRUE;
6491 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6492 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6493 { /* free border field */
6494 if (nachbarn >= life[2] && nachbarn <= life[3])
6496 Feld[xx][yy] = element;
6497 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6499 DrawLevelField(xx, yy);
6500 Stop[xx][yy] = TRUE;
6507 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6508 SND_GAME_OF_LIFE_GROWING);
6511 static void InitRobotWheel(int x, int y)
6513 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6516 static void RunRobotWheel(int x, int y)
6518 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6521 static void StopRobotWheel(int x, int y)
6523 if (ZX == x && ZY == y)
6527 static void InitTimegateWheel(int x, int y)
6529 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6532 static void RunTimegateWheel(int x, int y)
6534 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6537 void CheckExit(int x, int y)
6539 if (local_player->gems_still_needed > 0 ||
6540 local_player->sokobanfields_still_needed > 0 ||
6541 local_player->lights_still_needed > 0)
6543 int element = Feld[x][y];
6544 int graphic = el2img(element);
6546 if (IS_ANIMATED(graphic))
6547 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6552 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6555 Feld[x][y] = EL_EXIT_OPENING;
6557 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6560 void CheckExitSP(int x, int y)
6562 if (local_player->gems_still_needed > 0)
6564 int element = Feld[x][y];
6565 int graphic = el2img(element);
6567 if (IS_ANIMATED(graphic))
6568 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6573 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6576 Feld[x][y] = EL_SP_EXIT_OPENING;
6578 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6581 static void CloseAllOpenTimegates()
6585 for (y = 0; y < lev_fieldy; y++)
6587 for (x = 0; x < lev_fieldx; x++)
6589 int element = Feld[x][y];
6591 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6593 Feld[x][y] = EL_TIMEGATE_CLOSING;
6595 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6597 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6604 void EdelsteinFunkeln(int x, int y)
6606 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6609 if (Feld[x][y] == EL_BD_DIAMOND)
6612 if (MovDelay[x][y] == 0) /* next animation frame */
6613 MovDelay[x][y] = 11 * !SimpleRND(500);
6615 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6619 if (setup.direct_draw && MovDelay[x][y])
6620 SetDrawtoField(DRAW_BUFFERED);
6622 DrawLevelElementAnimation(x, y, Feld[x][y]);
6624 if (MovDelay[x][y] != 0)
6626 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6627 10 - MovDelay[x][y]);
6629 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6631 if (setup.direct_draw)
6635 dest_x = FX + SCREENX(x) * TILEX;
6636 dest_y = FY + SCREENY(y) * TILEY;
6638 BlitBitmap(drawto_field, window,
6639 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6640 SetDrawtoField(DRAW_DIRECT);
6646 void MauerWaechst(int x, int y)
6650 if (!MovDelay[x][y]) /* next animation frame */
6651 MovDelay[x][y] = 3 * delay;
6653 if (MovDelay[x][y]) /* wait some time before next frame */
6657 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6659 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6660 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6662 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6665 if (!MovDelay[x][y])
6667 if (MovDir[x][y] == MV_LEFT)
6669 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6670 DrawLevelField(x - 1, y);
6672 else if (MovDir[x][y] == MV_RIGHT)
6674 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6675 DrawLevelField(x + 1, y);
6677 else if (MovDir[x][y] == MV_UP)
6679 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6680 DrawLevelField(x, y - 1);
6684 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6685 DrawLevelField(x, y + 1);
6688 Feld[x][y] = Store[x][y];
6690 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6691 DrawLevelField(x, y);
6696 void MauerAbleger(int ax, int ay)
6698 int element = Feld[ax][ay];
6699 int graphic = el2img(element);
6700 boolean oben_frei = FALSE, unten_frei = FALSE;
6701 boolean links_frei = FALSE, rechts_frei = FALSE;
6702 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6703 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6704 boolean new_wall = FALSE;
6706 if (IS_ANIMATED(graphic))
6707 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6709 if (!MovDelay[ax][ay]) /* start building new wall */
6710 MovDelay[ax][ay] = 6;
6712 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6715 if (MovDelay[ax][ay])
6719 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6721 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6723 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6725 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6728 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6729 element == EL_EXPANDABLE_WALL_ANY)
6733 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6734 Store[ax][ay-1] = element;
6735 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6736 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6737 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6738 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6743 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6744 Store[ax][ay+1] = element;
6745 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6746 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6747 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6748 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6753 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6754 element == EL_EXPANDABLE_WALL_ANY ||
6755 element == EL_EXPANDABLE_WALL)
6759 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6760 Store[ax-1][ay] = element;
6761 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6762 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6763 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6764 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6770 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6771 Store[ax+1][ay] = element;
6772 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6773 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6774 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6775 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6780 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6781 DrawLevelField(ax, ay);
6783 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6785 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6786 unten_massiv = TRUE;
6787 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6788 links_massiv = TRUE;
6789 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6790 rechts_massiv = TRUE;
6792 if (((oben_massiv && unten_massiv) ||
6793 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6794 element == EL_EXPANDABLE_WALL) &&
6795 ((links_massiv && rechts_massiv) ||
6796 element == EL_EXPANDABLE_WALL_VERTICAL))
6797 Feld[ax][ay] = EL_WALL;
6801 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6803 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6807 void CheckForDragon(int x, int y)
6810 boolean dragon_found = FALSE;
6811 static int xy[4][2] =
6819 for (i = 0; i < NUM_DIRECTIONS; i++)
6821 for (j = 0; j < 4; j++)
6823 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6825 if (IN_LEV_FIELD(xx, yy) &&
6826 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6828 if (Feld[xx][yy] == EL_DRAGON)
6829 dragon_found = TRUE;
6838 for (i = 0; i < NUM_DIRECTIONS; i++)
6840 for (j = 0; j < 3; j++)
6842 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6844 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6846 Feld[xx][yy] = EL_EMPTY;
6847 DrawLevelField(xx, yy);
6856 static void InitBuggyBase(int x, int y)
6858 int element = Feld[x][y];
6859 int activating_delay = FRAMES_PER_SECOND / 4;
6862 (element == EL_SP_BUGGY_BASE ?
6863 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6864 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6866 element == EL_SP_BUGGY_BASE_ACTIVE ?
6867 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6870 static void WarnBuggyBase(int x, int y)
6873 static int xy[4][2] =
6881 for (i = 0; i < NUM_DIRECTIONS; i++)
6883 int xx = x + xy[i][0], yy = y + xy[i][1];
6885 if (IS_PLAYER(xx, yy))
6887 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6894 static void InitTrap(int x, int y)
6896 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6899 static void ActivateTrap(int x, int y)
6901 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6904 static void ChangeActiveTrap(int x, int y)
6906 int graphic = IMG_TRAP_ACTIVE;
6908 /* if new animation frame was drawn, correct crumbled sand border */
6909 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6910 DrawLevelFieldCrumbledSand(x, y);
6913 static void ChangeElementNowExt(int x, int y, int target_element)
6915 int previous_move_direction = MovDir[x][y];
6917 /* check if element under player changes from accessible to unaccessible
6918 (needed for special case of dropping element which then changes) */
6919 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6920 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6927 Feld[x][y] = target_element;
6929 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6931 ResetGfxAnimation(x, y);
6932 ResetRandomAnimationValue(x, y);
6934 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6935 MovDir[x][y] = previous_move_direction;
6938 InitField_WithBug1(x, y, FALSE);
6940 InitField(x, y, FALSE);
6941 if (CAN_MOVE(Feld[x][y]))
6945 DrawLevelField(x, y);
6947 if (GFX_CRUMBLED(Feld[x][y]))
6948 DrawLevelFieldCrumbledSandNeighbours(x, y);
6950 TestIfBadThingTouchesHero(x, y);
6951 TestIfPlayerTouchesCustomElement(x, y);
6952 TestIfElementTouchesCustomElement(x, y);
6954 if (ELEM_IS_PLAYER(target_element))
6955 RelocatePlayer(x, y, target_element);
6958 static boolean ChangeElementNow(int x, int y, int element, int page)
6960 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6963 /* always use default change event to prevent running into a loop */
6964 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6965 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6967 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
6969 /* reset actual trigger element and player */
6970 change->actual_trigger_element = EL_EMPTY;
6971 change->actual_trigger_player = EL_PLAYER_1;
6974 /* do not change already changed elements with same change event */
6976 if (Changed[x][y] & ChangeEvent[x][y])
6983 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6985 CheckTriggeredElementChangePage(x,y, Feld[x][y], CE_OTHER_IS_CHANGING, page);
6987 if (change->explode)
6994 if (change->use_target_content)
6996 boolean complete_replace = TRUE;
6997 boolean can_replace[3][3];
7000 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7003 boolean is_diggable;
7004 boolean is_destructible;
7005 int ex = x + xx - 1;
7006 int ey = y + yy - 1;
7007 int content_element = change->target_content[xx][yy];
7010 can_replace[xx][yy] = TRUE;
7012 if (ex == x && ey == y) /* do not check changing element itself */
7015 if (content_element == EL_EMPTY_SPACE)
7017 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7022 if (!IN_LEV_FIELD(ex, ey))
7024 can_replace[xx][yy] = FALSE;
7025 complete_replace = FALSE;
7032 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7033 e = MovingOrBlocked2Element(ex, ey);
7036 is_empty = (IS_FREE(ex, ey) || (IS_PLAYER(ex, ey) &&
7037 IS_WALKABLE(content_element)));
7038 is_diggable = (is_empty || IS_DIGGABLE(e));
7039 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7041 can_replace[xx][yy] =
7042 ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7043 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7044 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7046 if (!can_replace[xx][yy])
7047 complete_replace = FALSE;
7049 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7050 IS_WALKABLE(content_element)));
7052 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7054 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7057 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7058 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7059 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7061 can_replace[xx][yy] = FALSE;
7062 complete_replace = FALSE;
7067 if (!change->only_if_complete || complete_replace)
7069 boolean something_has_changed = FALSE;
7071 if (change->only_if_complete && change->use_random_replace &&
7072 RND(100) < change->random_percentage)
7075 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7077 int ex = x + xx - 1;
7078 int ey = y + yy - 1;
7079 int content_element;
7081 if (can_replace[xx][yy] && (!change->use_random_replace ||
7082 RND(100) < change->random_percentage))
7084 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7085 RemoveMovingField(ex, ey);
7087 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7089 content_element = change->target_content[xx][yy];
7090 target_element = GET_TARGET_ELEMENT(content_element, change);
7092 ChangeElementNowExt(ex, ey, target_element);
7094 something_has_changed = TRUE;
7096 /* for symmetry reasons, freeze newly created border elements */
7097 if (ex != x || ey != y)
7098 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7102 if (something_has_changed)
7103 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7108 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7110 ChangeElementNowExt(x, y, target_element);
7112 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7118 static void ChangeElement(int x, int y, int page)
7120 int element = MovingOrBlocked2Element(x, y);
7121 struct ElementInfo *ei = &element_info[element];
7122 struct ElementChangeInfo *change = &ei->change_page[page];
7125 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7128 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7129 x, y, element, element_info[element].token_name);
7130 printf("ChangeElement(): This should never happen!\n");
7135 /* this can happen with classic bombs on walkable, changing elements */
7136 if (!CAN_CHANGE(element))
7139 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7140 ChangeDelay[x][y] = 0;
7146 if (ChangeDelay[x][y] == 0) /* initialize element change */
7148 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7149 RND(change->delay_random * change->delay_frames)) + 1;
7151 ResetGfxAnimation(x, y);
7152 ResetRandomAnimationValue(x, y);
7154 if (change->pre_change_function)
7155 change->pre_change_function(x, y);
7158 ChangeDelay[x][y]--;
7160 if (ChangeDelay[x][y] != 0) /* continue element change */
7162 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7164 if (IS_ANIMATED(graphic))
7165 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7167 if (change->change_function)
7168 change->change_function(x, y);
7170 else /* finish element change */
7172 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7174 page = ChangePage[x][y];
7175 ChangePage[x][y] = -1;
7177 change = &ei->change_page[page];
7181 if (IS_MOVING(x, y) && !change->explode)
7183 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7186 ChangeDelay[x][y] = 1; /* try change after next move step */
7187 ChangePage[x][y] = page; /* remember page to use for change */
7192 if (ChangeElementNow(x, y, element, page))
7194 if (change->post_change_function)
7195 change->post_change_function(x, y);
7200 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7201 int trigger_element,
7208 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7210 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7213 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7215 int element = EL_CUSTOM_START + i;
7217 boolean change_element = FALSE;
7220 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7223 for (j = 0; j < element_info[element].num_change_pages; j++)
7225 struct ElementChangeInfo *change = &element_info[element].change_page[j];
7227 if (change->can_change &&
7228 change->events & CH_EVENT_BIT(trigger_event) &&
7229 change->trigger_side & trigger_side &&
7230 change->trigger_player & trigger_player &&
7231 change->trigger_page & trigger_page_bits &&
7232 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7235 if (!(change->events & CH_EVENT_BIT(trigger_event)))
7236 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7237 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7240 change_element = TRUE;
7243 change->actual_trigger_element = trigger_element;
7244 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7250 if (!change_element)
7253 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7256 if (x == lx && y == ly) /* do not change trigger element itself */
7260 if (Feld[x][y] == element)
7262 ChangeDelay[x][y] = 1;
7263 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7264 ChangeElement(x, y, page);
7272 static boolean CheckElementChangeExt(int x, int y,
7274 int trigger_element,
7280 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7283 if (Feld[x][y] == EL_BLOCKED)
7285 Blocked2Moving(x, y, &x, &y);
7286 element = Feld[x][y];
7290 if (Feld[x][y] != element) /* check if element has already changed */
7293 printf("::: %d ('%s') != %d ('%s') [%d]\n",
7294 Feld[x][y], element_info[Feld[x][y]].token_name,
7295 element, element_info[element].token_name,
7304 if (trigger_page < 0)
7306 boolean change_element = FALSE;
7309 for (i = 0; i < element_info[element].num_change_pages; i++)
7311 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7313 if (change->can_change &&
7314 change->events & CH_EVENT_BIT(trigger_event) &&
7315 change->trigger_side & trigger_side &&
7316 change->trigger_player & trigger_player)
7318 change_element = TRUE;
7321 change->actual_trigger_element = trigger_element;
7322 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7328 if (!change_element)
7333 struct ElementInfo *ei = &element_info[element];
7334 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7336 change->actual_trigger_element = trigger_element;
7337 change->actual_trigger_player = EL_PLAYER_1; /* unused */
7342 /* !!! this check misses pages with same event, but different side !!! */
7344 if (trigger_page < 0)
7345 trigger_page = element_info[element].event_page_nr[trigger_event];
7347 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7351 ChangeDelay[x][y] = 1;
7352 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7353 ChangeElement(x, y, trigger_page);
7358 static void PlayPlayerSound(struct PlayerInfo *player)
7360 int jx = player->jx, jy = player->jy;
7361 int element = player->element_nr;
7362 int last_action = player->last_action_waiting;
7363 int action = player->action_waiting;
7365 if (player->is_waiting)
7367 if (action != last_action)
7368 PlayLevelSoundElementAction(jx, jy, element, action);
7370 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7374 if (action != last_action)
7375 StopSound(element_info[element].sound[last_action]);
7377 if (last_action == ACTION_SLEEPING)
7378 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7382 static void PlayAllPlayersSound()
7386 for (i = 0; i < MAX_PLAYERS; i++)
7387 if (stored_player[i].active)
7388 PlayPlayerSound(&stored_player[i]);
7391 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7393 boolean last_waiting = player->is_waiting;
7394 int move_dir = player->MovDir;
7396 player->last_action_waiting = player->action_waiting;
7400 if (!last_waiting) /* not waiting -> waiting */
7402 player->is_waiting = TRUE;
7404 player->frame_counter_bored =
7406 game.player_boring_delay_fixed +
7407 SimpleRND(game.player_boring_delay_random);
7408 player->frame_counter_sleeping =
7410 game.player_sleeping_delay_fixed +
7411 SimpleRND(game.player_sleeping_delay_random);
7413 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7416 if (game.player_sleeping_delay_fixed +
7417 game.player_sleeping_delay_random > 0 &&
7418 player->anim_delay_counter == 0 &&
7419 player->post_delay_counter == 0 &&
7420 FrameCounter >= player->frame_counter_sleeping)
7421 player->is_sleeping = TRUE;
7422 else if (game.player_boring_delay_fixed +
7423 game.player_boring_delay_random > 0 &&
7424 FrameCounter >= player->frame_counter_bored)
7425 player->is_bored = TRUE;
7427 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7428 player->is_bored ? ACTION_BORING :
7431 if (player->is_sleeping)
7433 if (player->num_special_action_sleeping > 0)
7435 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7437 int last_special_action = player->special_action_sleeping;
7438 int num_special_action = player->num_special_action_sleeping;
7439 int special_action =
7440 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7441 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7442 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7443 last_special_action + 1 : ACTION_SLEEPING);
7444 int special_graphic =
7445 el_act_dir2img(player->element_nr, special_action, move_dir);
7447 player->anim_delay_counter =
7448 graphic_info[special_graphic].anim_delay_fixed +
7449 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7450 player->post_delay_counter =
7451 graphic_info[special_graphic].post_delay_fixed +
7452 SimpleRND(graphic_info[special_graphic].post_delay_random);
7454 player->special_action_sleeping = special_action;
7457 if (player->anim_delay_counter > 0)
7459 player->action_waiting = player->special_action_sleeping;
7460 player->anim_delay_counter--;
7462 else if (player->post_delay_counter > 0)
7464 player->post_delay_counter--;
7468 else if (player->is_bored)
7470 if (player->num_special_action_bored > 0)
7472 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7474 int special_action =
7475 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7476 int special_graphic =
7477 el_act_dir2img(player->element_nr, special_action, move_dir);
7479 player->anim_delay_counter =
7480 graphic_info[special_graphic].anim_delay_fixed +
7481 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7482 player->post_delay_counter =
7483 graphic_info[special_graphic].post_delay_fixed +
7484 SimpleRND(graphic_info[special_graphic].post_delay_random);
7486 player->special_action_bored = special_action;
7489 if (player->anim_delay_counter > 0)
7491 player->action_waiting = player->special_action_bored;
7492 player->anim_delay_counter--;
7494 else if (player->post_delay_counter > 0)
7496 player->post_delay_counter--;
7501 else if (last_waiting) /* waiting -> not waiting */
7503 player->is_waiting = FALSE;
7504 player->is_bored = FALSE;
7505 player->is_sleeping = FALSE;
7507 player->frame_counter_bored = -1;
7508 player->frame_counter_sleeping = -1;
7510 player->anim_delay_counter = 0;
7511 player->post_delay_counter = 0;
7513 player->action_waiting = ACTION_DEFAULT;
7515 player->special_action_bored = ACTION_DEFAULT;
7516 player->special_action_sleeping = ACTION_DEFAULT;
7521 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7524 static byte stored_player_action[MAX_PLAYERS];
7525 static int num_stored_actions = 0;
7527 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7528 int left = player_action & JOY_LEFT;
7529 int right = player_action & JOY_RIGHT;
7530 int up = player_action & JOY_UP;
7531 int down = player_action & JOY_DOWN;
7532 int button1 = player_action & JOY_BUTTON_1;
7533 int button2 = player_action & JOY_BUTTON_2;
7534 int dx = (left ? -1 : right ? 1 : 0);
7535 int dy = (up ? -1 : down ? 1 : 0);
7538 stored_player_action[player->index_nr] = 0;
7539 num_stored_actions++;
7543 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7546 if (!player->active || tape.pausing)
7550 printf("::: [%d %d %d %d] [%d %d]\n",
7551 left, right, up, down, button1, button2);
7557 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7562 if (player->MovPos == 0)
7563 CheckGravityMovement(player);
7566 snapped = SnapField(player, dx, dy);
7570 dropped = DropElement(player);
7572 moved = MovePlayer(player, dx, dy);
7575 if (tape.single_step && tape.recording && !tape.pausing)
7577 if (button1 || (dropped && !moved))
7579 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7580 SnapField(player, 0, 0); /* stop snapping */
7584 SetPlayerWaiting(player, FALSE);
7587 return player_action;
7589 stored_player_action[player->index_nr] = player_action;
7595 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7598 /* no actions for this player (no input at player's configured device) */
7600 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7601 SnapField(player, 0, 0);
7602 CheckGravityMovementWhenNotMoving(player);
7604 if (player->MovPos == 0)
7605 SetPlayerWaiting(player, TRUE);
7607 if (player->MovPos == 0) /* needed for tape.playing */
7608 player->is_moving = FALSE;
7610 player->is_dropping = FALSE;
7616 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7618 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7620 TapeRecordAction(stored_player_action);
7621 num_stored_actions = 0;
7628 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7630 static byte stored_player_action[MAX_PLAYERS];
7631 static int num_stored_actions = 0;
7632 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7633 int left = player_action & JOY_LEFT;
7634 int right = player_action & JOY_RIGHT;
7635 int up = player_action & JOY_UP;
7636 int down = player_action & JOY_DOWN;
7637 int button1 = player_action & JOY_BUTTON_1;
7638 int button2 = player_action & JOY_BUTTON_2;
7639 int dx = (left ? -1 : right ? 1 : 0);
7640 int dy = (up ? -1 : down ? 1 : 0);
7642 stored_player_action[player->index_nr] = 0;
7643 num_stored_actions++;
7645 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7647 if (!player->active || tape.pausing)
7652 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7655 snapped = SnapField(player, dx, dy);
7659 dropped = DropElement(player);
7661 moved = MovePlayer(player, dx, dy);
7664 if (tape.single_step && tape.recording && !tape.pausing)
7666 if (button1 || (dropped && !moved))
7668 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7669 SnapField(player, 0, 0); /* stop snapping */
7673 stored_player_action[player->index_nr] = player_action;
7677 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7679 /* no actions for this player (no input at player's configured device) */
7681 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7682 SnapField(player, 0, 0);
7683 CheckGravityMovementWhenNotMoving(player);
7685 if (player->MovPos == 0)
7686 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7688 if (player->MovPos == 0) /* needed for tape.playing */
7689 player->is_moving = FALSE;
7692 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7694 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7696 TapeRecordAction(stored_player_action);
7697 num_stored_actions = 0;
7704 static unsigned long action_delay = 0;
7705 unsigned long action_delay_value;
7706 int magic_wall_x = 0, magic_wall_y = 0;
7707 int i, x, y, element, graphic;
7708 byte *recorded_player_action;
7709 byte summarized_player_action = 0;
7711 byte tape_action[MAX_PLAYERS];
7714 if (game_status != GAME_MODE_PLAYING)
7717 action_delay_value =
7718 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7720 if (tape.playing && tape.index_search && !tape.pausing)
7721 action_delay_value = 0;
7723 /* ---------- main game synchronization point ---------- */
7725 WaitUntilDelayReached(&action_delay, action_delay_value);
7727 if (network_playing && !network_player_action_received)
7731 printf("DEBUG: try to get network player actions in time\n");
7735 #if defined(PLATFORM_UNIX)
7736 /* last chance to get network player actions without main loop delay */
7740 if (game_status != GAME_MODE_PLAYING)
7743 if (!network_player_action_received)
7747 printf("DEBUG: failed to get network player actions in time\n");
7758 printf("::: getting new tape action [%d]\n", FrameCounter);
7761 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7764 if (recorded_player_action == NULL && tape.pausing)
7769 printf("::: %d\n", stored_player[0].action);
7773 if (recorded_player_action != NULL)
7774 for (i = 0; i < MAX_PLAYERS; i++)
7775 stored_player[i].action = recorded_player_action[i];
7778 for (i = 0; i < MAX_PLAYERS; i++)
7780 summarized_player_action |= stored_player[i].action;
7782 if (!network_playing)
7783 stored_player[i].effective_action = stored_player[i].action;
7786 #if defined(PLATFORM_UNIX)
7787 if (network_playing)
7788 SendToServer_MovePlayer(summarized_player_action);
7791 if (!options.network && !setup.team_mode)
7792 local_player->effective_action = summarized_player_action;
7795 if (recorded_player_action != NULL)
7796 for (i = 0; i < MAX_PLAYERS; i++)
7797 stored_player[i].effective_action = recorded_player_action[i];
7801 for (i = 0; i < MAX_PLAYERS; i++)
7803 tape_action[i] = stored_player[i].effective_action;
7805 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7806 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7809 /* only save actions from input devices, but not programmed actions */
7811 TapeRecordAction(tape_action);
7814 for (i = 0; i < MAX_PLAYERS; i++)
7816 int actual_player_action = stored_player[i].effective_action;
7819 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
7820 - rnd_equinox_tetrachloride 048
7821 - rnd_equinox_tetrachloride_ii 096
7822 - rnd_emanuel_schmieg 002
7823 - doctor_sloan_ww 001, 020
7825 if (stored_player[i].MovPos == 0)
7826 CheckGravityMovement(&stored_player[i]);
7830 /* overwrite programmed action with tape action */
7831 if (stored_player[i].programmed_action)
7832 actual_player_action = stored_player[i].programmed_action;
7836 if (stored_player[i].programmed_action)
7837 printf("::: %d\n", stored_player[i].programmed_action);
7840 if (recorded_player_action)
7843 if (stored_player[i].programmed_action &&
7844 stored_player[i].programmed_action != recorded_player_action[i])
7845 printf("::: %d: %d <-> %d\n", i,
7846 stored_player[i].programmed_action, recorded_player_action[i]);
7850 actual_player_action = recorded_player_action[i];
7855 /* overwrite tape action with programmed action */
7856 if (stored_player[i].programmed_action)
7857 actual_player_action = stored_player[i].programmed_action;
7862 printf("::: action: %d: %x [%d]\n",
7863 stored_player[i].MovPos, actual_player_action, FrameCounter);
7867 PlayerActions(&stored_player[i], actual_player_action);
7869 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7871 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7872 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7875 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7880 TapeRecordAction(tape_action);
7883 network_player_action_received = FALSE;
7885 ScrollScreen(NULL, SCROLL_GO_ON);
7891 for (i = 0; i < MAX_PLAYERS; i++)
7892 stored_player[i].Frame++;
7896 /* for downwards compatibility, the following code emulates a fixed bug that
7897 occured when pushing elements (causing elements that just made their last
7898 pushing step to already (if possible) make their first falling step in the
7899 same game frame, which is bad); this code is also needed to use the famous
7900 "spring push bug" which is used in older levels and might be wanted to be
7901 used also in newer levels, but in this case the buggy pushing code is only
7902 affecting the "spring" element and no other elements */
7905 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7907 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7910 for (i = 0; i < MAX_PLAYERS; i++)
7912 struct PlayerInfo *player = &stored_player[i];
7917 if (player->active && player->is_pushing && player->is_moving &&
7919 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7920 Feld[x][y] == EL_SPRING))
7922 if (player->active && player->is_pushing && player->is_moving &&
7926 ContinueMoving(x, y);
7928 /* continue moving after pushing (this is actually a bug) */
7929 if (!IS_MOVING(x, y))
7938 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7940 Changed[x][y] = CE_BITMASK_DEFAULT;
7941 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7944 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7946 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7947 printf("GameActions(): This should never happen!\n");
7949 ChangePage[x][y] = -1;
7954 if (WasJustMoving[x][y] > 0)
7955 WasJustMoving[x][y]--;
7956 if (WasJustFalling[x][y] > 0)
7957 WasJustFalling[x][y]--;
7962 /* reset finished pushing action (not done in ContinueMoving() to allow
7963 continous pushing animation for elements with zero push delay) */
7964 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7966 ResetGfxAnimation(x, y);
7967 DrawLevelField(x, y);
7972 if (IS_BLOCKED(x, y))
7976 Blocked2Moving(x, y, &oldx, &oldy);
7977 if (!IS_MOVING(oldx, oldy))
7979 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7980 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7981 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7982 printf("GameActions(): This should never happen!\n");
7988 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7990 element = Feld[x][y];
7992 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7994 graphic = el2img(element);
8000 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8002 element = graphic = 0;
8006 if (graphic_info[graphic].anim_global_sync)
8007 GfxFrame[x][y] = FrameCounter;
8009 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8010 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8011 ResetRandomAnimationValue(x, y);
8013 SetRandomAnimationValue(x, y);
8016 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8019 if (IS_INACTIVE(element))
8021 if (IS_ANIMATED(graphic))
8022 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8028 /* this may take place after moving, so 'element' may have changed */
8030 if (IS_CHANGING(x, y))
8032 if (IS_CHANGING(x, y) &&
8033 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8037 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8038 element_info[element].event_page_nr[CE_DELAY]);
8040 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8043 element = Feld[x][y];
8044 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8048 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8053 element = Feld[x][y];
8054 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8056 if (element == EL_MOLE)
8057 printf("::: %d, %d, %d [%d]\n",
8058 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8062 if (element == EL_YAMYAM)
8063 printf("::: %d, %d, %d\n",
8064 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8068 if (IS_ANIMATED(graphic) &&
8072 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8075 if (element == EL_BUG)
8076 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8080 if (element == EL_MOLE)
8081 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8085 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8086 EdelsteinFunkeln(x, y);
8088 else if ((element == EL_ACID ||
8089 element == EL_EXIT_OPEN ||
8090 element == EL_SP_EXIT_OPEN ||
8091 element == EL_SP_TERMINAL ||
8092 element == EL_SP_TERMINAL_ACTIVE ||
8093 element == EL_EXTRA_TIME ||
8094 element == EL_SHIELD_NORMAL ||
8095 element == EL_SHIELD_DEADLY) &&
8096 IS_ANIMATED(graphic))
8097 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8098 else if (IS_MOVING(x, y))
8099 ContinueMoving(x, y);
8100 else if (IS_ACTIVE_BOMB(element))
8101 CheckDynamite(x, y);
8103 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8104 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
8106 else if (element == EL_AMOEBA_GROWING)
8107 AmoebeWaechst(x, y);
8108 else if (element == EL_AMOEBA_SHRINKING)
8109 AmoebaDisappearing(x, y);
8111 #if !USE_NEW_AMOEBA_CODE
8112 else if (IS_AMOEBALIVE(element))
8113 AmoebeAbleger(x, y);
8116 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8118 else if (element == EL_EXIT_CLOSED)
8120 else if (element == EL_SP_EXIT_CLOSED)
8122 else if (element == EL_EXPANDABLE_WALL_GROWING)
8124 else if (element == EL_EXPANDABLE_WALL ||
8125 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8126 element == EL_EXPANDABLE_WALL_VERTICAL ||
8127 element == EL_EXPANDABLE_WALL_ANY)
8129 else if (element == EL_FLAMES)
8130 CheckForDragon(x, y);
8132 else if (IS_AUTO_CHANGING(element))
8133 ChangeElement(x, y);
8135 else if (element == EL_EXPLOSION)
8136 ; /* drawing of correct explosion animation is handled separately */
8137 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8138 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8141 /* this may take place after moving, so 'element' may have changed */
8142 if (IS_AUTO_CHANGING(Feld[x][y]))
8143 ChangeElement(x, y);
8146 if (IS_BELT_ACTIVE(element))
8147 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8149 if (game.magic_wall_active)
8151 int jx = local_player->jx, jy = local_player->jy;
8153 /* play the element sound at the position nearest to the player */
8154 if ((element == EL_MAGIC_WALL_FULL ||
8155 element == EL_MAGIC_WALL_ACTIVE ||
8156 element == EL_MAGIC_WALL_EMPTYING ||
8157 element == EL_BD_MAGIC_WALL_FULL ||
8158 element == EL_BD_MAGIC_WALL_ACTIVE ||
8159 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8160 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8168 #if USE_NEW_AMOEBA_CODE
8169 /* new experimental amoeba growth stuff */
8171 if (!(FrameCounter % 8))
8174 static unsigned long random = 1684108901;
8176 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8179 x = (random >> 10) % lev_fieldx;
8180 y = (random >> 20) % lev_fieldy;
8182 x = RND(lev_fieldx);
8183 y = RND(lev_fieldy);
8185 element = Feld[x][y];
8187 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8188 if (!IS_PLAYER(x,y) &&
8189 (element == EL_EMPTY ||
8190 element == EL_SAND ||
8191 element == EL_QUICKSAND_EMPTY ||
8192 element == EL_ACID_SPLASH_LEFT ||
8193 element == EL_ACID_SPLASH_RIGHT))
8195 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8196 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8197 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8198 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8199 Feld[x][y] = EL_AMOEBA_DROP;
8202 random = random * 129 + 1;
8208 if (game.explosions_delayed)
8211 game.explosions_delayed = FALSE;
8213 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8215 element = Feld[x][y];
8217 if (ExplodeField[x][y])
8218 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8219 else if (element == EL_EXPLOSION)
8220 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
8222 ExplodeField[x][y] = EX_NO_EXPLOSION;
8225 game.explosions_delayed = TRUE;
8228 if (game.magic_wall_active)
8230 if (!(game.magic_wall_time_left % 4))
8232 int element = Feld[magic_wall_x][magic_wall_y];
8234 if (element == EL_BD_MAGIC_WALL_FULL ||
8235 element == EL_BD_MAGIC_WALL_ACTIVE ||
8236 element == EL_BD_MAGIC_WALL_EMPTYING)
8237 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8239 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8242 if (game.magic_wall_time_left > 0)
8244 game.magic_wall_time_left--;
8245 if (!game.magic_wall_time_left)
8247 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8249 element = Feld[x][y];
8251 if (element == EL_MAGIC_WALL_ACTIVE ||
8252 element == EL_MAGIC_WALL_FULL)
8254 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8255 DrawLevelField(x, y);
8257 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8258 element == EL_BD_MAGIC_WALL_FULL)
8260 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8261 DrawLevelField(x, y);
8265 game.magic_wall_active = FALSE;
8270 if (game.light_time_left > 0)
8272 game.light_time_left--;
8274 if (game.light_time_left == 0)
8275 RedrawAllLightSwitchesAndInvisibleElements();
8278 if (game.timegate_time_left > 0)
8280 game.timegate_time_left--;
8282 if (game.timegate_time_left == 0)
8283 CloseAllOpenTimegates();
8286 for (i = 0; i < MAX_PLAYERS; i++)
8288 struct PlayerInfo *player = &stored_player[i];
8290 if (SHIELD_ON(player))
8292 if (player->shield_deadly_time_left)
8293 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8294 else if (player->shield_normal_time_left)
8295 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8299 if (TimeFrames >= FRAMES_PER_SECOND)
8304 if (!level.use_step_counter)
8308 for (i = 0; i < MAX_PLAYERS; i++)
8310 struct PlayerInfo *player = &stored_player[i];
8312 if (SHIELD_ON(player))
8314 player->shield_normal_time_left--;
8316 if (player->shield_deadly_time_left > 0)
8317 player->shield_deadly_time_left--;
8325 if (TimeLeft <= 10 && setup.time_limit)
8326 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8328 DrawGameValue_Time(TimeLeft);
8330 if (!TimeLeft && setup.time_limit)
8331 for (i = 0; i < MAX_PLAYERS; i++)
8332 KillHero(&stored_player[i]);
8334 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8335 DrawGameValue_Time(TimePlayed);
8338 if (tape.recording || tape.playing)
8339 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8343 PlayAllPlayersSound();
8345 if (options.debug) /* calculate frames per second */
8347 static unsigned long fps_counter = 0;
8348 static int fps_frames = 0;
8349 unsigned long fps_delay_ms = Counter() - fps_counter;
8353 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8355 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8358 fps_counter = Counter();
8361 redraw_mask |= REDRAW_FPS;
8365 if (stored_player[0].jx != stored_player[0].last_jx ||
8366 stored_player[0].jy != stored_player[0].last_jy)
8367 printf("::: %d, %d, %d, %d, %d\n",
8368 stored_player[0].MovDir,
8369 stored_player[0].MovPos,
8370 stored_player[0].GfxPos,
8371 stored_player[0].Frame,
8372 stored_player[0].StepFrame);
8379 for (i = 0; i < MAX_PLAYERS; i++)
8382 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8384 stored_player[i].Frame += move_frames;
8386 if (stored_player[i].MovPos != 0)
8387 stored_player[i].StepFrame += move_frames;
8389 if (stored_player[i].drop_delay > 0)
8390 stored_player[i].drop_delay--;
8395 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8397 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8399 local_player->show_envelope = 0;
8404 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8406 int min_x = x, min_y = y, max_x = x, max_y = y;
8409 for (i = 0; i < MAX_PLAYERS; i++)
8411 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8413 if (!stored_player[i].active || &stored_player[i] == player)
8416 min_x = MIN(min_x, jx);
8417 min_y = MIN(min_y, jy);
8418 max_x = MAX(max_x, jx);
8419 max_y = MAX(max_y, jy);
8422 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8425 static boolean AllPlayersInVisibleScreen()
8429 for (i = 0; i < MAX_PLAYERS; i++)
8431 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8433 if (!stored_player[i].active)
8436 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8443 void ScrollLevel(int dx, int dy)
8445 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8448 BlitBitmap(drawto_field, drawto_field,
8449 FX + TILEX * (dx == -1) - softscroll_offset,
8450 FY + TILEY * (dy == -1) - softscroll_offset,
8451 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8452 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8453 FX + TILEX * (dx == 1) - softscroll_offset,
8454 FY + TILEY * (dy == 1) - softscroll_offset);
8458 x = (dx == 1 ? BX1 : BX2);
8459 for (y = BY1; y <= BY2; y++)
8460 DrawScreenField(x, y);
8465 y = (dy == 1 ? BY1 : BY2);
8466 for (x = BX1; x <= BX2; x++)
8467 DrawScreenField(x, y);
8470 redraw_mask |= REDRAW_FIELD;
8473 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8475 int nextx = x + dx, nexty = y + dy;
8476 int element = Feld[x][y];
8479 element != EL_SP_PORT_LEFT &&
8480 element != EL_SP_GRAVITY_PORT_LEFT &&
8481 element != EL_SP_PORT_HORIZONTAL &&
8482 element != EL_SP_PORT_ANY) ||
8484 element != EL_SP_PORT_RIGHT &&
8485 element != EL_SP_GRAVITY_PORT_RIGHT &&
8486 element != EL_SP_PORT_HORIZONTAL &&
8487 element != EL_SP_PORT_ANY) ||
8489 element != EL_SP_PORT_UP &&
8490 element != EL_SP_GRAVITY_PORT_UP &&
8491 element != EL_SP_PORT_VERTICAL &&
8492 element != EL_SP_PORT_ANY) ||
8494 element != EL_SP_PORT_DOWN &&
8495 element != EL_SP_GRAVITY_PORT_DOWN &&
8496 element != EL_SP_PORT_VERTICAL &&
8497 element != EL_SP_PORT_ANY) ||
8498 !IN_LEV_FIELD(nextx, nexty) ||
8499 !IS_FREE(nextx, nexty))
8505 static void CheckGravityMovement(struct PlayerInfo *player)
8507 if (game.gravity && !player->programmed_action)
8510 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8511 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8513 int move_dir_horizontal = player->action & MV_HORIZONTAL;
8514 int move_dir_vertical = player->action & MV_VERTICAL;
8517 (player->last_move_dir & MV_HORIZONTAL ?
8518 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8519 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8520 int jx = player->jx, jy = player->jy;
8521 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8522 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8523 int new_jx = jx + dx, new_jy = jy + dy;
8525 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8527 boolean player_is_snapping = player->action & JOY_BUTTON_1;
8530 boolean player_can_fall_down =
8531 (IN_LEV_FIELD(jx, jy + 1) &&
8532 (IS_FREE(jx, jy + 1) ||
8533 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
8535 boolean player_can_fall_down =
8536 (IN_LEV_FIELD(jx, jy + 1) &&
8537 (IS_FREE(jx, jy + 1)));
8539 boolean player_is_moving_to_valid_field =
8542 !player_is_snapping &&
8544 IN_LEV_FIELD(new_jx, new_jy) &&
8545 (Feld[new_jx][new_jy] == EL_SP_BASE ||
8546 Feld[new_jx][new_jy] == EL_SAND ||
8547 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8548 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
8549 /* !!! extend EL_SAND to anything diggable !!! */
8551 boolean player_is_standing_on_valid_field =
8552 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8553 (IS_WALKABLE(Feld[jx][jy]) &&
8554 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8557 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
8558 player_can_fall_down,
8559 player_is_standing_on_valid_field,
8560 player_is_moving_to_valid_field,
8561 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
8562 player->effective_action,
8563 player->can_fall_into_acid);
8566 if (player_can_fall_down &&
8567 !player_is_standing_on_valid_field &&
8568 !player_is_moving_to_valid_field)
8571 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
8572 jx, jy, FrameCounter);
8575 player->programmed_action = MV_DOWN;
8580 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8583 return CheckGravityMovement(player);
8586 if (game.gravity && !player->programmed_action)
8588 int jx = player->jx, jy = player->jy;
8589 boolean field_under_player_is_free =
8590 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8591 boolean player_is_standing_on_valid_field =
8592 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8593 (IS_WALKABLE(Feld[jx][jy]) &&
8594 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8596 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8597 player->programmed_action = MV_DOWN;
8603 -----------------------------------------------------------------------------
8604 dx, dy: direction (non-diagonal) to try to move the player to
8605 real_dx, real_dy: direction as read from input device (can be diagonal)
8608 boolean MovePlayerOneStep(struct PlayerInfo *player,
8609 int dx, int dy, int real_dx, int real_dy)
8612 static int trigger_sides[4][2] =
8614 /* enter side leave side */
8615 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8616 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8617 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8618 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8620 int move_direction = (dx == -1 ? MV_LEFT :
8621 dx == +1 ? MV_RIGHT :
8623 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8624 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8625 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8627 int jx = player->jx, jy = player->jy;
8628 int new_jx = jx + dx, new_jy = jy + dy;
8632 if (!player->active || (!dx && !dy))
8633 return MF_NO_ACTION;
8635 player->MovDir = (dx < 0 ? MV_LEFT :
8638 dy > 0 ? MV_DOWN : MV_NO_MOVING);
8640 if (!IN_LEV_FIELD(new_jx, new_jy))
8641 return MF_NO_ACTION;
8643 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8644 return MF_NO_ACTION;
8647 element = MovingOrBlocked2Element(new_jx, new_jy);
8649 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8652 if (DONT_RUN_INTO(element))
8654 if (element == EL_ACID && dx == 0 && dy == 1)
8656 SplashAcid(new_jx, new_jy);
8657 Feld[jx][jy] = EL_PLAYER_1;
8658 InitMovingField(jx, jy, MV_DOWN);
8659 Store[jx][jy] = EL_ACID;
8660 ContinueMoving(jx, jy);
8664 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8669 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8670 if (can_move != MF_MOVING)
8673 /* check if DigField() has caused relocation of the player */
8674 if (player->jx != jx || player->jy != jy)
8675 return MF_NO_ACTION;
8677 StorePlayer[jx][jy] = 0;
8678 player->last_jx = jx;
8679 player->last_jy = jy;
8680 player->jx = new_jx;
8681 player->jy = new_jy;
8682 StorePlayer[new_jx][new_jy] = player->element_nr;
8685 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8687 player->step_counter++;
8689 player->drop_delay = 0;
8691 PlayerVisit[jx][jy] = FrameCounter;
8693 ScrollPlayer(player, SCROLL_INIT);
8696 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8698 CheckTriggeredElementChangeSide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
8700 CheckElementChangeSide(jx, jy, Feld[jx][jy], CE_LEFT_BY_PLAYER,leave_side);
8703 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
8705 CheckTriggeredElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8706 CE_OTHER_GETS_ENTERED, enter_side);
8707 CheckElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8708 CE_ENTERED_BY_PLAYER, enter_side);
8715 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8717 int jx = player->jx, jy = player->jy;
8718 int old_jx = jx, old_jy = jy;
8719 int moved = MF_NO_ACTION;
8722 if (!player->active)
8727 if (player->MovPos == 0)
8729 player->is_moving = FALSE;
8730 player->is_digging = FALSE;
8731 player->is_collecting = FALSE;
8732 player->is_snapping = FALSE;
8733 player->is_pushing = FALSE;
8739 if (!player->active || (!dx && !dy))
8744 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8750 if (!FrameReached(&player->move_delay, player->move_delay_value))
8753 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8754 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
8760 /* remove the last programmed player action */
8761 player->programmed_action = 0;
8765 /* should only happen if pre-1.2 tape recordings are played */
8766 /* this is only for backward compatibility */
8768 int original_move_delay_value = player->move_delay_value;
8771 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8775 /* scroll remaining steps with finest movement resolution */
8776 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8778 while (player->MovPos)
8780 ScrollPlayer(player, SCROLL_GO_ON);
8781 ScrollScreen(NULL, SCROLL_GO_ON);
8787 player->move_delay_value = original_move_delay_value;
8790 if (player->last_move_dir & MV_HORIZONTAL)
8792 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8793 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8797 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8798 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8804 if (moved & MF_MOVING && !ScreenMovPos &&
8805 (player == local_player || !options.network))
8807 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8808 int offset = (setup.scroll_delay ? 3 : 0);
8810 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8812 /* actual player has left the screen -- scroll in that direction */
8813 if (jx != old_jx) /* player has moved horizontally */
8814 scroll_x += (jx - old_jx);
8815 else /* player has moved vertically */
8816 scroll_y += (jy - old_jy);
8820 if (jx != old_jx) /* player has moved horizontally */
8822 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8823 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8824 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8826 /* don't scroll over playfield boundaries */
8827 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8828 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8830 /* don't scroll more than one field at a time */
8831 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8833 /* don't scroll against the player's moving direction */
8834 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8835 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8836 scroll_x = old_scroll_x;
8838 else /* player has moved vertically */
8840 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8841 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8842 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8844 /* don't scroll over playfield boundaries */
8845 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8846 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8848 /* don't scroll more than one field at a time */
8849 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8851 /* don't scroll against the player's moving direction */
8852 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8853 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8854 scroll_y = old_scroll_y;
8858 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8860 if (!options.network && !AllPlayersInVisibleScreen())
8862 scroll_x = old_scroll_x;
8863 scroll_y = old_scroll_y;
8867 ScrollScreen(player, SCROLL_INIT);
8868 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8875 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8877 if (!(moved & MF_MOVING) && !player->is_pushing)
8882 player->StepFrame = 0;
8884 if (moved & MF_MOVING)
8886 if (old_jx != jx && old_jy == jy)
8887 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8888 else if (old_jx == jx && old_jy != jy)
8889 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8891 DrawLevelField(jx, jy); /* for "crumbled sand" */
8893 player->last_move_dir = player->MovDir;
8894 player->is_moving = TRUE;
8896 player->is_snapping = FALSE;
8900 player->is_switching = FALSE;
8903 player->is_dropping = FALSE;
8907 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
8909 static int trigger_sides[4][2] =
8911 /* enter side leave side */
8912 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8913 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8914 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8915 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8917 int move_direction = player->MovDir;
8918 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8919 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8922 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8924 CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8926 player->index_bit, leave_side);
8927 CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8929 player->index_bit, leave_side);
8932 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8934 CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
8935 CE_OTHER_GETS_ENTERED,
8936 player->index_bit, enter_side);
8937 CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
8938 player->index_bit, enter_side);
8949 CheckGravityMovementWhenNotMoving(player);
8952 player->last_move_dir = MV_NO_MOVING;
8954 player->is_moving = FALSE;
8957 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8959 TestIfHeroTouchesBadThing(jx, jy);
8960 TestIfPlayerTouchesCustomElement(jx, jy);
8963 if (!player->active)
8969 void ScrollPlayer(struct PlayerInfo *player, int mode)
8971 int jx = player->jx, jy = player->jy;
8972 int last_jx = player->last_jx, last_jy = player->last_jy;
8973 int move_stepsize = TILEX / player->move_delay_value;
8975 if (!player->active || !player->MovPos)
8978 if (mode == SCROLL_INIT)
8980 player->actual_frame_counter = FrameCounter;
8981 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8983 if (Feld[last_jx][last_jy] == EL_EMPTY)
8984 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8992 else if (!FrameReached(&player->actual_frame_counter, 1))
8995 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8996 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8998 if (!player->block_last_field &&
8999 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9000 Feld[last_jx][last_jy] = EL_EMPTY;
9002 /* before DrawPlayer() to draw correct player graphic for this case */
9003 if (player->MovPos == 0)
9004 CheckGravityMovement(player);
9007 DrawPlayer(player); /* needed here only to cleanup last field */
9010 if (player->MovPos == 0) /* player reached destination field */
9013 if (player->move_delay_reset_counter > 0)
9015 player->move_delay_reset_counter--;
9017 if (player->move_delay_reset_counter == 0)
9019 /* continue with normal speed after quickly moving through gate */
9020 HALVE_PLAYER_SPEED(player);
9022 /* be able to make the next move without delay */
9023 player->move_delay = 0;
9027 if (IS_PASSABLE(Feld[last_jx][last_jy]))
9029 /* continue with normal speed after quickly moving through gate */
9030 HALVE_PLAYER_SPEED(player);
9032 /* be able to make the next move without delay */
9033 player->move_delay = 0;
9037 if (player->block_last_field &&
9038 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9039 Feld[last_jx][last_jy] = EL_EMPTY;
9041 player->last_jx = jx;
9042 player->last_jy = jy;
9044 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9045 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9046 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9048 DrawPlayer(player); /* needed here only to cleanup last field */
9051 if (local_player->friends_still_needed == 0 ||
9052 IS_SP_ELEMENT(Feld[jx][jy]))
9053 player->LevelSolved = player->GameOver = TRUE;
9057 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9058 /* this breaks one level: "machine", level 000 */
9060 static int trigger_sides[4][2] =
9062 /* enter side leave side */
9063 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9064 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9065 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9066 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9068 int move_direction = player->MovDir;
9069 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9070 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9071 int old_jx = last_jx;
9072 int old_jy = last_jy;
9075 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9077 CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9079 player->index_bit, leave_side);
9080 CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9082 player->index_bit, leave_side);
9085 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9087 CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
9088 CE_OTHER_GETS_ENTERED,
9089 player->index_bit, enter_side);
9090 CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9091 player->index_bit, enter_side);
9098 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9100 TestIfHeroTouchesBadThing(jx, jy);
9101 TestIfPlayerTouchesCustomElement(jx, jy);
9103 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9106 if (!player->active)
9110 if (level.use_step_counter)
9116 for (i = 0; i < MAX_PLAYERS; i++)
9118 struct PlayerInfo *player = &stored_player[i];
9120 if (SHIELD_ON(player))
9122 player->shield_normal_time_left--;
9124 if (player->shield_deadly_time_left > 0)
9125 player->shield_deadly_time_left--;
9133 if (TimeLeft <= 10 && setup.time_limit)
9134 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9136 DrawGameValue_Time(TimeLeft);
9138 if (!TimeLeft && setup.time_limit)
9139 for (i = 0; i < MAX_PLAYERS; i++)
9140 KillHero(&stored_player[i]);
9142 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9143 DrawGameValue_Time(TimePlayed);
9146 if (tape.single_step && tape.recording && !tape.pausing &&
9147 !player->programmed_action)
9148 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9152 void ScrollScreen(struct PlayerInfo *player, int mode)
9154 static unsigned long screen_frame_counter = 0;
9156 if (mode == SCROLL_INIT)
9158 /* set scrolling step size according to actual player's moving speed */
9159 ScrollStepSize = TILEX / player->move_delay_value;
9161 screen_frame_counter = FrameCounter;
9162 ScreenMovDir = player->MovDir;
9163 ScreenMovPos = player->MovPos;
9164 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9167 else if (!FrameReached(&screen_frame_counter, 1))
9172 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9173 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9174 redraw_mask |= REDRAW_FIELD;
9177 ScreenMovDir = MV_NO_MOVING;
9180 void TestIfPlayerTouchesCustomElement(int x, int y)
9182 static int xy[4][2] =
9189 static int trigger_sides[4][2] =
9191 /* center side border side */
9192 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9193 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9194 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9195 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9197 static int touch_dir[4] =
9204 int center_element = Feld[x][y]; /* should always be non-moving! */
9207 for (i = 0; i < NUM_DIRECTIONS; i++)
9209 int xx = x + xy[i][0];
9210 int yy = y + xy[i][1];
9211 int center_side = trigger_sides[i][0];
9212 int border_side = trigger_sides[i][1];
9215 if (!IN_LEV_FIELD(xx, yy))
9218 if (IS_PLAYER(x, y))
9220 struct PlayerInfo *player = PLAYERINFO(x, y);
9222 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9223 border_element = Feld[xx][yy]; /* may be moving! */
9224 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9225 border_element = Feld[xx][yy];
9226 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9227 border_element = MovingOrBlocked2Element(xx, yy);
9229 continue; /* center and border element do not touch */
9231 CheckTriggeredElementChangePlayer(xx, yy, border_element,
9232 CE_OTHER_GETS_TOUCHED,
9233 player->index_bit, border_side);
9234 CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9235 player->index_bit, border_side);
9237 else if (IS_PLAYER(xx, yy))
9239 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9241 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9243 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9244 continue; /* center and border element do not touch */
9247 CheckTriggeredElementChangePlayer(x, y, center_element,
9248 CE_OTHER_GETS_TOUCHED,
9249 player->index_bit, center_side);
9250 CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9251 player->index_bit, center_side);
9258 void TestIfElementTouchesCustomElement(int x, int y)
9260 static int xy[4][2] =
9267 static int trigger_sides[4][2] =
9269 /* center side border side */
9270 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9271 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9272 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9273 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9275 static int touch_dir[4] =
9282 boolean change_center_element = FALSE;
9283 int center_element_change_page = 0;
9284 int center_element = Feld[x][y]; /* should always be non-moving! */
9285 int border_trigger_element;
9288 for (i = 0; i < NUM_DIRECTIONS; i++)
9290 int xx = x + xy[i][0];
9291 int yy = y + xy[i][1];
9292 int center_side = trigger_sides[i][0];
9293 int border_side = trigger_sides[i][1];
9296 if (!IN_LEV_FIELD(xx, yy))
9299 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9300 border_element = Feld[xx][yy]; /* may be moving! */
9301 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9302 border_element = Feld[xx][yy];
9303 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9304 border_element = MovingOrBlocked2Element(xx, yy);
9306 continue; /* center and border element do not touch */
9308 /* check for change of center element (but change it only once) */
9309 if (IS_CUSTOM_ELEMENT(center_element) &&
9310 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9311 !change_center_element)
9313 for (j = 0; j < element_info[center_element].num_change_pages; j++)
9315 struct ElementChangeInfo *change =
9316 &element_info[center_element].change_page[j];
9318 if (change->can_change &&
9319 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9320 change->trigger_side & border_side &&
9322 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9324 change->trigger_element == border_element
9328 change_center_element = TRUE;
9329 center_element_change_page = j;
9330 border_trigger_element = border_element;
9337 /* check for change of border element */
9338 if (IS_CUSTOM_ELEMENT(border_element) &&
9339 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9341 for (j = 0; j < element_info[border_element].num_change_pages; j++)
9343 struct ElementChangeInfo *change =
9344 &element_info[border_element].change_page[j];
9346 if (change->can_change &&
9347 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9348 change->trigger_side & center_side &&
9350 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9352 change->trigger_element == center_element
9357 printf("::: border_element %d, %d\n", x, y);
9360 CheckElementChangePage(xx, yy, border_element, center_element,
9361 CE_OTHER_IS_TOUCHING, j);
9368 if (change_center_element)
9371 printf("::: center_element %d, %d\n", x, y);
9374 CheckElementChangePage(x, y, center_element, border_trigger_element,
9375 CE_OTHER_IS_TOUCHING, center_element_change_page);
9379 void TestIfElementHitsCustomElement(int x, int y, int direction)
9381 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9382 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9383 int hitx = x + dx, hity = y + dy;
9384 int hitting_element = Feld[x][y];
9385 int touched_element;
9387 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9388 !IS_FREE(hitx, hity) &&
9389 (!IS_MOVING(hitx, hity) ||
9390 MovDir[hitx][hity] != direction ||
9391 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9394 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9398 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9402 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9403 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9405 CheckElementChangeSide(x, y, hitting_element, touched_element,
9406 CE_HITTING_SOMETHING, direction);
9408 if (IN_LEV_FIELD(hitx, hity))
9410 int opposite_direction = MV_DIR_OPPOSITE(direction);
9411 int hitting_side = direction;
9412 int touched_side = opposite_direction;
9414 int touched_element = MovingOrBlocked2Element(hitx, hity);
9417 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9418 MovDir[hitx][hity] != direction ||
9419 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9428 CheckElementChangeSide(hitx, hity, touched_element, hitting_element,
9429 CE_HIT_BY_SOMETHING, opposite_direction);
9431 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9432 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
9434 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9436 struct ElementChangeInfo *change =
9437 &element_info[hitting_element].change_page[i];
9439 if (change->can_change &&
9440 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
9441 change->trigger_side & touched_side &&
9444 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9446 change->trigger_element == touched_element
9450 CheckElementChangePage(x, y, hitting_element, touched_element,
9451 CE_OTHER_IS_HITTING, i);
9457 if (IS_CUSTOM_ELEMENT(touched_element) &&
9458 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
9460 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9462 struct ElementChangeInfo *change =
9463 &element_info[touched_element].change_page[i];
9465 if (change->can_change &&
9466 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
9467 change->trigger_side & hitting_side &&
9469 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9471 change->trigger_element == hitting_element
9475 CheckElementChangePage(hitx, hity, touched_element,
9476 hitting_element, CE_OTHER_GETS_HIT, i);
9486 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9488 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9489 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9490 int hitx = x + dx, hity = y + dy;
9491 int hitting_element = Feld[x][y];
9492 int touched_element;
9494 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9495 !IS_FREE(hitx, hity) &&
9496 (!IS_MOVING(hitx, hity) ||
9497 MovDir[hitx][hity] != direction ||
9498 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9501 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9505 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9509 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9510 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9512 CheckElementChangeSide(x, y, hitting_element, touched_element,
9513 EP_CAN_SMASH_EVERYTHING, direction);
9515 if (IN_LEV_FIELD(hitx, hity))
9517 int opposite_direction = MV_DIR_OPPOSITE(direction);
9518 int hitting_side = direction;
9519 int touched_side = opposite_direction;
9521 int touched_element = MovingOrBlocked2Element(hitx, hity);
9524 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9525 MovDir[hitx][hity] != direction ||
9526 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9535 CheckElementChangeSide(hitx, hity, touched_element, hitting_element,
9536 CE_SMASHED_BY_SOMETHING, opposite_direction);
9538 if (IS_CUSTOM_ELEMENT(hitting_element) &&
9539 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
9541 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9543 struct ElementChangeInfo *change =
9544 &element_info[hitting_element].change_page[i];
9546 if (change->can_change &&
9547 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
9548 change->trigger_side & touched_side &&
9551 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9553 change->trigger_element == touched_element
9557 CheckElementChangePage(x, y, hitting_element, touched_element,
9558 CE_OTHER_IS_SMASHING, i);
9564 if (IS_CUSTOM_ELEMENT(touched_element) &&
9565 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
9567 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9569 struct ElementChangeInfo *change =
9570 &element_info[touched_element].change_page[i];
9572 if (change->can_change &&
9573 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
9574 change->trigger_side & hitting_side &&
9576 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9578 change->trigger_element == hitting_element
9582 CheckElementChangePage(hitx, hity, touched_element,
9583 hitting_element, CE_OTHER_GETS_SMASHED, i);
9593 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9595 int i, kill_x = -1, kill_y = -1;
9596 static int test_xy[4][2] =
9603 static int test_dir[4] =
9611 for (i = 0; i < NUM_DIRECTIONS; i++)
9613 int test_x, test_y, test_move_dir, test_element;
9615 test_x = good_x + test_xy[i][0];
9616 test_y = good_y + test_xy[i][1];
9617 if (!IN_LEV_FIELD(test_x, test_y))
9621 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9624 test_element = Feld[test_x][test_y];
9626 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9629 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9630 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9632 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9633 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9641 if (kill_x != -1 || kill_y != -1)
9643 if (IS_PLAYER(good_x, good_y))
9645 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9647 if (player->shield_deadly_time_left > 0)
9648 Bang(kill_x, kill_y);
9649 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9653 Bang(good_x, good_y);
9657 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9659 int i, kill_x = -1, kill_y = -1;
9660 int bad_element = Feld[bad_x][bad_y];
9661 static int test_xy[4][2] =
9668 static int touch_dir[4] =
9675 static int test_dir[4] =
9683 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9686 for (i = 0; i < NUM_DIRECTIONS; i++)
9688 int test_x, test_y, test_move_dir, test_element;
9690 test_x = bad_x + test_xy[i][0];
9691 test_y = bad_y + test_xy[i][1];
9692 if (!IN_LEV_FIELD(test_x, test_y))
9696 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9698 test_element = Feld[test_x][test_y];
9700 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9701 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9703 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9704 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9706 /* good thing is player or penguin that does not move away */
9707 if (IS_PLAYER(test_x, test_y))
9709 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9711 if (bad_element == EL_ROBOT && player->is_moving)
9712 continue; /* robot does not kill player if he is moving */
9714 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9716 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9717 continue; /* center and border element do not touch */
9724 else if (test_element == EL_PENGUIN)
9733 if (kill_x != -1 || kill_y != -1)
9735 if (IS_PLAYER(kill_x, kill_y))
9737 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9739 if (player->shield_deadly_time_left > 0)
9741 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9745 Bang(kill_x, kill_y);
9749 void TestIfHeroTouchesBadThing(int x, int y)
9751 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9754 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
9756 TestIfGoodThingHitsBadThing(x, y, move_dir);
9759 void TestIfBadThingTouchesHero(int x, int y)
9761 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9764 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
9766 TestIfBadThingHitsGoodThing(x, y, move_dir);
9769 void TestIfFriendTouchesBadThing(int x, int y)
9771 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9774 void TestIfBadThingTouchesFriend(int x, int y)
9776 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9779 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9781 int i, kill_x = bad_x, kill_y = bad_y;
9782 static int xy[4][2] =
9790 for (i = 0; i < NUM_DIRECTIONS; i++)
9794 x = bad_x + xy[i][0];
9795 y = bad_y + xy[i][1];
9796 if (!IN_LEV_FIELD(x, y))
9799 element = Feld[x][y];
9800 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9801 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9809 if (kill_x != bad_x || kill_y != bad_y)
9813 void KillHero(struct PlayerInfo *player)
9815 int jx = player->jx, jy = player->jy;
9817 if (!player->active)
9820 /* remove accessible field at the player's position */
9821 Feld[jx][jy] = EL_EMPTY;
9823 /* deactivate shield (else Bang()/Explode() would not work right) */
9824 player->shield_normal_time_left = 0;
9825 player->shield_deadly_time_left = 0;
9831 static void KillHeroUnlessEnemyProtected(int x, int y)
9833 if (!PLAYER_ENEMY_PROTECTED(x, y))
9834 KillHero(PLAYERINFO(x, y));
9837 static void KillHeroUnlessExplosionProtected(int x, int y)
9839 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9840 KillHero(PLAYERINFO(x, y));
9843 void BuryHero(struct PlayerInfo *player)
9845 int jx = player->jx, jy = player->jy;
9847 if (!player->active)
9851 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9853 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
9855 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9857 player->GameOver = TRUE;
9861 void RemoveHero(struct PlayerInfo *player)
9863 int jx = player->jx, jy = player->jy;
9864 int i, found = FALSE;
9866 player->present = FALSE;
9867 player->active = FALSE;
9869 if (!ExplodeField[jx][jy])
9870 StorePlayer[jx][jy] = 0;
9872 for (i = 0; i < MAX_PLAYERS; i++)
9873 if (stored_player[i].active)
9877 AllPlayersGone = TRUE;
9884 =============================================================================
9885 checkDiagonalPushing()
9886 -----------------------------------------------------------------------------
9887 check if diagonal input device direction results in pushing of object
9888 (by checking if the alternative direction is walkable, diggable, ...)
9889 =============================================================================
9892 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9893 int x, int y, int real_dx, int real_dy)
9895 int jx, jy, dx, dy, xx, yy;
9897 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9900 /* diagonal direction: check alternative direction */
9905 xx = jx + (dx == 0 ? real_dx : 0);
9906 yy = jy + (dy == 0 ? real_dy : 0);
9908 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9912 =============================================================================
9914 -----------------------------------------------------------------------------
9915 x, y: field next to player (non-diagonal) to try to dig to
9916 real_dx, real_dy: direction as read from input device (can be diagonal)
9917 =============================================================================
9920 int DigField(struct PlayerInfo *player,
9921 int oldx, int oldy, int x, int y,
9922 int real_dx, int real_dy, int mode)
9924 static int trigger_sides[4] =
9926 CH_SIDE_RIGHT, /* moving left */
9927 CH_SIDE_LEFT, /* moving right */
9928 CH_SIDE_BOTTOM, /* moving up */
9929 CH_SIDE_TOP, /* moving down */
9932 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
9934 int jx = oldx, jy = oldy;
9935 int dx = x - jx, dy = y - jy;
9936 int nextx = x + dx, nexty = y + dy;
9937 int move_direction = (dx == -1 ? MV_LEFT :
9938 dx == +1 ? MV_RIGHT :
9940 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9941 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9942 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
9943 int old_element = Feld[jx][jy];
9946 if (player->MovPos == 0)
9948 player->is_digging = FALSE;
9949 player->is_collecting = FALSE;
9952 if (player->MovPos == 0) /* last pushing move finished */
9953 player->is_pushing = FALSE;
9955 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9957 player->is_switching = FALSE;
9958 player->push_delay = 0;
9960 return MF_NO_ACTION;
9963 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9964 return MF_NO_ACTION;
9969 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
9971 if (IS_TUBE(Feld[jx][jy]) ||
9972 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
9976 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
9977 int tube_leave_directions[][2] =
9979 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9980 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9981 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9982 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
9983 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
9984 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
9985 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
9986 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
9987 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
9988 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
9989 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
9990 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
9993 while (tube_leave_directions[i][0] != tube_element)
9996 if (tube_leave_directions[i][0] == -1) /* should not happen */
10000 if (!(tube_leave_directions[i][1] & move_direction))
10001 return MF_NO_ACTION; /* tube has no opening in this direction */
10006 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10007 old_element = Back[jx][jy];
10011 if (IS_WALKABLE(old_element) &&
10012 !(element_info[old_element].access_direction & move_direction))
10013 return MF_NO_ACTION; /* field has no opening in this direction */
10015 element = Feld[x][y];
10017 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10018 game.engine_version >= VERSION_IDENT(2,2,0,0))
10019 return MF_NO_ACTION;
10023 case EL_SP_PORT_LEFT:
10024 case EL_SP_PORT_RIGHT:
10025 case EL_SP_PORT_UP:
10026 case EL_SP_PORT_DOWN:
10027 case EL_SP_PORT_HORIZONTAL:
10028 case EL_SP_PORT_VERTICAL:
10029 case EL_SP_PORT_ANY:
10030 case EL_SP_GRAVITY_PORT_LEFT:
10031 case EL_SP_GRAVITY_PORT_RIGHT:
10032 case EL_SP_GRAVITY_PORT_UP:
10033 case EL_SP_GRAVITY_PORT_DOWN:
10035 if (!canEnterSupaplexPort(x, y, dx, dy))
10036 return MF_NO_ACTION;
10039 element != EL_SP_PORT_LEFT &&
10040 element != EL_SP_GRAVITY_PORT_LEFT &&
10041 element != EL_SP_PORT_HORIZONTAL &&
10042 element != EL_SP_PORT_ANY) ||
10044 element != EL_SP_PORT_RIGHT &&
10045 element != EL_SP_GRAVITY_PORT_RIGHT &&
10046 element != EL_SP_PORT_HORIZONTAL &&
10047 element != EL_SP_PORT_ANY) ||
10049 element != EL_SP_PORT_UP &&
10050 element != EL_SP_GRAVITY_PORT_UP &&
10051 element != EL_SP_PORT_VERTICAL &&
10052 element != EL_SP_PORT_ANY) ||
10054 element != EL_SP_PORT_DOWN &&
10055 element != EL_SP_GRAVITY_PORT_DOWN &&
10056 element != EL_SP_PORT_VERTICAL &&
10057 element != EL_SP_PORT_ANY) ||
10058 !IN_LEV_FIELD(nextx, nexty) ||
10059 !IS_FREE(nextx, nexty))
10060 return MF_NO_ACTION;
10063 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10064 element == EL_SP_GRAVITY_PORT_RIGHT ||
10065 element == EL_SP_GRAVITY_PORT_UP ||
10066 element == EL_SP_GRAVITY_PORT_DOWN)
10067 game.gravity = !game.gravity;
10069 /* automatically move to the next field with double speed */
10070 player->programmed_action = move_direction;
10072 if (player->move_delay_reset_counter == 0)
10074 player->move_delay_reset_counter = 2; /* two double speed steps */
10076 DOUBLE_PLAYER_SPEED(player);
10079 player->move_delay_reset_counter = 2;
10081 DOUBLE_PLAYER_SPEED(player);
10085 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10088 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10093 case EL_TUBE_VERTICAL:
10094 case EL_TUBE_HORIZONTAL:
10095 case EL_TUBE_VERTICAL_LEFT:
10096 case EL_TUBE_VERTICAL_RIGHT:
10097 case EL_TUBE_HORIZONTAL_UP:
10098 case EL_TUBE_HORIZONTAL_DOWN:
10099 case EL_TUBE_LEFT_UP:
10100 case EL_TUBE_LEFT_DOWN:
10101 case EL_TUBE_RIGHT_UP:
10102 case EL_TUBE_RIGHT_DOWN:
10105 int tube_enter_directions[][2] =
10107 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10108 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
10109 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
10110 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
10111 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
10112 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
10113 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
10114 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
10115 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
10116 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
10117 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
10118 { -1, MV_NO_MOVING }
10121 while (tube_enter_directions[i][0] != element)
10124 if (tube_enter_directions[i][0] == -1) /* should not happen */
10128 if (!(tube_enter_directions[i][1] & move_direction))
10129 return MF_NO_ACTION; /* tube has no opening in this direction */
10131 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10138 if (IS_WALKABLE(element))
10140 int sound_action = ACTION_WALKING;
10142 if (!(element_info[element].access_direction & opposite_direction))
10143 return MF_NO_ACTION; /* field not accessible from this direction */
10145 if (element >= EL_GATE_1 && element <= EL_GATE_4)
10147 if (!player->key[element - EL_GATE_1])
10148 return MF_NO_ACTION;
10150 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
10152 if (!player->key[element - EL_GATE_1_GRAY])
10153 return MF_NO_ACTION;
10155 else if (element == EL_EXIT_OPEN ||
10156 element == EL_SP_EXIT_OPEN ||
10157 element == EL_SP_EXIT_OPENING)
10159 sound_action = ACTION_PASSING; /* player is passing exit */
10161 else if (element == EL_EMPTY)
10163 sound_action = ACTION_MOVING; /* nothing to walk on */
10166 /* play sound from background or player, whatever is available */
10167 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
10168 PlayLevelSoundElementAction(x, y, element, sound_action);
10170 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10174 else if (IS_PASSABLE(element))
10176 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10177 return MF_NO_ACTION;
10179 if (IS_CUSTOM_ELEMENT(element) &&
10180 !(element_info[element].access_direction & opposite_direction))
10181 return MF_NO_ACTION; /* field not accessible from this direction */
10184 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10185 return MF_NO_ACTION;
10188 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
10190 if (!player->key[element - EL_EM_GATE_1])
10191 return MF_NO_ACTION;
10193 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
10195 if (!player->key[element - EL_EM_GATE_1_GRAY])
10196 return MF_NO_ACTION;
10199 /* automatically move to the next field with double speed */
10200 player->programmed_action = move_direction;
10202 if (player->move_delay_reset_counter == 0)
10204 player->move_delay_reset_counter = 2; /* two double speed steps */
10206 DOUBLE_PLAYER_SPEED(player);
10209 player->move_delay_reset_counter = 2;
10211 DOUBLE_PLAYER_SPEED(player);
10214 PlayLevelSoundAction(x, y, ACTION_PASSING);
10218 else if (IS_DIGGABLE(element))
10222 if (mode != DF_SNAP)
10225 GfxElement[x][y] = GFX_ELEMENT(element);
10228 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
10230 player->is_digging = TRUE;
10233 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10235 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED,
10236 player->index_bit, CH_SIDE_ANY);
10239 if (mode == DF_SNAP)
10240 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10245 else if (IS_COLLECTIBLE(element))
10249 if (mode != DF_SNAP)
10251 GfxElement[x][y] = element;
10252 player->is_collecting = TRUE;
10255 if (element == EL_SPEED_PILL)
10256 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10257 else if (element == EL_EXTRA_TIME && level.time > 0)
10260 DrawGameValue_Time(TimeLeft);
10262 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10264 player->shield_normal_time_left += 10;
10265 if (element == EL_SHIELD_DEADLY)
10266 player->shield_deadly_time_left += 10;
10268 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10270 if (player->inventory_size < MAX_INVENTORY_SIZE)
10271 player->inventory_element[player->inventory_size++] = element;
10273 DrawGameValue_Dynamite(local_player->inventory_size);
10275 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10277 player->dynabomb_count++;
10278 player->dynabombs_left++;
10280 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10282 player->dynabomb_size++;
10284 else if (element == EL_DYNABOMB_INCREASE_POWER)
10286 player->dynabomb_xl = TRUE;
10288 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
10289 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
10291 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
10292 element - EL_KEY_1 : element - EL_EM_KEY_1);
10294 player->key[key_nr] = TRUE;
10296 DrawGameValue_Keys(player);
10298 redraw_mask |= REDRAW_DOOR_1;
10300 else if (IS_ENVELOPE(element))
10303 player->show_envelope = element;
10305 ShowEnvelope(element - EL_ENVELOPE_1);
10308 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
10312 if (element_info[element].collect_count == 0)
10313 player->inventory_infinite_element = element;
10315 for (i = 0; i < element_info[element].collect_count; i++)
10316 if (player->inventory_size < MAX_INVENTORY_SIZE)
10317 player->inventory_element[player->inventory_size++] = element;
10319 DrawGameValue_Dynamite(local_player->inventory_size);
10321 else if (element_info[element].collect_count > 0)
10323 local_player->gems_still_needed -=
10324 element_info[element].collect_count;
10325 if (local_player->gems_still_needed < 0)
10326 local_player->gems_still_needed = 0;
10328 DrawGameValue_Emeralds(local_player->gems_still_needed);
10331 RaiseScoreElement(element);
10332 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10334 CheckTriggeredElementChangePlayer(x, y, element,
10335 CE_OTHER_GETS_COLLECTED,
10336 player->index_bit, CH_SIDE_ANY);
10339 if (mode == DF_SNAP)
10340 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10345 else if (IS_PUSHABLE(element))
10347 if (mode == DF_SNAP && element != EL_BD_ROCK)
10348 return MF_NO_ACTION;
10350 if (CAN_FALL(element) && dy)
10351 return MF_NO_ACTION;
10353 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10354 !(element == EL_SPRING && level.use_spring_bug))
10355 return MF_NO_ACTION;
10358 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10359 ((move_direction & MV_VERTICAL &&
10360 ((element_info[element].move_pattern & MV_LEFT &&
10361 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10362 (element_info[element].move_pattern & MV_RIGHT &&
10363 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10364 (move_direction & MV_HORIZONTAL &&
10365 ((element_info[element].move_pattern & MV_UP &&
10366 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10367 (element_info[element].move_pattern & MV_DOWN &&
10368 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10369 return MF_NO_ACTION;
10373 /* do not push elements already moving away faster than player */
10374 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10375 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10376 return MF_NO_ACTION;
10378 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
10379 return MF_NO_ACTION;
10383 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10385 if (player->push_delay_value == -1)
10386 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10388 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10390 if (!player->is_pushing)
10391 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10395 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
10396 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
10397 !player_is_pushing))
10398 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10401 if (!player->is_pushing &&
10402 game.engine_version >= VERSION_IDENT(2,2,0,7))
10403 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10407 printf("::: push delay: %ld [%d, %d] [%d]\n",
10408 player->push_delay_value, FrameCounter, game.engine_version,
10409 player->is_pushing);
10412 player->is_pushing = TRUE;
10414 if (!(IN_LEV_FIELD(nextx, nexty) &&
10415 (IS_FREE(nextx, nexty) ||
10416 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10417 IS_SB_ELEMENT(element)))))
10418 return MF_NO_ACTION;
10420 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10421 return MF_NO_ACTION;
10423 if (player->push_delay == 0) /* new pushing; restart delay */
10424 player->push_delay = FrameCounter;
10426 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
10427 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10428 element != EL_SPRING && element != EL_BALLOON)
10430 /* make sure that there is no move delay before next try to push */
10431 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10432 player->move_delay = INITIAL_MOVE_DELAY_OFF;
10434 return MF_NO_ACTION;
10438 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
10441 if (IS_SB_ELEMENT(element))
10443 if (element == EL_SOKOBAN_FIELD_FULL)
10445 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10446 local_player->sokobanfields_still_needed++;
10449 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10451 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10452 local_player->sokobanfields_still_needed--;
10455 Feld[x][y] = EL_SOKOBAN_OBJECT;
10457 if (Back[x][y] == Back[nextx][nexty])
10458 PlayLevelSoundAction(x, y, ACTION_PUSHING);
10459 else if (Back[x][y] != 0)
10460 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10463 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10466 if (local_player->sokobanfields_still_needed == 0 &&
10467 game.emulation == EMU_SOKOBAN)
10469 player->LevelSolved = player->GameOver = TRUE;
10470 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10474 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10476 InitMovingField(x, y, move_direction);
10477 GfxAction[x][y] = ACTION_PUSHING;
10479 if (mode == DF_SNAP)
10480 ContinueMoving(x, y);
10482 MovPos[x][y] = (dx != 0 ? dx : dy);
10484 Pushed[x][y] = TRUE;
10485 Pushed[nextx][nexty] = TRUE;
10487 if (game.engine_version < VERSION_IDENT(2,2,0,7))
10488 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10490 player->push_delay_value = -1; /* get new value later */
10492 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED,
10493 player->index_bit, dig_side);
10494 CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10495 player->index_bit, dig_side);
10499 else if (IS_SWITCHABLE(element))
10501 if (PLAYER_SWITCHING(player, x, y))
10504 player->is_switching = TRUE;
10505 player->switch_x = x;
10506 player->switch_y = y;
10508 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10510 if (element == EL_ROBOT_WHEEL)
10512 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10516 DrawLevelField(x, y);
10518 else if (element == EL_SP_TERMINAL)
10522 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
10524 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10526 else if (Feld[xx][yy] == EL_SP_TERMINAL)
10527 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10530 else if (IS_BELT_SWITCH(element))
10532 ToggleBeltSwitch(x, y);
10534 else if (element == EL_SWITCHGATE_SWITCH_UP ||
10535 element == EL_SWITCHGATE_SWITCH_DOWN)
10537 ToggleSwitchgateSwitch(x, y);
10539 else if (element == EL_LIGHT_SWITCH ||
10540 element == EL_LIGHT_SWITCH_ACTIVE)
10542 ToggleLightSwitch(x, y);
10545 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
10546 SND_LIGHT_SWITCH_ACTIVATING :
10547 SND_LIGHT_SWITCH_DEACTIVATING);
10550 else if (element == EL_TIMEGATE_SWITCH)
10552 ActivateTimegateSwitch(x, y);
10554 else if (element == EL_BALLOON_SWITCH_LEFT ||
10555 element == EL_BALLOON_SWITCH_RIGHT ||
10556 element == EL_BALLOON_SWITCH_UP ||
10557 element == EL_BALLOON_SWITCH_DOWN ||
10558 element == EL_BALLOON_SWITCH_ANY)
10560 if (element == EL_BALLOON_SWITCH_ANY)
10561 game.balloon_dir = move_direction;
10563 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10564 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10565 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10566 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10569 else if (element == EL_LAMP)
10571 Feld[x][y] = EL_LAMP_ACTIVE;
10572 local_player->lights_still_needed--;
10574 DrawLevelField(x, y);
10576 else if (element == EL_TIME_ORB_FULL)
10578 Feld[x][y] = EL_TIME_ORB_EMPTY;
10580 DrawGameValue_Time(TimeLeft);
10582 DrawLevelField(x, y);
10585 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
10593 if (!PLAYER_SWITCHING(player, x, y))
10595 player->is_switching = TRUE;
10596 player->switch_x = x;
10597 player->switch_y = y;
10599 CheckTriggeredElementChangePlayer(x, y, element,
10600 CE_OTHER_IS_SWITCHING,
10601 player->index_bit, dig_side);
10602 CheckElementChangePlayer(x, y, element, CE_SWITCHED,
10603 player->index_bit, dig_side);
10606 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED,
10607 player->index_bit, dig_side);
10608 CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10609 player->index_bit, dig_side);
10612 return MF_NO_ACTION;
10615 player->push_delay = 0;
10617 if (Feld[x][y] != element) /* really digged/collected something */
10618 player->is_collecting = !player->is_digging;
10623 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10625 int jx = player->jx, jy = player->jy;
10626 int x = jx + dx, y = jy + dy;
10627 int snap_direction = (dx == -1 ? MV_LEFT :
10628 dx == +1 ? MV_RIGHT :
10630 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10633 if (player->MovPos)
10636 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
10640 if (!player->active || !IN_LEV_FIELD(x, y))
10648 if (player->MovPos == 0)
10649 player->is_pushing = FALSE;
10651 player->is_snapping = FALSE;
10653 if (player->MovPos == 0)
10655 player->is_moving = FALSE;
10656 player->is_digging = FALSE;
10657 player->is_collecting = FALSE;
10663 if (player->is_snapping)
10666 player->MovDir = snap_direction;
10669 if (player->MovPos == 0)
10672 player->is_moving = FALSE;
10673 player->is_digging = FALSE;
10674 player->is_collecting = FALSE;
10677 player->is_dropping = FALSE;
10679 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10682 player->is_snapping = TRUE;
10685 if (player->MovPos == 0)
10688 player->is_moving = FALSE;
10689 player->is_digging = FALSE;
10690 player->is_collecting = FALSE;
10693 DrawLevelField(x, y);
10699 boolean DropElement(struct PlayerInfo *player)
10701 int jx = player->jx, jy = player->jy;
10702 int old_element = Feld[jx][jy];
10703 int new_element = (player->inventory_size > 0 ?
10704 player->inventory_element[player->inventory_size - 1] :
10705 player->inventory_infinite_element != EL_UNDEFINED ?
10706 player->inventory_infinite_element :
10707 player->dynabombs_left > 0 ?
10708 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10711 /* check if player is active, not moving and ready to drop */
10712 if (!player->active || player->MovPos || player->drop_delay > 0)
10715 /* check if player has anything that can be dropped */
10717 if (new_element == EL_UNDEFINED)
10720 if (player->inventory_size == 0 &&
10721 player->inventory_infinite_element == EL_UNDEFINED &&
10722 player->dynabombs_left == 0)
10726 /* check if anything can be dropped at the current position */
10727 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10730 /* collected custom elements can only be dropped on empty fields */
10732 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10735 if (player->inventory_size > 0 &&
10736 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
10737 && old_element != EL_EMPTY)
10741 if (old_element != EL_EMPTY)
10742 Back[jx][jy] = old_element; /* store old element on this field */
10744 ResetGfxAnimation(jx, jy);
10745 ResetRandomAnimationValue(jx, jy);
10747 if (player->inventory_size > 0 ||
10748 player->inventory_infinite_element != EL_UNDEFINED)
10750 if (player->inventory_size > 0)
10752 player->inventory_size--;
10755 new_element = player->inventory_element[player->inventory_size];
10758 DrawGameValue_Dynamite(local_player->inventory_size);
10760 if (new_element == EL_DYNAMITE)
10761 new_element = EL_DYNAMITE_ACTIVE;
10762 else if (new_element == EL_SP_DISK_RED)
10763 new_element = EL_SP_DISK_RED_ACTIVE;
10766 Feld[jx][jy] = new_element;
10768 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10769 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10771 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10774 /* needed if previous element just changed to "empty" in the last frame */
10775 Changed[jx][jy] = 0; /* allow another change */
10778 CheckTriggeredElementChangePlayer(jx, jy, new_element,
10779 CE_OTHER_GETS_DROPPED,
10780 player->index_bit, CH_SIDE_ANY);
10781 CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
10782 player->index_bit, CH_SIDE_ANY);
10784 TestIfElementTouchesCustomElement(jx, jy);
10786 else /* player is dropping a dyna bomb */
10788 player->dynabombs_left--;
10791 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
10794 Feld[jx][jy] = new_element;
10796 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10797 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10799 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10806 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
10809 InitField_WithBug1(jx, jy, FALSE);
10811 InitField(jx, jy, FALSE);
10812 if (CAN_MOVE(Feld[jx][jy]))
10813 InitMovDir(jx, jy);
10817 new_element = Feld[jx][jy];
10819 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10820 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10822 int move_stepsize = element_info[new_element].move_stepsize;
10823 int direction, dx, dy, nextx, nexty;
10825 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10826 MovDir[jx][jy] = player->MovDir;
10828 direction = MovDir[jx][jy];
10829 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10830 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10834 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
10837 WasJustMoving[jx][jy] = 3;
10839 InitMovingField(jx, jy, direction);
10840 ContinueMoving(jx, jy);
10845 Changed[jx][jy] = 0; /* allow another change */
10848 TestIfElementHitsCustomElement(jx, jy, direction);
10850 CheckElementChangeSide(jx, jy, new_element, touched_element,
10851 CE_HITTING_SOMETHING, direction);
10855 player->drop_delay = 2 * TILEX / move_stepsize + 1;
10859 player->drop_delay = 8 + 8 + 8;
10864 player->is_dropping = TRUE;
10870 /* ------------------------------------------------------------------------- */
10871 /* game sound playing functions */
10872 /* ------------------------------------------------------------------------- */
10874 static int *loop_sound_frame = NULL;
10875 static int *loop_sound_volume = NULL;
10877 void InitPlayLevelSound()
10879 int num_sounds = getSoundListSize();
10881 checked_free(loop_sound_frame);
10882 checked_free(loop_sound_volume);
10884 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10885 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10888 static void PlayLevelSound(int x, int y, int nr)
10890 int sx = SCREENX(x), sy = SCREENY(y);
10891 int volume, stereo_position;
10892 int max_distance = 8;
10893 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10895 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10896 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10899 if (!IN_LEV_FIELD(x, y) ||
10900 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10901 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10904 volume = SOUND_MAX_VOLUME;
10906 if (!IN_SCR_FIELD(sx, sy))
10908 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10909 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10911 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10914 stereo_position = (SOUND_MAX_LEFT +
10915 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10916 (SCR_FIELDX + 2 * max_distance));
10918 if (IS_LOOP_SOUND(nr))
10920 /* This assures that quieter loop sounds do not overwrite louder ones,
10921 while restarting sound volume comparison with each new game frame. */
10923 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10926 loop_sound_volume[nr] = volume;
10927 loop_sound_frame[nr] = FrameCounter;
10930 PlaySoundExt(nr, volume, stereo_position, type);
10933 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10935 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10936 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10937 y < LEVELY(BY1) ? LEVELY(BY1) :
10938 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10942 static void PlayLevelSoundAction(int x, int y, int action)
10944 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10947 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10949 int sound_effect = element_info[element].sound[action];
10951 if (sound_effect != SND_UNDEFINED)
10952 PlayLevelSound(x, y, sound_effect);
10955 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10958 int sound_effect = element_info[element].sound[action];
10960 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10961 PlayLevelSound(x, y, sound_effect);
10964 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10966 int sound_effect = element_info[Feld[x][y]].sound[action];
10968 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10969 PlayLevelSound(x, y, sound_effect);
10972 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10974 int sound_effect = element_info[Feld[x][y]].sound[action];
10976 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10977 StopSound(sound_effect);
10980 static void PlayLevelMusic()
10982 if (levelset.music[level_nr] != MUS_UNDEFINED)
10983 PlayMusic(levelset.music[level_nr]); /* from config file */
10985 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10988 void RaiseScore(int value)
10990 local_player->score += value;
10992 DrawGameValue_Score(local_player->score);
10995 void RaiseScoreElement(int element)
11000 case EL_BD_DIAMOND:
11001 case EL_EMERALD_YELLOW:
11002 case EL_EMERALD_RED:
11003 case EL_EMERALD_PURPLE:
11004 case EL_SP_INFOTRON:
11005 RaiseScore(level.score[SC_EMERALD]);
11008 RaiseScore(level.score[SC_DIAMOND]);
11011 RaiseScore(level.score[SC_CRYSTAL]);
11014 RaiseScore(level.score[SC_PEARL]);
11017 case EL_BD_BUTTERFLY:
11018 case EL_SP_ELECTRON:
11019 RaiseScore(level.score[SC_BUG]);
11022 case EL_BD_FIREFLY:
11023 case EL_SP_SNIKSNAK:
11024 RaiseScore(level.score[SC_SPACESHIP]);
11027 case EL_DARK_YAMYAM:
11028 RaiseScore(level.score[SC_YAMYAM]);
11031 RaiseScore(level.score[SC_ROBOT]);
11034 RaiseScore(level.score[SC_PACMAN]);
11037 RaiseScore(level.score[SC_NUT]);
11040 case EL_SP_DISK_RED:
11041 case EL_DYNABOMB_INCREASE_NUMBER:
11042 case EL_DYNABOMB_INCREASE_SIZE:
11043 case EL_DYNABOMB_INCREASE_POWER:
11044 RaiseScore(level.score[SC_DYNAMITE]);
11046 case EL_SHIELD_NORMAL:
11047 case EL_SHIELD_DEADLY:
11048 RaiseScore(level.score[SC_SHIELD]);
11050 case EL_EXTRA_TIME:
11051 RaiseScore(level.score[SC_TIME_BONUS]);
11057 RaiseScore(level.score[SC_KEY]);
11060 RaiseScore(element_info[element].collect_score);
11065 void RequestQuitGame(boolean ask_if_really_quit)
11067 if (AllPlayersGone ||
11068 !ask_if_really_quit ||
11069 level_editor_test_game ||
11070 Request("Do you really want to quit the game ?",
11071 REQ_ASK | REQ_STAY_CLOSED))
11073 #if defined(PLATFORM_UNIX)
11074 if (options.network)
11075 SendToServer_StopPlaying();
11079 game_status = GAME_MODE_MAIN;
11087 if (tape.playing && tape.index_search)
11089 SetDrawDeactivationMask(REDRAW_NONE);
11090 audio.sound_deactivated = FALSE;
11094 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11097 if (tape.playing && tape.index_search)
11099 SetDrawDeactivationMask(REDRAW_FIELD);
11100 audio.sound_deactivated = TRUE;
11108 /* ---------- new game button stuff ---------------------------------------- */
11110 /* graphic position values for game buttons */
11111 #define GAME_BUTTON_XSIZE 30
11112 #define GAME_BUTTON_YSIZE 30
11113 #define GAME_BUTTON_XPOS 5
11114 #define GAME_BUTTON_YPOS 215
11115 #define SOUND_BUTTON_XPOS 5
11116 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11118 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11119 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11120 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11121 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11122 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11123 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11130 } gamebutton_info[NUM_GAME_BUTTONS] =
11133 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11138 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11139 GAME_CTRL_ID_PAUSE,
11143 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11148 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11149 SOUND_CTRL_ID_MUSIC,
11150 "background music on/off"
11153 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11154 SOUND_CTRL_ID_LOOPS,
11155 "sound loops on/off"
11158 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11159 SOUND_CTRL_ID_SIMPLE,
11160 "normal sounds on/off"
11164 void CreateGameButtons()
11168 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11170 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11171 struct GadgetInfo *gi;
11174 unsigned long event_mask;
11175 int gd_xoffset, gd_yoffset;
11176 int gd_x1, gd_x2, gd_y1, gd_y2;
11179 gd_xoffset = gamebutton_info[i].x;
11180 gd_yoffset = gamebutton_info[i].y;
11181 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11182 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11184 if (id == GAME_CTRL_ID_STOP ||
11185 id == GAME_CTRL_ID_PAUSE ||
11186 id == GAME_CTRL_ID_PLAY)
11188 button_type = GD_TYPE_NORMAL_BUTTON;
11190 event_mask = GD_EVENT_RELEASED;
11191 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11192 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11196 button_type = GD_TYPE_CHECK_BUTTON;
11198 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11199 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11200 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11201 event_mask = GD_EVENT_PRESSED;
11202 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11203 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11206 gi = CreateGadget(GDI_CUSTOM_ID, id,
11207 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11208 GDI_X, DX + gd_xoffset,
11209 GDI_Y, DY + gd_yoffset,
11210 GDI_WIDTH, GAME_BUTTON_XSIZE,
11211 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11212 GDI_TYPE, button_type,
11213 GDI_STATE, GD_BUTTON_UNPRESSED,
11214 GDI_CHECKED, checked,
11215 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11216 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11217 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11218 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11219 GDI_EVENT_MASK, event_mask,
11220 GDI_CALLBACK_ACTION, HandleGameButtons,
11224 Error(ERR_EXIT, "cannot create gadget");
11226 game_gadget[id] = gi;
11230 void FreeGameButtons()
11234 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11235 FreeGadget(game_gadget[i]);
11238 static void MapGameButtons()
11242 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11243 MapGadget(game_gadget[i]);
11246 void UnmapGameButtons()
11250 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11251 UnmapGadget(game_gadget[i]);
11254 static void HandleGameButtons(struct GadgetInfo *gi)
11256 int id = gi->custom_id;
11258 if (game_status != GAME_MODE_PLAYING)
11263 case GAME_CTRL_ID_STOP:
11264 RequestQuitGame(TRUE);
11267 case GAME_CTRL_ID_PAUSE:
11268 if (options.network)
11270 #if defined(PLATFORM_UNIX)
11272 SendToServer_ContinuePlaying();
11274 SendToServer_PausePlaying();
11278 TapeTogglePause(TAPE_TOGGLE_MANUAL);
11281 case GAME_CTRL_ID_PLAY:
11284 #if defined(PLATFORM_UNIX)
11285 if (options.network)
11286 SendToServer_ContinuePlaying();
11290 tape.pausing = FALSE;
11291 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11296 case SOUND_CTRL_ID_MUSIC:
11297 if (setup.sound_music)
11299 setup.sound_music = FALSE;
11302 else if (audio.music_available)
11304 setup.sound = setup.sound_music = TRUE;
11306 SetAudioMode(setup.sound);
11312 case SOUND_CTRL_ID_LOOPS:
11313 if (setup.sound_loops)
11314 setup.sound_loops = FALSE;
11315 else if (audio.loops_available)
11317 setup.sound = setup.sound_loops = TRUE;
11318 SetAudioMode(setup.sound);
11322 case SOUND_CTRL_ID_SIMPLE:
11323 if (setup.sound_simple)
11324 setup.sound_simple = FALSE;
11325 else if (audio.sound_available)
11327 setup.sound = setup.sound_simple = TRUE;
11328 SetAudioMode(setup.sound);