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 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF (TRUE * 1)
30 #define USE_NEW_SP_SLIPPERY (TRUE * USE_NEW_STUFF * 1)
38 /* for MovePlayer() */
39 #define MF_NO_ACTION 0
43 /* for ScrollPlayer() */
45 #define SCROLL_GO_ON 1
48 #define EX_PHASE_START 0
49 #define EX_TYPE_NONE 0
50 #define EX_TYPE_NORMAL (1 << 0)
51 #define EX_TYPE_CENTER (1 << 1)
52 #define EX_TYPE_BORDER (1 << 2)
53 #define EX_TYPE_CROSS (1 << 3)
54 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
56 /* special positions in the game control window (relative to control window) */
59 #define XX_EMERALDS 29
60 #define YY_EMERALDS 54
61 #define XX_DYNAMITE 29
62 #define YY_DYNAMITE 89
71 /* special positions in the game control window (relative to main window) */
72 #define DX_LEVEL (DX + XX_LEVEL)
73 #define DY_LEVEL (DY + YY_LEVEL)
74 #define DX_EMERALDS (DX + XX_EMERALDS)
75 #define DY_EMERALDS (DY + YY_EMERALDS)
76 #define DX_DYNAMITE (DX + XX_DYNAMITE)
77 #define DY_DYNAMITE (DY + YY_DYNAMITE)
78 #define DX_KEYS (DX + XX_KEYS)
79 #define DY_KEYS (DY + YY_KEYS)
80 #define DX_SCORE (DX + XX_SCORE)
81 #define DY_SCORE (DY + YY_SCORE)
82 #define DX_TIME1 (DX + XX_TIME1)
83 #define DX_TIME2 (DX + XX_TIME2)
84 #define DY_TIME (DY + YY_TIME)
86 /* values for initial player move delay (initial delay counter value) */
87 #define INITIAL_MOVE_DELAY_OFF -1
88 #define INITIAL_MOVE_DELAY_ON 0
90 /* values for player movement speed (which is in fact a delay value) */
91 #define MOVE_DELAY_NORMAL_SPEED 8
92 #define MOVE_DELAY_HIGH_SPEED 4
94 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
95 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
96 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
97 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
99 /* values for other actions */
100 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
102 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
103 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
105 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
107 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
108 RND(element_info[e].push_delay_random))
109 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
110 RND(element_info[e].drop_delay_random))
111 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
112 RND(element_info[e].move_delay_random))
113 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
114 (element_info[e].move_delay_random))
115 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
116 RND((c)->delay_random * (c)->delay_frames))
118 #define GET_TARGET_ELEMENT(e, ch) \
119 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
120 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
122 #define GET_VALID_PLAYER_ELEMENT(e) \
123 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
125 #define CAN_GROW_INTO(e) \
126 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
128 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
129 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
132 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
133 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
134 (CAN_MOVE_INTO_ACID(e) && \
135 Feld[x][y] == EL_ACID) || \
138 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
139 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
140 (CAN_MOVE_INTO_ACID(e) && \
141 Feld[x][y] == EL_ACID) || \
144 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
145 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
147 (CAN_MOVE_INTO_ACID(e) && \
148 Feld[x][y] == EL_ACID) || \
149 (DONT_COLLIDE_WITH(e) && \
151 !PLAYER_ENEMY_PROTECTED(x, y))))
153 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
154 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
156 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
157 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
159 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
160 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
162 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
163 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
165 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
166 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
168 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
169 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
171 #define PIG_CAN_ENTER_FIELD(e, x, y) \
172 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
174 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
175 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
176 IS_FOOD_PENGUIN(Feld[x][y])))
177 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
178 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
180 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
181 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
183 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
184 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
186 #define GROUP_NR(e) ((e) - EL_GROUP_START)
187 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
188 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
189 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
191 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
192 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
194 #define CE_ENTER_FIELD_COND(e, x, y) \
195 (!IS_PLAYER(x, y) && \
196 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
198 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
199 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
201 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
202 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
204 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
205 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
206 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
207 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
209 /* game button identifiers */
210 #define GAME_CTRL_ID_STOP 0
211 #define GAME_CTRL_ID_PAUSE 1
212 #define GAME_CTRL_ID_PLAY 2
213 #define SOUND_CTRL_ID_MUSIC 3
214 #define SOUND_CTRL_ID_LOOPS 4
215 #define SOUND_CTRL_ID_SIMPLE 5
217 #define NUM_GAME_BUTTONS 6
220 /* forward declaration for internal use */
222 static void AdvanceFrameAndPlayerCounters(int);
224 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
225 static boolean MovePlayer(struct PlayerInfo *, int, int);
226 static void ScrollPlayer(struct PlayerInfo *, int);
227 static void ScrollScreen(struct PlayerInfo *, int);
229 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
231 static void InitBeltMovement(void);
232 static void CloseAllOpenTimegates(void);
233 static void CheckGravityMovement(struct PlayerInfo *);
234 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
235 static void KillHeroUnlessEnemyProtected(int, int);
236 static void KillHeroUnlessExplosionProtected(int, int);
238 static void TestIfPlayerTouchesCustomElement(int, int);
239 static void TestIfElementTouchesCustomElement(int, int);
240 static void TestIfElementHitsCustomElement(int, int, int);
242 static void TestIfElementSmashesCustomElement(int, int, int);
245 static void ChangeElement(int, int, int);
247 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
248 #define CheckTriggeredElementChange(x, y, e, ev) \
249 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
251 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
252 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
253 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
254 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
255 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
256 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
259 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
260 #define CheckElementChange(x, y, e, te, ev) \
261 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
262 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
263 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
264 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
265 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
267 static void PlayLevelSound(int, int, int);
268 static void PlayLevelSoundNearest(int, int, int);
269 static void PlayLevelSoundAction(int, int, int);
270 static void PlayLevelSoundElementAction(int, int, int, int);
271 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
272 static void PlayLevelSoundActionIfLoop(int, int, int);
273 static void StopLevelSoundActionIfLoop(int, int, int);
274 static void PlayLevelMusic();
276 static void MapGameButtons();
277 static void HandleGameButtons(struct GadgetInfo *);
279 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
282 /* ------------------------------------------------------------------------- */
283 /* definition of elements that automatically change to other elements after */
284 /* a specified time, eventually calling a function when changing */
285 /* ------------------------------------------------------------------------- */
287 /* forward declaration for changer functions */
288 static void InitBuggyBase(int x, int y);
289 static void WarnBuggyBase(int x, int y);
291 static void InitTrap(int x, int y);
292 static void ActivateTrap(int x, int y);
293 static void ChangeActiveTrap(int x, int y);
295 static void InitRobotWheel(int x, int y);
296 static void RunRobotWheel(int x, int y);
297 static void StopRobotWheel(int x, int y);
299 static void InitTimegateWheel(int x, int y);
300 static void RunTimegateWheel(int x, int y);
302 struct ChangingElementInfo
307 void (*pre_change_function)(int x, int y);
308 void (*change_function)(int x, int y);
309 void (*post_change_function)(int x, int y);
312 static struct ChangingElementInfo change_delay_list[] =
363 EL_SWITCHGATE_OPENING,
371 EL_SWITCHGATE_CLOSING,
372 EL_SWITCHGATE_CLOSED,
404 EL_ACID_SPLASH_RIGHT,
413 EL_SP_BUGGY_BASE_ACTIVATING,
420 EL_SP_BUGGY_BASE_ACTIVATING,
421 EL_SP_BUGGY_BASE_ACTIVE,
428 EL_SP_BUGGY_BASE_ACTIVE,
452 EL_ROBOT_WHEEL_ACTIVE,
460 EL_TIMEGATE_SWITCH_ACTIVE,
481 int push_delay_fixed, push_delay_random;
486 { EL_BALLOON, 0, 0 },
488 { EL_SOKOBAN_OBJECT, 2, 0 },
489 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
490 { EL_SATELLITE, 2, 0 },
491 { EL_SP_DISK_YELLOW, 2, 0 },
493 { EL_UNDEFINED, 0, 0 },
501 move_stepsize_list[] =
503 { EL_AMOEBA_DROP, 2 },
504 { EL_AMOEBA_DROPPING, 2 },
505 { EL_QUICKSAND_FILLING, 1 },
506 { EL_QUICKSAND_EMPTYING, 1 },
507 { EL_MAGIC_WALL_FILLING, 2 },
508 { EL_BD_MAGIC_WALL_FILLING, 2 },
509 { EL_MAGIC_WALL_EMPTYING, 2 },
510 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
520 collect_count_list[] =
523 { EL_BD_DIAMOND, 1 },
524 { EL_EMERALD_YELLOW, 1 },
525 { EL_EMERALD_RED, 1 },
526 { EL_EMERALD_PURPLE, 1 },
528 { EL_SP_INFOTRON, 1 },
540 access_direction_list[] =
542 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
543 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
544 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
545 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
546 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
547 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
548 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
549 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
550 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
551 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
552 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
554 { EL_SP_PORT_LEFT, MV_RIGHT },
555 { EL_SP_PORT_RIGHT, MV_LEFT },
556 { EL_SP_PORT_UP, MV_DOWN },
557 { EL_SP_PORT_DOWN, MV_UP },
558 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
559 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
560 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
561 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
562 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
563 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
564 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
565 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
566 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
567 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
568 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
569 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
570 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
571 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
572 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
574 { EL_UNDEFINED, MV_NO_MOVING }
577 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
579 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
580 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
581 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
582 IS_JUST_CHANGING(x, y))
584 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
587 void GetPlayerConfig()
589 if (!audio.sound_available)
590 setup.sound_simple = FALSE;
592 if (!audio.loops_available)
593 setup.sound_loops = FALSE;
595 if (!audio.music_available)
596 setup.sound_music = FALSE;
598 if (!video.fullscreen_available)
599 setup.fullscreen = FALSE;
601 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
603 SetAudioMode(setup.sound);
607 static int getBeltNrFromBeltElement(int element)
609 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
610 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
611 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
614 static int getBeltNrFromBeltActiveElement(int element)
616 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
617 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
618 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
621 static int getBeltNrFromBeltSwitchElement(int element)
623 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
624 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
625 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
628 static int getBeltDirNrFromBeltSwitchElement(int element)
630 static int belt_base_element[4] =
632 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
633 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
634 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
635 EL_CONVEYOR_BELT_4_SWITCH_LEFT
638 int belt_nr = getBeltNrFromBeltSwitchElement(element);
639 int belt_dir_nr = element - belt_base_element[belt_nr];
641 return (belt_dir_nr % 3);
644 static int getBeltDirFromBeltSwitchElement(int element)
646 static int belt_move_dir[3] =
653 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
655 return belt_move_dir[belt_dir_nr];
658 static void InitPlayerField(int x, int y, int element, boolean init_game)
660 if (element == EL_SP_MURPHY)
664 if (stored_player[0].present)
666 Feld[x][y] = EL_SP_MURPHY_CLONE;
672 stored_player[0].use_murphy_graphic = TRUE;
675 Feld[x][y] = EL_PLAYER_1;
681 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
682 int jx = player->jx, jy = player->jy;
684 player->present = TRUE;
686 player->block_last_field = (element == EL_SP_MURPHY ?
687 level.sp_block_last_field :
688 level.block_last_field);
690 /* ---------- initialize player's last field block delay --------------- */
692 /* always start with reliable default value (no adjustment needed) */
693 player->block_delay_adjustment = 0;
695 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
696 if (player->block_last_field && element == EL_SP_MURPHY)
697 player->block_delay_adjustment = 1;
699 /* special case 2: in game engines before 3.1.1, blocking was different */
700 if (game.use_block_last_field_bug)
701 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
703 if (!options.network || player->connected)
705 player->active = TRUE;
707 /* remove potentially duplicate players */
708 if (StorePlayer[jx][jy] == Feld[x][y])
709 StorePlayer[jx][jy] = 0;
711 StorePlayer[x][y] = Feld[x][y];
715 printf("Player %d activated.\n", player->element_nr);
716 printf("[Local player is %d and currently %s.]\n",
717 local_player->element_nr,
718 local_player->active ? "active" : "not active");
722 Feld[x][y] = EL_EMPTY;
724 player->jx = player->last_jx = x;
725 player->jy = player->last_jy = y;
729 static void InitField(int x, int y, boolean init_game)
731 int element = Feld[x][y];
740 InitPlayerField(x, y, element, init_game);
743 case EL_SOKOBAN_FIELD_PLAYER:
744 element = Feld[x][y] = EL_PLAYER_1;
745 InitField(x, y, init_game);
747 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
748 InitField(x, y, init_game);
751 case EL_SOKOBAN_FIELD_EMPTY:
752 local_player->sokobanfields_still_needed++;
756 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
757 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
758 else if (x > 0 && Feld[x-1][y] == EL_ACID)
759 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
760 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
761 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
762 else if (y > 0 && Feld[x][y-1] == EL_ACID)
763 Feld[x][y] = EL_ACID_POOL_BOTTOM;
764 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
765 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
773 case EL_SPACESHIP_RIGHT:
774 case EL_SPACESHIP_UP:
775 case EL_SPACESHIP_LEFT:
776 case EL_SPACESHIP_DOWN:
778 case EL_BD_BUTTERFLY_RIGHT:
779 case EL_BD_BUTTERFLY_UP:
780 case EL_BD_BUTTERFLY_LEFT:
781 case EL_BD_BUTTERFLY_DOWN:
782 case EL_BD_BUTTERFLY:
783 case EL_BD_FIREFLY_RIGHT:
784 case EL_BD_FIREFLY_UP:
785 case EL_BD_FIREFLY_LEFT:
786 case EL_BD_FIREFLY_DOWN:
788 case EL_PACMAN_RIGHT:
812 if (y == lev_fieldy - 1)
814 Feld[x][y] = EL_AMOEBA_GROWING;
815 Store[x][y] = EL_AMOEBA_WET;
819 case EL_DYNAMITE_ACTIVE:
820 case EL_SP_DISK_RED_ACTIVE:
821 case EL_DYNABOMB_PLAYER_1_ACTIVE:
822 case EL_DYNABOMB_PLAYER_2_ACTIVE:
823 case EL_DYNABOMB_PLAYER_3_ACTIVE:
824 case EL_DYNABOMB_PLAYER_4_ACTIVE:
829 local_player->lights_still_needed++;
833 local_player->friends_still_needed++;
838 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
841 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
842 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
843 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
844 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
845 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
846 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
847 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
848 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
849 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
850 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
851 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
852 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
855 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
856 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
857 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
859 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
861 game.belt_dir[belt_nr] = belt_dir;
862 game.belt_dir_nr[belt_nr] = belt_dir_nr;
864 else /* more than one switch -- set it like the first switch */
866 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
871 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
873 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
876 case EL_LIGHT_SWITCH_ACTIVE:
878 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
882 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
884 else if (IS_GROUP_ELEMENT(element))
886 struct ElementGroupInfo *group = element_info[element].group;
887 int last_anim_random_frame = gfx.anim_random_frame;
890 if (group->choice_mode == ANIM_RANDOM)
891 gfx.anim_random_frame = RND(group->num_elements_resolved);
893 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
894 group->choice_mode, 0,
897 if (group->choice_mode == ANIM_RANDOM)
898 gfx.anim_random_frame = last_anim_random_frame;
902 Feld[x][y] = group->element_resolved[element_pos];
904 InitField(x, y, init_game);
910 static inline void InitField_WithBug1(int x, int y, boolean init_game)
912 InitField(x, y, init_game);
914 /* not needed to call InitMovDir() -- already done by InitField()! */
915 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
916 CAN_MOVE(Feld[x][y]))
920 static inline void InitField_WithBug2(int x, int y, boolean init_game)
922 int old_element = Feld[x][y];
924 InitField(x, y, init_game);
926 /* not needed to call InitMovDir() -- already done by InitField()! */
927 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
928 CAN_MOVE(old_element) &&
929 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
932 /* this case is in fact a combination of not less than three bugs:
933 first, it calls InitMovDir() for elements that can move, although this is
934 already done by InitField(); then, it checks the element that was at this
935 field _before_ the call to InitField() (which can change it); lastly, it
936 was not called for "mole with direction" elements, which were treated as
937 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
941 inline void DrawGameValue_Emeralds(int value)
943 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
946 inline void DrawGameValue_Dynamite(int value)
948 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
951 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
955 /* currently only 4 of 8 possible keys are displayed */
956 for (i = 0; i < STD_NUM_KEYS; i++)
959 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
960 el2edimg(EL_KEY_1 + i));
962 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
963 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
964 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
968 inline void DrawGameValue_Score(int value)
970 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
973 inline void DrawGameValue_Time(int value)
976 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
978 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
981 inline void DrawGameValue_Level(int value)
984 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
987 /* misuse area for displaying emeralds to draw bigger level number */
988 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
989 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
991 /* now copy it to the area for displaying level number */
992 BlitBitmap(drawto, drawto,
993 DX_EMERALDS, DY_EMERALDS + 1,
994 getFontWidth(FONT_LEVEL_NUMBER) * 3,
995 getFontHeight(FONT_LEVEL_NUMBER) - 1,
996 DX_LEVEL - 1, DY_LEVEL + 1);
998 /* restore the area for displaying emeralds */
999 DrawGameValue_Emeralds(local_player->gems_still_needed);
1001 /* yes, this is all really ugly :-) */
1005 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1008 int key[MAX_NUM_KEYS];
1011 for (i = 0; i < MAX_NUM_KEYS; i++)
1012 key[i] = key_bits & (1 << i);
1014 DrawGameValue_Level(level_nr);
1016 DrawGameValue_Emeralds(emeralds);
1017 DrawGameValue_Dynamite(dynamite);
1018 DrawGameValue_Score(score);
1019 DrawGameValue_Time(time);
1021 DrawGameValue_Keys(key);
1024 void DrawGameDoorValues()
1028 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1030 DrawGameDoorValues_EM();
1035 DrawGameValue_Level(level_nr);
1037 DrawGameValue_Emeralds(local_player->gems_still_needed);
1038 DrawGameValue_Dynamite(local_player->inventory_size);
1039 DrawGameValue_Score(local_player->score);
1040 DrawGameValue_Time(TimeLeft);
1042 for (i = 0; i < MAX_PLAYERS; i++)
1043 DrawGameValue_Keys(stored_player[i].key);
1046 static void resolve_group_element(int group_element, int recursion_depth)
1048 static int group_nr;
1049 static struct ElementGroupInfo *group;
1050 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1053 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1055 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1056 group_element - EL_GROUP_START + 1);
1058 /* replace element which caused too deep recursion by question mark */
1059 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1064 if (recursion_depth == 0) /* initialization */
1066 group = element_info[group_element].group;
1067 group_nr = group_element - EL_GROUP_START;
1069 group->num_elements_resolved = 0;
1070 group->choice_pos = 0;
1073 for (i = 0; i < actual_group->num_elements; i++)
1075 int element = actual_group->element[i];
1077 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1080 if (IS_GROUP_ELEMENT(element))
1081 resolve_group_element(element, recursion_depth + 1);
1084 group->element_resolved[group->num_elements_resolved++] = element;
1085 element_info[element].in_group[group_nr] = TRUE;
1092 =============================================================================
1094 -----------------------------------------------------------------------------
1095 initialize game engine due to level / tape version number
1096 =============================================================================
1099 static void InitGameEngine()
1103 /* set game engine from tape file when re-playing, else from level file */
1104 game.engine_version = (tape.playing ? tape.engine_version :
1105 level.game_version);
1107 /* ---------------------------------------------------------------------- */
1108 /* set flags for bugs and changes according to active game engine version */
1109 /* ---------------------------------------------------------------------- */
1112 Summary of bugfix/change:
1113 Fixed handling for custom elements that change when pushed by the player.
1115 Fixed/changed in version:
1119 Before 3.1.0, custom elements that "change when pushing" changed directly
1120 after the player started pushing them (until then handled in "DigField()").
1121 Since 3.1.0, these custom elements are not changed until the "pushing"
1122 move of the element is finished (now handled in "ContinueMoving()").
1124 Affected levels/tapes:
1125 The first condition is generally needed for all levels/tapes before version
1126 3.1.0, which might use the old behaviour before it was changed; known tapes
1127 that are affected are some tapes from the level set "Walpurgis Gardens" by
1129 The second condition is an exception from the above case and is needed for
1130 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1131 above (including some development versions of 3.1.0), but before it was
1132 known that this change would break tapes like the above and was fixed in
1133 3.1.1, so that the changed behaviour was active although the engine version
1134 while recording maybe was before 3.1.0. There is at least one tape that is
1135 affected by this exception, which is the tape for the one-level set "Bug
1136 Machine" by Juergen Bonhagen.
1139 game.use_change_when_pushing_bug =
1140 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1142 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1143 tape.game_version < VERSION_IDENT(3,1,1,0)));
1146 Summary of bugfix/change:
1147 Fixed handling for blocking the field the player leaves when moving.
1149 Fixed/changed in version:
1153 Before 3.1.1, when "block last field when moving" was enabled, the field
1154 the player is leaving when moving was blocked for the time of the move,
1155 and was directly unblocked afterwards. This resulted in the last field
1156 being blocked for exactly one less than the number of frames of one player
1157 move. Additionally, even when blocking was disabled, the last field was
1158 blocked for exactly one frame.
1159 Since 3.1.1, due to changes in player movement handling, the last field
1160 is not blocked at all when blocking is disabled. When blocking is enabled,
1161 the last field is blocked for exactly the number of frames of one player
1162 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1163 last field is blocked for exactly one more than the number of frames of
1166 Affected levels/tapes:
1167 (!!! yet to be determined -- probably many !!!)
1170 game.use_block_last_field_bug =
1171 (game.engine_version < VERSION_IDENT(3,1,1,0));
1173 /* ---------------------------------------------------------------------- */
1175 /* dynamically adjust element properties according to game engine version */
1176 InitElementPropertiesEngine(game.engine_version);
1179 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1180 printf(" tape version == %06d [%s] [file: %06d]\n",
1181 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1183 printf(" => game.engine_version == %06d\n", game.engine_version);
1186 /* ---------- recursively resolve group elements ------------------------- */
1188 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1189 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1190 element_info[i].in_group[j] = FALSE;
1192 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1193 resolve_group_element(EL_GROUP_START + i, 0);
1195 /* ---------- initialize player's initial move delay --------------------- */
1197 /* dynamically adjust player properties according to level information */
1198 game.initial_move_delay_value =
1199 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1201 /* dynamically adjust player properties according to game engine version */
1202 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1203 game.initial_move_delay_value : 0);
1205 /* ---------- initialize player's initial push delay --------------------- */
1207 /* dynamically adjust player properties according to game engine version */
1208 game.initial_push_delay_value =
1209 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1211 /* ---------- initialize changing elements ------------------------------- */
1213 /* initialize changing elements information */
1214 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1216 struct ElementInfo *ei = &element_info[i];
1218 /* this pointer might have been changed in the level editor */
1219 ei->change = &ei->change_page[0];
1221 if (!IS_CUSTOM_ELEMENT(i))
1223 ei->change->target_element = EL_EMPTY_SPACE;
1224 ei->change->delay_fixed = 0;
1225 ei->change->delay_random = 0;
1226 ei->change->delay_frames = 1;
1229 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1231 ei->has_change_event[j] = FALSE;
1233 ei->event_page_nr[j] = 0;
1234 ei->event_page[j] = &ei->change_page[0];
1238 /* add changing elements from pre-defined list */
1239 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1241 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1242 struct ElementInfo *ei = &element_info[ch_delay->element];
1244 ei->change->target_element = ch_delay->target_element;
1245 ei->change->delay_fixed = ch_delay->change_delay;
1247 ei->change->pre_change_function = ch_delay->pre_change_function;
1248 ei->change->change_function = ch_delay->change_function;
1249 ei->change->post_change_function = ch_delay->post_change_function;
1251 ei->has_change_event[CE_DELAY] = TRUE;
1253 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1254 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1257 /* add change events from custom element configuration */
1258 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1260 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1262 for (j = 0; j < ei->num_change_pages; j++)
1264 if (!ei->change_page[j].can_change)
1267 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1269 /* only add event page for the first page found with this event */
1270 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1272 ei->has_change_event[k] = TRUE;
1274 ei->event_page_nr[k] = j;
1275 ei->event_page[k] = &ei->change_page[j];
1281 /* ---------- initialize internal run-time variables ------------- */
1283 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1285 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1287 for (j = 0; j < ei->num_change_pages; j++)
1289 ei->change_page[j].can_change_or_has_action =
1290 (ei->change_page[j].can_change |
1291 ei->change_page[j].has_action);
1295 /* ---------- initialize run-time trigger player and element ------------- */
1297 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1299 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1301 for (j = 0; j < ei->num_change_pages; j++)
1303 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1304 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1308 /* ---------- initialize trigger events ---------------------------------- */
1310 /* initialize trigger events information */
1311 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1312 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1313 trigger_events[i][j] = FALSE;
1315 /* add trigger events from element change event properties */
1316 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1318 struct ElementInfo *ei = &element_info[i];
1320 for (j = 0; j < ei->num_change_pages; j++)
1322 if (!ei->change_page[j].can_change)
1325 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1327 int trigger_element = ei->change_page[j].trigger_element;
1329 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1331 if (ei->change_page[j].has_event[k])
1333 if (IS_GROUP_ELEMENT(trigger_element))
1335 struct ElementGroupInfo *group =
1336 element_info[trigger_element].group;
1338 for (l = 0; l < group->num_elements_resolved; l++)
1339 trigger_events[group->element_resolved[l]][k] = TRUE;
1342 trigger_events[trigger_element][k] = TRUE;
1349 /* ---------- initialize push delay -------------------------------------- */
1351 /* initialize push delay values to default */
1352 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1354 if (!IS_CUSTOM_ELEMENT(i))
1356 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1357 element_info[i].push_delay_random = game.default_push_delay_random;
1361 /* set push delay value for certain elements from pre-defined list */
1362 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1364 int e = push_delay_list[i].element;
1366 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1367 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1370 /* set push delay value for Supaplex elements for newer engine versions */
1371 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1373 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1375 if (IS_SP_ELEMENT(i))
1377 /* set SP push delay to just enough to push under a falling zonk */
1378 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1380 element_info[i].push_delay_fixed = delay;
1381 element_info[i].push_delay_random = 0;
1386 /* ---------- initialize move stepsize ----------------------------------- */
1388 /* initialize move stepsize values to default */
1389 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1390 if (!IS_CUSTOM_ELEMENT(i))
1391 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1393 /* set move stepsize value for certain elements from pre-defined list */
1394 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1396 int e = move_stepsize_list[i].element;
1398 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1401 /* ---------- initialize gem count --------------------------------------- */
1403 /* initialize gem count values for each element */
1404 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1405 if (!IS_CUSTOM_ELEMENT(i))
1406 element_info[i].collect_count = 0;
1408 /* add gem count values for all elements from pre-defined list */
1409 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1410 element_info[collect_count_list[i].element].collect_count =
1411 collect_count_list[i].count;
1413 /* ---------- initialize access direction -------------------------------- */
1415 /* initialize access direction values to default (access from every side) */
1416 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1417 if (!IS_CUSTOM_ELEMENT(i))
1418 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1420 /* set access direction value for certain elements from pre-defined list */
1421 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1422 element_info[access_direction_list[i].element].access_direction =
1423 access_direction_list[i].direction;
1428 =============================================================================
1430 -----------------------------------------------------------------------------
1431 initialize and start new game
1432 =============================================================================
1437 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1438 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1439 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1444 /* don't play tapes over network */
1445 network_playing = (options.network && !tape.playing);
1447 for (i = 0; i < MAX_PLAYERS; i++)
1449 struct PlayerInfo *player = &stored_player[i];
1451 player->index_nr = i;
1452 player->index_bit = (1 << i);
1453 player->element_nr = EL_PLAYER_1 + i;
1455 player->present = FALSE;
1456 player->active = FALSE;
1459 player->effective_action = 0;
1460 player->programmed_action = 0;
1463 player->gems_still_needed = level.gems_needed;
1464 player->sokobanfields_still_needed = 0;
1465 player->lights_still_needed = 0;
1466 player->friends_still_needed = 0;
1468 for (j = 0; j < MAX_NUM_KEYS; j++)
1469 player->key[j] = FALSE;
1471 player->dynabomb_count = 0;
1472 player->dynabomb_size = 1;
1473 player->dynabombs_left = 0;
1474 player->dynabomb_xl = FALSE;
1476 player->MovDir = MV_NO_MOVING;
1479 player->GfxDir = MV_NO_MOVING;
1480 player->GfxAction = ACTION_DEFAULT;
1482 player->StepFrame = 0;
1484 player->use_murphy_graphic = FALSE;
1486 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1487 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1489 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1491 player->actual_frame_counter = 0;
1493 player->step_counter = 0;
1495 player->last_move_dir = MV_NO_MOVING;
1497 player->is_waiting = FALSE;
1498 player->is_moving = FALSE;
1499 player->is_auto_moving = FALSE;
1500 player->is_digging = FALSE;
1501 player->is_snapping = FALSE;
1502 player->is_collecting = FALSE;
1503 player->is_pushing = FALSE;
1504 player->is_switching = FALSE;
1505 player->is_dropping = FALSE;
1507 player->is_bored = FALSE;
1508 player->is_sleeping = FALSE;
1510 player->frame_counter_bored = -1;
1511 player->frame_counter_sleeping = -1;
1513 player->anim_delay_counter = 0;
1514 player->post_delay_counter = 0;
1516 player->action_waiting = ACTION_DEFAULT;
1517 player->last_action_waiting = ACTION_DEFAULT;
1518 player->special_action_bored = ACTION_DEFAULT;
1519 player->special_action_sleeping = ACTION_DEFAULT;
1521 player->num_special_action_bored = 0;
1522 player->num_special_action_sleeping = 0;
1524 /* determine number of special actions for bored and sleeping animation */
1525 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1527 boolean found = FALSE;
1529 for (k = 0; k < NUM_DIRECTIONS; k++)
1530 if (el_act_dir2img(player->element_nr, j, k) !=
1531 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1535 player->num_special_action_bored++;
1539 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1541 boolean found = FALSE;
1543 for (k = 0; k < NUM_DIRECTIONS; k++)
1544 if (el_act_dir2img(player->element_nr, j, k) !=
1545 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1549 player->num_special_action_sleeping++;
1554 player->switch_x = -1;
1555 player->switch_y = -1;
1557 player->drop_x = -1;
1558 player->drop_y = -1;
1560 player->show_envelope = 0;
1562 player->move_delay = game.initial_move_delay;
1563 player->move_delay_value = game.initial_move_delay_value;
1565 player->move_delay_reset_counter = 0;
1567 player->push_delay = -1; /* initialized when pushing starts */
1568 player->push_delay_value = game.initial_push_delay_value;
1570 player->drop_delay = 0;
1572 player->last_jx = player->last_jy = 0;
1573 player->jx = player->jy = 0;
1575 player->shield_normal_time_left = 0;
1576 player->shield_deadly_time_left = 0;
1578 player->inventory_infinite_element = EL_UNDEFINED;
1579 player->inventory_size = 0;
1581 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1582 SnapField(player, 0, 0);
1584 player->LevelSolved = FALSE;
1585 player->GameOver = FALSE;
1588 network_player_action_received = FALSE;
1590 #if defined(NETWORK_AVALIABLE)
1591 /* initial null action */
1592 if (network_playing)
1593 SendToServer_MovePlayer(MV_NO_MOVING);
1602 TimeLeft = level.time;
1605 ScreenMovDir = MV_NO_MOVING;
1609 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1611 AllPlayersGone = FALSE;
1613 game.yamyam_content_nr = 0;
1614 game.magic_wall_active = FALSE;
1615 game.magic_wall_time_left = 0;
1616 game.light_time_left = 0;
1617 game.timegate_time_left = 0;
1618 game.switchgate_pos = 0;
1619 game.balloon_dir = MV_NO_MOVING;
1620 game.gravity = level.initial_gravity;
1621 game.explosions_delayed = TRUE;
1623 game.envelope_active = FALSE;
1625 for (i = 0; i < NUM_BELTS; i++)
1627 game.belt_dir[i] = MV_NO_MOVING;
1628 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1631 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1632 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1634 for (x = 0; x < lev_fieldx; x++)
1636 for (y = 0; y < lev_fieldy; y++)
1638 Feld[x][y] = level.field[x][y];
1639 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1640 ChangeDelay[x][y] = 0;
1641 ChangePage[x][y] = -1;
1642 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1644 WasJustMoving[x][y] = 0;
1645 WasJustFalling[x][y] = 0;
1646 CheckCollision[x][y] = 0;
1648 Pushed[x][y] = FALSE;
1650 Changed[x][y] = FALSE;
1651 ChangeEvent[x][y] = -1;
1653 ExplodePhase[x][y] = 0;
1654 ExplodeDelay[x][y] = 0;
1655 ExplodeField[x][y] = EX_TYPE_NONE;
1657 RunnerVisit[x][y] = 0;
1658 PlayerVisit[x][y] = 0;
1661 GfxRandom[x][y] = INIT_GFX_RANDOM();
1662 GfxElement[x][y] = EL_UNDEFINED;
1663 GfxAction[x][y] = ACTION_DEFAULT;
1664 GfxDir[x][y] = MV_NO_MOVING;
1668 for (y = 0; y < lev_fieldy; y++)
1670 for (x = 0; x < lev_fieldx; x++)
1672 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1674 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1676 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1679 InitField(x, y, TRUE);
1685 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1686 emulate_sb ? EMU_SOKOBAN :
1687 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1689 /* initialize explosion and ignition delay */
1690 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1692 if (!IS_CUSTOM_ELEMENT(i))
1695 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1696 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1697 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1698 int last_phase = (num_phase + 1) * delay;
1699 int half_phase = (num_phase / 2) * delay;
1701 element_info[i].explosion_delay = last_phase - 1;
1702 element_info[i].ignition_delay = half_phase;
1704 if (i == EL_BLACK_ORB)
1705 element_info[i].ignition_delay = 1;
1709 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1710 element_info[i].explosion_delay = 1;
1712 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1713 element_info[i].ignition_delay = 1;
1717 /* correct non-moving belts to start moving left */
1718 for (i = 0; i < NUM_BELTS; i++)
1719 if (game.belt_dir[i] == MV_NO_MOVING)
1720 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1722 /* check if any connected player was not found in playfield */
1723 for (i = 0; i < MAX_PLAYERS; i++)
1725 struct PlayerInfo *player = &stored_player[i];
1727 if (player->connected && !player->present)
1729 for (j = 0; j < MAX_PLAYERS; j++)
1731 struct PlayerInfo *some_player = &stored_player[j];
1732 int jx = some_player->jx, jy = some_player->jy;
1734 /* assign first free player found that is present in the playfield */
1735 if (some_player->present && !some_player->connected)
1737 player->present = TRUE;
1738 player->active = TRUE;
1740 some_player->present = FALSE;
1741 some_player->active = FALSE;
1744 player->element_nr = some_player->element_nr;
1747 player->block_last_field = some_player->block_last_field;
1748 player->block_delay_adjustment = some_player->block_delay_adjustment;
1750 StorePlayer[jx][jy] = player->element_nr;
1751 player->jx = player->last_jx = jx;
1752 player->jy = player->last_jy = jy;
1762 /* when playing a tape, eliminate all players which do not participate */
1764 for (i = 0; i < MAX_PLAYERS; i++)
1766 if (stored_player[i].active && !tape.player_participates[i])
1768 struct PlayerInfo *player = &stored_player[i];
1769 int jx = player->jx, jy = player->jy;
1771 player->active = FALSE;
1772 StorePlayer[jx][jy] = 0;
1773 Feld[jx][jy] = EL_EMPTY;
1777 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1779 /* when in single player mode, eliminate all but the first active player */
1781 for (i = 0; i < MAX_PLAYERS; i++)
1783 if (stored_player[i].active)
1785 for (j = i + 1; j < MAX_PLAYERS; j++)
1787 if (stored_player[j].active)
1789 struct PlayerInfo *player = &stored_player[j];
1790 int jx = player->jx, jy = player->jy;
1792 player->active = FALSE;
1793 player->present = FALSE;
1795 StorePlayer[jx][jy] = 0;
1796 Feld[jx][jy] = EL_EMPTY;
1803 /* when recording the game, store which players take part in the game */
1806 for (i = 0; i < MAX_PLAYERS; i++)
1807 if (stored_player[i].active)
1808 tape.player_participates[i] = TRUE;
1813 for (i = 0; i < MAX_PLAYERS; i++)
1815 struct PlayerInfo *player = &stored_player[i];
1817 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1822 if (local_player == player)
1823 printf("Player %d is local player.\n", i+1);
1827 if (BorderElement == EL_EMPTY)
1830 SBX_Right = lev_fieldx - SCR_FIELDX;
1832 SBY_Lower = lev_fieldy - SCR_FIELDY;
1837 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1839 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1842 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1843 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1845 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1846 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1848 /* if local player not found, look for custom element that might create
1849 the player (make some assumptions about the right custom element) */
1850 if (!local_player->present)
1852 int start_x = 0, start_y = 0;
1853 int found_rating = 0;
1854 int found_element = EL_UNDEFINED;
1856 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1858 int element = Feld[x][y];
1863 if (!IS_CUSTOM_ELEMENT(element))
1866 if (CAN_CHANGE(element))
1868 for (i = 0; i < element_info[element].num_change_pages; i++)
1870 content = element_info[element].change_page[i].target_element;
1871 is_player = ELEM_IS_PLAYER(content);
1873 if (is_player && (found_rating < 3 || element < found_element))
1879 found_element = element;
1884 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1886 content = element_info[element].content[xx][yy];
1887 is_player = ELEM_IS_PLAYER(content);
1889 if (is_player && (found_rating < 2 || element < found_element))
1891 start_x = x + xx - 1;
1892 start_y = y + yy - 1;
1895 found_element = element;
1898 if (!CAN_CHANGE(element))
1901 for (i = 0; i < element_info[element].num_change_pages; i++)
1903 content= element_info[element].change_page[i].target_content[xx][yy];
1904 is_player = ELEM_IS_PLAYER(content);
1906 if (is_player && (found_rating < 1 || element < found_element))
1908 start_x = x + xx - 1;
1909 start_y = y + yy - 1;
1912 found_element = element;
1918 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1919 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1922 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1923 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1928 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1929 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1930 local_player->jx - MIDPOSX);
1932 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1933 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1934 local_player->jy - MIDPOSY);
1937 if (!game.restart_level)
1938 CloseDoor(DOOR_CLOSE_1);
1940 /* !!! FIX THIS (START) !!! */
1941 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1943 InitGameEngine_EM();
1950 /* after drawing the level, correct some elements */
1951 if (game.timegate_time_left == 0)
1952 CloseAllOpenTimegates();
1954 if (setup.soft_scrolling)
1955 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1957 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1960 /* !!! FIX THIS (END) !!! */
1962 if (!game.restart_level)
1964 /* copy default game door content to main double buffer */
1965 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1966 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1969 DrawGameDoorValues();
1971 if (!game.restart_level)
1975 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1976 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1977 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1981 /* copy actual game door content to door double buffer for OpenDoor() */
1982 BlitBitmap(drawto, bitmap_db_door,
1983 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1985 OpenDoor(DOOR_OPEN_ALL);
1987 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1989 if (setup.sound_music)
1992 KeyboardAutoRepeatOffUnlessAutoplay();
1996 for (i = 0; i < MAX_PLAYERS; i++)
1997 printf("Player %d %sactive.\n",
1998 i + 1, (stored_player[i].active ? "" : "not "));
2002 game.restart_level = FALSE;
2005 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2007 /* this is used for non-R'n'D game engines to update certain engine values */
2009 /* needed to determine if sounds are played within the visible screen area */
2010 scroll_x = actual_scroll_x;
2011 scroll_y = actual_scroll_y;
2014 void InitMovDir(int x, int y)
2016 int i, element = Feld[x][y];
2017 static int xy[4][2] =
2024 static int direction[3][4] =
2026 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2027 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2028 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2037 Feld[x][y] = EL_BUG;
2038 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2041 case EL_SPACESHIP_RIGHT:
2042 case EL_SPACESHIP_UP:
2043 case EL_SPACESHIP_LEFT:
2044 case EL_SPACESHIP_DOWN:
2045 Feld[x][y] = EL_SPACESHIP;
2046 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2049 case EL_BD_BUTTERFLY_RIGHT:
2050 case EL_BD_BUTTERFLY_UP:
2051 case EL_BD_BUTTERFLY_LEFT:
2052 case EL_BD_BUTTERFLY_DOWN:
2053 Feld[x][y] = EL_BD_BUTTERFLY;
2054 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2057 case EL_BD_FIREFLY_RIGHT:
2058 case EL_BD_FIREFLY_UP:
2059 case EL_BD_FIREFLY_LEFT:
2060 case EL_BD_FIREFLY_DOWN:
2061 Feld[x][y] = EL_BD_FIREFLY;
2062 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2065 case EL_PACMAN_RIGHT:
2067 case EL_PACMAN_LEFT:
2068 case EL_PACMAN_DOWN:
2069 Feld[x][y] = EL_PACMAN;
2070 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2073 case EL_SP_SNIKSNAK:
2074 MovDir[x][y] = MV_UP;
2077 case EL_SP_ELECTRON:
2078 MovDir[x][y] = MV_LEFT;
2085 Feld[x][y] = EL_MOLE;
2086 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2090 if (IS_CUSTOM_ELEMENT(element))
2092 struct ElementInfo *ei = &element_info[element];
2093 int move_direction_initial = ei->move_direction_initial;
2094 int move_pattern = ei->move_pattern;
2096 if (move_direction_initial == MV_START_PREVIOUS)
2098 if (MovDir[x][y] != MV_NO_MOVING)
2101 move_direction_initial = MV_START_AUTOMATIC;
2104 if (move_direction_initial == MV_START_RANDOM)
2105 MovDir[x][y] = 1 << RND(4);
2106 else if (move_direction_initial & MV_ANY_DIRECTION)
2107 MovDir[x][y] = move_direction_initial;
2108 else if (move_pattern == MV_ALL_DIRECTIONS ||
2109 move_pattern == MV_TURNING_LEFT ||
2110 move_pattern == MV_TURNING_RIGHT ||
2111 move_pattern == MV_TURNING_LEFT_RIGHT ||
2112 move_pattern == MV_TURNING_RIGHT_LEFT ||
2113 move_pattern == MV_TURNING_RANDOM)
2114 MovDir[x][y] = 1 << RND(4);
2115 else if (move_pattern == MV_HORIZONTAL)
2116 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2117 else if (move_pattern == MV_VERTICAL)
2118 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2119 else if (move_pattern & MV_ANY_DIRECTION)
2120 MovDir[x][y] = element_info[element].move_pattern;
2121 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2122 move_pattern == MV_ALONG_RIGHT_SIDE)
2124 /* use random direction as default start direction */
2125 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2126 MovDir[x][y] = 1 << RND(4);
2128 for (i = 0; i < NUM_DIRECTIONS; i++)
2130 int x1 = x + xy[i][0];
2131 int y1 = y + xy[i][1];
2133 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2135 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2136 MovDir[x][y] = direction[0][i];
2138 MovDir[x][y] = direction[1][i];
2147 MovDir[x][y] = 1 << RND(4);
2149 if (element != EL_BUG &&
2150 element != EL_SPACESHIP &&
2151 element != EL_BD_BUTTERFLY &&
2152 element != EL_BD_FIREFLY)
2155 for (i = 0; i < NUM_DIRECTIONS; i++)
2157 int x1 = x + xy[i][0];
2158 int y1 = y + xy[i][1];
2160 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2162 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2164 MovDir[x][y] = direction[0][i];
2167 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2168 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2170 MovDir[x][y] = direction[1][i];
2179 GfxDir[x][y] = MovDir[x][y];
2182 void InitAmoebaNr(int x, int y)
2185 int group_nr = AmoebeNachbarNr(x, y);
2189 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2191 if (AmoebaCnt[i] == 0)
2199 AmoebaNr[x][y] = group_nr;
2200 AmoebaCnt[group_nr]++;
2201 AmoebaCnt2[group_nr]++;
2207 boolean raise_level = FALSE;
2209 if (local_player->MovPos)
2212 if (tape.auto_play) /* tape might already be stopped here */
2213 tape.auto_play_level_solved = TRUE;
2215 local_player->LevelSolved = FALSE;
2217 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2221 if (!tape.playing && setup.sound_loops)
2222 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2223 SND_CTRL_PLAY_LOOP);
2225 while (TimeLeft > 0)
2227 if (!tape.playing && !setup.sound_loops)
2228 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2229 if (TimeLeft > 0 && !(TimeLeft % 10))
2230 RaiseScore(level.score[SC_TIME_BONUS]);
2231 if (TimeLeft > 100 && !(TimeLeft % 10))
2236 DrawGameValue_Time(TimeLeft);
2244 if (!tape.playing && setup.sound_loops)
2245 StopSound(SND_GAME_LEVELTIME_BONUS);
2247 else if (level.time == 0) /* level without time limit */
2249 if (!tape.playing && setup.sound_loops)
2250 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2251 SND_CTRL_PLAY_LOOP);
2253 while (TimePlayed < 999)
2255 if (!tape.playing && !setup.sound_loops)
2256 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2257 if (TimePlayed < 999 && !(TimePlayed % 10))
2258 RaiseScore(level.score[SC_TIME_BONUS]);
2259 if (TimePlayed < 900 && !(TimePlayed % 10))
2264 DrawGameValue_Time(TimePlayed);
2272 if (!tape.playing && setup.sound_loops)
2273 StopSound(SND_GAME_LEVELTIME_BONUS);
2276 /* close exit door after last player */
2277 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2278 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2279 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2281 int element = Feld[ExitX][ExitY];
2283 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2284 EL_SP_EXIT_CLOSING);
2286 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2289 /* Hero disappears */
2290 if (ExitX >= 0 && ExitY >= 0)
2291 DrawLevelField(ExitX, ExitY);
2298 CloseDoor(DOOR_CLOSE_1);
2303 SaveTape(tape.level_nr); /* Ask to save tape */
2306 if (level_nr == leveldir_current->handicap_level)
2308 leveldir_current->handicap_level++;
2309 SaveLevelSetup_SeriesInfo();
2312 if (level_editor_test_game)
2313 local_player->score = -1; /* no highscore when playing from editor */
2314 else if (level_nr < leveldir_current->last_level)
2315 raise_level = TRUE; /* advance to next level */
2317 if ((hi_pos = NewHiScore()) >= 0)
2319 game_status = GAME_MODE_SCORES;
2320 DrawHallOfFame(hi_pos);
2329 game_status = GAME_MODE_MAIN;
2346 LoadScore(level_nr);
2348 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2349 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2352 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2354 if (local_player->score > highscore[k].Score)
2356 /* player has made it to the hall of fame */
2358 if (k < MAX_SCORE_ENTRIES - 1)
2360 int m = MAX_SCORE_ENTRIES - 1;
2363 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2364 if (!strcmp(setup.player_name, highscore[l].Name))
2366 if (m == k) /* player's new highscore overwrites his old one */
2370 for (l = m; l > k; l--)
2372 strcpy(highscore[l].Name, highscore[l - 1].Name);
2373 highscore[l].Score = highscore[l - 1].Score;
2380 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2381 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2382 highscore[k].Score = local_player->score;
2388 else if (!strncmp(setup.player_name, highscore[k].Name,
2389 MAX_PLAYER_NAME_LEN))
2390 break; /* player already there with a higher score */
2396 SaveScore(level_nr);
2401 inline static int getElementMoveStepsize(int x, int y)
2403 int element = Feld[x][y];
2404 int direction = MovDir[x][y];
2405 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2406 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2407 int horiz_move = (dx != 0);
2408 int sign = (horiz_move ? dx : dy);
2409 int step = sign * element_info[element].move_stepsize;
2411 /* special values for move stepsize for spring and things on conveyor belt */
2415 if (element == EL_SPRING)
2416 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2417 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2418 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2419 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2421 if (CAN_FALL(element) &&
2422 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2423 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2424 else if (element == EL_SPRING)
2425 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2432 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2434 if (player->GfxAction != action || player->GfxDir != dir)
2437 printf("Player frame reset! (%d => %d, %d => %d)\n",
2438 player->GfxAction, action, player->GfxDir, dir);
2441 player->GfxAction = action;
2442 player->GfxDir = dir;
2444 player->StepFrame = 0;
2448 static void ResetRandomAnimationValue(int x, int y)
2450 GfxRandom[x][y] = INIT_GFX_RANDOM();
2453 static void ResetGfxAnimation(int x, int y)
2456 GfxAction[x][y] = ACTION_DEFAULT;
2457 GfxDir[x][y] = MovDir[x][y];
2460 void InitMovingField(int x, int y, int direction)
2462 int element = Feld[x][y];
2463 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2464 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2468 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2469 ResetGfxAnimation(x, y);
2471 MovDir[x][y] = direction;
2472 GfxDir[x][y] = direction;
2473 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2474 ACTION_FALLING : ACTION_MOVING);
2476 /* this is needed for CEs with property "can move" / "not moving" */
2478 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2480 if (Feld[newx][newy] == EL_EMPTY)
2481 Feld[newx][newy] = EL_BLOCKED;
2483 MovDir[newx][newy] = MovDir[x][y];
2484 GfxFrame[newx][newy] = GfxFrame[x][y];
2485 GfxRandom[newx][newy] = GfxRandom[x][y];
2486 GfxAction[newx][newy] = GfxAction[x][y];
2487 GfxDir[newx][newy] = GfxDir[x][y];
2491 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2493 int direction = MovDir[x][y];
2494 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2495 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2501 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2503 int oldx = x, oldy = y;
2504 int direction = MovDir[x][y];
2506 if (direction == MV_LEFT)
2508 else if (direction == MV_RIGHT)
2510 else if (direction == MV_UP)
2512 else if (direction == MV_DOWN)
2515 *comes_from_x = oldx;
2516 *comes_from_y = oldy;
2519 int MovingOrBlocked2Element(int x, int y)
2521 int element = Feld[x][y];
2523 if (element == EL_BLOCKED)
2527 Blocked2Moving(x, y, &oldx, &oldy);
2528 return Feld[oldx][oldy];
2534 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2536 /* like MovingOrBlocked2Element(), but if element is moving
2537 and (x,y) is the field the moving element is just leaving,
2538 return EL_BLOCKED instead of the element value */
2539 int element = Feld[x][y];
2541 if (IS_MOVING(x, y))
2543 if (element == EL_BLOCKED)
2547 Blocked2Moving(x, y, &oldx, &oldy);
2548 return Feld[oldx][oldy];
2557 static void RemoveField(int x, int y)
2559 Feld[x][y] = EL_EMPTY;
2566 ChangeDelay[x][y] = 0;
2567 ChangePage[x][y] = -1;
2568 Pushed[x][y] = FALSE;
2571 ExplodeField[x][y] = EX_TYPE_NONE;
2574 GfxElement[x][y] = EL_UNDEFINED;
2575 GfxAction[x][y] = ACTION_DEFAULT;
2576 GfxDir[x][y] = MV_NO_MOVING;
2579 void RemoveMovingField(int x, int y)
2581 int oldx = x, oldy = y, newx = x, newy = y;
2582 int element = Feld[x][y];
2583 int next_element = EL_UNDEFINED;
2585 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2588 if (IS_MOVING(x, y))
2590 Moving2Blocked(x, y, &newx, &newy);
2592 if (Feld[newx][newy] != EL_BLOCKED)
2594 /* element is moving, but target field is not free (blocked), but
2595 already occupied by something different (example: acid pool);
2596 in this case, only remove the moving field, but not the target */
2598 RemoveField(oldx, oldy);
2600 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2602 DrawLevelField(oldx, oldy);
2607 else if (element == EL_BLOCKED)
2609 Blocked2Moving(x, y, &oldx, &oldy);
2610 if (!IS_MOVING(oldx, oldy))
2614 if (element == EL_BLOCKED &&
2615 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2616 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2617 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2618 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2619 next_element = get_next_element(Feld[oldx][oldy]);
2621 RemoveField(oldx, oldy);
2622 RemoveField(newx, newy);
2624 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2626 if (next_element != EL_UNDEFINED)
2627 Feld[oldx][oldy] = next_element;
2629 DrawLevelField(oldx, oldy);
2630 DrawLevelField(newx, newy);
2633 void DrawDynamite(int x, int y)
2635 int sx = SCREENX(x), sy = SCREENY(y);
2636 int graphic = el2img(Feld[x][y]);
2639 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2642 if (IS_WALKABLE_INSIDE(Back[x][y]))
2646 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2647 else if (Store[x][y])
2648 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2650 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2652 if (Back[x][y] || Store[x][y])
2653 DrawGraphicThruMask(sx, sy, graphic, frame);
2655 DrawGraphic(sx, sy, graphic, frame);
2658 void CheckDynamite(int x, int y)
2660 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2664 if (MovDelay[x][y] != 0)
2667 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2673 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2678 void DrawRelocatePlayer(struct PlayerInfo *player)
2680 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2681 boolean no_delay = (tape.warp_forward);
2682 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2683 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2684 int jx = player->jx;
2685 int jy = player->jy;
2687 if (level.instant_relocation)
2689 int offset = (setup.scroll_delay ? 3 : 0);
2691 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2693 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2694 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2695 local_player->jx - MIDPOSX);
2697 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2698 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2699 local_player->jy - MIDPOSY);
2703 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2704 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2705 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2707 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2708 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2709 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2711 /* don't scroll over playfield boundaries */
2712 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2713 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2715 /* don't scroll over playfield boundaries */
2716 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2717 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2720 RedrawPlayfield(TRUE, 0,0,0,0);
2724 int scroll_xx = -999, scroll_yy = -999;
2726 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2728 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2731 int fx = FX, fy = FY;
2733 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2734 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2735 local_player->jx - MIDPOSX);
2737 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2738 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2739 local_player->jy - MIDPOSY);
2741 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2742 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2744 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2750 fx += dx * TILEX / 2;
2751 fy += dy * TILEY / 2;
2753 ScrollLevel(dx, dy);
2756 /* scroll in two steps of half tile size to make things smoother */
2757 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2759 Delay(wait_delay_value);
2761 /* scroll second step to align at full tile size */
2763 Delay(wait_delay_value);
2768 Delay(wait_delay_value);
2772 void RelocatePlayer(int jx, int jy, int el_player_raw)
2774 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
2775 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2776 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2777 boolean no_delay = (tape.warp_forward);
2778 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2779 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2780 int old_jx = player->jx;
2781 int old_jy = player->jy;
2782 int old_element = Feld[old_jx][old_jy];
2783 int element = Feld[jx][jy];
2784 boolean player_relocated = (old_jx != jx || old_jy != jy);
2786 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2787 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2788 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2789 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2790 int leave_side_horiz = move_dir_horiz;
2791 int leave_side_vert = move_dir_vert;
2792 int enter_side = enter_side_horiz | enter_side_vert;
2793 int leave_side = leave_side_horiz | leave_side_vert;
2795 if (player->GameOver) /* do not reanimate dead player */
2798 if (!player_relocated) /* no need to relocate the player */
2801 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2803 RemoveField(jx, jy); /* temporarily remove newly placed player */
2804 DrawLevelField(jx, jy);
2807 if (player->present)
2809 while (player->MovPos)
2811 ScrollPlayer(player, SCROLL_GO_ON);
2812 ScrollScreen(NULL, SCROLL_GO_ON);
2814 AdvanceFrameAndPlayerCounters(player->index_nr);
2819 Delay(wait_delay_value);
2822 DrawPlayer(player); /* needed here only to cleanup last field */
2823 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2825 player->is_moving = FALSE;
2828 if (IS_CUSTOM_ELEMENT(old_element))
2829 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
2831 player->index_bit, leave_side);
2833 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
2835 player->index_bit, leave_side);
2837 Feld[jx][jy] = el_player;
2838 InitPlayerField(jx, jy, el_player, TRUE);
2840 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
2842 Feld[jx][jy] = element;
2843 InitField(jx, jy, FALSE);
2846 if (player == local_player) /* only visually relocate local player */
2847 DrawRelocatePlayer(player);
2849 TestIfHeroTouchesBadThing(jx, jy);
2850 TestIfPlayerTouchesCustomElement(jx, jy);
2852 if (IS_CUSTOM_ELEMENT(element))
2853 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
2854 player->index_bit, enter_side);
2856 CheckTriggeredElementChangeByPlayer(jx, jy, element,
2858 player->index_bit, enter_side);
2861 void Explode(int ex, int ey, int phase, int mode)
2867 /* !!! eliminate this variable !!! */
2868 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2870 if (game.explosions_delayed)
2872 ExplodeField[ex][ey] = mode;
2876 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2878 int center_element = Feld[ex][ey];
2881 /* --- This is only really needed (and now handled) in "Impact()". --- */
2882 /* do not explode moving elements that left the explode field in time */
2883 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2884 center_element == EL_EMPTY &&
2885 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
2889 if (mode == EX_TYPE_NORMAL ||
2890 mode == EX_TYPE_CENTER ||
2891 mode == EX_TYPE_CROSS)
2892 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2894 /* remove things displayed in background while burning dynamite */
2895 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2898 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2900 /* put moving element to center field (and let it explode there) */
2901 center_element = MovingOrBlocked2Element(ex, ey);
2902 RemoveMovingField(ex, ey);
2903 Feld[ex][ey] = center_element;
2906 last_phase = element_info[center_element].explosion_delay + 1;
2908 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2910 int xx = x - ex + 1;
2911 int yy = y - ey + 1;
2914 if (!IN_LEV_FIELD(x, y) ||
2915 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
2916 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
2919 element = Feld[x][y];
2921 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2923 element = MovingOrBlocked2Element(x, y);
2925 if (!IS_EXPLOSION_PROOF(element))
2926 RemoveMovingField(x, y);
2929 /* indestructible elements can only explode in center (but not flames) */
2930 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
2931 mode == EX_TYPE_BORDER)) ||
2932 element == EL_FLAMES)
2935 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
2936 behaviour, for example when touching a yamyam that explodes to rocks
2937 with active deadly shield, a rock is created under the player !!! */
2938 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
2940 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
2941 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
2942 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
2944 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2947 if (IS_ACTIVE_BOMB(element))
2949 /* re-activate things under the bomb like gate or penguin */
2950 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
2957 /* save walkable background elements while explosion on same tile */
2958 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
2959 (x != ex || y != ey || mode == EX_TYPE_BORDER))
2960 Back[x][y] = element;
2962 /* ignite explodable elements reached by other explosion */
2963 if (element == EL_EXPLOSION)
2964 element = Store2[x][y];
2966 if (AmoebaNr[x][y] &&
2967 (element == EL_AMOEBA_FULL ||
2968 element == EL_BD_AMOEBA ||
2969 element == EL_AMOEBA_GROWING))
2971 AmoebaCnt[AmoebaNr[x][y]]--;
2972 AmoebaCnt2[AmoebaNr[x][y]]--;
2977 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2979 switch(StorePlayer[ex][ey])
2982 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2985 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2988 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2992 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2996 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
2997 Store[x][y] = EL_EMPTY;
2999 else if (center_element == EL_MOLE)
3000 Store[x][y] = EL_EMERALD_RED;
3001 else if (center_element == EL_PENGUIN)
3002 Store[x][y] = EL_EMERALD_PURPLE;
3003 else if (center_element == EL_BUG)
3004 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3005 else if (center_element == EL_BD_BUTTERFLY)
3006 Store[x][y] = EL_BD_DIAMOND;
3007 else if (center_element == EL_SP_ELECTRON)
3008 Store[x][y] = EL_SP_INFOTRON;
3009 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3010 Store[x][y] = level.amoeba_content;
3011 else if (center_element == EL_YAMYAM)
3012 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3013 else if (IS_CUSTOM_ELEMENT(center_element) &&
3014 element_info[center_element].content[xx][yy] != EL_EMPTY)
3015 Store[x][y] = element_info[center_element].content[xx][yy];
3016 else if (element == EL_WALL_EMERALD)
3017 Store[x][y] = EL_EMERALD;
3018 else if (element == EL_WALL_DIAMOND)
3019 Store[x][y] = EL_DIAMOND;
3020 else if (element == EL_WALL_BD_DIAMOND)
3021 Store[x][y] = EL_BD_DIAMOND;
3022 else if (element == EL_WALL_EMERALD_YELLOW)
3023 Store[x][y] = EL_EMERALD_YELLOW;
3024 else if (element == EL_WALL_EMERALD_RED)
3025 Store[x][y] = EL_EMERALD_RED;
3026 else if (element == EL_WALL_EMERALD_PURPLE)
3027 Store[x][y] = EL_EMERALD_PURPLE;
3028 else if (element == EL_WALL_PEARL)
3029 Store[x][y] = EL_PEARL;
3030 else if (element == EL_WALL_CRYSTAL)
3031 Store[x][y] = EL_CRYSTAL;
3032 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3033 Store[x][y] = element_info[element].content[1][1];
3035 Store[x][y] = EL_EMPTY;
3037 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3038 center_element == EL_AMOEBA_TO_DIAMOND)
3039 Store2[x][y] = element;
3041 Feld[x][y] = EL_EXPLOSION;
3042 GfxElement[x][y] = center_element;
3044 ExplodePhase[x][y] = 1;
3045 ExplodeDelay[x][y] = last_phase;
3050 if (center_element == EL_YAMYAM)
3051 game.yamyam_content_nr =
3052 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3064 GfxFrame[x][y] = 0; /* restart explosion animation */
3066 last_phase = ExplodeDelay[x][y];
3068 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3072 /* activate this even in non-DEBUG version until cause for crash in
3073 getGraphicAnimationFrame() (see below) is found and eliminated */
3078 if (GfxElement[x][y] == EL_UNDEFINED)
3081 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3082 printf("Explode(): This should never happen!\n");
3085 GfxElement[x][y] = EL_EMPTY;
3089 border_element = Store2[x][y];
3090 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3091 border_element = StorePlayer[x][y];
3093 if (phase == element_info[border_element].ignition_delay ||
3094 phase == last_phase)
3096 boolean border_explosion = FALSE;
3098 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3099 !PLAYER_EXPLOSION_PROTECTED(x, y))
3101 KillHeroUnlessExplosionProtected(x, y);
3102 border_explosion = TRUE;
3104 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3106 Feld[x][y] = Store2[x][y];
3109 border_explosion = TRUE;
3111 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3113 AmoebeUmwandeln(x, y);
3115 border_explosion = TRUE;
3118 /* if an element just explodes due to another explosion (chain-reaction),
3119 do not immediately end the new explosion when it was the last frame of
3120 the explosion (as it would be done in the following "if"-statement!) */
3121 if (border_explosion && phase == last_phase)
3125 if (phase == last_phase)
3129 element = Feld[x][y] = Store[x][y];
3130 Store[x][y] = Store2[x][y] = 0;
3131 GfxElement[x][y] = EL_UNDEFINED;
3133 /* player can escape from explosions and might therefore be still alive */
3134 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3135 element <= EL_PLAYER_IS_EXPLODING_4)
3136 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3138 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3139 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3140 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3143 /* restore probably existing indestructible background element */
3144 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3145 element = Feld[x][y] = Back[x][y];
3148 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3149 GfxDir[x][y] = MV_NO_MOVING;
3150 ChangeDelay[x][y] = 0;
3151 ChangePage[x][y] = -1;
3153 InitField_WithBug2(x, y, FALSE);
3155 DrawLevelField(x, y);
3157 TestIfElementTouchesCustomElement(x, y);
3159 if (GFX_CRUMBLED(element))
3160 DrawLevelFieldCrumbledSandNeighbours(x, y);
3162 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3163 StorePlayer[x][y] = 0;
3165 if (ELEM_IS_PLAYER(element))
3166 RelocatePlayer(x, y, element);
3168 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3170 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3171 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3174 DrawLevelFieldCrumbledSand(x, y);
3176 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3178 DrawLevelElement(x, y, Back[x][y]);
3179 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3181 else if (IS_WALKABLE_UNDER(Back[x][y]))
3183 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3184 DrawLevelElementThruMask(x, y, Back[x][y]);
3186 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3187 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3191 void DynaExplode(int ex, int ey)
3194 int dynabomb_element = Feld[ex][ey];
3195 int dynabomb_size = 1;
3196 boolean dynabomb_xl = FALSE;
3197 struct PlayerInfo *player;
3198 static int xy[4][2] =
3206 if (IS_ACTIVE_BOMB(dynabomb_element))
3208 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3209 dynabomb_size = player->dynabomb_size;
3210 dynabomb_xl = player->dynabomb_xl;
3211 player->dynabombs_left++;
3214 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3216 for (i = 0; i < NUM_DIRECTIONS; i++)
3218 for (j = 1; j <= dynabomb_size; j++)
3220 int x = ex + j * xy[i][0];
3221 int y = ey + j * xy[i][1];
3224 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3227 element = Feld[x][y];
3229 /* do not restart explosions of fields with active bombs */
3230 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3233 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3235 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3236 !IS_DIGGABLE(element) && !dynabomb_xl)
3242 void Bang(int x, int y)
3244 int element = MovingOrBlocked2Element(x, y);
3246 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3248 struct PlayerInfo *player = PLAYERINFO(x, y);
3250 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3251 player->element_nr);
3258 case EL_BD_BUTTERFLY:
3261 case EL_DARK_YAMYAM:
3265 RaiseScoreElement(element);
3266 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3268 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3269 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3270 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3271 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3272 case EL_DYNABOMB_INCREASE_NUMBER:
3273 case EL_DYNABOMB_INCREASE_SIZE:
3274 case EL_DYNABOMB_INCREASE_POWER:
3279 case EL_LAMP_ACTIVE:
3280 case EL_AMOEBA_TO_DIAMOND:
3281 if (IS_PLAYER(x, y))
3282 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3284 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3287 if (element_info[element].explosion_type == EXPLODES_CROSS)
3288 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3289 else if (element_info[element].explosion_type == EXPLODES_1X1)
3290 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3292 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3296 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3299 void SplashAcid(int x, int y)
3301 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3302 (!IN_LEV_FIELD(x - 1, y - 2) ||
3303 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3304 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3306 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3307 (!IN_LEV_FIELD(x + 1, y - 2) ||
3308 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3309 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3311 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3314 static void InitBeltMovement()
3316 static int belt_base_element[4] =
3318 EL_CONVEYOR_BELT_1_LEFT,
3319 EL_CONVEYOR_BELT_2_LEFT,
3320 EL_CONVEYOR_BELT_3_LEFT,
3321 EL_CONVEYOR_BELT_4_LEFT
3323 static int belt_base_active_element[4] =
3325 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3326 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3327 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3328 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3333 /* set frame order for belt animation graphic according to belt direction */
3334 for (i = 0; i < NUM_BELTS; i++)
3338 for (j = 0; j < NUM_BELT_PARTS; j++)
3340 int element = belt_base_active_element[belt_nr] + j;
3341 int graphic = el2img(element);
3343 if (game.belt_dir[i] == MV_LEFT)
3344 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3346 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3350 for (y = 0; y < lev_fieldy; y++)
3352 for (x = 0; x < lev_fieldx; x++)
3354 int element = Feld[x][y];
3356 for (i = 0; i < NUM_BELTS; i++)
3358 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3360 int e_belt_nr = getBeltNrFromBeltElement(element);
3363 if (e_belt_nr == belt_nr)
3365 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3367 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3375 static void ToggleBeltSwitch(int x, int y)
3377 static int belt_base_element[4] =
3379 EL_CONVEYOR_BELT_1_LEFT,
3380 EL_CONVEYOR_BELT_2_LEFT,
3381 EL_CONVEYOR_BELT_3_LEFT,
3382 EL_CONVEYOR_BELT_4_LEFT
3384 static int belt_base_active_element[4] =
3386 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3387 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3388 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3389 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3391 static int belt_base_switch_element[4] =
3393 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3394 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3395 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3396 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3398 static int belt_move_dir[4] =
3406 int element = Feld[x][y];
3407 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3408 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3409 int belt_dir = belt_move_dir[belt_dir_nr];
3412 if (!IS_BELT_SWITCH(element))
3415 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3416 game.belt_dir[belt_nr] = belt_dir;
3418 if (belt_dir_nr == 3)
3421 /* set frame order for belt animation graphic according to belt direction */
3422 for (i = 0; i < NUM_BELT_PARTS; i++)
3424 int element = belt_base_active_element[belt_nr] + i;
3425 int graphic = el2img(element);
3427 if (belt_dir == MV_LEFT)
3428 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3430 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3433 for (yy = 0; yy < lev_fieldy; yy++)
3435 for (xx = 0; xx < lev_fieldx; xx++)
3437 int element = Feld[xx][yy];
3439 if (IS_BELT_SWITCH(element))
3441 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3443 if (e_belt_nr == belt_nr)
3445 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3446 DrawLevelField(xx, yy);
3449 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3451 int e_belt_nr = getBeltNrFromBeltElement(element);
3453 if (e_belt_nr == belt_nr)
3455 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3457 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3458 DrawLevelField(xx, yy);
3461 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3463 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3465 if (e_belt_nr == belt_nr)
3467 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3469 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3470 DrawLevelField(xx, yy);
3477 static void ToggleSwitchgateSwitch(int x, int y)
3481 game.switchgate_pos = !game.switchgate_pos;
3483 for (yy = 0; yy < lev_fieldy; yy++)
3485 for (xx = 0; xx < lev_fieldx; xx++)
3487 int element = Feld[xx][yy];
3489 if (element == EL_SWITCHGATE_SWITCH_UP ||
3490 element == EL_SWITCHGATE_SWITCH_DOWN)
3492 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3493 DrawLevelField(xx, yy);
3495 else if (element == EL_SWITCHGATE_OPEN ||
3496 element == EL_SWITCHGATE_OPENING)
3498 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3500 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3502 else if (element == EL_SWITCHGATE_CLOSED ||
3503 element == EL_SWITCHGATE_CLOSING)
3505 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3507 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3513 static int getInvisibleActiveFromInvisibleElement(int element)
3515 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3516 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3517 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3521 static int getInvisibleFromInvisibleActiveElement(int element)
3523 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3524 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3525 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3529 static void RedrawAllLightSwitchesAndInvisibleElements()
3533 for (y = 0; y < lev_fieldy; y++)
3535 for (x = 0; x < lev_fieldx; x++)
3537 int element = Feld[x][y];
3539 if (element == EL_LIGHT_SWITCH &&
3540 game.light_time_left > 0)
3542 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3543 DrawLevelField(x, y);
3545 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3546 game.light_time_left == 0)
3548 Feld[x][y] = EL_LIGHT_SWITCH;
3549 DrawLevelField(x, y);
3551 else if (element == EL_INVISIBLE_STEELWALL ||
3552 element == EL_INVISIBLE_WALL ||
3553 element == EL_INVISIBLE_SAND)
3555 if (game.light_time_left > 0)
3556 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3558 DrawLevelField(x, y);
3560 /* uncrumble neighbour fields, if needed */
3561 if (element == EL_INVISIBLE_SAND)
3562 DrawLevelFieldCrumbledSandNeighbours(x, y);
3564 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3565 element == EL_INVISIBLE_WALL_ACTIVE ||
3566 element == EL_INVISIBLE_SAND_ACTIVE)
3568 if (game.light_time_left == 0)
3569 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3571 DrawLevelField(x, y);
3573 /* re-crumble neighbour fields, if needed */
3574 if (element == EL_INVISIBLE_SAND)
3575 DrawLevelFieldCrumbledSandNeighbours(x, y);
3581 static void ToggleLightSwitch(int x, int y)
3583 int element = Feld[x][y];
3585 game.light_time_left =
3586 (element == EL_LIGHT_SWITCH ?
3587 level.time_light * FRAMES_PER_SECOND : 0);
3589 RedrawAllLightSwitchesAndInvisibleElements();
3592 static void ActivateTimegateSwitch(int x, int y)
3596 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3598 for (yy = 0; yy < lev_fieldy; yy++)
3600 for (xx = 0; xx < lev_fieldx; xx++)
3602 int element = Feld[xx][yy];
3604 if (element == EL_TIMEGATE_CLOSED ||
3605 element == EL_TIMEGATE_CLOSING)
3607 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3608 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3612 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3614 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3615 DrawLevelField(xx, yy);
3622 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3625 void Impact(int x, int y)
3627 boolean last_line = (y == lev_fieldy - 1);
3628 boolean object_hit = FALSE;
3629 boolean impact = (last_line || object_hit);
3630 int element = Feld[x][y];
3631 int smashed = EL_STEELWALL;
3633 if (!last_line) /* check if element below was hit */
3635 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3638 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3639 MovDir[x][y + 1] != MV_DOWN ||
3640 MovPos[x][y + 1] <= TILEY / 2));
3642 /* do not smash moving elements that left the smashed field in time */
3643 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3644 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3648 smashed = MovingOrBlocked2Element(x, y + 1);
3650 impact = (last_line || object_hit);
3653 if (!last_line && smashed == EL_ACID) /* element falls into acid */
3655 SplashAcid(x, y + 1);
3659 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
3660 /* only reset graphic animation if graphic really changes after impact */
3662 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3664 ResetGfxAnimation(x, y);
3665 DrawLevelField(x, y);
3668 if (impact && CAN_EXPLODE_IMPACT(element))
3673 else if (impact && element == EL_PEARL)
3675 ResetGfxAnimation(x, y);
3677 Feld[x][y] = EL_PEARL_BREAKING;
3678 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3681 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
3683 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3688 if (impact && element == EL_AMOEBA_DROP)
3690 if (object_hit && IS_PLAYER(x, y + 1))
3691 KillHeroUnlessEnemyProtected(x, y + 1);
3692 else if (object_hit && smashed == EL_PENGUIN)
3696 Feld[x][y] = EL_AMOEBA_GROWING;
3697 Store[x][y] = EL_AMOEBA_WET;
3699 ResetRandomAnimationValue(x, y);
3704 if (object_hit) /* check which object was hit */
3706 if (CAN_PASS_MAGIC_WALL(element) &&
3707 (smashed == EL_MAGIC_WALL ||
3708 smashed == EL_BD_MAGIC_WALL))
3711 int activated_magic_wall =
3712 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3713 EL_BD_MAGIC_WALL_ACTIVE);
3715 /* activate magic wall / mill */
3716 for (yy = 0; yy < lev_fieldy; yy++)
3717 for (xx = 0; xx < lev_fieldx; xx++)
3718 if (Feld[xx][yy] == smashed)
3719 Feld[xx][yy] = activated_magic_wall;
3721 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3722 game.magic_wall_active = TRUE;
3724 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3725 SND_MAGIC_WALL_ACTIVATING :
3726 SND_BD_MAGIC_WALL_ACTIVATING));
3729 if (IS_PLAYER(x, y + 1))
3731 if (CAN_SMASH_PLAYER(element))
3733 KillHeroUnlessEnemyProtected(x, y + 1);
3737 else if (smashed == EL_PENGUIN)
3739 if (CAN_SMASH_PLAYER(element))
3745 else if (element == EL_BD_DIAMOND)
3747 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3753 else if (((element == EL_SP_INFOTRON ||
3754 element == EL_SP_ZONK) &&
3755 (smashed == EL_SP_SNIKSNAK ||
3756 smashed == EL_SP_ELECTRON ||
3757 smashed == EL_SP_DISK_ORANGE)) ||
3758 (element == EL_SP_INFOTRON &&
3759 smashed == EL_SP_DISK_YELLOW))
3764 else if (CAN_SMASH_EVERYTHING(element))
3766 if (IS_CLASSIC_ENEMY(smashed) ||
3767 CAN_EXPLODE_SMASHED(smashed))
3772 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3774 if (smashed == EL_LAMP ||
3775 smashed == EL_LAMP_ACTIVE)
3780 else if (smashed == EL_NUT)
3782 Feld[x][y + 1] = EL_NUT_BREAKING;
3783 PlayLevelSound(x, y, SND_NUT_BREAKING);
3784 RaiseScoreElement(EL_NUT);
3787 else if (smashed == EL_PEARL)
3789 ResetGfxAnimation(x, y);
3791 Feld[x][y + 1] = EL_PEARL_BREAKING;
3792 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3795 else if (smashed == EL_DIAMOND)
3797 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3798 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3801 else if (IS_BELT_SWITCH(smashed))
3803 ToggleBeltSwitch(x, y + 1);
3805 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3806 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3808 ToggleSwitchgateSwitch(x, y + 1);
3810 else if (smashed == EL_LIGHT_SWITCH ||
3811 smashed == EL_LIGHT_SWITCH_ACTIVE)
3813 ToggleLightSwitch(x, y + 1);
3818 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
3821 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
3823 CheckElementChangeBySide(x, y + 1, smashed, element,
3824 CE_SWITCHED, CH_SIDE_TOP);
3825 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
3826 CE_SWITCH_OF_X, CH_SIDE_TOP);
3831 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
3836 /* play sound of magic wall / mill */
3838 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3839 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3841 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3842 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3843 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3844 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3849 /* play sound of object that hits the ground */
3850 if (last_line || object_hit)
3851 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3854 inline static void TurnRoundExt(int x, int y)
3866 { 0, 0 }, { 0, 0 }, { 0, 0 },
3871 int left, right, back;
3875 { MV_DOWN, MV_UP, MV_RIGHT },
3876 { MV_UP, MV_DOWN, MV_LEFT },
3878 { MV_LEFT, MV_RIGHT, MV_DOWN },
3882 { MV_RIGHT, MV_LEFT, MV_UP }
3885 int element = Feld[x][y];
3886 int move_pattern = element_info[element].move_pattern;
3888 int old_move_dir = MovDir[x][y];
3889 int left_dir = turn[old_move_dir].left;
3890 int right_dir = turn[old_move_dir].right;
3891 int back_dir = turn[old_move_dir].back;
3893 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3894 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3895 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3896 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3898 int left_x = x + left_dx, left_y = y + left_dy;
3899 int right_x = x + right_dx, right_y = y + right_dy;
3900 int move_x = x + move_dx, move_y = y + move_dy;
3904 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3906 TestIfBadThingTouchesOtherBadThing(x, y);
3908 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
3909 MovDir[x][y] = right_dir;
3910 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3911 MovDir[x][y] = left_dir;
3913 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3915 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3918 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
3920 TestIfBadThingTouchesOtherBadThing(x, y);
3922 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
3923 MovDir[x][y] = left_dir;
3924 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3925 MovDir[x][y] = right_dir;
3927 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
3929 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3932 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3934 TestIfBadThingTouchesOtherBadThing(x, y);
3936 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
3937 MovDir[x][y] = left_dir;
3938 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
3939 MovDir[x][y] = right_dir;
3941 if (MovDir[x][y] != old_move_dir)
3944 else if (element == EL_YAMYAM)
3946 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
3947 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
3949 if (can_turn_left && can_turn_right)
3950 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3951 else if (can_turn_left)
3952 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3953 else if (can_turn_right)
3954 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3956 MovDir[x][y] = back_dir;
3958 MovDelay[x][y] = 16 + 16 * RND(3);
3960 else if (element == EL_DARK_YAMYAM)
3962 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
3964 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
3967 if (can_turn_left && can_turn_right)
3968 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3969 else if (can_turn_left)
3970 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3971 else if (can_turn_right)
3972 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3974 MovDir[x][y] = back_dir;
3976 MovDelay[x][y] = 16 + 16 * RND(3);
3978 else if (element == EL_PACMAN)
3980 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
3981 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
3983 if (can_turn_left && can_turn_right)
3984 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3985 else if (can_turn_left)
3986 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3987 else if (can_turn_right)
3988 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3990 MovDir[x][y] = back_dir;
3992 MovDelay[x][y] = 6 + RND(40);
3994 else if (element == EL_PIG)
3996 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
3997 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
3998 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
3999 boolean should_turn_left, should_turn_right, should_move_on;
4001 int rnd = RND(rnd_value);
4003 should_turn_left = (can_turn_left &&
4005 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4006 y + back_dy + left_dy)));
4007 should_turn_right = (can_turn_right &&
4009 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4010 y + back_dy + right_dy)));
4011 should_move_on = (can_move_on &&
4014 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4015 y + move_dy + left_dy) ||
4016 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4017 y + move_dy + right_dy)));
4019 if (should_turn_left || should_turn_right || should_move_on)
4021 if (should_turn_left && should_turn_right && should_move_on)
4022 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4023 rnd < 2 * rnd_value / 3 ? right_dir :
4025 else if (should_turn_left && should_turn_right)
4026 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4027 else if (should_turn_left && should_move_on)
4028 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4029 else if (should_turn_right && should_move_on)
4030 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4031 else if (should_turn_left)
4032 MovDir[x][y] = left_dir;
4033 else if (should_turn_right)
4034 MovDir[x][y] = right_dir;
4035 else if (should_move_on)
4036 MovDir[x][y] = old_move_dir;
4038 else if (can_move_on && rnd > rnd_value / 8)
4039 MovDir[x][y] = old_move_dir;
4040 else if (can_turn_left && can_turn_right)
4041 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4042 else if (can_turn_left && rnd > rnd_value / 8)
4043 MovDir[x][y] = left_dir;
4044 else if (can_turn_right && rnd > rnd_value/8)
4045 MovDir[x][y] = right_dir;
4047 MovDir[x][y] = back_dir;
4049 xx = x + move_xy[MovDir[x][y]].x;
4050 yy = y + move_xy[MovDir[x][y]].y;
4052 if (!IN_LEV_FIELD(xx, yy) ||
4053 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4054 MovDir[x][y] = old_move_dir;
4058 else if (element == EL_DRAGON)
4060 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4061 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4062 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4064 int rnd = RND(rnd_value);
4066 if (can_move_on && rnd > rnd_value / 8)
4067 MovDir[x][y] = old_move_dir;
4068 else if (can_turn_left && can_turn_right)
4069 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4070 else if (can_turn_left && rnd > rnd_value / 8)
4071 MovDir[x][y] = left_dir;
4072 else if (can_turn_right && rnd > rnd_value / 8)
4073 MovDir[x][y] = right_dir;
4075 MovDir[x][y] = back_dir;
4077 xx = x + move_xy[MovDir[x][y]].x;
4078 yy = y + move_xy[MovDir[x][y]].y;
4080 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4081 MovDir[x][y] = old_move_dir;
4085 else if (element == EL_MOLE)
4087 boolean can_move_on =
4088 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4089 IS_AMOEBOID(Feld[move_x][move_y]) ||
4090 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4093 boolean can_turn_left =
4094 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4095 IS_AMOEBOID(Feld[left_x][left_y])));
4097 boolean can_turn_right =
4098 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4099 IS_AMOEBOID(Feld[right_x][right_y])));
4101 if (can_turn_left && can_turn_right)
4102 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4103 else if (can_turn_left)
4104 MovDir[x][y] = left_dir;
4106 MovDir[x][y] = right_dir;
4109 if (MovDir[x][y] != old_move_dir)
4112 else if (element == EL_BALLOON)
4114 MovDir[x][y] = game.balloon_dir;
4117 else if (element == EL_SPRING)
4119 if (MovDir[x][y] & MV_HORIZONTAL &&
4120 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4121 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4122 MovDir[x][y] = MV_NO_MOVING;
4126 else if (element == EL_ROBOT ||
4127 element == EL_SATELLITE ||
4128 element == EL_PENGUIN)
4130 int attr_x = -1, attr_y = -1;
4141 for (i = 0; i < MAX_PLAYERS; i++)
4143 struct PlayerInfo *player = &stored_player[i];
4144 int jx = player->jx, jy = player->jy;
4146 if (!player->active)
4150 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4158 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4159 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4160 game.engine_version < VERSION_IDENT(3,1,0,0)))
4166 if (element == EL_PENGUIN)
4169 static int xy[4][2] =
4177 for (i = 0; i < NUM_DIRECTIONS; i++)
4179 int ex = x + xy[i][0];
4180 int ey = y + xy[i][1];
4182 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4191 MovDir[x][y] = MV_NO_MOVING;
4193 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4194 else if (attr_x > x)
4195 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4197 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4198 else if (attr_y > y)
4199 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4201 if (element == EL_ROBOT)
4205 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4206 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4207 Moving2Blocked(x, y, &newx, &newy);
4209 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4210 MovDelay[x][y] = 8 + 8 * !RND(3);
4212 MovDelay[x][y] = 16;
4214 else if (element == EL_PENGUIN)
4220 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4222 boolean first_horiz = RND(2);
4223 int new_move_dir = MovDir[x][y];
4226 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4227 Moving2Blocked(x, y, &newx, &newy);
4229 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4233 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4234 Moving2Blocked(x, y, &newx, &newy);
4236 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4239 MovDir[x][y] = old_move_dir;
4243 else /* (element == EL_SATELLITE) */
4249 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4251 boolean first_horiz = RND(2);
4252 int new_move_dir = MovDir[x][y];
4255 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4256 Moving2Blocked(x, y, &newx, &newy);
4258 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4262 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4263 Moving2Blocked(x, y, &newx, &newy);
4265 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4268 MovDir[x][y] = old_move_dir;
4273 else if (move_pattern == MV_TURNING_LEFT ||
4274 move_pattern == MV_TURNING_RIGHT ||
4275 move_pattern == MV_TURNING_LEFT_RIGHT ||
4276 move_pattern == MV_TURNING_RIGHT_LEFT ||
4277 move_pattern == MV_TURNING_RANDOM ||
4278 move_pattern == MV_ALL_DIRECTIONS)
4280 boolean can_turn_left =
4281 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4282 boolean can_turn_right =
4283 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4285 if (element_info[element].move_stepsize == 0) /* "not moving" */
4288 if (move_pattern == MV_TURNING_LEFT)
4289 MovDir[x][y] = left_dir;
4290 else if (move_pattern == MV_TURNING_RIGHT)
4291 MovDir[x][y] = right_dir;
4292 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4293 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4294 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4295 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4296 else if (move_pattern == MV_TURNING_RANDOM)
4297 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4298 can_turn_right && !can_turn_left ? right_dir :
4299 RND(2) ? left_dir : right_dir);
4300 else if (can_turn_left && can_turn_right)
4301 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4302 else if (can_turn_left)
4303 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4304 else if (can_turn_right)
4305 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4307 MovDir[x][y] = back_dir;
4309 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4311 else if (move_pattern == MV_HORIZONTAL ||
4312 move_pattern == MV_VERTICAL)
4314 if (move_pattern & old_move_dir)
4315 MovDir[x][y] = back_dir;
4316 else if (move_pattern == MV_HORIZONTAL)
4317 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4318 else if (move_pattern == MV_VERTICAL)
4319 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4321 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4323 else if (move_pattern & MV_ANY_DIRECTION)
4325 MovDir[x][y] = move_pattern;
4326 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4328 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4330 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4331 MovDir[x][y] = left_dir;
4332 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4333 MovDir[x][y] = right_dir;
4335 if (MovDir[x][y] != old_move_dir)
4336 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4338 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4340 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4341 MovDir[x][y] = right_dir;
4342 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4343 MovDir[x][y] = left_dir;
4345 if (MovDir[x][y] != old_move_dir)
4346 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4348 else if (move_pattern == MV_TOWARDS_PLAYER ||
4349 move_pattern == MV_AWAY_FROM_PLAYER)
4351 int attr_x = -1, attr_y = -1;
4353 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4364 for (i = 0; i < MAX_PLAYERS; i++)
4366 struct PlayerInfo *player = &stored_player[i];
4367 int jx = player->jx, jy = player->jy;
4369 if (!player->active)
4373 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4381 MovDir[x][y] = MV_NO_MOVING;
4383 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4384 else if (attr_x > x)
4385 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4387 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4388 else if (attr_y > y)
4389 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4391 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4393 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4395 boolean first_horiz = RND(2);
4396 int new_move_dir = MovDir[x][y];
4398 if (element_info[element].move_stepsize == 0) /* "not moving" */
4400 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
4401 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4407 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4408 Moving2Blocked(x, y, &newx, &newy);
4410 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4414 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4415 Moving2Blocked(x, y, &newx, &newy);
4417 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4420 MovDir[x][y] = old_move_dir;
4423 else if (move_pattern == MV_WHEN_PUSHED ||
4424 move_pattern == MV_WHEN_DROPPED)
4426 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4427 MovDir[x][y] = MV_NO_MOVING;
4431 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4433 static int test_xy[7][2] =
4443 static int test_dir[7] =
4453 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4454 int move_preference = -1000000; /* start with very low preference */
4455 int new_move_dir = MV_NO_MOVING;
4456 int start_test = RND(4);
4459 for (i = 0; i < NUM_DIRECTIONS; i++)
4461 int move_dir = test_dir[start_test + i];
4462 int move_dir_preference;
4464 xx = x + test_xy[start_test + i][0];
4465 yy = y + test_xy[start_test + i][1];
4467 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4468 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4470 new_move_dir = move_dir;
4475 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4478 move_dir_preference = -1 * RunnerVisit[xx][yy];
4479 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4480 move_dir_preference = PlayerVisit[xx][yy];
4482 if (move_dir_preference > move_preference)
4484 /* prefer field that has not been visited for the longest time */
4485 move_preference = move_dir_preference;
4486 new_move_dir = move_dir;
4488 else if (move_dir_preference == move_preference &&
4489 move_dir == old_move_dir)
4491 /* prefer last direction when all directions are preferred equally */
4492 move_preference = move_dir_preference;
4493 new_move_dir = move_dir;
4497 MovDir[x][y] = new_move_dir;
4498 if (old_move_dir != new_move_dir)
4499 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4503 static void TurnRound(int x, int y)
4505 int direction = MovDir[x][y];
4509 GfxDir[x][y] = MovDir[x][y];
4511 if (direction != MovDir[x][y])
4515 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4518 static boolean JustBeingPushed(int x, int y)
4522 for (i = 0; i < MAX_PLAYERS; i++)
4524 struct PlayerInfo *player = &stored_player[i];
4526 if (player->active && player->is_pushing && player->MovPos)
4528 int next_jx = player->jx + (player->jx - player->last_jx);
4529 int next_jy = player->jy + (player->jy - player->last_jy);
4531 if (x == next_jx && y == next_jy)
4539 void StartMoving(int x, int y)
4541 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4542 int element = Feld[x][y];
4547 if (MovDelay[x][y] == 0)
4548 GfxAction[x][y] = ACTION_DEFAULT;
4550 if (CAN_FALL(element) && y < lev_fieldy - 1)
4552 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4553 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4554 if (JustBeingPushed(x, y))
4557 if (element == EL_QUICKSAND_FULL)
4559 if (IS_FREE(x, y + 1))
4561 InitMovingField(x, y, MV_DOWN);
4562 started_moving = TRUE;
4564 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4565 Store[x][y] = EL_ROCK;
4567 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4569 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4571 if (!MovDelay[x][y])
4572 MovDelay[x][y] = TILEY + 1;
4581 Feld[x][y] = EL_QUICKSAND_EMPTY;
4582 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4583 Store[x][y + 1] = Store[x][y];
4586 PlayLevelSoundAction(x, y, ACTION_FILLING);
4589 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4590 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4592 InitMovingField(x, y, MV_DOWN);
4593 started_moving = TRUE;
4595 Feld[x][y] = EL_QUICKSAND_FILLING;
4596 Store[x][y] = element;
4598 PlayLevelSoundAction(x, y, ACTION_FILLING);
4600 else if (element == EL_MAGIC_WALL_FULL)
4602 if (IS_FREE(x, y + 1))
4604 InitMovingField(x, y, MV_DOWN);
4605 started_moving = TRUE;
4607 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4608 Store[x][y] = EL_CHANGED(Store[x][y]);
4610 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4612 if (!MovDelay[x][y])
4613 MovDelay[x][y] = TILEY/4 + 1;
4622 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4623 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4624 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4628 else if (element == EL_BD_MAGIC_WALL_FULL)
4630 if (IS_FREE(x, y + 1))
4632 InitMovingField(x, y, MV_DOWN);
4633 started_moving = TRUE;
4635 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4636 Store[x][y] = EL_CHANGED2(Store[x][y]);
4638 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4640 if (!MovDelay[x][y])
4641 MovDelay[x][y] = TILEY/4 + 1;
4650 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4651 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4652 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4656 else if (CAN_PASS_MAGIC_WALL(element) &&
4657 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4658 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4660 InitMovingField(x, y, MV_DOWN);
4661 started_moving = TRUE;
4664 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4665 EL_BD_MAGIC_WALL_FILLING);
4666 Store[x][y] = element;
4668 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4670 SplashAcid(x, y + 1);
4672 InitMovingField(x, y, MV_DOWN);
4673 started_moving = TRUE;
4675 Store[x][y] = EL_ACID;
4677 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4678 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
4680 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4681 CAN_FALL(element) && WasJustFalling[x][y] &&
4682 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
4684 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4685 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4686 (Feld[x][y + 1] == EL_BLOCKED)))
4688 /* this is needed for a special case not covered by calling "Impact()"
4689 from "ContinueMoving()": if an element moves to a tile directly below
4690 another element which was just falling on that tile (which was empty
4691 in the previous frame), the falling element above would just stop
4692 instead of smashing the element below (in previous version, the above
4693 element was just checked for "moving" instead of "falling", resulting
4694 in incorrect smashes caused by horizontal movement of the above
4695 element; also, the case of the player being the element to smash was
4696 simply not covered here... :-/ ) */
4698 CheckCollision[x][y] = 0;
4702 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4704 if (MovDir[x][y] == MV_NO_MOVING)
4706 InitMovingField(x, y, MV_DOWN);
4707 started_moving = TRUE;
4710 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4712 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4713 MovDir[x][y] = MV_DOWN;
4715 InitMovingField(x, y, MV_DOWN);
4716 started_moving = TRUE;
4718 else if (element == EL_AMOEBA_DROP)
4720 Feld[x][y] = EL_AMOEBA_GROWING;
4721 Store[x][y] = EL_AMOEBA_WET;
4723 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4724 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4725 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4726 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4728 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4729 (IS_FREE(x - 1, y + 1) ||
4730 Feld[x - 1][y + 1] == EL_ACID));
4731 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4732 (IS_FREE(x + 1, y + 1) ||
4733 Feld[x + 1][y + 1] == EL_ACID));
4734 boolean can_fall_any = (can_fall_left || can_fall_right);
4735 boolean can_fall_both = (can_fall_left && can_fall_right);
4737 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4739 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4741 if (slippery_type == SLIPPERY_ONLY_LEFT)
4742 can_fall_right = FALSE;
4743 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4744 can_fall_left = FALSE;
4745 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4746 can_fall_right = FALSE;
4747 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4748 can_fall_left = FALSE;
4750 can_fall_any = (can_fall_left || can_fall_right);
4751 can_fall_both = (can_fall_left && can_fall_right);
4754 #if USE_NEW_SP_SLIPPERY
4755 /* !!! better use the same properties as for custom elements here !!! */
4756 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
4757 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
4759 can_fall_right = FALSE; /* slip down on left side */
4760 can_fall_both = FALSE;
4766 if (game.emulation == EMU_BOULDERDASH ||
4767 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
4768 can_fall_right = FALSE; /* slip down on left side */
4770 can_fall_left = !(can_fall_right = RND(2));
4772 can_fall_both = FALSE;
4777 /* if not determined otherwise, prefer left side for slipping down */
4778 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4779 started_moving = TRUE;
4783 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4785 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4788 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4789 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4790 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4791 int belt_dir = game.belt_dir[belt_nr];
4793 if ((belt_dir == MV_LEFT && left_is_free) ||
4794 (belt_dir == MV_RIGHT && right_is_free))
4796 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4798 InitMovingField(x, y, belt_dir);
4799 started_moving = TRUE;
4801 Pushed[x][y] = TRUE;
4802 Pushed[nextx][y] = TRUE;
4804 GfxAction[x][y] = ACTION_DEFAULT;
4808 MovDir[x][y] = 0; /* if element was moving, stop it */
4813 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4815 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
4817 if (CAN_MOVE(element) && !started_moving)
4820 int move_pattern = element_info[element].move_pattern;
4825 if (MovDir[x][y] == MV_NO_MOVING)
4827 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
4828 x, y, element, element_info[element].token_name);
4829 printf("StartMoving(): This should never happen!\n");
4834 Moving2Blocked(x, y, &newx, &newy);
4836 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4839 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4840 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
4842 WasJustMoving[x][y] = 0;
4843 CheckCollision[x][y] = 0;
4845 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4847 if (Feld[x][y] != element) /* element has changed */
4851 if (!MovDelay[x][y]) /* start new movement phase */
4853 /* all objects that can change their move direction after each step
4854 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4856 if (element != EL_YAMYAM &&
4857 element != EL_DARK_YAMYAM &&
4858 element != EL_PACMAN &&
4859 !(move_pattern & MV_ANY_DIRECTION) &&
4860 move_pattern != MV_TURNING_LEFT &&
4861 move_pattern != MV_TURNING_RIGHT &&
4862 move_pattern != MV_TURNING_LEFT_RIGHT &&
4863 move_pattern != MV_TURNING_RIGHT_LEFT &&
4864 move_pattern != MV_TURNING_RANDOM)
4868 if (MovDelay[x][y] && (element == EL_BUG ||
4869 element == EL_SPACESHIP ||
4870 element == EL_SP_SNIKSNAK ||
4871 element == EL_SP_ELECTRON ||
4872 element == EL_MOLE))
4873 DrawLevelField(x, y);
4877 if (MovDelay[x][y]) /* wait some time before next movement */
4881 if (element == EL_ROBOT ||
4882 element == EL_YAMYAM ||
4883 element == EL_DARK_YAMYAM)
4885 DrawLevelElementAnimationIfNeeded(x, y, element);
4886 PlayLevelSoundAction(x, y, ACTION_WAITING);
4888 else if (element == EL_SP_ELECTRON)
4889 DrawLevelElementAnimationIfNeeded(x, y, element);
4890 else if (element == EL_DRAGON)
4893 int dir = MovDir[x][y];
4894 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4895 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4896 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4897 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4898 dir == MV_UP ? IMG_FLAMES_1_UP :
4899 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4900 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4902 GfxAction[x][y] = ACTION_ATTACKING;
4904 if (IS_PLAYER(x, y))
4905 DrawPlayerField(x, y);
4907 DrawLevelField(x, y);
4909 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4911 for (i = 1; i <= 3; i++)
4913 int xx = x + i * dx;
4914 int yy = y + i * dy;
4915 int sx = SCREENX(xx);
4916 int sy = SCREENY(yy);
4917 int flame_graphic = graphic + (i - 1);
4919 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4924 int flamed = MovingOrBlocked2Element(xx, yy);
4928 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
4930 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
4931 RemoveMovingField(xx, yy);
4933 RemoveField(xx, yy);
4935 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
4938 RemoveMovingField(xx, yy);
4941 ChangeDelay[xx][yy] = 0;
4943 Feld[xx][yy] = EL_FLAMES;
4945 if (IN_SCR_FIELD(sx, sy))
4947 DrawLevelFieldCrumbledSand(xx, yy);
4948 DrawGraphic(sx, sy, flame_graphic, frame);
4953 if (Feld[xx][yy] == EL_FLAMES)
4954 Feld[xx][yy] = EL_EMPTY;
4955 DrawLevelField(xx, yy);
4960 if (MovDelay[x][y]) /* element still has to wait some time */
4962 PlayLevelSoundAction(x, y, ACTION_WAITING);
4968 /* now make next step */
4970 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4972 if (DONT_COLLIDE_WITH(element) &&
4973 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4974 !PLAYER_ENEMY_PROTECTED(newx, newy))
4976 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4981 else if (CAN_MOVE_INTO_ACID(element) &&
4982 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
4983 (MovDir[x][y] == MV_DOWN ||
4984 game.engine_version >= VERSION_IDENT(3,1,0,0)))
4986 SplashAcid(newx, newy);
4987 Store[x][y] = EL_ACID;
4989 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4991 if (Feld[newx][newy] == EL_EXIT_OPEN)
4994 DrawLevelField(x, y);
4996 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4997 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4998 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5000 local_player->friends_still_needed--;
5001 if (!local_player->friends_still_needed &&
5002 !local_player->GameOver && AllPlayersGone)
5003 local_player->LevelSolved = local_player->GameOver = TRUE;
5007 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5009 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5010 DrawLevelField(newx, newy);
5012 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5014 else if (!IS_FREE(newx, newy))
5016 GfxAction[x][y] = ACTION_WAITING;
5018 if (IS_PLAYER(x, y))
5019 DrawPlayerField(x, y);
5021 DrawLevelField(x, y);
5026 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5028 if (IS_FOOD_PIG(Feld[newx][newy]))
5030 if (IS_MOVING(newx, newy))
5031 RemoveMovingField(newx, newy);
5034 Feld[newx][newy] = EL_EMPTY;
5035 DrawLevelField(newx, newy);
5038 PlayLevelSound(x, y, SND_PIG_DIGGING);
5040 else if (!IS_FREE(newx, newy))
5042 if (IS_PLAYER(x, y))
5043 DrawPlayerField(x, y);
5045 DrawLevelField(x, y);
5050 else if (IS_CUSTOM_ELEMENT(element) &&
5051 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5053 int new_element = Feld[newx][newy];
5055 if (!IS_FREE(newx, newy))
5057 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5058 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5061 /* no element can dig solid indestructible elements */
5062 if (IS_INDESTRUCTIBLE(new_element) &&
5063 !IS_DIGGABLE(new_element) &&
5064 !IS_COLLECTIBLE(new_element))
5067 if (AmoebaNr[newx][newy] &&
5068 (new_element == EL_AMOEBA_FULL ||
5069 new_element == EL_BD_AMOEBA ||
5070 new_element == EL_AMOEBA_GROWING))
5072 AmoebaCnt[AmoebaNr[newx][newy]]--;
5073 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5076 if (IS_MOVING(newx, newy))
5077 RemoveMovingField(newx, newy);
5080 RemoveField(newx, newy);
5081 DrawLevelField(newx, newy);
5084 /* if digged element was about to explode, prevent the explosion */
5085 ExplodeField[newx][newy] = EX_TYPE_NONE;
5087 PlayLevelSoundAction(x, y, action);
5090 Store[newx][newy] = EL_EMPTY;
5091 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5093 int move_leave_element = element_info[element].move_leave_element;
5095 /* this makes it possible to leave the removed element again */
5096 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
5097 new_element : move_leave_element);
5100 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5102 RunnerVisit[x][y] = FrameCounter;
5103 PlayerVisit[x][y] /= 8; /* expire player visit path */
5106 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5108 if (!IS_FREE(newx, newy))
5110 if (IS_PLAYER(x, y))
5111 DrawPlayerField(x, y);
5113 DrawLevelField(x, y);
5119 boolean wanna_flame = !RND(10);
5120 int dx = newx - x, dy = newy - y;
5121 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5122 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5123 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5124 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5125 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5126 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5129 IS_CLASSIC_ENEMY(element1) ||
5130 IS_CLASSIC_ENEMY(element2)) &&
5131 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5132 element1 != EL_FLAMES && element2 != EL_FLAMES)
5134 ResetGfxAnimation(x, y);
5135 GfxAction[x][y] = ACTION_ATTACKING;
5137 if (IS_PLAYER(x, y))
5138 DrawPlayerField(x, y);
5140 DrawLevelField(x, y);
5142 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5144 MovDelay[x][y] = 50;
5148 RemoveField(newx, newy);
5150 Feld[newx][newy] = EL_FLAMES;
5151 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5154 RemoveField(newx1, newy1);
5156 Feld[newx1][newy1] = EL_FLAMES;
5158 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5161 RemoveField(newx2, newy2);
5163 Feld[newx2][newy2] = EL_FLAMES;
5170 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5171 Feld[newx][newy] == EL_DIAMOND)
5173 if (IS_MOVING(newx, newy))
5174 RemoveMovingField(newx, newy);
5177 Feld[newx][newy] = EL_EMPTY;
5178 DrawLevelField(newx, newy);
5181 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5183 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5184 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5186 if (AmoebaNr[newx][newy])
5188 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5189 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5190 Feld[newx][newy] == EL_BD_AMOEBA)
5191 AmoebaCnt[AmoebaNr[newx][newy]]--;
5196 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5198 RemoveMovingField(newx, newy);
5201 if (IS_MOVING(newx, newy))
5203 RemoveMovingField(newx, newy);
5208 Feld[newx][newy] = EL_EMPTY;
5209 DrawLevelField(newx, newy);
5212 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5214 else if ((element == EL_PACMAN || element == EL_MOLE)
5215 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5217 if (AmoebaNr[newx][newy])
5219 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5220 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5221 Feld[newx][newy] == EL_BD_AMOEBA)
5222 AmoebaCnt[AmoebaNr[newx][newy]]--;
5225 if (element == EL_MOLE)
5227 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5228 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5230 ResetGfxAnimation(x, y);
5231 GfxAction[x][y] = ACTION_DIGGING;
5232 DrawLevelField(x, y);
5234 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5236 return; /* wait for shrinking amoeba */
5238 else /* element == EL_PACMAN */
5240 Feld[newx][newy] = EL_EMPTY;
5241 DrawLevelField(newx, newy);
5242 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5245 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5246 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5247 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5249 /* wait for shrinking amoeba to completely disappear */
5252 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5254 /* object was running against a wall */
5259 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
5260 if (move_pattern & MV_ANY_DIRECTION &&
5261 move_pattern == MovDir[x][y])
5263 int blocking_element =
5264 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5266 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5269 element = Feld[x][y]; /* element might have changed */
5273 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5274 DrawLevelElementAnimation(x, y, element);
5276 if (DONT_TOUCH(element))
5277 TestIfBadThingTouchesHero(x, y);
5282 InitMovingField(x, y, MovDir[x][y]);
5284 PlayLevelSoundAction(x, y, ACTION_MOVING);
5288 ContinueMoving(x, y);
5291 /* (emacs is confused here for some reason; this makes it happy again ;-) ) */
5296 void ContinueMoving(int x, int y)
5298 int element = Feld[x][y];
5299 int stored = Store[x][y];
5300 struct ElementInfo *ei = &element_info[element];
5301 int direction = MovDir[x][y];
5302 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5303 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5304 int newx = x + dx, newy = y + dy;
5305 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5306 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5307 boolean last_line = (newy == lev_fieldy - 1);
5309 MovPos[x][y] += getElementMoveStepsize(x, y);
5311 if (pushed_by_player) /* special case: moving object pushed by player */
5312 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5314 if (ABS(MovPos[x][y]) < TILEX)
5316 DrawLevelField(x, y);
5318 return; /* element is still moving */
5321 /* element reached destination field */
5323 Feld[x][y] = EL_EMPTY;
5324 Feld[newx][newy] = element;
5325 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5327 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
5329 element = Feld[newx][newy] = EL_ACID;
5331 else if (element == EL_MOLE)
5333 Feld[x][y] = EL_SAND;
5335 DrawLevelFieldCrumbledSandNeighbours(x, y);
5337 else if (element == EL_QUICKSAND_FILLING)
5339 element = Feld[newx][newy] = get_next_element(element);
5340 Store[newx][newy] = Store[x][y];
5342 else if (element == EL_QUICKSAND_EMPTYING)
5344 Feld[x][y] = get_next_element(element);
5345 element = Feld[newx][newy] = Store[x][y];
5347 else if (element == EL_MAGIC_WALL_FILLING)
5349 element = Feld[newx][newy] = get_next_element(element);
5350 if (!game.magic_wall_active)
5351 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5352 Store[newx][newy] = Store[x][y];
5354 else if (element == EL_MAGIC_WALL_EMPTYING)
5356 Feld[x][y] = get_next_element(element);
5357 if (!game.magic_wall_active)
5358 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5359 element = Feld[newx][newy] = Store[x][y];
5361 else if (element == EL_BD_MAGIC_WALL_FILLING)
5363 element = Feld[newx][newy] = get_next_element(element);
5364 if (!game.magic_wall_active)
5365 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5366 Store[newx][newy] = Store[x][y];
5368 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5370 Feld[x][y] = get_next_element(element);
5371 if (!game.magic_wall_active)
5372 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5373 element = Feld[newx][newy] = Store[x][y];
5375 else if (element == EL_AMOEBA_DROPPING)
5377 Feld[x][y] = get_next_element(element);
5378 element = Feld[newx][newy] = Store[x][y];
5380 else if (element == EL_SOKOBAN_OBJECT)
5383 Feld[x][y] = Back[x][y];
5385 if (Back[newx][newy])
5386 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5388 Back[x][y] = Back[newx][newy] = 0;
5391 Store[x][y] = EL_EMPTY;
5395 MovDelay[newx][newy] = 0;
5397 if (CAN_CHANGE(element))
5399 /* copy element change control values to new field */
5400 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5401 ChangePage[newx][newy] = ChangePage[x][y];
5402 Changed[newx][newy] = Changed[x][y];
5403 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5406 ChangeDelay[x][y] = 0;
5407 ChangePage[x][y] = -1;
5408 Changed[x][y] = FALSE;
5409 ChangeEvent[x][y] = -1;
5411 /* copy animation control values to new field */
5412 GfxFrame[newx][newy] = GfxFrame[x][y];
5413 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5414 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5415 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5417 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5419 /* some elements can leave other elements behind after moving */
5420 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
5421 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
5422 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
5424 int move_leave_element = ei->move_leave_element;
5426 /* this makes it possible to leave the removed element again */
5427 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
5428 ei->move_leave_element == EL_TRIGGER_ELEMENT)
5429 move_leave_element = stored;
5431 Feld[x][y] = move_leave_element;
5433 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
5434 MovDir[x][y] = direction;
5436 InitField(x, y, FALSE);
5438 if (GFX_CRUMBLED(Feld[x][y]))
5439 DrawLevelFieldCrumbledSandNeighbours(x, y);
5441 if (ELEM_IS_PLAYER(move_leave_element))
5442 RelocatePlayer(x, y, move_leave_element);
5445 /* do this after checking for left-behind element */
5446 ResetGfxAnimation(x, y); /* reset animation values for old field */
5448 if (!CAN_MOVE(element) ||
5449 (CAN_FALL(element) && direction == MV_DOWN &&
5450 (element == EL_SPRING ||
5451 element_info[element].move_pattern == MV_WHEN_PUSHED ||
5452 element_info[element].move_pattern == MV_WHEN_DROPPED)))
5453 GfxDir[x][y] = MovDir[newx][newy] = 0;
5455 DrawLevelField(x, y);
5456 DrawLevelField(newx, newy);
5458 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5460 /* prevent pushed element from moving on in pushed direction */
5461 if (pushed_by_player && CAN_MOVE(element) &&
5462 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5463 !(element_info[element].move_pattern & direction))
5464 TurnRound(newx, newy);
5466 /* prevent elements on conveyor belt from moving on in last direction */
5467 if (pushed_by_conveyor && CAN_FALL(element) &&
5468 direction & MV_HORIZONTAL)
5469 MovDir[newx][newy] = 0;
5471 if (!pushed_by_player)
5473 int nextx = newx + dx, nexty = newy + dy;
5474 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
5476 WasJustMoving[newx][newy] = 3;
5478 if (CAN_FALL(element) && direction == MV_DOWN)
5479 WasJustFalling[newx][newy] = 3;
5481 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
5482 CheckCollision[newx][newy] = 2;
5485 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5487 TestIfBadThingTouchesHero(newx, newy);
5488 TestIfBadThingTouchesFriend(newx, newy);
5490 if (!IS_CUSTOM_ELEMENT(element))
5491 TestIfBadThingTouchesOtherBadThing(newx, newy);
5493 else if (element == EL_PENGUIN)
5494 TestIfFriendTouchesBadThing(newx, newy);
5496 /* give the player one last chance (one more frame) to move away */
5497 if (CAN_FALL(element) && direction == MV_DOWN &&
5498 (last_line || (!IS_FREE(x, newy + 1) &&
5499 (!IS_PLAYER(x, newy + 1) ||
5500 game.engine_version < VERSION_IDENT(3,1,1,0)))))
5503 if (pushed_by_player && !game.use_change_when_pushing_bug)
5505 int dig_side = MV_DIR_OPPOSITE(direction);
5506 struct PlayerInfo *player = PLAYERINFO(x, y);
5508 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
5509 player->index_bit, dig_side);
5510 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
5511 player->index_bit, dig_side);
5514 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5516 TestIfElementHitsCustomElement(newx, newy, direction);
5517 TestIfPlayerTouchesCustomElement(newx, newy);
5518 TestIfElementTouchesCustomElement(newx, newy);
5521 int AmoebeNachbarNr(int ax, int ay)
5524 int element = Feld[ax][ay];
5526 static int xy[4][2] =
5534 for (i = 0; i < NUM_DIRECTIONS; i++)
5536 int x = ax + xy[i][0];
5537 int y = ay + xy[i][1];
5539 if (!IN_LEV_FIELD(x, y))
5542 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5543 group_nr = AmoebaNr[x][y];
5549 void AmoebenVereinigen(int ax, int ay)
5551 int i, x, y, xx, yy;
5552 int new_group_nr = AmoebaNr[ax][ay];
5553 static int xy[4][2] =
5561 if (new_group_nr == 0)
5564 for (i = 0; i < NUM_DIRECTIONS; i++)
5569 if (!IN_LEV_FIELD(x, y))
5572 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5573 Feld[x][y] == EL_BD_AMOEBA ||
5574 Feld[x][y] == EL_AMOEBA_DEAD) &&
5575 AmoebaNr[x][y] != new_group_nr)
5577 int old_group_nr = AmoebaNr[x][y];
5579 if (old_group_nr == 0)
5582 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5583 AmoebaCnt[old_group_nr] = 0;
5584 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5585 AmoebaCnt2[old_group_nr] = 0;
5587 for (yy = 0; yy < lev_fieldy; yy++)
5589 for (xx = 0; xx < lev_fieldx; xx++)
5591 if (AmoebaNr[xx][yy] == old_group_nr)
5592 AmoebaNr[xx][yy] = new_group_nr;
5599 void AmoebeUmwandeln(int ax, int ay)
5603 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5605 int group_nr = AmoebaNr[ax][ay];
5610 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5611 printf("AmoebeUmwandeln(): This should never happen!\n");
5616 for (y = 0; y < lev_fieldy; y++)
5618 for (x = 0; x < lev_fieldx; x++)
5620 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5623 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5627 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5628 SND_AMOEBA_TURNING_TO_GEM :
5629 SND_AMOEBA_TURNING_TO_ROCK));
5634 static int xy[4][2] =
5642 for (i = 0; i < NUM_DIRECTIONS; i++)
5647 if (!IN_LEV_FIELD(x, y))
5650 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5652 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5653 SND_AMOEBA_TURNING_TO_GEM :
5654 SND_AMOEBA_TURNING_TO_ROCK));
5661 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5664 int group_nr = AmoebaNr[ax][ay];
5665 boolean done = FALSE;
5670 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5671 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5676 for (y = 0; y < lev_fieldy; y++)
5678 for (x = 0; x < lev_fieldx; x++)
5680 if (AmoebaNr[x][y] == group_nr &&
5681 (Feld[x][y] == EL_AMOEBA_DEAD ||
5682 Feld[x][y] == EL_BD_AMOEBA ||
5683 Feld[x][y] == EL_AMOEBA_GROWING))
5686 Feld[x][y] = new_element;
5687 InitField(x, y, FALSE);
5688 DrawLevelField(x, y);
5695 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5696 SND_BD_AMOEBA_TURNING_TO_ROCK :
5697 SND_BD_AMOEBA_TURNING_TO_GEM));
5700 void AmoebeWaechst(int x, int y)
5702 static unsigned long sound_delay = 0;
5703 static unsigned long sound_delay_value = 0;
5705 if (!MovDelay[x][y]) /* start new growing cycle */
5709 if (DelayReached(&sound_delay, sound_delay_value))
5711 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5712 sound_delay_value = 30;
5716 if (MovDelay[x][y]) /* wait some time before growing bigger */
5719 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5721 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5722 6 - MovDelay[x][y]);
5724 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5727 if (!MovDelay[x][y])
5729 Feld[x][y] = Store[x][y];
5731 DrawLevelField(x, y);
5736 void AmoebaDisappearing(int x, int y)
5738 static unsigned long sound_delay = 0;
5739 static unsigned long sound_delay_value = 0;
5741 if (!MovDelay[x][y]) /* start new shrinking cycle */
5745 if (DelayReached(&sound_delay, sound_delay_value))
5746 sound_delay_value = 30;
5749 if (MovDelay[x][y]) /* wait some time before shrinking */
5752 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5754 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5755 6 - MovDelay[x][y]);
5757 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5760 if (!MovDelay[x][y])
5762 Feld[x][y] = EL_EMPTY;
5763 DrawLevelField(x, y);
5765 /* don't let mole enter this field in this cycle;
5766 (give priority to objects falling to this field from above) */
5772 void AmoebeAbleger(int ax, int ay)
5775 int element = Feld[ax][ay];
5776 int graphic = el2img(element);
5777 int newax = ax, neway = ay;
5778 static int xy[4][2] =
5786 if (!level.amoeba_speed)
5788 Feld[ax][ay] = EL_AMOEBA_DEAD;
5789 DrawLevelField(ax, ay);
5793 if (IS_ANIMATED(graphic))
5794 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5796 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5797 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5799 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5802 if (MovDelay[ax][ay])
5806 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5809 int x = ax + xy[start][0];
5810 int y = ay + xy[start][1];
5812 if (!IN_LEV_FIELD(x, y))
5815 if (IS_FREE(x, y) ||
5816 CAN_GROW_INTO(Feld[x][y]) ||
5817 Feld[x][y] == EL_QUICKSAND_EMPTY)
5823 if (newax == ax && neway == ay)
5826 else /* normal or "filled" (BD style) amoeba */
5829 boolean waiting_for_player = FALSE;
5831 for (i = 0; i < NUM_DIRECTIONS; i++)
5833 int j = (start + i) % 4;
5834 int x = ax + xy[j][0];
5835 int y = ay + xy[j][1];
5837 if (!IN_LEV_FIELD(x, y))
5840 if (IS_FREE(x, y) ||
5841 CAN_GROW_INTO(Feld[x][y]) ||
5842 Feld[x][y] == EL_QUICKSAND_EMPTY)
5848 else if (IS_PLAYER(x, y))
5849 waiting_for_player = TRUE;
5852 if (newax == ax && neway == ay) /* amoeba cannot grow */
5854 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
5856 Feld[ax][ay] = EL_AMOEBA_DEAD;
5857 DrawLevelField(ax, ay);
5858 AmoebaCnt[AmoebaNr[ax][ay]]--;
5860 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5862 if (element == EL_AMOEBA_FULL)
5863 AmoebeUmwandeln(ax, ay);
5864 else if (element == EL_BD_AMOEBA)
5865 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5870 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5872 /* amoeba gets larger by growing in some direction */
5874 int new_group_nr = AmoebaNr[ax][ay];
5877 if (new_group_nr == 0)
5879 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5880 printf("AmoebeAbleger(): This should never happen!\n");
5885 AmoebaNr[newax][neway] = new_group_nr;
5886 AmoebaCnt[new_group_nr]++;
5887 AmoebaCnt2[new_group_nr]++;
5889 /* if amoeba touches other amoeba(s) after growing, unify them */
5890 AmoebenVereinigen(newax, neway);
5892 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5894 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5900 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5901 (neway == lev_fieldy - 1 && newax != ax))
5903 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5904 Store[newax][neway] = element;
5906 else if (neway == ay)
5908 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5910 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5914 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5915 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5916 Store[ax][ay] = EL_AMOEBA_DROP;
5917 ContinueMoving(ax, ay);
5921 DrawLevelField(newax, neway);
5924 void Life(int ax, int ay)
5927 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5929 int element = Feld[ax][ay];
5930 int graphic = el2img(element);
5931 boolean changed = FALSE;
5933 if (IS_ANIMATED(graphic))
5934 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5939 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5940 MovDelay[ax][ay] = life_time;
5942 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5945 if (MovDelay[ax][ay])
5949 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5951 int xx = ax+x1, yy = ay+y1;
5954 if (!IN_LEV_FIELD(xx, yy))
5957 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5959 int x = xx+x2, y = yy+y2;
5961 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5964 if (((Feld[x][y] == element ||
5965 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5967 (IS_FREE(x, y) && Stop[x][y]))
5971 if (xx == ax && yy == ay) /* field in the middle */
5973 if (nachbarn < life[0] || nachbarn > life[1])
5975 Feld[xx][yy] = EL_EMPTY;
5977 DrawLevelField(xx, yy);
5978 Stop[xx][yy] = TRUE;
5982 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
5983 { /* free border field */
5984 if (nachbarn >= life[2] && nachbarn <= life[3])
5986 Feld[xx][yy] = element;
5987 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5989 DrawLevelField(xx, yy);
5990 Stop[xx][yy] = TRUE;
5997 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5998 SND_GAME_OF_LIFE_GROWING);
6001 static void InitRobotWheel(int x, int y)
6003 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6006 static void RunRobotWheel(int x, int y)
6008 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6011 static void StopRobotWheel(int x, int y)
6013 if (ZX == x && ZY == y)
6017 static void InitTimegateWheel(int x, int y)
6019 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6022 static void RunTimegateWheel(int x, int y)
6024 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6027 void CheckExit(int x, int y)
6029 if (local_player->gems_still_needed > 0 ||
6030 local_player->sokobanfields_still_needed > 0 ||
6031 local_player->lights_still_needed > 0)
6033 int element = Feld[x][y];
6034 int graphic = el2img(element);
6036 if (IS_ANIMATED(graphic))
6037 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6042 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6045 Feld[x][y] = EL_EXIT_OPENING;
6047 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6050 void CheckExitSP(int x, int y)
6052 if (local_player->gems_still_needed > 0)
6054 int element = Feld[x][y];
6055 int graphic = el2img(element);
6057 if (IS_ANIMATED(graphic))
6058 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6063 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6066 Feld[x][y] = EL_SP_EXIT_OPENING;
6068 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6071 static void CloseAllOpenTimegates()
6075 for (y = 0; y < lev_fieldy; y++)
6077 for (x = 0; x < lev_fieldx; x++)
6079 int element = Feld[x][y];
6081 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6083 Feld[x][y] = EL_TIMEGATE_CLOSING;
6085 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6091 void EdelsteinFunkeln(int x, int y)
6093 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6096 if (Feld[x][y] == EL_BD_DIAMOND)
6099 if (MovDelay[x][y] == 0) /* next animation frame */
6100 MovDelay[x][y] = 11 * !SimpleRND(500);
6102 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6106 if (setup.direct_draw && MovDelay[x][y])
6107 SetDrawtoField(DRAW_BUFFERED);
6109 DrawLevelElementAnimation(x, y, Feld[x][y]);
6111 if (MovDelay[x][y] != 0)
6113 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6114 10 - MovDelay[x][y]);
6116 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6118 if (setup.direct_draw)
6122 dest_x = FX + SCREENX(x) * TILEX;
6123 dest_y = FY + SCREENY(y) * TILEY;
6125 BlitBitmap(drawto_field, window,
6126 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6127 SetDrawtoField(DRAW_DIRECT);
6133 void MauerWaechst(int x, int y)
6137 if (!MovDelay[x][y]) /* next animation frame */
6138 MovDelay[x][y] = 3 * delay;
6140 if (MovDelay[x][y]) /* wait some time before next frame */
6144 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6146 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6147 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6149 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6152 if (!MovDelay[x][y])
6154 if (MovDir[x][y] == MV_LEFT)
6156 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6157 DrawLevelField(x - 1, y);
6159 else if (MovDir[x][y] == MV_RIGHT)
6161 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6162 DrawLevelField(x + 1, y);
6164 else if (MovDir[x][y] == MV_UP)
6166 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6167 DrawLevelField(x, y - 1);
6171 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6172 DrawLevelField(x, y + 1);
6175 Feld[x][y] = Store[x][y];
6177 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6178 DrawLevelField(x, y);
6183 void MauerAbleger(int ax, int ay)
6185 int element = Feld[ax][ay];
6186 int graphic = el2img(element);
6187 boolean oben_frei = FALSE, unten_frei = FALSE;
6188 boolean links_frei = FALSE, rechts_frei = FALSE;
6189 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6190 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6191 boolean new_wall = FALSE;
6193 if (IS_ANIMATED(graphic))
6194 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6196 if (!MovDelay[ax][ay]) /* start building new wall */
6197 MovDelay[ax][ay] = 6;
6199 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6202 if (MovDelay[ax][ay])
6206 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6208 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6210 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6212 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6215 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6216 element == EL_EXPANDABLE_WALL_ANY)
6220 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6221 Store[ax][ay-1] = element;
6222 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6223 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6224 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6225 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6230 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6231 Store[ax][ay+1] = element;
6232 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6233 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6234 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6235 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6240 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6241 element == EL_EXPANDABLE_WALL_ANY ||
6242 element == EL_EXPANDABLE_WALL)
6246 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6247 Store[ax-1][ay] = element;
6248 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6249 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6250 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6251 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6257 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6258 Store[ax+1][ay] = element;
6259 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6260 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6261 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6262 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6267 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6268 DrawLevelField(ax, ay);
6270 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6272 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6273 unten_massiv = TRUE;
6274 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6275 links_massiv = TRUE;
6276 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6277 rechts_massiv = TRUE;
6279 if (((oben_massiv && unten_massiv) ||
6280 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6281 element == EL_EXPANDABLE_WALL) &&
6282 ((links_massiv && rechts_massiv) ||
6283 element == EL_EXPANDABLE_WALL_VERTICAL))
6284 Feld[ax][ay] = EL_WALL;
6287 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6290 void CheckForDragon(int x, int y)
6293 boolean dragon_found = FALSE;
6294 static int xy[4][2] =
6302 for (i = 0; i < NUM_DIRECTIONS; i++)
6304 for (j = 0; j < 4; j++)
6306 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6308 if (IN_LEV_FIELD(xx, yy) &&
6309 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6311 if (Feld[xx][yy] == EL_DRAGON)
6312 dragon_found = TRUE;
6321 for (i = 0; i < NUM_DIRECTIONS; i++)
6323 for (j = 0; j < 3; j++)
6325 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6327 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6329 Feld[xx][yy] = EL_EMPTY;
6330 DrawLevelField(xx, yy);
6339 static void InitBuggyBase(int x, int y)
6341 int element = Feld[x][y];
6342 int activating_delay = FRAMES_PER_SECOND / 4;
6345 (element == EL_SP_BUGGY_BASE ?
6346 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6347 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6349 element == EL_SP_BUGGY_BASE_ACTIVE ?
6350 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6353 static void WarnBuggyBase(int x, int y)
6356 static int xy[4][2] =
6364 for (i = 0; i < NUM_DIRECTIONS; i++)
6366 int xx = x + xy[i][0], yy = y + xy[i][1];
6368 if (IS_PLAYER(xx, yy))
6370 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6377 static void InitTrap(int x, int y)
6379 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6382 static void ActivateTrap(int x, int y)
6384 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6387 static void ChangeActiveTrap(int x, int y)
6389 int graphic = IMG_TRAP_ACTIVE;
6391 /* if new animation frame was drawn, correct crumbled sand border */
6392 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6393 DrawLevelFieldCrumbledSand(x, y);
6396 static int getSpecialActionElement(int element, int number, int base_element)
6398 return (element != EL_EMPTY ? element :
6399 number != -1 ? base_element + number - 1 :
6403 static int getModifiedActionNumber(int value_old, int value_min, int value_max,
6404 int operator, int operand)
6406 int value_new = (operator == CA_MODE_ADD ? value_old + operand :
6407 operator == CA_MODE_SUBTRACT ? value_old - operand :
6408 operator == CA_MODE_MULTIPLY ? value_old * operand :
6409 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
6410 operator == CA_MODE_SET ? operand :
6413 return (value_new < value_min ? value_min :
6414 value_new > value_max ? value_max :
6418 static void ExecuteCustomElementAction(int element, int page)
6420 struct ElementInfo *ei = &element_info[element];
6421 struct ElementChangeInfo *change = &ei->change_page[page];
6422 int action_type = change->action_type;
6423 int action_mode = change->action_mode;
6424 int action_arg = change->action_arg;
6427 if (!change->has_action)
6430 /* ---------- determine action paramater values ---------- */
6432 int action_arg_element =
6433 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
6434 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
6435 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
6438 int action_arg_number =
6439 (action_arg <= CA_ARG_MAX ? action_arg :
6440 action_arg == CA_ARG_NUMBER_MIN ? CA_ARG_MIN :
6441 action_arg == CA_ARG_NUMBER_MAX ? CA_ARG_MAX :
6442 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
6443 action_arg == CA_ARG_NUMBER_CE_COUNT ? ei->collect_count :
6444 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) :
6447 /* (for explicit player choice, set invalid value to "no player") */
6448 int action_arg_player_bits =
6449 (action_arg == CA_ARG_PLAYER_ANY ? action_arg - CA_ARG_PLAYER :
6450 action_arg >= CA_ARG_PLAYER_1 &&
6451 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
6452 action_arg >= CA_ARG_1 &&
6453 action_arg <= CA_ARG_PLAYER_4 ? (1 << (action_arg - 1)) :
6454 action_arg_element >= EL_PLAYER_1 &&
6455 action_arg_element <= EL_PLAYER_4 ?
6456 (1 << (action_arg_element - EL_PLAYER_1)) :
6459 /* (for implicit player choice, set invalid value to "all players") */
6460 int trigger_player_bits =
6461 (change->actual_trigger_player >= EL_PLAYER_1 &&
6462 change->actual_trigger_player <= EL_PLAYER_4 ?
6463 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
6466 /* ---------- execute action ---------- */
6475 case CA_EXIT_PLAYER:
6477 for (i = 0; i < MAX_PLAYERS; i++)
6478 if (action_arg_player_bits & (1 << i))
6479 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
6484 case CA_KILL_PLAYER:
6486 for (i = 0; i < MAX_PLAYERS; i++)
6487 if (action_arg_player_bits & (1 << i))
6488 KillHero(&stored_player[i]);
6493 case CA_RESTART_LEVEL:
6495 game.restart_level = TRUE;
6500 case CA_SHOW_ENVELOPE:
6502 int element = getSpecialActionElement(action_arg_element,
6503 action_arg_number, EL_ENVELOPE_1);
6505 if (IS_ENVELOPE(element))
6506 local_player->show_envelope = element;
6513 int element = getSpecialActionElement(action_arg_element,
6514 action_arg_number, EL_KEY_1);
6516 if (IS_KEY(element))
6518 for (i = 0; i < MAX_PLAYERS; i++)
6520 if (trigger_player_bits & (1 << i))
6522 stored_player[i].key[KEY_NR(element)] = TRUE;
6524 DrawGameValue_Keys(stored_player[i].key);
6526 redraw_mask |= REDRAW_DOOR_1;
6536 int element = getSpecialActionElement(action_arg_element,
6537 action_arg_number, EL_KEY_1);
6539 if (IS_KEY(element))
6541 for (i = 0; i < MAX_PLAYERS; i++)
6543 if (trigger_player_bits & (1 << i))
6545 stored_player[i].key[KEY_NR(element)] = FALSE;
6547 DrawGameValue_Keys(stored_player[i].key);
6549 redraw_mask |= REDRAW_DOOR_1;
6557 case CA_SET_PLAYER_SPEED:
6559 for (i = 0; i < MAX_PLAYERS; i++)
6561 if (trigger_player_bits & (1 << i))
6563 if (action_arg == CA_ARG_NUMBER_RESET)
6564 stored_player[i].move_delay_value = game.initial_move_delay_value;
6565 else if (action_arg == CA_ARG_NUMBER_NORMAL)
6566 stored_player[i].move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6567 else if (action_arg == CA_ARG_NUMBER_MIN)
6568 stored_player[i].move_delay_value = 16;
6569 else if (action_arg == CA_ARG_NUMBER_MAX)
6570 stored_player[i].move_delay_value = MOVE_DELAY_HIGH_SPEED;
6574 if (action_mode == CA_MODE_ADD)
6576 action_mode = CA_MODE_DIVIDE;
6577 action_arg_number = (1 << action_arg_number);
6579 else if (action_mode == CA_MODE_SUBTRACT)
6581 action_mode = CA_MODE_MULTIPLY;
6582 action_arg_number = (1 << action_arg_number);
6585 int mode = (action_mode == CA_MODE_MULTIPLY ? CA_MODE_DIVIDE :
6586 action_mode == CA_MODE_DIVIDE ? CA_MODE_MULTIPLY :
6589 stored_player[i].move_delay_value =
6590 getModifiedActionNumber(stored_player[i].move_delay_value,
6592 action_mode, action_arg_number);
6603 local_player->gems_still_needed =
6604 getModifiedActionNumber(local_player->gems_still_needed, 0, 999,
6605 action_mode, action_arg_number);
6607 DrawGameValue_Emeralds(local_player->gems_still_needed);
6614 if (level.time > 0) /* only modify limited time value */
6616 TimeLeft = getModifiedActionNumber(TimeLeft, 0, 9999,
6617 action_mode, action_arg_number);
6619 DrawGameValue_Time(TimeLeft);
6627 local_player->score =
6628 getModifiedActionNumber(local_player->score, 0, 99999,
6629 action_mode, action_arg_number);
6631 DrawGameValue_Score(local_player->score);
6636 case CA_SET_CE_SCORE:
6638 printf("::: CA_SET_CE_SCORE -- not yet implemented\n");
6643 case CA_SET_CE_COUNT:
6645 printf("::: CA_SET_CE_COUNT -- not yet implemented\n");
6650 case CA_SET_DYNABOMB_NUMBER:
6652 printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n");
6657 case CA_SET_DYNABOMB_SIZE:
6659 printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n");
6664 case CA_SET_DYNABOMB_POWER:
6666 printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n");
6671 case CA_TOGGLE_PLAYER_GRAVITY:
6673 game.gravity = !game.gravity;
6678 case CA_ENABLE_PLAYER_GRAVITY:
6680 game.gravity = TRUE;
6685 case CA_DISABLE_PLAYER_GRAVITY:
6687 game.gravity = FALSE;
6697 static void ChangeElementNowExt(struct ElementChangeInfo *change,
6698 int x, int y, int target_element)
6700 int previous_move_direction = MovDir[x][y];
6701 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
6702 IS_WALKABLE(Feld[x][y]));
6704 /* check if element under player changes from accessible to unaccessible
6705 (needed for special case of dropping element which then changes) */
6706 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6707 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6715 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6716 RemoveMovingField(x, y);
6720 Feld[x][y] = target_element;
6722 ResetGfxAnimation(x, y);
6723 ResetRandomAnimationValue(x, y);
6725 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6726 MovDir[x][y] = previous_move_direction;
6728 InitField_WithBug1(x, y, FALSE);
6730 DrawLevelField(x, y);
6732 if (GFX_CRUMBLED(Feld[x][y]))
6733 DrawLevelFieldCrumbledSandNeighbours(x, y);
6736 /* "Changed[][]" not set yet to allow "entered by player" change one time */
6737 if (ELEM_IS_PLAYER(target_element))
6738 RelocatePlayer(x, y, target_element);
6741 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
6743 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6746 TestIfBadThingTouchesHero(x, y);
6747 TestIfPlayerTouchesCustomElement(x, y);
6748 TestIfElementTouchesCustomElement(x, y);
6751 static boolean ChangeElementNow(int x, int y, int element, int page)
6753 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6755 int old_element = Feld[x][y];
6757 /* always use default change event to prevent running into a loop */
6758 if (ChangeEvent[x][y] == -1)
6759 ChangeEvent[x][y] = CE_DELAY;
6761 if (ChangeEvent[x][y] == CE_DELAY)
6763 /* reset actual trigger element, trigger player and action element */
6764 change->actual_trigger_element = EL_EMPTY;
6765 change->actual_trigger_player = EL_PLAYER_1;
6769 /* do not change any elements that have already changed in this frame */
6773 /* do not change already changed elements with same change event */
6774 if (Changed[x][y] & ChangeEvent[x][y])
6779 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
6781 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6784 if (change->explode)
6791 if (change->use_target_content)
6793 boolean complete_replace = TRUE;
6794 boolean can_replace[3][3];
6797 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6800 boolean is_walkable;
6801 boolean is_diggable;
6802 boolean is_collectible;
6803 boolean is_removable;
6804 boolean is_destructible;
6805 int ex = x + xx - 1;
6806 int ey = y + yy - 1;
6807 int content_element = change->target_content[xx][yy];
6810 can_replace[xx][yy] = TRUE;
6812 if (ex == x && ey == y) /* do not check changing element itself */
6815 if (content_element == EL_EMPTY_SPACE)
6817 can_replace[xx][yy] = FALSE; /* do not replace border with space */
6822 if (!IN_LEV_FIELD(ex, ey))
6824 can_replace[xx][yy] = FALSE;
6825 complete_replace = FALSE;
6832 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6833 e = MovingOrBlocked2Element(ex, ey);
6835 is_empty = (IS_FREE(ex, ey) ||
6836 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
6838 is_walkable = (is_empty || IS_WALKABLE(e));
6839 is_diggable = (is_empty || IS_DIGGABLE(e));
6840 is_collectible = (is_empty || IS_COLLECTIBLE(e));
6841 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
6842 is_removable = (is_diggable || is_collectible);
6844 can_replace[xx][yy] =
6845 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
6846 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
6847 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
6848 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
6849 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
6850 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
6851 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
6853 if (!can_replace[xx][yy])
6854 complete_replace = FALSE;
6857 if (!change->only_if_complete || complete_replace)
6859 boolean something_has_changed = FALSE;
6861 if (change->only_if_complete && change->use_random_replace &&
6862 RND(100) < change->random_percentage)
6865 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6867 int ex = x + xx - 1;
6868 int ey = y + yy - 1;
6869 int content_element;
6871 if (can_replace[xx][yy] && (!change->use_random_replace ||
6872 RND(100) < change->random_percentage))
6874 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6875 RemoveMovingField(ex, ey);
6877 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6879 content_element = change->target_content[xx][yy];
6880 target_element = GET_TARGET_ELEMENT(content_element, change);
6882 ChangeElementNowExt(change, ex, ey, target_element);
6884 something_has_changed = TRUE;
6886 /* for symmetry reasons, freeze newly created border elements */
6887 if (ex != x || ey != y)
6888 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6892 if (something_has_changed)
6893 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6898 target_element = GET_TARGET_ELEMENT(change->target_element, change);
6900 ChangeElementNowExt(change, x, y, target_element);
6902 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6905 /* this uses direct change before indirect change */
6906 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
6911 static void ChangeElement(int x, int y, int page)
6913 int element = MovingOrBlocked2Element(x, y);
6914 struct ElementInfo *ei = &element_info[element];
6915 struct ElementChangeInfo *change = &ei->change_page[page];
6918 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
6921 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6922 x, y, element, element_info[element].token_name);
6923 printf("ChangeElement(): This should never happen!\n");
6928 /* this can happen with classic bombs on walkable, changing elements */
6929 if (!CAN_CHANGE(element))
6932 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
6933 ChangeDelay[x][y] = 0;
6939 if (ChangeDelay[x][y] == 0) /* initialize element change */
6941 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
6943 ResetGfxAnimation(x, y);
6944 ResetRandomAnimationValue(x, y);
6946 if (change->pre_change_function)
6947 change->pre_change_function(x, y);
6950 ChangeDelay[x][y]--;
6952 if (ChangeDelay[x][y] != 0) /* continue element change */
6954 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6956 if (IS_ANIMATED(graphic))
6957 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6959 if (change->change_function)
6960 change->change_function(x, y);
6962 else /* finish element change */
6964 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6966 page = ChangePage[x][y];
6967 ChangePage[x][y] = -1;
6969 change = &ei->change_page[page];
6972 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6974 ChangeDelay[x][y] = 1; /* try change after next move step */
6975 ChangePage[x][y] = page; /* remember page to use for change */
6980 if (ChangeElementNow(x, y, element, page))
6982 if (change->post_change_function)
6983 change->post_change_function(x, y);
6988 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
6989 int trigger_element,
6995 boolean change_done_any = FALSE;
6996 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
6999 if (!(trigger_events[trigger_element][trigger_event]))
7002 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7004 int element = EL_CUSTOM_START + i;
7005 boolean change_done = FALSE;
7007 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7008 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7011 for (p = 0; p < element_info[element].num_change_pages; p++)
7013 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7015 if (change->can_change_or_has_action &&
7016 change->has_event[trigger_event] &&
7017 change->trigger_side & trigger_side &&
7018 change->trigger_player & trigger_player &&
7019 change->trigger_page & trigger_page_bits &&
7020 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7022 change->actual_trigger_element = trigger_element;
7023 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7025 if (change->can_change && !change_done)
7027 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7029 if (Feld[x][y] == element)
7031 ChangeDelay[x][y] = 1;
7032 ChangeEvent[x][y] = trigger_event;
7033 ChangeElement(x, y, p);
7038 change_done_any = TRUE;
7041 if (change->has_action)
7042 ExecuteCustomElementAction(element, p);
7047 return change_done_any;
7050 static boolean CheckElementChangeExt(int x, int y,
7052 int trigger_element,
7057 boolean change_done = FALSE;
7060 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7061 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7064 if (Feld[x][y] == EL_BLOCKED)
7066 Blocked2Moving(x, y, &x, &y);
7067 element = Feld[x][y];
7070 if (Feld[x][y] != element) /* check if element has already changed */
7073 for (p = 0; p < element_info[element].num_change_pages; p++)
7075 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7077 boolean check_trigger_element =
7078 (trigger_event == CE_TOUCHING_X ||
7079 trigger_event == CE_HITTING_X ||
7080 trigger_event == CE_HIT_BY_X);
7082 if (change->can_change_or_has_action &&
7083 change->has_event[trigger_event] &&
7084 change->trigger_side & trigger_side &&
7085 change->trigger_player & trigger_player &&
7086 (!check_trigger_element ||
7087 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
7089 change->actual_trigger_element = trigger_element;
7090 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7092 if (change->can_change && !change_done)
7094 ChangeDelay[x][y] = 1;
7095 ChangeEvent[x][y] = trigger_event;
7096 ChangeElement(x, y, p);
7101 if (change->has_action)
7102 ExecuteCustomElementAction(element, p);
7109 static void PlayPlayerSound(struct PlayerInfo *player)
7111 int jx = player->jx, jy = player->jy;
7112 int element = player->element_nr;
7113 int last_action = player->last_action_waiting;
7114 int action = player->action_waiting;
7116 if (player->is_waiting)
7118 if (action != last_action)
7119 PlayLevelSoundElementAction(jx, jy, element, action);
7121 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7125 if (action != last_action)
7126 StopSound(element_info[element].sound[last_action]);
7128 if (last_action == ACTION_SLEEPING)
7129 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7133 static void PlayAllPlayersSound()
7137 for (i = 0; i < MAX_PLAYERS; i++)
7138 if (stored_player[i].active)
7139 PlayPlayerSound(&stored_player[i]);
7142 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7144 boolean last_waiting = player->is_waiting;
7145 int move_dir = player->MovDir;
7147 player->last_action_waiting = player->action_waiting;
7151 if (!last_waiting) /* not waiting -> waiting */
7153 player->is_waiting = TRUE;
7155 player->frame_counter_bored =
7157 game.player_boring_delay_fixed +
7158 SimpleRND(game.player_boring_delay_random);
7159 player->frame_counter_sleeping =
7161 game.player_sleeping_delay_fixed +
7162 SimpleRND(game.player_sleeping_delay_random);
7164 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7167 if (game.player_sleeping_delay_fixed +
7168 game.player_sleeping_delay_random > 0 &&
7169 player->anim_delay_counter == 0 &&
7170 player->post_delay_counter == 0 &&
7171 FrameCounter >= player->frame_counter_sleeping)
7172 player->is_sleeping = TRUE;
7173 else if (game.player_boring_delay_fixed +
7174 game.player_boring_delay_random > 0 &&
7175 FrameCounter >= player->frame_counter_bored)
7176 player->is_bored = TRUE;
7178 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7179 player->is_bored ? ACTION_BORING :
7182 if (player->is_sleeping)
7184 if (player->num_special_action_sleeping > 0)
7186 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7188 int last_special_action = player->special_action_sleeping;
7189 int num_special_action = player->num_special_action_sleeping;
7190 int special_action =
7191 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7192 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7193 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7194 last_special_action + 1 : ACTION_SLEEPING);
7195 int special_graphic =
7196 el_act_dir2img(player->element_nr, special_action, move_dir);
7198 player->anim_delay_counter =
7199 graphic_info[special_graphic].anim_delay_fixed +
7200 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7201 player->post_delay_counter =
7202 graphic_info[special_graphic].post_delay_fixed +
7203 SimpleRND(graphic_info[special_graphic].post_delay_random);
7205 player->special_action_sleeping = special_action;
7208 if (player->anim_delay_counter > 0)
7210 player->action_waiting = player->special_action_sleeping;
7211 player->anim_delay_counter--;
7213 else if (player->post_delay_counter > 0)
7215 player->post_delay_counter--;
7219 else if (player->is_bored)
7221 if (player->num_special_action_bored > 0)
7223 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7225 int special_action =
7226 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7227 int special_graphic =
7228 el_act_dir2img(player->element_nr, special_action, move_dir);
7230 player->anim_delay_counter =
7231 graphic_info[special_graphic].anim_delay_fixed +
7232 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7233 player->post_delay_counter =
7234 graphic_info[special_graphic].post_delay_fixed +
7235 SimpleRND(graphic_info[special_graphic].post_delay_random);
7237 player->special_action_bored = special_action;
7240 if (player->anim_delay_counter > 0)
7242 player->action_waiting = player->special_action_bored;
7243 player->anim_delay_counter--;
7245 else if (player->post_delay_counter > 0)
7247 player->post_delay_counter--;
7252 else if (last_waiting) /* waiting -> not waiting */
7254 player->is_waiting = FALSE;
7255 player->is_bored = FALSE;
7256 player->is_sleeping = FALSE;
7258 player->frame_counter_bored = -1;
7259 player->frame_counter_sleeping = -1;
7261 player->anim_delay_counter = 0;
7262 player->post_delay_counter = 0;
7264 player->action_waiting = ACTION_DEFAULT;
7266 player->special_action_bored = ACTION_DEFAULT;
7267 player->special_action_sleeping = ACTION_DEFAULT;
7271 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7273 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7274 int left = player_action & JOY_LEFT;
7275 int right = player_action & JOY_RIGHT;
7276 int up = player_action & JOY_UP;
7277 int down = player_action & JOY_DOWN;
7278 int button1 = player_action & JOY_BUTTON_1;
7279 int button2 = player_action & JOY_BUTTON_2;
7280 int dx = (left ? -1 : right ? 1 : 0);
7281 int dy = (up ? -1 : down ? 1 : 0);
7283 if (!player->active || tape.pausing)
7289 snapped = SnapField(player, dx, dy);
7293 dropped = DropElement(player);
7295 moved = MovePlayer(player, dx, dy);
7298 if (tape.single_step && tape.recording && !tape.pausing)
7300 if (button1 || (dropped && !moved))
7302 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7303 SnapField(player, 0, 0); /* stop snapping */
7307 SetPlayerWaiting(player, FALSE);
7309 return player_action;
7313 /* no actions for this player (no input at player's configured device) */
7315 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7316 SnapField(player, 0, 0);
7317 CheckGravityMovementWhenNotMoving(player);
7319 if (player->MovPos == 0)
7320 SetPlayerWaiting(player, TRUE);
7322 if (player->MovPos == 0) /* needed for tape.playing */
7323 player->is_moving = FALSE;
7325 player->is_dropping = FALSE;
7331 void AdvanceFrameAndPlayerCounters(int player_nr)
7335 /* advance frame counters (global frame counter and time frame counter) */
7339 /* advance player counters (counters for move delay, move animation etc.) */
7340 for (i = 0; i < MAX_PLAYERS; i++)
7342 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
7344 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7346 if (!advance_player_counters) /* not all players may be affected */
7349 stored_player[i].Frame += move_frames;
7351 if (stored_player[i].MovPos != 0)
7352 stored_player[i].StepFrame += move_frames;
7354 if (stored_player[i].move_delay > 0)
7355 stored_player[i].move_delay--;
7357 /* due to bugs in previous versions, counter must count up, not down */
7358 if (stored_player[i].push_delay != -1)
7359 stored_player[i].push_delay++;
7361 if (stored_player[i].drop_delay > 0)
7362 stored_player[i].drop_delay--;
7368 static unsigned long game_frame_delay = 0;
7369 unsigned long game_frame_delay_value;
7370 int magic_wall_x = 0, magic_wall_y = 0;
7371 int i, x, y, element, graphic;
7372 byte *recorded_player_action;
7373 byte summarized_player_action = 0;
7374 byte tape_action[MAX_PLAYERS];
7376 if (game_status != GAME_MODE_PLAYING)
7379 game_frame_delay_value =
7380 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7382 if (tape.playing && tape.warp_forward && !tape.pausing)
7383 game_frame_delay_value = 0;
7385 /* ---------- main game synchronization point ---------- */
7387 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
7389 if (network_playing && !network_player_action_received)
7391 /* try to get network player actions in time */
7393 #if defined(NETWORK_AVALIABLE)
7394 /* last chance to get network player actions without main loop delay */
7398 /* game was quit by network peer */
7399 if (game_status != GAME_MODE_PLAYING)
7402 if (!network_player_action_received)
7403 return; /* failed to get network player actions in time */
7409 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7412 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
7413 if (recorded_player_action == NULL && tape.pausing)
7417 for (i = 0; i < MAX_PLAYERS; i++)
7419 summarized_player_action |= stored_player[i].action;
7421 if (!network_playing)
7422 stored_player[i].effective_action = stored_player[i].action;
7425 #if defined(NETWORK_AVALIABLE)
7426 if (network_playing)
7427 SendToServer_MovePlayer(summarized_player_action);
7430 if (!options.network && !setup.team_mode)
7431 local_player->effective_action = summarized_player_action;
7433 if (recorded_player_action != NULL)
7434 for (i = 0; i < MAX_PLAYERS; i++)
7435 stored_player[i].effective_action = recorded_player_action[i];
7437 for (i = 0; i < MAX_PLAYERS; i++)
7439 tape_action[i] = stored_player[i].effective_action;
7441 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7442 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7445 /* only save actions from input devices, but not programmed actions */
7447 TapeRecordAction(tape_action);
7449 for (i = 0; i < MAX_PLAYERS; i++)
7451 int actual_player_action = stored_player[i].effective_action;
7454 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
7455 - rnd_equinox_tetrachloride 048
7456 - rnd_equinox_tetrachloride_ii 096
7457 - rnd_emanuel_schmieg 002
7458 - doctor_sloan_ww 001, 020
7460 if (stored_player[i].MovPos == 0)
7461 CheckGravityMovement(&stored_player[i]);
7464 /* overwrite programmed action with tape action */
7465 if (stored_player[i].programmed_action)
7466 actual_player_action = stored_player[i].programmed_action;
7469 PlayerActions(&stored_player[i], actual_player_action);
7471 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7473 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7474 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7477 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7480 network_player_action_received = FALSE;
7482 ScrollScreen(NULL, SCROLL_GO_ON);
7484 /* for backwards compatibility, the following code emulates a fixed bug that
7485 occured when pushing elements (causing elements that just made their last
7486 pushing step to already (if possible) make their first falling step in the
7487 same game frame, which is bad); this code is also needed to use the famous
7488 "spring push bug" which is used in older levels and might be wanted to be
7489 used also in newer levels, but in this case the buggy pushing code is only
7490 affecting the "spring" element and no other elements */
7492 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7494 for (i = 0; i < MAX_PLAYERS; i++)
7496 struct PlayerInfo *player = &stored_player[i];
7500 if (player->active && player->is_pushing && player->is_moving &&
7502 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7503 Feld[x][y] == EL_SPRING))
7505 ContinueMoving(x, y);
7507 /* continue moving after pushing (this is actually a bug) */
7508 if (!IS_MOVING(x, y))
7516 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7518 Changed[x][y] = FALSE;
7519 ChangeEvent[x][y] = -1;
7521 /* this must be handled before main playfield loop */
7522 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
7525 if (MovDelay[x][y] <= 0)
7530 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7532 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7533 printf("GameActions(): This should never happen!\n");
7535 ChangePage[x][y] = -1;
7540 if (WasJustMoving[x][y] > 0)
7541 WasJustMoving[x][y]--;
7542 if (WasJustFalling[x][y] > 0)
7543 WasJustFalling[x][y]--;
7544 if (CheckCollision[x][y] > 0)
7545 CheckCollision[x][y]--;
7549 /* reset finished pushing action (not done in ContinueMoving() to allow
7550 continous pushing animation for elements with zero push delay) */
7551 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7553 ResetGfxAnimation(x, y);
7554 DrawLevelField(x, y);
7558 if (IS_BLOCKED(x, y))
7562 Blocked2Moving(x, y, &oldx, &oldy);
7563 if (!IS_MOVING(oldx, oldy))
7565 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7566 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7567 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7568 printf("GameActions(): This should never happen!\n");
7574 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7576 element = Feld[x][y];
7577 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7579 if (graphic_info[graphic].anim_global_sync)
7580 GfxFrame[x][y] = FrameCounter;
7582 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7583 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7584 ResetRandomAnimationValue(x, y);
7586 SetRandomAnimationValue(x, y);
7588 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7590 if (IS_INACTIVE(element))
7592 if (IS_ANIMATED(graphic))
7593 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7598 /* this may take place after moving, so 'element' may have changed */
7599 if (IS_CHANGING(x, y) &&
7600 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7603 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7604 element_info[element].event_page_nr[CE_DELAY]);
7606 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7609 element = Feld[x][y];
7610 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7613 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7617 element = Feld[x][y];
7618 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7620 if (IS_ANIMATED(graphic) &&
7623 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7625 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7626 EdelsteinFunkeln(x, y);
7628 else if ((element == EL_ACID ||
7629 element == EL_EXIT_OPEN ||
7630 element == EL_SP_EXIT_OPEN ||
7631 element == EL_SP_TERMINAL ||
7632 element == EL_SP_TERMINAL_ACTIVE ||
7633 element == EL_EXTRA_TIME ||
7634 element == EL_SHIELD_NORMAL ||
7635 element == EL_SHIELD_DEADLY) &&
7636 IS_ANIMATED(graphic))
7637 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7638 else if (IS_MOVING(x, y))
7639 ContinueMoving(x, y);
7640 else if (IS_ACTIVE_BOMB(element))
7641 CheckDynamite(x, y);
7642 else if (element == EL_AMOEBA_GROWING)
7643 AmoebeWaechst(x, y);
7644 else if (element == EL_AMOEBA_SHRINKING)
7645 AmoebaDisappearing(x, y);
7647 #if !USE_NEW_AMOEBA_CODE
7648 else if (IS_AMOEBALIVE(element))
7649 AmoebeAbleger(x, y);
7652 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7654 else if (element == EL_EXIT_CLOSED)
7656 else if (element == EL_SP_EXIT_CLOSED)
7658 else if (element == EL_EXPANDABLE_WALL_GROWING)
7660 else if (element == EL_EXPANDABLE_WALL ||
7661 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7662 element == EL_EXPANDABLE_WALL_VERTICAL ||
7663 element == EL_EXPANDABLE_WALL_ANY)
7665 else if (element == EL_FLAMES)
7666 CheckForDragon(x, y);
7667 else if (element == EL_EXPLOSION)
7668 ; /* drawing of correct explosion animation is handled separately */
7669 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7670 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7672 if (IS_BELT_ACTIVE(element))
7673 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7675 if (game.magic_wall_active)
7677 int jx = local_player->jx, jy = local_player->jy;
7679 /* play the element sound at the position nearest to the player */
7680 if ((element == EL_MAGIC_WALL_FULL ||
7681 element == EL_MAGIC_WALL_ACTIVE ||
7682 element == EL_MAGIC_WALL_EMPTYING ||
7683 element == EL_BD_MAGIC_WALL_FULL ||
7684 element == EL_BD_MAGIC_WALL_ACTIVE ||
7685 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7686 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7694 #if USE_NEW_AMOEBA_CODE
7695 /* new experimental amoeba growth stuff */
7696 if (!(FrameCounter % 8))
7698 static unsigned long random = 1684108901;
7700 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7702 x = RND(lev_fieldx);
7703 y = RND(lev_fieldy);
7704 element = Feld[x][y];
7706 if (!IS_PLAYER(x,y) &&
7707 (element == EL_EMPTY ||
7708 CAN_GROW_INTO(element) ||
7709 element == EL_QUICKSAND_EMPTY ||
7710 element == EL_ACID_SPLASH_LEFT ||
7711 element == EL_ACID_SPLASH_RIGHT))
7713 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7714 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7715 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7716 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7717 Feld[x][y] = EL_AMOEBA_DROP;
7720 random = random * 129 + 1;
7726 if (game.explosions_delayed)
7729 game.explosions_delayed = FALSE;
7731 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7733 element = Feld[x][y];
7735 if (ExplodeField[x][y])
7736 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7737 else if (element == EL_EXPLOSION)
7738 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
7740 ExplodeField[x][y] = EX_TYPE_NONE;
7743 game.explosions_delayed = TRUE;
7746 if (game.magic_wall_active)
7748 if (!(game.magic_wall_time_left % 4))
7750 int element = Feld[magic_wall_x][magic_wall_y];
7752 if (element == EL_BD_MAGIC_WALL_FULL ||
7753 element == EL_BD_MAGIC_WALL_ACTIVE ||
7754 element == EL_BD_MAGIC_WALL_EMPTYING)
7755 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7757 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7760 if (game.magic_wall_time_left > 0)
7762 game.magic_wall_time_left--;
7763 if (!game.magic_wall_time_left)
7765 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7767 element = Feld[x][y];
7769 if (element == EL_MAGIC_WALL_ACTIVE ||
7770 element == EL_MAGIC_WALL_FULL)
7772 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7773 DrawLevelField(x, y);
7775 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7776 element == EL_BD_MAGIC_WALL_FULL)
7778 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7779 DrawLevelField(x, y);
7783 game.magic_wall_active = FALSE;
7788 if (game.light_time_left > 0)
7790 game.light_time_left--;
7792 if (game.light_time_left == 0)
7793 RedrawAllLightSwitchesAndInvisibleElements();
7796 if (game.timegate_time_left > 0)
7798 game.timegate_time_left--;
7800 if (game.timegate_time_left == 0)
7801 CloseAllOpenTimegates();
7804 for (i = 0; i < MAX_PLAYERS; i++)
7806 struct PlayerInfo *player = &stored_player[i];
7808 if (SHIELD_ON(player))
7810 if (player->shield_deadly_time_left)
7811 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7812 else if (player->shield_normal_time_left)
7813 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7817 if (TimeFrames >= FRAMES_PER_SECOND)
7822 for (i = 0; i < MAX_PLAYERS; i++)
7824 struct PlayerInfo *player = &stored_player[i];
7826 if (SHIELD_ON(player))
7828 player->shield_normal_time_left--;
7830 if (player->shield_deadly_time_left > 0)
7831 player->shield_deadly_time_left--;
7835 if (!level.use_step_counter)
7843 if (TimeLeft <= 10 && setup.time_limit)
7844 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7846 DrawGameValue_Time(TimeLeft);
7848 if (!TimeLeft && setup.time_limit)
7849 for (i = 0; i < MAX_PLAYERS; i++)
7850 KillHero(&stored_player[i]);
7852 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
7853 DrawGameValue_Time(TimePlayed);
7856 if (tape.recording || tape.playing)
7857 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
7861 PlayAllPlayersSound();
7863 if (options.debug) /* calculate frames per second */
7865 static unsigned long fps_counter = 0;
7866 static int fps_frames = 0;
7867 unsigned long fps_delay_ms = Counter() - fps_counter;
7871 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7873 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7876 fps_counter = Counter();
7879 redraw_mask |= REDRAW_FPS;
7882 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
7884 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7886 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7888 local_player->show_envelope = 0;
7891 /* use random number generator in every frame to make it less predictable */
7892 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
7896 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7898 int min_x = x, min_y = y, max_x = x, max_y = y;
7901 for (i = 0; i < MAX_PLAYERS; i++)
7903 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7905 if (!stored_player[i].active || &stored_player[i] == player)
7908 min_x = MIN(min_x, jx);
7909 min_y = MIN(min_y, jy);
7910 max_x = MAX(max_x, jx);
7911 max_y = MAX(max_y, jy);
7914 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7917 static boolean AllPlayersInVisibleScreen()
7921 for (i = 0; i < MAX_PLAYERS; i++)
7923 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7925 if (!stored_player[i].active)
7928 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7935 void ScrollLevel(int dx, int dy)
7937 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7940 BlitBitmap(drawto_field, drawto_field,
7941 FX + TILEX * (dx == -1) - softscroll_offset,
7942 FY + TILEY * (dy == -1) - softscroll_offset,
7943 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7944 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7945 FX + TILEX * (dx == 1) - softscroll_offset,
7946 FY + TILEY * (dy == 1) - softscroll_offset);
7950 x = (dx == 1 ? BX1 : BX2);
7951 for (y = BY1; y <= BY2; y++)
7952 DrawScreenField(x, y);
7957 y = (dy == 1 ? BY1 : BY2);
7958 for (x = BX1; x <= BX2; x++)
7959 DrawScreenField(x, y);
7962 redraw_mask |= REDRAW_FIELD;
7965 static boolean canFallDown(struct PlayerInfo *player)
7967 int jx = player->jx, jy = player->jy;
7969 return (IN_LEV_FIELD(jx, jy + 1) &&
7970 (IS_FREE(jx, jy + 1) ||
7971 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
7972 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
7973 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
7976 static boolean canPassField(int x, int y, int move_dir)
7978 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
7979 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7980 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7983 int element = Feld[x][y];
7985 return (IS_PASSABLE_FROM(element, opposite_dir) &&
7986 !CAN_MOVE(element) &&
7987 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
7988 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
7989 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
7992 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
7994 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
7995 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7996 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8000 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8001 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
8002 (IS_DIGGABLE(Feld[newx][newy]) ||
8003 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8004 canPassField(newx, newy, move_dir)));
8007 static void CheckGravityMovement(struct PlayerInfo *player)
8009 if (game.gravity && !player->programmed_action)
8011 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8012 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8013 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8014 int jx = player->jx, jy = player->jy;
8015 boolean player_is_moving_to_valid_field =
8016 (!player_is_snapping &&
8017 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8018 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8019 boolean player_can_fall_down = canFallDown(player);
8021 if (player_can_fall_down &&
8022 !player_is_moving_to_valid_field)
8023 player->programmed_action = MV_DOWN;
8027 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8029 return CheckGravityMovement(player);
8031 if (game.gravity && !player->programmed_action)
8033 int jx = player->jx, jy = player->jy;
8034 boolean field_under_player_is_free =
8035 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8036 boolean player_is_standing_on_valid_field =
8037 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8038 (IS_WALKABLE(Feld[jx][jy]) &&
8039 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8041 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8042 player->programmed_action = MV_DOWN;
8048 -----------------------------------------------------------------------------
8049 dx, dy: direction (non-diagonal) to try to move the player to
8050 real_dx, real_dy: direction as read from input device (can be diagonal)
8053 boolean MovePlayerOneStep(struct PlayerInfo *player,
8054 int dx, int dy, int real_dx, int real_dy)
8056 int jx = player->jx, jy = player->jy;
8057 int new_jx = jx + dx, new_jy = jy + dy;
8061 if (!player->active || (!dx && !dy))
8062 return MF_NO_ACTION;
8064 player->MovDir = (dx < 0 ? MV_LEFT :
8067 dy > 0 ? MV_DOWN : MV_NO_MOVING);
8069 if (!IN_LEV_FIELD(new_jx, new_jy))
8070 return MF_NO_ACTION;
8072 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8073 return MF_NO_ACTION;
8075 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8077 if (DONT_RUN_INTO(element))
8079 if (element == EL_ACID && dx == 0 && dy == 1)
8081 SplashAcid(new_jx, new_jy);
8082 Feld[jx][jy] = EL_PLAYER_1;
8083 InitMovingField(jx, jy, MV_DOWN);
8084 Store[jx][jy] = EL_ACID;
8085 ContinueMoving(jx, jy);
8089 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8094 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8095 if (can_move != MF_MOVING)
8098 /* check if DigField() has caused relocation of the player */
8099 if (player->jx != jx || player->jy != jy)
8100 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
8102 StorePlayer[jx][jy] = 0;
8103 player->last_jx = jx;
8104 player->last_jy = jy;
8105 player->jx = new_jx;
8106 player->jy = new_jy;
8107 StorePlayer[new_jx][new_jy] = player->element_nr;
8110 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8112 player->step_counter++;
8114 PlayerVisit[jx][jy] = FrameCounter;
8116 ScrollPlayer(player, SCROLL_INIT);
8121 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8123 int jx = player->jx, jy = player->jy;
8124 int old_jx = jx, old_jy = jy;
8125 int moved = MF_NO_ACTION;
8127 if (!player->active)
8132 if (player->MovPos == 0)
8134 player->is_moving = FALSE;
8135 player->is_digging = FALSE;
8136 player->is_collecting = FALSE;
8137 player->is_snapping = FALSE;
8138 player->is_pushing = FALSE;
8144 if (player->move_delay > 0)
8147 player->move_delay = -1; /* set to "uninitialized" value */
8149 /* store if player is automatically moved to next field */
8150 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
8152 /* remove the last programmed player action */
8153 player->programmed_action = 0;
8157 /* should only happen if pre-1.2 tape recordings are played */
8158 /* this is only for backward compatibility */
8160 int original_move_delay_value = player->move_delay_value;
8163 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8167 /* scroll remaining steps with finest movement resolution */
8168 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8170 while (player->MovPos)
8172 ScrollPlayer(player, SCROLL_GO_ON);
8173 ScrollScreen(NULL, SCROLL_GO_ON);
8175 AdvanceFrameAndPlayerCounters(player->index_nr);
8181 player->move_delay_value = original_move_delay_value;
8184 if (player->last_move_dir & MV_HORIZONTAL)
8186 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8187 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8191 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8192 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8198 if (moved & MF_MOVING && !ScreenMovPos &&
8199 (player == local_player || !options.network))
8201 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8202 int offset = (setup.scroll_delay ? 3 : 0);
8204 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8206 /* actual player has left the screen -- scroll in that direction */
8207 if (jx != old_jx) /* player has moved horizontally */
8208 scroll_x += (jx - old_jx);
8209 else /* player has moved vertically */
8210 scroll_y += (jy - old_jy);
8214 if (jx != old_jx) /* player has moved horizontally */
8216 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8217 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8218 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8220 /* don't scroll over playfield boundaries */
8221 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8222 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8224 /* don't scroll more than one field at a time */
8225 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8227 /* don't scroll against the player's moving direction */
8228 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8229 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8230 scroll_x = old_scroll_x;
8232 else /* player has moved vertically */
8234 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8235 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8236 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8238 /* don't scroll over playfield boundaries */
8239 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8240 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8242 /* don't scroll more than one field at a time */
8243 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8245 /* don't scroll against the player's moving direction */
8246 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8247 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8248 scroll_y = old_scroll_y;
8252 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8254 if (!options.network && !AllPlayersInVisibleScreen())
8256 scroll_x = old_scroll_x;
8257 scroll_y = old_scroll_y;
8261 ScrollScreen(player, SCROLL_INIT);
8262 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8267 player->StepFrame = 0;
8269 if (moved & MF_MOVING)
8271 if (old_jx != jx && old_jy == jy)
8272 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8273 else if (old_jx == jx && old_jy != jy)
8274 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8276 DrawLevelField(jx, jy); /* for "crumbled sand" */
8278 player->last_move_dir = player->MovDir;
8279 player->is_moving = TRUE;
8280 player->is_snapping = FALSE;
8281 player->is_switching = FALSE;
8282 player->is_dropping = FALSE;
8286 CheckGravityMovementWhenNotMoving(player);
8288 player->is_moving = FALSE;
8290 /* at this point, the player is allowed to move, but cannot move right now
8291 (e.g. because of something blocking the way) -- ensure that the player
8292 is also allowed to move in the next frame (in old versions before 3.1.1,
8293 the player was forced to wait again for eight frames before next try) */
8295 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8296 player->move_delay = 0; /* allow direct movement in the next frame */
8299 if (player->move_delay == -1) /* not yet initialized by DigField() */
8300 player->move_delay = player->move_delay_value;
8302 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8304 TestIfHeroTouchesBadThing(jx, jy);
8305 TestIfPlayerTouchesCustomElement(jx, jy);
8308 if (!player->active)
8314 void ScrollPlayer(struct PlayerInfo *player, int mode)
8316 int jx = player->jx, jy = player->jy;
8317 int last_jx = player->last_jx, last_jy = player->last_jy;
8318 int move_stepsize = TILEX / player->move_delay_value;
8320 if (!player->active || !player->MovPos)
8323 if (mode == SCROLL_INIT)
8325 player->actual_frame_counter = FrameCounter;
8326 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8328 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
8329 Feld[last_jx][last_jy] == EL_EMPTY)
8331 int last_field_block_delay = 0; /* start with no blocking at all */
8332 int block_delay_adjustment = player->block_delay_adjustment;
8334 /* if player blocks last field, add delay for exactly one move */
8335 if (player->block_last_field)
8337 last_field_block_delay += player->move_delay_value;
8339 /* when blocking enabled, prevent moving up despite gravity */
8340 if (game.gravity && player->MovDir == MV_UP)
8341 block_delay_adjustment = -1;
8344 /* add block delay adjustment (also possible when not blocking) */
8345 last_field_block_delay += block_delay_adjustment;
8347 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8348 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
8353 else if (!FrameReached(&player->actual_frame_counter, 1))
8356 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8357 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8359 /* before DrawPlayer() to draw correct player graphic for this case */
8360 if (player->MovPos == 0)
8361 CheckGravityMovement(player);
8363 if (player->MovPos == 0) /* player reached destination field */
8365 if (player->move_delay_reset_counter > 0)
8367 player->move_delay_reset_counter--;
8369 if (player->move_delay_reset_counter == 0)
8371 /* continue with normal speed after quickly moving through gate */
8372 HALVE_PLAYER_SPEED(player);
8374 /* be able to make the next move without delay */
8375 player->move_delay = 0;
8379 player->last_jx = jx;
8380 player->last_jy = jy;
8382 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8383 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8384 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8386 DrawPlayer(player); /* needed here only to cleanup last field */
8389 if (local_player->friends_still_needed == 0 ||
8390 IS_SP_ELEMENT(Feld[jx][jy]))
8391 player->LevelSolved = player->GameOver = TRUE;
8394 /* this breaks one level: "machine", level 000 */
8396 int move_direction = player->MovDir;
8397 int enter_side = MV_DIR_OPPOSITE(move_direction);
8398 int leave_side = move_direction;
8399 int old_jx = last_jx;
8400 int old_jy = last_jy;
8401 int old_element = Feld[old_jx][old_jy];
8402 int new_element = Feld[jx][jy];
8404 if (IS_CUSTOM_ELEMENT(old_element))
8405 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
8407 player->index_bit, leave_side);
8409 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
8411 player->index_bit, leave_side);
8413 if (IS_CUSTOM_ELEMENT(new_element))
8414 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
8415 player->index_bit, enter_side);
8417 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
8419 player->index_bit, enter_side);
8422 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8424 TestIfHeroTouchesBadThing(jx, jy);
8425 TestIfPlayerTouchesCustomElement(jx, jy);
8427 /* needed because pushed element has not yet reached its destination,
8428 so it would trigger a change event at its previous field location */
8429 if (!player->is_pushing)
8430 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8432 if (!player->active)
8436 if (level.use_step_counter)
8446 if (TimeLeft <= 10 && setup.time_limit)
8447 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8449 DrawGameValue_Time(TimeLeft);
8451 if (!TimeLeft && setup.time_limit)
8452 for (i = 0; i < MAX_PLAYERS; i++)
8453 KillHero(&stored_player[i]);
8455 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8456 DrawGameValue_Time(TimePlayed);
8459 if (tape.single_step && tape.recording && !tape.pausing &&
8460 !player->programmed_action)
8461 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8465 void ScrollScreen(struct PlayerInfo *player, int mode)
8467 static unsigned long screen_frame_counter = 0;
8469 if (mode == SCROLL_INIT)
8471 /* set scrolling step size according to actual player's moving speed */
8472 ScrollStepSize = TILEX / player->move_delay_value;
8474 screen_frame_counter = FrameCounter;
8475 ScreenMovDir = player->MovDir;
8476 ScreenMovPos = player->MovPos;
8477 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8480 else if (!FrameReached(&screen_frame_counter, 1))
8485 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8486 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8487 redraw_mask |= REDRAW_FIELD;
8490 ScreenMovDir = MV_NO_MOVING;
8493 void TestIfPlayerTouchesCustomElement(int x, int y)
8495 static int xy[4][2] =
8502 static int trigger_sides[4][2] =
8504 /* center side border side */
8505 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8506 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8507 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8508 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8510 static int touch_dir[4] =
8517 int center_element = Feld[x][y]; /* should always be non-moving! */
8520 for (i = 0; i < NUM_DIRECTIONS; i++)
8522 int xx = x + xy[i][0];
8523 int yy = y + xy[i][1];
8524 int center_side = trigger_sides[i][0];
8525 int border_side = trigger_sides[i][1];
8528 if (!IN_LEV_FIELD(xx, yy))
8531 if (IS_PLAYER(x, y))
8533 struct PlayerInfo *player = PLAYERINFO(x, y);
8535 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8536 border_element = Feld[xx][yy]; /* may be moving! */
8537 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8538 border_element = Feld[xx][yy];
8539 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8540 border_element = MovingOrBlocked2Element(xx, yy);
8542 continue; /* center and border element do not touch */
8544 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
8545 player->index_bit, border_side);
8546 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
8547 CE_PLAYER_TOUCHES_X,
8548 player->index_bit, border_side);
8550 else if (IS_PLAYER(xx, yy))
8552 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8554 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8556 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8557 continue; /* center and border element do not touch */
8560 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
8561 player->index_bit, center_side);
8562 CheckTriggeredElementChangeByPlayer(x, y, center_element,
8563 CE_PLAYER_TOUCHES_X,
8564 player->index_bit, center_side);
8570 void TestIfElementTouchesCustomElement(int x, int y)
8572 static int xy[4][2] =
8579 static int trigger_sides[4][2] =
8581 /* center side border side */
8582 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8583 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8584 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8585 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8587 static int touch_dir[4] =
8594 boolean change_center_element = FALSE;
8595 int center_element = Feld[x][y]; /* should always be non-moving! */
8598 for (i = 0; i < NUM_DIRECTIONS; i++)
8600 int xx = x + xy[i][0];
8601 int yy = y + xy[i][1];
8602 int center_side = trigger_sides[i][0];
8603 int border_side = trigger_sides[i][1];
8606 if (!IN_LEV_FIELD(xx, yy))
8609 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8610 border_element = Feld[xx][yy]; /* may be moving! */
8611 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8612 border_element = Feld[xx][yy];
8613 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8614 border_element = MovingOrBlocked2Element(xx, yy);
8616 continue; /* center and border element do not touch */
8618 /* check for change of center element (but change it only once) */
8619 if (!change_center_element)
8620 change_center_element =
8621 CheckElementChangeBySide(x, y, center_element, border_element,
8622 CE_TOUCHING_X, border_side);
8624 /* check for change of border element */
8625 CheckElementChangeBySide(xx, yy, border_element, center_element,
8626 CE_TOUCHING_X, center_side);
8630 void TestIfElementHitsCustomElement(int x, int y, int direction)
8632 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8633 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8634 int hitx = x + dx, hity = y + dy;
8635 int hitting_element = Feld[x][y];
8636 int touched_element;
8638 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8641 touched_element = (IN_LEV_FIELD(hitx, hity) ?
8642 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
8644 if (IN_LEV_FIELD(hitx, hity))
8646 int opposite_direction = MV_DIR_OPPOSITE(direction);
8647 int hitting_side = direction;
8648 int touched_side = opposite_direction;
8649 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8650 MovDir[hitx][hity] != direction ||
8651 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8657 CheckElementChangeBySide(x, y, hitting_element, touched_element,
8658 CE_HITTING_X, touched_side);
8660 CheckElementChangeBySide(hitx, hity, touched_element,
8661 hitting_element, CE_HIT_BY_X, hitting_side);
8663 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
8664 CE_HIT_BY_SOMETHING, opposite_direction);
8668 /* "hitting something" is also true when hitting the playfield border */
8669 CheckElementChangeBySide(x, y, hitting_element, touched_element,
8670 CE_HITTING_SOMETHING, direction);
8674 void TestIfElementSmashesCustomElement(int x, int y, int direction)
8676 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8677 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8678 int hitx = x + dx, hity = y + dy;
8679 int hitting_element = Feld[x][y];
8680 int touched_element;
8682 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8683 !IS_FREE(hitx, hity) &&
8684 (!IS_MOVING(hitx, hity) ||
8685 MovDir[hitx][hity] != direction ||
8686 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8689 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8693 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8697 touched_element = (IN_LEV_FIELD(hitx, hity) ?
8698 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
8700 CheckElementChangeBySide(x, y, hitting_element, touched_element,
8701 EP_CAN_SMASH_EVERYTHING, direction);
8703 if (IN_LEV_FIELD(hitx, hity))
8705 int opposite_direction = MV_DIR_OPPOSITE(direction);
8706 int hitting_side = direction;
8707 int touched_side = opposite_direction;
8709 int touched_element = MovingOrBlocked2Element(hitx, hity);
8712 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8713 MovDir[hitx][hity] != direction ||
8714 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8723 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
8724 CE_SMASHED_BY_SOMETHING, opposite_direction);
8726 CheckElementChangeBySide(x, y, hitting_element, touched_element,
8727 CE_OTHER_IS_SMASHING, touched_side);
8729 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
8730 CE_OTHER_GETS_SMASHED, hitting_side);
8736 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8738 int i, kill_x = -1, kill_y = -1;
8739 int bad_element = -1;
8740 static int test_xy[4][2] =
8747 static int test_dir[4] =
8755 for (i = 0; i < NUM_DIRECTIONS; i++)
8757 int test_x, test_y, test_move_dir, test_element;
8759 test_x = good_x + test_xy[i][0];
8760 test_y = good_y + test_xy[i][1];
8762 if (!IN_LEV_FIELD(test_x, test_y))
8766 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8768 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8770 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8771 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8773 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8774 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8778 bad_element = test_element;
8784 if (kill_x != -1 || kill_y != -1)
8786 if (IS_PLAYER(good_x, good_y))
8788 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8790 if (player->shield_deadly_time_left > 0 &&
8791 !IS_INDESTRUCTIBLE(bad_element))
8792 Bang(kill_x, kill_y);
8793 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
8797 Bang(good_x, good_y);
8801 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8803 int i, kill_x = -1, kill_y = -1;
8804 int bad_element = Feld[bad_x][bad_y];
8805 static int test_xy[4][2] =
8812 static int touch_dir[4] =
8819 static int test_dir[4] =
8827 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8830 for (i = 0; i < NUM_DIRECTIONS; i++)
8832 int test_x, test_y, test_move_dir, test_element;
8834 test_x = bad_x + test_xy[i][0];
8835 test_y = bad_y + test_xy[i][1];
8836 if (!IN_LEV_FIELD(test_x, test_y))
8840 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8842 test_element = Feld[test_x][test_y];
8844 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8845 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8847 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8848 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8850 /* good thing is player or penguin that does not move away */
8851 if (IS_PLAYER(test_x, test_y))
8853 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8855 if (bad_element == EL_ROBOT && player->is_moving)
8856 continue; /* robot does not kill player if he is moving */
8858 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8860 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8861 continue; /* center and border element do not touch */
8868 else if (test_element == EL_PENGUIN)
8877 if (kill_x != -1 || kill_y != -1)
8879 if (IS_PLAYER(kill_x, kill_y))
8881 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8883 if (player->shield_deadly_time_left > 0 &&
8884 !IS_INDESTRUCTIBLE(bad_element))
8886 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
8890 Bang(kill_x, kill_y);
8894 void TestIfHeroTouchesBadThing(int x, int y)
8896 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8899 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8901 TestIfGoodThingHitsBadThing(x, y, move_dir);
8904 void TestIfBadThingTouchesHero(int x, int y)
8906 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8909 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8911 TestIfBadThingHitsGoodThing(x, y, move_dir);
8914 void TestIfFriendTouchesBadThing(int x, int y)
8916 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8919 void TestIfBadThingTouchesFriend(int x, int y)
8921 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8924 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8926 int i, kill_x = bad_x, kill_y = bad_y;
8927 static int xy[4][2] =
8935 for (i = 0; i < NUM_DIRECTIONS; i++)
8939 x = bad_x + xy[i][0];
8940 y = bad_y + xy[i][1];
8941 if (!IN_LEV_FIELD(x, y))
8944 element = Feld[x][y];
8945 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8946 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8954 if (kill_x != bad_x || kill_y != bad_y)
8958 void KillHero(struct PlayerInfo *player)
8960 int jx = player->jx, jy = player->jy;
8962 if (!player->active)
8965 /* remove accessible field at the player's position */
8966 Feld[jx][jy] = EL_EMPTY;
8968 /* deactivate shield (else Bang()/Explode() would not work right) */
8969 player->shield_normal_time_left = 0;
8970 player->shield_deadly_time_left = 0;
8976 static void KillHeroUnlessEnemyProtected(int x, int y)
8978 if (!PLAYER_ENEMY_PROTECTED(x, y))
8979 KillHero(PLAYERINFO(x, y));
8982 static void KillHeroUnlessExplosionProtected(int x, int y)
8984 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
8985 KillHero(PLAYERINFO(x, y));
8988 void BuryHero(struct PlayerInfo *player)
8990 int jx = player->jx, jy = player->jy;
8992 if (!player->active)
8995 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8996 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8998 player->GameOver = TRUE;
9002 void RemoveHero(struct PlayerInfo *player)
9004 int jx = player->jx, jy = player->jy;
9005 int i, found = FALSE;
9007 player->present = FALSE;
9008 player->active = FALSE;
9010 if (!ExplodeField[jx][jy])
9011 StorePlayer[jx][jy] = 0;
9013 for (i = 0; i < MAX_PLAYERS; i++)
9014 if (stored_player[i].active)
9018 AllPlayersGone = TRUE;
9025 =============================================================================
9026 checkDiagonalPushing()
9027 -----------------------------------------------------------------------------
9028 check if diagonal input device direction results in pushing of object
9029 (by checking if the alternative direction is walkable, diggable, ...)
9030 =============================================================================
9033 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9034 int x, int y, int real_dx, int real_dy)
9036 int jx, jy, dx, dy, xx, yy;
9038 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9041 /* diagonal direction: check alternative direction */
9046 xx = jx + (dx == 0 ? real_dx : 0);
9047 yy = jy + (dy == 0 ? real_dy : 0);
9049 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9053 =============================================================================
9055 -----------------------------------------------------------------------------
9056 x, y: field next to player (non-diagonal) to try to dig to
9057 real_dx, real_dy: direction as read from input device (can be diagonal)
9058 =============================================================================
9061 int DigField(struct PlayerInfo *player,
9062 int oldx, int oldy, int x, int y,
9063 int real_dx, int real_dy, int mode)
9065 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
9066 boolean player_was_pushing = player->is_pushing;
9067 int jx = oldx, jy = oldy;
9068 int dx = x - jx, dy = y - jy;
9069 int nextx = x + dx, nexty = y + dy;
9070 int move_direction = (dx == -1 ? MV_LEFT :
9071 dx == +1 ? MV_RIGHT :
9073 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9074 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9075 int dig_side = MV_DIR_OPPOSITE(move_direction);
9076 int old_element = Feld[jx][jy];
9079 if (is_player) /* function can also be called by EL_PENGUIN */
9081 if (player->MovPos == 0)
9083 player->is_digging = FALSE;
9084 player->is_collecting = FALSE;
9087 if (player->MovPos == 0) /* last pushing move finished */
9088 player->is_pushing = FALSE;
9090 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9092 player->is_switching = FALSE;
9093 player->push_delay = -1;
9095 return MF_NO_ACTION;
9099 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9100 return MF_NO_ACTION;
9102 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9103 old_element = Back[jx][jy];
9105 /* in case of element dropped at player position, check background */
9106 else if (Back[jx][jy] != EL_EMPTY &&
9107 game.engine_version >= VERSION_IDENT(2,2,0,0))
9108 old_element = Back[jx][jy];
9110 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
9111 return MF_NO_ACTION; /* field has no opening in this direction */
9113 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
9114 return MF_NO_ACTION; /* field has no opening in this direction */
9116 element = Feld[x][y];
9118 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
9119 return MF_NO_ACTION;
9121 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9122 game.engine_version >= VERSION_IDENT(2,2,0,0))
9123 return MF_NO_ACTION;
9125 if (game.gravity && is_player && !player->is_auto_moving &&
9126 canFallDown(player) && move_direction != MV_DOWN &&
9127 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
9128 return MF_NO_ACTION; /* player cannot walk here due to gravity */
9130 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
9132 int sound_element = SND_ELEMENT(element);
9133 int sound_action = ACTION_WALKING;
9135 if (IS_RND_GATE(element))
9137 if (!player->key[RND_GATE_NR(element)])
9138 return MF_NO_ACTION;
9140 else if (IS_RND_GATE_GRAY(element))
9142 if (!player->key[RND_GATE_GRAY_NR(element)])
9143 return MF_NO_ACTION;
9145 else if (element == EL_EXIT_OPEN ||
9146 element == EL_SP_EXIT_OPEN ||
9147 element == EL_SP_EXIT_OPENING)
9149 sound_action = ACTION_PASSING; /* player is passing exit */
9151 else if (element == EL_EMPTY)
9153 sound_action = ACTION_MOVING; /* nothing to walk on */
9156 /* play sound from background or player, whatever is available */
9157 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
9158 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
9160 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9162 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
9164 if (!ACCESS_FROM(element, opposite_direction))
9165 return MF_NO_ACTION; /* field not accessible from this direction */
9167 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9168 return MF_NO_ACTION;
9170 if (IS_EM_GATE(element))
9172 if (!player->key[EM_GATE_NR(element)])
9173 return MF_NO_ACTION;
9175 else if (IS_EM_GATE_GRAY(element))
9177 if (!player->key[EM_GATE_GRAY_NR(element)])
9178 return MF_NO_ACTION;
9180 else if (IS_SP_PORT(element))
9182 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9183 element == EL_SP_GRAVITY_PORT_RIGHT ||
9184 element == EL_SP_GRAVITY_PORT_UP ||
9185 element == EL_SP_GRAVITY_PORT_DOWN)
9186 game.gravity = !game.gravity;
9187 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
9188 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
9189 element == EL_SP_GRAVITY_ON_PORT_UP ||
9190 element == EL_SP_GRAVITY_ON_PORT_DOWN)
9191 game.gravity = TRUE;
9192 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
9193 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
9194 element == EL_SP_GRAVITY_OFF_PORT_UP ||
9195 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
9196 game.gravity = FALSE;
9199 /* automatically move to the next field with double speed */
9200 player->programmed_action = move_direction;
9202 if (player->move_delay_reset_counter == 0)
9204 player->move_delay_reset_counter = 2; /* two double speed steps */
9206 DOUBLE_PLAYER_SPEED(player);
9209 PlayLevelSoundAction(x, y, ACTION_PASSING);
9211 else if (IS_DIGGABLE(element))
9215 if (mode != DF_SNAP)
9217 GfxElement[x][y] = GFX_ELEMENT(element);
9218 player->is_digging = TRUE;
9221 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9223 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
9224 player->index_bit, dig_side);
9226 if (mode == DF_SNAP)
9227 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9229 else if (IS_COLLECTIBLE(element))
9233 if (is_player && mode != DF_SNAP)
9235 GfxElement[x][y] = element;
9236 player->is_collecting = TRUE;
9239 if (element == EL_SPEED_PILL)
9241 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9243 else if (element == EL_EXTRA_TIME && level.time > 0)
9246 DrawGameValue_Time(TimeLeft);
9248 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9250 player->shield_normal_time_left += 10;
9251 if (element == EL_SHIELD_DEADLY)
9252 player->shield_deadly_time_left += 10;
9254 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9256 if (player->inventory_size < MAX_INVENTORY_SIZE)
9257 player->inventory_element[player->inventory_size++] = element;
9259 DrawGameValue_Dynamite(local_player->inventory_size);
9261 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9263 player->dynabomb_count++;
9264 player->dynabombs_left++;
9266 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9268 player->dynabomb_size++;
9270 else if (element == EL_DYNABOMB_INCREASE_POWER)
9272 player->dynabomb_xl = TRUE;
9274 else if (IS_KEY(element))
9276 player->key[KEY_NR(element)] = TRUE;
9278 DrawGameValue_Keys(player->key);
9280 redraw_mask |= REDRAW_DOOR_1;
9282 else if (IS_ENVELOPE(element))
9284 player->show_envelope = element;
9286 else if (IS_DROPPABLE(element) ||
9287 IS_THROWABLE(element)) /* can be collected and dropped */
9291 if (element_info[element].collect_count == 0)
9292 player->inventory_infinite_element = element;
9294 for (i = 0; i < element_info[element].collect_count; i++)
9295 if (player->inventory_size < MAX_INVENTORY_SIZE)
9296 player->inventory_element[player->inventory_size++] = element;
9298 DrawGameValue_Dynamite(local_player->inventory_size);
9300 else if (element_info[element].collect_count > 0)
9302 local_player->gems_still_needed -=
9303 element_info[element].collect_count;
9304 if (local_player->gems_still_needed < 0)
9305 local_player->gems_still_needed = 0;
9307 DrawGameValue_Emeralds(local_player->gems_still_needed);
9310 RaiseScoreElement(element);
9311 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9314 CheckTriggeredElementChangeByPlayer(x, y, element,
9315 CE_PLAYER_COLLECTS_X,
9316 player->index_bit, dig_side);
9318 if (mode == DF_SNAP)
9319 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9321 else if (IS_PUSHABLE(element))
9323 if (mode == DF_SNAP && element != EL_BD_ROCK)
9324 return MF_NO_ACTION;
9326 if (CAN_FALL(element) && dy)
9327 return MF_NO_ACTION;
9329 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9330 !(element == EL_SPRING && level.use_spring_bug))
9331 return MF_NO_ACTION;
9333 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9334 ((move_direction & MV_VERTICAL &&
9335 ((element_info[element].move_pattern & MV_LEFT &&
9336 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9337 (element_info[element].move_pattern & MV_RIGHT &&
9338 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9339 (move_direction & MV_HORIZONTAL &&
9340 ((element_info[element].move_pattern & MV_UP &&
9341 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9342 (element_info[element].move_pattern & MV_DOWN &&
9343 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9344 return MF_NO_ACTION;
9346 /* do not push elements already moving away faster than player */
9347 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9348 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9349 return MF_NO_ACTION;
9351 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9353 if (player->push_delay_value == -1 || !player_was_pushing)
9354 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9356 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9358 if (player->push_delay_value == -1)
9359 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9361 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9363 if (!player->is_pushing)
9364 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9367 player->is_pushing = TRUE;
9369 if (!(IN_LEV_FIELD(nextx, nexty) &&
9370 (IS_FREE(nextx, nexty) ||
9371 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9372 IS_SB_ELEMENT(element)))))
9373 return MF_NO_ACTION;
9375 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9376 return MF_NO_ACTION;
9378 if (player->push_delay == -1) /* new pushing; restart delay */
9379 player->push_delay = 0;
9381 if (player->push_delay < player->push_delay_value &&
9382 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9383 element != EL_SPRING && element != EL_BALLOON)
9385 /* make sure that there is no move delay before next try to push */
9386 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9387 player->move_delay = 0;
9389 return MF_NO_ACTION;
9392 if (IS_SB_ELEMENT(element))
9394 if (element == EL_SOKOBAN_FIELD_FULL)
9396 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9397 local_player->sokobanfields_still_needed++;
9400 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9402 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9403 local_player->sokobanfields_still_needed--;
9406 Feld[x][y] = EL_SOKOBAN_OBJECT;
9408 if (Back[x][y] == Back[nextx][nexty])
9409 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9410 else if (Back[x][y] != 0)
9411 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9414 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9417 if (local_player->sokobanfields_still_needed == 0 &&
9418 game.emulation == EMU_SOKOBAN)
9420 player->LevelSolved = player->GameOver = TRUE;
9421 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9425 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9427 InitMovingField(x, y, move_direction);
9428 GfxAction[x][y] = ACTION_PUSHING;
9430 if (mode == DF_SNAP)
9431 ContinueMoving(x, y);
9433 MovPos[x][y] = (dx != 0 ? dx : dy);
9435 Pushed[x][y] = TRUE;
9436 Pushed[nextx][nexty] = TRUE;
9438 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9439 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9441 player->push_delay_value = -1; /* get new value later */
9443 /* check for element change _after_ element has been pushed */
9444 if (game.use_change_when_pushing_bug)
9446 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
9447 player->index_bit, dig_side);
9448 CheckTriggeredElementChangeByPlayer(x,y, element, CE_PLAYER_PUSHES_X,
9449 player->index_bit, dig_side);
9452 else if (IS_SWITCHABLE(element))
9454 if (PLAYER_SWITCHING(player, x, y))
9456 CheckTriggeredElementChangeByPlayer(x,y, element,
9457 CE_PLAYER_PRESSES_X,
9458 player->index_bit, dig_side);
9463 player->is_switching = TRUE;
9464 player->switch_x = x;
9465 player->switch_y = y;
9467 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9469 if (element == EL_ROBOT_WHEEL)
9471 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9475 DrawLevelField(x, y);
9477 else if (element == EL_SP_TERMINAL)
9481 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9483 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9485 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9486 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9489 else if (IS_BELT_SWITCH(element))
9491 ToggleBeltSwitch(x, y);
9493 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9494 element == EL_SWITCHGATE_SWITCH_DOWN)
9496 ToggleSwitchgateSwitch(x, y);
9498 else if (element == EL_LIGHT_SWITCH ||
9499 element == EL_LIGHT_SWITCH_ACTIVE)
9501 ToggleLightSwitch(x, y);
9503 else if (element == EL_TIMEGATE_SWITCH)
9505 ActivateTimegateSwitch(x, y);
9507 else if (element == EL_BALLOON_SWITCH_LEFT ||
9508 element == EL_BALLOON_SWITCH_RIGHT ||
9509 element == EL_BALLOON_SWITCH_UP ||
9510 element == EL_BALLOON_SWITCH_DOWN ||
9511 element == EL_BALLOON_SWITCH_ANY)
9513 if (element == EL_BALLOON_SWITCH_ANY)
9514 game.balloon_dir = move_direction;
9516 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9517 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9518 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9519 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9522 else if (element == EL_LAMP)
9524 Feld[x][y] = EL_LAMP_ACTIVE;
9525 local_player->lights_still_needed--;
9527 ResetGfxAnimation(x, y);
9528 DrawLevelField(x, y);
9530 else if (element == EL_TIME_ORB_FULL)
9532 Feld[x][y] = EL_TIME_ORB_EMPTY;
9534 DrawGameValue_Time(TimeLeft);
9536 ResetGfxAnimation(x, y);
9537 DrawLevelField(x, y);
9540 CheckTriggeredElementChangeByPlayer(x, y, element,
9542 player->index_bit, dig_side);
9544 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
9545 player->index_bit, dig_side);
9551 if (!PLAYER_SWITCHING(player, x, y))
9553 player->is_switching = TRUE;
9554 player->switch_x = x;
9555 player->switch_y = y;
9557 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
9558 player->index_bit, dig_side);
9559 CheckTriggeredElementChangeByPlayer(x, y, element,
9561 player->index_bit, dig_side);
9564 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
9565 player->index_bit, dig_side);
9566 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
9567 player->index_bit, dig_side);
9569 return MF_NO_ACTION;
9572 player->push_delay = -1;
9574 if (is_player) /* function can also be called by EL_PENGUIN */
9576 if (Feld[x][y] != element) /* really digged/collected something */
9577 player->is_collecting = !player->is_digging;
9583 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9585 int jx = player->jx, jy = player->jy;
9586 int x = jx + dx, y = jy + dy;
9587 int snap_direction = (dx == -1 ? MV_LEFT :
9588 dx == +1 ? MV_RIGHT :
9590 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9592 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
9595 if (!player->active || !IN_LEV_FIELD(x, y))
9603 if (player->MovPos == 0)
9604 player->is_pushing = FALSE;
9606 player->is_snapping = FALSE;
9608 if (player->MovPos == 0)
9610 player->is_moving = FALSE;
9611 player->is_digging = FALSE;
9612 player->is_collecting = FALSE;
9618 if (player->is_snapping)
9621 player->MovDir = snap_direction;
9623 if (player->MovPos == 0)
9625 player->is_moving = FALSE;
9626 player->is_digging = FALSE;
9627 player->is_collecting = FALSE;
9630 player->is_dropping = FALSE;
9632 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9635 player->is_snapping = TRUE;
9637 if (player->MovPos == 0)
9639 player->is_moving = FALSE;
9640 player->is_digging = FALSE;
9641 player->is_collecting = FALSE;
9644 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
9645 DrawLevelField(player->last_jx, player->last_jy);
9647 DrawLevelField(x, y);
9652 boolean DropElement(struct PlayerInfo *player)
9654 int old_element, new_element;
9655 int dropx = player->jx, dropy = player->jy;
9656 int drop_direction = player->MovDir;
9657 int drop_side = drop_direction;
9658 int drop_element = (player->inventory_size > 0 ?
9659 player->inventory_element[player->inventory_size - 1] :
9660 player->inventory_infinite_element != EL_UNDEFINED ?
9661 player->inventory_infinite_element :
9662 player->dynabombs_left > 0 ?
9663 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
9666 /* do not drop an element on top of another element; when holding drop key
9667 pressed without moving, dropped element must move away before the next
9668 element can be dropped (this is especially important if the next element
9669 is dynamite, which can be placed on background for historical reasons) */
9670 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
9673 if (IS_THROWABLE(drop_element))
9675 dropx += GET_DX_FROM_DIR(drop_direction);
9676 dropy += GET_DY_FROM_DIR(drop_direction);
9678 if (!IN_LEV_FIELD(dropx, dropy))
9682 old_element = Feld[dropx][dropy]; /* old element at dropping position */
9683 new_element = drop_element; /* default: no change when dropping */
9685 /* check if player is active, not moving and ready to drop */
9686 if (!player->active || player->MovPos || player->drop_delay > 0)
9689 /* check if player has anything that can be dropped */
9690 if (new_element == EL_UNDEFINED)
9693 /* check if anything can be dropped at the current position */
9694 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9697 /* collected custom elements can only be dropped on empty fields */
9698 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
9701 if (old_element != EL_EMPTY)
9702 Back[dropx][dropy] = old_element; /* store old element on this field */
9704 ResetGfxAnimation(dropx, dropy);
9705 ResetRandomAnimationValue(dropx, dropy);
9707 if (player->inventory_size > 0 ||
9708 player->inventory_infinite_element != EL_UNDEFINED)
9710 if (player->inventory_size > 0)
9712 player->inventory_size--;
9714 DrawGameValue_Dynamite(local_player->inventory_size);
9716 if (new_element == EL_DYNAMITE)
9717 new_element = EL_DYNAMITE_ACTIVE;
9718 else if (new_element == EL_SP_DISK_RED)
9719 new_element = EL_SP_DISK_RED_ACTIVE;
9722 Feld[dropx][dropy] = new_element;
9724 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
9725 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
9726 el2img(Feld[dropx][dropy]), 0);
9728 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
9730 /* needed if previous element just changed to "empty" in the last frame */
9731 Changed[dropx][dropy] = FALSE; /* allow another change */
9733 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
9734 player->index_bit, drop_side);
9735 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
9737 player->index_bit, drop_side);
9739 TestIfElementTouchesCustomElement(dropx, dropy);
9741 else /* player is dropping a dyna bomb */
9743 player->dynabombs_left--;
9745 Feld[dropx][dropy] = new_element;
9747 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
9748 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
9749 el2img(Feld[dropx][dropy]), 0);
9751 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
9754 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
9755 InitField_WithBug1(dropx, dropy, FALSE);
9757 new_element = Feld[dropx][dropy]; /* element might have changed */
9759 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9760 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9762 int move_direction, nextx, nexty;
9764 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
9765 MovDir[dropx][dropy] = drop_direction;
9767 move_direction = MovDir[dropx][dropy];
9768 nextx = dropx + GET_DX_FROM_DIR(move_direction);
9769 nexty = dropy + GET_DY_FROM_DIR(move_direction);
9771 Changed[dropx][dropy] = FALSE; /* allow another change */
9772 CheckCollision[dropx][dropy] = 2;
9775 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
9776 player->is_dropping = TRUE;
9778 player->drop_x = dropx;
9779 player->drop_y = dropy;
9784 /* ------------------------------------------------------------------------- */
9785 /* game sound playing functions */
9786 /* ------------------------------------------------------------------------- */
9788 static int *loop_sound_frame = NULL;
9789 static int *loop_sound_volume = NULL;
9791 void InitPlayLevelSound()
9793 int num_sounds = getSoundListSize();
9795 checked_free(loop_sound_frame);
9796 checked_free(loop_sound_volume);
9798 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9799 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9802 static void PlayLevelSound(int x, int y, int nr)
9804 int sx = SCREENX(x), sy = SCREENY(y);
9805 int volume, stereo_position;
9806 int max_distance = 8;
9807 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9809 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9810 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9813 if (!IN_LEV_FIELD(x, y) ||
9814 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9815 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9818 volume = SOUND_MAX_VOLUME;
9820 if (!IN_SCR_FIELD(sx, sy))
9822 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9823 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9825 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9828 stereo_position = (SOUND_MAX_LEFT +
9829 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9830 (SCR_FIELDX + 2 * max_distance));
9832 if (IS_LOOP_SOUND(nr))
9834 /* This assures that quieter loop sounds do not overwrite louder ones,
9835 while restarting sound volume comparison with each new game frame. */
9837 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9840 loop_sound_volume[nr] = volume;
9841 loop_sound_frame[nr] = FrameCounter;
9844 PlaySoundExt(nr, volume, stereo_position, type);
9847 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9849 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9850 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9851 y < LEVELY(BY1) ? LEVELY(BY1) :
9852 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9856 static void PlayLevelSoundAction(int x, int y, int action)
9858 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9861 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9863 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
9865 if (sound_effect != SND_UNDEFINED)
9866 PlayLevelSound(x, y, sound_effect);
9869 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9872 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
9874 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9875 PlayLevelSound(x, y, sound_effect);
9878 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9880 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
9882 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9883 PlayLevelSound(x, y, sound_effect);
9886 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9888 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
9890 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9891 StopSound(sound_effect);
9894 static void PlayLevelMusic()
9896 if (levelset.music[level_nr] != MUS_UNDEFINED)
9897 PlayMusic(levelset.music[level_nr]); /* from config file */
9899 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9902 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
9904 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
9909 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
9913 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9917 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
9921 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
9925 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
9929 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
9933 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
9936 case SAMPLE_android_clone:
9937 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
9940 case SAMPLE_android_move:
9941 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
9945 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
9949 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
9953 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
9956 case SAMPLE_eater_eat:
9957 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9961 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
9964 case SAMPLE_collect:
9965 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9968 case SAMPLE_diamond:
9969 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
9973 /* !!! CHECK THIS !!! */
9975 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
9977 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
9981 case SAMPLE_wonderfall:
9982 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
9986 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
9990 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9994 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9998 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
10002 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10006 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
10009 case SAMPLE_wonder:
10010 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10014 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10017 case SAMPLE_exit_open:
10018 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
10021 case SAMPLE_exit_leave:
10022 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10025 case SAMPLE_dynamite:
10026 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10030 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10034 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10038 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10042 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
10046 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
10050 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10054 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
10059 void RaiseScore(int value)
10061 local_player->score += value;
10063 DrawGameValue_Score(local_player->score);
10066 void RaiseScoreElement(int element)
10071 case EL_BD_DIAMOND:
10072 case EL_EMERALD_YELLOW:
10073 case EL_EMERALD_RED:
10074 case EL_EMERALD_PURPLE:
10075 case EL_SP_INFOTRON:
10076 RaiseScore(level.score[SC_EMERALD]);
10079 RaiseScore(level.score[SC_DIAMOND]);
10082 RaiseScore(level.score[SC_CRYSTAL]);
10085 RaiseScore(level.score[SC_PEARL]);
10088 case EL_BD_BUTTERFLY:
10089 case EL_SP_ELECTRON:
10090 RaiseScore(level.score[SC_BUG]);
10093 case EL_BD_FIREFLY:
10094 case EL_SP_SNIKSNAK:
10095 RaiseScore(level.score[SC_SPACESHIP]);
10098 case EL_DARK_YAMYAM:
10099 RaiseScore(level.score[SC_YAMYAM]);
10102 RaiseScore(level.score[SC_ROBOT]);
10105 RaiseScore(level.score[SC_PACMAN]);
10108 RaiseScore(level.score[SC_NUT]);
10111 case EL_SP_DISK_RED:
10112 case EL_DYNABOMB_INCREASE_NUMBER:
10113 case EL_DYNABOMB_INCREASE_SIZE:
10114 case EL_DYNABOMB_INCREASE_POWER:
10115 RaiseScore(level.score[SC_DYNAMITE]);
10117 case EL_SHIELD_NORMAL:
10118 case EL_SHIELD_DEADLY:
10119 RaiseScore(level.score[SC_SHIELD]);
10121 case EL_EXTRA_TIME:
10122 RaiseScore(level.score[SC_TIME_BONUS]);
10136 RaiseScore(level.score[SC_KEY]);
10139 RaiseScore(element_info[element].collect_score);
10144 void RequestQuitGame(boolean ask_if_really_quit)
10146 if (AllPlayersGone ||
10147 !ask_if_really_quit ||
10148 level_editor_test_game ||
10149 Request("Do you really want to quit the game ?",
10150 REQ_ASK | REQ_STAY_CLOSED))
10152 #if defined(NETWORK_AVALIABLE)
10153 if (options.network)
10154 SendToServer_StopPlaying();
10158 game_status = GAME_MODE_MAIN;
10164 if (tape.playing && tape.deactivate_display)
10165 TapeDeactivateDisplayOff(TRUE);
10167 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10169 if (tape.playing && tape.deactivate_display)
10170 TapeDeactivateDisplayOn();
10175 /* ---------- new game button stuff ---------------------------------------- */
10177 /* graphic position values for game buttons */
10178 #define GAME_BUTTON_XSIZE 30
10179 #define GAME_BUTTON_YSIZE 30
10180 #define GAME_BUTTON_XPOS 5
10181 #define GAME_BUTTON_YPOS 215
10182 #define SOUND_BUTTON_XPOS 5
10183 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10185 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10186 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10187 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10188 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10189 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10190 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10197 } gamebutton_info[NUM_GAME_BUTTONS] =
10200 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10205 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10206 GAME_CTRL_ID_PAUSE,
10210 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10215 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10216 SOUND_CTRL_ID_MUSIC,
10217 "background music on/off"
10220 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10221 SOUND_CTRL_ID_LOOPS,
10222 "sound loops on/off"
10225 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10226 SOUND_CTRL_ID_SIMPLE,
10227 "normal sounds on/off"
10231 void CreateGameButtons()
10235 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10237 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10238 struct GadgetInfo *gi;
10241 unsigned long event_mask;
10242 int gd_xoffset, gd_yoffset;
10243 int gd_x1, gd_x2, gd_y1, gd_y2;
10246 gd_xoffset = gamebutton_info[i].x;
10247 gd_yoffset = gamebutton_info[i].y;
10248 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10249 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10251 if (id == GAME_CTRL_ID_STOP ||
10252 id == GAME_CTRL_ID_PAUSE ||
10253 id == GAME_CTRL_ID_PLAY)
10255 button_type = GD_TYPE_NORMAL_BUTTON;
10257 event_mask = GD_EVENT_RELEASED;
10258 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10259 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10263 button_type = GD_TYPE_CHECK_BUTTON;
10265 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10266 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10267 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10268 event_mask = GD_EVENT_PRESSED;
10269 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10270 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10273 gi = CreateGadget(GDI_CUSTOM_ID, id,
10274 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10275 GDI_X, DX + gd_xoffset,
10276 GDI_Y, DY + gd_yoffset,
10277 GDI_WIDTH, GAME_BUTTON_XSIZE,
10278 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10279 GDI_TYPE, button_type,
10280 GDI_STATE, GD_BUTTON_UNPRESSED,
10281 GDI_CHECKED, checked,
10282 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10283 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10284 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10285 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10286 GDI_EVENT_MASK, event_mask,
10287 GDI_CALLBACK_ACTION, HandleGameButtons,
10291 Error(ERR_EXIT, "cannot create gadget");
10293 game_gadget[id] = gi;
10297 void FreeGameButtons()
10301 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10302 FreeGadget(game_gadget[i]);
10305 static void MapGameButtons()
10309 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10310 MapGadget(game_gadget[i]);
10313 void UnmapGameButtons()
10317 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10318 UnmapGadget(game_gadget[i]);
10321 static void HandleGameButtons(struct GadgetInfo *gi)
10323 int id = gi->custom_id;
10325 if (game_status != GAME_MODE_PLAYING)
10330 case GAME_CTRL_ID_STOP:
10331 RequestQuitGame(TRUE);
10334 case GAME_CTRL_ID_PAUSE:
10335 if (options.network)
10337 #if defined(NETWORK_AVALIABLE)
10339 SendToServer_ContinuePlaying();
10341 SendToServer_PausePlaying();
10345 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10348 case GAME_CTRL_ID_PLAY:
10351 #if defined(NETWORK_AVALIABLE)
10352 if (options.network)
10353 SendToServer_ContinuePlaying();
10357 tape.pausing = FALSE;
10358 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10363 case SOUND_CTRL_ID_MUSIC:
10364 if (setup.sound_music)
10366 setup.sound_music = FALSE;
10369 else if (audio.music_available)
10371 setup.sound = setup.sound_music = TRUE;
10373 SetAudioMode(setup.sound);
10379 case SOUND_CTRL_ID_LOOPS:
10380 if (setup.sound_loops)
10381 setup.sound_loops = FALSE;
10382 else if (audio.loops_available)
10384 setup.sound = setup.sound_loops = TRUE;
10385 SetAudioMode(setup.sound);
10389 case SOUND_CTRL_ID_SIMPLE:
10390 if (setup.sound_simple)
10391 setup.sound_simple = FALSE;
10392 else if (audio.sound_available)
10394 setup.sound = setup.sound_simple = TRUE;
10395 SetAudioMode(setup.sound);