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_CUSTOM_VALUE (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_NEW_CUSTOM_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
129 RND(element_info[e].ce_value_random_initial))
130 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
131 RND((c)->delay_random * (c)->delay_frames))
133 #define GET_TARGET_ELEMENT(e, ch) \
134 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
135 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
137 #define GET_VALID_PLAYER_ELEMENT(e) \
138 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
140 #define CAN_GROW_INTO(e) \
141 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
143 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
144 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
147 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
148 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
149 (CAN_MOVE_INTO_ACID(e) && \
150 Feld[x][y] == EL_ACID) || \
153 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
154 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
155 (CAN_MOVE_INTO_ACID(e) && \
156 Feld[x][y] == EL_ACID) || \
159 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
160 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
162 (CAN_MOVE_INTO_ACID(e) && \
163 Feld[x][y] == EL_ACID) || \
164 (DONT_COLLIDE_WITH(e) && \
166 !PLAYER_ENEMY_PROTECTED(x, y))))
168 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
169 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
171 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
172 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
174 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
175 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
177 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
178 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
180 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
181 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
183 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
184 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
186 #define PIG_CAN_ENTER_FIELD(e, x, y) \
187 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
189 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
191 IS_FOOD_PENGUIN(Feld[x][y])))
192 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
193 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
195 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
196 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
198 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
199 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
201 #define GROUP_NR(e) ((e) - EL_GROUP_START)
202 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
203 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
204 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
206 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
207 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
209 #define CE_ENTER_FIELD_COND(e, x, y) \
210 (!IS_PLAYER(x, y) && \
211 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
213 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
214 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
216 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
217 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
219 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
220 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
221 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
222 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
224 /* game button identifiers */
225 #define GAME_CTRL_ID_STOP 0
226 #define GAME_CTRL_ID_PAUSE 1
227 #define GAME_CTRL_ID_PLAY 2
228 #define SOUND_CTRL_ID_MUSIC 3
229 #define SOUND_CTRL_ID_LOOPS 4
230 #define SOUND_CTRL_ID_SIMPLE 5
232 #define NUM_GAME_BUTTONS 6
235 /* forward declaration for internal use */
237 static void AdvanceFrameAndPlayerCounters(int);
239 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
240 static boolean MovePlayer(struct PlayerInfo *, int, int);
241 static void ScrollPlayer(struct PlayerInfo *, int);
242 static void ScrollScreen(struct PlayerInfo *, int);
244 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
246 static void InitBeltMovement(void);
247 static void CloseAllOpenTimegates(void);
248 static void CheckGravityMovement(struct PlayerInfo *);
249 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
250 static void KillPlayerUnlessEnemyProtected(int, int);
251 static void KillPlayerUnlessExplosionProtected(int, int);
253 static void TestIfPlayerTouchesCustomElement(int, int);
254 static void TestIfElementTouchesCustomElement(int, int);
255 static void TestIfElementHitsCustomElement(int, int, int);
257 static void TestIfElementSmashesCustomElement(int, int, int);
260 static void ChangeElement(int, int, int);
262 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
263 #define CheckTriggeredElementChange(x, y, e, ev) \
264 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
265 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
266 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
267 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
268 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
269 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
270 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
272 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
273 #define CheckElementChange(x, y, e, te, ev) \
274 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
275 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
276 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
277 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
278 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
280 static void PlayLevelSound(int, int, int);
281 static void PlayLevelSoundNearest(int, int, int);
282 static void PlayLevelSoundAction(int, int, int);
283 static void PlayLevelSoundElementAction(int, int, int, int);
284 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
285 static void PlayLevelSoundActionIfLoop(int, int, int);
286 static void StopLevelSoundActionIfLoop(int, int, int);
287 static void PlayLevelMusic();
289 static void MapGameButtons();
290 static void HandleGameButtons(struct GadgetInfo *);
292 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
295 /* ------------------------------------------------------------------------- */
296 /* definition of elements that automatically change to other elements after */
297 /* a specified time, eventually calling a function when changing */
298 /* ------------------------------------------------------------------------- */
300 /* forward declaration for changer functions */
301 static void InitBuggyBase(int x, int y);
302 static void WarnBuggyBase(int x, int y);
304 static void InitTrap(int x, int y);
305 static void ActivateTrap(int x, int y);
306 static void ChangeActiveTrap(int x, int y);
308 static void InitRobotWheel(int x, int y);
309 static void RunRobotWheel(int x, int y);
310 static void StopRobotWheel(int x, int y);
312 static void InitTimegateWheel(int x, int y);
313 static void RunTimegateWheel(int x, int y);
315 struct ChangingElementInfo
320 void (*pre_change_function)(int x, int y);
321 void (*change_function)(int x, int y);
322 void (*post_change_function)(int x, int y);
325 static struct ChangingElementInfo change_delay_list[] =
376 EL_SWITCHGATE_OPENING,
384 EL_SWITCHGATE_CLOSING,
385 EL_SWITCHGATE_CLOSED,
417 EL_ACID_SPLASH_RIGHT,
426 EL_SP_BUGGY_BASE_ACTIVATING,
433 EL_SP_BUGGY_BASE_ACTIVATING,
434 EL_SP_BUGGY_BASE_ACTIVE,
441 EL_SP_BUGGY_BASE_ACTIVE,
465 EL_ROBOT_WHEEL_ACTIVE,
473 EL_TIMEGATE_SWITCH_ACTIVE,
494 int push_delay_fixed, push_delay_random;
499 { EL_BALLOON, 0, 0 },
501 { EL_SOKOBAN_OBJECT, 2, 0 },
502 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
503 { EL_SATELLITE, 2, 0 },
504 { EL_SP_DISK_YELLOW, 2, 0 },
506 { EL_UNDEFINED, 0, 0 },
514 move_stepsize_list[] =
516 { EL_AMOEBA_DROP, 2 },
517 { EL_AMOEBA_DROPPING, 2 },
518 { EL_QUICKSAND_FILLING, 1 },
519 { EL_QUICKSAND_EMPTYING, 1 },
520 { EL_MAGIC_WALL_FILLING, 2 },
521 { EL_BD_MAGIC_WALL_FILLING, 2 },
522 { EL_MAGIC_WALL_EMPTYING, 2 },
523 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
533 collect_count_list[] =
536 { EL_BD_DIAMOND, 1 },
537 { EL_EMERALD_YELLOW, 1 },
538 { EL_EMERALD_RED, 1 },
539 { EL_EMERALD_PURPLE, 1 },
541 { EL_SP_INFOTRON, 1 },
553 access_direction_list[] =
555 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
556 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
557 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
558 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
559 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
560 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
561 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
562 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
563 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
564 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
565 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
567 { EL_SP_PORT_LEFT, MV_RIGHT },
568 { EL_SP_PORT_RIGHT, MV_LEFT },
569 { EL_SP_PORT_UP, MV_DOWN },
570 { EL_SP_PORT_DOWN, MV_UP },
571 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
572 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
573 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
574 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
575 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
576 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
577 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
578 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
579 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
580 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
581 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
582 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
583 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
584 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
585 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
587 { EL_UNDEFINED, MV_NONE }
590 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
592 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
593 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
594 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
595 IS_JUST_CHANGING(x, y))
597 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
600 void GetPlayerConfig()
602 if (!audio.sound_available)
603 setup.sound_simple = FALSE;
605 if (!audio.loops_available)
606 setup.sound_loops = FALSE;
608 if (!audio.music_available)
609 setup.sound_music = FALSE;
611 if (!video.fullscreen_available)
612 setup.fullscreen = FALSE;
614 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
616 SetAudioMode(setup.sound);
620 static int getBeltNrFromBeltElement(int element)
622 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
623 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
624 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
627 static int getBeltNrFromBeltActiveElement(int element)
629 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
630 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
631 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
634 static int getBeltNrFromBeltSwitchElement(int element)
636 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
637 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
638 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
641 static int getBeltDirNrFromBeltSwitchElement(int element)
643 static int belt_base_element[4] =
645 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
646 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
647 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
648 EL_CONVEYOR_BELT_4_SWITCH_LEFT
651 int belt_nr = getBeltNrFromBeltSwitchElement(element);
652 int belt_dir_nr = element - belt_base_element[belt_nr];
654 return (belt_dir_nr % 3);
657 static int getBeltDirFromBeltSwitchElement(int element)
659 static int belt_move_dir[3] =
666 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
668 return belt_move_dir[belt_dir_nr];
671 static void InitPlayerField(int x, int y, int element, boolean init_game)
673 if (element == EL_SP_MURPHY)
677 if (stored_player[0].present)
679 Feld[x][y] = EL_SP_MURPHY_CLONE;
685 stored_player[0].use_murphy_graphic = TRUE;
688 Feld[x][y] = EL_PLAYER_1;
694 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
695 int jx = player->jx, jy = player->jy;
697 player->present = TRUE;
699 player->block_last_field = (element == EL_SP_MURPHY ?
700 level.sp_block_last_field :
701 level.block_last_field);
703 /* ---------- initialize player's last field block delay --------------- */
705 /* always start with reliable default value (no adjustment needed) */
706 player->block_delay_adjustment = 0;
708 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
709 if (player->block_last_field && element == EL_SP_MURPHY)
710 player->block_delay_adjustment = 1;
712 /* special case 2: in game engines before 3.1.1, blocking was different */
713 if (game.use_block_last_field_bug)
714 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
716 if (!options.network || player->connected)
718 player->active = TRUE;
720 /* remove potentially duplicate players */
721 if (StorePlayer[jx][jy] == Feld[x][y])
722 StorePlayer[jx][jy] = 0;
724 StorePlayer[x][y] = Feld[x][y];
728 printf("Player %d activated.\n", player->element_nr);
729 printf("[Local player is %d and currently %s.]\n",
730 local_player->element_nr,
731 local_player->active ? "active" : "not active");
735 Feld[x][y] = EL_EMPTY;
737 player->jx = player->last_jx = x;
738 player->jy = player->last_jy = y;
742 static void InitField(int x, int y, boolean init_game)
744 int element = Feld[x][y];
753 InitPlayerField(x, y, element, init_game);
756 case EL_SOKOBAN_FIELD_PLAYER:
757 element = Feld[x][y] = EL_PLAYER_1;
758 InitField(x, y, init_game);
760 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
761 InitField(x, y, init_game);
764 case EL_SOKOBAN_FIELD_EMPTY:
765 local_player->sokobanfields_still_needed++;
769 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
770 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
771 else if (x > 0 && Feld[x-1][y] == EL_ACID)
772 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
773 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
774 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
775 else if (y > 0 && Feld[x][y-1] == EL_ACID)
776 Feld[x][y] = EL_ACID_POOL_BOTTOM;
777 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
778 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
786 case EL_SPACESHIP_RIGHT:
787 case EL_SPACESHIP_UP:
788 case EL_SPACESHIP_LEFT:
789 case EL_SPACESHIP_DOWN:
791 case EL_BD_BUTTERFLY_RIGHT:
792 case EL_BD_BUTTERFLY_UP:
793 case EL_BD_BUTTERFLY_LEFT:
794 case EL_BD_BUTTERFLY_DOWN:
795 case EL_BD_BUTTERFLY:
796 case EL_BD_FIREFLY_RIGHT:
797 case EL_BD_FIREFLY_UP:
798 case EL_BD_FIREFLY_LEFT:
799 case EL_BD_FIREFLY_DOWN:
801 case EL_PACMAN_RIGHT:
825 if (y == lev_fieldy - 1)
827 Feld[x][y] = EL_AMOEBA_GROWING;
828 Store[x][y] = EL_AMOEBA_WET;
832 case EL_DYNAMITE_ACTIVE:
833 case EL_SP_DISK_RED_ACTIVE:
834 case EL_DYNABOMB_PLAYER_1_ACTIVE:
835 case EL_DYNABOMB_PLAYER_2_ACTIVE:
836 case EL_DYNABOMB_PLAYER_3_ACTIVE:
837 case EL_DYNABOMB_PLAYER_4_ACTIVE:
842 local_player->lights_still_needed++;
846 local_player->friends_still_needed++;
851 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
854 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
855 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
856 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
857 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
858 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
859 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
860 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
861 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
862 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
863 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
864 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
865 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
868 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
869 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
870 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
872 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
874 game.belt_dir[belt_nr] = belt_dir;
875 game.belt_dir_nr[belt_nr] = belt_dir_nr;
877 else /* more than one switch -- set it like the first switch */
879 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
884 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
886 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
889 case EL_LIGHT_SWITCH_ACTIVE:
891 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
895 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
897 else if (IS_GROUP_ELEMENT(element))
899 struct ElementGroupInfo *group = element_info[element].group;
900 int last_anim_random_frame = gfx.anim_random_frame;
903 if (group->choice_mode == ANIM_RANDOM)
904 gfx.anim_random_frame = RND(group->num_elements_resolved);
906 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
907 group->choice_mode, 0,
910 if (group->choice_mode == ANIM_RANDOM)
911 gfx.anim_random_frame = last_anim_random_frame;
915 Feld[x][y] = group->element_resolved[element_pos];
917 InitField(x, y, init_game);
922 #if USE_NEW_CUSTOM_VALUE
925 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
927 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
933 static inline void InitField_WithBug1(int x, int y, boolean init_game)
935 InitField(x, y, init_game);
937 /* not needed to call InitMovDir() -- already done by InitField()! */
938 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
939 CAN_MOVE(Feld[x][y]))
943 static inline void InitField_WithBug2(int x, int y, boolean init_game)
945 int old_element = Feld[x][y];
947 InitField(x, y, init_game);
949 /* not needed to call InitMovDir() -- already done by InitField()! */
950 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
951 CAN_MOVE(old_element) &&
952 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
955 /* this case is in fact a combination of not less than three bugs:
956 first, it calls InitMovDir() for elements that can move, although this is
957 already done by InitField(); then, it checks the element that was at this
958 field _before_ the call to InitField() (which can change it); lastly, it
959 was not called for "mole with direction" elements, which were treated as
960 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
964 inline void DrawGameValue_Emeralds(int value)
966 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
969 inline void DrawGameValue_Dynamite(int value)
971 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
974 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
978 /* currently only 4 of 8 possible keys are displayed */
979 for (i = 0; i < STD_NUM_KEYS; i++)
982 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
983 el2edimg(EL_KEY_1 + i));
985 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
986 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
987 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
991 inline void DrawGameValue_Score(int value)
993 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
996 inline void DrawGameValue_Time(int value)
999 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1001 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1004 inline void DrawGameValue_Level(int value)
1007 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1010 /* misuse area for displaying emeralds to draw bigger level number */
1011 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1012 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1014 /* now copy it to the area for displaying level number */
1015 BlitBitmap(drawto, drawto,
1016 DX_EMERALDS, DY_EMERALDS + 1,
1017 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1018 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1019 DX_LEVEL - 1, DY_LEVEL + 1);
1021 /* restore the area for displaying emeralds */
1022 DrawGameValue_Emeralds(local_player->gems_still_needed);
1024 /* yes, this is all really ugly :-) */
1028 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1031 int key[MAX_NUM_KEYS];
1034 for (i = 0; i < MAX_NUM_KEYS; i++)
1035 key[i] = key_bits & (1 << i);
1037 DrawGameValue_Level(level_nr);
1039 DrawGameValue_Emeralds(emeralds);
1040 DrawGameValue_Dynamite(dynamite);
1041 DrawGameValue_Score(score);
1042 DrawGameValue_Time(time);
1044 DrawGameValue_Keys(key);
1047 void DrawGameDoorValues()
1051 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1053 DrawGameDoorValues_EM();
1058 DrawGameValue_Level(level_nr);
1060 DrawGameValue_Emeralds(local_player->gems_still_needed);
1061 DrawGameValue_Dynamite(local_player->inventory_size);
1062 DrawGameValue_Score(local_player->score);
1063 DrawGameValue_Time(TimeLeft);
1065 for (i = 0; i < MAX_PLAYERS; i++)
1066 DrawGameValue_Keys(stored_player[i].key);
1069 static void resolve_group_element(int group_element, int recursion_depth)
1071 static int group_nr;
1072 static struct ElementGroupInfo *group;
1073 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1076 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1078 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1079 group_element - EL_GROUP_START + 1);
1081 /* replace element which caused too deep recursion by question mark */
1082 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1087 if (recursion_depth == 0) /* initialization */
1089 group = element_info[group_element].group;
1090 group_nr = group_element - EL_GROUP_START;
1092 group->num_elements_resolved = 0;
1093 group->choice_pos = 0;
1096 for (i = 0; i < actual_group->num_elements; i++)
1098 int element = actual_group->element[i];
1100 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1103 if (IS_GROUP_ELEMENT(element))
1104 resolve_group_element(element, recursion_depth + 1);
1107 group->element_resolved[group->num_elements_resolved++] = element;
1108 element_info[element].in_group[group_nr] = TRUE;
1115 =============================================================================
1117 -----------------------------------------------------------------------------
1118 initialize game engine due to level / tape version number
1119 =============================================================================
1122 static void InitGameEngine()
1126 /* set game engine from tape file when re-playing, else from level file */
1127 game.engine_version = (tape.playing ? tape.engine_version :
1128 level.game_version);
1130 /* ---------------------------------------------------------------------- */
1131 /* set flags for bugs and changes according to active game engine version */
1132 /* ---------------------------------------------------------------------- */
1135 Summary of bugfix/change:
1136 Fixed handling for custom elements that change when pushed by the player.
1138 Fixed/changed in version:
1142 Before 3.1.0, custom elements that "change when pushing" changed directly
1143 after the player started pushing them (until then handled in "DigField()").
1144 Since 3.1.0, these custom elements are not changed until the "pushing"
1145 move of the element is finished (now handled in "ContinueMoving()").
1147 Affected levels/tapes:
1148 The first condition is generally needed for all levels/tapes before version
1149 3.1.0, which might use the old behaviour before it was changed; known tapes
1150 that are affected are some tapes from the level set "Walpurgis Gardens" by
1152 The second condition is an exception from the above case and is needed for
1153 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1154 above (including some development versions of 3.1.0), but before it was
1155 known that this change would break tapes like the above and was fixed in
1156 3.1.1, so that the changed behaviour was active although the engine version
1157 while recording maybe was before 3.1.0. There is at least one tape that is
1158 affected by this exception, which is the tape for the one-level set "Bug
1159 Machine" by Juergen Bonhagen.
1162 game.use_change_when_pushing_bug =
1163 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1165 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1166 tape.game_version < VERSION_IDENT(3,1,1,0)));
1169 Summary of bugfix/change:
1170 Fixed handling for blocking the field the player leaves when moving.
1172 Fixed/changed in version:
1176 Before 3.1.1, when "block last field when moving" was enabled, the field
1177 the player is leaving when moving was blocked for the time of the move,
1178 and was directly unblocked afterwards. This resulted in the last field
1179 being blocked for exactly one less than the number of frames of one player
1180 move. Additionally, even when blocking was disabled, the last field was
1181 blocked for exactly one frame.
1182 Since 3.1.1, due to changes in player movement handling, the last field
1183 is not blocked at all when blocking is disabled. When blocking is enabled,
1184 the last field is blocked for exactly the number of frames of one player
1185 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1186 last field is blocked for exactly one more than the number of frames of
1189 Affected levels/tapes:
1190 (!!! yet to be determined -- probably many !!!)
1193 game.use_block_last_field_bug =
1194 (game.engine_version < VERSION_IDENT(3,1,1,0));
1196 /* ---------------------------------------------------------------------- */
1198 /* dynamically adjust element properties according to game engine version */
1199 InitElementPropertiesEngine(game.engine_version);
1202 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1203 printf(" tape version == %06d [%s] [file: %06d]\n",
1204 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1206 printf(" => game.engine_version == %06d\n", game.engine_version);
1209 /* ---------- recursively resolve group elements ------------------------- */
1211 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1212 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1213 element_info[i].in_group[j] = FALSE;
1215 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1216 resolve_group_element(EL_GROUP_START + i, 0);
1218 /* ---------- initialize player's initial move delay --------------------- */
1220 /* dynamically adjust player properties according to level information */
1221 game.initial_move_delay_value =
1222 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1224 /* dynamically adjust player properties according to game engine version */
1225 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1226 game.initial_move_delay_value : 0);
1228 /* ---------- initialize player's initial push delay --------------------- */
1230 /* dynamically adjust player properties according to game engine version */
1231 game.initial_push_delay_value =
1232 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1234 /* ---------- initialize changing elements ------------------------------- */
1236 /* initialize changing elements information */
1237 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1239 struct ElementInfo *ei = &element_info[i];
1241 /* this pointer might have been changed in the level editor */
1242 ei->change = &ei->change_page[0];
1244 if (!IS_CUSTOM_ELEMENT(i))
1246 ei->change->target_element = EL_EMPTY_SPACE;
1247 ei->change->delay_fixed = 0;
1248 ei->change->delay_random = 0;
1249 ei->change->delay_frames = 1;
1252 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1254 ei->has_change_event[j] = FALSE;
1256 ei->event_page_nr[j] = 0;
1257 ei->event_page[j] = &ei->change_page[0];
1261 /* add changing elements from pre-defined list */
1262 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1264 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1265 struct ElementInfo *ei = &element_info[ch_delay->element];
1267 ei->change->target_element = ch_delay->target_element;
1268 ei->change->delay_fixed = ch_delay->change_delay;
1270 ei->change->pre_change_function = ch_delay->pre_change_function;
1271 ei->change->change_function = ch_delay->change_function;
1272 ei->change->post_change_function = ch_delay->post_change_function;
1274 ei->change->can_change = TRUE;
1275 ei->change->can_change_or_has_action = TRUE;
1277 ei->has_change_event[CE_DELAY] = TRUE;
1279 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1280 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1283 /* ---------- initialize internal run-time variables ------------- */
1285 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1287 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1289 for (j = 0; j < ei->num_change_pages; j++)
1291 ei->change_page[j].can_change_or_has_action =
1292 (ei->change_page[j].can_change |
1293 ei->change_page[j].has_action);
1297 /* add change events from custom element configuration */
1298 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1300 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1302 for (j = 0; j < ei->num_change_pages; j++)
1304 if (!ei->change_page[j].can_change_or_has_action)
1307 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1309 /* only add event page for the first page found with this event */
1310 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1312 ei->has_change_event[k] = TRUE;
1314 ei->event_page_nr[k] = j;
1315 ei->event_page[k] = &ei->change_page[j];
1321 /* ---------- initialize run-time trigger player and element ------------- */
1323 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1325 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1327 for (j = 0; j < ei->num_change_pages; j++)
1329 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1330 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1331 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1332 ei->change_page[j].actual_trigger_ce_value = 0;
1336 /* ---------- initialize trigger events ---------------------------------- */
1338 /* initialize trigger events information */
1339 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1340 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1341 trigger_events[i][j] = FALSE;
1343 /* add trigger events from element change event properties */
1344 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1346 struct ElementInfo *ei = &element_info[i];
1348 for (j = 0; j < ei->num_change_pages; j++)
1350 if (!ei->change_page[j].can_change_or_has_action)
1353 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1355 int trigger_element = ei->change_page[j].trigger_element;
1357 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1359 if (ei->change_page[j].has_event[k])
1361 if (IS_GROUP_ELEMENT(trigger_element))
1363 struct ElementGroupInfo *group =
1364 element_info[trigger_element].group;
1366 for (l = 0; l < group->num_elements_resolved; l++)
1367 trigger_events[group->element_resolved[l]][k] = TRUE;
1370 trigger_events[trigger_element][k] = TRUE;
1377 /* ---------- initialize push delay -------------------------------------- */
1379 /* initialize push delay values to default */
1380 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1382 if (!IS_CUSTOM_ELEMENT(i))
1384 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1385 element_info[i].push_delay_random = game.default_push_delay_random;
1389 /* set push delay value for certain elements from pre-defined list */
1390 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1392 int e = push_delay_list[i].element;
1394 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1395 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1398 /* set push delay value for Supaplex elements for newer engine versions */
1399 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1401 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1403 if (IS_SP_ELEMENT(i))
1405 /* set SP push delay to just enough to push under a falling zonk */
1406 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1408 element_info[i].push_delay_fixed = delay;
1409 element_info[i].push_delay_random = 0;
1414 /* ---------- initialize move stepsize ----------------------------------- */
1416 /* initialize move stepsize values to default */
1417 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1418 if (!IS_CUSTOM_ELEMENT(i))
1419 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1421 /* set move stepsize value for certain elements from pre-defined list */
1422 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1424 int e = move_stepsize_list[i].element;
1426 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1429 /* ---------- initialize collect score ----------------------------------- */
1431 /* initialize collect score values for custom elements from initial value */
1432 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1433 if (IS_CUSTOM_ELEMENT(i))
1434 element_info[i].collect_score = element_info[i].collect_score_initial;
1436 /* ---------- initialize collect count ----------------------------------- */
1438 /* initialize collect count values for non-custom elements */
1439 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1440 if (!IS_CUSTOM_ELEMENT(i))
1441 element_info[i].collect_count_initial = 0;
1443 /* add collect count values for all elements from pre-defined list */
1444 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1445 element_info[collect_count_list[i].element].collect_count_initial =
1446 collect_count_list[i].count;
1448 /* ---------- initialize access direction -------------------------------- */
1450 /* initialize access direction values to default (access from every side) */
1451 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1452 if (!IS_CUSTOM_ELEMENT(i))
1453 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1455 /* set access direction value for certain elements from pre-defined list */
1456 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1457 element_info[access_direction_list[i].element].access_direction =
1458 access_direction_list[i].direction;
1463 =============================================================================
1465 -----------------------------------------------------------------------------
1466 initialize and start new game
1467 =============================================================================
1472 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1473 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1474 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1479 /* don't play tapes over network */
1480 network_playing = (options.network && !tape.playing);
1482 for (i = 0; i < MAX_PLAYERS; i++)
1484 struct PlayerInfo *player = &stored_player[i];
1486 player->index_nr = i;
1487 player->index_bit = (1 << i);
1488 player->element_nr = EL_PLAYER_1 + i;
1490 player->present = FALSE;
1491 player->active = FALSE;
1494 player->effective_action = 0;
1495 player->programmed_action = 0;
1498 player->gems_still_needed = level.gems_needed;
1499 player->sokobanfields_still_needed = 0;
1500 player->lights_still_needed = 0;
1501 player->friends_still_needed = 0;
1503 for (j = 0; j < MAX_NUM_KEYS; j++)
1504 player->key[j] = FALSE;
1506 player->dynabomb_count = 0;
1507 player->dynabomb_size = 1;
1508 player->dynabombs_left = 0;
1509 player->dynabomb_xl = FALSE;
1511 player->MovDir = MV_NONE;
1514 player->GfxDir = MV_NONE;
1515 player->GfxAction = ACTION_DEFAULT;
1517 player->StepFrame = 0;
1519 player->use_murphy_graphic = FALSE;
1521 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1522 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1524 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1526 player->actual_frame_counter = 0;
1528 player->step_counter = 0;
1530 player->last_move_dir = MV_NONE;
1532 player->is_waiting = FALSE;
1533 player->is_moving = FALSE;
1534 player->is_auto_moving = FALSE;
1535 player->is_digging = FALSE;
1536 player->is_snapping = FALSE;
1537 player->is_collecting = FALSE;
1538 player->is_pushing = FALSE;
1539 player->is_switching = FALSE;
1540 player->is_dropping = FALSE;
1542 player->is_bored = FALSE;
1543 player->is_sleeping = FALSE;
1545 player->frame_counter_bored = -1;
1546 player->frame_counter_sleeping = -1;
1548 player->anim_delay_counter = 0;
1549 player->post_delay_counter = 0;
1551 player->action_waiting = ACTION_DEFAULT;
1552 player->last_action_waiting = ACTION_DEFAULT;
1553 player->special_action_bored = ACTION_DEFAULT;
1554 player->special_action_sleeping = ACTION_DEFAULT;
1556 player->num_special_action_bored = 0;
1557 player->num_special_action_sleeping = 0;
1559 /* determine number of special actions for bored and sleeping animation */
1560 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1562 boolean found = FALSE;
1564 for (k = 0; k < NUM_DIRECTIONS; k++)
1565 if (el_act_dir2img(player->element_nr, j, k) !=
1566 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1570 player->num_special_action_bored++;
1574 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1576 boolean found = FALSE;
1578 for (k = 0; k < NUM_DIRECTIONS; k++)
1579 if (el_act_dir2img(player->element_nr, j, k) !=
1580 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1584 player->num_special_action_sleeping++;
1589 player->switch_x = -1;
1590 player->switch_y = -1;
1592 player->drop_x = -1;
1593 player->drop_y = -1;
1595 player->show_envelope = 0;
1597 player->move_delay = game.initial_move_delay;
1598 player->move_delay_value = game.initial_move_delay_value;
1600 player->move_delay_value_next = -1;
1602 player->move_delay_reset_counter = 0;
1604 player->push_delay = -1; /* initialized when pushing starts */
1605 player->push_delay_value = game.initial_push_delay_value;
1607 player->drop_delay = 0;
1609 player->last_jx = player->last_jy = 0;
1610 player->jx = player->jy = 0;
1612 player->shield_normal_time_left = 0;
1613 player->shield_deadly_time_left = 0;
1615 player->inventory_infinite_element = EL_UNDEFINED;
1616 player->inventory_size = 0;
1618 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1619 SnapField(player, 0, 0);
1621 player->LevelSolved = FALSE;
1622 player->GameOver = FALSE;
1625 network_player_action_received = FALSE;
1627 #if defined(NETWORK_AVALIABLE)
1628 /* initial null action */
1629 if (network_playing)
1630 SendToServer_MovePlayer(MV_NONE);
1639 TimeLeft = level.time;
1642 ScreenMovDir = MV_NONE;
1646 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1648 AllPlayersGone = FALSE;
1650 game.yamyam_content_nr = 0;
1651 game.magic_wall_active = FALSE;
1652 game.magic_wall_time_left = 0;
1653 game.light_time_left = 0;
1654 game.timegate_time_left = 0;
1655 game.switchgate_pos = 0;
1656 game.wind_direction = level.wind_direction_initial;
1657 game.gravity = level.initial_gravity;
1658 game.explosions_delayed = TRUE;
1660 game.envelope_active = FALSE;
1662 for (i = 0; i < NUM_BELTS; i++)
1664 game.belt_dir[i] = MV_NONE;
1665 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1668 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1669 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1671 for (x = 0; x < lev_fieldx; x++)
1673 for (y = 0; y < lev_fieldy; y++)
1675 Feld[x][y] = level.field[x][y];
1676 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1677 ChangeDelay[x][y] = 0;
1678 ChangePage[x][y] = -1;
1679 #if USE_NEW_CUSTOM_VALUE
1680 CustomValue[x][y] = 0; /* initialized in InitField() */
1682 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1684 WasJustMoving[x][y] = 0;
1685 WasJustFalling[x][y] = 0;
1686 CheckCollision[x][y] = 0;
1688 Pushed[x][y] = FALSE;
1690 Changed[x][y] = FALSE;
1691 ChangeEvent[x][y] = -1;
1693 ExplodePhase[x][y] = 0;
1694 ExplodeDelay[x][y] = 0;
1695 ExplodeField[x][y] = EX_TYPE_NONE;
1697 RunnerVisit[x][y] = 0;
1698 PlayerVisit[x][y] = 0;
1701 GfxRandom[x][y] = INIT_GFX_RANDOM();
1702 GfxElement[x][y] = EL_UNDEFINED;
1703 GfxAction[x][y] = ACTION_DEFAULT;
1704 GfxDir[x][y] = MV_NONE;
1708 for (y = 0; y < lev_fieldy; y++)
1710 for (x = 0; x < lev_fieldx; x++)
1712 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1714 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1716 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1719 InitField(x, y, TRUE);
1725 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1726 emulate_sb ? EMU_SOKOBAN :
1727 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1729 #if USE_NEW_ALL_SLIPPERY
1730 /* initialize type of slippery elements */
1731 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1733 if (!IS_CUSTOM_ELEMENT(i))
1735 /* default: elements slip down either to the left or right randomly */
1736 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
1738 /* SP style elements prefer to slip down on the left side */
1739 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
1740 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1742 /* BD style elements prefer to slip down on the left side */
1743 if (game.emulation == EMU_BOULDERDASH)
1744 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1749 /* initialize explosion and ignition delay */
1750 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1752 if (!IS_CUSTOM_ELEMENT(i))
1755 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1756 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1757 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1758 int last_phase = (num_phase + 1) * delay;
1759 int half_phase = (num_phase / 2) * delay;
1761 element_info[i].explosion_delay = last_phase - 1;
1762 element_info[i].ignition_delay = half_phase;
1764 if (i == EL_BLACK_ORB)
1765 element_info[i].ignition_delay = 1;
1769 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1770 element_info[i].explosion_delay = 1;
1772 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1773 element_info[i].ignition_delay = 1;
1777 /* correct non-moving belts to start moving left */
1778 for (i = 0; i < NUM_BELTS; i++)
1779 if (game.belt_dir[i] == MV_NONE)
1780 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1782 /* check if any connected player was not found in playfield */
1783 for (i = 0; i < MAX_PLAYERS; i++)
1785 struct PlayerInfo *player = &stored_player[i];
1787 if (player->connected && !player->present)
1789 for (j = 0; j < MAX_PLAYERS; j++)
1791 struct PlayerInfo *some_player = &stored_player[j];
1792 int jx = some_player->jx, jy = some_player->jy;
1794 /* assign first free player found that is present in the playfield */
1795 if (some_player->present && !some_player->connected)
1797 player->present = TRUE;
1798 player->active = TRUE;
1800 some_player->present = FALSE;
1801 some_player->active = FALSE;
1804 player->element_nr = some_player->element_nr;
1807 player->block_last_field = some_player->block_last_field;
1808 player->block_delay_adjustment = some_player->block_delay_adjustment;
1810 StorePlayer[jx][jy] = player->element_nr;
1811 player->jx = player->last_jx = jx;
1812 player->jy = player->last_jy = jy;
1822 /* when playing a tape, eliminate all players which do not participate */
1824 for (i = 0; i < MAX_PLAYERS; i++)
1826 if (stored_player[i].active && !tape.player_participates[i])
1828 struct PlayerInfo *player = &stored_player[i];
1829 int jx = player->jx, jy = player->jy;
1831 player->active = FALSE;
1832 StorePlayer[jx][jy] = 0;
1833 Feld[jx][jy] = EL_EMPTY;
1837 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1839 /* when in single player mode, eliminate all but the first active player */
1841 for (i = 0; i < MAX_PLAYERS; i++)
1843 if (stored_player[i].active)
1845 for (j = i + 1; j < MAX_PLAYERS; j++)
1847 if (stored_player[j].active)
1849 struct PlayerInfo *player = &stored_player[j];
1850 int jx = player->jx, jy = player->jy;
1852 player->active = FALSE;
1853 player->present = FALSE;
1855 StorePlayer[jx][jy] = 0;
1856 Feld[jx][jy] = EL_EMPTY;
1863 /* when recording the game, store which players take part in the game */
1866 for (i = 0; i < MAX_PLAYERS; i++)
1867 if (stored_player[i].active)
1868 tape.player_participates[i] = TRUE;
1873 for (i = 0; i < MAX_PLAYERS; i++)
1875 struct PlayerInfo *player = &stored_player[i];
1877 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1882 if (local_player == player)
1883 printf("Player %d is local player.\n", i+1);
1887 if (BorderElement == EL_EMPTY)
1890 SBX_Right = lev_fieldx - SCR_FIELDX;
1892 SBY_Lower = lev_fieldy - SCR_FIELDY;
1897 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1899 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1902 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1903 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1905 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1906 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1908 /* if local player not found, look for custom element that might create
1909 the player (make some assumptions about the right custom element) */
1910 if (!local_player->present)
1912 int start_x = 0, start_y = 0;
1913 int found_rating = 0;
1914 int found_element = EL_UNDEFINED;
1916 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1918 int element = Feld[x][y];
1923 if (!IS_CUSTOM_ELEMENT(element))
1926 if (CAN_CHANGE(element))
1928 for (i = 0; i < element_info[element].num_change_pages; i++)
1930 content = element_info[element].change_page[i].target_element;
1931 is_player = ELEM_IS_PLAYER(content);
1933 if (is_player && (found_rating < 3 || element < found_element))
1939 found_element = element;
1944 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1946 content = element_info[element].content.e[xx][yy];
1947 is_player = ELEM_IS_PLAYER(content);
1949 if (is_player && (found_rating < 2 || element < found_element))
1951 start_x = x + xx - 1;
1952 start_y = y + yy - 1;
1955 found_element = element;
1958 if (!CAN_CHANGE(element))
1961 for (i = 0; i < element_info[element].num_change_pages; i++)
1964 element_info[element].change_page[i].target_content.e[xx][yy];
1966 is_player = ELEM_IS_PLAYER(content);
1968 if (is_player && (found_rating < 1 || element < found_element))
1970 start_x = x + xx - 1;
1971 start_y = y + yy - 1;
1974 found_element = element;
1980 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1981 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1984 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1985 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1990 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1991 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1992 local_player->jx - MIDPOSX);
1994 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1995 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1996 local_player->jy - MIDPOSY);
1999 if (!game.restart_level)
2000 CloseDoor(DOOR_CLOSE_1);
2002 /* !!! FIX THIS (START) !!! */
2003 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2005 InitGameEngine_EM();
2012 /* after drawing the level, correct some elements */
2013 if (game.timegate_time_left == 0)
2014 CloseAllOpenTimegates();
2016 if (setup.soft_scrolling)
2017 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2019 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2022 /* !!! FIX THIS (END) !!! */
2024 if (!game.restart_level)
2026 /* copy default game door content to main double buffer */
2027 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2028 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2031 DrawGameDoorValues();
2033 if (!game.restart_level)
2037 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2038 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2039 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2043 /* copy actual game door content to door double buffer for OpenDoor() */
2044 BlitBitmap(drawto, bitmap_db_door,
2045 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2047 OpenDoor(DOOR_OPEN_ALL);
2049 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2051 if (setup.sound_music)
2054 KeyboardAutoRepeatOffUnlessAutoplay();
2058 for (i = 0; i < MAX_PLAYERS; i++)
2059 printf("Player %d %sactive.\n",
2060 i + 1, (stored_player[i].active ? "" : "not "));
2064 game.restart_level = FALSE;
2067 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2069 /* this is used for non-R'n'D game engines to update certain engine values */
2071 /* needed to determine if sounds are played within the visible screen area */
2072 scroll_x = actual_scroll_x;
2073 scroll_y = actual_scroll_y;
2076 void InitMovDir(int x, int y)
2078 int i, element = Feld[x][y];
2079 static int xy[4][2] =
2086 static int direction[3][4] =
2088 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2089 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2090 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2099 Feld[x][y] = EL_BUG;
2100 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2103 case EL_SPACESHIP_RIGHT:
2104 case EL_SPACESHIP_UP:
2105 case EL_SPACESHIP_LEFT:
2106 case EL_SPACESHIP_DOWN:
2107 Feld[x][y] = EL_SPACESHIP;
2108 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2111 case EL_BD_BUTTERFLY_RIGHT:
2112 case EL_BD_BUTTERFLY_UP:
2113 case EL_BD_BUTTERFLY_LEFT:
2114 case EL_BD_BUTTERFLY_DOWN:
2115 Feld[x][y] = EL_BD_BUTTERFLY;
2116 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2119 case EL_BD_FIREFLY_RIGHT:
2120 case EL_BD_FIREFLY_UP:
2121 case EL_BD_FIREFLY_LEFT:
2122 case EL_BD_FIREFLY_DOWN:
2123 Feld[x][y] = EL_BD_FIREFLY;
2124 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2127 case EL_PACMAN_RIGHT:
2129 case EL_PACMAN_LEFT:
2130 case EL_PACMAN_DOWN:
2131 Feld[x][y] = EL_PACMAN;
2132 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2135 case EL_SP_SNIKSNAK:
2136 MovDir[x][y] = MV_UP;
2139 case EL_SP_ELECTRON:
2140 MovDir[x][y] = MV_LEFT;
2147 Feld[x][y] = EL_MOLE;
2148 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2152 if (IS_CUSTOM_ELEMENT(element))
2154 struct ElementInfo *ei = &element_info[element];
2155 int move_direction_initial = ei->move_direction_initial;
2156 int move_pattern = ei->move_pattern;
2158 if (move_direction_initial == MV_START_PREVIOUS)
2160 if (MovDir[x][y] != MV_NONE)
2163 move_direction_initial = MV_START_AUTOMATIC;
2166 if (move_direction_initial == MV_START_RANDOM)
2167 MovDir[x][y] = 1 << RND(4);
2168 else if (move_direction_initial & MV_ANY_DIRECTION)
2169 MovDir[x][y] = move_direction_initial;
2170 else if (move_pattern == MV_ALL_DIRECTIONS ||
2171 move_pattern == MV_TURNING_LEFT ||
2172 move_pattern == MV_TURNING_RIGHT ||
2173 move_pattern == MV_TURNING_LEFT_RIGHT ||
2174 move_pattern == MV_TURNING_RIGHT_LEFT ||
2175 move_pattern == MV_TURNING_RANDOM)
2176 MovDir[x][y] = 1 << RND(4);
2177 else if (move_pattern == MV_HORIZONTAL)
2178 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2179 else if (move_pattern == MV_VERTICAL)
2180 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2181 else if (move_pattern & MV_ANY_DIRECTION)
2182 MovDir[x][y] = element_info[element].move_pattern;
2183 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2184 move_pattern == MV_ALONG_RIGHT_SIDE)
2186 /* use random direction as default start direction */
2187 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2188 MovDir[x][y] = 1 << RND(4);
2190 for (i = 0; i < NUM_DIRECTIONS; i++)
2192 int x1 = x + xy[i][0];
2193 int y1 = y + xy[i][1];
2195 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2197 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2198 MovDir[x][y] = direction[0][i];
2200 MovDir[x][y] = direction[1][i];
2209 MovDir[x][y] = 1 << RND(4);
2211 if (element != EL_BUG &&
2212 element != EL_SPACESHIP &&
2213 element != EL_BD_BUTTERFLY &&
2214 element != EL_BD_FIREFLY)
2217 for (i = 0; i < NUM_DIRECTIONS; i++)
2219 int x1 = x + xy[i][0];
2220 int y1 = y + xy[i][1];
2222 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2224 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2226 MovDir[x][y] = direction[0][i];
2229 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2230 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2232 MovDir[x][y] = direction[1][i];
2241 GfxDir[x][y] = MovDir[x][y];
2244 void InitAmoebaNr(int x, int y)
2247 int group_nr = AmoebeNachbarNr(x, y);
2251 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2253 if (AmoebaCnt[i] == 0)
2261 AmoebaNr[x][y] = group_nr;
2262 AmoebaCnt[group_nr]++;
2263 AmoebaCnt2[group_nr]++;
2269 boolean raise_level = FALSE;
2271 if (local_player->MovPos)
2274 if (tape.auto_play) /* tape might already be stopped here */
2275 tape.auto_play_level_solved = TRUE;
2277 local_player->LevelSolved = FALSE;
2279 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2283 if (!tape.playing && setup.sound_loops)
2284 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2285 SND_CTRL_PLAY_LOOP);
2287 while (TimeLeft > 0)
2289 if (!tape.playing && !setup.sound_loops)
2290 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2291 if (TimeLeft > 0 && !(TimeLeft % 10))
2292 RaiseScore(level.score[SC_TIME_BONUS]);
2293 if (TimeLeft > 100 && !(TimeLeft % 10))
2298 DrawGameValue_Time(TimeLeft);
2306 if (!tape.playing && setup.sound_loops)
2307 StopSound(SND_GAME_LEVELTIME_BONUS);
2309 else if (level.time == 0) /* level without time limit */
2311 if (!tape.playing && setup.sound_loops)
2312 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2313 SND_CTRL_PLAY_LOOP);
2315 while (TimePlayed < 999)
2317 if (!tape.playing && !setup.sound_loops)
2318 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2319 if (TimePlayed < 999 && !(TimePlayed % 10))
2320 RaiseScore(level.score[SC_TIME_BONUS]);
2321 if (TimePlayed < 900 && !(TimePlayed % 10))
2326 DrawGameValue_Time(TimePlayed);
2334 if (!tape.playing && setup.sound_loops)
2335 StopSound(SND_GAME_LEVELTIME_BONUS);
2338 /* close exit door after last player */
2339 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2340 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2341 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2343 int element = Feld[ExitX][ExitY];
2345 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2346 EL_SP_EXIT_CLOSING);
2348 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2351 /* player disappears */
2352 if (ExitX >= 0 && ExitY >= 0)
2353 DrawLevelField(ExitX, ExitY);
2360 CloseDoor(DOOR_CLOSE_1);
2365 SaveTape(tape.level_nr); /* Ask to save tape */
2368 if (level_nr == leveldir_current->handicap_level)
2370 leveldir_current->handicap_level++;
2371 SaveLevelSetup_SeriesInfo();
2374 if (level_editor_test_game)
2375 local_player->score = -1; /* no highscore when playing from editor */
2376 else if (level_nr < leveldir_current->last_level)
2377 raise_level = TRUE; /* advance to next level */
2379 if ((hi_pos = NewHiScore()) >= 0)
2381 game_status = GAME_MODE_SCORES;
2382 DrawHallOfFame(hi_pos);
2391 game_status = GAME_MODE_MAIN;
2408 LoadScore(level_nr);
2410 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2411 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2414 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2416 if (local_player->score > highscore[k].Score)
2418 /* player has made it to the hall of fame */
2420 if (k < MAX_SCORE_ENTRIES - 1)
2422 int m = MAX_SCORE_ENTRIES - 1;
2425 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2426 if (!strcmp(setup.player_name, highscore[l].Name))
2428 if (m == k) /* player's new highscore overwrites his old one */
2432 for (l = m; l > k; l--)
2434 strcpy(highscore[l].Name, highscore[l - 1].Name);
2435 highscore[l].Score = highscore[l - 1].Score;
2442 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2443 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2444 highscore[k].Score = local_player->score;
2450 else if (!strncmp(setup.player_name, highscore[k].Name,
2451 MAX_PLAYER_NAME_LEN))
2452 break; /* player already there with a higher score */
2458 SaveScore(level_nr);
2463 inline static int getElementMoveStepsize(int x, int y)
2465 int element = Feld[x][y];
2466 int direction = MovDir[x][y];
2467 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2468 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2469 int horiz_move = (dx != 0);
2470 int sign = (horiz_move ? dx : dy);
2471 int step = sign * element_info[element].move_stepsize;
2473 /* special values for move stepsize for spring and things on conveyor belt */
2477 if (element == EL_SPRING)
2478 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2479 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2480 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2481 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2483 if (CAN_FALL(element) &&
2484 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2485 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2486 else if (element == EL_SPRING)
2487 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2494 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2496 if (player->GfxAction != action || player->GfxDir != dir)
2499 printf("Player frame reset! (%d => %d, %d => %d)\n",
2500 player->GfxAction, action, player->GfxDir, dir);
2503 player->GfxAction = action;
2504 player->GfxDir = dir;
2506 player->StepFrame = 0;
2510 static void ResetRandomAnimationValue(int x, int y)
2512 GfxRandom[x][y] = INIT_GFX_RANDOM();
2515 static void ResetGfxAnimation(int x, int y)
2518 GfxAction[x][y] = ACTION_DEFAULT;
2519 GfxDir[x][y] = MovDir[x][y];
2522 void InitMovingField(int x, int y, int direction)
2524 int element = Feld[x][y];
2525 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2526 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2530 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2531 ResetGfxAnimation(x, y);
2533 MovDir[x][y] = direction;
2534 GfxDir[x][y] = direction;
2535 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2536 ACTION_FALLING : ACTION_MOVING);
2538 /* this is needed for CEs with property "can move" / "not moving" */
2540 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2542 if (Feld[newx][newy] == EL_EMPTY)
2543 Feld[newx][newy] = EL_BLOCKED;
2545 MovDir[newx][newy] = MovDir[x][y];
2547 #if USE_NEW_CUSTOM_VALUE
2548 CustomValue[newx][newy] = CustomValue[x][y];
2551 GfxFrame[newx][newy] = GfxFrame[x][y];
2552 GfxRandom[newx][newy] = GfxRandom[x][y];
2553 GfxAction[newx][newy] = GfxAction[x][y];
2554 GfxDir[newx][newy] = GfxDir[x][y];
2558 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2560 int direction = MovDir[x][y];
2561 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2562 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2568 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2570 int oldx = x, oldy = y;
2571 int direction = MovDir[x][y];
2573 if (direction == MV_LEFT)
2575 else if (direction == MV_RIGHT)
2577 else if (direction == MV_UP)
2579 else if (direction == MV_DOWN)
2582 *comes_from_x = oldx;
2583 *comes_from_y = oldy;
2586 int MovingOrBlocked2Element(int x, int y)
2588 int element = Feld[x][y];
2590 if (element == EL_BLOCKED)
2594 Blocked2Moving(x, y, &oldx, &oldy);
2595 return Feld[oldx][oldy];
2601 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2603 /* like MovingOrBlocked2Element(), but if element is moving
2604 and (x,y) is the field the moving element is just leaving,
2605 return EL_BLOCKED instead of the element value */
2606 int element = Feld[x][y];
2608 if (IS_MOVING(x, y))
2610 if (element == EL_BLOCKED)
2614 Blocked2Moving(x, y, &oldx, &oldy);
2615 return Feld[oldx][oldy];
2624 static void RemoveField(int x, int y)
2626 Feld[x][y] = EL_EMPTY;
2632 #if USE_NEW_CUSTOM_VALUE
2633 CustomValue[x][y] = 0;
2637 ChangeDelay[x][y] = 0;
2638 ChangePage[x][y] = -1;
2639 Pushed[x][y] = FALSE;
2642 ExplodeField[x][y] = EX_TYPE_NONE;
2645 GfxElement[x][y] = EL_UNDEFINED;
2646 GfxAction[x][y] = ACTION_DEFAULT;
2647 GfxDir[x][y] = MV_NONE;
2650 void RemoveMovingField(int x, int y)
2652 int oldx = x, oldy = y, newx = x, newy = y;
2653 int element = Feld[x][y];
2654 int next_element = EL_UNDEFINED;
2656 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2659 if (IS_MOVING(x, y))
2661 Moving2Blocked(x, y, &newx, &newy);
2663 if (Feld[newx][newy] != EL_BLOCKED)
2665 /* element is moving, but target field is not free (blocked), but
2666 already occupied by something different (example: acid pool);
2667 in this case, only remove the moving field, but not the target */
2669 RemoveField(oldx, oldy);
2671 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2673 DrawLevelField(oldx, oldy);
2678 else if (element == EL_BLOCKED)
2680 Blocked2Moving(x, y, &oldx, &oldy);
2681 if (!IS_MOVING(oldx, oldy))
2685 if (element == EL_BLOCKED &&
2686 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2687 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2688 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2689 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2690 next_element = get_next_element(Feld[oldx][oldy]);
2692 RemoveField(oldx, oldy);
2693 RemoveField(newx, newy);
2695 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2697 if (next_element != EL_UNDEFINED)
2698 Feld[oldx][oldy] = next_element;
2700 DrawLevelField(oldx, oldy);
2701 DrawLevelField(newx, newy);
2704 void DrawDynamite(int x, int y)
2706 int sx = SCREENX(x), sy = SCREENY(y);
2707 int graphic = el2img(Feld[x][y]);
2710 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2713 if (IS_WALKABLE_INSIDE(Back[x][y]))
2717 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2718 else if (Store[x][y])
2719 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2721 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2723 if (Back[x][y] || Store[x][y])
2724 DrawGraphicThruMask(sx, sy, graphic, frame);
2726 DrawGraphic(sx, sy, graphic, frame);
2729 void CheckDynamite(int x, int y)
2731 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2735 if (MovDelay[x][y] != 0)
2738 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2744 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2749 void DrawRelocatePlayer(struct PlayerInfo *player)
2751 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2752 boolean no_delay = (tape.warp_forward);
2753 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2754 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2755 int jx = player->jx;
2756 int jy = player->jy;
2758 if (level.instant_relocation)
2760 int offset = (setup.scroll_delay ? 3 : 0);
2762 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2764 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2765 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2766 local_player->jx - MIDPOSX);
2768 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2769 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2770 local_player->jy - MIDPOSY);
2774 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2775 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2776 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2778 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2779 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2780 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2782 /* don't scroll over playfield boundaries */
2783 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2784 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2786 /* don't scroll over playfield boundaries */
2787 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2788 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2791 RedrawPlayfield(TRUE, 0,0,0,0);
2795 int scroll_xx = -999, scroll_yy = -999;
2797 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2799 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2802 int fx = FX, fy = FY;
2804 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2805 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2806 local_player->jx - MIDPOSX);
2808 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2809 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2810 local_player->jy - MIDPOSY);
2812 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2813 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2815 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2821 fx += dx * TILEX / 2;
2822 fy += dy * TILEY / 2;
2824 ScrollLevel(dx, dy);
2827 /* scroll in two steps of half tile size to make things smoother */
2828 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2830 Delay(wait_delay_value);
2832 /* scroll second step to align at full tile size */
2834 Delay(wait_delay_value);
2839 Delay(wait_delay_value);
2843 void RelocatePlayer(int jx, int jy, int el_player_raw)
2845 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
2846 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2847 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2848 boolean no_delay = (tape.warp_forward);
2849 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2850 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2851 int old_jx = player->jx;
2852 int old_jy = player->jy;
2853 int old_element = Feld[old_jx][old_jy];
2854 int element = Feld[jx][jy];
2855 boolean player_relocated = (old_jx != jx || old_jy != jy);
2857 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2858 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2859 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2860 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2861 int leave_side_horiz = move_dir_horiz;
2862 int leave_side_vert = move_dir_vert;
2863 int enter_side = enter_side_horiz | enter_side_vert;
2864 int leave_side = leave_side_horiz | leave_side_vert;
2866 if (player->GameOver) /* do not reanimate dead player */
2869 if (!player_relocated) /* no need to relocate the player */
2872 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2874 RemoveField(jx, jy); /* temporarily remove newly placed player */
2875 DrawLevelField(jx, jy);
2878 if (player->present)
2880 while (player->MovPos)
2882 ScrollPlayer(player, SCROLL_GO_ON);
2883 ScrollScreen(NULL, SCROLL_GO_ON);
2885 AdvanceFrameAndPlayerCounters(player->index_nr);
2890 Delay(wait_delay_value);
2893 DrawPlayer(player); /* needed here only to cleanup last field */
2894 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2896 player->is_moving = FALSE;
2899 if (IS_CUSTOM_ELEMENT(old_element))
2900 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
2902 player->index_bit, leave_side);
2904 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
2906 player->index_bit, leave_side);
2908 Feld[jx][jy] = el_player;
2909 InitPlayerField(jx, jy, el_player, TRUE);
2911 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
2913 Feld[jx][jy] = element;
2914 InitField(jx, jy, FALSE);
2917 if (player == local_player) /* only visually relocate local player */
2918 DrawRelocatePlayer(player);
2920 TestIfPlayerTouchesBadThing(jx, jy);
2921 TestIfPlayerTouchesCustomElement(jx, jy);
2923 if (IS_CUSTOM_ELEMENT(element))
2924 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
2925 player->index_bit, enter_side);
2927 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
2928 player->index_bit, enter_side);
2931 void Explode(int ex, int ey, int phase, int mode)
2937 /* !!! eliminate this variable !!! */
2938 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2940 if (game.explosions_delayed)
2942 ExplodeField[ex][ey] = mode;
2946 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2948 int center_element = Feld[ex][ey];
2951 /* --- This is only really needed (and now handled) in "Impact()". --- */
2952 /* do not explode moving elements that left the explode field in time */
2953 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2954 center_element == EL_EMPTY &&
2955 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
2959 if (mode == EX_TYPE_NORMAL ||
2960 mode == EX_TYPE_CENTER ||
2961 mode == EX_TYPE_CROSS)
2962 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2964 /* remove things displayed in background while burning dynamite */
2965 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2968 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2970 /* put moving element to center field (and let it explode there) */
2971 center_element = MovingOrBlocked2Element(ex, ey);
2972 RemoveMovingField(ex, ey);
2973 Feld[ex][ey] = center_element;
2976 last_phase = element_info[center_element].explosion_delay + 1;
2978 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2980 int xx = x - ex + 1;
2981 int yy = y - ey + 1;
2984 if (!IN_LEV_FIELD(x, y) ||
2985 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
2986 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
2989 element = Feld[x][y];
2991 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2993 element = MovingOrBlocked2Element(x, y);
2995 if (!IS_EXPLOSION_PROOF(element))
2996 RemoveMovingField(x, y);
2999 /* indestructible elements can only explode in center (but not flames) */
3000 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3001 mode == EX_TYPE_BORDER)) ||
3002 element == EL_FLAMES)
3005 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3006 behaviour, for example when touching a yamyam that explodes to rocks
3007 with active deadly shield, a rock is created under the player !!! */
3008 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3010 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3011 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3012 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3014 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3017 if (IS_ACTIVE_BOMB(element))
3019 /* re-activate things under the bomb like gate or penguin */
3020 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3027 /* save walkable background elements while explosion on same tile */
3028 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3029 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3030 Back[x][y] = element;
3032 /* ignite explodable elements reached by other explosion */
3033 if (element == EL_EXPLOSION)
3034 element = Store2[x][y];
3036 if (AmoebaNr[x][y] &&
3037 (element == EL_AMOEBA_FULL ||
3038 element == EL_BD_AMOEBA ||
3039 element == EL_AMOEBA_GROWING))
3041 AmoebaCnt[AmoebaNr[x][y]]--;
3042 AmoebaCnt2[AmoebaNr[x][y]]--;
3047 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3049 switch(StorePlayer[ex][ey])
3052 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3055 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3058 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3062 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3066 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3067 Store[x][y] = EL_EMPTY;
3069 else if (center_element == EL_MOLE)
3070 Store[x][y] = EL_EMERALD_RED;
3071 else if (center_element == EL_PENGUIN)
3072 Store[x][y] = EL_EMERALD_PURPLE;
3073 else if (center_element == EL_BUG)
3074 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3075 else if (center_element == EL_BD_BUTTERFLY)
3076 Store[x][y] = EL_BD_DIAMOND;
3077 else if (center_element == EL_SP_ELECTRON)
3078 Store[x][y] = EL_SP_INFOTRON;
3079 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3080 Store[x][y] = level.amoeba_content;
3081 else if (center_element == EL_YAMYAM)
3082 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3083 else if (IS_CUSTOM_ELEMENT(center_element) &&
3084 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3085 Store[x][y] = element_info[center_element].content.e[xx][yy];
3086 else if (element == EL_WALL_EMERALD)
3087 Store[x][y] = EL_EMERALD;
3088 else if (element == EL_WALL_DIAMOND)
3089 Store[x][y] = EL_DIAMOND;
3090 else if (element == EL_WALL_BD_DIAMOND)
3091 Store[x][y] = EL_BD_DIAMOND;
3092 else if (element == EL_WALL_EMERALD_YELLOW)
3093 Store[x][y] = EL_EMERALD_YELLOW;
3094 else if (element == EL_WALL_EMERALD_RED)
3095 Store[x][y] = EL_EMERALD_RED;
3096 else if (element == EL_WALL_EMERALD_PURPLE)
3097 Store[x][y] = EL_EMERALD_PURPLE;
3098 else if (element == EL_WALL_PEARL)
3099 Store[x][y] = EL_PEARL;
3100 else if (element == EL_WALL_CRYSTAL)
3101 Store[x][y] = EL_CRYSTAL;
3102 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3103 Store[x][y] = element_info[element].content.e[1][1];
3105 Store[x][y] = EL_EMPTY;
3107 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3108 center_element == EL_AMOEBA_TO_DIAMOND)
3109 Store2[x][y] = element;
3111 Feld[x][y] = EL_EXPLOSION;
3112 GfxElement[x][y] = center_element;
3114 ExplodePhase[x][y] = 1;
3115 ExplodeDelay[x][y] = last_phase;
3120 if (center_element == EL_YAMYAM)
3121 game.yamyam_content_nr =
3122 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3134 GfxFrame[x][y] = 0; /* restart explosion animation */
3136 last_phase = ExplodeDelay[x][y];
3138 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3142 /* activate this even in non-DEBUG version until cause for crash in
3143 getGraphicAnimationFrame() (see below) is found and eliminated */
3148 if (GfxElement[x][y] == EL_UNDEFINED)
3151 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3152 printf("Explode(): This should never happen!\n");
3155 GfxElement[x][y] = EL_EMPTY;
3159 border_element = Store2[x][y];
3160 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3161 border_element = StorePlayer[x][y];
3163 if (phase == element_info[border_element].ignition_delay ||
3164 phase == last_phase)
3166 boolean border_explosion = FALSE;
3168 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3169 !PLAYER_EXPLOSION_PROTECTED(x, y))
3171 KillPlayerUnlessExplosionProtected(x, y);
3172 border_explosion = TRUE;
3174 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3176 Feld[x][y] = Store2[x][y];
3179 border_explosion = TRUE;
3181 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3183 AmoebeUmwandeln(x, y);
3185 border_explosion = TRUE;
3188 /* if an element just explodes due to another explosion (chain-reaction),
3189 do not immediately end the new explosion when it was the last frame of
3190 the explosion (as it would be done in the following "if"-statement!) */
3191 if (border_explosion && phase == last_phase)
3195 if (phase == last_phase)
3199 element = Feld[x][y] = Store[x][y];
3200 Store[x][y] = Store2[x][y] = 0;
3201 GfxElement[x][y] = EL_UNDEFINED;
3203 /* player can escape from explosions and might therefore be still alive */
3204 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3205 element <= EL_PLAYER_IS_EXPLODING_4)
3206 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3208 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3209 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3210 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3213 /* restore probably existing indestructible background element */
3214 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3215 element = Feld[x][y] = Back[x][y];
3218 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3219 GfxDir[x][y] = MV_NONE;
3220 ChangeDelay[x][y] = 0;
3221 ChangePage[x][y] = -1;
3223 #if USE_NEW_CUSTOM_VALUE
3224 CustomValue[x][y] = 0;
3227 InitField_WithBug2(x, y, FALSE);
3229 DrawLevelField(x, y);
3231 TestIfElementTouchesCustomElement(x, y);
3233 if (GFX_CRUMBLED(element))
3234 DrawLevelFieldCrumbledSandNeighbours(x, y);
3236 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3237 StorePlayer[x][y] = 0;
3239 if (ELEM_IS_PLAYER(element))
3240 RelocatePlayer(x, y, element);
3242 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3244 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3245 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3248 DrawLevelFieldCrumbledSand(x, y);
3250 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3252 DrawLevelElement(x, y, Back[x][y]);
3253 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3255 else if (IS_WALKABLE_UNDER(Back[x][y]))
3257 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3258 DrawLevelElementThruMask(x, y, Back[x][y]);
3260 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3261 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3265 void DynaExplode(int ex, int ey)
3268 int dynabomb_element = Feld[ex][ey];
3269 int dynabomb_size = 1;
3270 boolean dynabomb_xl = FALSE;
3271 struct PlayerInfo *player;
3272 static int xy[4][2] =
3280 if (IS_ACTIVE_BOMB(dynabomb_element))
3282 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3283 dynabomb_size = player->dynabomb_size;
3284 dynabomb_xl = player->dynabomb_xl;
3285 player->dynabombs_left++;
3288 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3290 for (i = 0; i < NUM_DIRECTIONS; i++)
3292 for (j = 1; j <= dynabomb_size; j++)
3294 int x = ex + j * xy[i][0];
3295 int y = ey + j * xy[i][1];
3298 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3301 element = Feld[x][y];
3303 /* do not restart explosions of fields with active bombs */
3304 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3307 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3309 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3310 !IS_DIGGABLE(element) && !dynabomb_xl)
3316 void Bang(int x, int y)
3318 int element = MovingOrBlocked2Element(x, y);
3320 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3322 struct PlayerInfo *player = PLAYERINFO(x, y);
3324 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3325 player->element_nr);
3332 case EL_BD_BUTTERFLY:
3335 case EL_DARK_YAMYAM:
3339 RaiseScoreElement(element);
3340 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3342 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3343 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3344 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3345 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3346 case EL_DYNABOMB_INCREASE_NUMBER:
3347 case EL_DYNABOMB_INCREASE_SIZE:
3348 case EL_DYNABOMB_INCREASE_POWER:
3353 case EL_LAMP_ACTIVE:
3354 case EL_AMOEBA_TO_DIAMOND:
3355 if (IS_PLAYER(x, y))
3356 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3358 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3361 if (element_info[element].explosion_type == EXPLODES_CROSS)
3362 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3363 else if (element_info[element].explosion_type == EXPLODES_1X1)
3364 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3366 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3370 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3373 void SplashAcid(int x, int y)
3375 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3376 (!IN_LEV_FIELD(x - 1, y - 2) ||
3377 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3378 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3380 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3381 (!IN_LEV_FIELD(x + 1, y - 2) ||
3382 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3383 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3385 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3388 static void InitBeltMovement()
3390 static int belt_base_element[4] =
3392 EL_CONVEYOR_BELT_1_LEFT,
3393 EL_CONVEYOR_BELT_2_LEFT,
3394 EL_CONVEYOR_BELT_3_LEFT,
3395 EL_CONVEYOR_BELT_4_LEFT
3397 static int belt_base_active_element[4] =
3399 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3400 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3401 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3402 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3407 /* set frame order for belt animation graphic according to belt direction */
3408 for (i = 0; i < NUM_BELTS; i++)
3412 for (j = 0; j < NUM_BELT_PARTS; j++)
3414 int element = belt_base_active_element[belt_nr] + j;
3415 int graphic = el2img(element);
3417 if (game.belt_dir[i] == MV_LEFT)
3418 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3420 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3424 for (y = 0; y < lev_fieldy; y++)
3426 for (x = 0; x < lev_fieldx; x++)
3428 int element = Feld[x][y];
3430 for (i = 0; i < NUM_BELTS; i++)
3432 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3434 int e_belt_nr = getBeltNrFromBeltElement(element);
3437 if (e_belt_nr == belt_nr)
3439 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3441 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3449 static void ToggleBeltSwitch(int x, int y)
3451 static int belt_base_element[4] =
3453 EL_CONVEYOR_BELT_1_LEFT,
3454 EL_CONVEYOR_BELT_2_LEFT,
3455 EL_CONVEYOR_BELT_3_LEFT,
3456 EL_CONVEYOR_BELT_4_LEFT
3458 static int belt_base_active_element[4] =
3460 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3461 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3462 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3463 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3465 static int belt_base_switch_element[4] =
3467 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3468 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3469 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3470 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3472 static int belt_move_dir[4] =
3480 int element = Feld[x][y];
3481 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3482 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3483 int belt_dir = belt_move_dir[belt_dir_nr];
3486 if (!IS_BELT_SWITCH(element))
3489 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3490 game.belt_dir[belt_nr] = belt_dir;
3492 if (belt_dir_nr == 3)
3495 /* set frame order for belt animation graphic according to belt direction */
3496 for (i = 0; i < NUM_BELT_PARTS; i++)
3498 int element = belt_base_active_element[belt_nr] + i;
3499 int graphic = el2img(element);
3501 if (belt_dir == MV_LEFT)
3502 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3504 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3507 for (yy = 0; yy < lev_fieldy; yy++)
3509 for (xx = 0; xx < lev_fieldx; xx++)
3511 int element = Feld[xx][yy];
3513 if (IS_BELT_SWITCH(element))
3515 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3517 if (e_belt_nr == belt_nr)
3519 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3520 DrawLevelField(xx, yy);
3523 else if (IS_BELT(element) && belt_dir != MV_NONE)
3525 int e_belt_nr = getBeltNrFromBeltElement(element);
3527 if (e_belt_nr == belt_nr)
3529 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3531 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3532 DrawLevelField(xx, yy);
3535 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3537 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3539 if (e_belt_nr == belt_nr)
3541 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3543 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3544 DrawLevelField(xx, yy);
3551 static void ToggleSwitchgateSwitch(int x, int y)
3555 game.switchgate_pos = !game.switchgate_pos;
3557 for (yy = 0; yy < lev_fieldy; yy++)
3559 for (xx = 0; xx < lev_fieldx; xx++)
3561 int element = Feld[xx][yy];
3563 if (element == EL_SWITCHGATE_SWITCH_UP ||
3564 element == EL_SWITCHGATE_SWITCH_DOWN)
3566 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3567 DrawLevelField(xx, yy);
3569 else if (element == EL_SWITCHGATE_OPEN ||
3570 element == EL_SWITCHGATE_OPENING)
3572 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3574 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3576 else if (element == EL_SWITCHGATE_CLOSED ||
3577 element == EL_SWITCHGATE_CLOSING)
3579 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3581 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3587 static int getInvisibleActiveFromInvisibleElement(int element)
3589 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3590 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3591 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3595 static int getInvisibleFromInvisibleActiveElement(int element)
3597 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3598 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3599 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3603 static void RedrawAllLightSwitchesAndInvisibleElements()
3607 for (y = 0; y < lev_fieldy; y++)
3609 for (x = 0; x < lev_fieldx; x++)
3611 int element = Feld[x][y];
3613 if (element == EL_LIGHT_SWITCH &&
3614 game.light_time_left > 0)
3616 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3617 DrawLevelField(x, y);
3619 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3620 game.light_time_left == 0)
3622 Feld[x][y] = EL_LIGHT_SWITCH;
3623 DrawLevelField(x, y);
3625 else if (element == EL_INVISIBLE_STEELWALL ||
3626 element == EL_INVISIBLE_WALL ||
3627 element == EL_INVISIBLE_SAND)
3629 if (game.light_time_left > 0)
3630 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3632 DrawLevelField(x, y);
3634 /* uncrumble neighbour fields, if needed */
3635 if (element == EL_INVISIBLE_SAND)
3636 DrawLevelFieldCrumbledSandNeighbours(x, y);
3638 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3639 element == EL_INVISIBLE_WALL_ACTIVE ||
3640 element == EL_INVISIBLE_SAND_ACTIVE)
3642 if (game.light_time_left == 0)
3643 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3645 DrawLevelField(x, y);
3647 /* re-crumble neighbour fields, if needed */
3648 if (element == EL_INVISIBLE_SAND)
3649 DrawLevelFieldCrumbledSandNeighbours(x, y);
3655 static void ToggleLightSwitch(int x, int y)
3657 int element = Feld[x][y];
3659 game.light_time_left =
3660 (element == EL_LIGHT_SWITCH ?
3661 level.time_light * FRAMES_PER_SECOND : 0);
3663 RedrawAllLightSwitchesAndInvisibleElements();
3666 static void ActivateTimegateSwitch(int x, int y)
3670 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3672 for (yy = 0; yy < lev_fieldy; yy++)
3674 for (xx = 0; xx < lev_fieldx; xx++)
3676 int element = Feld[xx][yy];
3678 if (element == EL_TIMEGATE_CLOSED ||
3679 element == EL_TIMEGATE_CLOSING)
3681 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3682 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3686 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3688 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3689 DrawLevelField(xx, yy);
3696 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3699 void Impact(int x, int y)
3701 boolean last_line = (y == lev_fieldy - 1);
3702 boolean object_hit = FALSE;
3703 boolean impact = (last_line || object_hit);
3704 int element = Feld[x][y];
3705 int smashed = EL_STEELWALL;
3707 if (!last_line) /* check if element below was hit */
3709 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3712 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3713 MovDir[x][y + 1] != MV_DOWN ||
3714 MovPos[x][y + 1] <= TILEY / 2));
3716 /* do not smash moving elements that left the smashed field in time */
3717 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3718 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3722 smashed = MovingOrBlocked2Element(x, y + 1);
3724 impact = (last_line || object_hit);
3727 if (!last_line && smashed == EL_ACID) /* element falls into acid */
3729 SplashAcid(x, y + 1);
3733 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
3734 /* only reset graphic animation if graphic really changes after impact */
3736 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3738 ResetGfxAnimation(x, y);
3739 DrawLevelField(x, y);
3742 if (impact && CAN_EXPLODE_IMPACT(element))
3747 else if (impact && element == EL_PEARL)
3749 ResetGfxAnimation(x, y);
3751 Feld[x][y] = EL_PEARL_BREAKING;
3752 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3755 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
3757 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3762 if (impact && element == EL_AMOEBA_DROP)
3764 if (object_hit && IS_PLAYER(x, y + 1))
3765 KillPlayerUnlessEnemyProtected(x, y + 1);
3766 else if (object_hit && smashed == EL_PENGUIN)
3770 Feld[x][y] = EL_AMOEBA_GROWING;
3771 Store[x][y] = EL_AMOEBA_WET;
3773 ResetRandomAnimationValue(x, y);
3778 if (object_hit) /* check which object was hit */
3780 if (CAN_PASS_MAGIC_WALL(element) &&
3781 (smashed == EL_MAGIC_WALL ||
3782 smashed == EL_BD_MAGIC_WALL))
3785 int activated_magic_wall =
3786 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3787 EL_BD_MAGIC_WALL_ACTIVE);
3789 /* activate magic wall / mill */
3790 for (yy = 0; yy < lev_fieldy; yy++)
3791 for (xx = 0; xx < lev_fieldx; xx++)
3792 if (Feld[xx][yy] == smashed)
3793 Feld[xx][yy] = activated_magic_wall;
3795 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3796 game.magic_wall_active = TRUE;
3798 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3799 SND_MAGIC_WALL_ACTIVATING :
3800 SND_BD_MAGIC_WALL_ACTIVATING));
3803 if (IS_PLAYER(x, y + 1))
3805 if (CAN_SMASH_PLAYER(element))
3807 KillPlayerUnlessEnemyProtected(x, y + 1);
3811 else if (smashed == EL_PENGUIN)
3813 if (CAN_SMASH_PLAYER(element))
3819 else if (element == EL_BD_DIAMOND)
3821 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3827 else if (((element == EL_SP_INFOTRON ||
3828 element == EL_SP_ZONK) &&
3829 (smashed == EL_SP_SNIKSNAK ||
3830 smashed == EL_SP_ELECTRON ||
3831 smashed == EL_SP_DISK_ORANGE)) ||
3832 (element == EL_SP_INFOTRON &&
3833 smashed == EL_SP_DISK_YELLOW))
3838 else if (CAN_SMASH_EVERYTHING(element))
3840 if (IS_CLASSIC_ENEMY(smashed) ||
3841 CAN_EXPLODE_SMASHED(smashed))
3846 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3848 if (smashed == EL_LAMP ||
3849 smashed == EL_LAMP_ACTIVE)
3854 else if (smashed == EL_NUT)
3856 Feld[x][y + 1] = EL_NUT_BREAKING;
3857 PlayLevelSound(x, y, SND_NUT_BREAKING);
3858 RaiseScoreElement(EL_NUT);
3861 else if (smashed == EL_PEARL)
3863 ResetGfxAnimation(x, y);
3865 Feld[x][y + 1] = EL_PEARL_BREAKING;
3866 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3869 else if (smashed == EL_DIAMOND)
3871 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3872 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3875 else if (IS_BELT_SWITCH(smashed))
3877 ToggleBeltSwitch(x, y + 1);
3879 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3880 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3882 ToggleSwitchgateSwitch(x, y + 1);
3884 else if (smashed == EL_LIGHT_SWITCH ||
3885 smashed == EL_LIGHT_SWITCH_ACTIVE)
3887 ToggleLightSwitch(x, y + 1);
3892 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
3895 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
3897 CheckElementChangeBySide(x, y + 1, smashed, element,
3898 CE_SWITCHED, CH_SIDE_TOP);
3899 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
3905 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
3910 /* play sound of magic wall / mill */
3912 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3913 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3915 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3916 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3917 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3918 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3923 /* play sound of object that hits the ground */
3924 if (last_line || object_hit)
3925 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3928 inline static void TurnRoundExt(int x, int y)
3940 { 0, 0 }, { 0, 0 }, { 0, 0 },
3945 int left, right, back;
3949 { MV_DOWN, MV_UP, MV_RIGHT },
3950 { MV_UP, MV_DOWN, MV_LEFT },
3952 { MV_LEFT, MV_RIGHT, MV_DOWN },
3956 { MV_RIGHT, MV_LEFT, MV_UP }
3959 int element = Feld[x][y];
3960 int move_pattern = element_info[element].move_pattern;
3962 int old_move_dir = MovDir[x][y];
3963 int left_dir = turn[old_move_dir].left;
3964 int right_dir = turn[old_move_dir].right;
3965 int back_dir = turn[old_move_dir].back;
3967 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3968 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3969 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3970 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3972 int left_x = x + left_dx, left_y = y + left_dy;
3973 int right_x = x + right_dx, right_y = y + right_dy;
3974 int move_x = x + move_dx, move_y = y + move_dy;
3978 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3980 TestIfBadThingTouchesOtherBadThing(x, y);
3982 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
3983 MovDir[x][y] = right_dir;
3984 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3985 MovDir[x][y] = left_dir;
3987 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3989 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3992 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
3994 TestIfBadThingTouchesOtherBadThing(x, y);
3996 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
3997 MovDir[x][y] = left_dir;
3998 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3999 MovDir[x][y] = right_dir;
4001 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4003 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4006 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4008 TestIfBadThingTouchesOtherBadThing(x, y);
4010 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4011 MovDir[x][y] = left_dir;
4012 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4013 MovDir[x][y] = right_dir;
4015 if (MovDir[x][y] != old_move_dir)
4018 else if (element == EL_YAMYAM)
4020 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4021 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4023 if (can_turn_left && can_turn_right)
4024 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4025 else if (can_turn_left)
4026 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4027 else if (can_turn_right)
4028 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4030 MovDir[x][y] = back_dir;
4032 MovDelay[x][y] = 16 + 16 * RND(3);
4034 else if (element == EL_DARK_YAMYAM)
4036 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4038 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4041 if (can_turn_left && can_turn_right)
4042 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4043 else if (can_turn_left)
4044 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4045 else if (can_turn_right)
4046 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4048 MovDir[x][y] = back_dir;
4050 MovDelay[x][y] = 16 + 16 * RND(3);
4052 else if (element == EL_PACMAN)
4054 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4055 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4057 if (can_turn_left && can_turn_right)
4058 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4059 else if (can_turn_left)
4060 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4061 else if (can_turn_right)
4062 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4064 MovDir[x][y] = back_dir;
4066 MovDelay[x][y] = 6 + RND(40);
4068 else if (element == EL_PIG)
4070 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4071 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4072 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4073 boolean should_turn_left, should_turn_right, should_move_on;
4075 int rnd = RND(rnd_value);
4077 should_turn_left = (can_turn_left &&
4079 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4080 y + back_dy + left_dy)));
4081 should_turn_right = (can_turn_right &&
4083 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4084 y + back_dy + right_dy)));
4085 should_move_on = (can_move_on &&
4088 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4089 y + move_dy + left_dy) ||
4090 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4091 y + move_dy + right_dy)));
4093 if (should_turn_left || should_turn_right || should_move_on)
4095 if (should_turn_left && should_turn_right && should_move_on)
4096 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4097 rnd < 2 * rnd_value / 3 ? right_dir :
4099 else if (should_turn_left && should_turn_right)
4100 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4101 else if (should_turn_left && should_move_on)
4102 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4103 else if (should_turn_right && should_move_on)
4104 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4105 else if (should_turn_left)
4106 MovDir[x][y] = left_dir;
4107 else if (should_turn_right)
4108 MovDir[x][y] = right_dir;
4109 else if (should_move_on)
4110 MovDir[x][y] = old_move_dir;
4112 else if (can_move_on && rnd > rnd_value / 8)
4113 MovDir[x][y] = old_move_dir;
4114 else if (can_turn_left && can_turn_right)
4115 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4116 else if (can_turn_left && rnd > rnd_value / 8)
4117 MovDir[x][y] = left_dir;
4118 else if (can_turn_right && rnd > rnd_value/8)
4119 MovDir[x][y] = right_dir;
4121 MovDir[x][y] = back_dir;
4123 xx = x + move_xy[MovDir[x][y]].x;
4124 yy = y + move_xy[MovDir[x][y]].y;
4126 if (!IN_LEV_FIELD(xx, yy) ||
4127 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4128 MovDir[x][y] = old_move_dir;
4132 else if (element == EL_DRAGON)
4134 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4135 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4136 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4138 int rnd = RND(rnd_value);
4140 if (can_move_on && rnd > rnd_value / 8)
4141 MovDir[x][y] = old_move_dir;
4142 else if (can_turn_left && can_turn_right)
4143 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4144 else if (can_turn_left && rnd > rnd_value / 8)
4145 MovDir[x][y] = left_dir;
4146 else if (can_turn_right && rnd > rnd_value / 8)
4147 MovDir[x][y] = right_dir;
4149 MovDir[x][y] = back_dir;
4151 xx = x + move_xy[MovDir[x][y]].x;
4152 yy = y + move_xy[MovDir[x][y]].y;
4154 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4155 MovDir[x][y] = old_move_dir;
4159 else if (element == EL_MOLE)
4161 boolean can_move_on =
4162 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4163 IS_AMOEBOID(Feld[move_x][move_y]) ||
4164 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4167 boolean can_turn_left =
4168 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4169 IS_AMOEBOID(Feld[left_x][left_y])));
4171 boolean can_turn_right =
4172 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4173 IS_AMOEBOID(Feld[right_x][right_y])));
4175 if (can_turn_left && can_turn_right)
4176 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4177 else if (can_turn_left)
4178 MovDir[x][y] = left_dir;
4180 MovDir[x][y] = right_dir;
4183 if (MovDir[x][y] != old_move_dir)
4186 else if (element == EL_BALLOON)
4188 MovDir[x][y] = game.wind_direction;
4191 else if (element == EL_SPRING)
4193 if (MovDir[x][y] & MV_HORIZONTAL &&
4194 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4195 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4196 MovDir[x][y] = MV_NONE;
4200 else if (element == EL_ROBOT ||
4201 element == EL_SATELLITE ||
4202 element == EL_PENGUIN)
4204 int attr_x = -1, attr_y = -1;
4215 for (i = 0; i < MAX_PLAYERS; i++)
4217 struct PlayerInfo *player = &stored_player[i];
4218 int jx = player->jx, jy = player->jy;
4220 if (!player->active)
4224 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4232 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4233 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4234 game.engine_version < VERSION_IDENT(3,1,0,0)))
4240 if (element == EL_PENGUIN)
4243 static int xy[4][2] =
4251 for (i = 0; i < NUM_DIRECTIONS; i++)
4253 int ex = x + xy[i][0];
4254 int ey = y + xy[i][1];
4256 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4265 MovDir[x][y] = MV_NONE;
4267 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4268 else if (attr_x > x)
4269 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4271 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4272 else if (attr_y > y)
4273 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4275 if (element == EL_ROBOT)
4279 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4280 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4281 Moving2Blocked(x, y, &newx, &newy);
4283 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4284 MovDelay[x][y] = 8 + 8 * !RND(3);
4286 MovDelay[x][y] = 16;
4288 else if (element == EL_PENGUIN)
4294 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4296 boolean first_horiz = RND(2);
4297 int new_move_dir = MovDir[x][y];
4300 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4301 Moving2Blocked(x, y, &newx, &newy);
4303 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4307 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4308 Moving2Blocked(x, y, &newx, &newy);
4310 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4313 MovDir[x][y] = old_move_dir;
4317 else /* (element == EL_SATELLITE) */
4323 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4325 boolean first_horiz = RND(2);
4326 int new_move_dir = MovDir[x][y];
4329 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4330 Moving2Blocked(x, y, &newx, &newy);
4332 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4336 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4337 Moving2Blocked(x, y, &newx, &newy);
4339 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4342 MovDir[x][y] = old_move_dir;
4347 else if (move_pattern == MV_TURNING_LEFT ||
4348 move_pattern == MV_TURNING_RIGHT ||
4349 move_pattern == MV_TURNING_LEFT_RIGHT ||
4350 move_pattern == MV_TURNING_RIGHT_LEFT ||
4351 move_pattern == MV_TURNING_RANDOM ||
4352 move_pattern == MV_ALL_DIRECTIONS)
4354 boolean can_turn_left =
4355 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4356 boolean can_turn_right =
4357 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4359 if (element_info[element].move_stepsize == 0) /* "not moving" */
4362 if (move_pattern == MV_TURNING_LEFT)
4363 MovDir[x][y] = left_dir;
4364 else if (move_pattern == MV_TURNING_RIGHT)
4365 MovDir[x][y] = right_dir;
4366 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4367 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4368 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4369 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4370 else if (move_pattern == MV_TURNING_RANDOM)
4371 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4372 can_turn_right && !can_turn_left ? right_dir :
4373 RND(2) ? left_dir : right_dir);
4374 else if (can_turn_left && can_turn_right)
4375 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4376 else if (can_turn_left)
4377 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4378 else if (can_turn_right)
4379 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4381 MovDir[x][y] = back_dir;
4383 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4385 else if (move_pattern == MV_HORIZONTAL ||
4386 move_pattern == MV_VERTICAL)
4388 if (move_pattern & old_move_dir)
4389 MovDir[x][y] = back_dir;
4390 else if (move_pattern == MV_HORIZONTAL)
4391 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4392 else if (move_pattern == MV_VERTICAL)
4393 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4395 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4397 else if (move_pattern & MV_ANY_DIRECTION)
4399 MovDir[x][y] = move_pattern;
4400 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4402 else if (move_pattern & MV_WIND_DIRECTION)
4404 MovDir[x][y] = game.wind_direction;
4405 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4407 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4409 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4410 MovDir[x][y] = left_dir;
4411 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4412 MovDir[x][y] = right_dir;
4414 if (MovDir[x][y] != old_move_dir)
4415 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4417 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4419 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4420 MovDir[x][y] = right_dir;
4421 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4422 MovDir[x][y] = left_dir;
4424 if (MovDir[x][y] != old_move_dir)
4425 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4427 else if (move_pattern == MV_TOWARDS_PLAYER ||
4428 move_pattern == MV_AWAY_FROM_PLAYER)
4430 int attr_x = -1, attr_y = -1;
4432 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4443 for (i = 0; i < MAX_PLAYERS; i++)
4445 struct PlayerInfo *player = &stored_player[i];
4446 int jx = player->jx, jy = player->jy;
4448 if (!player->active)
4452 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4460 MovDir[x][y] = MV_NONE;
4462 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4463 else if (attr_x > x)
4464 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4466 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4467 else if (attr_y > y)
4468 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4470 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4472 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4474 boolean first_horiz = RND(2);
4475 int new_move_dir = MovDir[x][y];
4477 if (element_info[element].move_stepsize == 0) /* "not moving" */
4479 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
4480 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4486 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4487 Moving2Blocked(x, y, &newx, &newy);
4489 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4493 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4494 Moving2Blocked(x, y, &newx, &newy);
4496 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4499 MovDir[x][y] = old_move_dir;
4502 else if (move_pattern == MV_WHEN_PUSHED ||
4503 move_pattern == MV_WHEN_DROPPED)
4505 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4506 MovDir[x][y] = MV_NONE;
4510 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4512 static int test_xy[7][2] =
4522 static int test_dir[7] =
4532 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4533 int move_preference = -1000000; /* start with very low preference */
4534 int new_move_dir = MV_NONE;
4535 int start_test = RND(4);
4538 for (i = 0; i < NUM_DIRECTIONS; i++)
4540 int move_dir = test_dir[start_test + i];
4541 int move_dir_preference;
4543 xx = x + test_xy[start_test + i][0];
4544 yy = y + test_xy[start_test + i][1];
4546 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4547 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4549 new_move_dir = move_dir;
4554 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4557 move_dir_preference = -1 * RunnerVisit[xx][yy];
4558 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4559 move_dir_preference = PlayerVisit[xx][yy];
4561 if (move_dir_preference > move_preference)
4563 /* prefer field that has not been visited for the longest time */
4564 move_preference = move_dir_preference;
4565 new_move_dir = move_dir;
4567 else if (move_dir_preference == move_preference &&
4568 move_dir == old_move_dir)
4570 /* prefer last direction when all directions are preferred equally */
4571 move_preference = move_dir_preference;
4572 new_move_dir = move_dir;
4576 MovDir[x][y] = new_move_dir;
4577 if (old_move_dir != new_move_dir)
4578 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4582 static void TurnRound(int x, int y)
4584 int direction = MovDir[x][y];
4588 GfxDir[x][y] = MovDir[x][y];
4590 if (direction != MovDir[x][y])
4594 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4597 static boolean JustBeingPushed(int x, int y)
4601 for (i = 0; i < MAX_PLAYERS; i++)
4603 struct PlayerInfo *player = &stored_player[i];
4605 if (player->active && player->is_pushing && player->MovPos)
4607 int next_jx = player->jx + (player->jx - player->last_jx);
4608 int next_jy = player->jy + (player->jy - player->last_jy);
4610 if (x == next_jx && y == next_jy)
4618 void StartMoving(int x, int y)
4620 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4621 int element = Feld[x][y];
4626 if (MovDelay[x][y] == 0)
4627 GfxAction[x][y] = ACTION_DEFAULT;
4629 if (CAN_FALL(element) && y < lev_fieldy - 1)
4631 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4632 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4633 if (JustBeingPushed(x, y))
4636 if (element == EL_QUICKSAND_FULL)
4638 if (IS_FREE(x, y + 1))
4640 InitMovingField(x, y, MV_DOWN);
4641 started_moving = TRUE;
4643 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4644 Store[x][y] = EL_ROCK;
4646 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4648 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4650 if (!MovDelay[x][y])
4651 MovDelay[x][y] = TILEY + 1;
4660 Feld[x][y] = EL_QUICKSAND_EMPTY;
4661 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4662 Store[x][y + 1] = Store[x][y];
4665 PlayLevelSoundAction(x, y, ACTION_FILLING);
4668 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4669 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4671 InitMovingField(x, y, MV_DOWN);
4672 started_moving = TRUE;
4674 Feld[x][y] = EL_QUICKSAND_FILLING;
4675 Store[x][y] = element;
4677 PlayLevelSoundAction(x, y, ACTION_FILLING);
4679 else if (element == EL_MAGIC_WALL_FULL)
4681 if (IS_FREE(x, y + 1))
4683 InitMovingField(x, y, MV_DOWN);
4684 started_moving = TRUE;
4686 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4687 Store[x][y] = EL_CHANGED(Store[x][y]);
4689 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4691 if (!MovDelay[x][y])
4692 MovDelay[x][y] = TILEY/4 + 1;
4701 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4702 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4703 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4707 else if (element == EL_BD_MAGIC_WALL_FULL)
4709 if (IS_FREE(x, y + 1))
4711 InitMovingField(x, y, MV_DOWN);
4712 started_moving = TRUE;
4714 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4715 Store[x][y] = EL_CHANGED2(Store[x][y]);
4717 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4719 if (!MovDelay[x][y])
4720 MovDelay[x][y] = TILEY/4 + 1;
4729 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4730 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4731 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4735 else if (CAN_PASS_MAGIC_WALL(element) &&
4736 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4737 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4739 InitMovingField(x, y, MV_DOWN);
4740 started_moving = TRUE;
4743 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4744 EL_BD_MAGIC_WALL_FILLING);
4745 Store[x][y] = element;
4747 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4749 SplashAcid(x, y + 1);
4751 InitMovingField(x, y, MV_DOWN);
4752 started_moving = TRUE;
4754 Store[x][y] = EL_ACID;
4756 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4757 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
4759 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4760 CAN_FALL(element) && WasJustFalling[x][y] &&
4761 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
4763 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4764 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4765 (Feld[x][y + 1] == EL_BLOCKED)))
4767 /* this is needed for a special case not covered by calling "Impact()"
4768 from "ContinueMoving()": if an element moves to a tile directly below
4769 another element which was just falling on that tile (which was empty
4770 in the previous frame), the falling element above would just stop
4771 instead of smashing the element below (in previous version, the above
4772 element was just checked for "moving" instead of "falling", resulting
4773 in incorrect smashes caused by horizontal movement of the above
4774 element; also, the case of the player being the element to smash was
4775 simply not covered here... :-/ ) */
4777 CheckCollision[x][y] = 0;
4781 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4783 if (MovDir[x][y] == MV_NONE)
4785 InitMovingField(x, y, MV_DOWN);
4786 started_moving = TRUE;
4789 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4791 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4792 MovDir[x][y] = MV_DOWN;
4794 InitMovingField(x, y, MV_DOWN);
4795 started_moving = TRUE;
4797 else if (element == EL_AMOEBA_DROP)
4799 Feld[x][y] = EL_AMOEBA_GROWING;
4800 Store[x][y] = EL_AMOEBA_WET;
4802 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4803 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4804 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4805 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4807 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4808 (IS_FREE(x - 1, y + 1) ||
4809 Feld[x - 1][y + 1] == EL_ACID));
4810 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4811 (IS_FREE(x + 1, y + 1) ||
4812 Feld[x + 1][y + 1] == EL_ACID));
4813 boolean can_fall_any = (can_fall_left || can_fall_right);
4814 boolean can_fall_both = (can_fall_left && can_fall_right);
4815 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4817 #if USE_NEW_ALL_SLIPPERY
4818 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
4820 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4821 can_fall_right = FALSE;
4822 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4823 can_fall_left = FALSE;
4824 else if (slippery_type == SLIPPERY_ONLY_LEFT)
4825 can_fall_right = FALSE;
4826 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4827 can_fall_left = FALSE;
4829 can_fall_any = (can_fall_left || can_fall_right);
4830 can_fall_both = FALSE;
4833 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4835 if (slippery_type == SLIPPERY_ONLY_LEFT)
4836 can_fall_right = FALSE;
4837 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4838 can_fall_left = FALSE;
4839 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4840 can_fall_right = FALSE;
4841 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4842 can_fall_left = FALSE;
4844 can_fall_any = (can_fall_left || can_fall_right);
4845 can_fall_both = (can_fall_left && can_fall_right);
4849 #if USE_NEW_ALL_SLIPPERY
4851 #if USE_NEW_SP_SLIPPERY
4852 /* !!! better use the same properties as for custom elements here !!! */
4853 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
4854 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
4856 can_fall_right = FALSE; /* slip down on left side */
4857 can_fall_both = FALSE;
4862 #if USE_NEW_ALL_SLIPPERY
4865 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
4866 can_fall_right = FALSE; /* slip down on left side */
4868 can_fall_left = !(can_fall_right = RND(2));
4870 can_fall_both = FALSE;
4875 if (game.emulation == EMU_BOULDERDASH ||
4876 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
4877 can_fall_right = FALSE; /* slip down on left side */
4879 can_fall_left = !(can_fall_right = RND(2));
4881 can_fall_both = FALSE;
4887 /* if not determined otherwise, prefer left side for slipping down */
4888 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4889 started_moving = TRUE;
4893 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4895 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4898 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4899 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4900 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4901 int belt_dir = game.belt_dir[belt_nr];
4903 if ((belt_dir == MV_LEFT && left_is_free) ||
4904 (belt_dir == MV_RIGHT && right_is_free))
4906 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4908 InitMovingField(x, y, belt_dir);
4909 started_moving = TRUE;
4911 Pushed[x][y] = TRUE;
4912 Pushed[nextx][y] = TRUE;
4914 GfxAction[x][y] = ACTION_DEFAULT;
4918 MovDir[x][y] = 0; /* if element was moving, stop it */
4923 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4925 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
4927 if (CAN_MOVE(element) && !started_moving)
4930 int move_pattern = element_info[element].move_pattern;
4935 if (MovDir[x][y] == MV_NONE)
4937 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
4938 x, y, element, element_info[element].token_name);
4939 printf("StartMoving(): This should never happen!\n");
4944 Moving2Blocked(x, y, &newx, &newy);
4946 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4949 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4950 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
4952 WasJustMoving[x][y] = 0;
4953 CheckCollision[x][y] = 0;
4955 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4957 if (Feld[x][y] != element) /* element has changed */
4961 if (!MovDelay[x][y]) /* start new movement phase */
4963 /* all objects that can change their move direction after each step
4964 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4966 if (element != EL_YAMYAM &&
4967 element != EL_DARK_YAMYAM &&
4968 element != EL_PACMAN &&
4969 !(move_pattern & MV_ANY_DIRECTION) &&
4970 move_pattern != MV_TURNING_LEFT &&
4971 move_pattern != MV_TURNING_RIGHT &&
4972 move_pattern != MV_TURNING_LEFT_RIGHT &&
4973 move_pattern != MV_TURNING_RIGHT_LEFT &&
4974 move_pattern != MV_TURNING_RANDOM)
4978 if (MovDelay[x][y] && (element == EL_BUG ||
4979 element == EL_SPACESHIP ||
4980 element == EL_SP_SNIKSNAK ||
4981 element == EL_SP_ELECTRON ||
4982 element == EL_MOLE))
4983 DrawLevelField(x, y);
4987 if (MovDelay[x][y]) /* wait some time before next movement */
4991 if (element == EL_ROBOT ||
4992 element == EL_YAMYAM ||
4993 element == EL_DARK_YAMYAM)
4995 DrawLevelElementAnimationIfNeeded(x, y, element);
4996 PlayLevelSoundAction(x, y, ACTION_WAITING);
4998 else if (element == EL_SP_ELECTRON)
4999 DrawLevelElementAnimationIfNeeded(x, y, element);
5000 else if (element == EL_DRAGON)
5003 int dir = MovDir[x][y];
5004 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5005 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5006 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5007 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5008 dir == MV_UP ? IMG_FLAMES_1_UP :
5009 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5010 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5012 GfxAction[x][y] = ACTION_ATTACKING;
5014 if (IS_PLAYER(x, y))
5015 DrawPlayerField(x, y);
5017 DrawLevelField(x, y);
5019 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5021 for (i = 1; i <= 3; i++)
5023 int xx = x + i * dx;
5024 int yy = y + i * dy;
5025 int sx = SCREENX(xx);
5026 int sy = SCREENY(yy);
5027 int flame_graphic = graphic + (i - 1);
5029 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5034 int flamed = MovingOrBlocked2Element(xx, yy);
5038 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5040 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5041 RemoveMovingField(xx, yy);
5043 RemoveField(xx, yy);
5045 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5048 RemoveMovingField(xx, yy);
5051 ChangeDelay[xx][yy] = 0;
5053 Feld[xx][yy] = EL_FLAMES;
5055 if (IN_SCR_FIELD(sx, sy))
5057 DrawLevelFieldCrumbledSand(xx, yy);
5058 DrawGraphic(sx, sy, flame_graphic, frame);
5063 if (Feld[xx][yy] == EL_FLAMES)
5064 Feld[xx][yy] = EL_EMPTY;
5065 DrawLevelField(xx, yy);
5070 if (MovDelay[x][y]) /* element still has to wait some time */
5072 PlayLevelSoundAction(x, y, ACTION_WAITING);
5078 /* now make next step */
5080 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5082 if (DONT_COLLIDE_WITH(element) &&
5083 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5084 !PLAYER_ENEMY_PROTECTED(newx, newy))
5086 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5091 else if (CAN_MOVE_INTO_ACID(element) &&
5092 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5093 (MovDir[x][y] == MV_DOWN ||
5094 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5096 SplashAcid(newx, newy);
5097 Store[x][y] = EL_ACID;
5099 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5101 if (Feld[newx][newy] == EL_EXIT_OPEN)
5104 DrawLevelField(x, y);
5106 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5107 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5108 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5110 local_player->friends_still_needed--;
5111 if (!local_player->friends_still_needed &&
5112 !local_player->GameOver && AllPlayersGone)
5113 local_player->LevelSolved = local_player->GameOver = TRUE;
5117 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5119 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5120 DrawLevelField(newx, newy);
5122 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5124 else if (!IS_FREE(newx, newy))
5126 GfxAction[x][y] = ACTION_WAITING;
5128 if (IS_PLAYER(x, y))
5129 DrawPlayerField(x, y);
5131 DrawLevelField(x, y);
5136 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5138 if (IS_FOOD_PIG(Feld[newx][newy]))
5140 if (IS_MOVING(newx, newy))
5141 RemoveMovingField(newx, newy);
5144 Feld[newx][newy] = EL_EMPTY;
5145 DrawLevelField(newx, newy);
5148 PlayLevelSound(x, y, SND_PIG_DIGGING);
5150 else if (!IS_FREE(newx, newy))
5152 if (IS_PLAYER(x, y))
5153 DrawPlayerField(x, y);
5155 DrawLevelField(x, y);
5160 else if (IS_CUSTOM_ELEMENT(element) &&
5161 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5163 int new_element = Feld[newx][newy];
5165 if (!IS_FREE(newx, newy))
5167 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5168 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5171 /* no element can dig solid indestructible elements */
5172 if (IS_INDESTRUCTIBLE(new_element) &&
5173 !IS_DIGGABLE(new_element) &&
5174 !IS_COLLECTIBLE(new_element))
5177 if (AmoebaNr[newx][newy] &&
5178 (new_element == EL_AMOEBA_FULL ||
5179 new_element == EL_BD_AMOEBA ||
5180 new_element == EL_AMOEBA_GROWING))
5182 AmoebaCnt[AmoebaNr[newx][newy]]--;
5183 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5186 if (IS_MOVING(newx, newy))
5187 RemoveMovingField(newx, newy);
5190 RemoveField(newx, newy);
5191 DrawLevelField(newx, newy);
5194 /* if digged element was about to explode, prevent the explosion */
5195 ExplodeField[newx][newy] = EX_TYPE_NONE;
5197 PlayLevelSoundAction(x, y, action);
5200 Store[newx][newy] = EL_EMPTY;
5201 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5203 int move_leave_element = element_info[element].move_leave_element;
5205 /* this makes it possible to leave the removed element again */
5206 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
5207 new_element : move_leave_element);
5210 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5212 RunnerVisit[x][y] = FrameCounter;
5213 PlayerVisit[x][y] /= 8; /* expire player visit path */
5216 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5218 if (!IS_FREE(newx, newy))
5220 if (IS_PLAYER(x, y))
5221 DrawPlayerField(x, y);
5223 DrawLevelField(x, y);
5229 boolean wanna_flame = !RND(10);
5230 int dx = newx - x, dy = newy - y;
5231 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5232 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5233 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5234 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5235 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5236 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5239 IS_CLASSIC_ENEMY(element1) ||
5240 IS_CLASSIC_ENEMY(element2)) &&
5241 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5242 element1 != EL_FLAMES && element2 != EL_FLAMES)
5244 ResetGfxAnimation(x, y);
5245 GfxAction[x][y] = ACTION_ATTACKING;
5247 if (IS_PLAYER(x, y))
5248 DrawPlayerField(x, y);
5250 DrawLevelField(x, y);
5252 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5254 MovDelay[x][y] = 50;
5258 RemoveField(newx, newy);
5260 Feld[newx][newy] = EL_FLAMES;
5261 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5264 RemoveField(newx1, newy1);
5266 Feld[newx1][newy1] = EL_FLAMES;
5268 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5271 RemoveField(newx2, newy2);
5273 Feld[newx2][newy2] = EL_FLAMES;
5280 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5281 Feld[newx][newy] == EL_DIAMOND)
5283 if (IS_MOVING(newx, newy))
5284 RemoveMovingField(newx, newy);
5287 Feld[newx][newy] = EL_EMPTY;
5288 DrawLevelField(newx, newy);
5291 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5293 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5294 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5296 if (AmoebaNr[newx][newy])
5298 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5299 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5300 Feld[newx][newy] == EL_BD_AMOEBA)
5301 AmoebaCnt[AmoebaNr[newx][newy]]--;
5306 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5308 RemoveMovingField(newx, newy);
5311 if (IS_MOVING(newx, newy))
5313 RemoveMovingField(newx, newy);
5318 Feld[newx][newy] = EL_EMPTY;
5319 DrawLevelField(newx, newy);
5322 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5324 else if ((element == EL_PACMAN || element == EL_MOLE)
5325 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5327 if (AmoebaNr[newx][newy])
5329 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5330 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5331 Feld[newx][newy] == EL_BD_AMOEBA)
5332 AmoebaCnt[AmoebaNr[newx][newy]]--;
5335 if (element == EL_MOLE)
5337 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5338 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5340 ResetGfxAnimation(x, y);
5341 GfxAction[x][y] = ACTION_DIGGING;
5342 DrawLevelField(x, y);
5344 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5346 return; /* wait for shrinking amoeba */
5348 else /* element == EL_PACMAN */
5350 Feld[newx][newy] = EL_EMPTY;
5351 DrawLevelField(newx, newy);
5352 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5355 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5356 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5357 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5359 /* wait for shrinking amoeba to completely disappear */
5362 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5364 /* object was running against a wall */
5369 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
5370 if (move_pattern & MV_ANY_DIRECTION &&
5371 move_pattern == MovDir[x][y])
5373 int blocking_element =
5374 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5376 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5379 element = Feld[x][y]; /* element might have changed */
5383 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5384 DrawLevelElementAnimation(x, y, element);
5386 if (DONT_TOUCH(element))
5387 TestIfBadThingTouchesPlayer(x, y);
5392 InitMovingField(x, y, MovDir[x][y]);
5394 PlayLevelSoundAction(x, y, ACTION_MOVING);
5398 ContinueMoving(x, y);
5401 void ContinueMoving(int x, int y)
5403 int element = Feld[x][y];
5404 int stored = Store[x][y];
5405 struct ElementInfo *ei = &element_info[element];
5406 int direction = MovDir[x][y];
5407 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5408 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5409 int newx = x + dx, newy = y + dy;
5410 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5411 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5412 boolean last_line = (newy == lev_fieldy - 1);
5414 MovPos[x][y] += getElementMoveStepsize(x, y);
5416 if (pushed_by_player) /* special case: moving object pushed by player */
5417 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5419 if (ABS(MovPos[x][y]) < TILEX)
5421 DrawLevelField(x, y);
5423 return; /* element is still moving */
5426 /* element reached destination field */
5428 Feld[x][y] = EL_EMPTY;
5429 Feld[newx][newy] = element;
5430 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5432 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
5434 element = Feld[newx][newy] = EL_ACID;
5436 else if (element == EL_MOLE)
5438 Feld[x][y] = EL_SAND;
5440 DrawLevelFieldCrumbledSandNeighbours(x, y);
5442 else if (element == EL_QUICKSAND_FILLING)
5444 element = Feld[newx][newy] = get_next_element(element);
5445 Store[newx][newy] = Store[x][y];
5447 else if (element == EL_QUICKSAND_EMPTYING)
5449 Feld[x][y] = get_next_element(element);
5450 element = Feld[newx][newy] = Store[x][y];
5452 else if (element == EL_MAGIC_WALL_FILLING)
5454 element = Feld[newx][newy] = get_next_element(element);
5455 if (!game.magic_wall_active)
5456 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5457 Store[newx][newy] = Store[x][y];
5459 else if (element == EL_MAGIC_WALL_EMPTYING)
5461 Feld[x][y] = get_next_element(element);
5462 if (!game.magic_wall_active)
5463 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5464 element = Feld[newx][newy] = Store[x][y];
5466 #if USE_NEW_CUSTOM_VALUE
5467 InitField(newx, newy, FALSE);
5470 else if (element == EL_BD_MAGIC_WALL_FILLING)
5472 element = Feld[newx][newy] = get_next_element(element);
5473 if (!game.magic_wall_active)
5474 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5475 Store[newx][newy] = Store[x][y];
5477 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5479 Feld[x][y] = get_next_element(element);
5480 if (!game.magic_wall_active)
5481 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5482 element = Feld[newx][newy] = Store[x][y];
5484 #if USE_NEW_CUSTOM_VALUE
5485 InitField(newx, newy, FALSE);
5488 else if (element == EL_AMOEBA_DROPPING)
5490 Feld[x][y] = get_next_element(element);
5491 element = Feld[newx][newy] = Store[x][y];
5493 else if (element == EL_SOKOBAN_OBJECT)
5496 Feld[x][y] = Back[x][y];
5498 if (Back[newx][newy])
5499 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5501 Back[x][y] = Back[newx][newy] = 0;
5504 Store[x][y] = EL_EMPTY;
5509 MovDelay[newx][newy] = 0;
5511 if (CAN_CHANGE(element))
5513 /* copy element change control values to new field */
5514 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5515 ChangePage[newx][newy] = ChangePage[x][y];
5516 Changed[newx][newy] = Changed[x][y];
5517 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5519 #if USE_NEW_CUSTOM_VALUE
5520 CustomValue[newx][newy] = CustomValue[x][y];
5524 ChangeDelay[x][y] = 0;
5525 ChangePage[x][y] = -1;
5526 Changed[x][y] = FALSE;
5527 ChangeEvent[x][y] = -1;
5529 #if USE_NEW_CUSTOM_VALUE
5530 CustomValue[x][y] = 0;
5533 /* copy animation control values to new field */
5534 GfxFrame[newx][newy] = GfxFrame[x][y];
5535 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5536 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5537 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5539 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5541 /* some elements can leave other elements behind after moving */
5542 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
5543 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
5544 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
5546 int move_leave_element = ei->move_leave_element;
5548 /* this makes it possible to leave the removed element again */
5549 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
5550 ei->move_leave_element == EL_TRIGGER_ELEMENT)
5551 move_leave_element = stored;
5553 Feld[x][y] = move_leave_element;
5555 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
5556 MovDir[x][y] = direction;
5558 InitField(x, y, FALSE);
5560 if (GFX_CRUMBLED(Feld[x][y]))
5561 DrawLevelFieldCrumbledSandNeighbours(x, y);
5563 if (ELEM_IS_PLAYER(move_leave_element))
5564 RelocatePlayer(x, y, move_leave_element);
5567 /* do this after checking for left-behind element */
5568 ResetGfxAnimation(x, y); /* reset animation values for old field */
5570 if (!CAN_MOVE(element) ||
5571 (CAN_FALL(element) && direction == MV_DOWN &&
5572 (element == EL_SPRING ||
5573 element_info[element].move_pattern == MV_WHEN_PUSHED ||
5574 element_info[element].move_pattern == MV_WHEN_DROPPED)))
5575 GfxDir[x][y] = MovDir[newx][newy] = 0;
5577 DrawLevelField(x, y);
5578 DrawLevelField(newx, newy);
5580 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5582 /* prevent pushed element from moving on in pushed direction */
5583 if (pushed_by_player && CAN_MOVE(element) &&
5584 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5585 !(element_info[element].move_pattern & direction))
5586 TurnRound(newx, newy);
5588 /* prevent elements on conveyor belt from moving on in last direction */
5589 if (pushed_by_conveyor && CAN_FALL(element) &&
5590 direction & MV_HORIZONTAL)
5591 MovDir[newx][newy] = 0;
5593 if (!pushed_by_player)
5595 int nextx = newx + dx, nexty = newy + dy;
5596 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
5598 WasJustMoving[newx][newy] = 3;
5600 if (CAN_FALL(element) && direction == MV_DOWN)
5601 WasJustFalling[newx][newy] = 3;
5603 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
5604 CheckCollision[newx][newy] = 2;
5607 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5609 TestIfBadThingTouchesPlayer(newx, newy);
5610 TestIfBadThingTouchesFriend(newx, newy);
5612 if (!IS_CUSTOM_ELEMENT(element))
5613 TestIfBadThingTouchesOtherBadThing(newx, newy);
5615 else if (element == EL_PENGUIN)
5616 TestIfFriendTouchesBadThing(newx, newy);
5618 /* give the player one last chance (one more frame) to move away */
5619 if (CAN_FALL(element) && direction == MV_DOWN &&
5620 (last_line || (!IS_FREE(x, newy + 1) &&
5621 (!IS_PLAYER(x, newy + 1) ||
5622 game.engine_version < VERSION_IDENT(3,1,1,0)))))
5625 if (pushed_by_player && !game.use_change_when_pushing_bug)
5627 int dig_side = MV_DIR_OPPOSITE(direction);
5628 struct PlayerInfo *player = PLAYERINFO(x, y);
5630 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
5631 player->index_bit, dig_side);
5632 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
5633 player->index_bit, dig_side);
5636 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5638 TestIfElementHitsCustomElement(newx, newy, direction);
5639 TestIfPlayerTouchesCustomElement(newx, newy);
5640 TestIfElementTouchesCustomElement(newx, newy);
5643 int AmoebeNachbarNr(int ax, int ay)
5646 int element = Feld[ax][ay];
5648 static int xy[4][2] =
5656 for (i = 0; i < NUM_DIRECTIONS; i++)
5658 int x = ax + xy[i][0];
5659 int y = ay + xy[i][1];
5661 if (!IN_LEV_FIELD(x, y))
5664 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5665 group_nr = AmoebaNr[x][y];
5671 void AmoebenVereinigen(int ax, int ay)
5673 int i, x, y, xx, yy;
5674 int new_group_nr = AmoebaNr[ax][ay];
5675 static int xy[4][2] =
5683 if (new_group_nr == 0)
5686 for (i = 0; i < NUM_DIRECTIONS; i++)
5691 if (!IN_LEV_FIELD(x, y))
5694 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5695 Feld[x][y] == EL_BD_AMOEBA ||
5696 Feld[x][y] == EL_AMOEBA_DEAD) &&
5697 AmoebaNr[x][y] != new_group_nr)
5699 int old_group_nr = AmoebaNr[x][y];
5701 if (old_group_nr == 0)
5704 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5705 AmoebaCnt[old_group_nr] = 0;
5706 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5707 AmoebaCnt2[old_group_nr] = 0;
5709 for (yy = 0; yy < lev_fieldy; yy++)
5711 for (xx = 0; xx < lev_fieldx; xx++)
5713 if (AmoebaNr[xx][yy] == old_group_nr)
5714 AmoebaNr[xx][yy] = new_group_nr;
5721 void AmoebeUmwandeln(int ax, int ay)
5725 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5727 int group_nr = AmoebaNr[ax][ay];
5732 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5733 printf("AmoebeUmwandeln(): This should never happen!\n");
5738 for (y = 0; y < lev_fieldy; y++)
5740 for (x = 0; x < lev_fieldx; x++)
5742 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5745 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5749 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5750 SND_AMOEBA_TURNING_TO_GEM :
5751 SND_AMOEBA_TURNING_TO_ROCK));
5756 static int xy[4][2] =
5764 for (i = 0; i < NUM_DIRECTIONS; i++)
5769 if (!IN_LEV_FIELD(x, y))
5772 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5774 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5775 SND_AMOEBA_TURNING_TO_GEM :
5776 SND_AMOEBA_TURNING_TO_ROCK));
5783 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5786 int group_nr = AmoebaNr[ax][ay];
5787 boolean done = FALSE;
5792 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5793 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5798 for (y = 0; y < lev_fieldy; y++)
5800 for (x = 0; x < lev_fieldx; x++)
5802 if (AmoebaNr[x][y] == group_nr &&
5803 (Feld[x][y] == EL_AMOEBA_DEAD ||
5804 Feld[x][y] == EL_BD_AMOEBA ||
5805 Feld[x][y] == EL_AMOEBA_GROWING))
5808 Feld[x][y] = new_element;
5809 InitField(x, y, FALSE);
5810 DrawLevelField(x, y);
5817 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5818 SND_BD_AMOEBA_TURNING_TO_ROCK :
5819 SND_BD_AMOEBA_TURNING_TO_GEM));
5822 void AmoebeWaechst(int x, int y)
5824 static unsigned long sound_delay = 0;
5825 static unsigned long sound_delay_value = 0;
5827 if (!MovDelay[x][y]) /* start new growing cycle */
5831 if (DelayReached(&sound_delay, sound_delay_value))
5833 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5834 sound_delay_value = 30;
5838 if (MovDelay[x][y]) /* wait some time before growing bigger */
5841 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5843 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5844 6 - MovDelay[x][y]);
5846 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5849 if (!MovDelay[x][y])
5851 Feld[x][y] = Store[x][y];
5853 DrawLevelField(x, y);
5858 void AmoebaDisappearing(int x, int y)
5860 static unsigned long sound_delay = 0;
5861 static unsigned long sound_delay_value = 0;
5863 if (!MovDelay[x][y]) /* start new shrinking cycle */
5867 if (DelayReached(&sound_delay, sound_delay_value))
5868 sound_delay_value = 30;
5871 if (MovDelay[x][y]) /* wait some time before shrinking */
5874 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5876 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5877 6 - MovDelay[x][y]);
5879 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5882 if (!MovDelay[x][y])
5884 Feld[x][y] = EL_EMPTY;
5885 DrawLevelField(x, y);
5887 /* don't let mole enter this field in this cycle;
5888 (give priority to objects falling to this field from above) */
5894 void AmoebeAbleger(int ax, int ay)
5897 int element = Feld[ax][ay];
5898 int graphic = el2img(element);
5899 int newax = ax, neway = ay;
5900 static int xy[4][2] =
5908 if (!level.amoeba_speed)
5910 Feld[ax][ay] = EL_AMOEBA_DEAD;
5911 DrawLevelField(ax, ay);
5915 if (IS_ANIMATED(graphic))
5916 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5918 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5919 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5921 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5924 if (MovDelay[ax][ay])
5928 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5931 int x = ax + xy[start][0];
5932 int y = ay + xy[start][1];
5934 if (!IN_LEV_FIELD(x, y))
5937 if (IS_FREE(x, y) ||
5938 CAN_GROW_INTO(Feld[x][y]) ||
5939 Feld[x][y] == EL_QUICKSAND_EMPTY)
5945 if (newax == ax && neway == ay)
5948 else /* normal or "filled" (BD style) amoeba */
5951 boolean waiting_for_player = FALSE;
5953 for (i = 0; i < NUM_DIRECTIONS; i++)
5955 int j = (start + i) % 4;
5956 int x = ax + xy[j][0];
5957 int y = ay + xy[j][1];
5959 if (!IN_LEV_FIELD(x, y))
5962 if (IS_FREE(x, y) ||
5963 CAN_GROW_INTO(Feld[x][y]) ||
5964 Feld[x][y] == EL_QUICKSAND_EMPTY)
5970 else if (IS_PLAYER(x, y))
5971 waiting_for_player = TRUE;
5974 if (newax == ax && neway == ay) /* amoeba cannot grow */
5976 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
5978 Feld[ax][ay] = EL_AMOEBA_DEAD;
5979 DrawLevelField(ax, ay);
5980 AmoebaCnt[AmoebaNr[ax][ay]]--;
5982 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5984 if (element == EL_AMOEBA_FULL)
5985 AmoebeUmwandeln(ax, ay);
5986 else if (element == EL_BD_AMOEBA)
5987 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5992 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5994 /* amoeba gets larger by growing in some direction */
5996 int new_group_nr = AmoebaNr[ax][ay];
5999 if (new_group_nr == 0)
6001 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6002 printf("AmoebeAbleger(): This should never happen!\n");
6007 AmoebaNr[newax][neway] = new_group_nr;
6008 AmoebaCnt[new_group_nr]++;
6009 AmoebaCnt2[new_group_nr]++;
6011 /* if amoeba touches other amoeba(s) after growing, unify them */
6012 AmoebenVereinigen(newax, neway);
6014 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6016 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6022 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6023 (neway == lev_fieldy - 1 && newax != ax))
6025 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6026 Store[newax][neway] = element;
6028 else if (neway == ay)
6030 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6032 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6036 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6037 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6038 Store[ax][ay] = EL_AMOEBA_DROP;
6039 ContinueMoving(ax, ay);
6043 DrawLevelField(newax, neway);
6046 void Life(int ax, int ay)
6050 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6053 int element = Feld[ax][ay];
6054 int graphic = el2img(element);
6055 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6057 boolean changed = FALSE;
6059 if (IS_ANIMATED(graphic))
6060 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6065 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6066 MovDelay[ax][ay] = life_time;
6068 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6071 if (MovDelay[ax][ay])
6075 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6077 int xx = ax+x1, yy = ay+y1;
6080 if (!IN_LEV_FIELD(xx, yy))
6083 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6085 int x = xx+x2, y = yy+y2;
6087 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6090 if (((Feld[x][y] == element ||
6091 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6093 (IS_FREE(x, y) && Stop[x][y]))
6097 if (xx == ax && yy == ay) /* field in the middle */
6099 if (nachbarn < life_parameter[0] ||
6100 nachbarn > life_parameter[1])
6102 Feld[xx][yy] = EL_EMPTY;
6104 DrawLevelField(xx, yy);
6105 Stop[xx][yy] = TRUE;
6109 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6110 { /* free border field */
6111 if (nachbarn >= life_parameter[2] &&
6112 nachbarn <= life_parameter[3])
6114 Feld[xx][yy] = element;
6115 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6117 DrawLevelField(xx, yy);
6118 Stop[xx][yy] = TRUE;
6125 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6126 SND_GAME_OF_LIFE_GROWING);
6129 static void InitRobotWheel(int x, int y)
6131 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6134 static void RunRobotWheel(int x, int y)
6136 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6139 static void StopRobotWheel(int x, int y)
6141 if (ZX == x && ZY == y)
6145 static void InitTimegateWheel(int x, int y)
6147 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6150 static void RunTimegateWheel(int x, int y)
6152 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6155 void CheckExit(int x, int y)
6157 if (local_player->gems_still_needed > 0 ||
6158 local_player->sokobanfields_still_needed > 0 ||
6159 local_player->lights_still_needed > 0)
6161 int element = Feld[x][y];
6162 int graphic = el2img(element);
6164 if (IS_ANIMATED(graphic))
6165 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6170 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6173 Feld[x][y] = EL_EXIT_OPENING;
6175 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6178 void CheckExitSP(int x, int y)
6180 if (local_player->gems_still_needed > 0)
6182 int element = Feld[x][y];
6183 int graphic = el2img(element);
6185 if (IS_ANIMATED(graphic))
6186 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6191 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6194 Feld[x][y] = EL_SP_EXIT_OPENING;
6196 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6199 static void CloseAllOpenTimegates()
6203 for (y = 0; y < lev_fieldy; y++)
6205 for (x = 0; x < lev_fieldx; x++)
6207 int element = Feld[x][y];
6209 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6211 Feld[x][y] = EL_TIMEGATE_CLOSING;
6213 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6219 void EdelsteinFunkeln(int x, int y)
6221 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6224 if (Feld[x][y] == EL_BD_DIAMOND)
6227 if (MovDelay[x][y] == 0) /* next animation frame */
6228 MovDelay[x][y] = 11 * !SimpleRND(500);
6230 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6234 if (setup.direct_draw && MovDelay[x][y])
6235 SetDrawtoField(DRAW_BUFFERED);
6237 DrawLevelElementAnimation(x, y, Feld[x][y]);
6239 if (MovDelay[x][y] != 0)
6241 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6242 10 - MovDelay[x][y]);
6244 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6246 if (setup.direct_draw)
6250 dest_x = FX + SCREENX(x) * TILEX;
6251 dest_y = FY + SCREENY(y) * TILEY;
6253 BlitBitmap(drawto_field, window,
6254 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6255 SetDrawtoField(DRAW_DIRECT);
6261 void MauerWaechst(int x, int y)
6265 if (!MovDelay[x][y]) /* next animation frame */
6266 MovDelay[x][y] = 3 * delay;
6268 if (MovDelay[x][y]) /* wait some time before next frame */
6272 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6274 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6275 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6277 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6280 if (!MovDelay[x][y])
6282 if (MovDir[x][y] == MV_LEFT)
6284 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6285 DrawLevelField(x - 1, y);
6287 else if (MovDir[x][y] == MV_RIGHT)
6289 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6290 DrawLevelField(x + 1, y);
6292 else if (MovDir[x][y] == MV_UP)
6294 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6295 DrawLevelField(x, y - 1);
6299 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6300 DrawLevelField(x, y + 1);
6303 Feld[x][y] = Store[x][y];
6305 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6306 DrawLevelField(x, y);
6311 void MauerAbleger(int ax, int ay)
6313 int element = Feld[ax][ay];
6314 int graphic = el2img(element);
6315 boolean oben_frei = FALSE, unten_frei = FALSE;
6316 boolean links_frei = FALSE, rechts_frei = FALSE;
6317 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6318 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6319 boolean new_wall = FALSE;
6321 if (IS_ANIMATED(graphic))
6322 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6324 if (!MovDelay[ax][ay]) /* start building new wall */
6325 MovDelay[ax][ay] = 6;
6327 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6330 if (MovDelay[ax][ay])
6334 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6336 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6338 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6340 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6343 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6344 element == EL_EXPANDABLE_WALL_ANY)
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_UP;
6351 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6352 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6353 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6358 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6359 Store[ax][ay+1] = element;
6360 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6361 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6362 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6363 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6368 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6369 element == EL_EXPANDABLE_WALL_ANY ||
6370 element == EL_EXPANDABLE_WALL)
6374 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6375 Store[ax-1][ay] = element;
6376 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6377 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6378 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6379 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6385 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6386 Store[ax+1][ay] = element;
6387 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6388 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6389 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6390 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6395 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6396 DrawLevelField(ax, ay);
6398 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6400 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6401 unten_massiv = TRUE;
6402 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6403 links_massiv = TRUE;
6404 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6405 rechts_massiv = TRUE;
6407 if (((oben_massiv && unten_massiv) ||
6408 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6409 element == EL_EXPANDABLE_WALL) &&
6410 ((links_massiv && rechts_massiv) ||
6411 element == EL_EXPANDABLE_WALL_VERTICAL))
6412 Feld[ax][ay] = EL_WALL;
6415 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6418 void CheckForDragon(int x, int y)
6421 boolean dragon_found = FALSE;
6422 static int xy[4][2] =
6430 for (i = 0; i < NUM_DIRECTIONS; i++)
6432 for (j = 0; j < 4; j++)
6434 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6436 if (IN_LEV_FIELD(xx, yy) &&
6437 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6439 if (Feld[xx][yy] == EL_DRAGON)
6440 dragon_found = TRUE;
6449 for (i = 0; i < NUM_DIRECTIONS; i++)
6451 for (j = 0; j < 3; j++)
6453 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6455 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6457 Feld[xx][yy] = EL_EMPTY;
6458 DrawLevelField(xx, yy);
6467 static void InitBuggyBase(int x, int y)
6469 int element = Feld[x][y];
6470 int activating_delay = FRAMES_PER_SECOND / 4;
6473 (element == EL_SP_BUGGY_BASE ?
6474 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6475 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6477 element == EL_SP_BUGGY_BASE_ACTIVE ?
6478 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6481 static void WarnBuggyBase(int x, int y)
6484 static int xy[4][2] =
6492 for (i = 0; i < NUM_DIRECTIONS; i++)
6494 int xx = x + xy[i][0], yy = y + xy[i][1];
6496 if (IS_PLAYER(xx, yy))
6498 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6505 static void InitTrap(int x, int y)
6507 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6510 static void ActivateTrap(int x, int y)
6512 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6515 static void ChangeActiveTrap(int x, int y)
6517 int graphic = IMG_TRAP_ACTIVE;
6519 /* if new animation frame was drawn, correct crumbled sand border */
6520 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6521 DrawLevelFieldCrumbledSand(x, y);
6524 static int getSpecialActionElement(int element, int number, int base_element)
6526 return (element != EL_EMPTY ? element :
6527 number != -1 ? base_element + number - 1 :
6531 static int getModifiedActionNumber(int value_old, int operator, int operand,
6532 int value_min, int value_max)
6534 int value_new = (operator == CA_MODE_SET ? operand :
6535 operator == CA_MODE_ADD ? value_old + operand :
6536 operator == CA_MODE_SUBTRACT ? value_old - operand :
6537 operator == CA_MODE_MULTIPLY ? value_old * operand :
6538 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
6539 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
6542 return (value_new < value_min ? value_min :
6543 value_new > value_max ? value_max :
6547 static void ExecuteCustomElementAction(int x, int y, int element, int page)
6549 struct ElementInfo *ei = &element_info[element];
6550 struct ElementChangeInfo *change = &ei->change_page[page];
6551 int action_type = change->action_type;
6552 int action_mode = change->action_mode;
6553 int action_arg = change->action_arg;
6556 if (!change->has_action)
6559 /* ---------- determine action paramater values ---------- */
6561 int action_arg_element =
6562 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
6563 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
6564 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
6567 int action_arg_number_min =
6568 (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MIN :
6571 int action_arg_number_max =
6572 (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MAX :
6573 action_type == CA_SET_LEVEL_GEMS ? 999 :
6574 action_type == CA_SET_LEVEL_TIME ? 9999 :
6575 action_type == CA_SET_LEVEL_SCORE ? 99999 :
6576 action_type == CA_SET_CE_SCORE ? 9999 :
6577 action_type == CA_SET_CE_VALUE ? 9999 :
6580 int action_arg_number_reset =
6581 (action_type == CA_SET_PLAYER_SPEED ? TILEX/game.initial_move_delay_value :
6582 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
6583 action_type == CA_SET_LEVEL_TIME ? level.time :
6584 action_type == CA_SET_LEVEL_SCORE ? 0 :
6585 action_type == CA_SET_CE_SCORE ? 0 :
6587 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
6589 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
6593 int action_arg_number =
6594 (action_arg <= CA_ARG_MAX ? action_arg :
6595 action_arg >= CA_ARG_SPEED_VERY_SLOW &&
6596 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
6597 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
6598 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
6599 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
6600 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
6601 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
6602 #if USE_NEW_CUSTOM_VALUE
6603 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
6605 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
6607 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) :
6608 action_arg == CA_ARG_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
6609 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value :
6612 int action_arg_number_old =
6613 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
6614 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
6615 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
6616 action_type == CA_SET_CE_SCORE ? ei->collect_score :
6617 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
6620 int action_arg_number_new =
6621 getModifiedActionNumber(action_arg_number_old,
6622 action_mode, action_arg_number,
6623 action_arg_number_min, action_arg_number_max);
6625 int action_arg_player_bits =
6626 (action_arg == CA_ARG_PLAYER_ANY ? PLAYER_BITS_ANY :
6627 action_arg >= CA_ARG_PLAYER_1 &&
6628 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
6629 action_arg >= CA_ARG_1 &&
6630 action_arg <= CA_ARG_PLAYER_4 ? (1 << (action_arg - CA_ARG_1)) :
6631 action_arg_element >= EL_PLAYER_1 &&
6632 action_arg_element <= EL_PLAYER_4 ?
6633 (1 << (action_arg_element - EL_PLAYER_1)) :
6636 int trigger_player_bits =
6637 (change->actual_trigger_player >= EL_PLAYER_1 &&
6638 change->actual_trigger_player <= EL_PLAYER_4 ?
6639 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
6642 /* ---------- execute action ---------- */
6651 case CA_EXIT_PLAYER:
6653 for (i = 0; i < MAX_PLAYERS; i++)
6654 if (action_arg_player_bits & (1 << i))
6655 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
6660 case CA_KILL_PLAYER:
6662 for (i = 0; i < MAX_PLAYERS; i++)
6663 if (action_arg_player_bits & (1 << i))
6664 KillPlayer(&stored_player[i]);
6669 case CA_RESTART_LEVEL:
6671 game.restart_level = TRUE;
6676 case CA_SHOW_ENVELOPE:
6678 int element = getSpecialActionElement(action_arg_element,
6679 action_arg_number, EL_ENVELOPE_1);
6681 if (IS_ENVELOPE(element))
6682 local_player->show_envelope = element;
6689 int element = getSpecialActionElement(action_arg_element,
6690 action_arg_number, EL_KEY_1);
6692 if (IS_KEY(element))
6694 for (i = 0; i < MAX_PLAYERS; i++)
6696 if (trigger_player_bits & (1 << i))
6698 stored_player[i].key[KEY_NR(element)] = TRUE;
6700 DrawGameValue_Keys(stored_player[i].key);
6702 redraw_mask |= REDRAW_DOOR_1;
6712 int element = getSpecialActionElement(action_arg_element,
6713 action_arg_number, EL_KEY_1);
6715 if (IS_KEY(element))
6717 for (i = 0; i < MAX_PLAYERS; i++)
6719 if (trigger_player_bits & (1 << i))
6721 stored_player[i].key[KEY_NR(element)] = FALSE;
6723 DrawGameValue_Keys(stored_player[i].key);
6725 redraw_mask |= REDRAW_DOOR_1;
6734 case CA_SET_PLAYER_SPEED:
6736 for (i = 0; i < MAX_PLAYERS; i++)
6738 if (trigger_player_bits & (1 << i))
6740 int move_stepsize = TILEX / stored_player[i].move_delay_value;
6742 if (action_arg == CA_ARG_SPEED_SLOWER ||
6743 action_arg == CA_ARG_SPEED_FASTER)
6745 action_arg_number = 2;
6746 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
6751 getModifiedActionNumber(move_stepsize,
6754 action_arg_number_min,
6755 action_arg_number_max);
6757 /* make sure that value is power of 2 */
6758 move_stepsize = (1 << log_2(move_stepsize));
6760 /* do no immediately change -- the player might just be moving */
6761 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
6764 printf("::: move_delay_value == %d [%d]\n",
6765 stored_player[i].move_delay_value_next, action_arg_number);
6773 case CA_SET_PLAYER_SPEED:
6775 for (i = 0; i < MAX_PLAYERS; i++)
6777 if (trigger_player_bits & (1 << i))
6779 int move_stepsize = TILEX / stored_player[i].move_delay_value;
6781 if (action_mode == CA_MODE_ADD || action_mode == CA_MODE_SUBTRACT)
6783 /* translate "+" and "-" to "*" and "/" with powers of two */
6784 action_arg_number = 1 << action_arg_number;
6785 action_mode = (action_mode == CA_MODE_ADD ? CA_MODE_MULTIPLY :
6790 getModifiedActionNumber(move_stepsize,
6793 action_arg_number_min,
6794 action_arg_number_max);
6796 /* make sure that value is power of 2 */
6797 move_stepsize = (1 << log_2(move_stepsize));
6799 /* do no immediately change -- the player might just be moving */
6800 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
6803 printf("::: move_delay_value == %d [%d]\n",
6804 stored_player[i].move_delay_value_next, action_arg_number);
6813 case CA_SET_PLAYER_GRAVITY:
6815 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
6816 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
6817 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
6822 case CA_SET_WIND_DIRECTION:
6824 game.wind_direction = (action_arg >= CA_ARG_DIRECTION_NONE &&
6825 action_arg <= CA_ARG_DIRECTION_DOWN ?
6826 action_arg - CA_ARG_DIRECTION :
6827 action_arg == CA_ARG_DIRECTION_TRIGGER ?
6828 MV_DIR_OPPOSITE(change->actual_trigger_side) :
6829 game.wind_direction);
6834 case CA_SET_LEVEL_GEMS:
6836 local_player->gems_still_needed = action_arg_number_new;
6838 DrawGameValue_Emeralds(local_player->gems_still_needed);
6843 case CA_SET_LEVEL_TIME:
6845 if (level.time > 0) /* only modify limited time value */
6847 TimeLeft = action_arg_number_new;
6849 DrawGameValue_Time(TimeLeft);
6851 if (!TimeLeft && setup.time_limit)
6852 for (i = 0; i < MAX_PLAYERS; i++)
6853 KillPlayer(&stored_player[i]);
6859 case CA_SET_LEVEL_SCORE:
6861 local_player->score = action_arg_number_new;
6863 DrawGameValue_Score(local_player->score);
6868 case CA_SET_CE_SCORE:
6870 ei->collect_score = action_arg_number_new;
6875 case CA_SET_CE_VALUE:
6877 #if USE_NEW_CUSTOM_VALUE
6878 int last_custom_value = CustomValue[x][y];
6880 CustomValue[x][y] = action_arg_number_new;
6883 printf("::: Count == %d\n", CustomValue[x][y]);
6886 if (CustomValue[x][y] == 0 && last_custom_value > 0)
6889 printf("::: CE_VALUE_GETS_ZERO\n");
6892 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
6893 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
6901 case CA_SET_DYNABOMB_NUMBER:
6903 printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n");
6908 case CA_SET_DYNABOMB_SIZE:
6910 printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n");
6915 case CA_SET_DYNABOMB_POWER:
6917 printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n");
6928 static void ChangeElementNowExt(struct ElementChangeInfo *change,
6929 int x, int y, int target_element)
6931 int previous_move_direction = MovDir[x][y];
6932 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
6933 IS_WALKABLE(Feld[x][y]));
6935 /* check if element under player changes from accessible to unaccessible
6936 (needed for special case of dropping element which then changes) */
6937 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6938 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6946 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6947 RemoveMovingField(x, y);
6951 Feld[x][y] = target_element;
6953 ResetGfxAnimation(x, y);
6954 ResetRandomAnimationValue(x, y);
6956 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6957 MovDir[x][y] = previous_move_direction;
6959 InitField_WithBug1(x, y, FALSE);
6961 DrawLevelField(x, y);
6963 if (GFX_CRUMBLED(Feld[x][y]))
6964 DrawLevelFieldCrumbledSandNeighbours(x, y);
6967 /* "Changed[][]" not set yet to allow "entered by player" change one time */
6968 if (ELEM_IS_PLAYER(target_element))
6969 RelocatePlayer(x, y, target_element);
6972 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
6974 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6977 TestIfBadThingTouchesPlayer(x, y);
6978 TestIfPlayerTouchesCustomElement(x, y);
6979 TestIfElementTouchesCustomElement(x, y);
6982 static boolean ChangeElementNow(int x, int y, int element, int page)
6984 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6986 int old_element = Feld[x][y];
6988 /* always use default change event to prevent running into a loop */
6989 if (ChangeEvent[x][y] == -1)
6990 ChangeEvent[x][y] = CE_DELAY;
6992 if (ChangeEvent[x][y] == CE_DELAY)
6994 /* reset actual trigger element, trigger player and action element */
6995 change->actual_trigger_element = EL_EMPTY;
6996 change->actual_trigger_player = EL_PLAYER_1;
6997 change->actual_trigger_side = CH_SIDE_NONE;
6998 change->actual_trigger_ce_value = 0;
7002 /* do not change any elements that have already changed in this frame */
7006 /* do not change already changed elements with same change event */
7007 if (Changed[x][y] & ChangeEvent[x][y])
7012 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7014 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7017 if (change->explode)
7024 if (change->use_target_content)
7026 boolean complete_replace = TRUE;
7027 boolean can_replace[3][3];
7030 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7033 boolean is_walkable;
7034 boolean is_diggable;
7035 boolean is_collectible;
7036 boolean is_removable;
7037 boolean is_destructible;
7038 int ex = x + xx - 1;
7039 int ey = y + yy - 1;
7040 int content_element = change->target_content.e[xx][yy];
7043 can_replace[xx][yy] = TRUE;
7045 if (ex == x && ey == y) /* do not check changing element itself */
7048 if (content_element == EL_EMPTY_SPACE)
7050 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7055 if (!IN_LEV_FIELD(ex, ey))
7057 can_replace[xx][yy] = FALSE;
7058 complete_replace = FALSE;
7065 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7066 e = MovingOrBlocked2Element(ex, ey);
7068 is_empty = (IS_FREE(ex, ey) ||
7069 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7071 is_walkable = (is_empty || IS_WALKABLE(e));
7072 is_diggable = (is_empty || IS_DIGGABLE(e));
7073 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7074 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7075 is_removable = (is_diggable || is_collectible);
7077 can_replace[xx][yy] =
7078 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7079 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7080 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7081 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7082 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7083 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7084 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7086 if (!can_replace[xx][yy])
7087 complete_replace = FALSE;
7090 if (!change->only_if_complete || complete_replace)
7092 boolean something_has_changed = FALSE;
7094 if (change->only_if_complete && change->use_random_replace &&
7095 RND(100) < change->random_percentage)
7098 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7100 int ex = x + xx - 1;
7101 int ey = y + yy - 1;
7102 int content_element;
7104 if (can_replace[xx][yy] && (!change->use_random_replace ||
7105 RND(100) < change->random_percentage))
7107 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7108 RemoveMovingField(ex, ey);
7110 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7112 content_element = change->target_content.e[xx][yy];
7113 target_element = GET_TARGET_ELEMENT(content_element, change);
7115 ChangeElementNowExt(change, ex, ey, target_element);
7117 something_has_changed = TRUE;
7119 /* for symmetry reasons, freeze newly created border elements */
7120 if (ex != x || ey != y)
7121 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7125 if (something_has_changed)
7127 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7128 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7134 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7136 ChangeElementNowExt(change, x, y, target_element);
7138 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7139 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7142 /* this uses direct change before indirect change */
7143 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
7148 #if USE_NEW_DELAYED_ACTION
7150 static void ChangeElement(int x, int y, int page)
7152 int element = MovingOrBlocked2Element(x, y);
7153 struct ElementInfo *ei = &element_info[element];
7154 struct ElementChangeInfo *change = &ei->change_page[page];
7157 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
7158 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
7161 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7162 x, y, element, element_info[element].token_name);
7163 printf("ChangeElement(): This should never happen!\n");
7168 /* this can happen with classic bombs on walkable, changing elements */
7169 if (!CAN_CHANGE_OR_HAS_ACTION(element))
7172 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7173 ChangeDelay[x][y] = 0;
7179 if (ChangeDelay[x][y] == 0) /* initialize element change */
7181 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7183 if (change->can_change)
7185 ResetGfxAnimation(x, y);
7186 ResetRandomAnimationValue(x, y);
7188 if (change->pre_change_function)
7189 change->pre_change_function(x, y);
7193 ChangeDelay[x][y]--;
7195 if (ChangeDelay[x][y] != 0) /* continue element change */
7197 if (change->can_change)
7199 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7201 if (IS_ANIMATED(graphic))
7202 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7204 if (change->change_function)
7205 change->change_function(x, y);
7208 else /* finish element change */
7210 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7212 page = ChangePage[x][y];
7213 ChangePage[x][y] = -1;
7215 change = &ei->change_page[page];
7218 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7220 ChangeDelay[x][y] = 1; /* try change after next move step */
7221 ChangePage[x][y] = page; /* remember page to use for change */
7226 if (change->can_change)
7228 if (ChangeElementNow(x, y, element, page))
7230 if (change->post_change_function)
7231 change->post_change_function(x, y);
7235 if (change->has_action)
7236 ExecuteCustomElementAction(x, y, element, page);
7242 static void ChangeElement(int x, int y, int page)
7244 int element = MovingOrBlocked2Element(x, y);
7245 struct ElementInfo *ei = &element_info[element];
7246 struct ElementChangeInfo *change = &ei->change_page[page];
7249 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7252 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7253 x, y, element, element_info[element].token_name);
7254 printf("ChangeElement(): This should never happen!\n");
7259 /* this can happen with classic bombs on walkable, changing elements */
7260 if (!CAN_CHANGE(element))
7263 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7264 ChangeDelay[x][y] = 0;
7270 if (ChangeDelay[x][y] == 0) /* initialize element change */
7272 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7274 ResetGfxAnimation(x, y);
7275 ResetRandomAnimationValue(x, y);
7277 if (change->pre_change_function)
7278 change->pre_change_function(x, y);
7281 ChangeDelay[x][y]--;
7283 if (ChangeDelay[x][y] != 0) /* continue element change */
7285 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7287 if (IS_ANIMATED(graphic))
7288 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7290 if (change->change_function)
7291 change->change_function(x, y);
7293 else /* finish element change */
7295 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7297 page = ChangePage[x][y];
7298 ChangePage[x][y] = -1;
7300 change = &ei->change_page[page];
7303 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7305 ChangeDelay[x][y] = 1; /* try change after next move step */
7306 ChangePage[x][y] = page; /* remember page to use for change */
7311 if (ChangeElementNow(x, y, element, page))
7313 if (change->post_change_function)
7314 change->post_change_function(x, y);
7321 static boolean CheckTriggeredElementChangeExt(int x, int y,
7322 int trigger_element,
7328 boolean change_done_any = FALSE;
7329 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7332 if (!(trigger_events[trigger_element][trigger_event]))
7335 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7337 int element = EL_CUSTOM_START + i;
7338 boolean change_done = FALSE;
7341 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7342 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7345 for (p = 0; p < element_info[element].num_change_pages; p++)
7347 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7349 if (change->can_change_or_has_action &&
7350 change->has_event[trigger_event] &&
7351 change->trigger_side & trigger_side &&
7352 change->trigger_player & trigger_player &&
7353 change->trigger_page & trigger_page_bits &&
7354 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7356 change->actual_trigger_element = trigger_element;
7357 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7358 change->actual_trigger_side = trigger_side;
7359 change->actual_trigger_ce_value = CustomValue[x][y];
7361 if ((change->can_change && !change_done) || change->has_action)
7365 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7367 if (Feld[x][y] == element)
7369 if (change->can_change && !change_done)
7371 ChangeDelay[x][y] = 1;
7372 ChangeEvent[x][y] = trigger_event;
7373 ChangeElement(x, y, p);
7375 #if USE_NEW_DELAYED_ACTION
7376 else if (change->has_action)
7378 ExecuteCustomElementAction(x, y, element, p);
7379 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7382 if (change->has_action)
7384 ExecuteCustomElementAction(x, y, element, p);
7385 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7391 if (change->can_change)
7394 change_done_any = TRUE;
7401 return change_done_any;
7404 static boolean CheckElementChangeExt(int x, int y,
7406 int trigger_element,
7411 boolean change_done = FALSE;
7414 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7415 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7418 if (Feld[x][y] == EL_BLOCKED)
7420 Blocked2Moving(x, y, &x, &y);
7421 element = Feld[x][y];
7424 if (Feld[x][y] != element) /* check if element has already changed */
7427 for (p = 0; p < element_info[element].num_change_pages; p++)
7429 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7431 boolean check_trigger_element =
7432 (trigger_event == CE_TOUCHING_X ||
7433 trigger_event == CE_HITTING_X ||
7434 trigger_event == CE_HIT_BY_X);
7436 if (change->can_change_or_has_action &&
7437 change->has_event[trigger_event] &&
7438 change->trigger_side & trigger_side &&
7439 change->trigger_player & trigger_player &&
7440 (!check_trigger_element ||
7441 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
7443 change->actual_trigger_element = trigger_element;
7444 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7445 change->actual_trigger_side = trigger_side;
7446 change->actual_trigger_ce_value = CustomValue[x][y];
7448 if (change->can_change && !change_done)
7450 ChangeDelay[x][y] = 1;
7451 ChangeEvent[x][y] = trigger_event;
7452 ChangeElement(x, y, p);
7456 #if USE_NEW_DELAYED_ACTION
7457 else if (change->has_action)
7459 ExecuteCustomElementAction(x, y, element, p);
7460 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7463 if (change->has_action)
7465 ExecuteCustomElementAction(x, y, element, p);
7466 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7475 static void PlayPlayerSound(struct PlayerInfo *player)
7477 int jx = player->jx, jy = player->jy;
7478 int element = player->element_nr;
7479 int last_action = player->last_action_waiting;
7480 int action = player->action_waiting;
7482 if (player->is_waiting)
7484 if (action != last_action)
7485 PlayLevelSoundElementAction(jx, jy, element, action);
7487 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7491 if (action != last_action)
7492 StopSound(element_info[element].sound[last_action]);
7494 if (last_action == ACTION_SLEEPING)
7495 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7499 static void PlayAllPlayersSound()
7503 for (i = 0; i < MAX_PLAYERS; i++)
7504 if (stored_player[i].active)
7505 PlayPlayerSound(&stored_player[i]);
7508 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7510 boolean last_waiting = player->is_waiting;
7511 int move_dir = player->MovDir;
7513 player->last_action_waiting = player->action_waiting;
7517 if (!last_waiting) /* not waiting -> waiting */
7519 player->is_waiting = TRUE;
7521 player->frame_counter_bored =
7523 game.player_boring_delay_fixed +
7524 SimpleRND(game.player_boring_delay_random);
7525 player->frame_counter_sleeping =
7527 game.player_sleeping_delay_fixed +
7528 SimpleRND(game.player_sleeping_delay_random);
7530 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7533 if (game.player_sleeping_delay_fixed +
7534 game.player_sleeping_delay_random > 0 &&
7535 player->anim_delay_counter == 0 &&
7536 player->post_delay_counter == 0 &&
7537 FrameCounter >= player->frame_counter_sleeping)
7538 player->is_sleeping = TRUE;
7539 else if (game.player_boring_delay_fixed +
7540 game.player_boring_delay_random > 0 &&
7541 FrameCounter >= player->frame_counter_bored)
7542 player->is_bored = TRUE;
7544 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7545 player->is_bored ? ACTION_BORING :
7548 if (player->is_sleeping)
7550 if (player->num_special_action_sleeping > 0)
7552 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7554 int last_special_action = player->special_action_sleeping;
7555 int num_special_action = player->num_special_action_sleeping;
7556 int special_action =
7557 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7558 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7559 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7560 last_special_action + 1 : ACTION_SLEEPING);
7561 int special_graphic =
7562 el_act_dir2img(player->element_nr, special_action, move_dir);
7564 player->anim_delay_counter =
7565 graphic_info[special_graphic].anim_delay_fixed +
7566 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7567 player->post_delay_counter =
7568 graphic_info[special_graphic].post_delay_fixed +
7569 SimpleRND(graphic_info[special_graphic].post_delay_random);
7571 player->special_action_sleeping = special_action;
7574 if (player->anim_delay_counter > 0)
7576 player->action_waiting = player->special_action_sleeping;
7577 player->anim_delay_counter--;
7579 else if (player->post_delay_counter > 0)
7581 player->post_delay_counter--;
7585 else if (player->is_bored)
7587 if (player->num_special_action_bored > 0)
7589 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7591 int special_action =
7592 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7593 int special_graphic =
7594 el_act_dir2img(player->element_nr, special_action, move_dir);
7596 player->anim_delay_counter =
7597 graphic_info[special_graphic].anim_delay_fixed +
7598 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7599 player->post_delay_counter =
7600 graphic_info[special_graphic].post_delay_fixed +
7601 SimpleRND(graphic_info[special_graphic].post_delay_random);
7603 player->special_action_bored = special_action;
7606 if (player->anim_delay_counter > 0)
7608 player->action_waiting = player->special_action_bored;
7609 player->anim_delay_counter--;
7611 else if (player->post_delay_counter > 0)
7613 player->post_delay_counter--;
7618 else if (last_waiting) /* waiting -> not waiting */
7620 player->is_waiting = FALSE;
7621 player->is_bored = FALSE;
7622 player->is_sleeping = FALSE;
7624 player->frame_counter_bored = -1;
7625 player->frame_counter_sleeping = -1;
7627 player->anim_delay_counter = 0;
7628 player->post_delay_counter = 0;
7630 player->action_waiting = ACTION_DEFAULT;
7632 player->special_action_bored = ACTION_DEFAULT;
7633 player->special_action_sleeping = ACTION_DEFAULT;
7637 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7639 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7640 int left = player_action & JOY_LEFT;
7641 int right = player_action & JOY_RIGHT;
7642 int up = player_action & JOY_UP;
7643 int down = player_action & JOY_DOWN;
7644 int button1 = player_action & JOY_BUTTON_1;
7645 int button2 = player_action & JOY_BUTTON_2;
7646 int dx = (left ? -1 : right ? 1 : 0);
7647 int dy = (up ? -1 : down ? 1 : 0);
7649 if (!player->active || tape.pausing)
7655 snapped = SnapField(player, dx, dy);
7659 dropped = DropElement(player);
7661 moved = MovePlayer(player, dx, dy);
7664 if (tape.single_step && tape.recording && !tape.pausing)
7666 if (button1 || (dropped && !moved))
7668 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7669 SnapField(player, 0, 0); /* stop snapping */
7673 SetPlayerWaiting(player, FALSE);
7675 return player_action;
7679 /* no actions for this player (no input at player's configured device) */
7681 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7682 SnapField(player, 0, 0);
7683 CheckGravityMovementWhenNotMoving(player);
7685 if (player->MovPos == 0)
7686 SetPlayerWaiting(player, TRUE);
7688 if (player->MovPos == 0) /* needed for tape.playing */
7689 player->is_moving = FALSE;
7691 player->is_dropping = FALSE;
7697 void AdvanceFrameAndPlayerCounters(int player_nr)
7701 /* advance frame counters (global frame counter and time frame counter) */
7705 /* advance player counters (counters for move delay, move animation etc.) */
7706 for (i = 0; i < MAX_PLAYERS; i++)
7708 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
7709 int move_delay_value = stored_player[i].move_delay_value;
7710 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
7712 if (!advance_player_counters) /* not all players may be affected */
7715 #if USE_NEW_PLAYER_ANIM
7716 if (move_frames == 0) /* less than one move per game frame */
7718 int stepsize = TILEX / move_delay_value;
7719 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
7720 int count = (stored_player[i].is_moving ?
7721 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
7723 if (count % delay == 0)
7728 stored_player[i].Frame += move_frames;
7730 if (stored_player[i].MovPos != 0)
7731 stored_player[i].StepFrame += move_frames;
7733 if (stored_player[i].move_delay > 0)
7734 stored_player[i].move_delay--;
7736 /* due to bugs in previous versions, counter must count up, not down */
7737 if (stored_player[i].push_delay != -1)
7738 stored_player[i].push_delay++;
7740 if (stored_player[i].drop_delay > 0)
7741 stored_player[i].drop_delay--;
7747 static unsigned long game_frame_delay = 0;
7748 unsigned long game_frame_delay_value;
7749 int magic_wall_x = 0, magic_wall_y = 0;
7750 int i, x, y, element, graphic;
7751 byte *recorded_player_action;
7752 byte summarized_player_action = 0;
7753 byte tape_action[MAX_PLAYERS];
7755 if (game_status != GAME_MODE_PLAYING)
7758 game_frame_delay_value =
7759 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7761 if (tape.playing && tape.warp_forward && !tape.pausing)
7762 game_frame_delay_value = 0;
7764 /* ---------- main game synchronization point ---------- */
7766 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
7768 if (network_playing && !network_player_action_received)
7770 /* try to get network player actions in time */
7772 #if defined(NETWORK_AVALIABLE)
7773 /* last chance to get network player actions without main loop delay */
7777 /* game was quit by network peer */
7778 if (game_status != GAME_MODE_PLAYING)
7781 if (!network_player_action_received)
7782 return; /* failed to get network player actions in time */
7788 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7791 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
7792 if (recorded_player_action == NULL && tape.pausing)
7796 for (i = 0; i < MAX_PLAYERS; i++)
7798 summarized_player_action |= stored_player[i].action;
7800 if (!network_playing)
7801 stored_player[i].effective_action = stored_player[i].action;
7804 #if defined(NETWORK_AVALIABLE)
7805 if (network_playing)
7806 SendToServer_MovePlayer(summarized_player_action);
7809 if (!options.network && !setup.team_mode)
7810 local_player->effective_action = summarized_player_action;
7812 if (recorded_player_action != NULL)
7813 for (i = 0; i < MAX_PLAYERS; i++)
7814 stored_player[i].effective_action = recorded_player_action[i];
7816 for (i = 0; i < MAX_PLAYERS; i++)
7818 tape_action[i] = stored_player[i].effective_action;
7820 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7821 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7824 /* only save actions from input devices, but not programmed actions */
7826 TapeRecordAction(tape_action);
7828 for (i = 0; i < MAX_PLAYERS; i++)
7830 int actual_player_action = stored_player[i].effective_action;
7833 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
7834 - rnd_equinox_tetrachloride 048
7835 - rnd_equinox_tetrachloride_ii 096
7836 - rnd_emanuel_schmieg 002
7837 - doctor_sloan_ww 001, 020
7839 if (stored_player[i].MovPos == 0)
7840 CheckGravityMovement(&stored_player[i]);
7843 /* overwrite programmed action with tape action */
7844 if (stored_player[i].programmed_action)
7845 actual_player_action = stored_player[i].programmed_action;
7848 PlayerActions(&stored_player[i], actual_player_action);
7850 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7852 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7853 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7856 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7859 network_player_action_received = FALSE;
7861 ScrollScreen(NULL, SCROLL_GO_ON);
7863 /* for backwards compatibility, the following code emulates a fixed bug that
7864 occured when pushing elements (causing elements that just made their last
7865 pushing step to already (if possible) make their first falling step in the
7866 same game frame, which is bad); this code is also needed to use the famous
7867 "spring push bug" which is used in older levels and might be wanted to be
7868 used also in newer levels, but in this case the buggy pushing code is only
7869 affecting the "spring" element and no other elements */
7871 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7873 for (i = 0; i < MAX_PLAYERS; i++)
7875 struct PlayerInfo *player = &stored_player[i];
7879 if (player->active && player->is_pushing && player->is_moving &&
7881 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7882 Feld[x][y] == EL_SPRING))
7884 ContinueMoving(x, y);
7886 /* continue moving after pushing (this is actually a bug) */
7887 if (!IS_MOVING(x, y))
7895 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7897 Changed[x][y] = FALSE;
7898 ChangeEvent[x][y] = -1;
7900 /* this must be handled before main playfield loop */
7901 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
7904 if (MovDelay[x][y] <= 0)
7909 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7911 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7912 printf("GameActions(): This should never happen!\n");
7914 ChangePage[x][y] = -1;
7919 if (WasJustMoving[x][y] > 0)
7920 WasJustMoving[x][y]--;
7921 if (WasJustFalling[x][y] > 0)
7922 WasJustFalling[x][y]--;
7923 if (CheckCollision[x][y] > 0)
7924 CheckCollision[x][y]--;
7928 /* reset finished pushing action (not done in ContinueMoving() to allow
7929 continous pushing animation for elements with zero push delay) */
7930 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7932 ResetGfxAnimation(x, y);
7933 DrawLevelField(x, y);
7937 if (IS_BLOCKED(x, y))
7941 Blocked2Moving(x, y, &oldx, &oldy);
7942 if (!IS_MOVING(oldx, oldy))
7944 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7945 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7946 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7947 printf("GameActions(): This should never happen!\n");
7953 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7955 element = Feld[x][y];
7956 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7958 if (graphic_info[graphic].anim_global_sync)
7959 GfxFrame[x][y] = FrameCounter;
7961 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7962 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7963 ResetRandomAnimationValue(x, y);
7965 SetRandomAnimationValue(x, y);
7967 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7969 if (IS_INACTIVE(element))
7971 if (IS_ANIMATED(graphic))
7972 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7977 /* this may take place after moving, so 'element' may have changed */
7978 if (IS_CHANGING(x, y) &&
7979 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7981 int page = element_info[element].event_page_nr[CE_DELAY];
7983 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
7987 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
7991 ChangeElement(x, y, page);
7993 if (CAN_CHANGE(element))
7994 ChangeElement(x, y, page);
7996 if (HAS_ACTION(element))
7997 ExecuteCustomElementAction(x, y, element, page);
8002 element = Feld[x][y];
8003 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8006 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8010 element = Feld[x][y];
8011 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8013 if (IS_ANIMATED(graphic) &&
8016 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8018 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8019 EdelsteinFunkeln(x, y);
8021 else if ((element == EL_ACID ||
8022 element == EL_EXIT_OPEN ||
8023 element == EL_SP_EXIT_OPEN ||
8024 element == EL_SP_TERMINAL ||
8025 element == EL_SP_TERMINAL_ACTIVE ||
8026 element == EL_EXTRA_TIME ||
8027 element == EL_SHIELD_NORMAL ||
8028 element == EL_SHIELD_DEADLY) &&
8029 IS_ANIMATED(graphic))
8030 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8031 else if (IS_MOVING(x, y))
8032 ContinueMoving(x, y);
8033 else if (IS_ACTIVE_BOMB(element))
8034 CheckDynamite(x, y);
8035 else if (element == EL_AMOEBA_GROWING)
8036 AmoebeWaechst(x, y);
8037 else if (element == EL_AMOEBA_SHRINKING)
8038 AmoebaDisappearing(x, y);
8040 #if !USE_NEW_AMOEBA_CODE
8041 else if (IS_AMOEBALIVE(element))
8042 AmoebeAbleger(x, y);
8045 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8047 else if (element == EL_EXIT_CLOSED)
8049 else if (element == EL_SP_EXIT_CLOSED)
8051 else if (element == EL_EXPANDABLE_WALL_GROWING)
8053 else if (element == EL_EXPANDABLE_WALL ||
8054 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8055 element == EL_EXPANDABLE_WALL_VERTICAL ||
8056 element == EL_EXPANDABLE_WALL_ANY)
8058 else if (element == EL_FLAMES)
8059 CheckForDragon(x, y);
8060 else if (element == EL_EXPLOSION)
8061 ; /* drawing of correct explosion animation is handled separately */
8062 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8063 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8065 if (IS_BELT_ACTIVE(element))
8066 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8068 if (game.magic_wall_active)
8070 int jx = local_player->jx, jy = local_player->jy;
8072 /* play the element sound at the position nearest to the player */
8073 if ((element == EL_MAGIC_WALL_FULL ||
8074 element == EL_MAGIC_WALL_ACTIVE ||
8075 element == EL_MAGIC_WALL_EMPTYING ||
8076 element == EL_BD_MAGIC_WALL_FULL ||
8077 element == EL_BD_MAGIC_WALL_ACTIVE ||
8078 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8079 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8087 #if USE_NEW_AMOEBA_CODE
8088 /* new experimental amoeba growth stuff */
8089 if (!(FrameCounter % 8))
8091 static unsigned long random = 1684108901;
8093 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8095 x = RND(lev_fieldx);
8096 y = RND(lev_fieldy);
8097 element = Feld[x][y];
8099 if (!IS_PLAYER(x,y) &&
8100 (element == EL_EMPTY ||
8101 CAN_GROW_INTO(element) ||
8102 element == EL_QUICKSAND_EMPTY ||
8103 element == EL_ACID_SPLASH_LEFT ||
8104 element == EL_ACID_SPLASH_RIGHT))
8106 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8107 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8108 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8109 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8110 Feld[x][y] = EL_AMOEBA_DROP;
8113 random = random * 129 + 1;
8119 if (game.explosions_delayed)
8122 game.explosions_delayed = FALSE;
8124 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8126 element = Feld[x][y];
8128 if (ExplodeField[x][y])
8129 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8130 else if (element == EL_EXPLOSION)
8131 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8133 ExplodeField[x][y] = EX_TYPE_NONE;
8136 game.explosions_delayed = TRUE;
8139 if (game.magic_wall_active)
8141 if (!(game.magic_wall_time_left % 4))
8143 int element = Feld[magic_wall_x][magic_wall_y];
8145 if (element == EL_BD_MAGIC_WALL_FULL ||
8146 element == EL_BD_MAGIC_WALL_ACTIVE ||
8147 element == EL_BD_MAGIC_WALL_EMPTYING)
8148 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8150 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8153 if (game.magic_wall_time_left > 0)
8155 game.magic_wall_time_left--;
8156 if (!game.magic_wall_time_left)
8158 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8160 element = Feld[x][y];
8162 if (element == EL_MAGIC_WALL_ACTIVE ||
8163 element == EL_MAGIC_WALL_FULL)
8165 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8166 DrawLevelField(x, y);
8168 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8169 element == EL_BD_MAGIC_WALL_FULL)
8171 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8172 DrawLevelField(x, y);
8176 game.magic_wall_active = FALSE;
8181 if (game.light_time_left > 0)
8183 game.light_time_left--;
8185 if (game.light_time_left == 0)
8186 RedrawAllLightSwitchesAndInvisibleElements();
8189 if (game.timegate_time_left > 0)
8191 game.timegate_time_left--;
8193 if (game.timegate_time_left == 0)
8194 CloseAllOpenTimegates();
8197 for (i = 0; i < MAX_PLAYERS; i++)
8199 struct PlayerInfo *player = &stored_player[i];
8201 if (SHIELD_ON(player))
8203 if (player->shield_deadly_time_left)
8204 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8205 else if (player->shield_normal_time_left)
8206 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8210 if (TimeFrames >= FRAMES_PER_SECOND)
8215 for (i = 0; i < MAX_PLAYERS; i++)
8217 struct PlayerInfo *player = &stored_player[i];
8219 if (SHIELD_ON(player))
8221 player->shield_normal_time_left--;
8223 if (player->shield_deadly_time_left > 0)
8224 player->shield_deadly_time_left--;
8228 if (!level.use_step_counter)
8236 if (TimeLeft <= 10 && setup.time_limit)
8237 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8239 DrawGameValue_Time(TimeLeft);
8241 if (!TimeLeft && setup.time_limit)
8242 for (i = 0; i < MAX_PLAYERS; i++)
8243 KillPlayer(&stored_player[i]);
8245 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8246 DrawGameValue_Time(TimePlayed);
8249 if (tape.recording || tape.playing)
8250 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8254 PlayAllPlayersSound();
8256 if (options.debug) /* calculate frames per second */
8258 static unsigned long fps_counter = 0;
8259 static int fps_frames = 0;
8260 unsigned long fps_delay_ms = Counter() - fps_counter;
8264 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8266 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8269 fps_counter = Counter();
8272 redraw_mask |= REDRAW_FPS;
8275 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
8277 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8279 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8281 local_player->show_envelope = 0;
8284 /* use random number generator in every frame to make it less predictable */
8285 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8289 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8291 int min_x = x, min_y = y, max_x = x, max_y = y;
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 || &stored_player[i] == player)
8301 min_x = MIN(min_x, jx);
8302 min_y = MIN(min_y, jy);
8303 max_x = MAX(max_x, jx);
8304 max_y = MAX(max_y, jy);
8307 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8310 static boolean AllPlayersInVisibleScreen()
8314 for (i = 0; i < MAX_PLAYERS; i++)
8316 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8318 if (!stored_player[i].active)
8321 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8328 void ScrollLevel(int dx, int dy)
8330 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8333 BlitBitmap(drawto_field, drawto_field,
8334 FX + TILEX * (dx == -1) - softscroll_offset,
8335 FY + TILEY * (dy == -1) - softscroll_offset,
8336 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8337 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8338 FX + TILEX * (dx == 1) - softscroll_offset,
8339 FY + TILEY * (dy == 1) - softscroll_offset);
8343 x = (dx == 1 ? BX1 : BX2);
8344 for (y = BY1; y <= BY2; y++)
8345 DrawScreenField(x, y);
8350 y = (dy == 1 ? BY1 : BY2);
8351 for (x = BX1; x <= BX2; x++)
8352 DrawScreenField(x, y);
8355 redraw_mask |= REDRAW_FIELD;
8358 static boolean canFallDown(struct PlayerInfo *player)
8360 int jx = player->jx, jy = player->jy;
8362 return (IN_LEV_FIELD(jx, jy + 1) &&
8363 (IS_FREE(jx, jy + 1) ||
8364 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8365 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8366 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8369 static boolean canPassField(int x, int y, int move_dir)
8371 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8372 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8373 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8376 int element = Feld[x][y];
8378 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8379 !CAN_MOVE(element) &&
8380 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8381 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8382 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8385 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8387 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8388 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8389 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8393 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8394 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
8395 (IS_DIGGABLE(Feld[newx][newy]) ||
8396 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8397 canPassField(newx, newy, move_dir)));
8400 static void CheckGravityMovement(struct PlayerInfo *player)
8402 if (game.gravity && !player->programmed_action)
8404 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8405 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8406 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8407 int jx = player->jx, jy = player->jy;
8408 boolean player_is_moving_to_valid_field =
8409 (!player_is_snapping &&
8410 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8411 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8412 boolean player_can_fall_down = canFallDown(player);
8414 if (player_can_fall_down &&
8415 !player_is_moving_to_valid_field)
8416 player->programmed_action = MV_DOWN;
8420 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8422 return CheckGravityMovement(player);
8424 if (game.gravity && !player->programmed_action)
8426 int jx = player->jx, jy = player->jy;
8427 boolean field_under_player_is_free =
8428 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8429 boolean player_is_standing_on_valid_field =
8430 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8431 (IS_WALKABLE(Feld[jx][jy]) &&
8432 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8434 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8435 player->programmed_action = MV_DOWN;
8441 -----------------------------------------------------------------------------
8442 dx, dy: direction (non-diagonal) to try to move the player to
8443 real_dx, real_dy: direction as read from input device (can be diagonal)
8446 boolean MovePlayerOneStep(struct PlayerInfo *player,
8447 int dx, int dy, int real_dx, int real_dy)
8449 int jx = player->jx, jy = player->jy;
8450 int new_jx = jx + dx, new_jy = jy + dy;
8454 if (!player->active || (!dx && !dy))
8455 return MF_NO_ACTION;
8457 player->MovDir = (dx < 0 ? MV_LEFT :
8460 dy > 0 ? MV_DOWN : MV_NONE);
8462 if (!IN_LEV_FIELD(new_jx, new_jy))
8463 return MF_NO_ACTION;
8465 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8466 return MF_NO_ACTION;
8468 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8470 if (DONT_RUN_INTO(element))
8472 if (element == EL_ACID && dx == 0 && dy == 1)
8474 SplashAcid(new_jx, new_jy);
8475 Feld[jx][jy] = EL_PLAYER_1;
8476 InitMovingField(jx, jy, MV_DOWN);
8477 Store[jx][jy] = EL_ACID;
8478 ContinueMoving(jx, jy);
8482 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
8487 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8488 if (can_move != MF_MOVING)
8491 /* check if DigField() has caused relocation of the player */
8492 if (player->jx != jx || player->jy != jy)
8493 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
8495 StorePlayer[jx][jy] = 0;
8496 player->last_jx = jx;
8497 player->last_jy = jy;
8498 player->jx = new_jx;
8499 player->jy = new_jy;
8500 StorePlayer[new_jx][new_jy] = player->element_nr;
8502 if (player->move_delay_value_next != -1)
8504 player->move_delay_value = player->move_delay_value_next;
8505 player->move_delay_value_next = -1;
8509 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8511 player->step_counter++;
8513 PlayerVisit[jx][jy] = FrameCounter;
8515 ScrollPlayer(player, SCROLL_INIT);
8520 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8522 int jx = player->jx, jy = player->jy;
8523 int old_jx = jx, old_jy = jy;
8524 int moved = MF_NO_ACTION;
8526 if (!player->active)
8531 if (player->MovPos == 0)
8533 player->is_moving = FALSE;
8534 player->is_digging = FALSE;
8535 player->is_collecting = FALSE;
8536 player->is_snapping = FALSE;
8537 player->is_pushing = FALSE;
8543 if (player->move_delay > 0)
8546 player->move_delay = -1; /* set to "uninitialized" value */
8548 /* store if player is automatically moved to next field */
8549 player->is_auto_moving = (player->programmed_action != MV_NONE);
8551 /* remove the last programmed player action */
8552 player->programmed_action = 0;
8556 /* should only happen if pre-1.2 tape recordings are played */
8557 /* this is only for backward compatibility */
8559 int original_move_delay_value = player->move_delay_value;
8562 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8566 /* scroll remaining steps with finest movement resolution */
8567 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8569 while (player->MovPos)
8571 ScrollPlayer(player, SCROLL_GO_ON);
8572 ScrollScreen(NULL, SCROLL_GO_ON);
8574 AdvanceFrameAndPlayerCounters(player->index_nr);
8580 player->move_delay_value = original_move_delay_value;
8583 if (player->last_move_dir & MV_HORIZONTAL)
8585 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8586 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8590 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8591 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8597 if (moved & MF_MOVING && !ScreenMovPos &&
8598 (player == local_player || !options.network))
8600 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8601 int offset = (setup.scroll_delay ? 3 : 0);
8603 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8605 /* actual player has left the screen -- scroll in that direction */
8606 if (jx != old_jx) /* player has moved horizontally */
8607 scroll_x += (jx - old_jx);
8608 else /* player has moved vertically */
8609 scroll_y += (jy - old_jy);
8613 if (jx != old_jx) /* player has moved horizontally */
8615 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8616 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8617 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8619 /* don't scroll over playfield boundaries */
8620 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8621 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8623 /* don't scroll more than one field at a time */
8624 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8626 /* don't scroll against the player's moving direction */
8627 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8628 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8629 scroll_x = old_scroll_x;
8631 else /* player has moved vertically */
8633 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8634 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8635 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8637 /* don't scroll over playfield boundaries */
8638 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8639 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8641 /* don't scroll more than one field at a time */
8642 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8644 /* don't scroll against the player's moving direction */
8645 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8646 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8647 scroll_y = old_scroll_y;
8651 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8653 if (!options.network && !AllPlayersInVisibleScreen())
8655 scroll_x = old_scroll_x;
8656 scroll_y = old_scroll_y;
8660 ScrollScreen(player, SCROLL_INIT);
8661 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8666 player->StepFrame = 0;
8668 if (moved & MF_MOVING)
8670 if (old_jx != jx && old_jy == jy)
8671 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8672 else if (old_jx == jx && old_jy != jy)
8673 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8675 DrawLevelField(jx, jy); /* for "crumbled sand" */
8677 player->last_move_dir = player->MovDir;
8678 player->is_moving = TRUE;
8679 player->is_snapping = FALSE;
8680 player->is_switching = FALSE;
8681 player->is_dropping = FALSE;
8685 CheckGravityMovementWhenNotMoving(player);
8687 player->is_moving = FALSE;
8689 /* at this point, the player is allowed to move, but cannot move right now
8690 (e.g. because of something blocking the way) -- ensure that the player
8691 is also allowed to move in the next frame (in old versions before 3.1.1,
8692 the player was forced to wait again for eight frames before next try) */
8694 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8695 player->move_delay = 0; /* allow direct movement in the next frame */
8698 if (player->move_delay == -1) /* not yet initialized by DigField() */
8699 player->move_delay = player->move_delay_value;
8701 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8703 TestIfPlayerTouchesBadThing(jx, jy);
8704 TestIfPlayerTouchesCustomElement(jx, jy);
8707 if (!player->active)
8708 RemovePlayer(player);
8713 void ScrollPlayer(struct PlayerInfo *player, int mode)
8715 int jx = player->jx, jy = player->jy;
8716 int last_jx = player->last_jx, last_jy = player->last_jy;
8717 int move_stepsize = TILEX / player->move_delay_value;
8719 #if USE_NEW_PLAYER_SPEED
8720 if (!player->active)
8723 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
8726 if (!player->active || player->MovPos == 0)
8730 if (mode == SCROLL_INIT)
8732 player->actual_frame_counter = FrameCounter;
8733 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8735 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
8736 Feld[last_jx][last_jy] == EL_EMPTY)
8738 int last_field_block_delay = 0; /* start with no blocking at all */
8739 int block_delay_adjustment = player->block_delay_adjustment;
8741 /* if player blocks last field, add delay for exactly one move */
8742 if (player->block_last_field)
8744 last_field_block_delay += player->move_delay_value;
8746 /* when blocking enabled, prevent moving up despite gravity */
8747 if (game.gravity && player->MovDir == MV_UP)
8748 block_delay_adjustment = -1;
8751 /* add block delay adjustment (also possible when not blocking) */
8752 last_field_block_delay += block_delay_adjustment;
8754 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8755 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
8758 #if USE_NEW_PLAYER_SPEED
8759 if (player->MovPos != 0) /* player has not yet reached destination */
8765 else if (!FrameReached(&player->actual_frame_counter, 1))
8769 printf("::: player->MovPos: %d -> %d\n",
8771 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
8774 #if USE_NEW_PLAYER_SPEED
8775 if (player->MovPos != 0)
8777 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8778 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8780 /* before DrawPlayer() to draw correct player graphic for this case */
8781 if (player->MovPos == 0)
8782 CheckGravityMovement(player);
8785 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8786 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8788 /* before DrawPlayer() to draw correct player graphic for this case */
8789 if (player->MovPos == 0)
8790 CheckGravityMovement(player);
8793 if (player->MovPos == 0) /* player reached destination field */
8796 printf("::: player reached destination field\n");
8799 if (player->move_delay_reset_counter > 0)
8801 player->move_delay_reset_counter--;
8803 if (player->move_delay_reset_counter == 0)
8805 /* continue with normal speed after quickly moving through gate */
8806 HALVE_PLAYER_SPEED(player);
8808 /* be able to make the next move without delay */
8809 player->move_delay = 0;
8813 player->last_jx = jx;
8814 player->last_jy = jy;
8816 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8817 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8818 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8820 DrawPlayer(player); /* needed here only to cleanup last field */
8821 RemovePlayer(player);
8823 if (local_player->friends_still_needed == 0 ||
8824 IS_SP_ELEMENT(Feld[jx][jy]))
8825 player->LevelSolved = player->GameOver = TRUE;
8828 /* this breaks one level: "machine", level 000 */
8830 int move_direction = player->MovDir;
8831 int enter_side = MV_DIR_OPPOSITE(move_direction);
8832 int leave_side = move_direction;
8833 int old_jx = last_jx;
8834 int old_jy = last_jy;
8835 int old_element = Feld[old_jx][old_jy];
8836 int new_element = Feld[jx][jy];
8838 if (IS_CUSTOM_ELEMENT(old_element))
8839 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
8841 player->index_bit, leave_side);
8843 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
8845 player->index_bit, leave_side);
8847 if (IS_CUSTOM_ELEMENT(new_element))
8848 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
8849 player->index_bit, enter_side);
8851 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
8853 player->index_bit, enter_side);
8856 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8858 TestIfPlayerTouchesBadThing(jx, jy);
8859 TestIfPlayerTouchesCustomElement(jx, jy);
8861 /* needed because pushed element has not yet reached its destination,
8862 so it would trigger a change event at its previous field location */
8863 if (!player->is_pushing)
8864 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8866 if (!player->active)
8867 RemovePlayer(player);
8870 if (level.use_step_counter)
8880 if (TimeLeft <= 10 && setup.time_limit)
8881 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8883 DrawGameValue_Time(TimeLeft);
8885 if (!TimeLeft && setup.time_limit)
8886 for (i = 0; i < MAX_PLAYERS; i++)
8887 KillPlayer(&stored_player[i]);
8889 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8890 DrawGameValue_Time(TimePlayed);
8893 if (tape.single_step && tape.recording && !tape.pausing &&
8894 !player->programmed_action)
8895 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8899 void ScrollScreen(struct PlayerInfo *player, int mode)
8901 static unsigned long screen_frame_counter = 0;
8903 if (mode == SCROLL_INIT)
8905 /* set scrolling step size according to actual player's moving speed */
8906 ScrollStepSize = TILEX / player->move_delay_value;
8908 screen_frame_counter = FrameCounter;
8909 ScreenMovDir = player->MovDir;
8910 ScreenMovPos = player->MovPos;
8911 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8914 else if (!FrameReached(&screen_frame_counter, 1))
8919 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8920 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8921 redraw_mask |= REDRAW_FIELD;
8924 ScreenMovDir = MV_NONE;
8927 void TestIfPlayerTouchesCustomElement(int x, int y)
8929 static int xy[4][2] =
8936 static int trigger_sides[4][2] =
8938 /* center side border side */
8939 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8940 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8941 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8942 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8944 static int touch_dir[4] =
8951 int center_element = Feld[x][y]; /* should always be non-moving! */
8954 for (i = 0; i < NUM_DIRECTIONS; i++)
8956 int xx = x + xy[i][0];
8957 int yy = y + xy[i][1];
8958 int center_side = trigger_sides[i][0];
8959 int border_side = trigger_sides[i][1];
8962 if (!IN_LEV_FIELD(xx, yy))
8965 if (IS_PLAYER(x, y))
8967 struct PlayerInfo *player = PLAYERINFO(x, y);
8969 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8970 border_element = Feld[xx][yy]; /* may be moving! */
8971 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8972 border_element = Feld[xx][yy];
8973 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8974 border_element = MovingOrBlocked2Element(xx, yy);
8976 continue; /* center and border element do not touch */
8978 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
8979 player->index_bit, border_side);
8980 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
8981 CE_PLAYER_TOUCHES_X,
8982 player->index_bit, border_side);
8984 else if (IS_PLAYER(xx, yy))
8986 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8988 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8990 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8991 continue; /* center and border element do not touch */
8994 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
8995 player->index_bit, center_side);
8996 CheckTriggeredElementChangeByPlayer(x, y, center_element,
8997 CE_PLAYER_TOUCHES_X,
8998 player->index_bit, center_side);
9004 void TestIfElementTouchesCustomElement(int x, int y)
9006 static int xy[4][2] =
9013 static int trigger_sides[4][2] =
9015 /* center side border side */
9016 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9017 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9018 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9019 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9021 static int touch_dir[4] =
9028 boolean change_center_element = FALSE;
9029 int center_element = Feld[x][y]; /* should always be non-moving! */
9032 for (i = 0; i < NUM_DIRECTIONS; i++)
9034 int xx = x + xy[i][0];
9035 int yy = y + xy[i][1];
9036 int center_side = trigger_sides[i][0];
9037 int border_side = trigger_sides[i][1];
9040 if (!IN_LEV_FIELD(xx, yy))
9043 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9044 border_element = Feld[xx][yy]; /* may be moving! */
9045 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9046 border_element = Feld[xx][yy];
9047 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9048 border_element = MovingOrBlocked2Element(xx, yy);
9050 continue; /* center and border element do not touch */
9052 /* check for change of center element (but change it only once) */
9053 if (!change_center_element)
9054 change_center_element =
9055 CheckElementChangeBySide(x, y, center_element, border_element,
9056 CE_TOUCHING_X, border_side);
9058 /* check for change of border element */
9059 CheckElementChangeBySide(xx, yy, border_element, center_element,
9060 CE_TOUCHING_X, center_side);
9064 void TestIfElementHitsCustomElement(int x, int y, int direction)
9066 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9067 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9068 int hitx = x + dx, hity = y + dy;
9069 int hitting_element = Feld[x][y];
9070 int touched_element;
9072 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9075 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9076 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9078 if (IN_LEV_FIELD(hitx, hity))
9080 int opposite_direction = MV_DIR_OPPOSITE(direction);
9081 int hitting_side = direction;
9082 int touched_side = opposite_direction;
9083 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9084 MovDir[hitx][hity] != direction ||
9085 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9091 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9092 CE_HITTING_X, touched_side);
9094 CheckElementChangeBySide(hitx, hity, touched_element,
9095 hitting_element, CE_HIT_BY_X, hitting_side);
9097 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9098 CE_HIT_BY_SOMETHING, opposite_direction);
9102 /* "hitting something" is also true when hitting the playfield border */
9103 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9104 CE_HITTING_SOMETHING, direction);
9108 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9110 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9111 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9112 int hitx = x + dx, hity = y + dy;
9113 int hitting_element = Feld[x][y];
9114 int touched_element;
9116 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9117 !IS_FREE(hitx, hity) &&
9118 (!IS_MOVING(hitx, hity) ||
9119 MovDir[hitx][hity] != direction ||
9120 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9123 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9127 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9131 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9132 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9134 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9135 EP_CAN_SMASH_EVERYTHING, direction);
9137 if (IN_LEV_FIELD(hitx, hity))
9139 int opposite_direction = MV_DIR_OPPOSITE(direction);
9140 int hitting_side = direction;
9141 int touched_side = opposite_direction;
9143 int touched_element = MovingOrBlocked2Element(hitx, hity);
9146 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9147 MovDir[hitx][hity] != direction ||
9148 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9157 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9158 CE_SMASHED_BY_SOMETHING, opposite_direction);
9160 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9161 CE_OTHER_IS_SMASHING, touched_side);
9163 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9164 CE_OTHER_GETS_SMASHED, hitting_side);
9170 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9172 int i, kill_x = -1, kill_y = -1;
9173 int bad_element = -1;
9174 static int test_xy[4][2] =
9181 static int test_dir[4] =
9189 for (i = 0; i < NUM_DIRECTIONS; i++)
9191 int test_x, test_y, test_move_dir, test_element;
9193 test_x = good_x + test_xy[i][0];
9194 test_y = good_y + test_xy[i][1];
9196 if (!IN_LEV_FIELD(test_x, test_y))
9200 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9202 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9204 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9205 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9207 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9208 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9212 bad_element = test_element;
9218 if (kill_x != -1 || kill_y != -1)
9220 if (IS_PLAYER(good_x, good_y))
9222 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9224 if (player->shield_deadly_time_left > 0 &&
9225 !IS_INDESTRUCTIBLE(bad_element))
9226 Bang(kill_x, kill_y);
9227 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9231 Bang(good_x, good_y);
9235 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9237 int i, kill_x = -1, kill_y = -1;
9238 int bad_element = Feld[bad_x][bad_y];
9239 static int test_xy[4][2] =
9246 static int touch_dir[4] =
9253 static int test_dir[4] =
9261 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9264 for (i = 0; i < NUM_DIRECTIONS; i++)
9266 int test_x, test_y, test_move_dir, test_element;
9268 test_x = bad_x + test_xy[i][0];
9269 test_y = bad_y + test_xy[i][1];
9270 if (!IN_LEV_FIELD(test_x, test_y))
9274 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9276 test_element = Feld[test_x][test_y];
9278 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9279 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9281 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9282 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9284 /* good thing is player or penguin that does not move away */
9285 if (IS_PLAYER(test_x, test_y))
9287 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9289 if (bad_element == EL_ROBOT && player->is_moving)
9290 continue; /* robot does not kill player if he is moving */
9292 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9294 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9295 continue; /* center and border element do not touch */
9302 else if (test_element == EL_PENGUIN)
9311 if (kill_x != -1 || kill_y != -1)
9313 if (IS_PLAYER(kill_x, kill_y))
9315 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9317 if (player->shield_deadly_time_left > 0 &&
9318 !IS_INDESTRUCTIBLE(bad_element))
9320 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9324 Bang(kill_x, kill_y);
9328 void TestIfPlayerTouchesBadThing(int x, int y)
9330 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9333 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
9335 TestIfGoodThingHitsBadThing(x, y, move_dir);
9338 void TestIfBadThingTouchesPlayer(int x, int y)
9340 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9343 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
9345 TestIfBadThingHitsGoodThing(x, y, move_dir);
9348 void TestIfFriendTouchesBadThing(int x, int y)
9350 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9353 void TestIfBadThingTouchesFriend(int x, int y)
9355 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9358 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9360 int i, kill_x = bad_x, kill_y = bad_y;
9361 static int xy[4][2] =
9369 for (i = 0; i < NUM_DIRECTIONS; i++)
9373 x = bad_x + xy[i][0];
9374 y = bad_y + xy[i][1];
9375 if (!IN_LEV_FIELD(x, y))
9378 element = Feld[x][y];
9379 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9380 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9388 if (kill_x != bad_x || kill_y != bad_y)
9392 void KillPlayer(struct PlayerInfo *player)
9394 int jx = player->jx, jy = player->jy;
9396 if (!player->active)
9399 /* remove accessible field at the player's position */
9400 Feld[jx][jy] = EL_EMPTY;
9402 /* deactivate shield (else Bang()/Explode() would not work right) */
9403 player->shield_normal_time_left = 0;
9404 player->shield_deadly_time_left = 0;
9410 static void KillPlayerUnlessEnemyProtected(int x, int y)
9412 if (!PLAYER_ENEMY_PROTECTED(x, y))
9413 KillPlayer(PLAYERINFO(x, y));
9416 static void KillPlayerUnlessExplosionProtected(int x, int y)
9418 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9419 KillPlayer(PLAYERINFO(x, y));
9422 void BuryPlayer(struct PlayerInfo *player)
9424 int jx = player->jx, jy = player->jy;
9426 if (!player->active)
9429 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9430 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9432 player->GameOver = TRUE;
9433 RemovePlayer(player);
9436 void RemovePlayer(struct PlayerInfo *player)
9438 int jx = player->jx, jy = player->jy;
9439 int i, found = FALSE;
9441 player->present = FALSE;
9442 player->active = FALSE;
9444 if (!ExplodeField[jx][jy])
9445 StorePlayer[jx][jy] = 0;
9447 if (player->is_moving)
9448 DrawLevelField(player->last_jx, player->last_jy);
9450 for (i = 0; i < MAX_PLAYERS; i++)
9451 if (stored_player[i].active)
9455 AllPlayersGone = TRUE;
9462 =============================================================================
9463 checkDiagonalPushing()
9464 -----------------------------------------------------------------------------
9465 check if diagonal input device direction results in pushing of object
9466 (by checking if the alternative direction is walkable, diggable, ...)
9467 =============================================================================
9470 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9471 int x, int y, int real_dx, int real_dy)
9473 int jx, jy, dx, dy, xx, yy;
9475 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9478 /* diagonal direction: check alternative direction */
9483 xx = jx + (dx == 0 ? real_dx : 0);
9484 yy = jy + (dy == 0 ? real_dy : 0);
9486 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9490 =============================================================================
9492 -----------------------------------------------------------------------------
9493 x, y: field next to player (non-diagonal) to try to dig to
9494 real_dx, real_dy: direction as read from input device (can be diagonal)
9495 =============================================================================
9498 int DigField(struct PlayerInfo *player,
9499 int oldx, int oldy, int x, int y,
9500 int real_dx, int real_dy, int mode)
9502 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
9503 boolean player_was_pushing = player->is_pushing;
9504 int jx = oldx, jy = oldy;
9505 int dx = x - jx, dy = y - jy;
9506 int nextx = x + dx, nexty = y + dy;
9507 int move_direction = (dx == -1 ? MV_LEFT :
9508 dx == +1 ? MV_RIGHT :
9510 dy == +1 ? MV_DOWN : MV_NONE);
9511 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9512 int dig_side = MV_DIR_OPPOSITE(move_direction);
9513 int old_element = Feld[jx][jy];
9517 if (is_player) /* function can also be called by EL_PENGUIN */
9519 if (player->MovPos == 0)
9521 player->is_digging = FALSE;
9522 player->is_collecting = FALSE;
9525 if (player->MovPos == 0) /* last pushing move finished */
9526 player->is_pushing = FALSE;
9528 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9530 player->is_switching = FALSE;
9531 player->push_delay = -1;
9533 return MF_NO_ACTION;
9537 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9538 return MF_NO_ACTION;
9540 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9541 old_element = Back[jx][jy];
9543 /* in case of element dropped at player position, check background */
9544 else if (Back[jx][jy] != EL_EMPTY &&
9545 game.engine_version >= VERSION_IDENT(2,2,0,0))
9546 old_element = Back[jx][jy];
9548 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
9549 return MF_NO_ACTION; /* field has no opening in this direction */
9551 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
9552 return MF_NO_ACTION; /* field has no opening in this direction */
9554 element = Feld[x][y];
9555 #if USE_NEW_CUSTOM_VALUE
9558 collect_count = element_info[element].collect_count_initial;
9560 collect_count = CustomValue[x][y];
9564 collect_count = element_info[element].collect_count_initial;
9568 if (element != EL_BLOCKED &&
9569 CustomValue[x][y] != element_info[element].collect_count_initial)
9570 printf("::: %d: %d != %d\n",
9573 element_info[element].collect_count_initial);
9576 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
9577 return MF_NO_ACTION;
9579 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9580 game.engine_version >= VERSION_IDENT(2,2,0,0))
9581 return MF_NO_ACTION;
9583 if (game.gravity && is_player && !player->is_auto_moving &&
9584 canFallDown(player) && move_direction != MV_DOWN &&
9585 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
9586 return MF_NO_ACTION; /* player cannot walk here due to gravity */
9588 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
9590 int sound_element = SND_ELEMENT(element);
9591 int sound_action = ACTION_WALKING;
9593 if (IS_RND_GATE(element))
9595 if (!player->key[RND_GATE_NR(element)])
9596 return MF_NO_ACTION;
9598 else if (IS_RND_GATE_GRAY(element))
9600 if (!player->key[RND_GATE_GRAY_NR(element)])
9601 return MF_NO_ACTION;
9603 else if (element == EL_EXIT_OPEN ||
9604 element == EL_SP_EXIT_OPEN ||
9605 element == EL_SP_EXIT_OPENING)
9607 sound_action = ACTION_PASSING; /* player is passing exit */
9609 else if (element == EL_EMPTY)
9611 sound_action = ACTION_MOVING; /* nothing to walk on */
9614 /* play sound from background or player, whatever is available */
9615 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
9616 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
9618 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9620 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
9622 if (!ACCESS_FROM(element, opposite_direction))
9623 return MF_NO_ACTION; /* field not accessible from this direction */
9625 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9626 return MF_NO_ACTION;
9628 if (IS_EM_GATE(element))
9630 if (!player->key[EM_GATE_NR(element)])
9631 return MF_NO_ACTION;
9633 else if (IS_EM_GATE_GRAY(element))
9635 if (!player->key[EM_GATE_GRAY_NR(element)])
9636 return MF_NO_ACTION;
9638 else if (IS_SP_PORT(element))
9640 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9641 element == EL_SP_GRAVITY_PORT_RIGHT ||
9642 element == EL_SP_GRAVITY_PORT_UP ||
9643 element == EL_SP_GRAVITY_PORT_DOWN)
9644 game.gravity = !game.gravity;
9645 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
9646 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
9647 element == EL_SP_GRAVITY_ON_PORT_UP ||
9648 element == EL_SP_GRAVITY_ON_PORT_DOWN)
9649 game.gravity = TRUE;
9650 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
9651 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
9652 element == EL_SP_GRAVITY_OFF_PORT_UP ||
9653 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
9654 game.gravity = FALSE;
9657 /* automatically move to the next field with double speed */
9658 player->programmed_action = move_direction;
9660 if (player->move_delay_reset_counter == 0)
9662 player->move_delay_reset_counter = 2; /* two double speed steps */
9664 DOUBLE_PLAYER_SPEED(player);
9667 PlayLevelSoundAction(x, y, ACTION_PASSING);
9669 else if (IS_DIGGABLE(element))
9673 if (mode != DF_SNAP)
9675 GfxElement[x][y] = GFX_ELEMENT(element);
9676 player->is_digging = TRUE;
9679 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9681 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
9682 player->index_bit, dig_side);
9684 if (mode == DF_SNAP)
9685 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9687 else if (IS_COLLECTIBLE(element))
9691 if (is_player && mode != DF_SNAP)
9693 GfxElement[x][y] = element;
9694 player->is_collecting = TRUE;
9697 if (element == EL_SPEED_PILL)
9699 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9701 else if (element == EL_EXTRA_TIME && level.time > 0)
9703 TimeLeft += level.extra_time;
9704 DrawGameValue_Time(TimeLeft);
9706 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9708 player->shield_normal_time_left += level.shield_normal_time;
9709 if (element == EL_SHIELD_DEADLY)
9710 player->shield_deadly_time_left += level.shield_deadly_time;
9712 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9714 if (player->inventory_size < MAX_INVENTORY_SIZE)
9715 player->inventory_element[player->inventory_size++] = element;
9717 DrawGameValue_Dynamite(local_player->inventory_size);
9719 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9721 player->dynabomb_count++;
9722 player->dynabombs_left++;
9724 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9726 player->dynabomb_size++;
9728 else if (element == EL_DYNABOMB_INCREASE_POWER)
9730 player->dynabomb_xl = TRUE;
9732 else if (IS_KEY(element))
9734 player->key[KEY_NR(element)] = TRUE;
9736 DrawGameValue_Keys(player->key);
9738 redraw_mask |= REDRAW_DOOR_1;
9740 else if (IS_ENVELOPE(element))
9742 player->show_envelope = element;
9744 else if (IS_DROPPABLE(element) ||
9745 IS_THROWABLE(element)) /* can be collected and dropped */
9749 if (collect_count == 0)
9750 player->inventory_infinite_element = element;
9752 for (i = 0; i < collect_count; i++)
9753 if (player->inventory_size < MAX_INVENTORY_SIZE)
9754 player->inventory_element[player->inventory_size++] = element;
9756 DrawGameValue_Dynamite(local_player->inventory_size);
9758 else if (collect_count > 0)
9760 local_player->gems_still_needed -= collect_count;
9761 if (local_player->gems_still_needed < 0)
9762 local_player->gems_still_needed = 0;
9764 DrawGameValue_Emeralds(local_player->gems_still_needed);
9767 RaiseScoreElement(element);
9768 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9771 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
9772 player->index_bit, dig_side);
9774 if (mode == DF_SNAP)
9775 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9777 else if (IS_PUSHABLE(element))
9779 if (mode == DF_SNAP && element != EL_BD_ROCK)
9780 return MF_NO_ACTION;
9782 if (CAN_FALL(element) && dy)
9783 return MF_NO_ACTION;
9785 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9786 !(element == EL_SPRING && level.use_spring_bug))
9787 return MF_NO_ACTION;
9789 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9790 ((move_direction & MV_VERTICAL &&
9791 ((element_info[element].move_pattern & MV_LEFT &&
9792 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9793 (element_info[element].move_pattern & MV_RIGHT &&
9794 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9795 (move_direction & MV_HORIZONTAL &&
9796 ((element_info[element].move_pattern & MV_UP &&
9797 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9798 (element_info[element].move_pattern & MV_DOWN &&
9799 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9800 return MF_NO_ACTION;
9802 /* do not push elements already moving away faster than player */
9803 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9804 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9805 return MF_NO_ACTION;
9807 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9809 if (player->push_delay_value == -1 || !player_was_pushing)
9810 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9812 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9814 if (player->push_delay_value == -1)
9815 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9817 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9819 if (!player->is_pushing)
9820 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9823 player->is_pushing = TRUE;
9825 if (!(IN_LEV_FIELD(nextx, nexty) &&
9826 (IS_FREE(nextx, nexty) ||
9827 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9828 IS_SB_ELEMENT(element)))))
9829 return MF_NO_ACTION;
9831 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9832 return MF_NO_ACTION;
9834 if (player->push_delay == -1) /* new pushing; restart delay */
9835 player->push_delay = 0;
9837 if (player->push_delay < player->push_delay_value &&
9838 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9839 element != EL_SPRING && element != EL_BALLOON)
9841 /* make sure that there is no move delay before next try to push */
9842 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9843 player->move_delay = 0;
9845 return MF_NO_ACTION;
9848 if (IS_SB_ELEMENT(element))
9850 if (element == EL_SOKOBAN_FIELD_FULL)
9852 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9853 local_player->sokobanfields_still_needed++;
9856 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9858 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9859 local_player->sokobanfields_still_needed--;
9862 Feld[x][y] = EL_SOKOBAN_OBJECT;
9864 if (Back[x][y] == Back[nextx][nexty])
9865 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9866 else if (Back[x][y] != 0)
9867 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9870 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9873 if (local_player->sokobanfields_still_needed == 0 &&
9874 game.emulation == EMU_SOKOBAN)
9876 player->LevelSolved = player->GameOver = TRUE;
9877 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9881 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9883 InitMovingField(x, y, move_direction);
9884 GfxAction[x][y] = ACTION_PUSHING;
9886 if (mode == DF_SNAP)
9887 ContinueMoving(x, y);
9889 MovPos[x][y] = (dx != 0 ? dx : dy);
9891 Pushed[x][y] = TRUE;
9892 Pushed[nextx][nexty] = TRUE;
9894 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9895 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9897 player->push_delay_value = -1; /* get new value later */
9899 /* check for element change _after_ element has been pushed */
9900 if (game.use_change_when_pushing_bug)
9902 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
9903 player->index_bit, dig_side);
9904 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
9905 player->index_bit, dig_side);
9908 else if (IS_SWITCHABLE(element))
9910 if (PLAYER_SWITCHING(player, x, y))
9912 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
9913 player->index_bit, dig_side);
9918 player->is_switching = TRUE;
9919 player->switch_x = x;
9920 player->switch_y = y;
9922 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9924 if (element == EL_ROBOT_WHEEL)
9926 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9930 DrawLevelField(x, y);
9932 else if (element == EL_SP_TERMINAL)
9936 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9938 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9940 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9941 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9944 else if (IS_BELT_SWITCH(element))
9946 ToggleBeltSwitch(x, y);
9948 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9949 element == EL_SWITCHGATE_SWITCH_DOWN)
9951 ToggleSwitchgateSwitch(x, y);
9953 else if (element == EL_LIGHT_SWITCH ||
9954 element == EL_LIGHT_SWITCH_ACTIVE)
9956 ToggleLightSwitch(x, y);
9958 else if (element == EL_TIMEGATE_SWITCH)
9960 ActivateTimegateSwitch(x, y);
9962 else if (element == EL_BALLOON_SWITCH_LEFT ||
9963 element == EL_BALLOON_SWITCH_RIGHT ||
9964 element == EL_BALLOON_SWITCH_UP ||
9965 element == EL_BALLOON_SWITCH_DOWN ||
9966 element == EL_BALLOON_SWITCH_NONE ||
9967 element == EL_BALLOON_SWITCH_ANY)
9969 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9970 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9971 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9972 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9973 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
9976 else if (element == EL_LAMP)
9978 Feld[x][y] = EL_LAMP_ACTIVE;
9979 local_player->lights_still_needed--;
9981 ResetGfxAnimation(x, y);
9982 DrawLevelField(x, y);
9984 else if (element == EL_TIME_ORB_FULL)
9986 Feld[x][y] = EL_TIME_ORB_EMPTY;
9987 TimeLeft += level.time_orb_time;
9988 DrawGameValue_Time(TimeLeft);
9990 ResetGfxAnimation(x, y);
9991 DrawLevelField(x, y);
9994 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
9995 player->index_bit, dig_side);
9997 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
9998 player->index_bit, dig_side);
10004 if (!PLAYER_SWITCHING(player, x, y))
10006 player->is_switching = TRUE;
10007 player->switch_x = x;
10008 player->switch_y = y;
10010 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
10011 player->index_bit, dig_side);
10012 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10013 player->index_bit, dig_side);
10016 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10017 player->index_bit, dig_side);
10018 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10019 player->index_bit, dig_side);
10021 return MF_NO_ACTION;
10024 player->push_delay = -1;
10026 if (is_player) /* function can also be called by EL_PENGUIN */
10028 if (Feld[x][y] != element) /* really digged/collected something */
10029 player->is_collecting = !player->is_digging;
10035 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10037 int jx = player->jx, jy = player->jy;
10038 int x = jx + dx, y = jy + dy;
10039 int snap_direction = (dx == -1 ? MV_LEFT :
10040 dx == +1 ? MV_RIGHT :
10042 dy == +1 ? MV_DOWN : MV_NONE);
10044 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
10047 if (!player->active || !IN_LEV_FIELD(x, y))
10055 if (player->MovPos == 0)
10056 player->is_pushing = FALSE;
10058 player->is_snapping = FALSE;
10060 if (player->MovPos == 0)
10062 player->is_moving = FALSE;
10063 player->is_digging = FALSE;
10064 player->is_collecting = FALSE;
10070 if (player->is_snapping)
10073 player->MovDir = snap_direction;
10075 if (player->MovPos == 0)
10077 player->is_moving = FALSE;
10078 player->is_digging = FALSE;
10079 player->is_collecting = FALSE;
10082 player->is_dropping = FALSE;
10084 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10087 player->is_snapping = TRUE;
10089 if (player->MovPos == 0)
10091 player->is_moving = FALSE;
10092 player->is_digging = FALSE;
10093 player->is_collecting = FALSE;
10096 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
10097 DrawLevelField(player->last_jx, player->last_jy);
10099 DrawLevelField(x, y);
10104 boolean DropElement(struct PlayerInfo *player)
10106 int old_element, new_element;
10107 int dropx = player->jx, dropy = player->jy;
10108 int drop_direction = player->MovDir;
10109 int drop_side = drop_direction;
10110 int drop_element = (player->inventory_size > 0 ?
10111 player->inventory_element[player->inventory_size - 1] :
10112 player->inventory_infinite_element != EL_UNDEFINED ?
10113 player->inventory_infinite_element :
10114 player->dynabombs_left > 0 ?
10115 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10118 /* do not drop an element on top of another element; when holding drop key
10119 pressed without moving, dropped element must move away before the next
10120 element can be dropped (this is especially important if the next element
10121 is dynamite, which can be placed on background for historical reasons) */
10122 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
10125 if (IS_THROWABLE(drop_element))
10127 dropx += GET_DX_FROM_DIR(drop_direction);
10128 dropy += GET_DY_FROM_DIR(drop_direction);
10130 if (!IN_LEV_FIELD(dropx, dropy))
10134 old_element = Feld[dropx][dropy]; /* old element at dropping position */
10135 new_element = drop_element; /* default: no change when dropping */
10137 /* check if player is active, not moving and ready to drop */
10138 if (!player->active || player->MovPos || player->drop_delay > 0)
10141 /* check if player has anything that can be dropped */
10142 if (new_element == EL_UNDEFINED)
10145 /* check if anything can be dropped at the current position */
10146 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10149 /* collected custom elements can only be dropped on empty fields */
10150 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10153 if (old_element != EL_EMPTY)
10154 Back[dropx][dropy] = old_element; /* store old element on this field */
10156 ResetGfxAnimation(dropx, dropy);
10157 ResetRandomAnimationValue(dropx, dropy);
10159 if (player->inventory_size > 0 ||
10160 player->inventory_infinite_element != EL_UNDEFINED)
10162 if (player->inventory_size > 0)
10164 player->inventory_size--;
10166 DrawGameValue_Dynamite(local_player->inventory_size);
10168 if (new_element == EL_DYNAMITE)
10169 new_element = EL_DYNAMITE_ACTIVE;
10170 else if (new_element == EL_SP_DISK_RED)
10171 new_element = EL_SP_DISK_RED_ACTIVE;
10174 Feld[dropx][dropy] = new_element;
10176 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10177 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10178 el2img(Feld[dropx][dropy]), 0);
10180 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10182 /* needed if previous element just changed to "empty" in the last frame */
10183 Changed[dropx][dropy] = FALSE; /* allow another change */
10185 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
10186 player->index_bit, drop_side);
10187 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
10189 player->index_bit, drop_side);
10191 TestIfElementTouchesCustomElement(dropx, dropy);
10193 else /* player is dropping a dyna bomb */
10195 player->dynabombs_left--;
10197 Feld[dropx][dropy] = new_element;
10199 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10200 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10201 el2img(Feld[dropx][dropy]), 0);
10203 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10206 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
10207 InitField_WithBug1(dropx, dropy, FALSE);
10209 new_element = Feld[dropx][dropy]; /* element might have changed */
10211 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10212 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10214 int move_direction, nextx, nexty;
10216 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10217 MovDir[dropx][dropy] = drop_direction;
10219 move_direction = MovDir[dropx][dropy];
10220 nextx = dropx + GET_DX_FROM_DIR(move_direction);
10221 nexty = dropy + GET_DY_FROM_DIR(move_direction);
10223 Changed[dropx][dropy] = FALSE; /* allow another change */
10224 CheckCollision[dropx][dropy] = 2;
10227 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
10228 player->is_dropping = TRUE;
10230 player->drop_x = dropx;
10231 player->drop_y = dropy;
10236 /* ------------------------------------------------------------------------- */
10237 /* game sound playing functions */
10238 /* ------------------------------------------------------------------------- */
10240 static int *loop_sound_frame = NULL;
10241 static int *loop_sound_volume = NULL;
10243 void InitPlayLevelSound()
10245 int num_sounds = getSoundListSize();
10247 checked_free(loop_sound_frame);
10248 checked_free(loop_sound_volume);
10250 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10251 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10254 static void PlayLevelSound(int x, int y, int nr)
10256 int sx = SCREENX(x), sy = SCREENY(y);
10257 int volume, stereo_position;
10258 int max_distance = 8;
10259 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10261 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10262 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10265 if (!IN_LEV_FIELD(x, y) ||
10266 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10267 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10270 volume = SOUND_MAX_VOLUME;
10272 if (!IN_SCR_FIELD(sx, sy))
10274 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10275 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10277 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10280 stereo_position = (SOUND_MAX_LEFT +
10281 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10282 (SCR_FIELDX + 2 * max_distance));
10284 if (IS_LOOP_SOUND(nr))
10286 /* This assures that quieter loop sounds do not overwrite louder ones,
10287 while restarting sound volume comparison with each new game frame. */
10289 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10292 loop_sound_volume[nr] = volume;
10293 loop_sound_frame[nr] = FrameCounter;
10296 PlaySoundExt(nr, volume, stereo_position, type);
10299 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10301 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10302 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10303 y < LEVELY(BY1) ? LEVELY(BY1) :
10304 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10308 static void PlayLevelSoundAction(int x, int y, int action)
10310 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10313 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10315 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10317 if (sound_effect != SND_UNDEFINED)
10318 PlayLevelSound(x, y, sound_effect);
10321 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10324 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10326 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10327 PlayLevelSound(x, y, sound_effect);
10330 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10332 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10334 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10335 PlayLevelSound(x, y, sound_effect);
10338 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10340 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10342 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10343 StopSound(sound_effect);
10346 static void PlayLevelMusic()
10348 if (levelset.music[level_nr] != MUS_UNDEFINED)
10349 PlayMusic(levelset.music[level_nr]); /* from config file */
10351 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10354 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
10356 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
10361 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
10365 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10369 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10373 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10377 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10381 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10385 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10388 case SAMPLE_android_clone:
10389 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10392 case SAMPLE_android_move:
10393 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10396 case SAMPLE_spring:
10397 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10401 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
10405 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
10408 case SAMPLE_eater_eat:
10409 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10413 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10416 case SAMPLE_collect:
10417 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10420 case SAMPLE_diamond:
10421 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10424 case SAMPLE_squash:
10425 /* !!! CHECK THIS !!! */
10427 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10429 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
10433 case SAMPLE_wonderfall:
10434 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
10438 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10442 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10446 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10450 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
10454 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10458 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
10461 case SAMPLE_wonder:
10462 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10466 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10469 case SAMPLE_exit_open:
10470 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
10473 case SAMPLE_exit_leave:
10474 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10477 case SAMPLE_dynamite:
10478 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10482 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10486 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10490 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10494 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
10498 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
10502 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10506 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
10511 void RaiseScore(int value)
10513 local_player->score += value;
10515 DrawGameValue_Score(local_player->score);
10518 void RaiseScoreElement(int element)
10523 case EL_BD_DIAMOND:
10524 case EL_EMERALD_YELLOW:
10525 case EL_EMERALD_RED:
10526 case EL_EMERALD_PURPLE:
10527 case EL_SP_INFOTRON:
10528 RaiseScore(level.score[SC_EMERALD]);
10531 RaiseScore(level.score[SC_DIAMOND]);
10534 RaiseScore(level.score[SC_CRYSTAL]);
10537 RaiseScore(level.score[SC_PEARL]);
10540 case EL_BD_BUTTERFLY:
10541 case EL_SP_ELECTRON:
10542 RaiseScore(level.score[SC_BUG]);
10545 case EL_BD_FIREFLY:
10546 case EL_SP_SNIKSNAK:
10547 RaiseScore(level.score[SC_SPACESHIP]);
10550 case EL_DARK_YAMYAM:
10551 RaiseScore(level.score[SC_YAMYAM]);
10554 RaiseScore(level.score[SC_ROBOT]);
10557 RaiseScore(level.score[SC_PACMAN]);
10560 RaiseScore(level.score[SC_NUT]);
10563 case EL_SP_DISK_RED:
10564 case EL_DYNABOMB_INCREASE_NUMBER:
10565 case EL_DYNABOMB_INCREASE_SIZE:
10566 case EL_DYNABOMB_INCREASE_POWER:
10567 RaiseScore(level.score[SC_DYNAMITE]);
10569 case EL_SHIELD_NORMAL:
10570 case EL_SHIELD_DEADLY:
10571 RaiseScore(level.score[SC_SHIELD]);
10573 case EL_EXTRA_TIME:
10574 RaiseScore(level.score[SC_TIME_BONUS]);
10588 RaiseScore(level.score[SC_KEY]);
10591 RaiseScore(element_info[element].collect_score);
10596 void RequestQuitGame(boolean ask_if_really_quit)
10598 if (AllPlayersGone ||
10599 !ask_if_really_quit ||
10600 level_editor_test_game ||
10601 Request("Do you really want to quit the game ?",
10602 REQ_ASK | REQ_STAY_CLOSED))
10604 #if defined(NETWORK_AVALIABLE)
10605 if (options.network)
10606 SendToServer_StopPlaying();
10610 game_status = GAME_MODE_MAIN;
10616 if (tape.playing && tape.deactivate_display)
10617 TapeDeactivateDisplayOff(TRUE);
10619 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10621 if (tape.playing && tape.deactivate_display)
10622 TapeDeactivateDisplayOn();
10627 /* ---------- new game button stuff ---------------------------------------- */
10629 /* graphic position values for game buttons */
10630 #define GAME_BUTTON_XSIZE 30
10631 #define GAME_BUTTON_YSIZE 30
10632 #define GAME_BUTTON_XPOS 5
10633 #define GAME_BUTTON_YPOS 215
10634 #define SOUND_BUTTON_XPOS 5
10635 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10637 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10638 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10639 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10640 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10641 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10642 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10649 } gamebutton_info[NUM_GAME_BUTTONS] =
10652 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10657 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10658 GAME_CTRL_ID_PAUSE,
10662 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10667 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10668 SOUND_CTRL_ID_MUSIC,
10669 "background music on/off"
10672 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10673 SOUND_CTRL_ID_LOOPS,
10674 "sound loops on/off"
10677 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10678 SOUND_CTRL_ID_SIMPLE,
10679 "normal sounds on/off"
10683 void CreateGameButtons()
10687 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10689 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10690 struct GadgetInfo *gi;
10693 unsigned long event_mask;
10694 int gd_xoffset, gd_yoffset;
10695 int gd_x1, gd_x2, gd_y1, gd_y2;
10698 gd_xoffset = gamebutton_info[i].x;
10699 gd_yoffset = gamebutton_info[i].y;
10700 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10701 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10703 if (id == GAME_CTRL_ID_STOP ||
10704 id == GAME_CTRL_ID_PAUSE ||
10705 id == GAME_CTRL_ID_PLAY)
10707 button_type = GD_TYPE_NORMAL_BUTTON;
10709 event_mask = GD_EVENT_RELEASED;
10710 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10711 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10715 button_type = GD_TYPE_CHECK_BUTTON;
10717 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10718 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10719 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10720 event_mask = GD_EVENT_PRESSED;
10721 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10722 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10725 gi = CreateGadget(GDI_CUSTOM_ID, id,
10726 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10727 GDI_X, DX + gd_xoffset,
10728 GDI_Y, DY + gd_yoffset,
10729 GDI_WIDTH, GAME_BUTTON_XSIZE,
10730 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10731 GDI_TYPE, button_type,
10732 GDI_STATE, GD_BUTTON_UNPRESSED,
10733 GDI_CHECKED, checked,
10734 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10735 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10736 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10737 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10738 GDI_EVENT_MASK, event_mask,
10739 GDI_CALLBACK_ACTION, HandleGameButtons,
10743 Error(ERR_EXIT, "cannot create gadget");
10745 game_gadget[id] = gi;
10749 void FreeGameButtons()
10753 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10754 FreeGadget(game_gadget[i]);
10757 static void MapGameButtons()
10761 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10762 MapGadget(game_gadget[i]);
10765 void UnmapGameButtons()
10769 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10770 UnmapGadget(game_gadget[i]);
10773 static void HandleGameButtons(struct GadgetInfo *gi)
10775 int id = gi->custom_id;
10777 if (game_status != GAME_MODE_PLAYING)
10782 case GAME_CTRL_ID_STOP:
10783 RequestQuitGame(TRUE);
10786 case GAME_CTRL_ID_PAUSE:
10787 if (options.network)
10789 #if defined(NETWORK_AVALIABLE)
10791 SendToServer_ContinuePlaying();
10793 SendToServer_PausePlaying();
10797 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10800 case GAME_CTRL_ID_PLAY:
10803 #if defined(NETWORK_AVALIABLE)
10804 if (options.network)
10805 SendToServer_ContinuePlaying();
10809 tape.pausing = FALSE;
10810 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10815 case SOUND_CTRL_ID_MUSIC:
10816 if (setup.sound_music)
10818 setup.sound_music = FALSE;
10821 else if (audio.music_available)
10823 setup.sound = setup.sound_music = TRUE;
10825 SetAudioMode(setup.sound);
10831 case SOUND_CTRL_ID_LOOPS:
10832 if (setup.sound_loops)
10833 setup.sound_loops = FALSE;
10834 else if (audio.loops_available)
10836 setup.sound = setup.sound_loops = TRUE;
10837 SetAudioMode(setup.sound);
10841 case SOUND_CTRL_ID_SIMPLE:
10842 if (setup.sound_simple)
10843 setup.sound_simple = FALSE;
10844 else if (audio.sound_available)
10846 setup.sound = setup.sound_simple = TRUE;
10847 SetAudioMode(setup.sound);