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 ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_COLLECT_COUNT (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
42 /* for MovePlayer() */
43 #define MF_NO_ACTION 0
47 /* for ScrollPlayer() */
49 #define SCROLL_GO_ON 1
52 #define EX_PHASE_START 0
53 #define EX_TYPE_NONE 0
54 #define EX_TYPE_NORMAL (1 << 0)
55 #define EX_TYPE_CENTER (1 << 1)
56 #define EX_TYPE_BORDER (1 << 2)
57 #define EX_TYPE_CROSS (1 << 3)
58 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
60 /* special positions in the game control window (relative to control window) */
63 #define XX_EMERALDS 29
64 #define YY_EMERALDS 54
65 #define XX_DYNAMITE 29
66 #define YY_DYNAMITE 89
75 /* special positions in the game control window (relative to main window) */
76 #define DX_LEVEL (DX + XX_LEVEL)
77 #define DY_LEVEL (DY + YY_LEVEL)
78 #define DX_EMERALDS (DX + XX_EMERALDS)
79 #define DY_EMERALDS (DY + YY_EMERALDS)
80 #define DX_DYNAMITE (DX + XX_DYNAMITE)
81 #define DY_DYNAMITE (DY + YY_DYNAMITE)
82 #define DX_KEYS (DX + XX_KEYS)
83 #define DY_KEYS (DY + YY_KEYS)
84 #define DX_SCORE (DX + XX_SCORE)
85 #define DY_SCORE (DY + YY_SCORE)
86 #define DX_TIME1 (DX + XX_TIME1)
87 #define DX_TIME2 (DX + XX_TIME2)
88 #define DY_TIME (DY + YY_TIME)
90 /* values for initial player move delay (initial delay counter value) */
91 #define INITIAL_MOVE_DELAY_OFF -1
92 #define INITIAL_MOVE_DELAY_ON 0
94 /* values for player movement speed (which is in fact a delay value) */
95 #define MOVE_DELAY_MIN_SPEED 32
96 #define MOVE_DELAY_NORMAL_SPEED 8
97 #define MOVE_DELAY_HIGH_SPEED 4
98 #define MOVE_DELAY_MAX_SPEED 1
101 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
102 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
104 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
105 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
107 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
108 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
110 /* values for other actions */
111 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
112 #define MOVE_STEPSIZE_MIN (1)
113 #define MOVE_STEPSIZE_MAX (TILEX)
115 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
116 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
118 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
120 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
121 RND(element_info[e].push_delay_random))
122 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
123 RND(element_info[e].drop_delay_random))
124 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
125 RND(element_info[e].move_delay_random))
126 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
127 (element_info[e].move_delay_random))
128 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
129 RND((c)->delay_random * (c)->delay_frames))
131 #define GET_TARGET_ELEMENT(e, ch) \
132 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
133 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
135 #define GET_VALID_PLAYER_ELEMENT(e) \
136 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
138 #define CAN_GROW_INTO(e) \
139 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
141 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
142 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
145 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
146 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
147 (CAN_MOVE_INTO_ACID(e) && \
148 Feld[x][y] == EL_ACID) || \
151 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
152 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
153 (CAN_MOVE_INTO_ACID(e) && \
154 Feld[x][y] == EL_ACID) || \
157 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
158 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
160 (CAN_MOVE_INTO_ACID(e) && \
161 Feld[x][y] == EL_ACID) || \
162 (DONT_COLLIDE_WITH(e) && \
164 !PLAYER_ENEMY_PROTECTED(x, y))))
166 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
167 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
169 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
170 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
172 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
173 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
175 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
176 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
178 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
179 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
181 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
182 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
184 #define PIG_CAN_ENTER_FIELD(e, x, y) \
185 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
187 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
188 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
189 IS_FOOD_PENGUIN(Feld[x][y])))
190 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
191 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
193 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
194 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
196 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
197 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
199 #define GROUP_NR(e) ((e) - EL_GROUP_START)
200 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
201 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
202 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
204 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
205 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
207 #define CE_ENTER_FIELD_COND(e, x, y) \
208 (!IS_PLAYER(x, y) && \
209 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
211 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
212 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
214 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
215 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
217 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
218 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
219 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
220 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
222 /* game button identifiers */
223 #define GAME_CTRL_ID_STOP 0
224 #define GAME_CTRL_ID_PAUSE 1
225 #define GAME_CTRL_ID_PLAY 2
226 #define SOUND_CTRL_ID_MUSIC 3
227 #define SOUND_CTRL_ID_LOOPS 4
228 #define SOUND_CTRL_ID_SIMPLE 5
230 #define NUM_GAME_BUTTONS 6
233 /* forward declaration for internal use */
235 static void AdvanceFrameAndPlayerCounters(int);
237 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
238 static boolean MovePlayer(struct PlayerInfo *, int, int);
239 static void ScrollPlayer(struct PlayerInfo *, int);
240 static void ScrollScreen(struct PlayerInfo *, int);
242 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
244 static void InitBeltMovement(void);
245 static void CloseAllOpenTimegates(void);
246 static void CheckGravityMovement(struct PlayerInfo *);
247 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
248 static void KillPlayerUnlessEnemyProtected(int, int);
249 static void KillPlayerUnlessExplosionProtected(int, int);
251 static void TestIfPlayerTouchesCustomElement(int, int);
252 static void TestIfElementTouchesCustomElement(int, int);
253 static void TestIfElementHitsCustomElement(int, int, int);
255 static void TestIfElementSmashesCustomElement(int, int, int);
258 static void ChangeElement(int, int, int);
260 static boolean CheckTriggeredElementChangeExt(int, int, int,int,int);
261 #define CheckTriggeredElementChange(e, ev) \
262 CheckTriggeredElementChangeExt(e, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
263 #define CheckTriggeredElementChangeByPlayer(e, ev, p, s) \
264 CheckTriggeredElementChangeExt(e, ev, p, s, -1)
265 #define CheckTriggeredElementChangeBySide(e, ev, s) \
266 CheckTriggeredElementChangeExt(e, ev, CH_PLAYER_ANY, s, -1)
267 #define CheckTriggeredElementChangeByPage(e, ev, p) \
268 CheckTriggeredElementChangeExt(e, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
270 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
271 #define CheckElementChange(x, y, e, te, ev) \
272 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
273 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
274 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
275 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
276 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
278 static void PlayLevelSound(int, int, int);
279 static void PlayLevelSoundNearest(int, int, int);
280 static void PlayLevelSoundAction(int, int, int);
281 static void PlayLevelSoundElementAction(int, int, int, int);
282 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
283 static void PlayLevelSoundActionIfLoop(int, int, int);
284 static void StopLevelSoundActionIfLoop(int, int, int);
285 static void PlayLevelMusic();
287 static void MapGameButtons();
288 static void HandleGameButtons(struct GadgetInfo *);
290 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
293 /* ------------------------------------------------------------------------- */
294 /* definition of elements that automatically change to other elements after */
295 /* a specified time, eventually calling a function when changing */
296 /* ------------------------------------------------------------------------- */
298 /* forward declaration for changer functions */
299 static void InitBuggyBase(int x, int y);
300 static void WarnBuggyBase(int x, int y);
302 static void InitTrap(int x, int y);
303 static void ActivateTrap(int x, int y);
304 static void ChangeActiveTrap(int x, int y);
306 static void InitRobotWheel(int x, int y);
307 static void RunRobotWheel(int x, int y);
308 static void StopRobotWheel(int x, int y);
310 static void InitTimegateWheel(int x, int y);
311 static void RunTimegateWheel(int x, int y);
313 struct ChangingElementInfo
318 void (*pre_change_function)(int x, int y);
319 void (*change_function)(int x, int y);
320 void (*post_change_function)(int x, int y);
323 static struct ChangingElementInfo change_delay_list[] =
374 EL_SWITCHGATE_OPENING,
382 EL_SWITCHGATE_CLOSING,
383 EL_SWITCHGATE_CLOSED,
415 EL_ACID_SPLASH_RIGHT,
424 EL_SP_BUGGY_BASE_ACTIVATING,
431 EL_SP_BUGGY_BASE_ACTIVATING,
432 EL_SP_BUGGY_BASE_ACTIVE,
439 EL_SP_BUGGY_BASE_ACTIVE,
463 EL_ROBOT_WHEEL_ACTIVE,
471 EL_TIMEGATE_SWITCH_ACTIVE,
492 int push_delay_fixed, push_delay_random;
497 { EL_BALLOON, 0, 0 },
499 { EL_SOKOBAN_OBJECT, 2, 0 },
500 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
501 { EL_SATELLITE, 2, 0 },
502 { EL_SP_DISK_YELLOW, 2, 0 },
504 { EL_UNDEFINED, 0, 0 },
512 move_stepsize_list[] =
514 { EL_AMOEBA_DROP, 2 },
515 { EL_AMOEBA_DROPPING, 2 },
516 { EL_QUICKSAND_FILLING, 1 },
517 { EL_QUICKSAND_EMPTYING, 1 },
518 { EL_MAGIC_WALL_FILLING, 2 },
519 { EL_BD_MAGIC_WALL_FILLING, 2 },
520 { EL_MAGIC_WALL_EMPTYING, 2 },
521 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
531 collect_count_list[] =
534 { EL_BD_DIAMOND, 1 },
535 { EL_EMERALD_YELLOW, 1 },
536 { EL_EMERALD_RED, 1 },
537 { EL_EMERALD_PURPLE, 1 },
539 { EL_SP_INFOTRON, 1 },
551 access_direction_list[] =
553 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
554 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
555 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
556 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
557 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
558 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
559 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
560 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
561 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
562 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
563 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
565 { EL_SP_PORT_LEFT, MV_RIGHT },
566 { EL_SP_PORT_RIGHT, MV_LEFT },
567 { EL_SP_PORT_UP, MV_DOWN },
568 { EL_SP_PORT_DOWN, MV_UP },
569 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
570 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
571 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
572 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
573 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
574 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
575 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
576 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
577 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
578 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
579 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
580 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
581 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
582 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
583 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
585 { EL_UNDEFINED, MV_NONE }
588 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
590 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
591 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
592 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
593 IS_JUST_CHANGING(x, y))
595 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
598 void GetPlayerConfig()
600 if (!audio.sound_available)
601 setup.sound_simple = FALSE;
603 if (!audio.loops_available)
604 setup.sound_loops = FALSE;
606 if (!audio.music_available)
607 setup.sound_music = FALSE;
609 if (!video.fullscreen_available)
610 setup.fullscreen = FALSE;
612 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
614 SetAudioMode(setup.sound);
618 static int getBeltNrFromBeltElement(int element)
620 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
621 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
622 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
625 static int getBeltNrFromBeltActiveElement(int element)
627 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
628 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
629 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
632 static int getBeltNrFromBeltSwitchElement(int element)
634 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
635 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
636 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
639 static int getBeltDirNrFromBeltSwitchElement(int element)
641 static int belt_base_element[4] =
643 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
644 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
645 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
646 EL_CONVEYOR_BELT_4_SWITCH_LEFT
649 int belt_nr = getBeltNrFromBeltSwitchElement(element);
650 int belt_dir_nr = element - belt_base_element[belt_nr];
652 return (belt_dir_nr % 3);
655 static int getBeltDirFromBeltSwitchElement(int element)
657 static int belt_move_dir[3] =
664 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
666 return belt_move_dir[belt_dir_nr];
669 static void InitPlayerField(int x, int y, int element, boolean init_game)
671 if (element == EL_SP_MURPHY)
675 if (stored_player[0].present)
677 Feld[x][y] = EL_SP_MURPHY_CLONE;
683 stored_player[0].use_murphy_graphic = TRUE;
686 Feld[x][y] = EL_PLAYER_1;
692 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
693 int jx = player->jx, jy = player->jy;
695 player->present = TRUE;
697 player->block_last_field = (element == EL_SP_MURPHY ?
698 level.sp_block_last_field :
699 level.block_last_field);
701 /* ---------- initialize player's last field block delay --------------- */
703 /* always start with reliable default value (no adjustment needed) */
704 player->block_delay_adjustment = 0;
706 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
707 if (player->block_last_field && element == EL_SP_MURPHY)
708 player->block_delay_adjustment = 1;
710 /* special case 2: in game engines before 3.1.1, blocking was different */
711 if (game.use_block_last_field_bug)
712 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
714 if (!options.network || player->connected)
716 player->active = TRUE;
718 /* remove potentially duplicate players */
719 if (StorePlayer[jx][jy] == Feld[x][y])
720 StorePlayer[jx][jy] = 0;
722 StorePlayer[x][y] = Feld[x][y];
726 printf("Player %d activated.\n", player->element_nr);
727 printf("[Local player is %d and currently %s.]\n",
728 local_player->element_nr,
729 local_player->active ? "active" : "not active");
733 Feld[x][y] = EL_EMPTY;
735 player->jx = player->last_jx = x;
736 player->jy = player->last_jy = y;
740 static void InitField(int x, int y, boolean init_game)
742 int element = Feld[x][y];
751 InitPlayerField(x, y, element, init_game);
754 case EL_SOKOBAN_FIELD_PLAYER:
755 element = Feld[x][y] = EL_PLAYER_1;
756 InitField(x, y, init_game);
758 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
759 InitField(x, y, init_game);
762 case EL_SOKOBAN_FIELD_EMPTY:
763 local_player->sokobanfields_still_needed++;
767 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
768 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
769 else if (x > 0 && Feld[x-1][y] == EL_ACID)
770 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
771 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
772 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
773 else if (y > 0 && Feld[x][y-1] == EL_ACID)
774 Feld[x][y] = EL_ACID_POOL_BOTTOM;
775 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
776 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
784 case EL_SPACESHIP_RIGHT:
785 case EL_SPACESHIP_UP:
786 case EL_SPACESHIP_LEFT:
787 case EL_SPACESHIP_DOWN:
789 case EL_BD_BUTTERFLY_RIGHT:
790 case EL_BD_BUTTERFLY_UP:
791 case EL_BD_BUTTERFLY_LEFT:
792 case EL_BD_BUTTERFLY_DOWN:
793 case EL_BD_BUTTERFLY:
794 case EL_BD_FIREFLY_RIGHT:
795 case EL_BD_FIREFLY_UP:
796 case EL_BD_FIREFLY_LEFT:
797 case EL_BD_FIREFLY_DOWN:
799 case EL_PACMAN_RIGHT:
823 if (y == lev_fieldy - 1)
825 Feld[x][y] = EL_AMOEBA_GROWING;
826 Store[x][y] = EL_AMOEBA_WET;
830 case EL_DYNAMITE_ACTIVE:
831 case EL_SP_DISK_RED_ACTIVE:
832 case EL_DYNABOMB_PLAYER_1_ACTIVE:
833 case EL_DYNABOMB_PLAYER_2_ACTIVE:
834 case EL_DYNABOMB_PLAYER_3_ACTIVE:
835 case EL_DYNABOMB_PLAYER_4_ACTIVE:
840 local_player->lights_still_needed++;
844 local_player->friends_still_needed++;
849 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
852 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
853 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
854 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
855 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
856 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
857 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
858 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
859 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
860 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
861 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
862 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
863 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
866 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
867 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
868 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
870 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
872 game.belt_dir[belt_nr] = belt_dir;
873 game.belt_dir_nr[belt_nr] = belt_dir_nr;
875 else /* more than one switch -- set it like the first switch */
877 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
882 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
884 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
887 case EL_LIGHT_SWITCH_ACTIVE:
889 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
893 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
895 else if (IS_GROUP_ELEMENT(element))
897 struct ElementGroupInfo *group = element_info[element].group;
898 int last_anim_random_frame = gfx.anim_random_frame;
901 if (group->choice_mode == ANIM_RANDOM)
902 gfx.anim_random_frame = RND(group->num_elements_resolved);
904 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
905 group->choice_mode, 0,
908 if (group->choice_mode == ANIM_RANDOM)
909 gfx.anim_random_frame = last_anim_random_frame;
913 Feld[x][y] = group->element_resolved[element_pos];
915 InitField(x, y, init_game);
920 #if USE_NEW_COLLECT_COUNT
921 Count[x][y] = element_info[Feld[x][y]].collect_count_initial;
925 static inline void InitField_WithBug1(int x, int y, boolean init_game)
927 InitField(x, y, init_game);
929 /* not needed to call InitMovDir() -- already done by InitField()! */
930 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
931 CAN_MOVE(Feld[x][y]))
935 static inline void InitField_WithBug2(int x, int y, boolean init_game)
937 int old_element = Feld[x][y];
939 InitField(x, y, init_game);
941 /* not needed to call InitMovDir() -- already done by InitField()! */
942 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
943 CAN_MOVE(old_element) &&
944 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
947 /* this case is in fact a combination of not less than three bugs:
948 first, it calls InitMovDir() for elements that can move, although this is
949 already done by InitField(); then, it checks the element that was at this
950 field _before_ the call to InitField() (which can change it); lastly, it
951 was not called for "mole with direction" elements, which were treated as
952 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
956 inline void DrawGameValue_Emeralds(int value)
958 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
961 inline void DrawGameValue_Dynamite(int value)
963 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
966 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
970 /* currently only 4 of 8 possible keys are displayed */
971 for (i = 0; i < STD_NUM_KEYS; i++)
974 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
975 el2edimg(EL_KEY_1 + i));
977 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
978 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
979 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
983 inline void DrawGameValue_Score(int value)
985 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
988 inline void DrawGameValue_Time(int value)
991 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
993 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
996 inline void DrawGameValue_Level(int value)
999 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1002 /* misuse area for displaying emeralds to draw bigger level number */
1003 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1004 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1006 /* now copy it to the area for displaying level number */
1007 BlitBitmap(drawto, drawto,
1008 DX_EMERALDS, DY_EMERALDS + 1,
1009 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1010 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1011 DX_LEVEL - 1, DY_LEVEL + 1);
1013 /* restore the area for displaying emeralds */
1014 DrawGameValue_Emeralds(local_player->gems_still_needed);
1016 /* yes, this is all really ugly :-) */
1020 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1023 int key[MAX_NUM_KEYS];
1026 for (i = 0; i < MAX_NUM_KEYS; i++)
1027 key[i] = key_bits & (1 << i);
1029 DrawGameValue_Level(level_nr);
1031 DrawGameValue_Emeralds(emeralds);
1032 DrawGameValue_Dynamite(dynamite);
1033 DrawGameValue_Score(score);
1034 DrawGameValue_Time(time);
1036 DrawGameValue_Keys(key);
1039 void DrawGameDoorValues()
1043 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1045 DrawGameDoorValues_EM();
1050 DrawGameValue_Level(level_nr);
1052 DrawGameValue_Emeralds(local_player->gems_still_needed);
1053 DrawGameValue_Dynamite(local_player->inventory_size);
1054 DrawGameValue_Score(local_player->score);
1055 DrawGameValue_Time(TimeLeft);
1057 for (i = 0; i < MAX_PLAYERS; i++)
1058 DrawGameValue_Keys(stored_player[i].key);
1061 static void resolve_group_element(int group_element, int recursion_depth)
1063 static int group_nr;
1064 static struct ElementGroupInfo *group;
1065 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1068 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1070 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1071 group_element - EL_GROUP_START + 1);
1073 /* replace element which caused too deep recursion by question mark */
1074 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1079 if (recursion_depth == 0) /* initialization */
1081 group = element_info[group_element].group;
1082 group_nr = group_element - EL_GROUP_START;
1084 group->num_elements_resolved = 0;
1085 group->choice_pos = 0;
1088 for (i = 0; i < actual_group->num_elements; i++)
1090 int element = actual_group->element[i];
1092 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1095 if (IS_GROUP_ELEMENT(element))
1096 resolve_group_element(element, recursion_depth + 1);
1099 group->element_resolved[group->num_elements_resolved++] = element;
1100 element_info[element].in_group[group_nr] = TRUE;
1107 =============================================================================
1109 -----------------------------------------------------------------------------
1110 initialize game engine due to level / tape version number
1111 =============================================================================
1114 static void InitGameEngine()
1118 /* set game engine from tape file when re-playing, else from level file */
1119 game.engine_version = (tape.playing ? tape.engine_version :
1120 level.game_version);
1122 /* ---------------------------------------------------------------------- */
1123 /* set flags for bugs and changes according to active game engine version */
1124 /* ---------------------------------------------------------------------- */
1127 Summary of bugfix/change:
1128 Fixed handling for custom elements that change when pushed by the player.
1130 Fixed/changed in version:
1134 Before 3.1.0, custom elements that "change when pushing" changed directly
1135 after the player started pushing them (until then handled in "DigField()").
1136 Since 3.1.0, these custom elements are not changed until the "pushing"
1137 move of the element is finished (now handled in "ContinueMoving()").
1139 Affected levels/tapes:
1140 The first condition is generally needed for all levels/tapes before version
1141 3.1.0, which might use the old behaviour before it was changed; known tapes
1142 that are affected are some tapes from the level set "Walpurgis Gardens" by
1144 The second condition is an exception from the above case and is needed for
1145 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1146 above (including some development versions of 3.1.0), but before it was
1147 known that this change would break tapes like the above and was fixed in
1148 3.1.1, so that the changed behaviour was active although the engine version
1149 while recording maybe was before 3.1.0. There is at least one tape that is
1150 affected by this exception, which is the tape for the one-level set "Bug
1151 Machine" by Juergen Bonhagen.
1154 game.use_change_when_pushing_bug =
1155 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1157 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1158 tape.game_version < VERSION_IDENT(3,1,1,0)));
1161 Summary of bugfix/change:
1162 Fixed handling for blocking the field the player leaves when moving.
1164 Fixed/changed in version:
1168 Before 3.1.1, when "block last field when moving" was enabled, the field
1169 the player is leaving when moving was blocked for the time of the move,
1170 and was directly unblocked afterwards. This resulted in the last field
1171 being blocked for exactly one less than the number of frames of one player
1172 move. Additionally, even when blocking was disabled, the last field was
1173 blocked for exactly one frame.
1174 Since 3.1.1, due to changes in player movement handling, the last field
1175 is not blocked at all when blocking is disabled. When blocking is enabled,
1176 the last field is blocked for exactly the number of frames of one player
1177 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1178 last field is blocked for exactly one more than the number of frames of
1181 Affected levels/tapes:
1182 (!!! yet to be determined -- probably many !!!)
1185 game.use_block_last_field_bug =
1186 (game.engine_version < VERSION_IDENT(3,1,1,0));
1188 /* ---------------------------------------------------------------------- */
1190 /* dynamically adjust element properties according to game engine version */
1191 InitElementPropertiesEngine(game.engine_version);
1194 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1195 printf(" tape version == %06d [%s] [file: %06d]\n",
1196 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1198 printf(" => game.engine_version == %06d\n", game.engine_version);
1201 /* ---------- recursively resolve group elements ------------------------- */
1203 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1204 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1205 element_info[i].in_group[j] = FALSE;
1207 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1208 resolve_group_element(EL_GROUP_START + i, 0);
1210 /* ---------- initialize player's initial move delay --------------------- */
1212 /* dynamically adjust player properties according to level information */
1213 game.initial_move_delay_value =
1214 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1216 /* dynamically adjust player properties according to game engine version */
1217 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1218 game.initial_move_delay_value : 0);
1220 /* ---------- initialize player's initial push delay --------------------- */
1222 /* dynamically adjust player properties according to game engine version */
1223 game.initial_push_delay_value =
1224 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1226 /* ---------- initialize changing elements ------------------------------- */
1228 /* initialize changing elements information */
1229 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1231 struct ElementInfo *ei = &element_info[i];
1233 /* this pointer might have been changed in the level editor */
1234 ei->change = &ei->change_page[0];
1236 if (!IS_CUSTOM_ELEMENT(i))
1238 ei->change->target_element = EL_EMPTY_SPACE;
1239 ei->change->delay_fixed = 0;
1240 ei->change->delay_random = 0;
1241 ei->change->delay_frames = 1;
1244 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1246 ei->has_change_event[j] = FALSE;
1248 ei->event_page_nr[j] = 0;
1249 ei->event_page[j] = &ei->change_page[0];
1253 /* add changing elements from pre-defined list */
1254 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1256 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1257 struct ElementInfo *ei = &element_info[ch_delay->element];
1259 ei->change->target_element = ch_delay->target_element;
1260 ei->change->delay_fixed = ch_delay->change_delay;
1262 ei->change->pre_change_function = ch_delay->pre_change_function;
1263 ei->change->change_function = ch_delay->change_function;
1264 ei->change->post_change_function = ch_delay->post_change_function;
1266 ei->change->can_change = TRUE;
1267 ei->change->can_change_or_has_action = TRUE;
1269 ei->has_change_event[CE_DELAY] = TRUE;
1271 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1272 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1275 /* ---------- initialize internal run-time variables ------------- */
1277 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1279 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1281 for (j = 0; j < ei->num_change_pages; j++)
1283 ei->change_page[j].can_change_or_has_action =
1284 (ei->change_page[j].can_change |
1285 ei->change_page[j].has_action);
1289 /* add change events from custom element configuration */
1290 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1292 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1294 for (j = 0; j < ei->num_change_pages; j++)
1296 if (!ei->change_page[j].can_change_or_has_action)
1299 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1301 /* only add event page for the first page found with this event */
1302 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1304 ei->has_change_event[k] = TRUE;
1306 ei->event_page_nr[k] = j;
1307 ei->event_page[k] = &ei->change_page[j];
1313 /* ---------- initialize run-time trigger player and element ------------- */
1315 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1317 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1319 for (j = 0; j < ei->num_change_pages; j++)
1321 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1322 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1323 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1327 /* ---------- initialize trigger events ---------------------------------- */
1329 /* initialize trigger events information */
1330 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1331 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1332 trigger_events[i][j] = FALSE;
1334 /* add trigger events from element change event properties */
1335 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1337 struct ElementInfo *ei = &element_info[i];
1339 for (j = 0; j < ei->num_change_pages; j++)
1341 if (!ei->change_page[j].can_change_or_has_action)
1344 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1346 int trigger_element = ei->change_page[j].trigger_element;
1348 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1350 if (ei->change_page[j].has_event[k])
1352 if (IS_GROUP_ELEMENT(trigger_element))
1354 struct ElementGroupInfo *group =
1355 element_info[trigger_element].group;
1357 for (l = 0; l < group->num_elements_resolved; l++)
1358 trigger_events[group->element_resolved[l]][k] = TRUE;
1361 trigger_events[trigger_element][k] = TRUE;
1368 /* ---------- initialize push delay -------------------------------------- */
1370 /* initialize push delay values to default */
1371 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1373 if (!IS_CUSTOM_ELEMENT(i))
1375 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1376 element_info[i].push_delay_random = game.default_push_delay_random;
1380 /* set push delay value for certain elements from pre-defined list */
1381 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1383 int e = push_delay_list[i].element;
1385 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1386 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1389 /* set push delay value for Supaplex elements for newer engine versions */
1390 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1392 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1394 if (IS_SP_ELEMENT(i))
1396 /* set SP push delay to just enough to push under a falling zonk */
1397 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1399 element_info[i].push_delay_fixed = delay;
1400 element_info[i].push_delay_random = 0;
1405 /* ---------- initialize move stepsize ----------------------------------- */
1407 /* initialize move stepsize values to default */
1408 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1409 if (!IS_CUSTOM_ELEMENT(i))
1410 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1412 /* set move stepsize value for certain elements from pre-defined list */
1413 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1415 int e = move_stepsize_list[i].element;
1417 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1420 /* ---------- initialize collect score ----------------------------------- */
1422 /* initialize collect score values for custom elements from initial value */
1423 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1424 if (IS_CUSTOM_ELEMENT(i))
1425 element_info[i].collect_score = element_info[i].collect_score_initial;
1427 /* ---------- initialize collect count ----------------------------------- */
1429 /* initialize collect count values for non-custom elements */
1430 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1431 if (!IS_CUSTOM_ELEMENT(i))
1432 element_info[i].collect_count_initial = 0;
1434 /* add collect count values for all elements from pre-defined list */
1435 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1436 element_info[collect_count_list[i].element].collect_count_initial =
1437 collect_count_list[i].count;
1439 /* ---------- initialize access direction -------------------------------- */
1441 /* initialize access direction values to default (access from every side) */
1442 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1443 if (!IS_CUSTOM_ELEMENT(i))
1444 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1446 /* set access direction value for certain elements from pre-defined list */
1447 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1448 element_info[access_direction_list[i].element].access_direction =
1449 access_direction_list[i].direction;
1454 =============================================================================
1456 -----------------------------------------------------------------------------
1457 initialize and start new game
1458 =============================================================================
1463 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1464 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1465 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1470 /* don't play tapes over network */
1471 network_playing = (options.network && !tape.playing);
1473 for (i = 0; i < MAX_PLAYERS; i++)
1475 struct PlayerInfo *player = &stored_player[i];
1477 player->index_nr = i;
1478 player->index_bit = (1 << i);
1479 player->element_nr = EL_PLAYER_1 + i;
1481 player->present = FALSE;
1482 player->active = FALSE;
1485 player->effective_action = 0;
1486 player->programmed_action = 0;
1489 player->gems_still_needed = level.gems_needed;
1490 player->sokobanfields_still_needed = 0;
1491 player->lights_still_needed = 0;
1492 player->friends_still_needed = 0;
1494 for (j = 0; j < MAX_NUM_KEYS; j++)
1495 player->key[j] = FALSE;
1497 player->dynabomb_count = 0;
1498 player->dynabomb_size = 1;
1499 player->dynabombs_left = 0;
1500 player->dynabomb_xl = FALSE;
1502 player->MovDir = MV_NONE;
1505 player->GfxDir = MV_NONE;
1506 player->GfxAction = ACTION_DEFAULT;
1508 player->StepFrame = 0;
1510 player->use_murphy_graphic = FALSE;
1512 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1513 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1515 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1517 player->actual_frame_counter = 0;
1519 player->step_counter = 0;
1521 player->last_move_dir = MV_NONE;
1523 player->is_waiting = FALSE;
1524 player->is_moving = FALSE;
1525 player->is_auto_moving = FALSE;
1526 player->is_digging = FALSE;
1527 player->is_snapping = FALSE;
1528 player->is_collecting = FALSE;
1529 player->is_pushing = FALSE;
1530 player->is_switching = FALSE;
1531 player->is_dropping = FALSE;
1533 player->is_bored = FALSE;
1534 player->is_sleeping = FALSE;
1536 player->frame_counter_bored = -1;
1537 player->frame_counter_sleeping = -1;
1539 player->anim_delay_counter = 0;
1540 player->post_delay_counter = 0;
1542 player->action_waiting = ACTION_DEFAULT;
1543 player->last_action_waiting = ACTION_DEFAULT;
1544 player->special_action_bored = ACTION_DEFAULT;
1545 player->special_action_sleeping = ACTION_DEFAULT;
1547 player->num_special_action_bored = 0;
1548 player->num_special_action_sleeping = 0;
1550 /* determine number of special actions for bored and sleeping animation */
1551 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1553 boolean found = FALSE;
1555 for (k = 0; k < NUM_DIRECTIONS; k++)
1556 if (el_act_dir2img(player->element_nr, j, k) !=
1557 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1561 player->num_special_action_bored++;
1565 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1567 boolean found = FALSE;
1569 for (k = 0; k < NUM_DIRECTIONS; k++)
1570 if (el_act_dir2img(player->element_nr, j, k) !=
1571 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1575 player->num_special_action_sleeping++;
1580 player->switch_x = -1;
1581 player->switch_y = -1;
1583 player->drop_x = -1;
1584 player->drop_y = -1;
1586 player->show_envelope = 0;
1588 player->move_delay = game.initial_move_delay;
1589 player->move_delay_value = game.initial_move_delay_value;
1591 player->move_delay_value_next = -1;
1593 player->move_delay_reset_counter = 0;
1595 player->push_delay = -1; /* initialized when pushing starts */
1596 player->push_delay_value = game.initial_push_delay_value;
1598 player->drop_delay = 0;
1600 player->last_jx = player->last_jy = 0;
1601 player->jx = player->jy = 0;
1603 player->shield_normal_time_left = 0;
1604 player->shield_deadly_time_left = 0;
1606 player->inventory_infinite_element = EL_UNDEFINED;
1607 player->inventory_size = 0;
1609 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1610 SnapField(player, 0, 0);
1612 player->LevelSolved = FALSE;
1613 player->GameOver = FALSE;
1616 network_player_action_received = FALSE;
1618 #if defined(NETWORK_AVALIABLE)
1619 /* initial null action */
1620 if (network_playing)
1621 SendToServer_MovePlayer(MV_NONE);
1630 TimeLeft = level.time;
1633 ScreenMovDir = MV_NONE;
1637 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1639 AllPlayersGone = FALSE;
1641 game.yamyam_content_nr = 0;
1642 game.magic_wall_active = FALSE;
1643 game.magic_wall_time_left = 0;
1644 game.light_time_left = 0;
1645 game.timegate_time_left = 0;
1646 game.switchgate_pos = 0;
1647 game.wind_direction = level.wind_direction_initial;
1648 game.gravity = level.initial_gravity;
1649 game.explosions_delayed = TRUE;
1651 game.envelope_active = FALSE;
1653 for (i = 0; i < NUM_BELTS; i++)
1655 game.belt_dir[i] = MV_NONE;
1656 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1659 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1660 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1662 for (x = 0; x < lev_fieldx; x++)
1664 for (y = 0; y < lev_fieldy; y++)
1666 Feld[x][y] = level.field[x][y];
1667 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1668 ChangeDelay[x][y] = 0;
1669 ChangePage[x][y] = -1;
1670 #if USE_NEW_COLLECT_COUNT
1671 Count[x][y] = 0; /* initialized in InitField() */
1673 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1675 WasJustMoving[x][y] = 0;
1676 WasJustFalling[x][y] = 0;
1677 CheckCollision[x][y] = 0;
1679 Pushed[x][y] = FALSE;
1681 Changed[x][y] = FALSE;
1682 ChangeEvent[x][y] = -1;
1684 ExplodePhase[x][y] = 0;
1685 ExplodeDelay[x][y] = 0;
1686 ExplodeField[x][y] = EX_TYPE_NONE;
1688 RunnerVisit[x][y] = 0;
1689 PlayerVisit[x][y] = 0;
1692 GfxRandom[x][y] = INIT_GFX_RANDOM();
1693 GfxElement[x][y] = EL_UNDEFINED;
1694 GfxAction[x][y] = ACTION_DEFAULT;
1695 GfxDir[x][y] = MV_NONE;
1699 for (y = 0; y < lev_fieldy; y++)
1701 for (x = 0; x < lev_fieldx; x++)
1703 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1705 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1707 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1710 InitField(x, y, TRUE);
1716 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1717 emulate_sb ? EMU_SOKOBAN :
1718 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1720 #if USE_NEW_ALL_SLIPPERY
1721 /* initialize type of slippery elements */
1722 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1724 if (!IS_CUSTOM_ELEMENT(i))
1726 /* default: elements slip down either to the left or right randomly */
1727 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
1729 /* SP style elements prefer to slip down on the left side */
1730 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
1731 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1733 /* BD style elements prefer to slip down on the left side */
1734 if (game.emulation == EMU_BOULDERDASH)
1735 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1740 /* initialize explosion and ignition delay */
1741 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1743 if (!IS_CUSTOM_ELEMENT(i))
1746 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1747 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1748 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1749 int last_phase = (num_phase + 1) * delay;
1750 int half_phase = (num_phase / 2) * delay;
1752 element_info[i].explosion_delay = last_phase - 1;
1753 element_info[i].ignition_delay = half_phase;
1755 if (i == EL_BLACK_ORB)
1756 element_info[i].ignition_delay = 1;
1760 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1761 element_info[i].explosion_delay = 1;
1763 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1764 element_info[i].ignition_delay = 1;
1768 /* correct non-moving belts to start moving left */
1769 for (i = 0; i < NUM_BELTS; i++)
1770 if (game.belt_dir[i] == MV_NONE)
1771 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1773 /* check if any connected player was not found in playfield */
1774 for (i = 0; i < MAX_PLAYERS; i++)
1776 struct PlayerInfo *player = &stored_player[i];
1778 if (player->connected && !player->present)
1780 for (j = 0; j < MAX_PLAYERS; j++)
1782 struct PlayerInfo *some_player = &stored_player[j];
1783 int jx = some_player->jx, jy = some_player->jy;
1785 /* assign first free player found that is present in the playfield */
1786 if (some_player->present && !some_player->connected)
1788 player->present = TRUE;
1789 player->active = TRUE;
1791 some_player->present = FALSE;
1792 some_player->active = FALSE;
1795 player->element_nr = some_player->element_nr;
1798 player->block_last_field = some_player->block_last_field;
1799 player->block_delay_adjustment = some_player->block_delay_adjustment;
1801 StorePlayer[jx][jy] = player->element_nr;
1802 player->jx = player->last_jx = jx;
1803 player->jy = player->last_jy = jy;
1813 /* when playing a tape, eliminate all players which do not participate */
1815 for (i = 0; i < MAX_PLAYERS; i++)
1817 if (stored_player[i].active && !tape.player_participates[i])
1819 struct PlayerInfo *player = &stored_player[i];
1820 int jx = player->jx, jy = player->jy;
1822 player->active = FALSE;
1823 StorePlayer[jx][jy] = 0;
1824 Feld[jx][jy] = EL_EMPTY;
1828 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1830 /* when in single player mode, eliminate all but the first active player */
1832 for (i = 0; i < MAX_PLAYERS; i++)
1834 if (stored_player[i].active)
1836 for (j = i + 1; j < MAX_PLAYERS; j++)
1838 if (stored_player[j].active)
1840 struct PlayerInfo *player = &stored_player[j];
1841 int jx = player->jx, jy = player->jy;
1843 player->active = FALSE;
1844 player->present = FALSE;
1846 StorePlayer[jx][jy] = 0;
1847 Feld[jx][jy] = EL_EMPTY;
1854 /* when recording the game, store which players take part in the game */
1857 for (i = 0; i < MAX_PLAYERS; i++)
1858 if (stored_player[i].active)
1859 tape.player_participates[i] = TRUE;
1864 for (i = 0; i < MAX_PLAYERS; i++)
1866 struct PlayerInfo *player = &stored_player[i];
1868 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1873 if (local_player == player)
1874 printf("Player %d is local player.\n", i+1);
1878 if (BorderElement == EL_EMPTY)
1881 SBX_Right = lev_fieldx - SCR_FIELDX;
1883 SBY_Lower = lev_fieldy - SCR_FIELDY;
1888 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1890 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1893 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1894 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1896 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1897 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1899 /* if local player not found, look for custom element that might create
1900 the player (make some assumptions about the right custom element) */
1901 if (!local_player->present)
1903 int start_x = 0, start_y = 0;
1904 int found_rating = 0;
1905 int found_element = EL_UNDEFINED;
1907 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1909 int element = Feld[x][y];
1914 if (!IS_CUSTOM_ELEMENT(element))
1917 if (CAN_CHANGE(element))
1919 for (i = 0; i < element_info[element].num_change_pages; i++)
1921 content = element_info[element].change_page[i].target_element;
1922 is_player = ELEM_IS_PLAYER(content);
1924 if (is_player && (found_rating < 3 || element < found_element))
1930 found_element = element;
1935 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1937 content = element_info[element].content.e[xx][yy];
1938 is_player = ELEM_IS_PLAYER(content);
1940 if (is_player && (found_rating < 2 || element < found_element))
1942 start_x = x + xx - 1;
1943 start_y = y + yy - 1;
1946 found_element = element;
1949 if (!CAN_CHANGE(element))
1952 for (i = 0; i < element_info[element].num_change_pages; i++)
1955 element_info[element].change_page[i].target_content.e[xx][yy];
1957 is_player = ELEM_IS_PLAYER(content);
1959 if (is_player && (found_rating < 1 || element < found_element))
1961 start_x = x + xx - 1;
1962 start_y = y + yy - 1;
1965 found_element = element;
1971 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1972 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1975 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1976 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1981 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1982 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1983 local_player->jx - MIDPOSX);
1985 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1986 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1987 local_player->jy - MIDPOSY);
1990 if (!game.restart_level)
1991 CloseDoor(DOOR_CLOSE_1);
1993 /* !!! FIX THIS (START) !!! */
1994 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1996 InitGameEngine_EM();
2003 /* after drawing the level, correct some elements */
2004 if (game.timegate_time_left == 0)
2005 CloseAllOpenTimegates();
2007 if (setup.soft_scrolling)
2008 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2010 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2013 /* !!! FIX THIS (END) !!! */
2015 if (!game.restart_level)
2017 /* copy default game door content to main double buffer */
2018 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2019 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2022 DrawGameDoorValues();
2024 if (!game.restart_level)
2028 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2029 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2030 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2034 /* copy actual game door content to door double buffer for OpenDoor() */
2035 BlitBitmap(drawto, bitmap_db_door,
2036 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2038 OpenDoor(DOOR_OPEN_ALL);
2040 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2042 if (setup.sound_music)
2045 KeyboardAutoRepeatOffUnlessAutoplay();
2049 for (i = 0; i < MAX_PLAYERS; i++)
2050 printf("Player %d %sactive.\n",
2051 i + 1, (stored_player[i].active ? "" : "not "));
2055 game.restart_level = FALSE;
2058 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2060 /* this is used for non-R'n'D game engines to update certain engine values */
2062 /* needed to determine if sounds are played within the visible screen area */
2063 scroll_x = actual_scroll_x;
2064 scroll_y = actual_scroll_y;
2067 void InitMovDir(int x, int y)
2069 int i, element = Feld[x][y];
2070 static int xy[4][2] =
2077 static int direction[3][4] =
2079 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2080 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2081 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2090 Feld[x][y] = EL_BUG;
2091 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2094 case EL_SPACESHIP_RIGHT:
2095 case EL_SPACESHIP_UP:
2096 case EL_SPACESHIP_LEFT:
2097 case EL_SPACESHIP_DOWN:
2098 Feld[x][y] = EL_SPACESHIP;
2099 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2102 case EL_BD_BUTTERFLY_RIGHT:
2103 case EL_BD_BUTTERFLY_UP:
2104 case EL_BD_BUTTERFLY_LEFT:
2105 case EL_BD_BUTTERFLY_DOWN:
2106 Feld[x][y] = EL_BD_BUTTERFLY;
2107 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2110 case EL_BD_FIREFLY_RIGHT:
2111 case EL_BD_FIREFLY_UP:
2112 case EL_BD_FIREFLY_LEFT:
2113 case EL_BD_FIREFLY_DOWN:
2114 Feld[x][y] = EL_BD_FIREFLY;
2115 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2118 case EL_PACMAN_RIGHT:
2120 case EL_PACMAN_LEFT:
2121 case EL_PACMAN_DOWN:
2122 Feld[x][y] = EL_PACMAN;
2123 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2126 case EL_SP_SNIKSNAK:
2127 MovDir[x][y] = MV_UP;
2130 case EL_SP_ELECTRON:
2131 MovDir[x][y] = MV_LEFT;
2138 Feld[x][y] = EL_MOLE;
2139 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2143 if (IS_CUSTOM_ELEMENT(element))
2145 struct ElementInfo *ei = &element_info[element];
2146 int move_direction_initial = ei->move_direction_initial;
2147 int move_pattern = ei->move_pattern;
2149 if (move_direction_initial == MV_START_PREVIOUS)
2151 if (MovDir[x][y] != MV_NONE)
2154 move_direction_initial = MV_START_AUTOMATIC;
2157 if (move_direction_initial == MV_START_RANDOM)
2158 MovDir[x][y] = 1 << RND(4);
2159 else if (move_direction_initial & MV_ANY_DIRECTION)
2160 MovDir[x][y] = move_direction_initial;
2161 else if (move_pattern == MV_ALL_DIRECTIONS ||
2162 move_pattern == MV_TURNING_LEFT ||
2163 move_pattern == MV_TURNING_RIGHT ||
2164 move_pattern == MV_TURNING_LEFT_RIGHT ||
2165 move_pattern == MV_TURNING_RIGHT_LEFT ||
2166 move_pattern == MV_TURNING_RANDOM)
2167 MovDir[x][y] = 1 << RND(4);
2168 else if (move_pattern == MV_HORIZONTAL)
2169 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2170 else if (move_pattern == MV_VERTICAL)
2171 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2172 else if (move_pattern & MV_ANY_DIRECTION)
2173 MovDir[x][y] = element_info[element].move_pattern;
2174 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2175 move_pattern == MV_ALONG_RIGHT_SIDE)
2177 /* use random direction as default start direction */
2178 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2179 MovDir[x][y] = 1 << RND(4);
2181 for (i = 0; i < NUM_DIRECTIONS; i++)
2183 int x1 = x + xy[i][0];
2184 int y1 = y + xy[i][1];
2186 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2188 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2189 MovDir[x][y] = direction[0][i];
2191 MovDir[x][y] = direction[1][i];
2200 MovDir[x][y] = 1 << RND(4);
2202 if (element != EL_BUG &&
2203 element != EL_SPACESHIP &&
2204 element != EL_BD_BUTTERFLY &&
2205 element != EL_BD_FIREFLY)
2208 for (i = 0; i < NUM_DIRECTIONS; i++)
2210 int x1 = x + xy[i][0];
2211 int y1 = y + xy[i][1];
2213 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2215 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2217 MovDir[x][y] = direction[0][i];
2220 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2221 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2223 MovDir[x][y] = direction[1][i];
2232 GfxDir[x][y] = MovDir[x][y];
2235 void InitAmoebaNr(int x, int y)
2238 int group_nr = AmoebeNachbarNr(x, y);
2242 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2244 if (AmoebaCnt[i] == 0)
2252 AmoebaNr[x][y] = group_nr;
2253 AmoebaCnt[group_nr]++;
2254 AmoebaCnt2[group_nr]++;
2260 boolean raise_level = FALSE;
2262 if (local_player->MovPos)
2265 if (tape.auto_play) /* tape might already be stopped here */
2266 tape.auto_play_level_solved = TRUE;
2268 local_player->LevelSolved = FALSE;
2270 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2274 if (!tape.playing && setup.sound_loops)
2275 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2276 SND_CTRL_PLAY_LOOP);
2278 while (TimeLeft > 0)
2280 if (!tape.playing && !setup.sound_loops)
2281 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2282 if (TimeLeft > 0 && !(TimeLeft % 10))
2283 RaiseScore(level.score[SC_TIME_BONUS]);
2284 if (TimeLeft > 100 && !(TimeLeft % 10))
2289 DrawGameValue_Time(TimeLeft);
2297 if (!tape.playing && setup.sound_loops)
2298 StopSound(SND_GAME_LEVELTIME_BONUS);
2300 else if (level.time == 0) /* level without time limit */
2302 if (!tape.playing && setup.sound_loops)
2303 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2304 SND_CTRL_PLAY_LOOP);
2306 while (TimePlayed < 999)
2308 if (!tape.playing && !setup.sound_loops)
2309 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2310 if (TimePlayed < 999 && !(TimePlayed % 10))
2311 RaiseScore(level.score[SC_TIME_BONUS]);
2312 if (TimePlayed < 900 && !(TimePlayed % 10))
2317 DrawGameValue_Time(TimePlayed);
2325 if (!tape.playing && setup.sound_loops)
2326 StopSound(SND_GAME_LEVELTIME_BONUS);
2329 /* close exit door after last player */
2330 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2331 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2332 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2334 int element = Feld[ExitX][ExitY];
2336 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2337 EL_SP_EXIT_CLOSING);
2339 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2342 /* player disappears */
2343 if (ExitX >= 0 && ExitY >= 0)
2344 DrawLevelField(ExitX, ExitY);
2351 CloseDoor(DOOR_CLOSE_1);
2356 SaveTape(tape.level_nr); /* Ask to save tape */
2359 if (level_nr == leveldir_current->handicap_level)
2361 leveldir_current->handicap_level++;
2362 SaveLevelSetup_SeriesInfo();
2365 if (level_editor_test_game)
2366 local_player->score = -1; /* no highscore when playing from editor */
2367 else if (level_nr < leveldir_current->last_level)
2368 raise_level = TRUE; /* advance to next level */
2370 if ((hi_pos = NewHiScore()) >= 0)
2372 game_status = GAME_MODE_SCORES;
2373 DrawHallOfFame(hi_pos);
2382 game_status = GAME_MODE_MAIN;
2399 LoadScore(level_nr);
2401 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2402 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2405 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2407 if (local_player->score > highscore[k].Score)
2409 /* player has made it to the hall of fame */
2411 if (k < MAX_SCORE_ENTRIES - 1)
2413 int m = MAX_SCORE_ENTRIES - 1;
2416 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2417 if (!strcmp(setup.player_name, highscore[l].Name))
2419 if (m == k) /* player's new highscore overwrites his old one */
2423 for (l = m; l > k; l--)
2425 strcpy(highscore[l].Name, highscore[l - 1].Name);
2426 highscore[l].Score = highscore[l - 1].Score;
2433 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2434 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2435 highscore[k].Score = local_player->score;
2441 else if (!strncmp(setup.player_name, highscore[k].Name,
2442 MAX_PLAYER_NAME_LEN))
2443 break; /* player already there with a higher score */
2449 SaveScore(level_nr);
2454 inline static int getElementMoveStepsize(int x, int y)
2456 int element = Feld[x][y];
2457 int direction = MovDir[x][y];
2458 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2459 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2460 int horiz_move = (dx != 0);
2461 int sign = (horiz_move ? dx : dy);
2462 int step = sign * element_info[element].move_stepsize;
2464 /* special values for move stepsize for spring and things on conveyor belt */
2468 if (element == EL_SPRING)
2469 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2470 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2471 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2472 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2474 if (CAN_FALL(element) &&
2475 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2476 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2477 else if (element == EL_SPRING)
2478 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2485 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2487 if (player->GfxAction != action || player->GfxDir != dir)
2490 printf("Player frame reset! (%d => %d, %d => %d)\n",
2491 player->GfxAction, action, player->GfxDir, dir);
2494 player->GfxAction = action;
2495 player->GfxDir = dir;
2497 player->StepFrame = 0;
2501 static void ResetRandomAnimationValue(int x, int y)
2503 GfxRandom[x][y] = INIT_GFX_RANDOM();
2506 static void ResetGfxAnimation(int x, int y)
2509 GfxAction[x][y] = ACTION_DEFAULT;
2510 GfxDir[x][y] = MovDir[x][y];
2513 void InitMovingField(int x, int y, int direction)
2515 int element = Feld[x][y];
2516 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2517 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2521 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2522 ResetGfxAnimation(x, y);
2524 MovDir[x][y] = direction;
2525 GfxDir[x][y] = direction;
2526 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2527 ACTION_FALLING : ACTION_MOVING);
2529 /* this is needed for CEs with property "can move" / "not moving" */
2531 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2533 if (Feld[newx][newy] == EL_EMPTY)
2534 Feld[newx][newy] = EL_BLOCKED;
2536 MovDir[newx][newy] = MovDir[x][y];
2538 #if USE_NEW_COLLECT_COUNT
2539 Count[newx][newy] = Count[x][y];
2542 GfxFrame[newx][newy] = GfxFrame[x][y];
2543 GfxRandom[newx][newy] = GfxRandom[x][y];
2544 GfxAction[newx][newy] = GfxAction[x][y];
2545 GfxDir[newx][newy] = GfxDir[x][y];
2549 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2551 int direction = MovDir[x][y];
2552 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2553 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2559 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2561 int oldx = x, oldy = y;
2562 int direction = MovDir[x][y];
2564 if (direction == MV_LEFT)
2566 else if (direction == MV_RIGHT)
2568 else if (direction == MV_UP)
2570 else if (direction == MV_DOWN)
2573 *comes_from_x = oldx;
2574 *comes_from_y = oldy;
2577 int MovingOrBlocked2Element(int x, int y)
2579 int element = Feld[x][y];
2581 if (element == EL_BLOCKED)
2585 Blocked2Moving(x, y, &oldx, &oldy);
2586 return Feld[oldx][oldy];
2592 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2594 /* like MovingOrBlocked2Element(), but if element is moving
2595 and (x,y) is the field the moving element is just leaving,
2596 return EL_BLOCKED instead of the element value */
2597 int element = Feld[x][y];
2599 if (IS_MOVING(x, y))
2601 if (element == EL_BLOCKED)
2605 Blocked2Moving(x, y, &oldx, &oldy);
2606 return Feld[oldx][oldy];
2615 static void RemoveField(int x, int y)
2617 Feld[x][y] = EL_EMPTY;
2623 #if USE_NEW_COLLECT_COUNT
2628 ChangeDelay[x][y] = 0;
2629 ChangePage[x][y] = -1;
2630 Pushed[x][y] = FALSE;
2633 ExplodeField[x][y] = EX_TYPE_NONE;
2636 GfxElement[x][y] = EL_UNDEFINED;
2637 GfxAction[x][y] = ACTION_DEFAULT;
2638 GfxDir[x][y] = MV_NONE;
2641 void RemoveMovingField(int x, int y)
2643 int oldx = x, oldy = y, newx = x, newy = y;
2644 int element = Feld[x][y];
2645 int next_element = EL_UNDEFINED;
2647 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2650 if (IS_MOVING(x, y))
2652 Moving2Blocked(x, y, &newx, &newy);
2654 if (Feld[newx][newy] != EL_BLOCKED)
2656 /* element is moving, but target field is not free (blocked), but
2657 already occupied by something different (example: acid pool);
2658 in this case, only remove the moving field, but not the target */
2660 RemoveField(oldx, oldy);
2662 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2664 DrawLevelField(oldx, oldy);
2669 else if (element == EL_BLOCKED)
2671 Blocked2Moving(x, y, &oldx, &oldy);
2672 if (!IS_MOVING(oldx, oldy))
2676 if (element == EL_BLOCKED &&
2677 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2678 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2679 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2680 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2681 next_element = get_next_element(Feld[oldx][oldy]);
2683 RemoveField(oldx, oldy);
2684 RemoveField(newx, newy);
2686 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2688 if (next_element != EL_UNDEFINED)
2689 Feld[oldx][oldy] = next_element;
2691 DrawLevelField(oldx, oldy);
2692 DrawLevelField(newx, newy);
2695 void DrawDynamite(int x, int y)
2697 int sx = SCREENX(x), sy = SCREENY(y);
2698 int graphic = el2img(Feld[x][y]);
2701 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2704 if (IS_WALKABLE_INSIDE(Back[x][y]))
2708 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2709 else if (Store[x][y])
2710 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2712 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2714 if (Back[x][y] || Store[x][y])
2715 DrawGraphicThruMask(sx, sy, graphic, frame);
2717 DrawGraphic(sx, sy, graphic, frame);
2720 void CheckDynamite(int x, int y)
2722 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2726 if (MovDelay[x][y] != 0)
2729 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2735 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2740 void DrawRelocatePlayer(struct PlayerInfo *player)
2742 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2743 boolean no_delay = (tape.warp_forward);
2744 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2745 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2746 int jx = player->jx;
2747 int jy = player->jy;
2749 if (level.instant_relocation)
2751 int offset = (setup.scroll_delay ? 3 : 0);
2753 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2755 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2756 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2757 local_player->jx - MIDPOSX);
2759 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2760 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2761 local_player->jy - MIDPOSY);
2765 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2766 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2767 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2769 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2770 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2771 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2773 /* don't scroll over playfield boundaries */
2774 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2775 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2777 /* don't scroll over playfield boundaries */
2778 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2779 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2782 RedrawPlayfield(TRUE, 0,0,0,0);
2786 int scroll_xx = -999, scroll_yy = -999;
2788 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2790 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2793 int fx = FX, fy = FY;
2795 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2796 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2797 local_player->jx - MIDPOSX);
2799 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2800 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2801 local_player->jy - MIDPOSY);
2803 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2804 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2806 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2812 fx += dx * TILEX / 2;
2813 fy += dy * TILEY / 2;
2815 ScrollLevel(dx, dy);
2818 /* scroll in two steps of half tile size to make things smoother */
2819 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2821 Delay(wait_delay_value);
2823 /* scroll second step to align at full tile size */
2825 Delay(wait_delay_value);
2830 Delay(wait_delay_value);
2834 void RelocatePlayer(int jx, int jy, int el_player_raw)
2836 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
2837 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2838 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2839 boolean no_delay = (tape.warp_forward);
2840 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2841 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2842 int old_jx = player->jx;
2843 int old_jy = player->jy;
2844 int old_element = Feld[old_jx][old_jy];
2845 int element = Feld[jx][jy];
2846 boolean player_relocated = (old_jx != jx || old_jy != jy);
2848 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2849 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2850 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2851 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2852 int leave_side_horiz = move_dir_horiz;
2853 int leave_side_vert = move_dir_vert;
2854 int enter_side = enter_side_horiz | enter_side_vert;
2855 int leave_side = leave_side_horiz | leave_side_vert;
2857 if (player->GameOver) /* do not reanimate dead player */
2860 if (!player_relocated) /* no need to relocate the player */
2863 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2865 RemoveField(jx, jy); /* temporarily remove newly placed player */
2866 DrawLevelField(jx, jy);
2869 if (player->present)
2871 while (player->MovPos)
2873 ScrollPlayer(player, SCROLL_GO_ON);
2874 ScrollScreen(NULL, SCROLL_GO_ON);
2876 AdvanceFrameAndPlayerCounters(player->index_nr);
2881 Delay(wait_delay_value);
2884 DrawPlayer(player); /* needed here only to cleanup last field */
2885 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2887 player->is_moving = FALSE;
2890 if (IS_CUSTOM_ELEMENT(old_element))
2891 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
2893 player->index_bit, leave_side);
2895 CheckTriggeredElementChangeByPlayer(old_element, CE_PLAYER_LEAVES_X,
2896 player->index_bit, leave_side);
2898 Feld[jx][jy] = el_player;
2899 InitPlayerField(jx, jy, el_player, TRUE);
2901 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
2903 Feld[jx][jy] = element;
2904 InitField(jx, jy, FALSE);
2907 if (player == local_player) /* only visually relocate local player */
2908 DrawRelocatePlayer(player);
2910 TestIfPlayerTouchesBadThing(jx, jy);
2911 TestIfPlayerTouchesCustomElement(jx, jy);
2913 if (IS_CUSTOM_ELEMENT(element))
2914 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
2915 player->index_bit, enter_side);
2917 CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_ENTERS_X,
2918 player->index_bit, enter_side);
2921 void Explode(int ex, int ey, int phase, int mode)
2927 /* !!! eliminate this variable !!! */
2928 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2930 if (game.explosions_delayed)
2932 ExplodeField[ex][ey] = mode;
2936 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2938 int center_element = Feld[ex][ey];
2941 /* --- This is only really needed (and now handled) in "Impact()". --- */
2942 /* do not explode moving elements that left the explode field in time */
2943 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2944 center_element == EL_EMPTY &&
2945 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
2949 if (mode == EX_TYPE_NORMAL ||
2950 mode == EX_TYPE_CENTER ||
2951 mode == EX_TYPE_CROSS)
2952 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2954 /* remove things displayed in background while burning dynamite */
2955 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2958 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2960 /* put moving element to center field (and let it explode there) */
2961 center_element = MovingOrBlocked2Element(ex, ey);
2962 RemoveMovingField(ex, ey);
2963 Feld[ex][ey] = center_element;
2966 last_phase = element_info[center_element].explosion_delay + 1;
2968 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2970 int xx = x - ex + 1;
2971 int yy = y - ey + 1;
2974 if (!IN_LEV_FIELD(x, y) ||
2975 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
2976 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
2979 element = Feld[x][y];
2981 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2983 element = MovingOrBlocked2Element(x, y);
2985 if (!IS_EXPLOSION_PROOF(element))
2986 RemoveMovingField(x, y);
2989 /* indestructible elements can only explode in center (but not flames) */
2990 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
2991 mode == EX_TYPE_BORDER)) ||
2992 element == EL_FLAMES)
2995 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
2996 behaviour, for example when touching a yamyam that explodes to rocks
2997 with active deadly shield, a rock is created under the player !!! */
2998 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3000 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3001 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3002 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3004 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3007 if (IS_ACTIVE_BOMB(element))
3009 /* re-activate things under the bomb like gate or penguin */
3010 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3017 /* save walkable background elements while explosion on same tile */
3018 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3019 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3020 Back[x][y] = element;
3022 /* ignite explodable elements reached by other explosion */
3023 if (element == EL_EXPLOSION)
3024 element = Store2[x][y];
3026 if (AmoebaNr[x][y] &&
3027 (element == EL_AMOEBA_FULL ||
3028 element == EL_BD_AMOEBA ||
3029 element == EL_AMOEBA_GROWING))
3031 AmoebaCnt[AmoebaNr[x][y]]--;
3032 AmoebaCnt2[AmoebaNr[x][y]]--;
3037 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3039 switch(StorePlayer[ex][ey])
3042 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3045 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3048 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3052 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3056 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3057 Store[x][y] = EL_EMPTY;
3059 else if (center_element == EL_MOLE)
3060 Store[x][y] = EL_EMERALD_RED;
3061 else if (center_element == EL_PENGUIN)
3062 Store[x][y] = EL_EMERALD_PURPLE;
3063 else if (center_element == EL_BUG)
3064 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3065 else if (center_element == EL_BD_BUTTERFLY)
3066 Store[x][y] = EL_BD_DIAMOND;
3067 else if (center_element == EL_SP_ELECTRON)
3068 Store[x][y] = EL_SP_INFOTRON;
3069 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3070 Store[x][y] = level.amoeba_content;
3071 else if (center_element == EL_YAMYAM)
3072 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3073 else if (IS_CUSTOM_ELEMENT(center_element) &&
3074 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3075 Store[x][y] = element_info[center_element].content.e[xx][yy];
3076 else if (element == EL_WALL_EMERALD)
3077 Store[x][y] = EL_EMERALD;
3078 else if (element == EL_WALL_DIAMOND)
3079 Store[x][y] = EL_DIAMOND;
3080 else if (element == EL_WALL_BD_DIAMOND)
3081 Store[x][y] = EL_BD_DIAMOND;
3082 else if (element == EL_WALL_EMERALD_YELLOW)
3083 Store[x][y] = EL_EMERALD_YELLOW;
3084 else if (element == EL_WALL_EMERALD_RED)
3085 Store[x][y] = EL_EMERALD_RED;
3086 else if (element == EL_WALL_EMERALD_PURPLE)
3087 Store[x][y] = EL_EMERALD_PURPLE;
3088 else if (element == EL_WALL_PEARL)
3089 Store[x][y] = EL_PEARL;
3090 else if (element == EL_WALL_CRYSTAL)
3091 Store[x][y] = EL_CRYSTAL;
3092 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3093 Store[x][y] = element_info[element].content.e[1][1];
3095 Store[x][y] = EL_EMPTY;
3097 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3098 center_element == EL_AMOEBA_TO_DIAMOND)
3099 Store2[x][y] = element;
3101 Feld[x][y] = EL_EXPLOSION;
3102 GfxElement[x][y] = center_element;
3104 ExplodePhase[x][y] = 1;
3105 ExplodeDelay[x][y] = last_phase;
3110 if (center_element == EL_YAMYAM)
3111 game.yamyam_content_nr =
3112 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3124 GfxFrame[x][y] = 0; /* restart explosion animation */
3126 last_phase = ExplodeDelay[x][y];
3128 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3132 /* activate this even in non-DEBUG version until cause for crash in
3133 getGraphicAnimationFrame() (see below) is found and eliminated */
3138 if (GfxElement[x][y] == EL_UNDEFINED)
3141 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3142 printf("Explode(): This should never happen!\n");
3145 GfxElement[x][y] = EL_EMPTY;
3149 border_element = Store2[x][y];
3150 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3151 border_element = StorePlayer[x][y];
3153 if (phase == element_info[border_element].ignition_delay ||
3154 phase == last_phase)
3156 boolean border_explosion = FALSE;
3158 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3159 !PLAYER_EXPLOSION_PROTECTED(x, y))
3161 KillPlayerUnlessExplosionProtected(x, y);
3162 border_explosion = TRUE;
3164 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3166 Feld[x][y] = Store2[x][y];
3169 border_explosion = TRUE;
3171 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3173 AmoebeUmwandeln(x, y);
3175 border_explosion = TRUE;
3178 /* if an element just explodes due to another explosion (chain-reaction),
3179 do not immediately end the new explosion when it was the last frame of
3180 the explosion (as it would be done in the following "if"-statement!) */
3181 if (border_explosion && phase == last_phase)
3185 if (phase == last_phase)
3189 element = Feld[x][y] = Store[x][y];
3190 Store[x][y] = Store2[x][y] = 0;
3191 GfxElement[x][y] = EL_UNDEFINED;
3193 /* player can escape from explosions and might therefore be still alive */
3194 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3195 element <= EL_PLAYER_IS_EXPLODING_4)
3196 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3198 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3199 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3200 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3203 /* restore probably existing indestructible background element */
3204 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3205 element = Feld[x][y] = Back[x][y];
3208 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3209 GfxDir[x][y] = MV_NONE;
3210 ChangeDelay[x][y] = 0;
3211 ChangePage[x][y] = -1;
3213 #if USE_NEW_COLLECT_COUNT
3217 InitField_WithBug2(x, y, FALSE);
3219 DrawLevelField(x, y);
3221 TestIfElementTouchesCustomElement(x, y);
3223 if (GFX_CRUMBLED(element))
3224 DrawLevelFieldCrumbledSandNeighbours(x, y);
3226 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3227 StorePlayer[x][y] = 0;
3229 if (ELEM_IS_PLAYER(element))
3230 RelocatePlayer(x, y, element);
3232 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3234 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3235 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3238 DrawLevelFieldCrumbledSand(x, y);
3240 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3242 DrawLevelElement(x, y, Back[x][y]);
3243 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3245 else if (IS_WALKABLE_UNDER(Back[x][y]))
3247 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3248 DrawLevelElementThruMask(x, y, Back[x][y]);
3250 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3251 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3255 void DynaExplode(int ex, int ey)
3258 int dynabomb_element = Feld[ex][ey];
3259 int dynabomb_size = 1;
3260 boolean dynabomb_xl = FALSE;
3261 struct PlayerInfo *player;
3262 static int xy[4][2] =
3270 if (IS_ACTIVE_BOMB(dynabomb_element))
3272 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3273 dynabomb_size = player->dynabomb_size;
3274 dynabomb_xl = player->dynabomb_xl;
3275 player->dynabombs_left++;
3278 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3280 for (i = 0; i < NUM_DIRECTIONS; i++)
3282 for (j = 1; j <= dynabomb_size; j++)
3284 int x = ex + j * xy[i][0];
3285 int y = ey + j * xy[i][1];
3288 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3291 element = Feld[x][y];
3293 /* do not restart explosions of fields with active bombs */
3294 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3297 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3299 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3300 !IS_DIGGABLE(element) && !dynabomb_xl)
3306 void Bang(int x, int y)
3308 int element = MovingOrBlocked2Element(x, y);
3310 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3312 struct PlayerInfo *player = PLAYERINFO(x, y);
3314 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3315 player->element_nr);
3322 case EL_BD_BUTTERFLY:
3325 case EL_DARK_YAMYAM:
3329 RaiseScoreElement(element);
3330 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3332 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3333 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3334 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3335 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3336 case EL_DYNABOMB_INCREASE_NUMBER:
3337 case EL_DYNABOMB_INCREASE_SIZE:
3338 case EL_DYNABOMB_INCREASE_POWER:
3343 case EL_LAMP_ACTIVE:
3344 case EL_AMOEBA_TO_DIAMOND:
3345 if (IS_PLAYER(x, y))
3346 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3348 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3351 if (element_info[element].explosion_type == EXPLODES_CROSS)
3352 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3353 else if (element_info[element].explosion_type == EXPLODES_1X1)
3354 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3356 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3360 CheckTriggeredElementChange(element, CE_EXPLOSION_OF_X);
3363 void SplashAcid(int x, int y)
3365 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3366 (!IN_LEV_FIELD(x - 1, y - 2) ||
3367 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3368 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3370 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3371 (!IN_LEV_FIELD(x + 1, y - 2) ||
3372 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3373 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3375 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3378 static void InitBeltMovement()
3380 static int belt_base_element[4] =
3382 EL_CONVEYOR_BELT_1_LEFT,
3383 EL_CONVEYOR_BELT_2_LEFT,
3384 EL_CONVEYOR_BELT_3_LEFT,
3385 EL_CONVEYOR_BELT_4_LEFT
3387 static int belt_base_active_element[4] =
3389 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3390 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3391 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3392 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3397 /* set frame order for belt animation graphic according to belt direction */
3398 for (i = 0; i < NUM_BELTS; i++)
3402 for (j = 0; j < NUM_BELT_PARTS; j++)
3404 int element = belt_base_active_element[belt_nr] + j;
3405 int graphic = el2img(element);
3407 if (game.belt_dir[i] == MV_LEFT)
3408 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3410 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3414 for (y = 0; y < lev_fieldy; y++)
3416 for (x = 0; x < lev_fieldx; x++)
3418 int element = Feld[x][y];
3420 for (i = 0; i < NUM_BELTS; i++)
3422 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3424 int e_belt_nr = getBeltNrFromBeltElement(element);
3427 if (e_belt_nr == belt_nr)
3429 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3431 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3439 static void ToggleBeltSwitch(int x, int y)
3441 static int belt_base_element[4] =
3443 EL_CONVEYOR_BELT_1_LEFT,
3444 EL_CONVEYOR_BELT_2_LEFT,
3445 EL_CONVEYOR_BELT_3_LEFT,
3446 EL_CONVEYOR_BELT_4_LEFT
3448 static int belt_base_active_element[4] =
3450 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3451 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3452 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3453 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3455 static int belt_base_switch_element[4] =
3457 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3458 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3459 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3460 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3462 static int belt_move_dir[4] =
3470 int element = Feld[x][y];
3471 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3472 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3473 int belt_dir = belt_move_dir[belt_dir_nr];
3476 if (!IS_BELT_SWITCH(element))
3479 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3480 game.belt_dir[belt_nr] = belt_dir;
3482 if (belt_dir_nr == 3)
3485 /* set frame order for belt animation graphic according to belt direction */
3486 for (i = 0; i < NUM_BELT_PARTS; i++)
3488 int element = belt_base_active_element[belt_nr] + i;
3489 int graphic = el2img(element);
3491 if (belt_dir == MV_LEFT)
3492 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3494 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3497 for (yy = 0; yy < lev_fieldy; yy++)
3499 for (xx = 0; xx < lev_fieldx; xx++)
3501 int element = Feld[xx][yy];
3503 if (IS_BELT_SWITCH(element))
3505 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3507 if (e_belt_nr == belt_nr)
3509 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3510 DrawLevelField(xx, yy);
3513 else if (IS_BELT(element) && belt_dir != MV_NONE)
3515 int e_belt_nr = getBeltNrFromBeltElement(element);
3517 if (e_belt_nr == belt_nr)
3519 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3521 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3522 DrawLevelField(xx, yy);
3525 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3527 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3529 if (e_belt_nr == belt_nr)
3531 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3533 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3534 DrawLevelField(xx, yy);
3541 static void ToggleSwitchgateSwitch(int x, int y)
3545 game.switchgate_pos = !game.switchgate_pos;
3547 for (yy = 0; yy < lev_fieldy; yy++)
3549 for (xx = 0; xx < lev_fieldx; xx++)
3551 int element = Feld[xx][yy];
3553 if (element == EL_SWITCHGATE_SWITCH_UP ||
3554 element == EL_SWITCHGATE_SWITCH_DOWN)
3556 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3557 DrawLevelField(xx, yy);
3559 else if (element == EL_SWITCHGATE_OPEN ||
3560 element == EL_SWITCHGATE_OPENING)
3562 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3564 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3566 else if (element == EL_SWITCHGATE_CLOSED ||
3567 element == EL_SWITCHGATE_CLOSING)
3569 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3571 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3577 static int getInvisibleActiveFromInvisibleElement(int element)
3579 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3580 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3581 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3585 static int getInvisibleFromInvisibleActiveElement(int element)
3587 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3588 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3589 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3593 static void RedrawAllLightSwitchesAndInvisibleElements()
3597 for (y = 0; y < lev_fieldy; y++)
3599 for (x = 0; x < lev_fieldx; x++)
3601 int element = Feld[x][y];
3603 if (element == EL_LIGHT_SWITCH &&
3604 game.light_time_left > 0)
3606 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3607 DrawLevelField(x, y);
3609 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3610 game.light_time_left == 0)
3612 Feld[x][y] = EL_LIGHT_SWITCH;
3613 DrawLevelField(x, y);
3615 else if (element == EL_INVISIBLE_STEELWALL ||
3616 element == EL_INVISIBLE_WALL ||
3617 element == EL_INVISIBLE_SAND)
3619 if (game.light_time_left > 0)
3620 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3622 DrawLevelField(x, y);
3624 /* uncrumble neighbour fields, if needed */
3625 if (element == EL_INVISIBLE_SAND)
3626 DrawLevelFieldCrumbledSandNeighbours(x, y);
3628 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3629 element == EL_INVISIBLE_WALL_ACTIVE ||
3630 element == EL_INVISIBLE_SAND_ACTIVE)
3632 if (game.light_time_left == 0)
3633 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3635 DrawLevelField(x, y);
3637 /* re-crumble neighbour fields, if needed */
3638 if (element == EL_INVISIBLE_SAND)
3639 DrawLevelFieldCrumbledSandNeighbours(x, y);
3645 static void ToggleLightSwitch(int x, int y)
3647 int element = Feld[x][y];
3649 game.light_time_left =
3650 (element == EL_LIGHT_SWITCH ?
3651 level.time_light * FRAMES_PER_SECOND : 0);
3653 RedrawAllLightSwitchesAndInvisibleElements();
3656 static void ActivateTimegateSwitch(int x, int y)
3660 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3662 for (yy = 0; yy < lev_fieldy; yy++)
3664 for (xx = 0; xx < lev_fieldx; xx++)
3666 int element = Feld[xx][yy];
3668 if (element == EL_TIMEGATE_CLOSED ||
3669 element == EL_TIMEGATE_CLOSING)
3671 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3672 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3676 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3678 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3679 DrawLevelField(xx, yy);
3686 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3689 void Impact(int x, int y)
3691 boolean last_line = (y == lev_fieldy - 1);
3692 boolean object_hit = FALSE;
3693 boolean impact = (last_line || object_hit);
3694 int element = Feld[x][y];
3695 int smashed = EL_STEELWALL;
3697 if (!last_line) /* check if element below was hit */
3699 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3702 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3703 MovDir[x][y + 1] != MV_DOWN ||
3704 MovPos[x][y + 1] <= TILEY / 2));
3706 /* do not smash moving elements that left the smashed field in time */
3707 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3708 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3712 smashed = MovingOrBlocked2Element(x, y + 1);
3714 impact = (last_line || object_hit);
3717 if (!last_line && smashed == EL_ACID) /* element falls into acid */
3719 SplashAcid(x, y + 1);
3723 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
3724 /* only reset graphic animation if graphic really changes after impact */
3726 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3728 ResetGfxAnimation(x, y);
3729 DrawLevelField(x, y);
3732 if (impact && CAN_EXPLODE_IMPACT(element))
3737 else if (impact && element == EL_PEARL)
3739 ResetGfxAnimation(x, y);
3741 Feld[x][y] = EL_PEARL_BREAKING;
3742 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3745 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
3747 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3752 if (impact && element == EL_AMOEBA_DROP)
3754 if (object_hit && IS_PLAYER(x, y + 1))
3755 KillPlayerUnlessEnemyProtected(x, y + 1);
3756 else if (object_hit && smashed == EL_PENGUIN)
3760 Feld[x][y] = EL_AMOEBA_GROWING;
3761 Store[x][y] = EL_AMOEBA_WET;
3763 ResetRandomAnimationValue(x, y);
3768 if (object_hit) /* check which object was hit */
3770 if (CAN_PASS_MAGIC_WALL(element) &&
3771 (smashed == EL_MAGIC_WALL ||
3772 smashed == EL_BD_MAGIC_WALL))
3775 int activated_magic_wall =
3776 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3777 EL_BD_MAGIC_WALL_ACTIVE);
3779 /* activate magic wall / mill */
3780 for (yy = 0; yy < lev_fieldy; yy++)
3781 for (xx = 0; xx < lev_fieldx; xx++)
3782 if (Feld[xx][yy] == smashed)
3783 Feld[xx][yy] = activated_magic_wall;
3785 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3786 game.magic_wall_active = TRUE;
3788 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3789 SND_MAGIC_WALL_ACTIVATING :
3790 SND_BD_MAGIC_WALL_ACTIVATING));
3793 if (IS_PLAYER(x, y + 1))
3795 if (CAN_SMASH_PLAYER(element))
3797 KillPlayerUnlessEnemyProtected(x, y + 1);
3801 else if (smashed == EL_PENGUIN)
3803 if (CAN_SMASH_PLAYER(element))
3809 else if (element == EL_BD_DIAMOND)
3811 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3817 else if (((element == EL_SP_INFOTRON ||
3818 element == EL_SP_ZONK) &&
3819 (smashed == EL_SP_SNIKSNAK ||
3820 smashed == EL_SP_ELECTRON ||
3821 smashed == EL_SP_DISK_ORANGE)) ||
3822 (element == EL_SP_INFOTRON &&
3823 smashed == EL_SP_DISK_YELLOW))
3828 else if (CAN_SMASH_EVERYTHING(element))
3830 if (IS_CLASSIC_ENEMY(smashed) ||
3831 CAN_EXPLODE_SMASHED(smashed))
3836 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3838 if (smashed == EL_LAMP ||
3839 smashed == EL_LAMP_ACTIVE)
3844 else if (smashed == EL_NUT)
3846 Feld[x][y + 1] = EL_NUT_BREAKING;
3847 PlayLevelSound(x, y, SND_NUT_BREAKING);
3848 RaiseScoreElement(EL_NUT);
3851 else if (smashed == EL_PEARL)
3853 ResetGfxAnimation(x, y);
3855 Feld[x][y + 1] = EL_PEARL_BREAKING;
3856 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3859 else if (smashed == EL_DIAMOND)
3861 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3862 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3865 else if (IS_BELT_SWITCH(smashed))
3867 ToggleBeltSwitch(x, y + 1);
3869 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3870 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3872 ToggleSwitchgateSwitch(x, y + 1);
3874 else if (smashed == EL_LIGHT_SWITCH ||
3875 smashed == EL_LIGHT_SWITCH_ACTIVE)
3877 ToggleLightSwitch(x, y + 1);
3882 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
3885 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
3887 CheckElementChangeBySide(x, y + 1, smashed, element,
3888 CE_SWITCHED, CH_SIDE_TOP);
3889 CheckTriggeredElementChangeBySide(smashed, CE_SWITCH_OF_X,
3895 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
3900 /* play sound of magic wall / mill */
3902 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3903 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3905 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3906 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3907 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3908 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3913 /* play sound of object that hits the ground */
3914 if (last_line || object_hit)
3915 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3918 inline static void TurnRoundExt(int x, int y)
3930 { 0, 0 }, { 0, 0 }, { 0, 0 },
3935 int left, right, back;
3939 { MV_DOWN, MV_UP, MV_RIGHT },
3940 { MV_UP, MV_DOWN, MV_LEFT },
3942 { MV_LEFT, MV_RIGHT, MV_DOWN },
3946 { MV_RIGHT, MV_LEFT, MV_UP }
3949 int element = Feld[x][y];
3950 int move_pattern = element_info[element].move_pattern;
3952 int old_move_dir = MovDir[x][y];
3953 int left_dir = turn[old_move_dir].left;
3954 int right_dir = turn[old_move_dir].right;
3955 int back_dir = turn[old_move_dir].back;
3957 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3958 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3959 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3960 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3962 int left_x = x + left_dx, left_y = y + left_dy;
3963 int right_x = x + right_dx, right_y = y + right_dy;
3964 int move_x = x + move_dx, move_y = y + move_dy;
3968 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3970 TestIfBadThingTouchesOtherBadThing(x, y);
3972 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
3973 MovDir[x][y] = right_dir;
3974 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3975 MovDir[x][y] = left_dir;
3977 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3979 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3982 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
3984 TestIfBadThingTouchesOtherBadThing(x, y);
3986 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
3987 MovDir[x][y] = left_dir;
3988 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3989 MovDir[x][y] = right_dir;
3991 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
3993 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3996 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3998 TestIfBadThingTouchesOtherBadThing(x, y);
4000 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4001 MovDir[x][y] = left_dir;
4002 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4003 MovDir[x][y] = right_dir;
4005 if (MovDir[x][y] != old_move_dir)
4008 else if (element == EL_YAMYAM)
4010 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4011 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4013 if (can_turn_left && can_turn_right)
4014 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4015 else if (can_turn_left)
4016 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4017 else if (can_turn_right)
4018 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4020 MovDir[x][y] = back_dir;
4022 MovDelay[x][y] = 16 + 16 * RND(3);
4024 else if (element == EL_DARK_YAMYAM)
4026 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4028 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4031 if (can_turn_left && can_turn_right)
4032 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4033 else if (can_turn_left)
4034 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4035 else if (can_turn_right)
4036 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4038 MovDir[x][y] = back_dir;
4040 MovDelay[x][y] = 16 + 16 * RND(3);
4042 else if (element == EL_PACMAN)
4044 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4045 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4047 if (can_turn_left && can_turn_right)
4048 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4049 else if (can_turn_left)
4050 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4051 else if (can_turn_right)
4052 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4054 MovDir[x][y] = back_dir;
4056 MovDelay[x][y] = 6 + RND(40);
4058 else if (element == EL_PIG)
4060 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4061 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4062 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4063 boolean should_turn_left, should_turn_right, should_move_on;
4065 int rnd = RND(rnd_value);
4067 should_turn_left = (can_turn_left &&
4069 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4070 y + back_dy + left_dy)));
4071 should_turn_right = (can_turn_right &&
4073 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4074 y + back_dy + right_dy)));
4075 should_move_on = (can_move_on &&
4078 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4079 y + move_dy + left_dy) ||
4080 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4081 y + move_dy + right_dy)));
4083 if (should_turn_left || should_turn_right || should_move_on)
4085 if (should_turn_left && should_turn_right && should_move_on)
4086 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4087 rnd < 2 * rnd_value / 3 ? right_dir :
4089 else if (should_turn_left && should_turn_right)
4090 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4091 else if (should_turn_left && should_move_on)
4092 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4093 else if (should_turn_right && should_move_on)
4094 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4095 else if (should_turn_left)
4096 MovDir[x][y] = left_dir;
4097 else if (should_turn_right)
4098 MovDir[x][y] = right_dir;
4099 else if (should_move_on)
4100 MovDir[x][y] = old_move_dir;
4102 else if (can_move_on && rnd > rnd_value / 8)
4103 MovDir[x][y] = old_move_dir;
4104 else if (can_turn_left && can_turn_right)
4105 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4106 else if (can_turn_left && rnd > rnd_value / 8)
4107 MovDir[x][y] = left_dir;
4108 else if (can_turn_right && rnd > rnd_value/8)
4109 MovDir[x][y] = right_dir;
4111 MovDir[x][y] = back_dir;
4113 xx = x + move_xy[MovDir[x][y]].x;
4114 yy = y + move_xy[MovDir[x][y]].y;
4116 if (!IN_LEV_FIELD(xx, yy) ||
4117 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4118 MovDir[x][y] = old_move_dir;
4122 else if (element == EL_DRAGON)
4124 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4125 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4126 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4128 int rnd = RND(rnd_value);
4130 if (can_move_on && rnd > rnd_value / 8)
4131 MovDir[x][y] = old_move_dir;
4132 else if (can_turn_left && can_turn_right)
4133 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4134 else if (can_turn_left && rnd > rnd_value / 8)
4135 MovDir[x][y] = left_dir;
4136 else if (can_turn_right && rnd > rnd_value / 8)
4137 MovDir[x][y] = right_dir;
4139 MovDir[x][y] = back_dir;
4141 xx = x + move_xy[MovDir[x][y]].x;
4142 yy = y + move_xy[MovDir[x][y]].y;
4144 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4145 MovDir[x][y] = old_move_dir;
4149 else if (element == EL_MOLE)
4151 boolean can_move_on =
4152 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4153 IS_AMOEBOID(Feld[move_x][move_y]) ||
4154 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4157 boolean can_turn_left =
4158 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4159 IS_AMOEBOID(Feld[left_x][left_y])));
4161 boolean can_turn_right =
4162 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4163 IS_AMOEBOID(Feld[right_x][right_y])));
4165 if (can_turn_left && can_turn_right)
4166 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4167 else if (can_turn_left)
4168 MovDir[x][y] = left_dir;
4170 MovDir[x][y] = right_dir;
4173 if (MovDir[x][y] != old_move_dir)
4176 else if (element == EL_BALLOON)
4178 MovDir[x][y] = game.wind_direction;
4181 else if (element == EL_SPRING)
4183 if (MovDir[x][y] & MV_HORIZONTAL &&
4184 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4185 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4186 MovDir[x][y] = MV_NONE;
4190 else if (element == EL_ROBOT ||
4191 element == EL_SATELLITE ||
4192 element == EL_PENGUIN)
4194 int attr_x = -1, attr_y = -1;
4205 for (i = 0; i < MAX_PLAYERS; i++)
4207 struct PlayerInfo *player = &stored_player[i];
4208 int jx = player->jx, jy = player->jy;
4210 if (!player->active)
4214 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4222 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4223 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4224 game.engine_version < VERSION_IDENT(3,1,0,0)))
4230 if (element == EL_PENGUIN)
4233 static int xy[4][2] =
4241 for (i = 0; i < NUM_DIRECTIONS; i++)
4243 int ex = x + xy[i][0];
4244 int ey = y + xy[i][1];
4246 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4255 MovDir[x][y] = MV_NONE;
4257 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4258 else if (attr_x > x)
4259 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4261 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4262 else if (attr_y > y)
4263 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4265 if (element == EL_ROBOT)
4269 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4270 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4271 Moving2Blocked(x, y, &newx, &newy);
4273 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4274 MovDelay[x][y] = 8 + 8 * !RND(3);
4276 MovDelay[x][y] = 16;
4278 else if (element == EL_PENGUIN)
4284 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4286 boolean first_horiz = RND(2);
4287 int new_move_dir = MovDir[x][y];
4290 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4291 Moving2Blocked(x, y, &newx, &newy);
4293 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4297 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4298 Moving2Blocked(x, y, &newx, &newy);
4300 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4303 MovDir[x][y] = old_move_dir;
4307 else /* (element == EL_SATELLITE) */
4313 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4315 boolean first_horiz = RND(2);
4316 int new_move_dir = MovDir[x][y];
4319 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4320 Moving2Blocked(x, y, &newx, &newy);
4322 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4326 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4327 Moving2Blocked(x, y, &newx, &newy);
4329 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4332 MovDir[x][y] = old_move_dir;
4337 else if (move_pattern == MV_TURNING_LEFT ||
4338 move_pattern == MV_TURNING_RIGHT ||
4339 move_pattern == MV_TURNING_LEFT_RIGHT ||
4340 move_pattern == MV_TURNING_RIGHT_LEFT ||
4341 move_pattern == MV_TURNING_RANDOM ||
4342 move_pattern == MV_ALL_DIRECTIONS)
4344 boolean can_turn_left =
4345 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4346 boolean can_turn_right =
4347 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4349 if (element_info[element].move_stepsize == 0) /* "not moving" */
4352 if (move_pattern == MV_TURNING_LEFT)
4353 MovDir[x][y] = left_dir;
4354 else if (move_pattern == MV_TURNING_RIGHT)
4355 MovDir[x][y] = right_dir;
4356 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4357 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4358 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4359 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4360 else if (move_pattern == MV_TURNING_RANDOM)
4361 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4362 can_turn_right && !can_turn_left ? right_dir :
4363 RND(2) ? left_dir : right_dir);
4364 else if (can_turn_left && can_turn_right)
4365 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4366 else if (can_turn_left)
4367 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4368 else if (can_turn_right)
4369 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4371 MovDir[x][y] = back_dir;
4373 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4375 else if (move_pattern == MV_HORIZONTAL ||
4376 move_pattern == MV_VERTICAL)
4378 if (move_pattern & old_move_dir)
4379 MovDir[x][y] = back_dir;
4380 else if (move_pattern == MV_HORIZONTAL)
4381 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4382 else if (move_pattern == MV_VERTICAL)
4383 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4385 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4387 else if (move_pattern & MV_ANY_DIRECTION)
4389 MovDir[x][y] = move_pattern;
4390 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4392 else if (move_pattern & MV_WIND_DIRECTION)
4394 MovDir[x][y] = game.wind_direction;
4395 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4397 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4399 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4400 MovDir[x][y] = left_dir;
4401 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4402 MovDir[x][y] = right_dir;
4404 if (MovDir[x][y] != old_move_dir)
4405 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4407 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4409 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4410 MovDir[x][y] = right_dir;
4411 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4412 MovDir[x][y] = left_dir;
4414 if (MovDir[x][y] != old_move_dir)
4415 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4417 else if (move_pattern == MV_TOWARDS_PLAYER ||
4418 move_pattern == MV_AWAY_FROM_PLAYER)
4420 int attr_x = -1, attr_y = -1;
4422 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4433 for (i = 0; i < MAX_PLAYERS; i++)
4435 struct PlayerInfo *player = &stored_player[i];
4436 int jx = player->jx, jy = player->jy;
4438 if (!player->active)
4442 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4450 MovDir[x][y] = MV_NONE;
4452 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4453 else if (attr_x > x)
4454 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4456 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4457 else if (attr_y > y)
4458 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4460 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4462 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4464 boolean first_horiz = RND(2);
4465 int new_move_dir = MovDir[x][y];
4467 if (element_info[element].move_stepsize == 0) /* "not moving" */
4469 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
4470 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4476 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4477 Moving2Blocked(x, y, &newx, &newy);
4479 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4483 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4484 Moving2Blocked(x, y, &newx, &newy);
4486 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4489 MovDir[x][y] = old_move_dir;
4492 else if (move_pattern == MV_WHEN_PUSHED ||
4493 move_pattern == MV_WHEN_DROPPED)
4495 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4496 MovDir[x][y] = MV_NONE;
4500 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4502 static int test_xy[7][2] =
4512 static int test_dir[7] =
4522 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4523 int move_preference = -1000000; /* start with very low preference */
4524 int new_move_dir = MV_NONE;
4525 int start_test = RND(4);
4528 for (i = 0; i < NUM_DIRECTIONS; i++)
4530 int move_dir = test_dir[start_test + i];
4531 int move_dir_preference;
4533 xx = x + test_xy[start_test + i][0];
4534 yy = y + test_xy[start_test + i][1];
4536 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4537 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4539 new_move_dir = move_dir;
4544 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4547 move_dir_preference = -1 * RunnerVisit[xx][yy];
4548 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4549 move_dir_preference = PlayerVisit[xx][yy];
4551 if (move_dir_preference > move_preference)
4553 /* prefer field that has not been visited for the longest time */
4554 move_preference = move_dir_preference;
4555 new_move_dir = move_dir;
4557 else if (move_dir_preference == move_preference &&
4558 move_dir == old_move_dir)
4560 /* prefer last direction when all directions are preferred equally */
4561 move_preference = move_dir_preference;
4562 new_move_dir = move_dir;
4566 MovDir[x][y] = new_move_dir;
4567 if (old_move_dir != new_move_dir)
4568 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4572 static void TurnRound(int x, int y)
4574 int direction = MovDir[x][y];
4578 GfxDir[x][y] = MovDir[x][y];
4580 if (direction != MovDir[x][y])
4584 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4587 static boolean JustBeingPushed(int x, int y)
4591 for (i = 0; i < MAX_PLAYERS; i++)
4593 struct PlayerInfo *player = &stored_player[i];
4595 if (player->active && player->is_pushing && player->MovPos)
4597 int next_jx = player->jx + (player->jx - player->last_jx);
4598 int next_jy = player->jy + (player->jy - player->last_jy);
4600 if (x == next_jx && y == next_jy)
4608 void StartMoving(int x, int y)
4610 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4611 int element = Feld[x][y];
4616 if (MovDelay[x][y] == 0)
4617 GfxAction[x][y] = ACTION_DEFAULT;
4619 if (CAN_FALL(element) && y < lev_fieldy - 1)
4621 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4622 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4623 if (JustBeingPushed(x, y))
4626 if (element == EL_QUICKSAND_FULL)
4628 if (IS_FREE(x, y + 1))
4630 InitMovingField(x, y, MV_DOWN);
4631 started_moving = TRUE;
4633 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4634 Store[x][y] = EL_ROCK;
4636 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4638 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4640 if (!MovDelay[x][y])
4641 MovDelay[x][y] = TILEY + 1;
4650 Feld[x][y] = EL_QUICKSAND_EMPTY;
4651 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4652 Store[x][y + 1] = Store[x][y];
4655 PlayLevelSoundAction(x, y, ACTION_FILLING);
4658 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4659 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4661 InitMovingField(x, y, MV_DOWN);
4662 started_moving = TRUE;
4664 Feld[x][y] = EL_QUICKSAND_FILLING;
4665 Store[x][y] = element;
4667 PlayLevelSoundAction(x, y, ACTION_FILLING);
4669 else if (element == EL_MAGIC_WALL_FULL)
4671 if (IS_FREE(x, y + 1))
4673 InitMovingField(x, y, MV_DOWN);
4674 started_moving = TRUE;
4676 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4677 Store[x][y] = EL_CHANGED(Store[x][y]);
4679 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4681 if (!MovDelay[x][y])
4682 MovDelay[x][y] = TILEY/4 + 1;
4691 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4692 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4693 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4697 else if (element == EL_BD_MAGIC_WALL_FULL)
4699 if (IS_FREE(x, y + 1))
4701 InitMovingField(x, y, MV_DOWN);
4702 started_moving = TRUE;
4704 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4705 Store[x][y] = EL_CHANGED2(Store[x][y]);
4707 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4709 if (!MovDelay[x][y])
4710 MovDelay[x][y] = TILEY/4 + 1;
4719 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4720 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4721 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4725 else if (CAN_PASS_MAGIC_WALL(element) &&
4726 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4727 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4729 InitMovingField(x, y, MV_DOWN);
4730 started_moving = TRUE;
4733 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4734 EL_BD_MAGIC_WALL_FILLING);
4735 Store[x][y] = element;
4737 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4739 SplashAcid(x, y + 1);
4741 InitMovingField(x, y, MV_DOWN);
4742 started_moving = TRUE;
4744 Store[x][y] = EL_ACID;
4746 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4747 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
4749 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4750 CAN_FALL(element) && WasJustFalling[x][y] &&
4751 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
4753 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4754 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4755 (Feld[x][y + 1] == EL_BLOCKED)))
4757 /* this is needed for a special case not covered by calling "Impact()"
4758 from "ContinueMoving()": if an element moves to a tile directly below
4759 another element which was just falling on that tile (which was empty
4760 in the previous frame), the falling element above would just stop
4761 instead of smashing the element below (in previous version, the above
4762 element was just checked for "moving" instead of "falling", resulting
4763 in incorrect smashes caused by horizontal movement of the above
4764 element; also, the case of the player being the element to smash was
4765 simply not covered here... :-/ ) */
4767 CheckCollision[x][y] = 0;
4771 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4773 if (MovDir[x][y] == MV_NONE)
4775 InitMovingField(x, y, MV_DOWN);
4776 started_moving = TRUE;
4779 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4781 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4782 MovDir[x][y] = MV_DOWN;
4784 InitMovingField(x, y, MV_DOWN);
4785 started_moving = TRUE;
4787 else if (element == EL_AMOEBA_DROP)
4789 Feld[x][y] = EL_AMOEBA_GROWING;
4790 Store[x][y] = EL_AMOEBA_WET;
4792 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4793 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4794 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4795 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4797 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4798 (IS_FREE(x - 1, y + 1) ||
4799 Feld[x - 1][y + 1] == EL_ACID));
4800 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4801 (IS_FREE(x + 1, y + 1) ||
4802 Feld[x + 1][y + 1] == EL_ACID));
4803 boolean can_fall_any = (can_fall_left || can_fall_right);
4804 boolean can_fall_both = (can_fall_left && can_fall_right);
4805 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4807 #if USE_NEW_ALL_SLIPPERY
4808 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
4810 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4811 can_fall_right = FALSE;
4812 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4813 can_fall_left = FALSE;
4814 else if (slippery_type == SLIPPERY_ONLY_LEFT)
4815 can_fall_right = FALSE;
4816 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4817 can_fall_left = FALSE;
4819 can_fall_any = (can_fall_left || can_fall_right);
4820 can_fall_both = FALSE;
4823 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4825 if (slippery_type == SLIPPERY_ONLY_LEFT)
4826 can_fall_right = FALSE;
4827 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4828 can_fall_left = FALSE;
4829 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4830 can_fall_right = FALSE;
4831 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4832 can_fall_left = FALSE;
4834 can_fall_any = (can_fall_left || can_fall_right);
4835 can_fall_both = (can_fall_left && can_fall_right);
4839 #if USE_NEW_ALL_SLIPPERY
4841 #if USE_NEW_SP_SLIPPERY
4842 /* !!! better use the same properties as for custom elements here !!! */
4843 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
4844 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
4846 can_fall_right = FALSE; /* slip down on left side */
4847 can_fall_both = FALSE;
4852 #if USE_NEW_ALL_SLIPPERY
4855 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
4856 can_fall_right = FALSE; /* slip down on left side */
4858 can_fall_left = !(can_fall_right = RND(2));
4860 can_fall_both = FALSE;
4865 if (game.emulation == EMU_BOULDERDASH ||
4866 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
4867 can_fall_right = FALSE; /* slip down on left side */
4869 can_fall_left = !(can_fall_right = RND(2));
4871 can_fall_both = FALSE;
4877 /* if not determined otherwise, prefer left side for slipping down */
4878 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4879 started_moving = TRUE;
4883 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4885 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4888 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4889 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4890 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4891 int belt_dir = game.belt_dir[belt_nr];
4893 if ((belt_dir == MV_LEFT && left_is_free) ||
4894 (belt_dir == MV_RIGHT && right_is_free))
4896 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4898 InitMovingField(x, y, belt_dir);
4899 started_moving = TRUE;
4901 Pushed[x][y] = TRUE;
4902 Pushed[nextx][y] = TRUE;
4904 GfxAction[x][y] = ACTION_DEFAULT;
4908 MovDir[x][y] = 0; /* if element was moving, stop it */
4913 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4915 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
4917 if (CAN_MOVE(element) && !started_moving)
4920 int move_pattern = element_info[element].move_pattern;
4925 if (MovDir[x][y] == MV_NONE)
4927 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
4928 x, y, element, element_info[element].token_name);
4929 printf("StartMoving(): This should never happen!\n");
4934 Moving2Blocked(x, y, &newx, &newy);
4936 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4939 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4940 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
4942 WasJustMoving[x][y] = 0;
4943 CheckCollision[x][y] = 0;
4945 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4947 if (Feld[x][y] != element) /* element has changed */
4951 if (!MovDelay[x][y]) /* start new movement phase */
4953 /* all objects that can change their move direction after each step
4954 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4956 if (element != EL_YAMYAM &&
4957 element != EL_DARK_YAMYAM &&
4958 element != EL_PACMAN &&
4959 !(move_pattern & MV_ANY_DIRECTION) &&
4960 move_pattern != MV_TURNING_LEFT &&
4961 move_pattern != MV_TURNING_RIGHT &&
4962 move_pattern != MV_TURNING_LEFT_RIGHT &&
4963 move_pattern != MV_TURNING_RIGHT_LEFT &&
4964 move_pattern != MV_TURNING_RANDOM)
4968 if (MovDelay[x][y] && (element == EL_BUG ||
4969 element == EL_SPACESHIP ||
4970 element == EL_SP_SNIKSNAK ||
4971 element == EL_SP_ELECTRON ||
4972 element == EL_MOLE))
4973 DrawLevelField(x, y);
4977 if (MovDelay[x][y]) /* wait some time before next movement */
4981 if (element == EL_ROBOT ||
4982 element == EL_YAMYAM ||
4983 element == EL_DARK_YAMYAM)
4985 DrawLevelElementAnimationIfNeeded(x, y, element);
4986 PlayLevelSoundAction(x, y, ACTION_WAITING);
4988 else if (element == EL_SP_ELECTRON)
4989 DrawLevelElementAnimationIfNeeded(x, y, element);
4990 else if (element == EL_DRAGON)
4993 int dir = MovDir[x][y];
4994 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4995 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4996 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4997 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4998 dir == MV_UP ? IMG_FLAMES_1_UP :
4999 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5000 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5002 GfxAction[x][y] = ACTION_ATTACKING;
5004 if (IS_PLAYER(x, y))
5005 DrawPlayerField(x, y);
5007 DrawLevelField(x, y);
5009 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5011 for (i = 1; i <= 3; i++)
5013 int xx = x + i * dx;
5014 int yy = y + i * dy;
5015 int sx = SCREENX(xx);
5016 int sy = SCREENY(yy);
5017 int flame_graphic = graphic + (i - 1);
5019 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5024 int flamed = MovingOrBlocked2Element(xx, yy);
5028 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5030 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5031 RemoveMovingField(xx, yy);
5033 RemoveField(xx, yy);
5035 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5038 RemoveMovingField(xx, yy);
5041 ChangeDelay[xx][yy] = 0;
5043 Feld[xx][yy] = EL_FLAMES;
5045 if (IN_SCR_FIELD(sx, sy))
5047 DrawLevelFieldCrumbledSand(xx, yy);
5048 DrawGraphic(sx, sy, flame_graphic, frame);
5053 if (Feld[xx][yy] == EL_FLAMES)
5054 Feld[xx][yy] = EL_EMPTY;
5055 DrawLevelField(xx, yy);
5060 if (MovDelay[x][y]) /* element still has to wait some time */
5062 PlayLevelSoundAction(x, y, ACTION_WAITING);
5068 /* now make next step */
5070 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5072 if (DONT_COLLIDE_WITH(element) &&
5073 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5074 !PLAYER_ENEMY_PROTECTED(newx, newy))
5076 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5081 else if (CAN_MOVE_INTO_ACID(element) &&
5082 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5083 (MovDir[x][y] == MV_DOWN ||
5084 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5086 SplashAcid(newx, newy);
5087 Store[x][y] = EL_ACID;
5089 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5091 if (Feld[newx][newy] == EL_EXIT_OPEN)
5094 DrawLevelField(x, y);
5096 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5097 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5098 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5100 local_player->friends_still_needed--;
5101 if (!local_player->friends_still_needed &&
5102 !local_player->GameOver && AllPlayersGone)
5103 local_player->LevelSolved = local_player->GameOver = TRUE;
5107 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5109 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5110 DrawLevelField(newx, newy);
5112 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5114 else if (!IS_FREE(newx, newy))
5116 GfxAction[x][y] = ACTION_WAITING;
5118 if (IS_PLAYER(x, y))
5119 DrawPlayerField(x, y);
5121 DrawLevelField(x, y);
5126 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5128 if (IS_FOOD_PIG(Feld[newx][newy]))
5130 if (IS_MOVING(newx, newy))
5131 RemoveMovingField(newx, newy);
5134 Feld[newx][newy] = EL_EMPTY;
5135 DrawLevelField(newx, newy);
5138 PlayLevelSound(x, y, SND_PIG_DIGGING);
5140 else if (!IS_FREE(newx, newy))
5142 if (IS_PLAYER(x, y))
5143 DrawPlayerField(x, y);
5145 DrawLevelField(x, y);
5150 else if (IS_CUSTOM_ELEMENT(element) &&
5151 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5153 int new_element = Feld[newx][newy];
5155 if (!IS_FREE(newx, newy))
5157 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5158 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5161 /* no element can dig solid indestructible elements */
5162 if (IS_INDESTRUCTIBLE(new_element) &&
5163 !IS_DIGGABLE(new_element) &&
5164 !IS_COLLECTIBLE(new_element))
5167 if (AmoebaNr[newx][newy] &&
5168 (new_element == EL_AMOEBA_FULL ||
5169 new_element == EL_BD_AMOEBA ||
5170 new_element == EL_AMOEBA_GROWING))
5172 AmoebaCnt[AmoebaNr[newx][newy]]--;
5173 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5176 if (IS_MOVING(newx, newy))
5177 RemoveMovingField(newx, newy);
5180 RemoveField(newx, newy);
5181 DrawLevelField(newx, newy);
5184 /* if digged element was about to explode, prevent the explosion */
5185 ExplodeField[newx][newy] = EX_TYPE_NONE;
5187 PlayLevelSoundAction(x, y, action);
5190 Store[newx][newy] = EL_EMPTY;
5191 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5193 int move_leave_element = element_info[element].move_leave_element;
5195 /* this makes it possible to leave the removed element again */
5196 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
5197 new_element : move_leave_element);
5200 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5202 RunnerVisit[x][y] = FrameCounter;
5203 PlayerVisit[x][y] /= 8; /* expire player visit path */
5206 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5208 if (!IS_FREE(newx, newy))
5210 if (IS_PLAYER(x, y))
5211 DrawPlayerField(x, y);
5213 DrawLevelField(x, y);
5219 boolean wanna_flame = !RND(10);
5220 int dx = newx - x, dy = newy - y;
5221 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5222 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5223 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5224 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5225 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5226 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5229 IS_CLASSIC_ENEMY(element1) ||
5230 IS_CLASSIC_ENEMY(element2)) &&
5231 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5232 element1 != EL_FLAMES && element2 != EL_FLAMES)
5234 ResetGfxAnimation(x, y);
5235 GfxAction[x][y] = ACTION_ATTACKING;
5237 if (IS_PLAYER(x, y))
5238 DrawPlayerField(x, y);
5240 DrawLevelField(x, y);
5242 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5244 MovDelay[x][y] = 50;
5248 RemoveField(newx, newy);
5250 Feld[newx][newy] = EL_FLAMES;
5251 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5254 RemoveField(newx1, newy1);
5256 Feld[newx1][newy1] = EL_FLAMES;
5258 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5261 RemoveField(newx2, newy2);
5263 Feld[newx2][newy2] = EL_FLAMES;
5270 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5271 Feld[newx][newy] == EL_DIAMOND)
5273 if (IS_MOVING(newx, newy))
5274 RemoveMovingField(newx, newy);
5277 Feld[newx][newy] = EL_EMPTY;
5278 DrawLevelField(newx, newy);
5281 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5283 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5284 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5286 if (AmoebaNr[newx][newy])
5288 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5289 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5290 Feld[newx][newy] == EL_BD_AMOEBA)
5291 AmoebaCnt[AmoebaNr[newx][newy]]--;
5296 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5298 RemoveMovingField(newx, newy);
5301 if (IS_MOVING(newx, newy))
5303 RemoveMovingField(newx, newy);
5308 Feld[newx][newy] = EL_EMPTY;
5309 DrawLevelField(newx, newy);
5312 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5314 else if ((element == EL_PACMAN || element == EL_MOLE)
5315 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5317 if (AmoebaNr[newx][newy])
5319 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5320 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5321 Feld[newx][newy] == EL_BD_AMOEBA)
5322 AmoebaCnt[AmoebaNr[newx][newy]]--;
5325 if (element == EL_MOLE)
5327 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5328 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5330 ResetGfxAnimation(x, y);
5331 GfxAction[x][y] = ACTION_DIGGING;
5332 DrawLevelField(x, y);
5334 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5336 return; /* wait for shrinking amoeba */
5338 else /* element == EL_PACMAN */
5340 Feld[newx][newy] = EL_EMPTY;
5341 DrawLevelField(newx, newy);
5342 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5345 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5346 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5347 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5349 /* wait for shrinking amoeba to completely disappear */
5352 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5354 /* object was running against a wall */
5359 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
5360 if (move_pattern & MV_ANY_DIRECTION &&
5361 move_pattern == MovDir[x][y])
5363 int blocking_element =
5364 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5366 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5369 element = Feld[x][y]; /* element might have changed */
5373 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5374 DrawLevelElementAnimation(x, y, element);
5376 if (DONT_TOUCH(element))
5377 TestIfBadThingTouchesPlayer(x, y);
5382 InitMovingField(x, y, MovDir[x][y]);
5384 PlayLevelSoundAction(x, y, ACTION_MOVING);
5388 ContinueMoving(x, y);
5391 void ContinueMoving(int x, int y)
5393 int element = Feld[x][y];
5394 int stored = Store[x][y];
5395 struct ElementInfo *ei = &element_info[element];
5396 int direction = MovDir[x][y];
5397 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5398 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5399 int newx = x + dx, newy = y + dy;
5400 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5401 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5402 boolean last_line = (newy == lev_fieldy - 1);
5404 MovPos[x][y] += getElementMoveStepsize(x, y);
5406 if (pushed_by_player) /* special case: moving object pushed by player */
5407 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5409 if (ABS(MovPos[x][y]) < TILEX)
5411 DrawLevelField(x, y);
5413 return; /* element is still moving */
5416 /* element reached destination field */
5418 Feld[x][y] = EL_EMPTY;
5419 Feld[newx][newy] = element;
5420 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5422 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
5424 element = Feld[newx][newy] = EL_ACID;
5426 else if (element == EL_MOLE)
5428 Feld[x][y] = EL_SAND;
5430 DrawLevelFieldCrumbledSandNeighbours(x, y);
5432 else if (element == EL_QUICKSAND_FILLING)
5434 element = Feld[newx][newy] = get_next_element(element);
5435 Store[newx][newy] = Store[x][y];
5437 else if (element == EL_QUICKSAND_EMPTYING)
5439 Feld[x][y] = get_next_element(element);
5440 element = Feld[newx][newy] = Store[x][y];
5442 else if (element == EL_MAGIC_WALL_FILLING)
5444 element = Feld[newx][newy] = get_next_element(element);
5445 if (!game.magic_wall_active)
5446 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5447 Store[newx][newy] = Store[x][y];
5449 else if (element == EL_MAGIC_WALL_EMPTYING)
5451 Feld[x][y] = get_next_element(element);
5452 if (!game.magic_wall_active)
5453 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5454 element = Feld[newx][newy] = Store[x][y];
5456 #if USE_NEW_COLLECT_COUNT
5457 InitField(newx, newy, FALSE);
5460 else if (element == EL_BD_MAGIC_WALL_FILLING)
5462 element = Feld[newx][newy] = get_next_element(element);
5463 if (!game.magic_wall_active)
5464 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5465 Store[newx][newy] = Store[x][y];
5467 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5469 Feld[x][y] = get_next_element(element);
5470 if (!game.magic_wall_active)
5471 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5472 element = Feld[newx][newy] = Store[x][y];
5474 #if USE_NEW_COLLECT_COUNT
5475 InitField(newx, newy, FALSE);
5478 else if (element == EL_AMOEBA_DROPPING)
5480 Feld[x][y] = get_next_element(element);
5481 element = Feld[newx][newy] = Store[x][y];
5483 else if (element == EL_SOKOBAN_OBJECT)
5486 Feld[x][y] = Back[x][y];
5488 if (Back[newx][newy])
5489 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5491 Back[x][y] = Back[newx][newy] = 0;
5494 Store[x][y] = EL_EMPTY;
5499 MovDelay[newx][newy] = 0;
5501 if (CAN_CHANGE(element))
5503 /* copy element change control values to new field */
5504 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5505 ChangePage[newx][newy] = ChangePage[x][y];
5506 Changed[newx][newy] = Changed[x][y];
5507 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5509 #if USE_NEW_COLLECT_COUNT
5510 Count[newx][newy] = Count[x][y];
5514 ChangeDelay[x][y] = 0;
5515 ChangePage[x][y] = -1;
5516 Changed[x][y] = FALSE;
5517 ChangeEvent[x][y] = -1;
5519 #if USE_NEW_COLLECT_COUNT
5523 /* copy animation control values to new field */
5524 GfxFrame[newx][newy] = GfxFrame[x][y];
5525 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5526 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5527 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5529 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5531 /* some elements can leave other elements behind after moving */
5532 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
5533 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
5534 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
5536 int move_leave_element = ei->move_leave_element;
5538 /* this makes it possible to leave the removed element again */
5539 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
5540 ei->move_leave_element == EL_TRIGGER_ELEMENT)
5541 move_leave_element = stored;
5543 Feld[x][y] = move_leave_element;
5545 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
5546 MovDir[x][y] = direction;
5548 InitField(x, y, FALSE);
5550 if (GFX_CRUMBLED(Feld[x][y]))
5551 DrawLevelFieldCrumbledSandNeighbours(x, y);
5553 if (ELEM_IS_PLAYER(move_leave_element))
5554 RelocatePlayer(x, y, move_leave_element);
5557 /* do this after checking for left-behind element */
5558 ResetGfxAnimation(x, y); /* reset animation values for old field */
5560 if (!CAN_MOVE(element) ||
5561 (CAN_FALL(element) && direction == MV_DOWN &&
5562 (element == EL_SPRING ||
5563 element_info[element].move_pattern == MV_WHEN_PUSHED ||
5564 element_info[element].move_pattern == MV_WHEN_DROPPED)))
5565 GfxDir[x][y] = MovDir[newx][newy] = 0;
5567 DrawLevelField(x, y);
5568 DrawLevelField(newx, newy);
5570 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5572 /* prevent pushed element from moving on in pushed direction */
5573 if (pushed_by_player && CAN_MOVE(element) &&
5574 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5575 !(element_info[element].move_pattern & direction))
5576 TurnRound(newx, newy);
5578 /* prevent elements on conveyor belt from moving on in last direction */
5579 if (pushed_by_conveyor && CAN_FALL(element) &&
5580 direction & MV_HORIZONTAL)
5581 MovDir[newx][newy] = 0;
5583 if (!pushed_by_player)
5585 int nextx = newx + dx, nexty = newy + dy;
5586 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
5588 WasJustMoving[newx][newy] = 3;
5590 if (CAN_FALL(element) && direction == MV_DOWN)
5591 WasJustFalling[newx][newy] = 3;
5593 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
5594 CheckCollision[newx][newy] = 2;
5597 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5599 TestIfBadThingTouchesPlayer(newx, newy);
5600 TestIfBadThingTouchesFriend(newx, newy);
5602 if (!IS_CUSTOM_ELEMENT(element))
5603 TestIfBadThingTouchesOtherBadThing(newx, newy);
5605 else if (element == EL_PENGUIN)
5606 TestIfFriendTouchesBadThing(newx, newy);
5608 /* give the player one last chance (one more frame) to move away */
5609 if (CAN_FALL(element) && direction == MV_DOWN &&
5610 (last_line || (!IS_FREE(x, newy + 1) &&
5611 (!IS_PLAYER(x, newy + 1) ||
5612 game.engine_version < VERSION_IDENT(3,1,1,0)))))
5615 if (pushed_by_player && !game.use_change_when_pushing_bug)
5617 int dig_side = MV_DIR_OPPOSITE(direction);
5618 struct PlayerInfo *player = PLAYERINFO(x, y);
5620 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
5621 player->index_bit, dig_side);
5622 CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PUSHES_X,
5623 player->index_bit, dig_side);
5626 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5628 TestIfElementHitsCustomElement(newx, newy, direction);
5629 TestIfPlayerTouchesCustomElement(newx, newy);
5630 TestIfElementTouchesCustomElement(newx, newy);
5633 int AmoebeNachbarNr(int ax, int ay)
5636 int element = Feld[ax][ay];
5638 static int xy[4][2] =
5646 for (i = 0; i < NUM_DIRECTIONS; i++)
5648 int x = ax + xy[i][0];
5649 int y = ay + xy[i][1];
5651 if (!IN_LEV_FIELD(x, y))
5654 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5655 group_nr = AmoebaNr[x][y];
5661 void AmoebenVereinigen(int ax, int ay)
5663 int i, x, y, xx, yy;
5664 int new_group_nr = AmoebaNr[ax][ay];
5665 static int xy[4][2] =
5673 if (new_group_nr == 0)
5676 for (i = 0; i < NUM_DIRECTIONS; i++)
5681 if (!IN_LEV_FIELD(x, y))
5684 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5685 Feld[x][y] == EL_BD_AMOEBA ||
5686 Feld[x][y] == EL_AMOEBA_DEAD) &&
5687 AmoebaNr[x][y] != new_group_nr)
5689 int old_group_nr = AmoebaNr[x][y];
5691 if (old_group_nr == 0)
5694 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5695 AmoebaCnt[old_group_nr] = 0;
5696 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5697 AmoebaCnt2[old_group_nr] = 0;
5699 for (yy = 0; yy < lev_fieldy; yy++)
5701 for (xx = 0; xx < lev_fieldx; xx++)
5703 if (AmoebaNr[xx][yy] == old_group_nr)
5704 AmoebaNr[xx][yy] = new_group_nr;
5711 void AmoebeUmwandeln(int ax, int ay)
5715 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5717 int group_nr = AmoebaNr[ax][ay];
5722 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5723 printf("AmoebeUmwandeln(): This should never happen!\n");
5728 for (y = 0; y < lev_fieldy; y++)
5730 for (x = 0; x < lev_fieldx; x++)
5732 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5735 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5739 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5740 SND_AMOEBA_TURNING_TO_GEM :
5741 SND_AMOEBA_TURNING_TO_ROCK));
5746 static int xy[4][2] =
5754 for (i = 0; i < NUM_DIRECTIONS; i++)
5759 if (!IN_LEV_FIELD(x, y))
5762 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5764 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5765 SND_AMOEBA_TURNING_TO_GEM :
5766 SND_AMOEBA_TURNING_TO_ROCK));
5773 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5776 int group_nr = AmoebaNr[ax][ay];
5777 boolean done = FALSE;
5782 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5783 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5788 for (y = 0; y < lev_fieldy; y++)
5790 for (x = 0; x < lev_fieldx; x++)
5792 if (AmoebaNr[x][y] == group_nr &&
5793 (Feld[x][y] == EL_AMOEBA_DEAD ||
5794 Feld[x][y] == EL_BD_AMOEBA ||
5795 Feld[x][y] == EL_AMOEBA_GROWING))
5798 Feld[x][y] = new_element;
5799 InitField(x, y, FALSE);
5800 DrawLevelField(x, y);
5807 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5808 SND_BD_AMOEBA_TURNING_TO_ROCK :
5809 SND_BD_AMOEBA_TURNING_TO_GEM));
5812 void AmoebeWaechst(int x, int y)
5814 static unsigned long sound_delay = 0;
5815 static unsigned long sound_delay_value = 0;
5817 if (!MovDelay[x][y]) /* start new growing cycle */
5821 if (DelayReached(&sound_delay, sound_delay_value))
5823 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5824 sound_delay_value = 30;
5828 if (MovDelay[x][y]) /* wait some time before growing bigger */
5831 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5833 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5834 6 - MovDelay[x][y]);
5836 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5839 if (!MovDelay[x][y])
5841 Feld[x][y] = Store[x][y];
5843 DrawLevelField(x, y);
5848 void AmoebaDisappearing(int x, int y)
5850 static unsigned long sound_delay = 0;
5851 static unsigned long sound_delay_value = 0;
5853 if (!MovDelay[x][y]) /* start new shrinking cycle */
5857 if (DelayReached(&sound_delay, sound_delay_value))
5858 sound_delay_value = 30;
5861 if (MovDelay[x][y]) /* wait some time before shrinking */
5864 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5866 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5867 6 - MovDelay[x][y]);
5869 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5872 if (!MovDelay[x][y])
5874 Feld[x][y] = EL_EMPTY;
5875 DrawLevelField(x, y);
5877 /* don't let mole enter this field in this cycle;
5878 (give priority to objects falling to this field from above) */
5884 void AmoebeAbleger(int ax, int ay)
5887 int element = Feld[ax][ay];
5888 int graphic = el2img(element);
5889 int newax = ax, neway = ay;
5890 static int xy[4][2] =
5898 if (!level.amoeba_speed)
5900 Feld[ax][ay] = EL_AMOEBA_DEAD;
5901 DrawLevelField(ax, ay);
5905 if (IS_ANIMATED(graphic))
5906 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5908 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5909 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5911 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5914 if (MovDelay[ax][ay])
5918 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5921 int x = ax + xy[start][0];
5922 int y = ay + xy[start][1];
5924 if (!IN_LEV_FIELD(x, y))
5927 if (IS_FREE(x, y) ||
5928 CAN_GROW_INTO(Feld[x][y]) ||
5929 Feld[x][y] == EL_QUICKSAND_EMPTY)
5935 if (newax == ax && neway == ay)
5938 else /* normal or "filled" (BD style) amoeba */
5941 boolean waiting_for_player = FALSE;
5943 for (i = 0; i < NUM_DIRECTIONS; i++)
5945 int j = (start + i) % 4;
5946 int x = ax + xy[j][0];
5947 int y = ay + xy[j][1];
5949 if (!IN_LEV_FIELD(x, y))
5952 if (IS_FREE(x, y) ||
5953 CAN_GROW_INTO(Feld[x][y]) ||
5954 Feld[x][y] == EL_QUICKSAND_EMPTY)
5960 else if (IS_PLAYER(x, y))
5961 waiting_for_player = TRUE;
5964 if (newax == ax && neway == ay) /* amoeba cannot grow */
5966 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
5968 Feld[ax][ay] = EL_AMOEBA_DEAD;
5969 DrawLevelField(ax, ay);
5970 AmoebaCnt[AmoebaNr[ax][ay]]--;
5972 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5974 if (element == EL_AMOEBA_FULL)
5975 AmoebeUmwandeln(ax, ay);
5976 else if (element == EL_BD_AMOEBA)
5977 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5982 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5984 /* amoeba gets larger by growing in some direction */
5986 int new_group_nr = AmoebaNr[ax][ay];
5989 if (new_group_nr == 0)
5991 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5992 printf("AmoebeAbleger(): This should never happen!\n");
5997 AmoebaNr[newax][neway] = new_group_nr;
5998 AmoebaCnt[new_group_nr]++;
5999 AmoebaCnt2[new_group_nr]++;
6001 /* if amoeba touches other amoeba(s) after growing, unify them */
6002 AmoebenVereinigen(newax, neway);
6004 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6006 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6012 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6013 (neway == lev_fieldy - 1 && newax != ax))
6015 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6016 Store[newax][neway] = element;
6018 else if (neway == ay)
6020 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6022 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6026 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6027 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6028 Store[ax][ay] = EL_AMOEBA_DROP;
6029 ContinueMoving(ax, ay);
6033 DrawLevelField(newax, neway);
6036 void Life(int ax, int ay)
6040 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6043 int element = Feld[ax][ay];
6044 int graphic = el2img(element);
6045 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6047 boolean changed = FALSE;
6049 if (IS_ANIMATED(graphic))
6050 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6055 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6056 MovDelay[ax][ay] = life_time;
6058 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6061 if (MovDelay[ax][ay])
6065 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6067 int xx = ax+x1, yy = ay+y1;
6070 if (!IN_LEV_FIELD(xx, yy))
6073 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6075 int x = xx+x2, y = yy+y2;
6077 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6080 if (((Feld[x][y] == element ||
6081 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6083 (IS_FREE(x, y) && Stop[x][y]))
6087 if (xx == ax && yy == ay) /* field in the middle */
6089 if (nachbarn < life_parameter[0] ||
6090 nachbarn > life_parameter[1])
6092 Feld[xx][yy] = EL_EMPTY;
6094 DrawLevelField(xx, yy);
6095 Stop[xx][yy] = TRUE;
6099 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6100 { /* free border field */
6101 if (nachbarn >= life_parameter[2] &&
6102 nachbarn <= life_parameter[3])
6104 Feld[xx][yy] = element;
6105 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6107 DrawLevelField(xx, yy);
6108 Stop[xx][yy] = TRUE;
6115 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6116 SND_GAME_OF_LIFE_GROWING);
6119 static void InitRobotWheel(int x, int y)
6121 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6124 static void RunRobotWheel(int x, int y)
6126 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6129 static void StopRobotWheel(int x, int y)
6131 if (ZX == x && ZY == y)
6135 static void InitTimegateWheel(int x, int y)
6137 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6140 static void RunTimegateWheel(int x, int y)
6142 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6145 void CheckExit(int x, int y)
6147 if (local_player->gems_still_needed > 0 ||
6148 local_player->sokobanfields_still_needed > 0 ||
6149 local_player->lights_still_needed > 0)
6151 int element = Feld[x][y];
6152 int graphic = el2img(element);
6154 if (IS_ANIMATED(graphic))
6155 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6160 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6163 Feld[x][y] = EL_EXIT_OPENING;
6165 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6168 void CheckExitSP(int x, int y)
6170 if (local_player->gems_still_needed > 0)
6172 int element = Feld[x][y];
6173 int graphic = el2img(element);
6175 if (IS_ANIMATED(graphic))
6176 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6181 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6184 Feld[x][y] = EL_SP_EXIT_OPENING;
6186 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6189 static void CloseAllOpenTimegates()
6193 for (y = 0; y < lev_fieldy; y++)
6195 for (x = 0; x < lev_fieldx; x++)
6197 int element = Feld[x][y];
6199 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6201 Feld[x][y] = EL_TIMEGATE_CLOSING;
6203 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6209 void EdelsteinFunkeln(int x, int y)
6211 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6214 if (Feld[x][y] == EL_BD_DIAMOND)
6217 if (MovDelay[x][y] == 0) /* next animation frame */
6218 MovDelay[x][y] = 11 * !SimpleRND(500);
6220 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6224 if (setup.direct_draw && MovDelay[x][y])
6225 SetDrawtoField(DRAW_BUFFERED);
6227 DrawLevelElementAnimation(x, y, Feld[x][y]);
6229 if (MovDelay[x][y] != 0)
6231 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6232 10 - MovDelay[x][y]);
6234 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6236 if (setup.direct_draw)
6240 dest_x = FX + SCREENX(x) * TILEX;
6241 dest_y = FY + SCREENY(y) * TILEY;
6243 BlitBitmap(drawto_field, window,
6244 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6245 SetDrawtoField(DRAW_DIRECT);
6251 void MauerWaechst(int x, int y)
6255 if (!MovDelay[x][y]) /* next animation frame */
6256 MovDelay[x][y] = 3 * delay;
6258 if (MovDelay[x][y]) /* wait some time before next frame */
6262 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6264 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6265 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6267 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6270 if (!MovDelay[x][y])
6272 if (MovDir[x][y] == MV_LEFT)
6274 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6275 DrawLevelField(x - 1, y);
6277 else if (MovDir[x][y] == MV_RIGHT)
6279 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6280 DrawLevelField(x + 1, y);
6282 else if (MovDir[x][y] == MV_UP)
6284 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6285 DrawLevelField(x, y - 1);
6289 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6290 DrawLevelField(x, y + 1);
6293 Feld[x][y] = Store[x][y];
6295 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6296 DrawLevelField(x, y);
6301 void MauerAbleger(int ax, int ay)
6303 int element = Feld[ax][ay];
6304 int graphic = el2img(element);
6305 boolean oben_frei = FALSE, unten_frei = FALSE;
6306 boolean links_frei = FALSE, rechts_frei = FALSE;
6307 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6308 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6309 boolean new_wall = FALSE;
6311 if (IS_ANIMATED(graphic))
6312 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6314 if (!MovDelay[ax][ay]) /* start building new wall */
6315 MovDelay[ax][ay] = 6;
6317 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6320 if (MovDelay[ax][ay])
6324 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6326 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6328 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6330 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6333 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6334 element == EL_EXPANDABLE_WALL_ANY)
6338 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6339 Store[ax][ay-1] = element;
6340 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6341 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6342 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6343 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6348 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6349 Store[ax][ay+1] = element;
6350 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6351 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6352 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6353 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6358 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6359 element == EL_EXPANDABLE_WALL_ANY ||
6360 element == EL_EXPANDABLE_WALL)
6364 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6365 Store[ax-1][ay] = element;
6366 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6367 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6368 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6369 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6375 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6376 Store[ax+1][ay] = element;
6377 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6378 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6379 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6380 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6385 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6386 DrawLevelField(ax, ay);
6388 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6390 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6391 unten_massiv = TRUE;
6392 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6393 links_massiv = TRUE;
6394 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6395 rechts_massiv = TRUE;
6397 if (((oben_massiv && unten_massiv) ||
6398 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6399 element == EL_EXPANDABLE_WALL) &&
6400 ((links_massiv && rechts_massiv) ||
6401 element == EL_EXPANDABLE_WALL_VERTICAL))
6402 Feld[ax][ay] = EL_WALL;
6405 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6408 void CheckForDragon(int x, int y)
6411 boolean dragon_found = FALSE;
6412 static int xy[4][2] =
6420 for (i = 0; i < NUM_DIRECTIONS; i++)
6422 for (j = 0; j < 4; j++)
6424 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6426 if (IN_LEV_FIELD(xx, yy) &&
6427 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6429 if (Feld[xx][yy] == EL_DRAGON)
6430 dragon_found = TRUE;
6439 for (i = 0; i < NUM_DIRECTIONS; i++)
6441 for (j = 0; j < 3; j++)
6443 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6445 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6447 Feld[xx][yy] = EL_EMPTY;
6448 DrawLevelField(xx, yy);
6457 static void InitBuggyBase(int x, int y)
6459 int element = Feld[x][y];
6460 int activating_delay = FRAMES_PER_SECOND / 4;
6463 (element == EL_SP_BUGGY_BASE ?
6464 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6465 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6467 element == EL_SP_BUGGY_BASE_ACTIVE ?
6468 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6471 static void WarnBuggyBase(int x, int y)
6474 static int xy[4][2] =
6482 for (i = 0; i < NUM_DIRECTIONS; i++)
6484 int xx = x + xy[i][0], yy = y + xy[i][1];
6486 if (IS_PLAYER(xx, yy))
6488 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6495 static void InitTrap(int x, int y)
6497 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6500 static void ActivateTrap(int x, int y)
6502 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6505 static void ChangeActiveTrap(int x, int y)
6507 int graphic = IMG_TRAP_ACTIVE;
6509 /* if new animation frame was drawn, correct crumbled sand border */
6510 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6511 DrawLevelFieldCrumbledSand(x, y);
6514 static int getSpecialActionElement(int element, int number, int base_element)
6516 return (element != EL_EMPTY ? element :
6517 number != -1 ? base_element + number - 1 :
6521 static int getModifiedActionNumber(int value_old, int operator, int operand,
6522 int value_min, int value_max)
6524 int value_new = (operator == CA_MODE_SET ? operand :
6525 operator == CA_MODE_ADD ? value_old + operand :
6526 operator == CA_MODE_SUBTRACT ? value_old - operand :
6527 operator == CA_MODE_MULTIPLY ? value_old * operand :
6528 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
6529 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
6532 return (value_new < value_min ? value_min :
6533 value_new > value_max ? value_max :
6537 static void ExecuteCustomElementAction(int x, int y, int element, int page)
6539 struct ElementInfo *ei = &element_info[element];
6540 struct ElementChangeInfo *change = &ei->change_page[page];
6541 int action_type = change->action_type;
6542 int action_mode = change->action_mode;
6543 int action_arg = change->action_arg;
6546 if (!change->has_action)
6549 /* ---------- determine action paramater values ---------- */
6551 int action_arg_element =
6552 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
6553 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
6554 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
6557 int action_arg_number_min =
6558 (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MIN :
6561 int action_arg_number_max =
6562 (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MAX :
6563 action_type == CA_SET_GEMS ? 999 :
6564 action_type == CA_SET_TIME ? 9999 :
6565 action_type == CA_SET_SCORE ? 99999 :
6566 action_type == CA_SET_CE_SCORE ? 9999 :
6567 action_type == CA_SET_CE_COUNT ? 9999 :
6570 int action_arg_number_reset =
6571 (action_type == CA_SET_PLAYER_SPEED ? TILEX/game.initial_move_delay_value :
6572 action_type == CA_SET_GEMS ? level.gems_needed :
6573 action_type == CA_SET_TIME ? level.time :
6574 action_type == CA_SET_SCORE ? 0 :
6575 action_type == CA_SET_CE_SCORE ? 0 :
6576 action_type == CA_SET_CE_COUNT ? ei->collect_count_initial :
6579 int action_arg_number =
6580 (action_arg <= CA_ARG_MAX ? action_arg :
6581 action_arg >= CA_ARG_SPEED_VERY_SLOW &&
6582 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
6583 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
6584 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
6585 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
6586 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
6587 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
6588 #if USE_NEW_COLLECT_COUNT
6589 action_arg == CA_ARG_NUMBER_CE_COUNT ? Count[x][y] :
6591 action_arg == CA_ARG_NUMBER_CE_COUNT ? ei->collect_count_initial :
6593 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) :
6596 int action_arg_number_old =
6597 (action_type == CA_SET_GEMS ? local_player->gems_still_needed :
6598 action_type == CA_SET_TIME ? TimeLeft :
6599 action_type == CA_SET_SCORE ? local_player->score :
6600 action_type == CA_SET_CE_SCORE ? ei->collect_score :
6601 action_type == CA_SET_CE_COUNT ? Count[x][y] :
6604 int action_arg_number_new =
6605 getModifiedActionNumber(action_arg_number_old,
6606 action_mode, action_arg_number,
6607 action_arg_number_min, action_arg_number_max);
6609 int action_arg_player_bits =
6610 (action_arg == CA_ARG_PLAYER_ANY ? PLAYER_BITS_ANY :
6611 action_arg >= CA_ARG_PLAYER_1 &&
6612 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
6613 action_arg >= CA_ARG_1 &&
6614 action_arg <= CA_ARG_PLAYER_4 ? (1 << (action_arg - CA_ARG_1)) :
6615 action_arg_element >= EL_PLAYER_1 &&
6616 action_arg_element <= EL_PLAYER_4 ?
6617 (1 << (action_arg_element - EL_PLAYER_1)) :
6620 int trigger_player_bits =
6621 (change->actual_trigger_player >= EL_PLAYER_1 &&
6622 change->actual_trigger_player <= EL_PLAYER_4 ?
6623 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
6626 /* ---------- execute action ---------- */
6635 case CA_EXIT_PLAYER:
6637 for (i = 0; i < MAX_PLAYERS; i++)
6638 if (action_arg_player_bits & (1 << i))
6639 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
6644 case CA_KILL_PLAYER:
6646 for (i = 0; i < MAX_PLAYERS; i++)
6647 if (action_arg_player_bits & (1 << i))
6648 KillPlayer(&stored_player[i]);
6653 case CA_RESTART_LEVEL:
6655 game.restart_level = TRUE;
6660 case CA_SHOW_ENVELOPE:
6662 int element = getSpecialActionElement(action_arg_element,
6663 action_arg_number, EL_ENVELOPE_1);
6665 if (IS_ENVELOPE(element))
6666 local_player->show_envelope = element;
6673 int element = getSpecialActionElement(action_arg_element,
6674 action_arg_number, EL_KEY_1);
6676 if (IS_KEY(element))
6678 for (i = 0; i < MAX_PLAYERS; i++)
6680 if (trigger_player_bits & (1 << i))
6682 stored_player[i].key[KEY_NR(element)] = TRUE;
6684 DrawGameValue_Keys(stored_player[i].key);
6686 redraw_mask |= REDRAW_DOOR_1;
6696 int element = getSpecialActionElement(action_arg_element,
6697 action_arg_number, EL_KEY_1);
6699 if (IS_KEY(element))
6701 for (i = 0; i < MAX_PLAYERS; i++)
6703 if (trigger_player_bits & (1 << i))
6705 stored_player[i].key[KEY_NR(element)] = FALSE;
6707 DrawGameValue_Keys(stored_player[i].key);
6709 redraw_mask |= REDRAW_DOOR_1;
6719 local_player->gems_still_needed = action_arg_number_new;
6721 DrawGameValue_Emeralds(local_player->gems_still_needed);
6728 if (level.time > 0) /* only modify limited time value */
6730 TimeLeft = action_arg_number_new;
6732 DrawGameValue_Time(TimeLeft);
6734 if (!TimeLeft && setup.time_limit)
6735 for (i = 0; i < MAX_PLAYERS; i++)
6736 KillPlayer(&stored_player[i]);
6744 local_player->score = action_arg_number_new;
6746 DrawGameValue_Score(local_player->score);
6751 case CA_SET_CE_SCORE:
6753 ei->collect_score = action_arg_number_new;
6758 case CA_SET_CE_COUNT:
6760 #if USE_NEW_COLLECT_COUNT
6761 int count_last = Count[x][y];
6763 Count[x][y] = action_arg_number_new;
6766 printf("::: Count == %d\n", Count[x][y]);
6769 if (Count[x][y] == 0 && count_last > 0)
6772 printf("::: CE_COUNT_AT_ZERO\n");
6775 CheckElementChange(x, y, element, EL_UNDEFINED, CE_COUNT_AT_ZERO);
6776 CheckTriggeredElementChange(element, CE_COUNT_AT_ZERO_OF_X);
6784 case CA_SET_PLAYER_SPEED:
6786 for (i = 0; i < MAX_PLAYERS; i++)
6788 if (trigger_player_bits & (1 << i))
6790 int move_stepsize = TILEX / stored_player[i].move_delay_value;
6792 if (action_arg == CA_ARG_SPEED_SLOWER ||
6793 action_arg == CA_ARG_SPEED_FASTER)
6795 action_arg_number = 2;
6796 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
6801 getModifiedActionNumber(move_stepsize,
6804 action_arg_number_min,
6805 action_arg_number_max);
6807 /* make sure that value is power of 2 */
6808 move_stepsize = (1 << log_2(move_stepsize));
6810 /* do no immediately change -- the player might just be moving */
6811 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
6814 printf("::: move_delay_value == %d [%d]\n",
6815 stored_player[i].move_delay_value_next, action_arg_number);
6823 case CA_SET_PLAYER_SPEED:
6825 for (i = 0; i < MAX_PLAYERS; i++)
6827 if (trigger_player_bits & (1 << i))
6829 int move_stepsize = TILEX / stored_player[i].move_delay_value;
6831 if (action_mode == CA_MODE_ADD || action_mode == CA_MODE_SUBTRACT)
6833 /* translate "+" and "-" to "*" and "/" with powers of two */
6834 action_arg_number = 1 << action_arg_number;
6835 action_mode = (action_mode == CA_MODE_ADD ? CA_MODE_MULTIPLY :
6840 getModifiedActionNumber(move_stepsize,
6843 action_arg_number_min,
6844 action_arg_number_max);
6846 /* make sure that value is power of 2 */
6847 move_stepsize = (1 << log_2(move_stepsize));
6849 /* do no immediately change -- the player might just be moving */
6850 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
6853 printf("::: move_delay_value == %d [%d]\n",
6854 stored_player[i].move_delay_value_next, action_arg_number);
6863 case CA_SET_PLAYER_GRAVITY:
6865 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
6866 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
6867 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
6872 case CA_SET_WIND_DIRECTION:
6874 game.wind_direction = (action_arg >= CA_ARG_DIRECTION_NONE &&
6875 action_arg <= CA_ARG_DIRECTION_DOWN ?
6876 action_arg - CA_ARG_DIRECTION :
6877 action_arg == CA_ARG_DIRECTION_TRIGGER ?
6878 MV_DIR_OPPOSITE(change->actual_trigger_side) :
6879 game.wind_direction);
6885 case CA_SET_DYNABOMB_NUMBER:
6887 printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n");
6892 case CA_SET_DYNABOMB_SIZE:
6894 printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n");
6899 case CA_SET_DYNABOMB_POWER:
6901 printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n");
6912 static void ChangeElementNowExt(struct ElementChangeInfo *change,
6913 int x, int y, int target_element)
6915 int previous_move_direction = MovDir[x][y];
6916 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
6917 IS_WALKABLE(Feld[x][y]));
6919 /* check if element under player changes from accessible to unaccessible
6920 (needed for special case of dropping element which then changes) */
6921 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6922 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6930 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6931 RemoveMovingField(x, y);
6935 Feld[x][y] = target_element;
6937 ResetGfxAnimation(x, y);
6938 ResetRandomAnimationValue(x, y);
6940 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6941 MovDir[x][y] = previous_move_direction;
6943 InitField_WithBug1(x, y, FALSE);
6945 DrawLevelField(x, y);
6947 if (GFX_CRUMBLED(Feld[x][y]))
6948 DrawLevelFieldCrumbledSandNeighbours(x, y);
6951 /* "Changed[][]" not set yet to allow "entered by player" change one time */
6952 if (ELEM_IS_PLAYER(target_element))
6953 RelocatePlayer(x, y, target_element);
6956 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
6958 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6961 TestIfBadThingTouchesPlayer(x, y);
6962 TestIfPlayerTouchesCustomElement(x, y);
6963 TestIfElementTouchesCustomElement(x, y);
6966 static boolean ChangeElementNow(int x, int y, int element, int page)
6968 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6970 int old_element = Feld[x][y];
6972 /* always use default change event to prevent running into a loop */
6973 if (ChangeEvent[x][y] == -1)
6974 ChangeEvent[x][y] = CE_DELAY;
6976 if (ChangeEvent[x][y] == CE_DELAY)
6978 /* reset actual trigger element, trigger player and action element */
6979 change->actual_trigger_element = EL_EMPTY;
6980 change->actual_trigger_player = EL_PLAYER_1;
6981 change->actual_trigger_side = CH_SIDE_NONE;
6985 /* do not change any elements that have already changed in this frame */
6989 /* do not change already changed elements with same change event */
6990 if (Changed[x][y] & ChangeEvent[x][y])
6995 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
6997 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7000 if (change->explode)
7007 if (change->use_target_content)
7009 boolean complete_replace = TRUE;
7010 boolean can_replace[3][3];
7013 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7016 boolean is_walkable;
7017 boolean is_diggable;
7018 boolean is_collectible;
7019 boolean is_removable;
7020 boolean is_destructible;
7021 int ex = x + xx - 1;
7022 int ey = y + yy - 1;
7023 int content_element = change->target_content.e[xx][yy];
7026 can_replace[xx][yy] = TRUE;
7028 if (ex == x && ey == y) /* do not check changing element itself */
7031 if (content_element == EL_EMPTY_SPACE)
7033 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7038 if (!IN_LEV_FIELD(ex, ey))
7040 can_replace[xx][yy] = FALSE;
7041 complete_replace = FALSE;
7048 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7049 e = MovingOrBlocked2Element(ex, ey);
7051 is_empty = (IS_FREE(ex, ey) ||
7052 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7054 is_walkable = (is_empty || IS_WALKABLE(e));
7055 is_diggable = (is_empty || IS_DIGGABLE(e));
7056 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7057 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7058 is_removable = (is_diggable || is_collectible);
7060 can_replace[xx][yy] =
7061 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7062 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7063 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7064 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7065 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7066 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7067 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7069 if (!can_replace[xx][yy])
7070 complete_replace = FALSE;
7073 if (!change->only_if_complete || complete_replace)
7075 boolean something_has_changed = FALSE;
7077 if (change->only_if_complete && change->use_random_replace &&
7078 RND(100) < change->random_percentage)
7081 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7083 int ex = x + xx - 1;
7084 int ey = y + yy - 1;
7085 int content_element;
7087 if (can_replace[xx][yy] && (!change->use_random_replace ||
7088 RND(100) < change->random_percentage))
7090 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7091 RemoveMovingField(ex, ey);
7093 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7095 content_element = change->target_content.e[xx][yy];
7096 target_element = GET_TARGET_ELEMENT(content_element, change);
7098 ChangeElementNowExt(change, ex, ey, target_element);
7100 something_has_changed = TRUE;
7102 /* for symmetry reasons, freeze newly created border elements */
7103 if (ex != x || ey != y)
7104 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7108 if (something_has_changed)
7110 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7111 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7117 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7119 ChangeElementNowExt(change, x, y, target_element);
7121 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7122 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7125 /* this uses direct change before indirect change */
7126 CheckTriggeredElementChangeByPage(old_element, CE_CHANGE_OF_X, page);
7131 #if USE_NEW_DELAYED_ACTION
7133 static void ChangeElement(int x, int y, int page)
7135 int element = MovingOrBlocked2Element(x, y);
7136 struct ElementInfo *ei = &element_info[element];
7137 struct ElementChangeInfo *change = &ei->change_page[page];
7140 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
7141 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
7144 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7145 x, y, element, element_info[element].token_name);
7146 printf("ChangeElement(): This should never happen!\n");
7151 /* this can happen with classic bombs on walkable, changing elements */
7152 if (!CAN_CHANGE_OR_HAS_ACTION(element))
7155 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7156 ChangeDelay[x][y] = 0;
7162 if (ChangeDelay[x][y] == 0) /* initialize element change */
7164 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7166 if (change->can_change)
7168 ResetGfxAnimation(x, y);
7169 ResetRandomAnimationValue(x, y);
7171 if (change->pre_change_function)
7172 change->pre_change_function(x, y);
7176 ChangeDelay[x][y]--;
7178 if (ChangeDelay[x][y] != 0) /* continue element change */
7180 if (change->can_change)
7182 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7184 if (IS_ANIMATED(graphic))
7185 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7187 if (change->change_function)
7188 change->change_function(x, y);
7191 else /* finish element change */
7193 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7195 page = ChangePage[x][y];
7196 ChangePage[x][y] = -1;
7198 change = &ei->change_page[page];
7201 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7203 ChangeDelay[x][y] = 1; /* try change after next move step */
7204 ChangePage[x][y] = page; /* remember page to use for change */
7209 if (change->can_change)
7211 if (ChangeElementNow(x, y, element, page))
7213 if (change->post_change_function)
7214 change->post_change_function(x, y);
7218 if (change->has_action)
7219 ExecuteCustomElementAction(x, y, element, page);
7225 static void ChangeElement(int x, int y, int page)
7227 int element = MovingOrBlocked2Element(x, y);
7228 struct ElementInfo *ei = &element_info[element];
7229 struct ElementChangeInfo *change = &ei->change_page[page];
7232 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7235 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7236 x, y, element, element_info[element].token_name);
7237 printf("ChangeElement(): This should never happen!\n");
7242 /* this can happen with classic bombs on walkable, changing elements */
7243 if (!CAN_CHANGE(element))
7246 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7247 ChangeDelay[x][y] = 0;
7253 if (ChangeDelay[x][y] == 0) /* initialize element change */
7255 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7257 ResetGfxAnimation(x, y);
7258 ResetRandomAnimationValue(x, y);
7260 if (change->pre_change_function)
7261 change->pre_change_function(x, y);
7264 ChangeDelay[x][y]--;
7266 if (ChangeDelay[x][y] != 0) /* continue element change */
7268 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7270 if (IS_ANIMATED(graphic))
7271 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7273 if (change->change_function)
7274 change->change_function(x, y);
7276 else /* finish element change */
7278 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7280 page = ChangePage[x][y];
7281 ChangePage[x][y] = -1;
7283 change = &ei->change_page[page];
7286 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7288 ChangeDelay[x][y] = 1; /* try change after next move step */
7289 ChangePage[x][y] = page; /* remember page to use for change */
7294 if (ChangeElementNow(x, y, element, page))
7296 if (change->post_change_function)
7297 change->post_change_function(x, y);
7304 static boolean CheckTriggeredElementChangeExt(int trigger_element,
7310 boolean change_done_any = FALSE;
7311 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7314 if (!(trigger_events[trigger_element][trigger_event]))
7317 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7319 int element = EL_CUSTOM_START + i;
7320 boolean change_done = FALSE;
7323 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7324 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7327 for (p = 0; p < element_info[element].num_change_pages; p++)
7329 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7331 if (change->can_change_or_has_action &&
7332 change->has_event[trigger_event] &&
7333 change->trigger_side & trigger_side &&
7334 change->trigger_player & trigger_player &&
7335 change->trigger_page & trigger_page_bits &&
7336 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7338 change->actual_trigger_element = trigger_element;
7339 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7340 change->actual_trigger_side = trigger_side;
7342 if ((change->can_change && !change_done) || change->has_action)
7346 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7348 if (Feld[x][y] == element)
7350 if (change->can_change && !change_done)
7352 ChangeDelay[x][y] = 1;
7353 ChangeEvent[x][y] = trigger_event;
7354 ChangeElement(x, y, p);
7356 #if USE_NEW_DELAYED_ACTION
7357 else if (change->has_action)
7359 ExecuteCustomElementAction(x, y, element, p);
7360 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7363 if (change->has_action)
7365 ExecuteCustomElementAction(x, y, element, p);
7366 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7372 if (change->can_change)
7375 change_done_any = TRUE;
7382 return change_done_any;
7385 static boolean CheckElementChangeExt(int x, int y,
7387 int trigger_element,
7392 boolean change_done = FALSE;
7395 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7396 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7399 if (Feld[x][y] == EL_BLOCKED)
7401 Blocked2Moving(x, y, &x, &y);
7402 element = Feld[x][y];
7405 if (Feld[x][y] != element) /* check if element has already changed */
7408 for (p = 0; p < element_info[element].num_change_pages; p++)
7410 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7412 boolean check_trigger_element =
7413 (trigger_event == CE_TOUCHING_X ||
7414 trigger_event == CE_HITTING_X ||
7415 trigger_event == CE_HIT_BY_X);
7417 if (change->can_change_or_has_action &&
7418 change->has_event[trigger_event] &&
7419 change->trigger_side & trigger_side &&
7420 change->trigger_player & trigger_player &&
7421 (!check_trigger_element ||
7422 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
7424 change->actual_trigger_element = trigger_element;
7425 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7426 change->actual_trigger_side = trigger_side;
7428 if (change->can_change && !change_done)
7430 ChangeDelay[x][y] = 1;
7431 ChangeEvent[x][y] = trigger_event;
7432 ChangeElement(x, y, p);
7436 #if USE_NEW_DELAYED_ACTION
7437 else if (change->has_action)
7439 ExecuteCustomElementAction(x, y, element, p);
7440 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7443 if (change->has_action)
7445 ExecuteCustomElementAction(x, y, element, p);
7446 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7455 static void PlayPlayerSound(struct PlayerInfo *player)
7457 int jx = player->jx, jy = player->jy;
7458 int element = player->element_nr;
7459 int last_action = player->last_action_waiting;
7460 int action = player->action_waiting;
7462 if (player->is_waiting)
7464 if (action != last_action)
7465 PlayLevelSoundElementAction(jx, jy, element, action);
7467 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7471 if (action != last_action)
7472 StopSound(element_info[element].sound[last_action]);
7474 if (last_action == ACTION_SLEEPING)
7475 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7479 static void PlayAllPlayersSound()
7483 for (i = 0; i < MAX_PLAYERS; i++)
7484 if (stored_player[i].active)
7485 PlayPlayerSound(&stored_player[i]);
7488 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7490 boolean last_waiting = player->is_waiting;
7491 int move_dir = player->MovDir;
7493 player->last_action_waiting = player->action_waiting;
7497 if (!last_waiting) /* not waiting -> waiting */
7499 player->is_waiting = TRUE;
7501 player->frame_counter_bored =
7503 game.player_boring_delay_fixed +
7504 SimpleRND(game.player_boring_delay_random);
7505 player->frame_counter_sleeping =
7507 game.player_sleeping_delay_fixed +
7508 SimpleRND(game.player_sleeping_delay_random);
7510 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7513 if (game.player_sleeping_delay_fixed +
7514 game.player_sleeping_delay_random > 0 &&
7515 player->anim_delay_counter == 0 &&
7516 player->post_delay_counter == 0 &&
7517 FrameCounter >= player->frame_counter_sleeping)
7518 player->is_sleeping = TRUE;
7519 else if (game.player_boring_delay_fixed +
7520 game.player_boring_delay_random > 0 &&
7521 FrameCounter >= player->frame_counter_bored)
7522 player->is_bored = TRUE;
7524 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7525 player->is_bored ? ACTION_BORING :
7528 if (player->is_sleeping)
7530 if (player->num_special_action_sleeping > 0)
7532 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7534 int last_special_action = player->special_action_sleeping;
7535 int num_special_action = player->num_special_action_sleeping;
7536 int special_action =
7537 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7538 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7539 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7540 last_special_action + 1 : ACTION_SLEEPING);
7541 int special_graphic =
7542 el_act_dir2img(player->element_nr, special_action, move_dir);
7544 player->anim_delay_counter =
7545 graphic_info[special_graphic].anim_delay_fixed +
7546 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7547 player->post_delay_counter =
7548 graphic_info[special_graphic].post_delay_fixed +
7549 SimpleRND(graphic_info[special_graphic].post_delay_random);
7551 player->special_action_sleeping = special_action;
7554 if (player->anim_delay_counter > 0)
7556 player->action_waiting = player->special_action_sleeping;
7557 player->anim_delay_counter--;
7559 else if (player->post_delay_counter > 0)
7561 player->post_delay_counter--;
7565 else if (player->is_bored)
7567 if (player->num_special_action_bored > 0)
7569 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7571 int special_action =
7572 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7573 int special_graphic =
7574 el_act_dir2img(player->element_nr, special_action, move_dir);
7576 player->anim_delay_counter =
7577 graphic_info[special_graphic].anim_delay_fixed +
7578 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7579 player->post_delay_counter =
7580 graphic_info[special_graphic].post_delay_fixed +
7581 SimpleRND(graphic_info[special_graphic].post_delay_random);
7583 player->special_action_bored = special_action;
7586 if (player->anim_delay_counter > 0)
7588 player->action_waiting = player->special_action_bored;
7589 player->anim_delay_counter--;
7591 else if (player->post_delay_counter > 0)
7593 player->post_delay_counter--;
7598 else if (last_waiting) /* waiting -> not waiting */
7600 player->is_waiting = FALSE;
7601 player->is_bored = FALSE;
7602 player->is_sleeping = FALSE;
7604 player->frame_counter_bored = -1;
7605 player->frame_counter_sleeping = -1;
7607 player->anim_delay_counter = 0;
7608 player->post_delay_counter = 0;
7610 player->action_waiting = ACTION_DEFAULT;
7612 player->special_action_bored = ACTION_DEFAULT;
7613 player->special_action_sleeping = ACTION_DEFAULT;
7617 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7619 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7620 int left = player_action & JOY_LEFT;
7621 int right = player_action & JOY_RIGHT;
7622 int up = player_action & JOY_UP;
7623 int down = player_action & JOY_DOWN;
7624 int button1 = player_action & JOY_BUTTON_1;
7625 int button2 = player_action & JOY_BUTTON_2;
7626 int dx = (left ? -1 : right ? 1 : 0);
7627 int dy = (up ? -1 : down ? 1 : 0);
7629 if (!player->active || tape.pausing)
7635 snapped = SnapField(player, dx, dy);
7639 dropped = DropElement(player);
7641 moved = MovePlayer(player, dx, dy);
7644 if (tape.single_step && tape.recording && !tape.pausing)
7646 if (button1 || (dropped && !moved))
7648 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7649 SnapField(player, 0, 0); /* stop snapping */
7653 SetPlayerWaiting(player, FALSE);
7655 return player_action;
7659 /* no actions for this player (no input at player's configured device) */
7661 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7662 SnapField(player, 0, 0);
7663 CheckGravityMovementWhenNotMoving(player);
7665 if (player->MovPos == 0)
7666 SetPlayerWaiting(player, TRUE);
7668 if (player->MovPos == 0) /* needed for tape.playing */
7669 player->is_moving = FALSE;
7671 player->is_dropping = FALSE;
7677 void AdvanceFrameAndPlayerCounters(int player_nr)
7681 /* advance frame counters (global frame counter and time frame counter) */
7685 /* advance player counters (counters for move delay, move animation etc.) */
7686 for (i = 0; i < MAX_PLAYERS; i++)
7688 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
7689 int move_delay_value = stored_player[i].move_delay_value;
7690 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
7692 if (!advance_player_counters) /* not all players may be affected */
7695 #if USE_NEW_PLAYER_ANIM
7696 if (move_frames == 0) /* less than one move per game frame */
7698 int stepsize = TILEX / move_delay_value;
7699 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
7700 int count = (stored_player[i].is_moving ?
7701 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
7703 if (count % delay == 0)
7708 stored_player[i].Frame += move_frames;
7710 if (stored_player[i].MovPos != 0)
7711 stored_player[i].StepFrame += move_frames;
7713 if (stored_player[i].move_delay > 0)
7714 stored_player[i].move_delay--;
7716 /* due to bugs in previous versions, counter must count up, not down */
7717 if (stored_player[i].push_delay != -1)
7718 stored_player[i].push_delay++;
7720 if (stored_player[i].drop_delay > 0)
7721 stored_player[i].drop_delay--;
7727 static unsigned long game_frame_delay = 0;
7728 unsigned long game_frame_delay_value;
7729 int magic_wall_x = 0, magic_wall_y = 0;
7730 int i, x, y, element, graphic;
7731 byte *recorded_player_action;
7732 byte summarized_player_action = 0;
7733 byte tape_action[MAX_PLAYERS];
7735 if (game_status != GAME_MODE_PLAYING)
7738 game_frame_delay_value =
7739 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7741 if (tape.playing && tape.warp_forward && !tape.pausing)
7742 game_frame_delay_value = 0;
7744 /* ---------- main game synchronization point ---------- */
7746 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
7748 if (network_playing && !network_player_action_received)
7750 /* try to get network player actions in time */
7752 #if defined(NETWORK_AVALIABLE)
7753 /* last chance to get network player actions without main loop delay */
7757 /* game was quit by network peer */
7758 if (game_status != GAME_MODE_PLAYING)
7761 if (!network_player_action_received)
7762 return; /* failed to get network player actions in time */
7768 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7771 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
7772 if (recorded_player_action == NULL && tape.pausing)
7776 for (i = 0; i < MAX_PLAYERS; i++)
7778 summarized_player_action |= stored_player[i].action;
7780 if (!network_playing)
7781 stored_player[i].effective_action = stored_player[i].action;
7784 #if defined(NETWORK_AVALIABLE)
7785 if (network_playing)
7786 SendToServer_MovePlayer(summarized_player_action);
7789 if (!options.network && !setup.team_mode)
7790 local_player->effective_action = summarized_player_action;
7792 if (recorded_player_action != NULL)
7793 for (i = 0; i < MAX_PLAYERS; i++)
7794 stored_player[i].effective_action = recorded_player_action[i];
7796 for (i = 0; i < MAX_PLAYERS; i++)
7798 tape_action[i] = stored_player[i].effective_action;
7800 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7801 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7804 /* only save actions from input devices, but not programmed actions */
7806 TapeRecordAction(tape_action);
7808 for (i = 0; i < MAX_PLAYERS; i++)
7810 int actual_player_action = stored_player[i].effective_action;
7813 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
7814 - rnd_equinox_tetrachloride 048
7815 - rnd_equinox_tetrachloride_ii 096
7816 - rnd_emanuel_schmieg 002
7817 - doctor_sloan_ww 001, 020
7819 if (stored_player[i].MovPos == 0)
7820 CheckGravityMovement(&stored_player[i]);
7823 /* overwrite programmed action with tape action */
7824 if (stored_player[i].programmed_action)
7825 actual_player_action = stored_player[i].programmed_action;
7828 PlayerActions(&stored_player[i], actual_player_action);
7830 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7832 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7833 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7836 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7839 network_player_action_received = FALSE;
7841 ScrollScreen(NULL, SCROLL_GO_ON);
7843 /* for backwards compatibility, the following code emulates a fixed bug that
7844 occured when pushing elements (causing elements that just made their last
7845 pushing step to already (if possible) make their first falling step in the
7846 same game frame, which is bad); this code is also needed to use the famous
7847 "spring push bug" which is used in older levels and might be wanted to be
7848 used also in newer levels, but in this case the buggy pushing code is only
7849 affecting the "spring" element and no other elements */
7851 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7853 for (i = 0; i < MAX_PLAYERS; i++)
7855 struct PlayerInfo *player = &stored_player[i];
7859 if (player->active && player->is_pushing && player->is_moving &&
7861 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7862 Feld[x][y] == EL_SPRING))
7864 ContinueMoving(x, y);
7866 /* continue moving after pushing (this is actually a bug) */
7867 if (!IS_MOVING(x, y))
7875 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7877 Changed[x][y] = FALSE;
7878 ChangeEvent[x][y] = -1;
7880 /* this must be handled before main playfield loop */
7881 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
7884 if (MovDelay[x][y] <= 0)
7889 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7891 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7892 printf("GameActions(): This should never happen!\n");
7894 ChangePage[x][y] = -1;
7899 if (WasJustMoving[x][y] > 0)
7900 WasJustMoving[x][y]--;
7901 if (WasJustFalling[x][y] > 0)
7902 WasJustFalling[x][y]--;
7903 if (CheckCollision[x][y] > 0)
7904 CheckCollision[x][y]--;
7908 /* reset finished pushing action (not done in ContinueMoving() to allow
7909 continous pushing animation for elements with zero push delay) */
7910 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7912 ResetGfxAnimation(x, y);
7913 DrawLevelField(x, y);
7917 if (IS_BLOCKED(x, y))
7921 Blocked2Moving(x, y, &oldx, &oldy);
7922 if (!IS_MOVING(oldx, oldy))
7924 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7925 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7926 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7927 printf("GameActions(): This should never happen!\n");
7933 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7935 element = Feld[x][y];
7936 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7938 if (graphic_info[graphic].anim_global_sync)
7939 GfxFrame[x][y] = FrameCounter;
7941 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7942 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7943 ResetRandomAnimationValue(x, y);
7945 SetRandomAnimationValue(x, y);
7947 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7949 if (IS_INACTIVE(element))
7951 if (IS_ANIMATED(graphic))
7952 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7957 /* this may take place after moving, so 'element' may have changed */
7958 if (IS_CHANGING(x, y) &&
7959 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7961 int page = element_info[element].event_page_nr[CE_DELAY];
7963 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
7967 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
7971 ChangeElement(x, y, page);
7973 if (CAN_CHANGE(element))
7974 ChangeElement(x, y, page);
7976 if (HAS_ACTION(element))
7977 ExecuteCustomElementAction(x, y, element, page);
7982 element = Feld[x][y];
7983 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7986 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7990 element = Feld[x][y];
7991 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7993 if (IS_ANIMATED(graphic) &&
7996 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7998 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7999 EdelsteinFunkeln(x, y);
8001 else if ((element == EL_ACID ||
8002 element == EL_EXIT_OPEN ||
8003 element == EL_SP_EXIT_OPEN ||
8004 element == EL_SP_TERMINAL ||
8005 element == EL_SP_TERMINAL_ACTIVE ||
8006 element == EL_EXTRA_TIME ||
8007 element == EL_SHIELD_NORMAL ||
8008 element == EL_SHIELD_DEADLY) &&
8009 IS_ANIMATED(graphic))
8010 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8011 else if (IS_MOVING(x, y))
8012 ContinueMoving(x, y);
8013 else if (IS_ACTIVE_BOMB(element))
8014 CheckDynamite(x, y);
8015 else if (element == EL_AMOEBA_GROWING)
8016 AmoebeWaechst(x, y);
8017 else if (element == EL_AMOEBA_SHRINKING)
8018 AmoebaDisappearing(x, y);
8020 #if !USE_NEW_AMOEBA_CODE
8021 else if (IS_AMOEBALIVE(element))
8022 AmoebeAbleger(x, y);
8025 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8027 else if (element == EL_EXIT_CLOSED)
8029 else if (element == EL_SP_EXIT_CLOSED)
8031 else if (element == EL_EXPANDABLE_WALL_GROWING)
8033 else if (element == EL_EXPANDABLE_WALL ||
8034 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8035 element == EL_EXPANDABLE_WALL_VERTICAL ||
8036 element == EL_EXPANDABLE_WALL_ANY)
8038 else if (element == EL_FLAMES)
8039 CheckForDragon(x, y);
8040 else if (element == EL_EXPLOSION)
8041 ; /* drawing of correct explosion animation is handled separately */
8042 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8043 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8045 if (IS_BELT_ACTIVE(element))
8046 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8048 if (game.magic_wall_active)
8050 int jx = local_player->jx, jy = local_player->jy;
8052 /* play the element sound at the position nearest to the player */
8053 if ((element == EL_MAGIC_WALL_FULL ||
8054 element == EL_MAGIC_WALL_ACTIVE ||
8055 element == EL_MAGIC_WALL_EMPTYING ||
8056 element == EL_BD_MAGIC_WALL_FULL ||
8057 element == EL_BD_MAGIC_WALL_ACTIVE ||
8058 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8059 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8067 #if USE_NEW_AMOEBA_CODE
8068 /* new experimental amoeba growth stuff */
8069 if (!(FrameCounter % 8))
8071 static unsigned long random = 1684108901;
8073 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8075 x = RND(lev_fieldx);
8076 y = RND(lev_fieldy);
8077 element = Feld[x][y];
8079 if (!IS_PLAYER(x,y) &&
8080 (element == EL_EMPTY ||
8081 CAN_GROW_INTO(element) ||
8082 element == EL_QUICKSAND_EMPTY ||
8083 element == EL_ACID_SPLASH_LEFT ||
8084 element == EL_ACID_SPLASH_RIGHT))
8086 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8087 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8088 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8089 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8090 Feld[x][y] = EL_AMOEBA_DROP;
8093 random = random * 129 + 1;
8099 if (game.explosions_delayed)
8102 game.explosions_delayed = FALSE;
8104 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8106 element = Feld[x][y];
8108 if (ExplodeField[x][y])
8109 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8110 else if (element == EL_EXPLOSION)
8111 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8113 ExplodeField[x][y] = EX_TYPE_NONE;
8116 game.explosions_delayed = TRUE;
8119 if (game.magic_wall_active)
8121 if (!(game.magic_wall_time_left % 4))
8123 int element = Feld[magic_wall_x][magic_wall_y];
8125 if (element == EL_BD_MAGIC_WALL_FULL ||
8126 element == EL_BD_MAGIC_WALL_ACTIVE ||
8127 element == EL_BD_MAGIC_WALL_EMPTYING)
8128 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8130 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8133 if (game.magic_wall_time_left > 0)
8135 game.magic_wall_time_left--;
8136 if (!game.magic_wall_time_left)
8138 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8140 element = Feld[x][y];
8142 if (element == EL_MAGIC_WALL_ACTIVE ||
8143 element == EL_MAGIC_WALL_FULL)
8145 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8146 DrawLevelField(x, y);
8148 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8149 element == EL_BD_MAGIC_WALL_FULL)
8151 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8152 DrawLevelField(x, y);
8156 game.magic_wall_active = FALSE;
8161 if (game.light_time_left > 0)
8163 game.light_time_left--;
8165 if (game.light_time_left == 0)
8166 RedrawAllLightSwitchesAndInvisibleElements();
8169 if (game.timegate_time_left > 0)
8171 game.timegate_time_left--;
8173 if (game.timegate_time_left == 0)
8174 CloseAllOpenTimegates();
8177 for (i = 0; i < MAX_PLAYERS; i++)
8179 struct PlayerInfo *player = &stored_player[i];
8181 if (SHIELD_ON(player))
8183 if (player->shield_deadly_time_left)
8184 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8185 else if (player->shield_normal_time_left)
8186 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8190 if (TimeFrames >= FRAMES_PER_SECOND)
8195 for (i = 0; i < MAX_PLAYERS; i++)
8197 struct PlayerInfo *player = &stored_player[i];
8199 if (SHIELD_ON(player))
8201 player->shield_normal_time_left--;
8203 if (player->shield_deadly_time_left > 0)
8204 player->shield_deadly_time_left--;
8208 if (!level.use_step_counter)
8216 if (TimeLeft <= 10 && setup.time_limit)
8217 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8219 DrawGameValue_Time(TimeLeft);
8221 if (!TimeLeft && setup.time_limit)
8222 for (i = 0; i < MAX_PLAYERS; i++)
8223 KillPlayer(&stored_player[i]);
8225 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8226 DrawGameValue_Time(TimePlayed);
8229 if (tape.recording || tape.playing)
8230 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8234 PlayAllPlayersSound();
8236 if (options.debug) /* calculate frames per second */
8238 static unsigned long fps_counter = 0;
8239 static int fps_frames = 0;
8240 unsigned long fps_delay_ms = Counter() - fps_counter;
8244 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8246 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8249 fps_counter = Counter();
8252 redraw_mask |= REDRAW_FPS;
8255 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
8257 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8259 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8261 local_player->show_envelope = 0;
8264 /* use random number generator in every frame to make it less predictable */
8265 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8269 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8271 int min_x = x, min_y = y, max_x = x, max_y = y;
8274 for (i = 0; i < MAX_PLAYERS; i++)
8276 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8278 if (!stored_player[i].active || &stored_player[i] == player)
8281 min_x = MIN(min_x, jx);
8282 min_y = MIN(min_y, jy);
8283 max_x = MAX(max_x, jx);
8284 max_y = MAX(max_y, jy);
8287 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8290 static boolean AllPlayersInVisibleScreen()
8294 for (i = 0; i < MAX_PLAYERS; i++)
8296 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8298 if (!stored_player[i].active)
8301 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8308 void ScrollLevel(int dx, int dy)
8310 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8313 BlitBitmap(drawto_field, drawto_field,
8314 FX + TILEX * (dx == -1) - softscroll_offset,
8315 FY + TILEY * (dy == -1) - softscroll_offset,
8316 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8317 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8318 FX + TILEX * (dx == 1) - softscroll_offset,
8319 FY + TILEY * (dy == 1) - softscroll_offset);
8323 x = (dx == 1 ? BX1 : BX2);
8324 for (y = BY1; y <= BY2; y++)
8325 DrawScreenField(x, y);
8330 y = (dy == 1 ? BY1 : BY2);
8331 for (x = BX1; x <= BX2; x++)
8332 DrawScreenField(x, y);
8335 redraw_mask |= REDRAW_FIELD;
8338 static boolean canFallDown(struct PlayerInfo *player)
8340 int jx = player->jx, jy = player->jy;
8342 return (IN_LEV_FIELD(jx, jy + 1) &&
8343 (IS_FREE(jx, jy + 1) ||
8344 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8345 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8346 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8349 static boolean canPassField(int x, int y, int move_dir)
8351 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8352 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8353 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8356 int element = Feld[x][y];
8358 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8359 !CAN_MOVE(element) &&
8360 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8361 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8362 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8365 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8367 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8368 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8369 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8373 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8374 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
8375 (IS_DIGGABLE(Feld[newx][newy]) ||
8376 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8377 canPassField(newx, newy, move_dir)));
8380 static void CheckGravityMovement(struct PlayerInfo *player)
8382 if (game.gravity && !player->programmed_action)
8384 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8385 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8386 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8387 int jx = player->jx, jy = player->jy;
8388 boolean player_is_moving_to_valid_field =
8389 (!player_is_snapping &&
8390 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8391 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8392 boolean player_can_fall_down = canFallDown(player);
8394 if (player_can_fall_down &&
8395 !player_is_moving_to_valid_field)
8396 player->programmed_action = MV_DOWN;
8400 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8402 return CheckGravityMovement(player);
8404 if (game.gravity && !player->programmed_action)
8406 int jx = player->jx, jy = player->jy;
8407 boolean field_under_player_is_free =
8408 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8409 boolean player_is_standing_on_valid_field =
8410 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8411 (IS_WALKABLE(Feld[jx][jy]) &&
8412 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8414 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8415 player->programmed_action = MV_DOWN;
8421 -----------------------------------------------------------------------------
8422 dx, dy: direction (non-diagonal) to try to move the player to
8423 real_dx, real_dy: direction as read from input device (can be diagonal)
8426 boolean MovePlayerOneStep(struct PlayerInfo *player,
8427 int dx, int dy, int real_dx, int real_dy)
8429 int jx = player->jx, jy = player->jy;
8430 int new_jx = jx + dx, new_jy = jy + dy;
8434 if (!player->active || (!dx && !dy))
8435 return MF_NO_ACTION;
8437 player->MovDir = (dx < 0 ? MV_LEFT :
8440 dy > 0 ? MV_DOWN : MV_NONE);
8442 if (!IN_LEV_FIELD(new_jx, new_jy))
8443 return MF_NO_ACTION;
8445 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8446 return MF_NO_ACTION;
8448 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8450 if (DONT_RUN_INTO(element))
8452 if (element == EL_ACID && dx == 0 && dy == 1)
8454 SplashAcid(new_jx, new_jy);
8455 Feld[jx][jy] = EL_PLAYER_1;
8456 InitMovingField(jx, jy, MV_DOWN);
8457 Store[jx][jy] = EL_ACID;
8458 ContinueMoving(jx, jy);
8462 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
8467 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8468 if (can_move != MF_MOVING)
8471 /* check if DigField() has caused relocation of the player */
8472 if (player->jx != jx || player->jy != jy)
8473 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
8475 StorePlayer[jx][jy] = 0;
8476 player->last_jx = jx;
8477 player->last_jy = jy;
8478 player->jx = new_jx;
8479 player->jy = new_jy;
8480 StorePlayer[new_jx][new_jy] = player->element_nr;
8482 if (player->move_delay_value_next != -1)
8484 player->move_delay_value = player->move_delay_value_next;
8485 player->move_delay_value_next = -1;
8489 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8491 player->step_counter++;
8493 PlayerVisit[jx][jy] = FrameCounter;
8495 ScrollPlayer(player, SCROLL_INIT);
8500 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8502 int jx = player->jx, jy = player->jy;
8503 int old_jx = jx, old_jy = jy;
8504 int moved = MF_NO_ACTION;
8506 if (!player->active)
8511 if (player->MovPos == 0)
8513 player->is_moving = FALSE;
8514 player->is_digging = FALSE;
8515 player->is_collecting = FALSE;
8516 player->is_snapping = FALSE;
8517 player->is_pushing = FALSE;
8523 if (player->move_delay > 0)
8526 player->move_delay = -1; /* set to "uninitialized" value */
8528 /* store if player is automatically moved to next field */
8529 player->is_auto_moving = (player->programmed_action != MV_NONE);
8531 /* remove the last programmed player action */
8532 player->programmed_action = 0;
8536 /* should only happen if pre-1.2 tape recordings are played */
8537 /* this is only for backward compatibility */
8539 int original_move_delay_value = player->move_delay_value;
8542 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8546 /* scroll remaining steps with finest movement resolution */
8547 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8549 while (player->MovPos)
8551 ScrollPlayer(player, SCROLL_GO_ON);
8552 ScrollScreen(NULL, SCROLL_GO_ON);
8554 AdvanceFrameAndPlayerCounters(player->index_nr);
8560 player->move_delay_value = original_move_delay_value;
8563 if (player->last_move_dir & MV_HORIZONTAL)
8565 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8566 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8570 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8571 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8577 if (moved & MF_MOVING && !ScreenMovPos &&
8578 (player == local_player || !options.network))
8580 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8581 int offset = (setup.scroll_delay ? 3 : 0);
8583 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8585 /* actual player has left the screen -- scroll in that direction */
8586 if (jx != old_jx) /* player has moved horizontally */
8587 scroll_x += (jx - old_jx);
8588 else /* player has moved vertically */
8589 scroll_y += (jy - old_jy);
8593 if (jx != old_jx) /* player has moved horizontally */
8595 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8596 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8597 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8599 /* don't scroll over playfield boundaries */
8600 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8601 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8603 /* don't scroll more than one field at a time */
8604 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8606 /* don't scroll against the player's moving direction */
8607 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8608 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8609 scroll_x = old_scroll_x;
8611 else /* player has moved vertically */
8613 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8614 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8615 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8617 /* don't scroll over playfield boundaries */
8618 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8619 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8621 /* don't scroll more than one field at a time */
8622 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8624 /* don't scroll against the player's moving direction */
8625 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8626 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8627 scroll_y = old_scroll_y;
8631 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8633 if (!options.network && !AllPlayersInVisibleScreen())
8635 scroll_x = old_scroll_x;
8636 scroll_y = old_scroll_y;
8640 ScrollScreen(player, SCROLL_INIT);
8641 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8646 player->StepFrame = 0;
8648 if (moved & MF_MOVING)
8650 if (old_jx != jx && old_jy == jy)
8651 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8652 else if (old_jx == jx && old_jy != jy)
8653 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8655 DrawLevelField(jx, jy); /* for "crumbled sand" */
8657 player->last_move_dir = player->MovDir;
8658 player->is_moving = TRUE;
8659 player->is_snapping = FALSE;
8660 player->is_switching = FALSE;
8661 player->is_dropping = FALSE;
8665 CheckGravityMovementWhenNotMoving(player);
8667 player->is_moving = FALSE;
8669 /* at this point, the player is allowed to move, but cannot move right now
8670 (e.g. because of something blocking the way) -- ensure that the player
8671 is also allowed to move in the next frame (in old versions before 3.1.1,
8672 the player was forced to wait again for eight frames before next try) */
8674 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8675 player->move_delay = 0; /* allow direct movement in the next frame */
8678 if (player->move_delay == -1) /* not yet initialized by DigField() */
8679 player->move_delay = player->move_delay_value;
8681 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8683 TestIfPlayerTouchesBadThing(jx, jy);
8684 TestIfPlayerTouchesCustomElement(jx, jy);
8687 if (!player->active)
8688 RemovePlayer(player);
8693 void ScrollPlayer(struct PlayerInfo *player, int mode)
8695 int jx = player->jx, jy = player->jy;
8696 int last_jx = player->last_jx, last_jy = player->last_jy;
8697 int move_stepsize = TILEX / player->move_delay_value;
8699 #if USE_NEW_PLAYER_SPEED
8700 if (!player->active)
8703 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
8706 if (!player->active || player->MovPos == 0)
8710 if (mode == SCROLL_INIT)
8712 player->actual_frame_counter = FrameCounter;
8713 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8715 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
8716 Feld[last_jx][last_jy] == EL_EMPTY)
8718 int last_field_block_delay = 0; /* start with no blocking at all */
8719 int block_delay_adjustment = player->block_delay_adjustment;
8721 /* if player blocks last field, add delay for exactly one move */
8722 if (player->block_last_field)
8724 last_field_block_delay += player->move_delay_value;
8726 /* when blocking enabled, prevent moving up despite gravity */
8727 if (game.gravity && player->MovDir == MV_UP)
8728 block_delay_adjustment = -1;
8731 /* add block delay adjustment (also possible when not blocking) */
8732 last_field_block_delay += block_delay_adjustment;
8734 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8735 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
8738 #if USE_NEW_PLAYER_SPEED
8739 if (player->MovPos != 0) /* player has not yet reached destination */
8745 else if (!FrameReached(&player->actual_frame_counter, 1))
8749 printf("::: player->MovPos: %d -> %d\n",
8751 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
8754 #if USE_NEW_PLAYER_SPEED
8755 if (player->MovPos != 0)
8757 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8758 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8760 /* before DrawPlayer() to draw correct player graphic for this case */
8761 if (player->MovPos == 0)
8762 CheckGravityMovement(player);
8765 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8766 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8768 /* before DrawPlayer() to draw correct player graphic for this case */
8769 if (player->MovPos == 0)
8770 CheckGravityMovement(player);
8773 if (player->MovPos == 0) /* player reached destination field */
8776 printf("::: player reached destination field\n");
8779 if (player->move_delay_reset_counter > 0)
8781 player->move_delay_reset_counter--;
8783 if (player->move_delay_reset_counter == 0)
8785 /* continue with normal speed after quickly moving through gate */
8786 HALVE_PLAYER_SPEED(player);
8788 /* be able to make the next move without delay */
8789 player->move_delay = 0;
8793 player->last_jx = jx;
8794 player->last_jy = jy;
8796 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8797 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8798 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8800 DrawPlayer(player); /* needed here only to cleanup last field */
8801 RemovePlayer(player);
8803 if (local_player->friends_still_needed == 0 ||
8804 IS_SP_ELEMENT(Feld[jx][jy]))
8805 player->LevelSolved = player->GameOver = TRUE;
8808 /* this breaks one level: "machine", level 000 */
8810 int move_direction = player->MovDir;
8811 int enter_side = MV_DIR_OPPOSITE(move_direction);
8812 int leave_side = move_direction;
8813 int old_jx = last_jx;
8814 int old_jy = last_jy;
8815 int old_element = Feld[old_jx][old_jy];
8816 int new_element = Feld[jx][jy];
8818 if (IS_CUSTOM_ELEMENT(old_element))
8819 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
8821 player->index_bit, leave_side);
8823 CheckTriggeredElementChangeByPlayer(old_element, CE_PLAYER_LEAVES_X,
8824 player->index_bit, leave_side);
8826 if (IS_CUSTOM_ELEMENT(new_element))
8827 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
8828 player->index_bit, enter_side);
8830 CheckTriggeredElementChangeByPlayer(new_element, CE_PLAYER_ENTERS_X,
8831 player->index_bit, enter_side);
8834 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8836 TestIfPlayerTouchesBadThing(jx, jy);
8837 TestIfPlayerTouchesCustomElement(jx, jy);
8839 /* needed because pushed element has not yet reached its destination,
8840 so it would trigger a change event at its previous field location */
8841 if (!player->is_pushing)
8842 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8844 if (!player->active)
8845 RemovePlayer(player);
8848 if (level.use_step_counter)
8858 if (TimeLeft <= 10 && setup.time_limit)
8859 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8861 DrawGameValue_Time(TimeLeft);
8863 if (!TimeLeft && setup.time_limit)
8864 for (i = 0; i < MAX_PLAYERS; i++)
8865 KillPlayer(&stored_player[i]);
8867 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8868 DrawGameValue_Time(TimePlayed);
8871 if (tape.single_step && tape.recording && !tape.pausing &&
8872 !player->programmed_action)
8873 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8877 void ScrollScreen(struct PlayerInfo *player, int mode)
8879 static unsigned long screen_frame_counter = 0;
8881 if (mode == SCROLL_INIT)
8883 /* set scrolling step size according to actual player's moving speed */
8884 ScrollStepSize = TILEX / player->move_delay_value;
8886 screen_frame_counter = FrameCounter;
8887 ScreenMovDir = player->MovDir;
8888 ScreenMovPos = player->MovPos;
8889 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8892 else if (!FrameReached(&screen_frame_counter, 1))
8897 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8898 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8899 redraw_mask |= REDRAW_FIELD;
8902 ScreenMovDir = MV_NONE;
8905 void TestIfPlayerTouchesCustomElement(int x, int y)
8907 static int xy[4][2] =
8914 static int trigger_sides[4][2] =
8916 /* center side border side */
8917 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8918 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8919 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8920 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8922 static int touch_dir[4] =
8929 int center_element = Feld[x][y]; /* should always be non-moving! */
8932 for (i = 0; i < NUM_DIRECTIONS; i++)
8934 int xx = x + xy[i][0];
8935 int yy = y + xy[i][1];
8936 int center_side = trigger_sides[i][0];
8937 int border_side = trigger_sides[i][1];
8940 if (!IN_LEV_FIELD(xx, yy))
8943 if (IS_PLAYER(x, y))
8945 struct PlayerInfo *player = PLAYERINFO(x, y);
8947 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8948 border_element = Feld[xx][yy]; /* may be moving! */
8949 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8950 border_element = Feld[xx][yy];
8951 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8952 border_element = MovingOrBlocked2Element(xx, yy);
8954 continue; /* center and border element do not touch */
8956 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
8957 player->index_bit, border_side);
8958 CheckTriggeredElementChangeByPlayer(border_element, CE_PLAYER_TOUCHES_X,
8959 player->index_bit, border_side);
8961 else if (IS_PLAYER(xx, yy))
8963 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8965 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8967 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8968 continue; /* center and border element do not touch */
8971 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
8972 player->index_bit, center_side);
8973 CheckTriggeredElementChangeByPlayer(center_element, CE_PLAYER_TOUCHES_X,
8974 player->index_bit, center_side);
8980 void TestIfElementTouchesCustomElement(int x, int y)
8982 static int xy[4][2] =
8989 static int trigger_sides[4][2] =
8991 /* center side border side */
8992 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8993 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8994 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8995 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8997 static int touch_dir[4] =
9004 boolean change_center_element = FALSE;
9005 int center_element = Feld[x][y]; /* should always be non-moving! */
9008 for (i = 0; i < NUM_DIRECTIONS; i++)
9010 int xx = x + xy[i][0];
9011 int yy = y + xy[i][1];
9012 int center_side = trigger_sides[i][0];
9013 int border_side = trigger_sides[i][1];
9016 if (!IN_LEV_FIELD(xx, yy))
9019 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9020 border_element = Feld[xx][yy]; /* may be moving! */
9021 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9022 border_element = Feld[xx][yy];
9023 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9024 border_element = MovingOrBlocked2Element(xx, yy);
9026 continue; /* center and border element do not touch */
9028 /* check for change of center element (but change it only once) */
9029 if (!change_center_element)
9030 change_center_element =
9031 CheckElementChangeBySide(x, y, center_element, border_element,
9032 CE_TOUCHING_X, border_side);
9034 /* check for change of border element */
9035 CheckElementChangeBySide(xx, yy, border_element, center_element,
9036 CE_TOUCHING_X, center_side);
9040 void TestIfElementHitsCustomElement(int x, int y, int direction)
9042 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9043 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9044 int hitx = x + dx, hity = y + dy;
9045 int hitting_element = Feld[x][y];
9046 int touched_element;
9048 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9051 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9052 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9054 if (IN_LEV_FIELD(hitx, hity))
9056 int opposite_direction = MV_DIR_OPPOSITE(direction);
9057 int hitting_side = direction;
9058 int touched_side = opposite_direction;
9059 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9060 MovDir[hitx][hity] != direction ||
9061 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9067 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9068 CE_HITTING_X, touched_side);
9070 CheckElementChangeBySide(hitx, hity, touched_element,
9071 hitting_element, CE_HIT_BY_X, hitting_side);
9073 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9074 CE_HIT_BY_SOMETHING, opposite_direction);
9078 /* "hitting something" is also true when hitting the playfield border */
9079 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9080 CE_HITTING_SOMETHING, direction);
9084 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9086 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9087 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9088 int hitx = x + dx, hity = y + dy;
9089 int hitting_element = Feld[x][y];
9090 int touched_element;
9092 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9093 !IS_FREE(hitx, hity) &&
9094 (!IS_MOVING(hitx, hity) ||
9095 MovDir[hitx][hity] != direction ||
9096 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9099 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9103 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9107 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9108 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9110 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9111 EP_CAN_SMASH_EVERYTHING, direction);
9113 if (IN_LEV_FIELD(hitx, hity))
9115 int opposite_direction = MV_DIR_OPPOSITE(direction);
9116 int hitting_side = direction;
9117 int touched_side = opposite_direction;
9119 int touched_element = MovingOrBlocked2Element(hitx, hity);
9122 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9123 MovDir[hitx][hity] != direction ||
9124 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9133 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9134 CE_SMASHED_BY_SOMETHING, opposite_direction);
9136 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9137 CE_OTHER_IS_SMASHING, touched_side);
9139 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9140 CE_OTHER_GETS_SMASHED, hitting_side);
9146 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9148 int i, kill_x = -1, kill_y = -1;
9149 int bad_element = -1;
9150 static int test_xy[4][2] =
9157 static int test_dir[4] =
9165 for (i = 0; i < NUM_DIRECTIONS; i++)
9167 int test_x, test_y, test_move_dir, test_element;
9169 test_x = good_x + test_xy[i][0];
9170 test_y = good_y + test_xy[i][1];
9172 if (!IN_LEV_FIELD(test_x, test_y))
9176 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9178 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9180 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9181 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9183 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9184 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9188 bad_element = test_element;
9194 if (kill_x != -1 || kill_y != -1)
9196 if (IS_PLAYER(good_x, good_y))
9198 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9200 if (player->shield_deadly_time_left > 0 &&
9201 !IS_INDESTRUCTIBLE(bad_element))
9202 Bang(kill_x, kill_y);
9203 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9207 Bang(good_x, good_y);
9211 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9213 int i, kill_x = -1, kill_y = -1;
9214 int bad_element = Feld[bad_x][bad_y];
9215 static int test_xy[4][2] =
9222 static int touch_dir[4] =
9229 static int test_dir[4] =
9237 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9240 for (i = 0; i < NUM_DIRECTIONS; i++)
9242 int test_x, test_y, test_move_dir, test_element;
9244 test_x = bad_x + test_xy[i][0];
9245 test_y = bad_y + test_xy[i][1];
9246 if (!IN_LEV_FIELD(test_x, test_y))
9250 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9252 test_element = Feld[test_x][test_y];
9254 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9255 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9257 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9258 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9260 /* good thing is player or penguin that does not move away */
9261 if (IS_PLAYER(test_x, test_y))
9263 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9265 if (bad_element == EL_ROBOT && player->is_moving)
9266 continue; /* robot does not kill player if he is moving */
9268 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9270 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9271 continue; /* center and border element do not touch */
9278 else if (test_element == EL_PENGUIN)
9287 if (kill_x != -1 || kill_y != -1)
9289 if (IS_PLAYER(kill_x, kill_y))
9291 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9293 if (player->shield_deadly_time_left > 0 &&
9294 !IS_INDESTRUCTIBLE(bad_element))
9296 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9300 Bang(kill_x, kill_y);
9304 void TestIfPlayerTouchesBadThing(int x, int y)
9306 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9309 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
9311 TestIfGoodThingHitsBadThing(x, y, move_dir);
9314 void TestIfBadThingTouchesPlayer(int x, int y)
9316 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9319 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
9321 TestIfBadThingHitsGoodThing(x, y, move_dir);
9324 void TestIfFriendTouchesBadThing(int x, int y)
9326 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9329 void TestIfBadThingTouchesFriend(int x, int y)
9331 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9334 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9336 int i, kill_x = bad_x, kill_y = bad_y;
9337 static int xy[4][2] =
9345 for (i = 0; i < NUM_DIRECTIONS; i++)
9349 x = bad_x + xy[i][0];
9350 y = bad_y + xy[i][1];
9351 if (!IN_LEV_FIELD(x, y))
9354 element = Feld[x][y];
9355 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9356 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9364 if (kill_x != bad_x || kill_y != bad_y)
9368 void KillPlayer(struct PlayerInfo *player)
9370 int jx = player->jx, jy = player->jy;
9372 if (!player->active)
9375 /* remove accessible field at the player's position */
9376 Feld[jx][jy] = EL_EMPTY;
9378 /* deactivate shield (else Bang()/Explode() would not work right) */
9379 player->shield_normal_time_left = 0;
9380 player->shield_deadly_time_left = 0;
9386 static void KillPlayerUnlessEnemyProtected(int x, int y)
9388 if (!PLAYER_ENEMY_PROTECTED(x, y))
9389 KillPlayer(PLAYERINFO(x, y));
9392 static void KillPlayerUnlessExplosionProtected(int x, int y)
9394 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9395 KillPlayer(PLAYERINFO(x, y));
9398 void BuryPlayer(struct PlayerInfo *player)
9400 int jx = player->jx, jy = player->jy;
9402 if (!player->active)
9405 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9406 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9408 player->GameOver = TRUE;
9409 RemovePlayer(player);
9412 void RemovePlayer(struct PlayerInfo *player)
9414 int jx = player->jx, jy = player->jy;
9415 int i, found = FALSE;
9417 player->present = FALSE;
9418 player->active = FALSE;
9420 if (!ExplodeField[jx][jy])
9421 StorePlayer[jx][jy] = 0;
9423 if (player->is_moving)
9424 DrawLevelField(player->last_jx, player->last_jy);
9426 for (i = 0; i < MAX_PLAYERS; i++)
9427 if (stored_player[i].active)
9431 AllPlayersGone = TRUE;
9438 =============================================================================
9439 checkDiagonalPushing()
9440 -----------------------------------------------------------------------------
9441 check if diagonal input device direction results in pushing of object
9442 (by checking if the alternative direction is walkable, diggable, ...)
9443 =============================================================================
9446 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9447 int x, int y, int real_dx, int real_dy)
9449 int jx, jy, dx, dy, xx, yy;
9451 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9454 /* diagonal direction: check alternative direction */
9459 xx = jx + (dx == 0 ? real_dx : 0);
9460 yy = jy + (dy == 0 ? real_dy : 0);
9462 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9466 =============================================================================
9468 -----------------------------------------------------------------------------
9469 x, y: field next to player (non-diagonal) to try to dig to
9470 real_dx, real_dy: direction as read from input device (can be diagonal)
9471 =============================================================================
9474 int DigField(struct PlayerInfo *player,
9475 int oldx, int oldy, int x, int y,
9476 int real_dx, int real_dy, int mode)
9478 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
9479 boolean player_was_pushing = player->is_pushing;
9480 int jx = oldx, jy = oldy;
9481 int dx = x - jx, dy = y - jy;
9482 int nextx = x + dx, nexty = y + dy;
9483 int move_direction = (dx == -1 ? MV_LEFT :
9484 dx == +1 ? MV_RIGHT :
9486 dy == +1 ? MV_DOWN : MV_NONE);
9487 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9488 int dig_side = MV_DIR_OPPOSITE(move_direction);
9489 int old_element = Feld[jx][jy];
9493 if (is_player) /* function can also be called by EL_PENGUIN */
9495 if (player->MovPos == 0)
9497 player->is_digging = FALSE;
9498 player->is_collecting = FALSE;
9501 if (player->MovPos == 0) /* last pushing move finished */
9502 player->is_pushing = FALSE;
9504 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9506 player->is_switching = FALSE;
9507 player->push_delay = -1;
9509 return MF_NO_ACTION;
9513 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9514 return MF_NO_ACTION;
9516 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9517 old_element = Back[jx][jy];
9519 /* in case of element dropped at player position, check background */
9520 else if (Back[jx][jy] != EL_EMPTY &&
9521 game.engine_version >= VERSION_IDENT(2,2,0,0))
9522 old_element = Back[jx][jy];
9524 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
9525 return MF_NO_ACTION; /* field has no opening in this direction */
9527 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
9528 return MF_NO_ACTION; /* field has no opening in this direction */
9530 element = Feld[x][y];
9531 #if USE_NEW_COLLECT_COUNT
9532 collect_count = Count[x][y];
9534 collect_count = element_info[element].collect_count_initial;
9538 if (element != EL_BLOCKED &&
9539 Count[x][y] != element_info[element].collect_count_initial)
9540 printf("::: %d: %d != %d\n",
9543 element_info[element].collect_count_initial);
9546 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
9547 return MF_NO_ACTION;
9549 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9550 game.engine_version >= VERSION_IDENT(2,2,0,0))
9551 return MF_NO_ACTION;
9553 if (game.gravity && is_player && !player->is_auto_moving &&
9554 canFallDown(player) && move_direction != MV_DOWN &&
9555 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
9556 return MF_NO_ACTION; /* player cannot walk here due to gravity */
9558 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
9560 int sound_element = SND_ELEMENT(element);
9561 int sound_action = ACTION_WALKING;
9563 if (IS_RND_GATE(element))
9565 if (!player->key[RND_GATE_NR(element)])
9566 return MF_NO_ACTION;
9568 else if (IS_RND_GATE_GRAY(element))
9570 if (!player->key[RND_GATE_GRAY_NR(element)])
9571 return MF_NO_ACTION;
9573 else if (element == EL_EXIT_OPEN ||
9574 element == EL_SP_EXIT_OPEN ||
9575 element == EL_SP_EXIT_OPENING)
9577 sound_action = ACTION_PASSING; /* player is passing exit */
9579 else if (element == EL_EMPTY)
9581 sound_action = ACTION_MOVING; /* nothing to walk on */
9584 /* play sound from background or player, whatever is available */
9585 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
9586 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
9588 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9590 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
9592 if (!ACCESS_FROM(element, opposite_direction))
9593 return MF_NO_ACTION; /* field not accessible from this direction */
9595 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9596 return MF_NO_ACTION;
9598 if (IS_EM_GATE(element))
9600 if (!player->key[EM_GATE_NR(element)])
9601 return MF_NO_ACTION;
9603 else if (IS_EM_GATE_GRAY(element))
9605 if (!player->key[EM_GATE_GRAY_NR(element)])
9606 return MF_NO_ACTION;
9608 else if (IS_SP_PORT(element))
9610 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9611 element == EL_SP_GRAVITY_PORT_RIGHT ||
9612 element == EL_SP_GRAVITY_PORT_UP ||
9613 element == EL_SP_GRAVITY_PORT_DOWN)
9614 game.gravity = !game.gravity;
9615 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
9616 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
9617 element == EL_SP_GRAVITY_ON_PORT_UP ||
9618 element == EL_SP_GRAVITY_ON_PORT_DOWN)
9619 game.gravity = TRUE;
9620 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
9621 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
9622 element == EL_SP_GRAVITY_OFF_PORT_UP ||
9623 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
9624 game.gravity = FALSE;
9627 /* automatically move to the next field with double speed */
9628 player->programmed_action = move_direction;
9630 if (player->move_delay_reset_counter == 0)
9632 player->move_delay_reset_counter = 2; /* two double speed steps */
9634 DOUBLE_PLAYER_SPEED(player);
9637 PlayLevelSoundAction(x, y, ACTION_PASSING);
9639 else if (IS_DIGGABLE(element))
9643 if (mode != DF_SNAP)
9645 GfxElement[x][y] = GFX_ELEMENT(element);
9646 player->is_digging = TRUE;
9649 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9651 CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_DIGS_X,
9652 player->index_bit, dig_side);
9654 if (mode == DF_SNAP)
9655 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9657 else if (IS_COLLECTIBLE(element))
9661 if (is_player && mode != DF_SNAP)
9663 GfxElement[x][y] = element;
9664 player->is_collecting = TRUE;
9667 if (element == EL_SPEED_PILL)
9669 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9671 else if (element == EL_EXTRA_TIME && level.time > 0)
9673 TimeLeft += level.extra_time;
9674 DrawGameValue_Time(TimeLeft);
9676 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9678 player->shield_normal_time_left += level.shield_normal_time;
9679 if (element == EL_SHIELD_DEADLY)
9680 player->shield_deadly_time_left += level.shield_deadly_time;
9682 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9684 if (player->inventory_size < MAX_INVENTORY_SIZE)
9685 player->inventory_element[player->inventory_size++] = element;
9687 DrawGameValue_Dynamite(local_player->inventory_size);
9689 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9691 player->dynabomb_count++;
9692 player->dynabombs_left++;
9694 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9696 player->dynabomb_size++;
9698 else if (element == EL_DYNABOMB_INCREASE_POWER)
9700 player->dynabomb_xl = TRUE;
9702 else if (IS_KEY(element))
9704 player->key[KEY_NR(element)] = TRUE;
9706 DrawGameValue_Keys(player->key);
9708 redraw_mask |= REDRAW_DOOR_1;
9710 else if (IS_ENVELOPE(element))
9712 player->show_envelope = element;
9714 else if (IS_DROPPABLE(element) ||
9715 IS_THROWABLE(element)) /* can be collected and dropped */
9719 if (collect_count == 0)
9720 player->inventory_infinite_element = element;
9722 for (i = 0; i < collect_count; i++)
9723 if (player->inventory_size < MAX_INVENTORY_SIZE)
9724 player->inventory_element[player->inventory_size++] = element;
9726 DrawGameValue_Dynamite(local_player->inventory_size);
9728 else if (collect_count > 0)
9730 local_player->gems_still_needed -= collect_count;
9731 if (local_player->gems_still_needed < 0)
9732 local_player->gems_still_needed = 0;
9734 DrawGameValue_Emeralds(local_player->gems_still_needed);
9737 RaiseScoreElement(element);
9738 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9741 CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_COLLECTS_X,
9742 player->index_bit, dig_side);
9744 if (mode == DF_SNAP)
9745 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9747 else if (IS_PUSHABLE(element))
9749 if (mode == DF_SNAP && element != EL_BD_ROCK)
9750 return MF_NO_ACTION;
9752 if (CAN_FALL(element) && dy)
9753 return MF_NO_ACTION;
9755 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9756 !(element == EL_SPRING && level.use_spring_bug))
9757 return MF_NO_ACTION;
9759 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9760 ((move_direction & MV_VERTICAL &&
9761 ((element_info[element].move_pattern & MV_LEFT &&
9762 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9763 (element_info[element].move_pattern & MV_RIGHT &&
9764 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9765 (move_direction & MV_HORIZONTAL &&
9766 ((element_info[element].move_pattern & MV_UP &&
9767 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9768 (element_info[element].move_pattern & MV_DOWN &&
9769 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9770 return MF_NO_ACTION;
9772 /* do not push elements already moving away faster than player */
9773 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9774 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9775 return MF_NO_ACTION;
9777 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9779 if (player->push_delay_value == -1 || !player_was_pushing)
9780 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9782 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9784 if (player->push_delay_value == -1)
9785 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9787 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9789 if (!player->is_pushing)
9790 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9793 player->is_pushing = TRUE;
9795 if (!(IN_LEV_FIELD(nextx, nexty) &&
9796 (IS_FREE(nextx, nexty) ||
9797 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9798 IS_SB_ELEMENT(element)))))
9799 return MF_NO_ACTION;
9801 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9802 return MF_NO_ACTION;
9804 if (player->push_delay == -1) /* new pushing; restart delay */
9805 player->push_delay = 0;
9807 if (player->push_delay < player->push_delay_value &&
9808 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9809 element != EL_SPRING && element != EL_BALLOON)
9811 /* make sure that there is no move delay before next try to push */
9812 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9813 player->move_delay = 0;
9815 return MF_NO_ACTION;
9818 if (IS_SB_ELEMENT(element))
9820 if (element == EL_SOKOBAN_FIELD_FULL)
9822 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9823 local_player->sokobanfields_still_needed++;
9826 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9828 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9829 local_player->sokobanfields_still_needed--;
9832 Feld[x][y] = EL_SOKOBAN_OBJECT;
9834 if (Back[x][y] == Back[nextx][nexty])
9835 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9836 else if (Back[x][y] != 0)
9837 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9840 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9843 if (local_player->sokobanfields_still_needed == 0 &&
9844 game.emulation == EMU_SOKOBAN)
9846 player->LevelSolved = player->GameOver = TRUE;
9847 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9851 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9853 InitMovingField(x, y, move_direction);
9854 GfxAction[x][y] = ACTION_PUSHING;
9856 if (mode == DF_SNAP)
9857 ContinueMoving(x, y);
9859 MovPos[x][y] = (dx != 0 ? dx : dy);
9861 Pushed[x][y] = TRUE;
9862 Pushed[nextx][nexty] = TRUE;
9864 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9865 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9867 player->push_delay_value = -1; /* get new value later */
9869 /* check for element change _after_ element has been pushed */
9870 if (game.use_change_when_pushing_bug)
9872 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
9873 player->index_bit, dig_side);
9874 CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PUSHES_X,
9875 player->index_bit, dig_side);
9878 else if (IS_SWITCHABLE(element))
9880 if (PLAYER_SWITCHING(player, x, y))
9882 CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X,
9883 player->index_bit, dig_side);
9888 player->is_switching = TRUE;
9889 player->switch_x = x;
9890 player->switch_y = y;
9892 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9894 if (element == EL_ROBOT_WHEEL)
9896 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9900 DrawLevelField(x, y);
9902 else if (element == EL_SP_TERMINAL)
9906 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9908 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9910 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9911 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9914 else if (IS_BELT_SWITCH(element))
9916 ToggleBeltSwitch(x, y);
9918 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9919 element == EL_SWITCHGATE_SWITCH_DOWN)
9921 ToggleSwitchgateSwitch(x, y);
9923 else if (element == EL_LIGHT_SWITCH ||
9924 element == EL_LIGHT_SWITCH_ACTIVE)
9926 ToggleLightSwitch(x, y);
9928 else if (element == EL_TIMEGATE_SWITCH)
9930 ActivateTimegateSwitch(x, y);
9932 else if (element == EL_BALLOON_SWITCH_LEFT ||
9933 element == EL_BALLOON_SWITCH_RIGHT ||
9934 element == EL_BALLOON_SWITCH_UP ||
9935 element == EL_BALLOON_SWITCH_DOWN ||
9936 element == EL_BALLOON_SWITCH_NONE ||
9937 element == EL_BALLOON_SWITCH_ANY)
9939 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9940 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9941 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9942 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9943 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
9946 else if (element == EL_LAMP)
9948 Feld[x][y] = EL_LAMP_ACTIVE;
9949 local_player->lights_still_needed--;
9951 ResetGfxAnimation(x, y);
9952 DrawLevelField(x, y);
9954 else if (element == EL_TIME_ORB_FULL)
9956 Feld[x][y] = EL_TIME_ORB_EMPTY;
9957 TimeLeft += level.time_orb_time;
9958 DrawGameValue_Time(TimeLeft);
9960 ResetGfxAnimation(x, y);
9961 DrawLevelField(x, y);
9964 CheckTriggeredElementChangeByPlayer(element, CE_SWITCH_OF_X,
9965 player->index_bit, dig_side);
9967 CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X,
9968 player->index_bit, dig_side);
9974 if (!PLAYER_SWITCHING(player, x, y))
9976 player->is_switching = TRUE;
9977 player->switch_x = x;
9978 player->switch_y = y;
9980 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
9981 player->index_bit, dig_side);
9982 CheckTriggeredElementChangeByPlayer(element, CE_SWITCH_OF_X,
9983 player->index_bit, dig_side);
9986 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
9987 player->index_bit, dig_side);
9988 CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X,
9989 player->index_bit, dig_side);
9991 return MF_NO_ACTION;
9994 player->push_delay = -1;
9996 if (is_player) /* function can also be called by EL_PENGUIN */
9998 if (Feld[x][y] != element) /* really digged/collected something */
9999 player->is_collecting = !player->is_digging;
10005 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10007 int jx = player->jx, jy = player->jy;
10008 int x = jx + dx, y = jy + dy;
10009 int snap_direction = (dx == -1 ? MV_LEFT :
10010 dx == +1 ? MV_RIGHT :
10012 dy == +1 ? MV_DOWN : MV_NONE);
10014 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
10017 if (!player->active || !IN_LEV_FIELD(x, y))
10025 if (player->MovPos == 0)
10026 player->is_pushing = FALSE;
10028 player->is_snapping = FALSE;
10030 if (player->MovPos == 0)
10032 player->is_moving = FALSE;
10033 player->is_digging = FALSE;
10034 player->is_collecting = FALSE;
10040 if (player->is_snapping)
10043 player->MovDir = snap_direction;
10045 if (player->MovPos == 0)
10047 player->is_moving = FALSE;
10048 player->is_digging = FALSE;
10049 player->is_collecting = FALSE;
10052 player->is_dropping = FALSE;
10054 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10057 player->is_snapping = TRUE;
10059 if (player->MovPos == 0)
10061 player->is_moving = FALSE;
10062 player->is_digging = FALSE;
10063 player->is_collecting = FALSE;
10066 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
10067 DrawLevelField(player->last_jx, player->last_jy);
10069 DrawLevelField(x, y);
10074 boolean DropElement(struct PlayerInfo *player)
10076 int old_element, new_element;
10077 int dropx = player->jx, dropy = player->jy;
10078 int drop_direction = player->MovDir;
10079 int drop_side = drop_direction;
10080 int drop_element = (player->inventory_size > 0 ?
10081 player->inventory_element[player->inventory_size - 1] :
10082 player->inventory_infinite_element != EL_UNDEFINED ?
10083 player->inventory_infinite_element :
10084 player->dynabombs_left > 0 ?
10085 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10088 /* do not drop an element on top of another element; when holding drop key
10089 pressed without moving, dropped element must move away before the next
10090 element can be dropped (this is especially important if the next element
10091 is dynamite, which can be placed on background for historical reasons) */
10092 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
10095 if (IS_THROWABLE(drop_element))
10097 dropx += GET_DX_FROM_DIR(drop_direction);
10098 dropy += GET_DY_FROM_DIR(drop_direction);
10100 if (!IN_LEV_FIELD(dropx, dropy))
10104 old_element = Feld[dropx][dropy]; /* old element at dropping position */
10105 new_element = drop_element; /* default: no change when dropping */
10107 /* check if player is active, not moving and ready to drop */
10108 if (!player->active || player->MovPos || player->drop_delay > 0)
10111 /* check if player has anything that can be dropped */
10112 if (new_element == EL_UNDEFINED)
10115 /* check if anything can be dropped at the current position */
10116 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10119 /* collected custom elements can only be dropped on empty fields */
10120 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10123 if (old_element != EL_EMPTY)
10124 Back[dropx][dropy] = old_element; /* store old element on this field */
10126 ResetGfxAnimation(dropx, dropy);
10127 ResetRandomAnimationValue(dropx, dropy);
10129 if (player->inventory_size > 0 ||
10130 player->inventory_infinite_element != EL_UNDEFINED)
10132 if (player->inventory_size > 0)
10134 player->inventory_size--;
10136 DrawGameValue_Dynamite(local_player->inventory_size);
10138 if (new_element == EL_DYNAMITE)
10139 new_element = EL_DYNAMITE_ACTIVE;
10140 else if (new_element == EL_SP_DISK_RED)
10141 new_element = EL_SP_DISK_RED_ACTIVE;
10144 Feld[dropx][dropy] = new_element;
10146 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10147 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10148 el2img(Feld[dropx][dropy]), 0);
10150 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10152 /* needed if previous element just changed to "empty" in the last frame */
10153 Changed[dropx][dropy] = FALSE; /* allow another change */
10155 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
10156 player->index_bit, drop_side);
10157 CheckTriggeredElementChangeByPlayer(new_element, CE_PLAYER_DROPS_X,
10158 player->index_bit, drop_side);
10160 TestIfElementTouchesCustomElement(dropx, dropy);
10162 else /* player is dropping a dyna bomb */
10164 player->dynabombs_left--;
10166 Feld[dropx][dropy] = new_element;
10168 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10169 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10170 el2img(Feld[dropx][dropy]), 0);
10172 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10175 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
10176 InitField_WithBug1(dropx, dropy, FALSE);
10178 new_element = Feld[dropx][dropy]; /* element might have changed */
10180 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10181 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10183 int move_direction, nextx, nexty;
10185 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10186 MovDir[dropx][dropy] = drop_direction;
10188 move_direction = MovDir[dropx][dropy];
10189 nextx = dropx + GET_DX_FROM_DIR(move_direction);
10190 nexty = dropy + GET_DY_FROM_DIR(move_direction);
10192 Changed[dropx][dropy] = FALSE; /* allow another change */
10193 CheckCollision[dropx][dropy] = 2;
10196 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
10197 player->is_dropping = TRUE;
10199 player->drop_x = dropx;
10200 player->drop_y = dropy;
10205 /* ------------------------------------------------------------------------- */
10206 /* game sound playing functions */
10207 /* ------------------------------------------------------------------------- */
10209 static int *loop_sound_frame = NULL;
10210 static int *loop_sound_volume = NULL;
10212 void InitPlayLevelSound()
10214 int num_sounds = getSoundListSize();
10216 checked_free(loop_sound_frame);
10217 checked_free(loop_sound_volume);
10219 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10220 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10223 static void PlayLevelSound(int x, int y, int nr)
10225 int sx = SCREENX(x), sy = SCREENY(y);
10226 int volume, stereo_position;
10227 int max_distance = 8;
10228 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10230 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10231 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10234 if (!IN_LEV_FIELD(x, y) ||
10235 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10236 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10239 volume = SOUND_MAX_VOLUME;
10241 if (!IN_SCR_FIELD(sx, sy))
10243 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10244 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10246 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10249 stereo_position = (SOUND_MAX_LEFT +
10250 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10251 (SCR_FIELDX + 2 * max_distance));
10253 if (IS_LOOP_SOUND(nr))
10255 /* This assures that quieter loop sounds do not overwrite louder ones,
10256 while restarting sound volume comparison with each new game frame. */
10258 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10261 loop_sound_volume[nr] = volume;
10262 loop_sound_frame[nr] = FrameCounter;
10265 PlaySoundExt(nr, volume, stereo_position, type);
10268 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10270 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10271 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10272 y < LEVELY(BY1) ? LEVELY(BY1) :
10273 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10277 static void PlayLevelSoundAction(int x, int y, int action)
10279 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10282 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10284 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10286 if (sound_effect != SND_UNDEFINED)
10287 PlayLevelSound(x, y, sound_effect);
10290 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10293 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10295 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10296 PlayLevelSound(x, y, sound_effect);
10299 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10301 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10303 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10304 PlayLevelSound(x, y, sound_effect);
10307 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10309 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10311 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10312 StopSound(sound_effect);
10315 static void PlayLevelMusic()
10317 if (levelset.music[level_nr] != MUS_UNDEFINED)
10318 PlayMusic(levelset.music[level_nr]); /* from config file */
10320 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10323 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
10325 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
10330 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
10334 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10338 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10342 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10346 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10350 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10354 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10357 case SAMPLE_android_clone:
10358 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10361 case SAMPLE_android_move:
10362 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10365 case SAMPLE_spring:
10366 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10370 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
10374 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
10377 case SAMPLE_eater_eat:
10378 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10382 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10385 case SAMPLE_collect:
10386 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10389 case SAMPLE_diamond:
10390 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10393 case SAMPLE_squash:
10394 /* !!! CHECK THIS !!! */
10396 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10398 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
10402 case SAMPLE_wonderfall:
10403 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
10407 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10411 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10415 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10419 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
10423 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10427 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
10430 case SAMPLE_wonder:
10431 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10435 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10438 case SAMPLE_exit_open:
10439 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
10442 case SAMPLE_exit_leave:
10443 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10446 case SAMPLE_dynamite:
10447 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10451 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10455 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10459 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10463 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
10467 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
10471 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10475 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
10480 void RaiseScore(int value)
10482 local_player->score += value;
10484 DrawGameValue_Score(local_player->score);
10487 void RaiseScoreElement(int element)
10492 case EL_BD_DIAMOND:
10493 case EL_EMERALD_YELLOW:
10494 case EL_EMERALD_RED:
10495 case EL_EMERALD_PURPLE:
10496 case EL_SP_INFOTRON:
10497 RaiseScore(level.score[SC_EMERALD]);
10500 RaiseScore(level.score[SC_DIAMOND]);
10503 RaiseScore(level.score[SC_CRYSTAL]);
10506 RaiseScore(level.score[SC_PEARL]);
10509 case EL_BD_BUTTERFLY:
10510 case EL_SP_ELECTRON:
10511 RaiseScore(level.score[SC_BUG]);
10514 case EL_BD_FIREFLY:
10515 case EL_SP_SNIKSNAK:
10516 RaiseScore(level.score[SC_SPACESHIP]);
10519 case EL_DARK_YAMYAM:
10520 RaiseScore(level.score[SC_YAMYAM]);
10523 RaiseScore(level.score[SC_ROBOT]);
10526 RaiseScore(level.score[SC_PACMAN]);
10529 RaiseScore(level.score[SC_NUT]);
10532 case EL_SP_DISK_RED:
10533 case EL_DYNABOMB_INCREASE_NUMBER:
10534 case EL_DYNABOMB_INCREASE_SIZE:
10535 case EL_DYNABOMB_INCREASE_POWER:
10536 RaiseScore(level.score[SC_DYNAMITE]);
10538 case EL_SHIELD_NORMAL:
10539 case EL_SHIELD_DEADLY:
10540 RaiseScore(level.score[SC_SHIELD]);
10542 case EL_EXTRA_TIME:
10543 RaiseScore(level.score[SC_TIME_BONUS]);
10557 RaiseScore(level.score[SC_KEY]);
10560 RaiseScore(element_info[element].collect_score);
10565 void RequestQuitGame(boolean ask_if_really_quit)
10567 if (AllPlayersGone ||
10568 !ask_if_really_quit ||
10569 level_editor_test_game ||
10570 Request("Do you really want to quit the game ?",
10571 REQ_ASK | REQ_STAY_CLOSED))
10573 #if defined(NETWORK_AVALIABLE)
10574 if (options.network)
10575 SendToServer_StopPlaying();
10579 game_status = GAME_MODE_MAIN;
10585 if (tape.playing && tape.deactivate_display)
10586 TapeDeactivateDisplayOff(TRUE);
10588 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10590 if (tape.playing && tape.deactivate_display)
10591 TapeDeactivateDisplayOn();
10596 /* ---------- new game button stuff ---------------------------------------- */
10598 /* graphic position values for game buttons */
10599 #define GAME_BUTTON_XSIZE 30
10600 #define GAME_BUTTON_YSIZE 30
10601 #define GAME_BUTTON_XPOS 5
10602 #define GAME_BUTTON_YPOS 215
10603 #define SOUND_BUTTON_XPOS 5
10604 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10606 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10607 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10608 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10609 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10610 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10611 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10618 } gamebutton_info[NUM_GAME_BUTTONS] =
10621 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10626 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10627 GAME_CTRL_ID_PAUSE,
10631 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10636 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10637 SOUND_CTRL_ID_MUSIC,
10638 "background music on/off"
10641 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10642 SOUND_CTRL_ID_LOOPS,
10643 "sound loops on/off"
10646 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10647 SOUND_CTRL_ID_SIMPLE,
10648 "normal sounds on/off"
10652 void CreateGameButtons()
10656 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10658 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10659 struct GadgetInfo *gi;
10662 unsigned long event_mask;
10663 int gd_xoffset, gd_yoffset;
10664 int gd_x1, gd_x2, gd_y1, gd_y2;
10667 gd_xoffset = gamebutton_info[i].x;
10668 gd_yoffset = gamebutton_info[i].y;
10669 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10670 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10672 if (id == GAME_CTRL_ID_STOP ||
10673 id == GAME_CTRL_ID_PAUSE ||
10674 id == GAME_CTRL_ID_PLAY)
10676 button_type = GD_TYPE_NORMAL_BUTTON;
10678 event_mask = GD_EVENT_RELEASED;
10679 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10680 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10684 button_type = GD_TYPE_CHECK_BUTTON;
10686 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10687 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10688 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10689 event_mask = GD_EVENT_PRESSED;
10690 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10691 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10694 gi = CreateGadget(GDI_CUSTOM_ID, id,
10695 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10696 GDI_X, DX + gd_xoffset,
10697 GDI_Y, DY + gd_yoffset,
10698 GDI_WIDTH, GAME_BUTTON_XSIZE,
10699 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10700 GDI_TYPE, button_type,
10701 GDI_STATE, GD_BUTTON_UNPRESSED,
10702 GDI_CHECKED, checked,
10703 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10704 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10705 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10706 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10707 GDI_EVENT_MASK, event_mask,
10708 GDI_CALLBACK_ACTION, HandleGameButtons,
10712 Error(ERR_EXIT, "cannot create gadget");
10714 game_gadget[id] = gi;
10718 void FreeGameButtons()
10722 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10723 FreeGadget(game_gadget[i]);
10726 static void MapGameButtons()
10730 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10731 MapGadget(game_gadget[i]);
10734 void UnmapGameButtons()
10738 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10739 UnmapGadget(game_gadget[i]);
10742 static void HandleGameButtons(struct GadgetInfo *gi)
10744 int id = gi->custom_id;
10746 if (game_status != GAME_MODE_PLAYING)
10751 case GAME_CTRL_ID_STOP:
10752 RequestQuitGame(TRUE);
10755 case GAME_CTRL_ID_PAUSE:
10756 if (options.network)
10758 #if defined(NETWORK_AVALIABLE)
10760 SendToServer_ContinuePlaying();
10762 SendToServer_PausePlaying();
10766 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10769 case GAME_CTRL_ID_PLAY:
10772 #if defined(NETWORK_AVALIABLE)
10773 if (options.network)
10774 SendToServer_ContinuePlaying();
10778 tape.pausing = FALSE;
10779 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10784 case SOUND_CTRL_ID_MUSIC:
10785 if (setup.sound_music)
10787 setup.sound_music = FALSE;
10790 else if (audio.music_available)
10792 setup.sound = setup.sound_music = TRUE;
10794 SetAudioMode(setup.sound);
10800 case SOUND_CTRL_ID_LOOPS:
10801 if (setup.sound_loops)
10802 setup.sound_loops = FALSE;
10803 else if (audio.loops_available)
10805 setup.sound = setup.sound_loops = TRUE;
10806 SetAudioMode(setup.sound);
10810 case SOUND_CTRL_ID_SIMPLE:
10811 if (setup.sound_simple)
10812 setup.sound_simple = FALSE;
10813 else if (audio.sound_available)
10815 setup.sound = setup.sound_simple = TRUE;
10816 SetAudioMode(setup.sound);