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)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
43 /* for MovePlayer() */
44 #define MF_NO_ACTION 0
48 /* for ScrollPlayer() */
50 #define SCROLL_GO_ON 1
53 #define EX_PHASE_START 0
54 #define EX_TYPE_NONE 0
55 #define EX_TYPE_NORMAL (1 << 0)
56 #define EX_TYPE_CENTER (1 << 1)
57 #define EX_TYPE_BORDER (1 << 2)
58 #define EX_TYPE_CROSS (1 << 3)
59 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
61 /* special positions in the game control window (relative to control window) */
64 #define XX_EMERALDS 29
65 #define YY_EMERALDS 54
66 #define XX_DYNAMITE 29
67 #define YY_DYNAMITE 89
76 /* special positions in the game control window (relative to main window) */
77 #define DX_LEVEL (DX + XX_LEVEL)
78 #define DY_LEVEL (DY + YY_LEVEL)
79 #define DX_EMERALDS (DX + XX_EMERALDS)
80 #define DY_EMERALDS (DY + YY_EMERALDS)
81 #define DX_DYNAMITE (DX + XX_DYNAMITE)
82 #define DY_DYNAMITE (DY + YY_DYNAMITE)
83 #define DX_KEYS (DX + XX_KEYS)
84 #define DY_KEYS (DY + YY_KEYS)
85 #define DX_SCORE (DX + XX_SCORE)
86 #define DY_SCORE (DY + YY_SCORE)
87 #define DX_TIME1 (DX + XX_TIME1)
88 #define DX_TIME2 (DX + XX_TIME2)
89 #define DY_TIME (DY + YY_TIME)
91 /* values for initial player move delay (initial delay counter value) */
92 #define INITIAL_MOVE_DELAY_OFF -1
93 #define INITIAL_MOVE_DELAY_ON 0
95 /* values for player movement speed (which is in fact a delay value) */
96 #define MOVE_DELAY_MIN_SPEED 32
97 #define MOVE_DELAY_NORMAL_SPEED 8
98 #define MOVE_DELAY_HIGH_SPEED 4
99 #define MOVE_DELAY_MAX_SPEED 1
102 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
103 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
105 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
106 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
108 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
109 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
111 /* values for other actions */
112 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
113 #define MOVE_STEPSIZE_MIN (1)
114 #define MOVE_STEPSIZE_MAX (TILEX)
116 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
117 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
119 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
121 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
122 RND(element_info[e].push_delay_random))
123 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
124 RND(element_info[e].drop_delay_random))
125 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
126 RND(element_info[e].move_delay_random))
127 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
128 (element_info[e].move_delay_random))
129 #define GET_NEW_CUSTOM_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
130 RND(element_info[e].ce_value_random_initial))
131 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
132 RND((c)->delay_random * (c)->delay_frames))
134 #define GET_TARGET_ELEMENT(e, ch) \
135 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
136 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
138 #define CAN_GROW_INTO(e) \
139 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
141 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
142 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
145 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
146 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
147 (CAN_MOVE_INTO_ACID(e) && \
148 Feld[x][y] == EL_ACID) || \
151 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
152 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
153 (CAN_MOVE_INTO_ACID(e) && \
154 Feld[x][y] == EL_ACID) || \
157 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
158 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
160 (CAN_MOVE_INTO_ACID(e) && \
161 Feld[x][y] == EL_ACID) || \
162 (DONT_COLLIDE_WITH(e) && \
164 !PLAYER_ENEMY_PROTECTED(x, y))))
166 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
167 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
169 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
170 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
172 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
173 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
175 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
176 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
178 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
179 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
181 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
182 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
184 #define PIG_CAN_ENTER_FIELD(e, x, y) \
185 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
187 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
188 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
189 IS_FOOD_PENGUIN(Feld[x][y])))
190 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
191 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
193 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
194 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
196 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
197 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
199 #define GROUP_NR(e) ((e) - EL_GROUP_START)
200 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
201 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
202 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
204 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
205 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
207 #define CE_ENTER_FIELD_COND(e, x, y) \
208 (!IS_PLAYER(x, y) && \
209 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
211 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
212 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
214 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
215 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
217 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
218 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
219 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
220 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
222 /* game button identifiers */
223 #define GAME_CTRL_ID_STOP 0
224 #define GAME_CTRL_ID_PAUSE 1
225 #define GAME_CTRL_ID_PLAY 2
226 #define SOUND_CTRL_ID_MUSIC 3
227 #define SOUND_CTRL_ID_LOOPS 4
228 #define SOUND_CTRL_ID_SIMPLE 5
230 #define NUM_GAME_BUTTONS 6
233 /* forward declaration for internal use */
235 static void AdvanceFrameAndPlayerCounters(int);
237 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
238 static boolean MovePlayer(struct PlayerInfo *, int, int);
239 static void ScrollPlayer(struct PlayerInfo *, int);
240 static void ScrollScreen(struct PlayerInfo *, int);
242 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
244 static void InitBeltMovement(void);
245 static void CloseAllOpenTimegates(void);
246 static void CheckGravityMovement(struct PlayerInfo *);
247 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
248 static void KillPlayerUnlessEnemyProtected(int, int);
249 static void KillPlayerUnlessExplosionProtected(int, int);
251 static void TestIfPlayerTouchesCustomElement(int, int);
252 static void TestIfElementTouchesCustomElement(int, int);
253 static void TestIfElementHitsCustomElement(int, int, int);
255 static void TestIfElementSmashesCustomElement(int, int, int);
258 static void ChangeElement(int, int, int);
260 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
261 #define CheckTriggeredElementChange(x, y, e, ev) \
262 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
263 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
264 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
265 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
266 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
267 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
268 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
270 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
271 #define CheckElementChange(x, y, e, te, ev) \
272 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
273 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
274 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
275 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
276 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
278 static void PlayLevelSound(int, int, int);
279 static void PlayLevelSoundNearest(int, int, int);
280 static void PlayLevelSoundAction(int, int, int);
281 static void PlayLevelSoundElementAction(int, int, int, int);
282 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
283 static void PlayLevelSoundActionIfLoop(int, int, int);
284 static void StopLevelSoundActionIfLoop(int, int, int);
285 static void PlayLevelMusic();
287 static void MapGameButtons();
288 static void HandleGameButtons(struct GadgetInfo *);
290 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
293 /* ------------------------------------------------------------------------- */
294 /* definition of elements that automatically change to other elements after */
295 /* a specified time, eventually calling a function when changing */
296 /* ------------------------------------------------------------------------- */
298 /* forward declaration for changer functions */
299 static void InitBuggyBase(int x, int y);
300 static void WarnBuggyBase(int x, int y);
302 static void InitTrap(int x, int y);
303 static void ActivateTrap(int x, int y);
304 static void ChangeActiveTrap(int x, int y);
306 static void InitRobotWheel(int x, int y);
307 static void RunRobotWheel(int x, int y);
308 static void StopRobotWheel(int x, int y);
310 static void InitTimegateWheel(int x, int y);
311 static void RunTimegateWheel(int x, int y);
313 struct ChangingElementInfo
318 void (*pre_change_function)(int x, int y);
319 void (*change_function)(int x, int y);
320 void (*post_change_function)(int x, int y);
323 static struct ChangingElementInfo change_delay_list[] =
374 EL_SWITCHGATE_OPENING,
382 EL_SWITCHGATE_CLOSING,
383 EL_SWITCHGATE_CLOSED,
415 EL_ACID_SPLASH_RIGHT,
424 EL_SP_BUGGY_BASE_ACTIVATING,
431 EL_SP_BUGGY_BASE_ACTIVATING,
432 EL_SP_BUGGY_BASE_ACTIVE,
439 EL_SP_BUGGY_BASE_ACTIVE,
463 EL_ROBOT_WHEEL_ACTIVE,
471 EL_TIMEGATE_SWITCH_ACTIVE,
492 int push_delay_fixed, push_delay_random;
497 { EL_BALLOON, 0, 0 },
499 { EL_SOKOBAN_OBJECT, 2, 0 },
500 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
501 { EL_SATELLITE, 2, 0 },
502 { EL_SP_DISK_YELLOW, 2, 0 },
504 { EL_UNDEFINED, 0, 0 },
512 move_stepsize_list[] =
514 { EL_AMOEBA_DROP, 2 },
515 { EL_AMOEBA_DROPPING, 2 },
516 { EL_QUICKSAND_FILLING, 1 },
517 { EL_QUICKSAND_EMPTYING, 1 },
518 { EL_MAGIC_WALL_FILLING, 2 },
519 { EL_BD_MAGIC_WALL_FILLING, 2 },
520 { EL_MAGIC_WALL_EMPTYING, 2 },
521 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
531 collect_count_list[] =
534 { EL_BD_DIAMOND, 1 },
535 { EL_EMERALD_YELLOW, 1 },
536 { EL_EMERALD_RED, 1 },
537 { EL_EMERALD_PURPLE, 1 },
539 { EL_SP_INFOTRON, 1 },
551 access_direction_list[] =
553 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
554 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
555 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
556 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
557 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
558 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
559 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
560 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
561 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
562 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
563 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
565 { EL_SP_PORT_LEFT, MV_RIGHT },
566 { EL_SP_PORT_RIGHT, MV_LEFT },
567 { EL_SP_PORT_UP, MV_DOWN },
568 { EL_SP_PORT_DOWN, MV_UP },
569 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
570 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
571 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
572 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
573 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
574 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
575 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
576 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
577 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
578 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
579 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
580 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
581 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
582 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
583 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
585 { EL_UNDEFINED, MV_NONE }
588 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
590 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
591 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
592 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
593 IS_JUST_CHANGING(x, y))
595 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
598 void GetPlayerConfig()
600 if (!audio.sound_available)
601 setup.sound_simple = FALSE;
603 if (!audio.loops_available)
604 setup.sound_loops = FALSE;
606 if (!audio.music_available)
607 setup.sound_music = FALSE;
609 if (!video.fullscreen_available)
610 setup.fullscreen = FALSE;
612 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
614 SetAudioMode(setup.sound);
618 static int getBeltNrFromBeltElement(int element)
620 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
621 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
622 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
625 static int getBeltNrFromBeltActiveElement(int element)
627 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
628 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
629 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
632 static int getBeltNrFromBeltSwitchElement(int element)
634 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
635 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
636 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
639 static int getBeltDirNrFromBeltSwitchElement(int element)
641 static int belt_base_element[4] =
643 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
644 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
645 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
646 EL_CONVEYOR_BELT_4_SWITCH_LEFT
649 int belt_nr = getBeltNrFromBeltSwitchElement(element);
650 int belt_dir_nr = element - belt_base_element[belt_nr];
652 return (belt_dir_nr % 3);
655 static int getBeltDirFromBeltSwitchElement(int element)
657 static int belt_move_dir[3] =
664 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
666 return belt_move_dir[belt_dir_nr];
669 static void InitPlayerField(int x, int y, int element, boolean init_game)
671 if (element == EL_SP_MURPHY)
675 if (stored_player[0].present)
677 Feld[x][y] = EL_SP_MURPHY_CLONE;
683 stored_player[0].use_murphy_graphic = TRUE;
686 Feld[x][y] = EL_PLAYER_1;
692 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
693 int jx = player->jx, jy = player->jy;
695 player->present = TRUE;
697 player->block_last_field = (element == EL_SP_MURPHY ?
698 level.sp_block_last_field :
699 level.block_last_field);
701 /* ---------- initialize player's last field block delay --------------- */
703 /* always start with reliable default value (no adjustment needed) */
704 player->block_delay_adjustment = 0;
706 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
707 if (player->block_last_field && element == EL_SP_MURPHY)
708 player->block_delay_adjustment = 1;
710 /* special case 2: in game engines before 3.1.1, blocking was different */
711 if (game.use_block_last_field_bug)
712 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
714 if (!options.network || player->connected)
716 player->active = TRUE;
718 /* remove potentially duplicate players */
719 if (StorePlayer[jx][jy] == Feld[x][y])
720 StorePlayer[jx][jy] = 0;
722 StorePlayer[x][y] = Feld[x][y];
726 printf("Player %d activated.\n", player->element_nr);
727 printf("[Local player is %d and currently %s.]\n",
728 local_player->element_nr,
729 local_player->active ? "active" : "not active");
733 Feld[x][y] = EL_EMPTY;
735 player->jx = player->last_jx = x;
736 player->jy = player->last_jy = y;
740 static void InitField(int x, int y, boolean init_game)
742 int element = Feld[x][y];
751 InitPlayerField(x, y, element, init_game);
754 case EL_SOKOBAN_FIELD_PLAYER:
755 element = Feld[x][y] = EL_PLAYER_1;
756 InitField(x, y, init_game);
758 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
759 InitField(x, y, init_game);
762 case EL_SOKOBAN_FIELD_EMPTY:
763 local_player->sokobanfields_still_needed++;
767 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
768 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
769 else if (x > 0 && Feld[x-1][y] == EL_ACID)
770 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
771 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
772 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
773 else if (y > 0 && Feld[x][y-1] == EL_ACID)
774 Feld[x][y] = EL_ACID_POOL_BOTTOM;
775 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
776 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
784 case EL_SPACESHIP_RIGHT:
785 case EL_SPACESHIP_UP:
786 case EL_SPACESHIP_LEFT:
787 case EL_SPACESHIP_DOWN:
789 case EL_BD_BUTTERFLY_RIGHT:
790 case EL_BD_BUTTERFLY_UP:
791 case EL_BD_BUTTERFLY_LEFT:
792 case EL_BD_BUTTERFLY_DOWN:
793 case EL_BD_BUTTERFLY:
794 case EL_BD_FIREFLY_RIGHT:
795 case EL_BD_FIREFLY_UP:
796 case EL_BD_FIREFLY_LEFT:
797 case EL_BD_FIREFLY_DOWN:
799 case EL_PACMAN_RIGHT:
823 if (y == lev_fieldy - 1)
825 Feld[x][y] = EL_AMOEBA_GROWING;
826 Store[x][y] = EL_AMOEBA_WET;
830 case EL_DYNAMITE_ACTIVE:
831 case EL_SP_DISK_RED_ACTIVE:
832 case EL_DYNABOMB_PLAYER_1_ACTIVE:
833 case EL_DYNABOMB_PLAYER_2_ACTIVE:
834 case EL_DYNABOMB_PLAYER_3_ACTIVE:
835 case EL_DYNABOMB_PLAYER_4_ACTIVE:
840 local_player->lights_still_needed++;
844 local_player->friends_still_needed++;
849 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
852 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
853 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
854 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
855 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
856 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
857 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
858 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
859 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
860 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
861 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
862 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
863 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
866 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
867 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
868 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
870 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
872 game.belt_dir[belt_nr] = belt_dir;
873 game.belt_dir_nr[belt_nr] = belt_dir_nr;
875 else /* more than one switch -- set it like the first switch */
877 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
882 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
884 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
887 case EL_LIGHT_SWITCH_ACTIVE:
889 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
894 if (IS_CUSTOM_ELEMENT(element))
896 if (CAN_MOVE(element))
899 #if USE_NEW_CUSTOM_VALUE
900 if (!element_info[element].use_last_ce_value)
901 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
905 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
908 else if (IS_GROUP_ELEMENT(element))
910 struct ElementGroupInfo *group = element_info[element].group;
911 int last_anim_random_frame = gfx.anim_random_frame;
914 if (group->choice_mode == ANIM_RANDOM)
915 gfx.anim_random_frame = RND(group->num_elements_resolved);
917 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
918 group->choice_mode, 0,
921 if (group->choice_mode == ANIM_RANDOM)
922 gfx.anim_random_frame = last_anim_random_frame;
926 Feld[x][y] = group->element_resolved[element_pos];
928 InitField(x, y, init_game);
935 #if USE_NEW_CUSTOM_VALUE
938 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
940 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
948 static inline void InitField_WithBug1(int x, int y, boolean init_game)
950 InitField(x, y, init_game);
952 /* not needed to call InitMovDir() -- already done by InitField()! */
953 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
954 CAN_MOVE(Feld[x][y]))
958 static inline void InitField_WithBug2(int x, int y, boolean init_game)
960 int old_element = Feld[x][y];
962 InitField(x, y, init_game);
964 /* not needed to call InitMovDir() -- already done by InitField()! */
965 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
966 CAN_MOVE(old_element) &&
967 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
970 /* this case is in fact a combination of not less than three bugs:
971 first, it calls InitMovDir() for elements that can move, although this is
972 already done by InitField(); then, it checks the element that was at this
973 field _before_ the call to InitField() (which can change it); lastly, it
974 was not called for "mole with direction" elements, which were treated as
975 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
979 inline void DrawGameValue_Emeralds(int value)
981 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
984 inline void DrawGameValue_Dynamite(int value)
986 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
989 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
993 /* currently only 4 of 8 possible keys are displayed */
994 for (i = 0; i < STD_NUM_KEYS; i++)
997 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
998 el2edimg(EL_KEY_1 + i));
1000 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1001 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1002 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1006 inline void DrawGameValue_Score(int value)
1008 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1011 inline void DrawGameValue_Time(int value)
1014 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1016 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1019 inline void DrawGameValue_Level(int value)
1022 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1025 /* misuse area for displaying emeralds to draw bigger level number */
1026 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1027 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1029 /* now copy it to the area for displaying level number */
1030 BlitBitmap(drawto, drawto,
1031 DX_EMERALDS, DY_EMERALDS + 1,
1032 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1033 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1034 DX_LEVEL - 1, DY_LEVEL + 1);
1036 /* restore the area for displaying emeralds */
1037 DrawGameValue_Emeralds(local_player->gems_still_needed);
1039 /* yes, this is all really ugly :-) */
1043 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1046 int key[MAX_NUM_KEYS];
1049 for (i = 0; i < MAX_NUM_KEYS; i++)
1050 key[i] = key_bits & (1 << i);
1052 DrawGameValue_Level(level_nr);
1054 DrawGameValue_Emeralds(emeralds);
1055 DrawGameValue_Dynamite(dynamite);
1056 DrawGameValue_Score(score);
1057 DrawGameValue_Time(time);
1059 DrawGameValue_Keys(key);
1062 void DrawGameDoorValues()
1066 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1068 DrawGameDoorValues_EM();
1073 DrawGameValue_Level(level_nr);
1075 DrawGameValue_Emeralds(local_player->gems_still_needed);
1076 DrawGameValue_Dynamite(local_player->inventory_size);
1077 DrawGameValue_Score(local_player->score);
1078 DrawGameValue_Time(TimeLeft);
1080 for (i = 0; i < MAX_PLAYERS; i++)
1081 DrawGameValue_Keys(stored_player[i].key);
1084 static void resolve_group_element(int group_element, int recursion_depth)
1086 static int group_nr;
1087 static struct ElementGroupInfo *group;
1088 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1091 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1093 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1094 group_element - EL_GROUP_START + 1);
1096 /* replace element which caused too deep recursion by question mark */
1097 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1102 if (recursion_depth == 0) /* initialization */
1104 group = element_info[group_element].group;
1105 group_nr = group_element - EL_GROUP_START;
1107 group->num_elements_resolved = 0;
1108 group->choice_pos = 0;
1111 for (i = 0; i < actual_group->num_elements; i++)
1113 int element = actual_group->element[i];
1115 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1118 if (IS_GROUP_ELEMENT(element))
1119 resolve_group_element(element, recursion_depth + 1);
1122 group->element_resolved[group->num_elements_resolved++] = element;
1123 element_info[element].in_group[group_nr] = TRUE;
1130 =============================================================================
1132 -----------------------------------------------------------------------------
1133 initialize game engine due to level / tape version number
1134 =============================================================================
1137 static void InitGameEngine()
1141 /* set game engine from tape file when re-playing, else from level file */
1142 game.engine_version = (tape.playing ? tape.engine_version :
1143 level.game_version);
1145 /* ---------------------------------------------------------------------- */
1146 /* set flags for bugs and changes according to active game engine version */
1147 /* ---------------------------------------------------------------------- */
1150 Summary of bugfix/change:
1151 Fixed handling for custom elements that change when pushed by the player.
1153 Fixed/changed in version:
1157 Before 3.1.0, custom elements that "change when pushing" changed directly
1158 after the player started pushing them (until then handled in "DigField()").
1159 Since 3.1.0, these custom elements are not changed until the "pushing"
1160 move of the element is finished (now handled in "ContinueMoving()").
1162 Affected levels/tapes:
1163 The first condition is generally needed for all levels/tapes before version
1164 3.1.0, which might use the old behaviour before it was changed; known tapes
1165 that are affected are some tapes from the level set "Walpurgis Gardens" by
1167 The second condition is an exception from the above case and is needed for
1168 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1169 above (including some development versions of 3.1.0), but before it was
1170 known that this change would break tapes like the above and was fixed in
1171 3.1.1, so that the changed behaviour was active although the engine version
1172 while recording maybe was before 3.1.0. There is at least one tape that is
1173 affected by this exception, which is the tape for the one-level set "Bug
1174 Machine" by Juergen Bonhagen.
1177 game.use_change_when_pushing_bug =
1178 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1180 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1181 tape.game_version < VERSION_IDENT(3,1,1,0)));
1184 Summary of bugfix/change:
1185 Fixed handling for blocking the field the player leaves when moving.
1187 Fixed/changed in version:
1191 Before 3.1.1, when "block last field when moving" was enabled, the field
1192 the player is leaving when moving was blocked for the time of the move,
1193 and was directly unblocked afterwards. This resulted in the last field
1194 being blocked for exactly one less than the number of frames of one player
1195 move. Additionally, even when blocking was disabled, the last field was
1196 blocked for exactly one frame.
1197 Since 3.1.1, due to changes in player movement handling, the last field
1198 is not blocked at all when blocking is disabled. When blocking is enabled,
1199 the last field is blocked for exactly the number of frames of one player
1200 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1201 last field is blocked for exactly one more than the number of frames of
1204 Affected levels/tapes:
1205 (!!! yet to be determined -- probably many !!!)
1208 game.use_block_last_field_bug =
1209 (game.engine_version < VERSION_IDENT(3,1,1,0));
1211 /* ---------------------------------------------------------------------- */
1213 /* dynamically adjust element properties according to game engine version */
1214 InitElementPropertiesEngine(game.engine_version);
1217 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1218 printf(" tape version == %06d [%s] [file: %06d]\n",
1219 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1221 printf(" => game.engine_version == %06d\n", game.engine_version);
1224 /* ---------- recursively resolve group elements ------------------------- */
1226 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1227 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1228 element_info[i].in_group[j] = FALSE;
1230 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1231 resolve_group_element(EL_GROUP_START + i, 0);
1233 /* ---------- initialize player's initial move delay --------------------- */
1235 /* dynamically adjust player properties according to level information */
1236 game.initial_move_delay_value =
1237 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1239 /* dynamically adjust player properties according to game engine version */
1240 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1241 game.initial_move_delay_value : 0);
1243 /* ---------- initialize player's initial push delay --------------------- */
1245 /* dynamically adjust player properties according to game engine version */
1246 game.initial_push_delay_value =
1247 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1249 /* ---------- initialize changing elements ------------------------------- */
1251 /* initialize changing elements information */
1252 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1254 struct ElementInfo *ei = &element_info[i];
1256 /* this pointer might have been changed in the level editor */
1257 ei->change = &ei->change_page[0];
1259 if (!IS_CUSTOM_ELEMENT(i))
1261 ei->change->target_element = EL_EMPTY_SPACE;
1262 ei->change->delay_fixed = 0;
1263 ei->change->delay_random = 0;
1264 ei->change->delay_frames = 1;
1267 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1269 ei->has_change_event[j] = FALSE;
1271 ei->event_page_nr[j] = 0;
1272 ei->event_page[j] = &ei->change_page[0];
1276 /* add changing elements from pre-defined list */
1277 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1279 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1280 struct ElementInfo *ei = &element_info[ch_delay->element];
1282 ei->change->target_element = ch_delay->target_element;
1283 ei->change->delay_fixed = ch_delay->change_delay;
1285 ei->change->pre_change_function = ch_delay->pre_change_function;
1286 ei->change->change_function = ch_delay->change_function;
1287 ei->change->post_change_function = ch_delay->post_change_function;
1289 ei->change->can_change = TRUE;
1290 ei->change->can_change_or_has_action = TRUE;
1292 ei->has_change_event[CE_DELAY] = TRUE;
1294 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1295 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1298 /* ---------- initialize internal run-time variables ------------- */
1300 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1302 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1304 for (j = 0; j < ei->num_change_pages; j++)
1306 ei->change_page[j].can_change_or_has_action =
1307 (ei->change_page[j].can_change |
1308 ei->change_page[j].has_action);
1312 /* add change events from custom element configuration */
1313 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1315 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1317 for (j = 0; j < ei->num_change_pages; j++)
1319 if (!ei->change_page[j].can_change_or_has_action)
1322 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1324 /* only add event page for the first page found with this event */
1325 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1327 ei->has_change_event[k] = TRUE;
1329 ei->event_page_nr[k] = j;
1330 ei->event_page[k] = &ei->change_page[j];
1336 /* ---------- initialize run-time trigger player and element ------------- */
1338 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1340 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1342 for (j = 0; j < ei->num_change_pages; j++)
1344 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1345 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1346 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1347 ei->change_page[j].actual_trigger_ce_value = 0;
1351 /* ---------- initialize trigger events ---------------------------------- */
1353 /* initialize trigger events information */
1354 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1355 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1356 trigger_events[i][j] = FALSE;
1358 /* add trigger events from element change event properties */
1359 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1361 struct ElementInfo *ei = &element_info[i];
1363 for (j = 0; j < ei->num_change_pages; j++)
1365 if (!ei->change_page[j].can_change_or_has_action)
1368 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1370 int trigger_element = ei->change_page[j].trigger_element;
1372 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1374 if (ei->change_page[j].has_event[k])
1376 if (IS_GROUP_ELEMENT(trigger_element))
1378 struct ElementGroupInfo *group =
1379 element_info[trigger_element].group;
1381 for (l = 0; l < group->num_elements_resolved; l++)
1382 trigger_events[group->element_resolved[l]][k] = TRUE;
1385 trigger_events[trigger_element][k] = TRUE;
1392 /* ---------- initialize push delay -------------------------------------- */
1394 /* initialize push delay values to default */
1395 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1397 if (!IS_CUSTOM_ELEMENT(i))
1399 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1400 element_info[i].push_delay_random = game.default_push_delay_random;
1404 /* set push delay value for certain elements from pre-defined list */
1405 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1407 int e = push_delay_list[i].element;
1409 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1410 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1413 /* set push delay value for Supaplex elements for newer engine versions */
1414 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1416 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1418 if (IS_SP_ELEMENT(i))
1420 /* set SP push delay to just enough to push under a falling zonk */
1421 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1423 element_info[i].push_delay_fixed = delay;
1424 element_info[i].push_delay_random = 0;
1429 /* ---------- initialize move stepsize ----------------------------------- */
1431 /* initialize move stepsize values to default */
1432 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1433 if (!IS_CUSTOM_ELEMENT(i))
1434 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1436 /* set move stepsize value for certain elements from pre-defined list */
1437 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1439 int e = move_stepsize_list[i].element;
1441 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1444 /* ---------- initialize collect score ----------------------------------- */
1446 /* initialize collect score values for custom elements from initial value */
1447 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1448 if (IS_CUSTOM_ELEMENT(i))
1449 element_info[i].collect_score = element_info[i].collect_score_initial;
1451 /* ---------- initialize collect count ----------------------------------- */
1453 /* initialize collect count values for non-custom elements */
1454 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1455 if (!IS_CUSTOM_ELEMENT(i))
1456 element_info[i].collect_count_initial = 0;
1458 /* add collect count values for all elements from pre-defined list */
1459 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1460 element_info[collect_count_list[i].element].collect_count_initial =
1461 collect_count_list[i].count;
1463 /* ---------- initialize access direction -------------------------------- */
1465 /* initialize access direction values to default (access from every side) */
1466 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1467 if (!IS_CUSTOM_ELEMENT(i))
1468 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1470 /* set access direction value for certain elements from pre-defined list */
1471 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1472 element_info[access_direction_list[i].element].access_direction =
1473 access_direction_list[i].direction;
1478 =============================================================================
1480 -----------------------------------------------------------------------------
1481 initialize and start new game
1482 =============================================================================
1487 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1488 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1489 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1494 /* don't play tapes over network */
1495 network_playing = (options.network && !tape.playing);
1497 for (i = 0; i < MAX_PLAYERS; i++)
1499 struct PlayerInfo *player = &stored_player[i];
1501 player->index_nr = i;
1502 player->index_bit = (1 << i);
1503 player->element_nr = EL_PLAYER_1 + i;
1505 player->present = FALSE;
1506 player->active = FALSE;
1509 player->effective_action = 0;
1510 player->programmed_action = 0;
1513 player->gems_still_needed = level.gems_needed;
1514 player->sokobanfields_still_needed = 0;
1515 player->lights_still_needed = 0;
1516 player->friends_still_needed = 0;
1518 for (j = 0; j < MAX_NUM_KEYS; j++)
1519 player->key[j] = FALSE;
1521 player->dynabomb_count = 0;
1522 player->dynabomb_size = 1;
1523 player->dynabombs_left = 0;
1524 player->dynabomb_xl = FALSE;
1526 player->MovDir = MV_NONE;
1529 player->GfxDir = MV_NONE;
1530 player->GfxAction = ACTION_DEFAULT;
1532 player->StepFrame = 0;
1534 player->use_murphy_graphic = FALSE;
1536 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1537 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1539 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1541 player->actual_frame_counter = 0;
1543 player->step_counter = 0;
1545 player->last_move_dir = MV_NONE;
1547 player->is_waiting = FALSE;
1548 player->is_moving = FALSE;
1549 player->is_auto_moving = FALSE;
1550 player->is_digging = FALSE;
1551 player->is_snapping = FALSE;
1552 player->is_collecting = FALSE;
1553 player->is_pushing = FALSE;
1554 player->is_switching = FALSE;
1555 player->is_dropping = FALSE;
1557 player->is_bored = FALSE;
1558 player->is_sleeping = FALSE;
1560 player->cannot_move = FALSE;
1562 player->frame_counter_bored = -1;
1563 player->frame_counter_sleeping = -1;
1565 player->anim_delay_counter = 0;
1566 player->post_delay_counter = 0;
1568 player->action_waiting = ACTION_DEFAULT;
1569 player->last_action_waiting = ACTION_DEFAULT;
1570 player->special_action_bored = ACTION_DEFAULT;
1571 player->special_action_sleeping = ACTION_DEFAULT;
1573 player->num_special_action_bored = 0;
1574 player->num_special_action_sleeping = 0;
1576 /* determine number of special actions for bored and sleeping animation */
1577 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1579 boolean found = FALSE;
1581 for (k = 0; k < NUM_DIRECTIONS; k++)
1582 if (el_act_dir2img(player->element_nr, j, k) !=
1583 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1587 player->num_special_action_bored++;
1591 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1593 boolean found = FALSE;
1595 for (k = 0; k < NUM_DIRECTIONS; k++)
1596 if (el_act_dir2img(player->element_nr, j, k) !=
1597 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1601 player->num_special_action_sleeping++;
1606 player->switch_x = -1;
1607 player->switch_y = -1;
1609 player->drop_x = -1;
1610 player->drop_y = -1;
1612 player->show_envelope = 0;
1614 player->move_delay = game.initial_move_delay;
1615 player->move_delay_value = game.initial_move_delay_value;
1617 player->move_delay_value_next = -1;
1619 player->move_delay_reset_counter = 0;
1621 player->push_delay = -1; /* initialized when pushing starts */
1622 player->push_delay_value = game.initial_push_delay_value;
1624 player->drop_delay = 0;
1626 player->last_jx = player->last_jy = 0;
1627 player->jx = player->jy = 0;
1629 player->shield_normal_time_left = 0;
1630 player->shield_deadly_time_left = 0;
1632 player->inventory_infinite_element = EL_UNDEFINED;
1633 player->inventory_size = 0;
1635 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1636 SnapField(player, 0, 0);
1638 player->LevelSolved = FALSE;
1639 player->GameOver = FALSE;
1642 network_player_action_received = FALSE;
1644 #if defined(NETWORK_AVALIABLE)
1645 /* initial null action */
1646 if (network_playing)
1647 SendToServer_MovePlayer(MV_NONE);
1656 TimeLeft = level.time;
1659 ScreenMovDir = MV_NONE;
1663 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1665 AllPlayersGone = FALSE;
1667 game.yamyam_content_nr = 0;
1668 game.magic_wall_active = FALSE;
1669 game.magic_wall_time_left = 0;
1670 game.light_time_left = 0;
1671 game.timegate_time_left = 0;
1672 game.switchgate_pos = 0;
1673 game.wind_direction = level.wind_direction_initial;
1674 game.gravity = level.initial_gravity;
1675 game.explosions_delayed = TRUE;
1677 game.envelope_active = FALSE;
1679 for (i = 0; i < NUM_BELTS; i++)
1681 game.belt_dir[i] = MV_NONE;
1682 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1685 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1686 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1688 for (x = 0; x < lev_fieldx; x++)
1690 for (y = 0; y < lev_fieldy; y++)
1692 Feld[x][y] = level.field[x][y];
1693 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1694 ChangeDelay[x][y] = 0;
1695 ChangePage[x][y] = -1;
1696 #if USE_NEW_CUSTOM_VALUE
1697 CustomValue[x][y] = 0; /* initialized in InitField() */
1699 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1701 WasJustMoving[x][y] = 0;
1702 WasJustFalling[x][y] = 0;
1703 CheckCollision[x][y] = 0;
1705 Pushed[x][y] = FALSE;
1707 Changed[x][y] = FALSE;
1708 ChangeEvent[x][y] = -1;
1710 ExplodePhase[x][y] = 0;
1711 ExplodeDelay[x][y] = 0;
1712 ExplodeField[x][y] = EX_TYPE_NONE;
1714 RunnerVisit[x][y] = 0;
1715 PlayerVisit[x][y] = 0;
1718 GfxRandom[x][y] = INIT_GFX_RANDOM();
1719 GfxElement[x][y] = EL_UNDEFINED;
1720 GfxAction[x][y] = ACTION_DEFAULT;
1721 GfxDir[x][y] = MV_NONE;
1725 for (y = 0; y < lev_fieldy; y++)
1727 for (x = 0; x < lev_fieldx; x++)
1729 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1731 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1733 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1736 InitField(x, y, TRUE);
1742 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1743 emulate_sb ? EMU_SOKOBAN :
1744 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1746 #if USE_NEW_ALL_SLIPPERY
1747 /* initialize type of slippery elements */
1748 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1750 if (!IS_CUSTOM_ELEMENT(i))
1752 /* default: elements slip down either to the left or right randomly */
1753 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
1755 /* SP style elements prefer to slip down on the left side */
1756 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
1757 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1759 /* BD style elements prefer to slip down on the left side */
1760 if (game.emulation == EMU_BOULDERDASH)
1761 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1766 /* initialize explosion and ignition delay */
1767 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1769 if (!IS_CUSTOM_ELEMENT(i))
1772 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1773 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1774 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1775 int last_phase = (num_phase + 1) * delay;
1776 int half_phase = (num_phase / 2) * delay;
1778 element_info[i].explosion_delay = last_phase - 1;
1779 element_info[i].ignition_delay = half_phase;
1781 if (i == EL_BLACK_ORB)
1782 element_info[i].ignition_delay = 1;
1786 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1787 element_info[i].explosion_delay = 1;
1789 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1790 element_info[i].ignition_delay = 1;
1794 /* correct non-moving belts to start moving left */
1795 for (i = 0; i < NUM_BELTS; i++)
1796 if (game.belt_dir[i] == MV_NONE)
1797 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1799 /* check if any connected player was not found in playfield */
1800 for (i = 0; i < MAX_PLAYERS; i++)
1802 struct PlayerInfo *player = &stored_player[i];
1804 if (player->connected && !player->present)
1806 for (j = 0; j < MAX_PLAYERS; j++)
1808 struct PlayerInfo *some_player = &stored_player[j];
1809 int jx = some_player->jx, jy = some_player->jy;
1811 /* assign first free player found that is present in the playfield */
1812 if (some_player->present && !some_player->connected)
1814 player->present = TRUE;
1815 player->active = TRUE;
1817 some_player->present = FALSE;
1818 some_player->active = FALSE;
1821 player->element_nr = some_player->element_nr;
1824 player->block_last_field = some_player->block_last_field;
1825 player->block_delay_adjustment = some_player->block_delay_adjustment;
1827 StorePlayer[jx][jy] = player->element_nr;
1828 player->jx = player->last_jx = jx;
1829 player->jy = player->last_jy = jy;
1839 /* when playing a tape, eliminate all players which do not participate */
1841 for (i = 0; i < MAX_PLAYERS; i++)
1843 if (stored_player[i].active && !tape.player_participates[i])
1845 struct PlayerInfo *player = &stored_player[i];
1846 int jx = player->jx, jy = player->jy;
1848 player->active = FALSE;
1849 StorePlayer[jx][jy] = 0;
1850 Feld[jx][jy] = EL_EMPTY;
1854 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1856 /* when in single player mode, eliminate all but the first active player */
1858 for (i = 0; i < MAX_PLAYERS; i++)
1860 if (stored_player[i].active)
1862 for (j = i + 1; j < MAX_PLAYERS; j++)
1864 if (stored_player[j].active)
1866 struct PlayerInfo *player = &stored_player[j];
1867 int jx = player->jx, jy = player->jy;
1869 player->active = FALSE;
1870 player->present = FALSE;
1872 StorePlayer[jx][jy] = 0;
1873 Feld[jx][jy] = EL_EMPTY;
1880 /* when recording the game, store which players take part in the game */
1883 for (i = 0; i < MAX_PLAYERS; i++)
1884 if (stored_player[i].active)
1885 tape.player_participates[i] = TRUE;
1890 for (i = 0; i < MAX_PLAYERS; i++)
1892 struct PlayerInfo *player = &stored_player[i];
1894 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1899 if (local_player == player)
1900 printf("Player %d is local player.\n", i+1);
1904 if (BorderElement == EL_EMPTY)
1907 SBX_Right = lev_fieldx - SCR_FIELDX;
1909 SBY_Lower = lev_fieldy - SCR_FIELDY;
1914 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1916 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1919 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1920 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1922 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1923 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1925 /* if local player not found, look for custom element that might create
1926 the player (make some assumptions about the right custom element) */
1927 if (!local_player->present)
1929 int start_x = 0, start_y = 0;
1930 int found_rating = 0;
1931 int found_element = EL_UNDEFINED;
1932 int player_nr = local_player->index_nr;
1934 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1936 int element = Feld[x][y];
1941 if (level.use_start_element[player_nr] &&
1942 level.start_element[player_nr] == element &&
1949 found_element = element;
1952 if (!IS_CUSTOM_ELEMENT(element))
1955 if (CAN_CHANGE(element))
1957 for (i = 0; i < element_info[element].num_change_pages; i++)
1959 /* check for player created from custom element as single target */
1960 content = element_info[element].change_page[i].target_element;
1961 is_player = ELEM_IS_PLAYER(content);
1963 if (is_player && (found_rating < 3 || element < found_element))
1969 found_element = element;
1974 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1976 /* check for player created from custom element as explosion content */
1977 content = element_info[element].content.e[xx][yy];
1978 is_player = ELEM_IS_PLAYER(content);
1980 if (is_player && (found_rating < 2 || element < found_element))
1982 start_x = x + xx - 1;
1983 start_y = y + yy - 1;
1986 found_element = element;
1989 if (!CAN_CHANGE(element))
1992 for (i = 0; i < element_info[element].num_change_pages; i++)
1994 /* check for player created from custom element as extended target */
1996 element_info[element].change_page[i].target_content.e[xx][yy];
1998 is_player = ELEM_IS_PLAYER(content);
2000 if (is_player && (found_rating < 1 || element < found_element))
2002 start_x = x + xx - 1;
2003 start_y = y + yy - 1;
2006 found_element = element;
2012 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2013 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2016 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2017 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2022 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2023 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2024 local_player->jx - MIDPOSX);
2026 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2027 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2028 local_player->jy - MIDPOSY);
2031 if (!game.restart_level)
2032 CloseDoor(DOOR_CLOSE_1);
2034 /* !!! FIX THIS (START) !!! */
2035 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2037 InitGameEngine_EM();
2044 /* after drawing the level, correct some elements */
2045 if (game.timegate_time_left == 0)
2046 CloseAllOpenTimegates();
2048 if (setup.soft_scrolling)
2049 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2051 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2054 /* !!! FIX THIS (END) !!! */
2056 if (!game.restart_level)
2058 /* copy default game door content to main double buffer */
2059 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2060 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2063 DrawGameDoorValues();
2065 if (!game.restart_level)
2069 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2070 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2071 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2075 /* copy actual game door content to door double buffer for OpenDoor() */
2076 BlitBitmap(drawto, bitmap_db_door,
2077 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2079 OpenDoor(DOOR_OPEN_ALL);
2081 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2083 if (setup.sound_music)
2086 KeyboardAutoRepeatOffUnlessAutoplay();
2090 for (i = 0; i < MAX_PLAYERS; i++)
2091 printf("Player %d %sactive.\n",
2092 i + 1, (stored_player[i].active ? "" : "not "));
2096 game.restart_level = FALSE;
2099 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2101 /* this is used for non-R'n'D game engines to update certain engine values */
2103 /* needed to determine if sounds are played within the visible screen area */
2104 scroll_x = actual_scroll_x;
2105 scroll_y = actual_scroll_y;
2108 void InitMovDir(int x, int y)
2110 int i, element = Feld[x][y];
2111 static int xy[4][2] =
2118 static int direction[3][4] =
2120 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2121 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2122 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2131 Feld[x][y] = EL_BUG;
2132 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2135 case EL_SPACESHIP_RIGHT:
2136 case EL_SPACESHIP_UP:
2137 case EL_SPACESHIP_LEFT:
2138 case EL_SPACESHIP_DOWN:
2139 Feld[x][y] = EL_SPACESHIP;
2140 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2143 case EL_BD_BUTTERFLY_RIGHT:
2144 case EL_BD_BUTTERFLY_UP:
2145 case EL_BD_BUTTERFLY_LEFT:
2146 case EL_BD_BUTTERFLY_DOWN:
2147 Feld[x][y] = EL_BD_BUTTERFLY;
2148 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2151 case EL_BD_FIREFLY_RIGHT:
2152 case EL_BD_FIREFLY_UP:
2153 case EL_BD_FIREFLY_LEFT:
2154 case EL_BD_FIREFLY_DOWN:
2155 Feld[x][y] = EL_BD_FIREFLY;
2156 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2159 case EL_PACMAN_RIGHT:
2161 case EL_PACMAN_LEFT:
2162 case EL_PACMAN_DOWN:
2163 Feld[x][y] = EL_PACMAN;
2164 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2167 case EL_SP_SNIKSNAK:
2168 MovDir[x][y] = MV_UP;
2171 case EL_SP_ELECTRON:
2172 MovDir[x][y] = MV_LEFT;
2179 Feld[x][y] = EL_MOLE;
2180 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2184 if (IS_CUSTOM_ELEMENT(element))
2186 struct ElementInfo *ei = &element_info[element];
2187 int move_direction_initial = ei->move_direction_initial;
2188 int move_pattern = ei->move_pattern;
2190 if (move_direction_initial == MV_START_PREVIOUS)
2192 if (MovDir[x][y] != MV_NONE)
2195 move_direction_initial = MV_START_AUTOMATIC;
2198 if (move_direction_initial == MV_START_RANDOM)
2199 MovDir[x][y] = 1 << RND(4);
2200 else if (move_direction_initial & MV_ANY_DIRECTION)
2201 MovDir[x][y] = move_direction_initial;
2202 else if (move_pattern == MV_ALL_DIRECTIONS ||
2203 move_pattern == MV_TURNING_LEFT ||
2204 move_pattern == MV_TURNING_RIGHT ||
2205 move_pattern == MV_TURNING_LEFT_RIGHT ||
2206 move_pattern == MV_TURNING_RIGHT_LEFT ||
2207 move_pattern == MV_TURNING_RANDOM)
2208 MovDir[x][y] = 1 << RND(4);
2209 else if (move_pattern == MV_HORIZONTAL)
2210 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2211 else if (move_pattern == MV_VERTICAL)
2212 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2213 else if (move_pattern & MV_ANY_DIRECTION)
2214 MovDir[x][y] = element_info[element].move_pattern;
2215 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2216 move_pattern == MV_ALONG_RIGHT_SIDE)
2218 /* use random direction as default start direction */
2219 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2220 MovDir[x][y] = 1 << RND(4);
2222 for (i = 0; i < NUM_DIRECTIONS; i++)
2224 int x1 = x + xy[i][0];
2225 int y1 = y + xy[i][1];
2227 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2229 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2230 MovDir[x][y] = direction[0][i];
2232 MovDir[x][y] = direction[1][i];
2241 MovDir[x][y] = 1 << RND(4);
2243 if (element != EL_BUG &&
2244 element != EL_SPACESHIP &&
2245 element != EL_BD_BUTTERFLY &&
2246 element != EL_BD_FIREFLY)
2249 for (i = 0; i < NUM_DIRECTIONS; i++)
2251 int x1 = x + xy[i][0];
2252 int y1 = y + xy[i][1];
2254 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2256 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2258 MovDir[x][y] = direction[0][i];
2261 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2262 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2264 MovDir[x][y] = direction[1][i];
2273 GfxDir[x][y] = MovDir[x][y];
2276 void InitAmoebaNr(int x, int y)
2279 int group_nr = AmoebeNachbarNr(x, y);
2283 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2285 if (AmoebaCnt[i] == 0)
2293 AmoebaNr[x][y] = group_nr;
2294 AmoebaCnt[group_nr]++;
2295 AmoebaCnt2[group_nr]++;
2301 boolean raise_level = FALSE;
2303 if (local_player->MovPos)
2306 if (tape.auto_play) /* tape might already be stopped here */
2307 tape.auto_play_level_solved = TRUE;
2309 local_player->LevelSolved = FALSE;
2311 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2315 if (!tape.playing && setup.sound_loops)
2316 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2317 SND_CTRL_PLAY_LOOP);
2319 while (TimeLeft > 0)
2321 if (!tape.playing && !setup.sound_loops)
2322 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2323 if (TimeLeft > 0 && !(TimeLeft % 10))
2324 RaiseScore(level.score[SC_TIME_BONUS]);
2325 if (TimeLeft > 100 && !(TimeLeft % 10))
2330 DrawGameValue_Time(TimeLeft);
2338 if (!tape.playing && setup.sound_loops)
2339 StopSound(SND_GAME_LEVELTIME_BONUS);
2341 else if (level.time == 0) /* level without time limit */
2343 if (!tape.playing && setup.sound_loops)
2344 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2345 SND_CTRL_PLAY_LOOP);
2347 while (TimePlayed < 999)
2349 if (!tape.playing && !setup.sound_loops)
2350 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2351 if (TimePlayed < 999 && !(TimePlayed % 10))
2352 RaiseScore(level.score[SC_TIME_BONUS]);
2353 if (TimePlayed < 900 && !(TimePlayed % 10))
2358 DrawGameValue_Time(TimePlayed);
2366 if (!tape.playing && setup.sound_loops)
2367 StopSound(SND_GAME_LEVELTIME_BONUS);
2370 /* close exit door after last player */
2371 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2372 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2373 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2375 int element = Feld[ExitX][ExitY];
2377 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2378 EL_SP_EXIT_CLOSING);
2380 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2383 /* player disappears */
2384 if (ExitX >= 0 && ExitY >= 0)
2385 DrawLevelField(ExitX, ExitY);
2392 CloseDoor(DOOR_CLOSE_1);
2397 SaveTape(tape.level_nr); /* Ask to save tape */
2400 if (level_nr == leveldir_current->handicap_level)
2402 leveldir_current->handicap_level++;
2403 SaveLevelSetup_SeriesInfo();
2406 if (level_editor_test_game)
2407 local_player->score = -1; /* no highscore when playing from editor */
2408 else if (level_nr < leveldir_current->last_level)
2409 raise_level = TRUE; /* advance to next level */
2411 if ((hi_pos = NewHiScore()) >= 0)
2413 game_status = GAME_MODE_SCORES;
2414 DrawHallOfFame(hi_pos);
2423 game_status = GAME_MODE_MAIN;
2440 LoadScore(level_nr);
2442 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2443 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2446 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2448 if (local_player->score > highscore[k].Score)
2450 /* player has made it to the hall of fame */
2452 if (k < MAX_SCORE_ENTRIES - 1)
2454 int m = MAX_SCORE_ENTRIES - 1;
2457 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2458 if (!strcmp(setup.player_name, highscore[l].Name))
2460 if (m == k) /* player's new highscore overwrites his old one */
2464 for (l = m; l > k; l--)
2466 strcpy(highscore[l].Name, highscore[l - 1].Name);
2467 highscore[l].Score = highscore[l - 1].Score;
2474 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2475 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2476 highscore[k].Score = local_player->score;
2482 else if (!strncmp(setup.player_name, highscore[k].Name,
2483 MAX_PLAYER_NAME_LEN))
2484 break; /* player already there with a higher score */
2490 SaveScore(level_nr);
2495 inline static int getElementMoveStepsize(int x, int y)
2497 int element = Feld[x][y];
2498 int direction = MovDir[x][y];
2499 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2500 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2501 int horiz_move = (dx != 0);
2502 int sign = (horiz_move ? dx : dy);
2503 int step = sign * element_info[element].move_stepsize;
2505 /* special values for move stepsize for spring and things on conveyor belt */
2509 if (element == EL_SPRING)
2510 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2511 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2512 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2513 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2515 if (CAN_FALL(element) &&
2516 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2517 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2518 else if (element == EL_SPRING)
2519 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2526 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2528 if (player->GfxAction != action || player->GfxDir != dir)
2531 printf("Player frame reset! (%d => %d, %d => %d)\n",
2532 player->GfxAction, action, player->GfxDir, dir);
2535 player->GfxAction = action;
2536 player->GfxDir = dir;
2538 player->StepFrame = 0;
2542 static void ResetRandomAnimationValue(int x, int y)
2544 GfxRandom[x][y] = INIT_GFX_RANDOM();
2547 static void ResetGfxAnimation(int x, int y)
2550 GfxAction[x][y] = ACTION_DEFAULT;
2551 GfxDir[x][y] = MovDir[x][y];
2554 void InitMovingField(int x, int y, int direction)
2556 int element = Feld[x][y];
2557 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2558 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2562 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2563 ResetGfxAnimation(x, y);
2565 MovDir[x][y] = direction;
2566 GfxDir[x][y] = direction;
2567 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2568 ACTION_FALLING : ACTION_MOVING);
2570 /* this is needed for CEs with property "can move" / "not moving" */
2572 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2574 if (Feld[newx][newy] == EL_EMPTY)
2575 Feld[newx][newy] = EL_BLOCKED;
2577 MovDir[newx][newy] = MovDir[x][y];
2579 #if USE_NEW_CUSTOM_VALUE
2580 CustomValue[newx][newy] = CustomValue[x][y];
2583 GfxFrame[newx][newy] = GfxFrame[x][y];
2584 GfxRandom[newx][newy] = GfxRandom[x][y];
2585 GfxAction[newx][newy] = GfxAction[x][y];
2586 GfxDir[newx][newy] = GfxDir[x][y];
2590 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2592 int direction = MovDir[x][y];
2593 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2594 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2600 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2602 int oldx = x, oldy = y;
2603 int direction = MovDir[x][y];
2605 if (direction == MV_LEFT)
2607 else if (direction == MV_RIGHT)
2609 else if (direction == MV_UP)
2611 else if (direction == MV_DOWN)
2614 *comes_from_x = oldx;
2615 *comes_from_y = oldy;
2618 int MovingOrBlocked2Element(int x, int y)
2620 int element = Feld[x][y];
2622 if (element == EL_BLOCKED)
2626 Blocked2Moving(x, y, &oldx, &oldy);
2627 return Feld[oldx][oldy];
2633 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2635 /* like MovingOrBlocked2Element(), but if element is moving
2636 and (x,y) is the field the moving element is just leaving,
2637 return EL_BLOCKED instead of the element value */
2638 int element = Feld[x][y];
2640 if (IS_MOVING(x, y))
2642 if (element == EL_BLOCKED)
2646 Blocked2Moving(x, y, &oldx, &oldy);
2647 return Feld[oldx][oldy];
2656 static void RemoveField(int x, int y)
2658 Feld[x][y] = EL_EMPTY;
2664 #if USE_NEW_CUSTOM_VALUE
2665 CustomValue[x][y] = 0;
2669 ChangeDelay[x][y] = 0;
2670 ChangePage[x][y] = -1;
2671 Pushed[x][y] = FALSE;
2674 ExplodeField[x][y] = EX_TYPE_NONE;
2677 GfxElement[x][y] = EL_UNDEFINED;
2678 GfxAction[x][y] = ACTION_DEFAULT;
2679 GfxDir[x][y] = MV_NONE;
2682 void RemoveMovingField(int x, int y)
2684 int oldx = x, oldy = y, newx = x, newy = y;
2685 int element = Feld[x][y];
2686 int next_element = EL_UNDEFINED;
2688 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2691 if (IS_MOVING(x, y))
2693 Moving2Blocked(x, y, &newx, &newy);
2695 if (Feld[newx][newy] != EL_BLOCKED)
2697 /* element is moving, but target field is not free (blocked), but
2698 already occupied by something different (example: acid pool);
2699 in this case, only remove the moving field, but not the target */
2701 RemoveField(oldx, oldy);
2703 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2705 DrawLevelField(oldx, oldy);
2710 else if (element == EL_BLOCKED)
2712 Blocked2Moving(x, y, &oldx, &oldy);
2713 if (!IS_MOVING(oldx, oldy))
2717 if (element == EL_BLOCKED &&
2718 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2719 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2720 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2721 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2722 next_element = get_next_element(Feld[oldx][oldy]);
2724 RemoveField(oldx, oldy);
2725 RemoveField(newx, newy);
2727 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2729 if (next_element != EL_UNDEFINED)
2730 Feld[oldx][oldy] = next_element;
2732 DrawLevelField(oldx, oldy);
2733 DrawLevelField(newx, newy);
2736 void DrawDynamite(int x, int y)
2738 int sx = SCREENX(x), sy = SCREENY(y);
2739 int graphic = el2img(Feld[x][y]);
2742 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2745 if (IS_WALKABLE_INSIDE(Back[x][y]))
2749 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2750 else if (Store[x][y])
2751 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2753 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2755 if (Back[x][y] || Store[x][y])
2756 DrawGraphicThruMask(sx, sy, graphic, frame);
2758 DrawGraphic(sx, sy, graphic, frame);
2761 void CheckDynamite(int x, int y)
2763 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2767 if (MovDelay[x][y] != 0)
2770 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2776 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2781 void DrawRelocatePlayer(struct PlayerInfo *player)
2783 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2784 boolean no_delay = (tape.warp_forward);
2785 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2786 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2787 int jx = player->jx;
2788 int jy = player->jy;
2790 if (level.instant_relocation)
2792 int offset = (setup.scroll_delay ? 3 : 0);
2794 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2796 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2797 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2798 local_player->jx - MIDPOSX);
2800 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2801 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2802 local_player->jy - MIDPOSY);
2806 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2807 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2808 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2810 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2811 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2812 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2814 /* don't scroll over playfield boundaries */
2815 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2816 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2818 /* don't scroll over playfield boundaries */
2819 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2820 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2823 RedrawPlayfield(TRUE, 0,0,0,0);
2827 int scroll_xx = -999, scroll_yy = -999;
2829 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2831 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2834 int fx = FX, fy = FY;
2836 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2837 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2838 local_player->jx - MIDPOSX);
2840 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2841 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2842 local_player->jy - MIDPOSY);
2844 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2845 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2847 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2853 fx += dx * TILEX / 2;
2854 fy += dy * TILEY / 2;
2856 ScrollLevel(dx, dy);
2859 /* scroll in two steps of half tile size to make things smoother */
2860 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2862 Delay(wait_delay_value);
2864 /* scroll second step to align at full tile size */
2866 Delay(wait_delay_value);
2871 Delay(wait_delay_value);
2875 void RelocatePlayer(int jx, int jy, int el_player_raw)
2877 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
2878 int player_nr = GET_PLAYER_NR(el_player);
2879 struct PlayerInfo *player = &stored_player[player_nr];
2880 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2881 boolean no_delay = (tape.warp_forward);
2882 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2883 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2884 int old_jx = player->jx;
2885 int old_jy = player->jy;
2886 int old_element = Feld[old_jx][old_jy];
2887 int element = Feld[jx][jy];
2888 boolean player_relocated = (old_jx != jx || old_jy != jy);
2890 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2891 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2892 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2893 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2894 int leave_side_horiz = move_dir_horiz;
2895 int leave_side_vert = move_dir_vert;
2896 int enter_side = enter_side_horiz | enter_side_vert;
2897 int leave_side = leave_side_horiz | leave_side_vert;
2899 if (player->GameOver) /* do not reanimate dead player */
2902 if (!player_relocated) /* no need to relocate the player */
2905 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2907 RemoveField(jx, jy); /* temporarily remove newly placed player */
2908 DrawLevelField(jx, jy);
2911 if (player->present)
2913 while (player->MovPos)
2915 ScrollPlayer(player, SCROLL_GO_ON);
2916 ScrollScreen(NULL, SCROLL_GO_ON);
2918 AdvanceFrameAndPlayerCounters(player->index_nr);
2923 Delay(wait_delay_value);
2926 DrawPlayer(player); /* needed here only to cleanup last field */
2927 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2929 player->is_moving = FALSE;
2932 if (IS_CUSTOM_ELEMENT(old_element))
2933 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
2935 player->index_bit, leave_side);
2937 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
2939 player->index_bit, leave_side);
2941 Feld[jx][jy] = el_player;
2942 InitPlayerField(jx, jy, el_player, TRUE);
2944 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
2946 Feld[jx][jy] = element;
2947 InitField(jx, jy, FALSE);
2950 if (player == local_player) /* only visually relocate local player */
2951 DrawRelocatePlayer(player);
2953 TestIfPlayerTouchesBadThing(jx, jy);
2954 TestIfPlayerTouchesCustomElement(jx, jy);
2956 if (IS_CUSTOM_ELEMENT(element))
2957 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
2958 player->index_bit, enter_side);
2960 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
2961 player->index_bit, enter_side);
2964 void Explode(int ex, int ey, int phase, int mode)
2970 /* !!! eliminate this variable !!! */
2971 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2973 if (game.explosions_delayed)
2975 ExplodeField[ex][ey] = mode;
2979 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2981 int center_element = Feld[ex][ey];
2984 /* --- This is only really needed (and now handled) in "Impact()". --- */
2985 /* do not explode moving elements that left the explode field in time */
2986 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2987 center_element == EL_EMPTY &&
2988 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
2992 if (mode == EX_TYPE_NORMAL ||
2993 mode == EX_TYPE_CENTER ||
2994 mode == EX_TYPE_CROSS)
2995 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2997 /* remove things displayed in background while burning dynamite */
2998 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3001 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3003 /* put moving element to center field (and let it explode there) */
3004 center_element = MovingOrBlocked2Element(ex, ey);
3005 RemoveMovingField(ex, ey);
3006 Feld[ex][ey] = center_element;
3009 last_phase = element_info[center_element].explosion_delay + 1;
3011 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3013 int xx = x - ex + 1;
3014 int yy = y - ey + 1;
3017 if (!IN_LEV_FIELD(x, y) ||
3018 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3019 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3022 element = Feld[x][y];
3024 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3026 element = MovingOrBlocked2Element(x, y);
3028 if (!IS_EXPLOSION_PROOF(element))
3029 RemoveMovingField(x, y);
3032 /* indestructible elements can only explode in center (but not flames) */
3033 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3034 mode == EX_TYPE_BORDER)) ||
3035 element == EL_FLAMES)
3038 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3039 behaviour, for example when touching a yamyam that explodes to rocks
3040 with active deadly shield, a rock is created under the player !!! */
3041 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3043 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3044 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3045 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3047 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3050 if (IS_ACTIVE_BOMB(element))
3052 /* re-activate things under the bomb like gate or penguin */
3053 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3060 /* save walkable background elements while explosion on same tile */
3061 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3062 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3063 Back[x][y] = element;
3065 /* ignite explodable elements reached by other explosion */
3066 if (element == EL_EXPLOSION)
3067 element = Store2[x][y];
3069 if (AmoebaNr[x][y] &&
3070 (element == EL_AMOEBA_FULL ||
3071 element == EL_BD_AMOEBA ||
3072 element == EL_AMOEBA_GROWING))
3074 AmoebaCnt[AmoebaNr[x][y]]--;
3075 AmoebaCnt2[AmoebaNr[x][y]]--;
3080 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3082 switch(StorePlayer[ex][ey])
3085 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3088 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3091 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3095 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3099 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3100 Store[x][y] = EL_EMPTY;
3102 else if (center_element == EL_MOLE)
3103 Store[x][y] = EL_EMERALD_RED;
3104 else if (center_element == EL_PENGUIN)
3105 Store[x][y] = EL_EMERALD_PURPLE;
3106 else if (center_element == EL_BUG)
3107 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3108 else if (center_element == EL_BD_BUTTERFLY)
3109 Store[x][y] = EL_BD_DIAMOND;
3110 else if (center_element == EL_SP_ELECTRON)
3111 Store[x][y] = EL_SP_INFOTRON;
3112 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3113 Store[x][y] = level.amoeba_content;
3114 else if (center_element == EL_YAMYAM)
3115 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3116 else if (IS_CUSTOM_ELEMENT(center_element) &&
3117 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3118 Store[x][y] = element_info[center_element].content.e[xx][yy];
3119 else if (element == EL_WALL_EMERALD)
3120 Store[x][y] = EL_EMERALD;
3121 else if (element == EL_WALL_DIAMOND)
3122 Store[x][y] = EL_DIAMOND;
3123 else if (element == EL_WALL_BD_DIAMOND)
3124 Store[x][y] = EL_BD_DIAMOND;
3125 else if (element == EL_WALL_EMERALD_YELLOW)
3126 Store[x][y] = EL_EMERALD_YELLOW;
3127 else if (element == EL_WALL_EMERALD_RED)
3128 Store[x][y] = EL_EMERALD_RED;
3129 else if (element == EL_WALL_EMERALD_PURPLE)
3130 Store[x][y] = EL_EMERALD_PURPLE;
3131 else if (element == EL_WALL_PEARL)
3132 Store[x][y] = EL_PEARL;
3133 else if (element == EL_WALL_CRYSTAL)
3134 Store[x][y] = EL_CRYSTAL;
3135 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3136 Store[x][y] = element_info[element].content.e[1][1];
3138 Store[x][y] = EL_EMPTY;
3140 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3141 center_element == EL_AMOEBA_TO_DIAMOND)
3142 Store2[x][y] = element;
3144 Feld[x][y] = EL_EXPLOSION;
3145 GfxElement[x][y] = center_element;
3147 ExplodePhase[x][y] = 1;
3148 ExplodeDelay[x][y] = last_phase;
3153 if (center_element == EL_YAMYAM)
3154 game.yamyam_content_nr =
3155 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3167 GfxFrame[x][y] = 0; /* restart explosion animation */
3169 last_phase = ExplodeDelay[x][y];
3171 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3175 /* activate this even in non-DEBUG version until cause for crash in
3176 getGraphicAnimationFrame() (see below) is found and eliminated */
3181 if (GfxElement[x][y] == EL_UNDEFINED)
3184 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3185 printf("Explode(): This should never happen!\n");
3188 GfxElement[x][y] = EL_EMPTY;
3192 border_element = Store2[x][y];
3193 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3194 border_element = StorePlayer[x][y];
3196 if (phase == element_info[border_element].ignition_delay ||
3197 phase == last_phase)
3199 boolean border_explosion = FALSE;
3201 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3202 !PLAYER_EXPLOSION_PROTECTED(x, y))
3204 KillPlayerUnlessExplosionProtected(x, y);
3205 border_explosion = TRUE;
3207 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3209 Feld[x][y] = Store2[x][y];
3212 border_explosion = TRUE;
3214 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3216 AmoebeUmwandeln(x, y);
3218 border_explosion = TRUE;
3221 /* if an element just explodes due to another explosion (chain-reaction),
3222 do not immediately end the new explosion when it was the last frame of
3223 the explosion (as it would be done in the following "if"-statement!) */
3224 if (border_explosion && phase == last_phase)
3228 if (phase == last_phase)
3232 element = Feld[x][y] = Store[x][y];
3233 Store[x][y] = Store2[x][y] = 0;
3234 GfxElement[x][y] = EL_UNDEFINED;
3236 /* player can escape from explosions and might therefore be still alive */
3237 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3238 element <= EL_PLAYER_IS_EXPLODING_4)
3239 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3241 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3242 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3243 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3246 /* restore probably existing indestructible background element */
3247 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3248 element = Feld[x][y] = Back[x][y];
3251 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3252 GfxDir[x][y] = MV_NONE;
3253 ChangeDelay[x][y] = 0;
3254 ChangePage[x][y] = -1;
3256 #if USE_NEW_CUSTOM_VALUE
3257 CustomValue[x][y] = 0;
3260 InitField_WithBug2(x, y, FALSE);
3262 DrawLevelField(x, y);
3264 TestIfElementTouchesCustomElement(x, y);
3266 if (GFX_CRUMBLED(element))
3267 DrawLevelFieldCrumbledSandNeighbours(x, y);
3269 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3270 StorePlayer[x][y] = 0;
3272 if (ELEM_IS_PLAYER(element))
3273 RelocatePlayer(x, y, element);
3275 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3277 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3278 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3281 DrawLevelFieldCrumbledSand(x, y);
3283 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3285 DrawLevelElement(x, y, Back[x][y]);
3286 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3288 else if (IS_WALKABLE_UNDER(Back[x][y]))
3290 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3291 DrawLevelElementThruMask(x, y, Back[x][y]);
3293 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3294 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3298 void DynaExplode(int ex, int ey)
3301 int dynabomb_element = Feld[ex][ey];
3302 int dynabomb_size = 1;
3303 boolean dynabomb_xl = FALSE;
3304 struct PlayerInfo *player;
3305 static int xy[4][2] =
3313 if (IS_ACTIVE_BOMB(dynabomb_element))
3315 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3316 dynabomb_size = player->dynabomb_size;
3317 dynabomb_xl = player->dynabomb_xl;
3318 player->dynabombs_left++;
3321 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3323 for (i = 0; i < NUM_DIRECTIONS; i++)
3325 for (j = 1; j <= dynabomb_size; j++)
3327 int x = ex + j * xy[i][0];
3328 int y = ey + j * xy[i][1];
3331 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3334 element = Feld[x][y];
3336 /* do not restart explosions of fields with active bombs */
3337 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3340 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3342 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3343 !IS_DIGGABLE(element) && !dynabomb_xl)
3349 void Bang(int x, int y)
3351 int element = MovingOrBlocked2Element(x, y);
3353 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3355 struct PlayerInfo *player = PLAYERINFO(x, y);
3357 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3358 player->element_nr);
3365 case EL_BD_BUTTERFLY:
3368 case EL_DARK_YAMYAM:
3372 RaiseScoreElement(element);
3373 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3375 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3376 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3377 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3378 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3379 case EL_DYNABOMB_INCREASE_NUMBER:
3380 case EL_DYNABOMB_INCREASE_SIZE:
3381 case EL_DYNABOMB_INCREASE_POWER:
3386 case EL_LAMP_ACTIVE:
3387 case EL_AMOEBA_TO_DIAMOND:
3388 if (IS_PLAYER(x, y))
3389 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3391 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3394 if (element_info[element].explosion_type == EXPLODES_CROSS)
3395 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3396 else if (element_info[element].explosion_type == EXPLODES_1X1)
3397 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3399 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3403 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3406 void SplashAcid(int x, int y)
3408 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3409 (!IN_LEV_FIELD(x - 1, y - 2) ||
3410 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3411 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3413 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3414 (!IN_LEV_FIELD(x + 1, y - 2) ||
3415 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3416 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3418 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3421 static void InitBeltMovement()
3423 static int belt_base_element[4] =
3425 EL_CONVEYOR_BELT_1_LEFT,
3426 EL_CONVEYOR_BELT_2_LEFT,
3427 EL_CONVEYOR_BELT_3_LEFT,
3428 EL_CONVEYOR_BELT_4_LEFT
3430 static int belt_base_active_element[4] =
3432 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3433 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3434 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3435 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3440 /* set frame order for belt animation graphic according to belt direction */
3441 for (i = 0; i < NUM_BELTS; i++)
3445 for (j = 0; j < NUM_BELT_PARTS; j++)
3447 int element = belt_base_active_element[belt_nr] + j;
3448 int graphic = el2img(element);
3450 if (game.belt_dir[i] == MV_LEFT)
3451 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3453 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3457 for (y = 0; y < lev_fieldy; y++)
3459 for (x = 0; x < lev_fieldx; x++)
3461 int element = Feld[x][y];
3463 for (i = 0; i < NUM_BELTS; i++)
3465 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3467 int e_belt_nr = getBeltNrFromBeltElement(element);
3470 if (e_belt_nr == belt_nr)
3472 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3474 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3482 static void ToggleBeltSwitch(int x, int y)
3484 static int belt_base_element[4] =
3486 EL_CONVEYOR_BELT_1_LEFT,
3487 EL_CONVEYOR_BELT_2_LEFT,
3488 EL_CONVEYOR_BELT_3_LEFT,
3489 EL_CONVEYOR_BELT_4_LEFT
3491 static int belt_base_active_element[4] =
3493 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3494 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3495 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3496 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3498 static int belt_base_switch_element[4] =
3500 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3501 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3502 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3503 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3505 static int belt_move_dir[4] =
3513 int element = Feld[x][y];
3514 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3515 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3516 int belt_dir = belt_move_dir[belt_dir_nr];
3519 if (!IS_BELT_SWITCH(element))
3522 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3523 game.belt_dir[belt_nr] = belt_dir;
3525 if (belt_dir_nr == 3)
3528 /* set frame order for belt animation graphic according to belt direction */
3529 for (i = 0; i < NUM_BELT_PARTS; i++)
3531 int element = belt_base_active_element[belt_nr] + i;
3532 int graphic = el2img(element);
3534 if (belt_dir == MV_LEFT)
3535 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3537 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3540 for (yy = 0; yy < lev_fieldy; yy++)
3542 for (xx = 0; xx < lev_fieldx; xx++)
3544 int element = Feld[xx][yy];
3546 if (IS_BELT_SWITCH(element))
3548 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3550 if (e_belt_nr == belt_nr)
3552 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3553 DrawLevelField(xx, yy);
3556 else if (IS_BELT(element) && belt_dir != MV_NONE)
3558 int e_belt_nr = getBeltNrFromBeltElement(element);
3560 if (e_belt_nr == belt_nr)
3562 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3564 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3565 DrawLevelField(xx, yy);
3568 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3570 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3572 if (e_belt_nr == belt_nr)
3574 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3576 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3577 DrawLevelField(xx, yy);
3584 static void ToggleSwitchgateSwitch(int x, int y)
3588 game.switchgate_pos = !game.switchgate_pos;
3590 for (yy = 0; yy < lev_fieldy; yy++)
3592 for (xx = 0; xx < lev_fieldx; xx++)
3594 int element = Feld[xx][yy];
3596 if (element == EL_SWITCHGATE_SWITCH_UP ||
3597 element == EL_SWITCHGATE_SWITCH_DOWN)
3599 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3600 DrawLevelField(xx, yy);
3602 else if (element == EL_SWITCHGATE_OPEN ||
3603 element == EL_SWITCHGATE_OPENING)
3605 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3607 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3609 else if (element == EL_SWITCHGATE_CLOSED ||
3610 element == EL_SWITCHGATE_CLOSING)
3612 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3614 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3620 static int getInvisibleActiveFromInvisibleElement(int element)
3622 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3623 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3624 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3628 static int getInvisibleFromInvisibleActiveElement(int element)
3630 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3631 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3632 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3636 static void RedrawAllLightSwitchesAndInvisibleElements()
3640 for (y = 0; y < lev_fieldy; y++)
3642 for (x = 0; x < lev_fieldx; x++)
3644 int element = Feld[x][y];
3646 if (element == EL_LIGHT_SWITCH &&
3647 game.light_time_left > 0)
3649 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3650 DrawLevelField(x, y);
3652 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3653 game.light_time_left == 0)
3655 Feld[x][y] = EL_LIGHT_SWITCH;
3656 DrawLevelField(x, y);
3658 else if (element == EL_INVISIBLE_STEELWALL ||
3659 element == EL_INVISIBLE_WALL ||
3660 element == EL_INVISIBLE_SAND)
3662 if (game.light_time_left > 0)
3663 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3665 DrawLevelField(x, y);
3667 /* uncrumble neighbour fields, if needed */
3668 if (element == EL_INVISIBLE_SAND)
3669 DrawLevelFieldCrumbledSandNeighbours(x, y);
3671 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3672 element == EL_INVISIBLE_WALL_ACTIVE ||
3673 element == EL_INVISIBLE_SAND_ACTIVE)
3675 if (game.light_time_left == 0)
3676 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3678 DrawLevelField(x, y);
3680 /* re-crumble neighbour fields, if needed */
3681 if (element == EL_INVISIBLE_SAND)
3682 DrawLevelFieldCrumbledSandNeighbours(x, y);
3688 static void ToggleLightSwitch(int x, int y)
3690 int element = Feld[x][y];
3692 game.light_time_left =
3693 (element == EL_LIGHT_SWITCH ?
3694 level.time_light * FRAMES_PER_SECOND : 0);
3696 RedrawAllLightSwitchesAndInvisibleElements();
3699 static void ActivateTimegateSwitch(int x, int y)
3703 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3705 for (yy = 0; yy < lev_fieldy; yy++)
3707 for (xx = 0; xx < lev_fieldx; xx++)
3709 int element = Feld[xx][yy];
3711 if (element == EL_TIMEGATE_CLOSED ||
3712 element == EL_TIMEGATE_CLOSING)
3714 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3715 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3719 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3721 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3722 DrawLevelField(xx, yy);
3729 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3732 void Impact(int x, int y)
3734 boolean last_line = (y == lev_fieldy - 1);
3735 boolean object_hit = FALSE;
3736 boolean impact = (last_line || object_hit);
3737 int element = Feld[x][y];
3738 int smashed = EL_STEELWALL;
3740 if (!last_line) /* check if element below was hit */
3742 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3745 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3746 MovDir[x][y + 1] != MV_DOWN ||
3747 MovPos[x][y + 1] <= TILEY / 2));
3749 /* do not smash moving elements that left the smashed field in time */
3750 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3751 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3755 smashed = MovingOrBlocked2Element(x, y + 1);
3757 impact = (last_line || object_hit);
3760 if (!last_line && smashed == EL_ACID) /* element falls into acid */
3762 SplashAcid(x, y + 1);
3766 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
3767 /* only reset graphic animation if graphic really changes after impact */
3769 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3771 ResetGfxAnimation(x, y);
3772 DrawLevelField(x, y);
3775 if (impact && CAN_EXPLODE_IMPACT(element))
3780 else if (impact && element == EL_PEARL)
3782 ResetGfxAnimation(x, y);
3784 Feld[x][y] = EL_PEARL_BREAKING;
3785 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3788 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
3790 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3795 if (impact && element == EL_AMOEBA_DROP)
3797 if (object_hit && IS_PLAYER(x, y + 1))
3798 KillPlayerUnlessEnemyProtected(x, y + 1);
3799 else if (object_hit && smashed == EL_PENGUIN)
3803 Feld[x][y] = EL_AMOEBA_GROWING;
3804 Store[x][y] = EL_AMOEBA_WET;
3806 ResetRandomAnimationValue(x, y);
3811 if (object_hit) /* check which object was hit */
3813 if (CAN_PASS_MAGIC_WALL(element) &&
3814 (smashed == EL_MAGIC_WALL ||
3815 smashed == EL_BD_MAGIC_WALL))
3818 int activated_magic_wall =
3819 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3820 EL_BD_MAGIC_WALL_ACTIVE);
3822 /* activate magic wall / mill */
3823 for (yy = 0; yy < lev_fieldy; yy++)
3824 for (xx = 0; xx < lev_fieldx; xx++)
3825 if (Feld[xx][yy] == smashed)
3826 Feld[xx][yy] = activated_magic_wall;
3828 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3829 game.magic_wall_active = TRUE;
3831 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3832 SND_MAGIC_WALL_ACTIVATING :
3833 SND_BD_MAGIC_WALL_ACTIVATING));
3836 if (IS_PLAYER(x, y + 1))
3838 if (CAN_SMASH_PLAYER(element))
3840 KillPlayerUnlessEnemyProtected(x, y + 1);
3844 else if (smashed == EL_PENGUIN)
3846 if (CAN_SMASH_PLAYER(element))
3852 else if (element == EL_BD_DIAMOND)
3854 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3860 else if (((element == EL_SP_INFOTRON ||
3861 element == EL_SP_ZONK) &&
3862 (smashed == EL_SP_SNIKSNAK ||
3863 smashed == EL_SP_ELECTRON ||
3864 smashed == EL_SP_DISK_ORANGE)) ||
3865 (element == EL_SP_INFOTRON &&
3866 smashed == EL_SP_DISK_YELLOW))
3871 else if (CAN_SMASH_EVERYTHING(element))
3873 if (IS_CLASSIC_ENEMY(smashed) ||
3874 CAN_EXPLODE_SMASHED(smashed))
3879 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3881 if (smashed == EL_LAMP ||
3882 smashed == EL_LAMP_ACTIVE)
3887 else if (smashed == EL_NUT)
3889 Feld[x][y + 1] = EL_NUT_BREAKING;
3890 PlayLevelSound(x, y, SND_NUT_BREAKING);
3891 RaiseScoreElement(EL_NUT);
3894 else if (smashed == EL_PEARL)
3896 ResetGfxAnimation(x, y);
3898 Feld[x][y + 1] = EL_PEARL_BREAKING;
3899 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3902 else if (smashed == EL_DIAMOND)
3904 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3905 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3908 else if (IS_BELT_SWITCH(smashed))
3910 ToggleBeltSwitch(x, y + 1);
3912 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3913 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3915 ToggleSwitchgateSwitch(x, y + 1);
3917 else if (smashed == EL_LIGHT_SWITCH ||
3918 smashed == EL_LIGHT_SWITCH_ACTIVE)
3920 ToggleLightSwitch(x, y + 1);
3925 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
3928 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
3930 CheckElementChangeBySide(x, y + 1, smashed, element,
3931 CE_SWITCHED, CH_SIDE_TOP);
3932 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
3938 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
3943 /* play sound of magic wall / mill */
3945 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3946 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3948 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3949 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3950 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3951 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3956 /* play sound of object that hits the ground */
3957 if (last_line || object_hit)
3958 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3961 inline static void TurnRoundExt(int x, int y)
3973 { 0, 0 }, { 0, 0 }, { 0, 0 },
3978 int left, right, back;
3982 { MV_DOWN, MV_UP, MV_RIGHT },
3983 { MV_UP, MV_DOWN, MV_LEFT },
3985 { MV_LEFT, MV_RIGHT, MV_DOWN },
3989 { MV_RIGHT, MV_LEFT, MV_UP }
3992 int element = Feld[x][y];
3993 int move_pattern = element_info[element].move_pattern;
3995 int old_move_dir = MovDir[x][y];
3996 int left_dir = turn[old_move_dir].left;
3997 int right_dir = turn[old_move_dir].right;
3998 int back_dir = turn[old_move_dir].back;
4000 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4001 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4002 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4003 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4005 int left_x = x + left_dx, left_y = y + left_dy;
4006 int right_x = x + right_dx, right_y = y + right_dy;
4007 int move_x = x + move_dx, move_y = y + move_dy;
4011 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4013 TestIfBadThingTouchesOtherBadThing(x, y);
4015 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4016 MovDir[x][y] = right_dir;
4017 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4018 MovDir[x][y] = left_dir;
4020 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4022 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4025 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4027 TestIfBadThingTouchesOtherBadThing(x, y);
4029 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4030 MovDir[x][y] = left_dir;
4031 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4032 MovDir[x][y] = right_dir;
4034 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4036 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4039 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4041 TestIfBadThingTouchesOtherBadThing(x, y);
4043 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4044 MovDir[x][y] = left_dir;
4045 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4046 MovDir[x][y] = right_dir;
4048 if (MovDir[x][y] != old_move_dir)
4051 else if (element == EL_YAMYAM)
4053 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4054 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4056 if (can_turn_left && can_turn_right)
4057 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4058 else if (can_turn_left)
4059 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4060 else if (can_turn_right)
4061 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4063 MovDir[x][y] = back_dir;
4065 MovDelay[x][y] = 16 + 16 * RND(3);
4067 else if (element == EL_DARK_YAMYAM)
4069 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4071 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4074 if (can_turn_left && can_turn_right)
4075 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4076 else if (can_turn_left)
4077 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4078 else if (can_turn_right)
4079 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4081 MovDir[x][y] = back_dir;
4083 MovDelay[x][y] = 16 + 16 * RND(3);
4085 else if (element == EL_PACMAN)
4087 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4088 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4090 if (can_turn_left && can_turn_right)
4091 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4092 else if (can_turn_left)
4093 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4094 else if (can_turn_right)
4095 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4097 MovDir[x][y] = back_dir;
4099 MovDelay[x][y] = 6 + RND(40);
4101 else if (element == EL_PIG)
4103 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4104 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4105 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4106 boolean should_turn_left, should_turn_right, should_move_on;
4108 int rnd = RND(rnd_value);
4110 should_turn_left = (can_turn_left &&
4112 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4113 y + back_dy + left_dy)));
4114 should_turn_right = (can_turn_right &&
4116 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4117 y + back_dy + right_dy)));
4118 should_move_on = (can_move_on &&
4121 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4122 y + move_dy + left_dy) ||
4123 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4124 y + move_dy + right_dy)));
4126 if (should_turn_left || should_turn_right || should_move_on)
4128 if (should_turn_left && should_turn_right && should_move_on)
4129 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4130 rnd < 2 * rnd_value / 3 ? right_dir :
4132 else if (should_turn_left && should_turn_right)
4133 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4134 else if (should_turn_left && should_move_on)
4135 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4136 else if (should_turn_right && should_move_on)
4137 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4138 else if (should_turn_left)
4139 MovDir[x][y] = left_dir;
4140 else if (should_turn_right)
4141 MovDir[x][y] = right_dir;
4142 else if (should_move_on)
4143 MovDir[x][y] = old_move_dir;
4145 else if (can_move_on && rnd > rnd_value / 8)
4146 MovDir[x][y] = old_move_dir;
4147 else if (can_turn_left && can_turn_right)
4148 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4149 else if (can_turn_left && rnd > rnd_value / 8)
4150 MovDir[x][y] = left_dir;
4151 else if (can_turn_right && rnd > rnd_value/8)
4152 MovDir[x][y] = right_dir;
4154 MovDir[x][y] = back_dir;
4156 xx = x + move_xy[MovDir[x][y]].x;
4157 yy = y + move_xy[MovDir[x][y]].y;
4159 if (!IN_LEV_FIELD(xx, yy) ||
4160 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4161 MovDir[x][y] = old_move_dir;
4165 else if (element == EL_DRAGON)
4167 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4168 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4169 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4171 int rnd = RND(rnd_value);
4173 if (can_move_on && rnd > rnd_value / 8)
4174 MovDir[x][y] = old_move_dir;
4175 else if (can_turn_left && can_turn_right)
4176 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4177 else if (can_turn_left && rnd > rnd_value / 8)
4178 MovDir[x][y] = left_dir;
4179 else if (can_turn_right && rnd > rnd_value / 8)
4180 MovDir[x][y] = right_dir;
4182 MovDir[x][y] = back_dir;
4184 xx = x + move_xy[MovDir[x][y]].x;
4185 yy = y + move_xy[MovDir[x][y]].y;
4187 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4188 MovDir[x][y] = old_move_dir;
4192 else if (element == EL_MOLE)
4194 boolean can_move_on =
4195 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4196 IS_AMOEBOID(Feld[move_x][move_y]) ||
4197 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4200 boolean can_turn_left =
4201 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4202 IS_AMOEBOID(Feld[left_x][left_y])));
4204 boolean can_turn_right =
4205 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4206 IS_AMOEBOID(Feld[right_x][right_y])));
4208 if (can_turn_left && can_turn_right)
4209 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4210 else if (can_turn_left)
4211 MovDir[x][y] = left_dir;
4213 MovDir[x][y] = right_dir;
4216 if (MovDir[x][y] != old_move_dir)
4219 else if (element == EL_BALLOON)
4221 MovDir[x][y] = game.wind_direction;
4224 else if (element == EL_SPRING)
4226 if (MovDir[x][y] & MV_HORIZONTAL &&
4227 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4228 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4229 MovDir[x][y] = MV_NONE;
4233 else if (element == EL_ROBOT ||
4234 element == EL_SATELLITE ||
4235 element == EL_PENGUIN)
4237 int attr_x = -1, attr_y = -1;
4248 for (i = 0; i < MAX_PLAYERS; i++)
4250 struct PlayerInfo *player = &stored_player[i];
4251 int jx = player->jx, jy = player->jy;
4253 if (!player->active)
4257 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4265 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4266 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4267 game.engine_version < VERSION_IDENT(3,1,0,0)))
4273 if (element == EL_PENGUIN)
4276 static int xy[4][2] =
4284 for (i = 0; i < NUM_DIRECTIONS; i++)
4286 int ex = x + xy[i][0];
4287 int ey = y + xy[i][1];
4289 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4298 MovDir[x][y] = MV_NONE;
4300 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4301 else if (attr_x > x)
4302 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4304 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4305 else if (attr_y > y)
4306 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4308 if (element == EL_ROBOT)
4312 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4313 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4314 Moving2Blocked(x, y, &newx, &newy);
4316 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4317 MovDelay[x][y] = 8 + 8 * !RND(3);
4319 MovDelay[x][y] = 16;
4321 else if (element == EL_PENGUIN)
4327 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4329 boolean first_horiz = RND(2);
4330 int new_move_dir = MovDir[x][y];
4333 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4334 Moving2Blocked(x, y, &newx, &newy);
4336 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4340 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4341 Moving2Blocked(x, y, &newx, &newy);
4343 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4346 MovDir[x][y] = old_move_dir;
4350 else /* (element == EL_SATELLITE) */
4356 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4358 boolean first_horiz = RND(2);
4359 int new_move_dir = MovDir[x][y];
4362 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4363 Moving2Blocked(x, y, &newx, &newy);
4365 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4369 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4370 Moving2Blocked(x, y, &newx, &newy);
4372 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4375 MovDir[x][y] = old_move_dir;
4380 else if (move_pattern == MV_TURNING_LEFT ||
4381 move_pattern == MV_TURNING_RIGHT ||
4382 move_pattern == MV_TURNING_LEFT_RIGHT ||
4383 move_pattern == MV_TURNING_RIGHT_LEFT ||
4384 move_pattern == MV_TURNING_RANDOM ||
4385 move_pattern == MV_ALL_DIRECTIONS)
4387 boolean can_turn_left =
4388 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4389 boolean can_turn_right =
4390 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4392 if (element_info[element].move_stepsize == 0) /* "not moving" */
4395 if (move_pattern == MV_TURNING_LEFT)
4396 MovDir[x][y] = left_dir;
4397 else if (move_pattern == MV_TURNING_RIGHT)
4398 MovDir[x][y] = right_dir;
4399 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4400 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4401 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4402 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4403 else if (move_pattern == MV_TURNING_RANDOM)
4404 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4405 can_turn_right && !can_turn_left ? right_dir :
4406 RND(2) ? left_dir : right_dir);
4407 else if (can_turn_left && can_turn_right)
4408 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4409 else if (can_turn_left)
4410 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4411 else if (can_turn_right)
4412 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4414 MovDir[x][y] = back_dir;
4416 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4418 else if (move_pattern == MV_HORIZONTAL ||
4419 move_pattern == MV_VERTICAL)
4421 if (move_pattern & old_move_dir)
4422 MovDir[x][y] = back_dir;
4423 else if (move_pattern == MV_HORIZONTAL)
4424 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4425 else if (move_pattern == MV_VERTICAL)
4426 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4428 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4430 else if (move_pattern & MV_ANY_DIRECTION)
4432 MovDir[x][y] = move_pattern;
4433 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4435 else if (move_pattern & MV_WIND_DIRECTION)
4437 MovDir[x][y] = game.wind_direction;
4438 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4440 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4442 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4443 MovDir[x][y] = left_dir;
4444 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4445 MovDir[x][y] = right_dir;
4447 if (MovDir[x][y] != old_move_dir)
4448 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4450 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4452 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4453 MovDir[x][y] = right_dir;
4454 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4455 MovDir[x][y] = left_dir;
4457 if (MovDir[x][y] != old_move_dir)
4458 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4460 else if (move_pattern == MV_TOWARDS_PLAYER ||
4461 move_pattern == MV_AWAY_FROM_PLAYER)
4463 int attr_x = -1, attr_y = -1;
4465 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4476 for (i = 0; i < MAX_PLAYERS; i++)
4478 struct PlayerInfo *player = &stored_player[i];
4479 int jx = player->jx, jy = player->jy;
4481 if (!player->active)
4485 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4493 MovDir[x][y] = MV_NONE;
4495 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4496 else if (attr_x > x)
4497 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4499 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4500 else if (attr_y > y)
4501 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4503 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4505 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4507 boolean first_horiz = RND(2);
4508 int new_move_dir = MovDir[x][y];
4510 if (element_info[element].move_stepsize == 0) /* "not moving" */
4512 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
4513 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4519 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4520 Moving2Blocked(x, y, &newx, &newy);
4522 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4526 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4527 Moving2Blocked(x, y, &newx, &newy);
4529 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4532 MovDir[x][y] = old_move_dir;
4535 else if (move_pattern == MV_WHEN_PUSHED ||
4536 move_pattern == MV_WHEN_DROPPED)
4538 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4539 MovDir[x][y] = MV_NONE;
4543 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4545 static int test_xy[7][2] =
4555 static int test_dir[7] =
4565 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4566 int move_preference = -1000000; /* start with very low preference */
4567 int new_move_dir = MV_NONE;
4568 int start_test = RND(4);
4571 for (i = 0; i < NUM_DIRECTIONS; i++)
4573 int move_dir = test_dir[start_test + i];
4574 int move_dir_preference;
4576 xx = x + test_xy[start_test + i][0];
4577 yy = y + test_xy[start_test + i][1];
4579 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4580 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4582 new_move_dir = move_dir;
4587 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4590 move_dir_preference = -1 * RunnerVisit[xx][yy];
4591 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4592 move_dir_preference = PlayerVisit[xx][yy];
4594 if (move_dir_preference > move_preference)
4596 /* prefer field that has not been visited for the longest time */
4597 move_preference = move_dir_preference;
4598 new_move_dir = move_dir;
4600 else if (move_dir_preference == move_preference &&
4601 move_dir == old_move_dir)
4603 /* prefer last direction when all directions are preferred equally */
4604 move_preference = move_dir_preference;
4605 new_move_dir = move_dir;
4609 MovDir[x][y] = new_move_dir;
4610 if (old_move_dir != new_move_dir)
4611 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4615 static void TurnRound(int x, int y)
4617 int direction = MovDir[x][y];
4621 GfxDir[x][y] = MovDir[x][y];
4623 if (direction != MovDir[x][y])
4627 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4630 static boolean JustBeingPushed(int x, int y)
4634 for (i = 0; i < MAX_PLAYERS; i++)
4636 struct PlayerInfo *player = &stored_player[i];
4638 if (player->active && player->is_pushing && player->MovPos)
4640 int next_jx = player->jx + (player->jx - player->last_jx);
4641 int next_jy = player->jy + (player->jy - player->last_jy);
4643 if (x == next_jx && y == next_jy)
4651 void StartMoving(int x, int y)
4653 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4654 int element = Feld[x][y];
4659 if (MovDelay[x][y] == 0)
4660 GfxAction[x][y] = ACTION_DEFAULT;
4662 if (CAN_FALL(element) && y < lev_fieldy - 1)
4664 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4665 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4666 if (JustBeingPushed(x, y))
4669 if (element == EL_QUICKSAND_FULL)
4671 if (IS_FREE(x, y + 1))
4673 InitMovingField(x, y, MV_DOWN);
4674 started_moving = TRUE;
4676 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4677 Store[x][y] = EL_ROCK;
4679 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4681 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4683 if (!MovDelay[x][y])
4684 MovDelay[x][y] = TILEY + 1;
4693 Feld[x][y] = EL_QUICKSAND_EMPTY;
4694 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4695 Store[x][y + 1] = Store[x][y];
4698 PlayLevelSoundAction(x, y, ACTION_FILLING);
4701 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4702 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4704 InitMovingField(x, y, MV_DOWN);
4705 started_moving = TRUE;
4707 Feld[x][y] = EL_QUICKSAND_FILLING;
4708 Store[x][y] = element;
4710 PlayLevelSoundAction(x, y, ACTION_FILLING);
4712 else if (element == EL_MAGIC_WALL_FULL)
4714 if (IS_FREE(x, y + 1))
4716 InitMovingField(x, y, MV_DOWN);
4717 started_moving = TRUE;
4719 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4720 Store[x][y] = EL_CHANGED(Store[x][y]);
4722 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4724 if (!MovDelay[x][y])
4725 MovDelay[x][y] = TILEY/4 + 1;
4734 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4735 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4736 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4740 else if (element == EL_BD_MAGIC_WALL_FULL)
4742 if (IS_FREE(x, y + 1))
4744 InitMovingField(x, y, MV_DOWN);
4745 started_moving = TRUE;
4747 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4748 Store[x][y] = EL_CHANGED2(Store[x][y]);
4750 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4752 if (!MovDelay[x][y])
4753 MovDelay[x][y] = TILEY/4 + 1;
4762 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4763 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4764 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4768 else if (CAN_PASS_MAGIC_WALL(element) &&
4769 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4770 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4772 InitMovingField(x, y, MV_DOWN);
4773 started_moving = TRUE;
4776 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4777 EL_BD_MAGIC_WALL_FILLING);
4778 Store[x][y] = element;
4780 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4782 SplashAcid(x, y + 1);
4784 InitMovingField(x, y, MV_DOWN);
4785 started_moving = TRUE;
4787 Store[x][y] = EL_ACID;
4789 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4790 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
4792 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4793 CAN_FALL(element) && WasJustFalling[x][y] &&
4794 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
4796 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4797 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4798 (Feld[x][y + 1] == EL_BLOCKED)))
4800 /* this is needed for a special case not covered by calling "Impact()"
4801 from "ContinueMoving()": if an element moves to a tile directly below
4802 another element which was just falling on that tile (which was empty
4803 in the previous frame), the falling element above would just stop
4804 instead of smashing the element below (in previous version, the above
4805 element was just checked for "moving" instead of "falling", resulting
4806 in incorrect smashes caused by horizontal movement of the above
4807 element; also, the case of the player being the element to smash was
4808 simply not covered here... :-/ ) */
4810 CheckCollision[x][y] = 0;
4814 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4816 if (MovDir[x][y] == MV_NONE)
4818 InitMovingField(x, y, MV_DOWN);
4819 started_moving = TRUE;
4822 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4824 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4825 MovDir[x][y] = MV_DOWN;
4827 InitMovingField(x, y, MV_DOWN);
4828 started_moving = TRUE;
4830 else if (element == EL_AMOEBA_DROP)
4832 Feld[x][y] = EL_AMOEBA_GROWING;
4833 Store[x][y] = EL_AMOEBA_WET;
4835 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4836 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4837 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4838 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4840 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4841 (IS_FREE(x - 1, y + 1) ||
4842 Feld[x - 1][y + 1] == EL_ACID));
4843 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4844 (IS_FREE(x + 1, y + 1) ||
4845 Feld[x + 1][y + 1] == EL_ACID));
4846 boolean can_fall_any = (can_fall_left || can_fall_right);
4847 boolean can_fall_both = (can_fall_left && can_fall_right);
4848 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4850 #if USE_NEW_ALL_SLIPPERY
4851 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
4853 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4854 can_fall_right = FALSE;
4855 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4856 can_fall_left = FALSE;
4857 else if (slippery_type == SLIPPERY_ONLY_LEFT)
4858 can_fall_right = FALSE;
4859 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4860 can_fall_left = FALSE;
4862 can_fall_any = (can_fall_left || can_fall_right);
4863 can_fall_both = FALSE;
4866 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4868 if (slippery_type == SLIPPERY_ONLY_LEFT)
4869 can_fall_right = FALSE;
4870 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4871 can_fall_left = FALSE;
4872 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4873 can_fall_right = FALSE;
4874 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4875 can_fall_left = FALSE;
4877 can_fall_any = (can_fall_left || can_fall_right);
4878 can_fall_both = (can_fall_left && can_fall_right);
4882 #if USE_NEW_ALL_SLIPPERY
4884 #if USE_NEW_SP_SLIPPERY
4885 /* !!! better use the same properties as for custom elements here !!! */
4886 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
4887 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
4889 can_fall_right = FALSE; /* slip down on left side */
4890 can_fall_both = FALSE;
4895 #if USE_NEW_ALL_SLIPPERY
4898 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
4899 can_fall_right = FALSE; /* slip down on left side */
4901 can_fall_left = !(can_fall_right = RND(2));
4903 can_fall_both = FALSE;
4908 if (game.emulation == EMU_BOULDERDASH ||
4909 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
4910 can_fall_right = FALSE; /* slip down on left side */
4912 can_fall_left = !(can_fall_right = RND(2));
4914 can_fall_both = FALSE;
4920 /* if not determined otherwise, prefer left side for slipping down */
4921 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4922 started_moving = TRUE;
4926 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4928 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4931 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4932 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4933 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4934 int belt_dir = game.belt_dir[belt_nr];
4936 if ((belt_dir == MV_LEFT && left_is_free) ||
4937 (belt_dir == MV_RIGHT && right_is_free))
4939 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4941 InitMovingField(x, y, belt_dir);
4942 started_moving = TRUE;
4944 Pushed[x][y] = TRUE;
4945 Pushed[nextx][y] = TRUE;
4947 GfxAction[x][y] = ACTION_DEFAULT;
4951 MovDir[x][y] = 0; /* if element was moving, stop it */
4956 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4958 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
4960 if (CAN_MOVE(element) && !started_moving)
4963 int move_pattern = element_info[element].move_pattern;
4968 if (MovDir[x][y] == MV_NONE)
4970 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
4971 x, y, element, element_info[element].token_name);
4972 printf("StartMoving(): This should never happen!\n");
4977 Moving2Blocked(x, y, &newx, &newy);
4979 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4982 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4983 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
4985 WasJustMoving[x][y] = 0;
4986 CheckCollision[x][y] = 0;
4988 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4990 if (Feld[x][y] != element) /* element has changed */
4994 if (!MovDelay[x][y]) /* start new movement phase */
4996 /* all objects that can change their move direction after each step
4997 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4999 if (element != EL_YAMYAM &&
5000 element != EL_DARK_YAMYAM &&
5001 element != EL_PACMAN &&
5002 !(move_pattern & MV_ANY_DIRECTION) &&
5003 move_pattern != MV_TURNING_LEFT &&
5004 move_pattern != MV_TURNING_RIGHT &&
5005 move_pattern != MV_TURNING_LEFT_RIGHT &&
5006 move_pattern != MV_TURNING_RIGHT_LEFT &&
5007 move_pattern != MV_TURNING_RANDOM)
5011 if (MovDelay[x][y] && (element == EL_BUG ||
5012 element == EL_SPACESHIP ||
5013 element == EL_SP_SNIKSNAK ||
5014 element == EL_SP_ELECTRON ||
5015 element == EL_MOLE))
5016 DrawLevelField(x, y);
5020 if (MovDelay[x][y]) /* wait some time before next movement */
5024 if (element == EL_ROBOT ||
5025 element == EL_YAMYAM ||
5026 element == EL_DARK_YAMYAM)
5028 DrawLevelElementAnimationIfNeeded(x, y, element);
5029 PlayLevelSoundAction(x, y, ACTION_WAITING);
5031 else if (element == EL_SP_ELECTRON)
5032 DrawLevelElementAnimationIfNeeded(x, y, element);
5033 else if (element == EL_DRAGON)
5036 int dir = MovDir[x][y];
5037 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5038 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5039 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5040 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5041 dir == MV_UP ? IMG_FLAMES_1_UP :
5042 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5043 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5045 GfxAction[x][y] = ACTION_ATTACKING;
5047 if (IS_PLAYER(x, y))
5048 DrawPlayerField(x, y);
5050 DrawLevelField(x, y);
5052 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5054 for (i = 1; i <= 3; i++)
5056 int xx = x + i * dx;
5057 int yy = y + i * dy;
5058 int sx = SCREENX(xx);
5059 int sy = SCREENY(yy);
5060 int flame_graphic = graphic + (i - 1);
5062 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5067 int flamed = MovingOrBlocked2Element(xx, yy);
5071 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5073 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5074 RemoveMovingField(xx, yy);
5076 RemoveField(xx, yy);
5078 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5081 RemoveMovingField(xx, yy);
5084 ChangeDelay[xx][yy] = 0;
5086 Feld[xx][yy] = EL_FLAMES;
5088 if (IN_SCR_FIELD(sx, sy))
5090 DrawLevelFieldCrumbledSand(xx, yy);
5091 DrawGraphic(sx, sy, flame_graphic, frame);
5096 if (Feld[xx][yy] == EL_FLAMES)
5097 Feld[xx][yy] = EL_EMPTY;
5098 DrawLevelField(xx, yy);
5103 if (MovDelay[x][y]) /* element still has to wait some time */
5105 PlayLevelSoundAction(x, y, ACTION_WAITING);
5111 /* now make next step */
5113 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5115 if (DONT_COLLIDE_WITH(element) &&
5116 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5117 !PLAYER_ENEMY_PROTECTED(newx, newy))
5119 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5124 else if (CAN_MOVE_INTO_ACID(element) &&
5125 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5126 (MovDir[x][y] == MV_DOWN ||
5127 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5129 SplashAcid(newx, newy);
5130 Store[x][y] = EL_ACID;
5132 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5134 if (Feld[newx][newy] == EL_EXIT_OPEN)
5137 DrawLevelField(x, y);
5139 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5140 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5141 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5143 local_player->friends_still_needed--;
5144 if (!local_player->friends_still_needed &&
5145 !local_player->GameOver && AllPlayersGone)
5146 local_player->LevelSolved = local_player->GameOver = TRUE;
5150 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5152 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5153 DrawLevelField(newx, newy);
5155 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5157 else if (!IS_FREE(newx, newy))
5159 GfxAction[x][y] = ACTION_WAITING;
5161 if (IS_PLAYER(x, y))
5162 DrawPlayerField(x, y);
5164 DrawLevelField(x, y);
5169 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5171 if (IS_FOOD_PIG(Feld[newx][newy]))
5173 if (IS_MOVING(newx, newy))
5174 RemoveMovingField(newx, newy);
5177 Feld[newx][newy] = EL_EMPTY;
5178 DrawLevelField(newx, newy);
5181 PlayLevelSound(x, y, SND_PIG_DIGGING);
5183 else if (!IS_FREE(newx, newy))
5185 if (IS_PLAYER(x, y))
5186 DrawPlayerField(x, y);
5188 DrawLevelField(x, y);
5193 else if (IS_CUSTOM_ELEMENT(element) &&
5194 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5196 int new_element = Feld[newx][newy];
5198 if (!IS_FREE(newx, newy))
5200 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5201 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5204 /* no element can dig solid indestructible elements */
5205 if (IS_INDESTRUCTIBLE(new_element) &&
5206 !IS_DIGGABLE(new_element) &&
5207 !IS_COLLECTIBLE(new_element))
5210 if (AmoebaNr[newx][newy] &&
5211 (new_element == EL_AMOEBA_FULL ||
5212 new_element == EL_BD_AMOEBA ||
5213 new_element == EL_AMOEBA_GROWING))
5215 AmoebaCnt[AmoebaNr[newx][newy]]--;
5216 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5219 if (IS_MOVING(newx, newy))
5220 RemoveMovingField(newx, newy);
5223 RemoveField(newx, newy);
5224 DrawLevelField(newx, newy);
5227 /* if digged element was about to explode, prevent the explosion */
5228 ExplodeField[newx][newy] = EX_TYPE_NONE;
5230 PlayLevelSoundAction(x, y, action);
5233 Store[newx][newy] = EL_EMPTY;
5234 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5236 int move_leave_element = element_info[element].move_leave_element;
5238 /* this makes it possible to leave the removed element again */
5239 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
5240 new_element : move_leave_element);
5243 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5245 RunnerVisit[x][y] = FrameCounter;
5246 PlayerVisit[x][y] /= 8; /* expire player visit path */
5249 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5251 if (!IS_FREE(newx, newy))
5253 if (IS_PLAYER(x, y))
5254 DrawPlayerField(x, y);
5256 DrawLevelField(x, y);
5262 boolean wanna_flame = !RND(10);
5263 int dx = newx - x, dy = newy - y;
5264 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5265 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5266 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5267 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5268 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5269 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5272 IS_CLASSIC_ENEMY(element1) ||
5273 IS_CLASSIC_ENEMY(element2)) &&
5274 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5275 element1 != EL_FLAMES && element2 != EL_FLAMES)
5277 ResetGfxAnimation(x, y);
5278 GfxAction[x][y] = ACTION_ATTACKING;
5280 if (IS_PLAYER(x, y))
5281 DrawPlayerField(x, y);
5283 DrawLevelField(x, y);
5285 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5287 MovDelay[x][y] = 50;
5291 RemoveField(newx, newy);
5293 Feld[newx][newy] = EL_FLAMES;
5294 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5297 RemoveField(newx1, newy1);
5299 Feld[newx1][newy1] = EL_FLAMES;
5301 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5304 RemoveField(newx2, newy2);
5306 Feld[newx2][newy2] = EL_FLAMES;
5313 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5314 Feld[newx][newy] == EL_DIAMOND)
5316 if (IS_MOVING(newx, newy))
5317 RemoveMovingField(newx, newy);
5320 Feld[newx][newy] = EL_EMPTY;
5321 DrawLevelField(newx, newy);
5324 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5326 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5327 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5329 if (AmoebaNr[newx][newy])
5331 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5332 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5333 Feld[newx][newy] == EL_BD_AMOEBA)
5334 AmoebaCnt[AmoebaNr[newx][newy]]--;
5339 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5341 RemoveMovingField(newx, newy);
5344 if (IS_MOVING(newx, newy))
5346 RemoveMovingField(newx, newy);
5351 Feld[newx][newy] = EL_EMPTY;
5352 DrawLevelField(newx, newy);
5355 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5357 else if ((element == EL_PACMAN || element == EL_MOLE)
5358 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5360 if (AmoebaNr[newx][newy])
5362 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5363 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5364 Feld[newx][newy] == EL_BD_AMOEBA)
5365 AmoebaCnt[AmoebaNr[newx][newy]]--;
5368 if (element == EL_MOLE)
5370 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5371 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5373 ResetGfxAnimation(x, y);
5374 GfxAction[x][y] = ACTION_DIGGING;
5375 DrawLevelField(x, y);
5377 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5379 return; /* wait for shrinking amoeba */
5381 else /* element == EL_PACMAN */
5383 Feld[newx][newy] = EL_EMPTY;
5384 DrawLevelField(newx, newy);
5385 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5388 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5389 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5390 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5392 /* wait for shrinking amoeba to completely disappear */
5395 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5397 /* object was running against a wall */
5402 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
5403 if (move_pattern & MV_ANY_DIRECTION &&
5404 move_pattern == MovDir[x][y])
5406 int blocking_element =
5407 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5409 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5412 element = Feld[x][y]; /* element might have changed */
5416 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5417 DrawLevelElementAnimation(x, y, element);
5419 if (DONT_TOUCH(element))
5420 TestIfBadThingTouchesPlayer(x, y);
5425 InitMovingField(x, y, MovDir[x][y]);
5427 PlayLevelSoundAction(x, y, ACTION_MOVING);
5431 ContinueMoving(x, y);
5434 void ContinueMoving(int x, int y)
5436 int element = Feld[x][y];
5437 int stored = Store[x][y];
5438 struct ElementInfo *ei = &element_info[element];
5439 int direction = MovDir[x][y];
5440 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5441 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5442 int newx = x + dx, newy = y + dy;
5443 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5444 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5445 boolean last_line = (newy == lev_fieldy - 1);
5447 MovPos[x][y] += getElementMoveStepsize(x, y);
5449 if (pushed_by_player) /* special case: moving object pushed by player */
5450 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5452 if (ABS(MovPos[x][y]) < TILEX)
5454 DrawLevelField(x, y);
5456 return; /* element is still moving */
5459 /* element reached destination field */
5461 Feld[x][y] = EL_EMPTY;
5462 Feld[newx][newy] = element;
5463 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5465 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
5467 element = Feld[newx][newy] = EL_ACID;
5469 else if (element == EL_MOLE)
5471 Feld[x][y] = EL_SAND;
5473 DrawLevelFieldCrumbledSandNeighbours(x, y);
5475 else if (element == EL_QUICKSAND_FILLING)
5477 element = Feld[newx][newy] = get_next_element(element);
5478 Store[newx][newy] = Store[x][y];
5480 else if (element == EL_QUICKSAND_EMPTYING)
5482 Feld[x][y] = get_next_element(element);
5483 element = Feld[newx][newy] = Store[x][y];
5485 else if (element == EL_MAGIC_WALL_FILLING)
5487 element = Feld[newx][newy] = get_next_element(element);
5488 if (!game.magic_wall_active)
5489 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5490 Store[newx][newy] = Store[x][y];
5492 else if (element == EL_MAGIC_WALL_EMPTYING)
5494 Feld[x][y] = get_next_element(element);
5495 if (!game.magic_wall_active)
5496 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5497 element = Feld[newx][newy] = Store[x][y];
5499 #if USE_NEW_CUSTOM_VALUE
5500 InitField(newx, newy, FALSE);
5503 else if (element == EL_BD_MAGIC_WALL_FILLING)
5505 element = Feld[newx][newy] = get_next_element(element);
5506 if (!game.magic_wall_active)
5507 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5508 Store[newx][newy] = Store[x][y];
5510 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5512 Feld[x][y] = get_next_element(element);
5513 if (!game.magic_wall_active)
5514 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5515 element = Feld[newx][newy] = Store[x][y];
5517 #if USE_NEW_CUSTOM_VALUE
5518 InitField(newx, newy, FALSE);
5521 else if (element == EL_AMOEBA_DROPPING)
5523 Feld[x][y] = get_next_element(element);
5524 element = Feld[newx][newy] = Store[x][y];
5526 else if (element == EL_SOKOBAN_OBJECT)
5529 Feld[x][y] = Back[x][y];
5531 if (Back[newx][newy])
5532 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5534 Back[x][y] = Back[newx][newy] = 0;
5537 Store[x][y] = EL_EMPTY;
5542 MovDelay[newx][newy] = 0;
5544 if (CAN_CHANGE(element))
5546 /* copy element change control values to new field */
5547 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5548 ChangePage[newx][newy] = ChangePage[x][y];
5549 Changed[newx][newy] = Changed[x][y];
5550 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5552 #if USE_NEW_CUSTOM_VALUE
5553 CustomValue[newx][newy] = CustomValue[x][y];
5557 ChangeDelay[x][y] = 0;
5558 ChangePage[x][y] = -1;
5559 Changed[x][y] = FALSE;
5560 ChangeEvent[x][y] = -1;
5562 #if USE_NEW_CUSTOM_VALUE
5563 CustomValue[x][y] = 0;
5566 /* copy animation control values to new field */
5567 GfxFrame[newx][newy] = GfxFrame[x][y];
5568 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5569 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5570 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5572 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5574 /* some elements can leave other elements behind after moving */
5575 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
5576 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
5577 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
5579 int move_leave_element = ei->move_leave_element;
5581 /* this makes it possible to leave the removed element again */
5582 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
5583 ei->move_leave_element == EL_TRIGGER_ELEMENT)
5584 move_leave_element = stored;
5586 Feld[x][y] = move_leave_element;
5588 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
5589 MovDir[x][y] = direction;
5591 InitField(x, y, FALSE);
5593 if (GFX_CRUMBLED(Feld[x][y]))
5594 DrawLevelFieldCrumbledSandNeighbours(x, y);
5596 if (ELEM_IS_PLAYER(move_leave_element))
5597 RelocatePlayer(x, y, move_leave_element);
5600 /* do this after checking for left-behind element */
5601 ResetGfxAnimation(x, y); /* reset animation values for old field */
5603 if (!CAN_MOVE(element) ||
5604 (CAN_FALL(element) && direction == MV_DOWN &&
5605 (element == EL_SPRING ||
5606 element_info[element].move_pattern == MV_WHEN_PUSHED ||
5607 element_info[element].move_pattern == MV_WHEN_DROPPED)))
5608 GfxDir[x][y] = MovDir[newx][newy] = 0;
5610 DrawLevelField(x, y);
5611 DrawLevelField(newx, newy);
5613 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5615 /* prevent pushed element from moving on in pushed direction */
5616 if (pushed_by_player && CAN_MOVE(element) &&
5617 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5618 !(element_info[element].move_pattern & direction))
5619 TurnRound(newx, newy);
5621 /* prevent elements on conveyor belt from moving on in last direction */
5622 if (pushed_by_conveyor && CAN_FALL(element) &&
5623 direction & MV_HORIZONTAL)
5624 MovDir[newx][newy] = 0;
5626 if (!pushed_by_player)
5628 int nextx = newx + dx, nexty = newy + dy;
5629 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
5631 WasJustMoving[newx][newy] = 3;
5633 if (CAN_FALL(element) && direction == MV_DOWN)
5634 WasJustFalling[newx][newy] = 3;
5636 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
5637 CheckCollision[newx][newy] = 2;
5640 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5642 TestIfBadThingTouchesPlayer(newx, newy);
5643 TestIfBadThingTouchesFriend(newx, newy);
5645 if (!IS_CUSTOM_ELEMENT(element))
5646 TestIfBadThingTouchesOtherBadThing(newx, newy);
5648 else if (element == EL_PENGUIN)
5649 TestIfFriendTouchesBadThing(newx, newy);
5651 /* give the player one last chance (one more frame) to move away */
5652 if (CAN_FALL(element) && direction == MV_DOWN &&
5653 (last_line || (!IS_FREE(x, newy + 1) &&
5654 (!IS_PLAYER(x, newy + 1) ||
5655 game.engine_version < VERSION_IDENT(3,1,1,0)))))
5658 if (pushed_by_player && !game.use_change_when_pushing_bug)
5660 int dig_side = MV_DIR_OPPOSITE(direction);
5661 struct PlayerInfo *player = PLAYERINFO(x, y);
5663 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
5664 player->index_bit, dig_side);
5665 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
5666 player->index_bit, dig_side);
5669 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5671 TestIfElementHitsCustomElement(newx, newy, direction);
5672 TestIfPlayerTouchesCustomElement(newx, newy);
5673 TestIfElementTouchesCustomElement(newx, newy);
5676 int AmoebeNachbarNr(int ax, int ay)
5679 int element = Feld[ax][ay];
5681 static int xy[4][2] =
5689 for (i = 0; i < NUM_DIRECTIONS; i++)
5691 int x = ax + xy[i][0];
5692 int y = ay + xy[i][1];
5694 if (!IN_LEV_FIELD(x, y))
5697 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5698 group_nr = AmoebaNr[x][y];
5704 void AmoebenVereinigen(int ax, int ay)
5706 int i, x, y, xx, yy;
5707 int new_group_nr = AmoebaNr[ax][ay];
5708 static int xy[4][2] =
5716 if (new_group_nr == 0)
5719 for (i = 0; i < NUM_DIRECTIONS; i++)
5724 if (!IN_LEV_FIELD(x, y))
5727 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5728 Feld[x][y] == EL_BD_AMOEBA ||
5729 Feld[x][y] == EL_AMOEBA_DEAD) &&
5730 AmoebaNr[x][y] != new_group_nr)
5732 int old_group_nr = AmoebaNr[x][y];
5734 if (old_group_nr == 0)
5737 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5738 AmoebaCnt[old_group_nr] = 0;
5739 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5740 AmoebaCnt2[old_group_nr] = 0;
5742 for (yy = 0; yy < lev_fieldy; yy++)
5744 for (xx = 0; xx < lev_fieldx; xx++)
5746 if (AmoebaNr[xx][yy] == old_group_nr)
5747 AmoebaNr[xx][yy] = new_group_nr;
5754 void AmoebeUmwandeln(int ax, int ay)
5758 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5760 int group_nr = AmoebaNr[ax][ay];
5765 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5766 printf("AmoebeUmwandeln(): This should never happen!\n");
5771 for (y = 0; y < lev_fieldy; y++)
5773 for (x = 0; x < lev_fieldx; x++)
5775 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5778 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5782 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5783 SND_AMOEBA_TURNING_TO_GEM :
5784 SND_AMOEBA_TURNING_TO_ROCK));
5789 static int xy[4][2] =
5797 for (i = 0; i < NUM_DIRECTIONS; i++)
5802 if (!IN_LEV_FIELD(x, y))
5805 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5807 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5808 SND_AMOEBA_TURNING_TO_GEM :
5809 SND_AMOEBA_TURNING_TO_ROCK));
5816 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5819 int group_nr = AmoebaNr[ax][ay];
5820 boolean done = FALSE;
5825 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5826 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5831 for (y = 0; y < lev_fieldy; y++)
5833 for (x = 0; x < lev_fieldx; x++)
5835 if (AmoebaNr[x][y] == group_nr &&
5836 (Feld[x][y] == EL_AMOEBA_DEAD ||
5837 Feld[x][y] == EL_BD_AMOEBA ||
5838 Feld[x][y] == EL_AMOEBA_GROWING))
5841 Feld[x][y] = new_element;
5842 InitField(x, y, FALSE);
5843 DrawLevelField(x, y);
5850 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5851 SND_BD_AMOEBA_TURNING_TO_ROCK :
5852 SND_BD_AMOEBA_TURNING_TO_GEM));
5855 void AmoebeWaechst(int x, int y)
5857 static unsigned long sound_delay = 0;
5858 static unsigned long sound_delay_value = 0;
5860 if (!MovDelay[x][y]) /* start new growing cycle */
5864 if (DelayReached(&sound_delay, sound_delay_value))
5866 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5867 sound_delay_value = 30;
5871 if (MovDelay[x][y]) /* wait some time before growing bigger */
5874 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5876 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5877 6 - MovDelay[x][y]);
5879 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5882 if (!MovDelay[x][y])
5884 Feld[x][y] = Store[x][y];
5886 DrawLevelField(x, y);
5891 void AmoebaDisappearing(int x, int y)
5893 static unsigned long sound_delay = 0;
5894 static unsigned long sound_delay_value = 0;
5896 if (!MovDelay[x][y]) /* start new shrinking cycle */
5900 if (DelayReached(&sound_delay, sound_delay_value))
5901 sound_delay_value = 30;
5904 if (MovDelay[x][y]) /* wait some time before shrinking */
5907 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5909 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5910 6 - MovDelay[x][y]);
5912 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5915 if (!MovDelay[x][y])
5917 Feld[x][y] = EL_EMPTY;
5918 DrawLevelField(x, y);
5920 /* don't let mole enter this field in this cycle;
5921 (give priority to objects falling to this field from above) */
5927 void AmoebeAbleger(int ax, int ay)
5930 int element = Feld[ax][ay];
5931 int graphic = el2img(element);
5932 int newax = ax, neway = ay;
5933 static int xy[4][2] =
5941 if (!level.amoeba_speed)
5943 Feld[ax][ay] = EL_AMOEBA_DEAD;
5944 DrawLevelField(ax, ay);
5948 if (IS_ANIMATED(graphic))
5949 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5951 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5952 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5954 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5957 if (MovDelay[ax][ay])
5961 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5964 int x = ax + xy[start][0];
5965 int y = ay + xy[start][1];
5967 if (!IN_LEV_FIELD(x, y))
5970 if (IS_FREE(x, y) ||
5971 CAN_GROW_INTO(Feld[x][y]) ||
5972 Feld[x][y] == EL_QUICKSAND_EMPTY)
5978 if (newax == ax && neway == ay)
5981 else /* normal or "filled" (BD style) amoeba */
5984 boolean waiting_for_player = FALSE;
5986 for (i = 0; i < NUM_DIRECTIONS; i++)
5988 int j = (start + i) % 4;
5989 int x = ax + xy[j][0];
5990 int y = ay + xy[j][1];
5992 if (!IN_LEV_FIELD(x, y))
5995 if (IS_FREE(x, y) ||
5996 CAN_GROW_INTO(Feld[x][y]) ||
5997 Feld[x][y] == EL_QUICKSAND_EMPTY)
6003 else if (IS_PLAYER(x, y))
6004 waiting_for_player = TRUE;
6007 if (newax == ax && neway == ay) /* amoeba cannot grow */
6009 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6011 Feld[ax][ay] = EL_AMOEBA_DEAD;
6012 DrawLevelField(ax, ay);
6013 AmoebaCnt[AmoebaNr[ax][ay]]--;
6015 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6017 if (element == EL_AMOEBA_FULL)
6018 AmoebeUmwandeln(ax, ay);
6019 else if (element == EL_BD_AMOEBA)
6020 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6025 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6027 /* amoeba gets larger by growing in some direction */
6029 int new_group_nr = AmoebaNr[ax][ay];
6032 if (new_group_nr == 0)
6034 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6035 printf("AmoebeAbleger(): This should never happen!\n");
6040 AmoebaNr[newax][neway] = new_group_nr;
6041 AmoebaCnt[new_group_nr]++;
6042 AmoebaCnt2[new_group_nr]++;
6044 /* if amoeba touches other amoeba(s) after growing, unify them */
6045 AmoebenVereinigen(newax, neway);
6047 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6049 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6055 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6056 (neway == lev_fieldy - 1 && newax != ax))
6058 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6059 Store[newax][neway] = element;
6061 else if (neway == ay)
6063 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6065 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6069 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6070 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6071 Store[ax][ay] = EL_AMOEBA_DROP;
6072 ContinueMoving(ax, ay);
6076 DrawLevelField(newax, neway);
6079 void Life(int ax, int ay)
6083 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6086 int element = Feld[ax][ay];
6087 int graphic = el2img(element);
6088 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6090 boolean changed = FALSE;
6092 if (IS_ANIMATED(graphic))
6093 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6098 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6099 MovDelay[ax][ay] = life_time;
6101 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6104 if (MovDelay[ax][ay])
6108 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6110 int xx = ax+x1, yy = ay+y1;
6113 if (!IN_LEV_FIELD(xx, yy))
6116 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6118 int x = xx+x2, y = yy+y2;
6120 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6123 if (((Feld[x][y] == element ||
6124 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6126 (IS_FREE(x, y) && Stop[x][y]))
6130 if (xx == ax && yy == ay) /* field in the middle */
6132 if (nachbarn < life_parameter[0] ||
6133 nachbarn > life_parameter[1])
6135 Feld[xx][yy] = EL_EMPTY;
6137 DrawLevelField(xx, yy);
6138 Stop[xx][yy] = TRUE;
6142 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6143 { /* free border field */
6144 if (nachbarn >= life_parameter[2] &&
6145 nachbarn <= life_parameter[3])
6147 Feld[xx][yy] = element;
6148 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6150 DrawLevelField(xx, yy);
6151 Stop[xx][yy] = TRUE;
6158 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6159 SND_GAME_OF_LIFE_GROWING);
6162 static void InitRobotWheel(int x, int y)
6164 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6167 static void RunRobotWheel(int x, int y)
6169 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6172 static void StopRobotWheel(int x, int y)
6174 if (ZX == x && ZY == y)
6178 static void InitTimegateWheel(int x, int y)
6180 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6183 static void RunTimegateWheel(int x, int y)
6185 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6188 void CheckExit(int x, int y)
6190 if (local_player->gems_still_needed > 0 ||
6191 local_player->sokobanfields_still_needed > 0 ||
6192 local_player->lights_still_needed > 0)
6194 int element = Feld[x][y];
6195 int graphic = el2img(element);
6197 if (IS_ANIMATED(graphic))
6198 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6203 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6206 Feld[x][y] = EL_EXIT_OPENING;
6208 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6211 void CheckExitSP(int x, int y)
6213 if (local_player->gems_still_needed > 0)
6215 int element = Feld[x][y];
6216 int graphic = el2img(element);
6218 if (IS_ANIMATED(graphic))
6219 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6224 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6227 Feld[x][y] = EL_SP_EXIT_OPENING;
6229 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6232 static void CloseAllOpenTimegates()
6236 for (y = 0; y < lev_fieldy; y++)
6238 for (x = 0; x < lev_fieldx; x++)
6240 int element = Feld[x][y];
6242 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6244 Feld[x][y] = EL_TIMEGATE_CLOSING;
6246 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6252 void EdelsteinFunkeln(int x, int y)
6254 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6257 if (Feld[x][y] == EL_BD_DIAMOND)
6260 if (MovDelay[x][y] == 0) /* next animation frame */
6261 MovDelay[x][y] = 11 * !SimpleRND(500);
6263 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6267 if (setup.direct_draw && MovDelay[x][y])
6268 SetDrawtoField(DRAW_BUFFERED);
6270 DrawLevelElementAnimation(x, y, Feld[x][y]);
6272 if (MovDelay[x][y] != 0)
6274 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6275 10 - MovDelay[x][y]);
6277 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6279 if (setup.direct_draw)
6283 dest_x = FX + SCREENX(x) * TILEX;
6284 dest_y = FY + SCREENY(y) * TILEY;
6286 BlitBitmap(drawto_field, window,
6287 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6288 SetDrawtoField(DRAW_DIRECT);
6294 void MauerWaechst(int x, int y)
6298 if (!MovDelay[x][y]) /* next animation frame */
6299 MovDelay[x][y] = 3 * delay;
6301 if (MovDelay[x][y]) /* wait some time before next frame */
6305 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6307 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6308 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6310 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6313 if (!MovDelay[x][y])
6315 if (MovDir[x][y] == MV_LEFT)
6317 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6318 DrawLevelField(x - 1, y);
6320 else if (MovDir[x][y] == MV_RIGHT)
6322 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6323 DrawLevelField(x + 1, y);
6325 else if (MovDir[x][y] == MV_UP)
6327 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6328 DrawLevelField(x, y - 1);
6332 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6333 DrawLevelField(x, y + 1);
6336 Feld[x][y] = Store[x][y];
6338 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6339 DrawLevelField(x, y);
6344 void MauerAbleger(int ax, int ay)
6346 int element = Feld[ax][ay];
6347 int graphic = el2img(element);
6348 boolean oben_frei = FALSE, unten_frei = FALSE;
6349 boolean links_frei = FALSE, rechts_frei = FALSE;
6350 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6351 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6352 boolean new_wall = FALSE;
6354 if (IS_ANIMATED(graphic))
6355 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6357 if (!MovDelay[ax][ay]) /* start building new wall */
6358 MovDelay[ax][ay] = 6;
6360 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6363 if (MovDelay[ax][ay])
6367 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6369 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6371 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6373 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6376 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6377 element == EL_EXPANDABLE_WALL_ANY)
6381 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6382 Store[ax][ay-1] = element;
6383 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6384 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6385 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6386 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6391 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6392 Store[ax][ay+1] = element;
6393 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6394 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6395 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6396 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6401 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6402 element == EL_EXPANDABLE_WALL_ANY ||
6403 element == EL_EXPANDABLE_WALL)
6407 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6408 Store[ax-1][ay] = element;
6409 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6410 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6411 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6412 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6418 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6419 Store[ax+1][ay] = element;
6420 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6421 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6422 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6423 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6428 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6429 DrawLevelField(ax, ay);
6431 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6433 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6434 unten_massiv = TRUE;
6435 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6436 links_massiv = TRUE;
6437 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6438 rechts_massiv = TRUE;
6440 if (((oben_massiv && unten_massiv) ||
6441 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6442 element == EL_EXPANDABLE_WALL) &&
6443 ((links_massiv && rechts_massiv) ||
6444 element == EL_EXPANDABLE_WALL_VERTICAL))
6445 Feld[ax][ay] = EL_WALL;
6448 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6451 void CheckForDragon(int x, int y)
6454 boolean dragon_found = FALSE;
6455 static int xy[4][2] =
6463 for (i = 0; i < NUM_DIRECTIONS; i++)
6465 for (j = 0; j < 4; j++)
6467 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6469 if (IN_LEV_FIELD(xx, yy) &&
6470 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6472 if (Feld[xx][yy] == EL_DRAGON)
6473 dragon_found = TRUE;
6482 for (i = 0; i < NUM_DIRECTIONS; i++)
6484 for (j = 0; j < 3; j++)
6486 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6488 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6490 Feld[xx][yy] = EL_EMPTY;
6491 DrawLevelField(xx, yy);
6500 static void InitBuggyBase(int x, int y)
6502 int element = Feld[x][y];
6503 int activating_delay = FRAMES_PER_SECOND / 4;
6506 (element == EL_SP_BUGGY_BASE ?
6507 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6508 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6510 element == EL_SP_BUGGY_BASE_ACTIVE ?
6511 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6514 static void WarnBuggyBase(int x, int y)
6517 static int xy[4][2] =
6525 for (i = 0; i < NUM_DIRECTIONS; i++)
6527 int xx = x + xy[i][0], yy = y + xy[i][1];
6529 if (IS_PLAYER(xx, yy))
6531 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6538 static void InitTrap(int x, int y)
6540 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6543 static void ActivateTrap(int x, int y)
6545 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6548 static void ChangeActiveTrap(int x, int y)
6550 int graphic = IMG_TRAP_ACTIVE;
6552 /* if new animation frame was drawn, correct crumbled sand border */
6553 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6554 DrawLevelFieldCrumbledSand(x, y);
6557 static int getSpecialActionElement(int element, int number, int base_element)
6559 return (element != EL_EMPTY ? element :
6560 number != -1 ? base_element + number - 1 :
6564 static int getModifiedActionNumber(int value_old, int operator, int operand,
6565 int value_min, int value_max)
6567 int value_new = (operator == CA_MODE_SET ? operand :
6568 operator == CA_MODE_ADD ? value_old + operand :
6569 operator == CA_MODE_SUBTRACT ? value_old - operand :
6570 operator == CA_MODE_MULTIPLY ? value_old * operand :
6571 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
6572 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
6575 return (value_new < value_min ? value_min :
6576 value_new > value_max ? value_max :
6580 static void ExecuteCustomElementAction(int x, int y, int element, int page)
6582 struct ElementInfo *ei = &element_info[element];
6583 struct ElementChangeInfo *change = &ei->change_page[page];
6584 int action_type = change->action_type;
6585 int action_mode = change->action_mode;
6586 int action_arg = change->action_arg;
6589 if (!change->has_action)
6592 /* ---------- determine action paramater values ---------- */
6594 int action_arg_element =
6595 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
6596 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
6597 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
6600 int action_arg_direction =
6601 (action_arg >= CA_ARG_DIRECTION_LEFT &&
6602 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
6603 action_arg == CA_ARG_DIRECTION_TRIGGER ?
6604 change->actual_trigger_side :
6605 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
6606 MV_DIR_OPPOSITE(change->actual_trigger_side) :
6609 int action_arg_number_min =
6610 (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MIN :
6613 int action_arg_number_max =
6614 (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MAX :
6615 action_type == CA_SET_LEVEL_GEMS ? 999 :
6616 action_type == CA_SET_LEVEL_TIME ? 9999 :
6617 action_type == CA_SET_LEVEL_SCORE ? 99999 :
6618 action_type == CA_SET_CE_SCORE ? 9999 :
6619 action_type == CA_SET_CE_VALUE ? 9999 :
6622 int action_arg_number_reset =
6623 (action_type == CA_SET_PLAYER_SPEED ? TILEX/game.initial_move_delay_value :
6624 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
6625 action_type == CA_SET_LEVEL_TIME ? level.time :
6626 action_type == CA_SET_LEVEL_SCORE ? 0 :
6627 action_type == CA_SET_CE_SCORE ? 0 :
6629 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
6631 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
6635 int action_arg_number =
6636 (action_arg <= CA_ARG_MAX ? action_arg :
6637 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
6638 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
6639 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
6640 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
6641 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
6642 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
6643 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
6644 #if USE_NEW_CUSTOM_VALUE
6645 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
6647 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
6649 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) :
6650 action_arg == CA_ARG_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
6651 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value :
6654 int action_arg_number_old =
6655 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
6656 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
6657 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
6658 action_type == CA_SET_CE_SCORE ? ei->collect_score :
6659 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
6662 int action_arg_number_new =
6663 getModifiedActionNumber(action_arg_number_old,
6664 action_mode, action_arg_number,
6665 action_arg_number_min, action_arg_number_max);
6667 int trigger_player_bits =
6668 (change->actual_trigger_player >= EL_PLAYER_1 &&
6669 change->actual_trigger_player <= EL_PLAYER_4 ?
6670 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
6673 int action_arg_player_bits =
6674 (action_arg >= CA_ARG_PLAYER_1 &&
6675 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
6676 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
6679 /* ---------- execute action ---------- */
6688 case CA_EXIT_PLAYER:
6690 for (i = 0; i < MAX_PLAYERS; i++)
6691 if (action_arg_player_bits & (1 << i))
6692 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
6697 case CA_KILL_PLAYER:
6699 for (i = 0; i < MAX_PLAYERS; i++)
6700 if (action_arg_player_bits & (1 << i))
6701 KillPlayer(&stored_player[i]);
6706 case CA_MOVE_PLAYER:
6708 /* automatically move to the next field in specified direction */
6709 for (i = 0; i < MAX_PLAYERS; i++)
6710 if (trigger_player_bits & (1 << i))
6711 stored_player[i].programmed_action = action_arg_direction;
6716 case CA_RESTART_LEVEL:
6718 game.restart_level = TRUE;
6723 case CA_SHOW_ENVELOPE:
6725 int element = getSpecialActionElement(action_arg_element,
6726 action_arg_number, EL_ENVELOPE_1);
6728 if (IS_ENVELOPE(element))
6729 local_player->show_envelope = element;
6734 case CA_SET_PLAYER_KEYS:
6736 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
6737 int element = getSpecialActionElement(action_arg_element,
6738 action_arg_number, EL_KEY_1);
6740 if (IS_KEY(element))
6742 for (i = 0; i < MAX_PLAYERS; i++)
6744 if (trigger_player_bits & (1 << i))
6746 stored_player[i].key[KEY_NR(element)] = key_state;
6748 DrawGameValue_Keys(stored_player[i].key);
6750 redraw_mask |= REDRAW_DOOR_1;
6758 case CA_SET_PLAYER_SPEED:
6760 for (i = 0; i < MAX_PLAYERS; i++)
6762 if (trigger_player_bits & (1 << i))
6764 int move_stepsize = TILEX / stored_player[i].move_delay_value;
6766 if (action_arg == CA_ARG_SPEED_SLOWER ||
6767 action_arg == CA_ARG_SPEED_FASTER)
6769 action_arg_number = 2;
6770 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
6775 getModifiedActionNumber(move_stepsize,
6778 action_arg_number_min,
6779 action_arg_number_max);
6781 /* make sure that value is power of 2 */
6782 move_stepsize = (1 << log_2(move_stepsize));
6784 /* do no immediately change -- the player might just be moving */
6785 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
6787 stored_player[i].cannot_move =
6788 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
6795 case CA_SET_PLAYER_SHIELD:
6797 for (i = 0; i < MAX_PLAYERS; i++)
6799 if (trigger_player_bits & (1 << i))
6801 if (action_arg == CA_ARG_SHIELD_OFF)
6803 stored_player[i].shield_normal_time_left = 0;
6804 stored_player[i].shield_deadly_time_left = 0;
6806 else if (action_arg == CA_ARG_SHIELD_NORMAL)
6808 stored_player[i].shield_normal_time_left = 999999;
6810 else if (action_arg == CA_ARG_SHIELD_DEADLY)
6812 stored_player[i].shield_normal_time_left = 999999;
6813 stored_player[i].shield_deadly_time_left = 999999;
6821 case CA_SET_PLAYER_ARTWORK:
6823 for (i = 0; i < MAX_PLAYERS; i++)
6825 int element = action_arg_element;
6831 case CA_SET_LEVEL_GRAVITY:
6833 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
6834 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
6835 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
6840 case CA_SET_LEVEL_WIND:
6842 game.wind_direction = action_arg_direction;
6847 case CA_SET_LEVEL_GEMS:
6849 local_player->gems_still_needed = action_arg_number_new;
6851 DrawGameValue_Emeralds(local_player->gems_still_needed);
6856 case CA_SET_LEVEL_TIME:
6858 if (level.time > 0) /* only modify limited time value */
6860 TimeLeft = action_arg_number_new;
6862 DrawGameValue_Time(TimeLeft);
6864 if (!TimeLeft && setup.time_limit)
6865 for (i = 0; i < MAX_PLAYERS; i++)
6866 KillPlayer(&stored_player[i]);
6872 case CA_SET_LEVEL_SCORE:
6874 local_player->score = action_arg_number_new;
6876 DrawGameValue_Score(local_player->score);
6881 case CA_SET_CE_SCORE:
6883 ei->collect_score = action_arg_number_new;
6888 case CA_SET_CE_VALUE:
6890 #if USE_NEW_CUSTOM_VALUE
6891 int last_custom_value = CustomValue[x][y];
6893 CustomValue[x][y] = action_arg_number_new;
6896 printf("::: Count == %d\n", CustomValue[x][y]);
6899 if (CustomValue[x][y] == 0 && last_custom_value > 0)
6902 printf("::: CE_VALUE_GETS_ZERO\n");
6905 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
6906 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
6918 static void ChangeElementNowExt(struct ElementChangeInfo *change,
6919 int x, int y, int target_element)
6921 int previous_move_direction = MovDir[x][y];
6922 #if USE_NEW_CUSTOM_VALUE
6923 int last_ce_value = CustomValue[x][y];
6925 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
6926 IS_WALKABLE(Feld[x][y]));
6928 /* check if element under player changes from accessible to unaccessible
6929 (needed for special case of dropping element which then changes) */
6930 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6931 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6939 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6940 RemoveMovingField(x, y);
6944 Feld[x][y] = target_element;
6946 ResetGfxAnimation(x, y);
6947 ResetRandomAnimationValue(x, y);
6949 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6950 MovDir[x][y] = previous_move_direction;
6952 #if USE_NEW_CUSTOM_VALUE
6953 if (element_info[Feld[x][y]].use_last_ce_value)
6954 CustomValue[x][y] = last_ce_value;
6957 InitField_WithBug1(x, y, FALSE);
6959 DrawLevelField(x, y);
6961 if (GFX_CRUMBLED(Feld[x][y]))
6962 DrawLevelFieldCrumbledSandNeighbours(x, y);
6965 /* "Changed[][]" not set yet to allow "entered by player" change one time */
6966 if (ELEM_IS_PLAYER(target_element))
6967 RelocatePlayer(x, y, target_element);
6970 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
6972 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6975 TestIfBadThingTouchesPlayer(x, y);
6976 TestIfPlayerTouchesCustomElement(x, y);
6977 TestIfElementTouchesCustomElement(x, y);
6980 static boolean ChangeElementNow(int x, int y, int element, int page)
6982 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6984 int old_element = Feld[x][y];
6986 /* always use default change event to prevent running into a loop */
6987 if (ChangeEvent[x][y] == -1)
6988 ChangeEvent[x][y] = CE_DELAY;
6990 if (ChangeEvent[x][y] == CE_DELAY)
6992 /* reset actual trigger element, trigger player and action element */
6993 change->actual_trigger_element = EL_EMPTY;
6994 change->actual_trigger_player = EL_PLAYER_1;
6995 change->actual_trigger_side = CH_SIDE_NONE;
6996 change->actual_trigger_ce_value = 0;
7000 /* do not change any elements that have already changed in this frame */
7004 /* do not change already changed elements with same change event */
7005 if (Changed[x][y] & ChangeEvent[x][y])
7010 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7012 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7015 if (change->explode)
7022 if (change->use_target_content)
7024 boolean complete_replace = TRUE;
7025 boolean can_replace[3][3];
7028 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7031 boolean is_walkable;
7032 boolean is_diggable;
7033 boolean is_collectible;
7034 boolean is_removable;
7035 boolean is_destructible;
7036 int ex = x + xx - 1;
7037 int ey = y + yy - 1;
7038 int content_element = change->target_content.e[xx][yy];
7041 can_replace[xx][yy] = TRUE;
7043 if (ex == x && ey == y) /* do not check changing element itself */
7046 if (content_element == EL_EMPTY_SPACE)
7048 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7053 if (!IN_LEV_FIELD(ex, ey))
7055 can_replace[xx][yy] = FALSE;
7056 complete_replace = FALSE;
7063 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7064 e = MovingOrBlocked2Element(ex, ey);
7066 is_empty = (IS_FREE(ex, ey) ||
7067 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7069 is_walkable = (is_empty || IS_WALKABLE(e));
7070 is_diggable = (is_empty || IS_DIGGABLE(e));
7071 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7072 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7073 is_removable = (is_diggable || is_collectible);
7075 can_replace[xx][yy] =
7076 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7077 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7078 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7079 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7080 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7081 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7082 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7084 if (!can_replace[xx][yy])
7085 complete_replace = FALSE;
7088 if (!change->only_if_complete || complete_replace)
7090 boolean something_has_changed = FALSE;
7092 if (change->only_if_complete && change->use_random_replace &&
7093 RND(100) < change->random_percentage)
7096 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7098 int ex = x + xx - 1;
7099 int ey = y + yy - 1;
7100 int content_element;
7102 if (can_replace[xx][yy] && (!change->use_random_replace ||
7103 RND(100) < change->random_percentage))
7105 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7106 RemoveMovingField(ex, ey);
7108 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7110 content_element = change->target_content.e[xx][yy];
7111 target_element = GET_TARGET_ELEMENT(content_element, change);
7113 ChangeElementNowExt(change, ex, ey, target_element);
7115 something_has_changed = TRUE;
7117 /* for symmetry reasons, freeze newly created border elements */
7118 if (ex != x || ey != y)
7119 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7123 if (something_has_changed)
7125 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7126 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7132 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7134 ChangeElementNowExt(change, x, y, target_element);
7136 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7137 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7140 /* this uses direct change before indirect change */
7141 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
7146 #if USE_NEW_DELAYED_ACTION
7148 static void ChangeElement(int x, int y, int page)
7150 int element = MovingOrBlocked2Element(x, y);
7151 struct ElementInfo *ei = &element_info[element];
7152 struct ElementChangeInfo *change = &ei->change_page[page];
7155 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
7156 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
7159 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7160 x, y, element, element_info[element].token_name);
7161 printf("ChangeElement(): This should never happen!\n");
7166 /* this can happen with classic bombs on walkable, changing elements */
7167 if (!CAN_CHANGE_OR_HAS_ACTION(element))
7170 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7171 ChangeDelay[x][y] = 0;
7177 if (ChangeDelay[x][y] == 0) /* initialize element change */
7179 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7181 if (change->can_change)
7183 ResetGfxAnimation(x, y);
7184 ResetRandomAnimationValue(x, y);
7186 if (change->pre_change_function)
7187 change->pre_change_function(x, y);
7191 ChangeDelay[x][y]--;
7193 if (ChangeDelay[x][y] != 0) /* continue element change */
7195 if (change->can_change)
7197 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7199 if (IS_ANIMATED(graphic))
7200 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7202 if (change->change_function)
7203 change->change_function(x, y);
7206 else /* finish element change */
7208 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7210 page = ChangePage[x][y];
7211 ChangePage[x][y] = -1;
7213 change = &ei->change_page[page];
7216 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7218 ChangeDelay[x][y] = 1; /* try change after next move step */
7219 ChangePage[x][y] = page; /* remember page to use for change */
7224 if (change->can_change)
7226 if (ChangeElementNow(x, y, element, page))
7228 if (change->post_change_function)
7229 change->post_change_function(x, y);
7233 if (change->has_action)
7234 ExecuteCustomElementAction(x, y, element, page);
7240 static void ChangeElement(int x, int y, int page)
7242 int element = MovingOrBlocked2Element(x, y);
7243 struct ElementInfo *ei = &element_info[element];
7244 struct ElementChangeInfo *change = &ei->change_page[page];
7247 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7250 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7251 x, y, element, element_info[element].token_name);
7252 printf("ChangeElement(): This should never happen!\n");
7257 /* this can happen with classic bombs on walkable, changing elements */
7258 if (!CAN_CHANGE(element))
7261 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7262 ChangeDelay[x][y] = 0;
7268 if (ChangeDelay[x][y] == 0) /* initialize element change */
7270 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7272 ResetGfxAnimation(x, y);
7273 ResetRandomAnimationValue(x, y);
7275 if (change->pre_change_function)
7276 change->pre_change_function(x, y);
7279 ChangeDelay[x][y]--;
7281 if (ChangeDelay[x][y] != 0) /* continue element change */
7283 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7285 if (IS_ANIMATED(graphic))
7286 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7288 if (change->change_function)
7289 change->change_function(x, y);
7291 else /* finish element change */
7293 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7295 page = ChangePage[x][y];
7296 ChangePage[x][y] = -1;
7298 change = &ei->change_page[page];
7301 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7303 ChangeDelay[x][y] = 1; /* try change after next move step */
7304 ChangePage[x][y] = page; /* remember page to use for change */
7309 if (ChangeElementNow(x, y, element, page))
7311 if (change->post_change_function)
7312 change->post_change_function(x, y);
7319 static boolean CheckTriggeredElementChangeExt(int x, int y,
7320 int trigger_element,
7326 boolean change_done_any = FALSE;
7327 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7330 if (!(trigger_events[trigger_element][trigger_event]))
7333 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7335 int element = EL_CUSTOM_START + i;
7336 boolean change_done = FALSE;
7339 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7340 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7343 for (p = 0; p < element_info[element].num_change_pages; p++)
7345 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7347 if (change->can_change_or_has_action &&
7348 change->has_event[trigger_event] &&
7349 change->trigger_side & trigger_side &&
7350 change->trigger_player & trigger_player &&
7351 change->trigger_page & trigger_page_bits &&
7352 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7354 change->actual_trigger_element = trigger_element;
7355 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7356 change->actual_trigger_side = trigger_side;
7357 change->actual_trigger_ce_value = CustomValue[x][y];
7359 if ((change->can_change && !change_done) || change->has_action)
7363 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7365 if (Feld[x][y] == element)
7367 if (change->can_change && !change_done)
7369 ChangeDelay[x][y] = 1;
7370 ChangeEvent[x][y] = trigger_event;
7371 ChangeElement(x, y, p);
7373 #if USE_NEW_DELAYED_ACTION
7374 else if (change->has_action)
7376 ExecuteCustomElementAction(x, y, element, p);
7377 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7380 if (change->has_action)
7382 ExecuteCustomElementAction(x, y, element, p);
7383 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7389 if (change->can_change)
7392 change_done_any = TRUE;
7399 return change_done_any;
7402 static boolean CheckElementChangeExt(int x, int y,
7404 int trigger_element,
7409 boolean change_done = FALSE;
7412 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7413 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7416 if (Feld[x][y] == EL_BLOCKED)
7418 Blocked2Moving(x, y, &x, &y);
7419 element = Feld[x][y];
7422 if (Feld[x][y] != element) /* check if element has already changed */
7425 for (p = 0; p < element_info[element].num_change_pages; p++)
7427 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7429 boolean check_trigger_element =
7430 (trigger_event == CE_TOUCHING_X ||
7431 trigger_event == CE_HITTING_X ||
7432 trigger_event == CE_HIT_BY_X);
7434 if (change->can_change_or_has_action &&
7435 change->has_event[trigger_event] &&
7436 change->trigger_side & trigger_side &&
7437 change->trigger_player & trigger_player &&
7438 (!check_trigger_element ||
7439 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
7441 change->actual_trigger_element = trigger_element;
7442 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7443 change->actual_trigger_side = trigger_side;
7444 change->actual_trigger_ce_value = CustomValue[x][y];
7446 if (change->can_change && !change_done)
7448 ChangeDelay[x][y] = 1;
7449 ChangeEvent[x][y] = trigger_event;
7450 ChangeElement(x, y, p);
7454 #if USE_NEW_DELAYED_ACTION
7455 else if (change->has_action)
7457 ExecuteCustomElementAction(x, y, element, p);
7458 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7461 if (change->has_action)
7463 ExecuteCustomElementAction(x, y, element, p);
7464 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7473 static void PlayPlayerSound(struct PlayerInfo *player)
7475 int jx = player->jx, jy = player->jy;
7476 int element = player->element_nr;
7477 int last_action = player->last_action_waiting;
7478 int action = player->action_waiting;
7480 if (player->is_waiting)
7482 if (action != last_action)
7483 PlayLevelSoundElementAction(jx, jy, element, action);
7485 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7489 if (action != last_action)
7490 StopSound(element_info[element].sound[last_action]);
7492 if (last_action == ACTION_SLEEPING)
7493 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7497 static void PlayAllPlayersSound()
7501 for (i = 0; i < MAX_PLAYERS; i++)
7502 if (stored_player[i].active)
7503 PlayPlayerSound(&stored_player[i]);
7506 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7508 boolean last_waiting = player->is_waiting;
7509 int move_dir = player->MovDir;
7511 player->last_action_waiting = player->action_waiting;
7515 if (!last_waiting) /* not waiting -> waiting */
7517 player->is_waiting = TRUE;
7519 player->frame_counter_bored =
7521 game.player_boring_delay_fixed +
7522 SimpleRND(game.player_boring_delay_random);
7523 player->frame_counter_sleeping =
7525 game.player_sleeping_delay_fixed +
7526 SimpleRND(game.player_sleeping_delay_random);
7528 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7531 if (game.player_sleeping_delay_fixed +
7532 game.player_sleeping_delay_random > 0 &&
7533 player->anim_delay_counter == 0 &&
7534 player->post_delay_counter == 0 &&
7535 FrameCounter >= player->frame_counter_sleeping)
7536 player->is_sleeping = TRUE;
7537 else if (game.player_boring_delay_fixed +
7538 game.player_boring_delay_random > 0 &&
7539 FrameCounter >= player->frame_counter_bored)
7540 player->is_bored = TRUE;
7542 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7543 player->is_bored ? ACTION_BORING :
7546 if (player->is_sleeping)
7548 if (player->num_special_action_sleeping > 0)
7550 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7552 int last_special_action = player->special_action_sleeping;
7553 int num_special_action = player->num_special_action_sleeping;
7554 int special_action =
7555 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7556 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7557 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7558 last_special_action + 1 : ACTION_SLEEPING);
7559 int special_graphic =
7560 el_act_dir2img(player->element_nr, special_action, move_dir);
7562 player->anim_delay_counter =
7563 graphic_info[special_graphic].anim_delay_fixed +
7564 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7565 player->post_delay_counter =
7566 graphic_info[special_graphic].post_delay_fixed +
7567 SimpleRND(graphic_info[special_graphic].post_delay_random);
7569 player->special_action_sleeping = special_action;
7572 if (player->anim_delay_counter > 0)
7574 player->action_waiting = player->special_action_sleeping;
7575 player->anim_delay_counter--;
7577 else if (player->post_delay_counter > 0)
7579 player->post_delay_counter--;
7583 else if (player->is_bored)
7585 if (player->num_special_action_bored > 0)
7587 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7589 int special_action =
7590 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7591 int special_graphic =
7592 el_act_dir2img(player->element_nr, special_action, move_dir);
7594 player->anim_delay_counter =
7595 graphic_info[special_graphic].anim_delay_fixed +
7596 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7597 player->post_delay_counter =
7598 graphic_info[special_graphic].post_delay_fixed +
7599 SimpleRND(graphic_info[special_graphic].post_delay_random);
7601 player->special_action_bored = special_action;
7604 if (player->anim_delay_counter > 0)
7606 player->action_waiting = player->special_action_bored;
7607 player->anim_delay_counter--;
7609 else if (player->post_delay_counter > 0)
7611 player->post_delay_counter--;
7616 else if (last_waiting) /* waiting -> not waiting */
7618 player->is_waiting = FALSE;
7619 player->is_bored = FALSE;
7620 player->is_sleeping = FALSE;
7622 player->frame_counter_bored = -1;
7623 player->frame_counter_sleeping = -1;
7625 player->anim_delay_counter = 0;
7626 player->post_delay_counter = 0;
7628 player->action_waiting = ACTION_DEFAULT;
7630 player->special_action_bored = ACTION_DEFAULT;
7631 player->special_action_sleeping = ACTION_DEFAULT;
7635 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7637 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7638 int left = player_action & JOY_LEFT;
7639 int right = player_action & JOY_RIGHT;
7640 int up = player_action & JOY_UP;
7641 int down = player_action & JOY_DOWN;
7642 int button1 = player_action & JOY_BUTTON_1;
7643 int button2 = player_action & JOY_BUTTON_2;
7644 int dx = (left ? -1 : right ? 1 : 0);
7645 int dy = (up ? -1 : down ? 1 : 0);
7647 if (!player->active || tape.pausing)
7653 snapped = SnapField(player, dx, dy);
7657 dropped = DropElement(player);
7659 moved = MovePlayer(player, dx, dy);
7662 if (tape.single_step && tape.recording && !tape.pausing)
7664 if (button1 || (dropped && !moved))
7666 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7667 SnapField(player, 0, 0); /* stop snapping */
7671 SetPlayerWaiting(player, FALSE);
7673 return player_action;
7677 /* no actions for this player (no input at player's configured device) */
7679 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7680 SnapField(player, 0, 0);
7681 CheckGravityMovementWhenNotMoving(player);
7683 if (player->MovPos == 0)
7684 SetPlayerWaiting(player, TRUE);
7686 if (player->MovPos == 0) /* needed for tape.playing */
7687 player->is_moving = FALSE;
7689 player->is_dropping = FALSE;
7695 void AdvanceFrameAndPlayerCounters(int player_nr)
7699 /* advance frame counters (global frame counter and time frame counter) */
7703 /* advance player counters (counters for move delay, move animation etc.) */
7704 for (i = 0; i < MAX_PLAYERS; i++)
7706 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
7707 int move_delay_value = stored_player[i].move_delay_value;
7708 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
7710 if (!advance_player_counters) /* not all players may be affected */
7713 #if USE_NEW_PLAYER_ANIM
7714 if (move_frames == 0) /* less than one move per game frame */
7716 int stepsize = TILEX / move_delay_value;
7717 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
7718 int count = (stored_player[i].is_moving ?
7719 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
7721 if (count % delay == 0)
7726 stored_player[i].Frame += move_frames;
7728 if (stored_player[i].MovPos != 0)
7729 stored_player[i].StepFrame += move_frames;
7731 if (stored_player[i].move_delay > 0)
7732 stored_player[i].move_delay--;
7734 /* due to bugs in previous versions, counter must count up, not down */
7735 if (stored_player[i].push_delay != -1)
7736 stored_player[i].push_delay++;
7738 if (stored_player[i].drop_delay > 0)
7739 stored_player[i].drop_delay--;
7745 static unsigned long game_frame_delay = 0;
7746 unsigned long game_frame_delay_value;
7747 int magic_wall_x = 0, magic_wall_y = 0;
7748 int i, x, y, element, graphic;
7749 byte *recorded_player_action;
7750 byte summarized_player_action = 0;
7751 byte tape_action[MAX_PLAYERS];
7753 if (game_status != GAME_MODE_PLAYING)
7756 game_frame_delay_value =
7757 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7759 if (tape.playing && tape.warp_forward && !tape.pausing)
7760 game_frame_delay_value = 0;
7762 /* ---------- main game synchronization point ---------- */
7764 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
7766 if (network_playing && !network_player_action_received)
7768 /* try to get network player actions in time */
7770 #if defined(NETWORK_AVALIABLE)
7771 /* last chance to get network player actions without main loop delay */
7775 /* game was quit by network peer */
7776 if (game_status != GAME_MODE_PLAYING)
7779 if (!network_player_action_received)
7780 return; /* failed to get network player actions in time */
7786 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7789 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
7790 if (recorded_player_action == NULL && tape.pausing)
7794 for (i = 0; i < MAX_PLAYERS; i++)
7796 summarized_player_action |= stored_player[i].action;
7798 if (!network_playing)
7799 stored_player[i].effective_action = stored_player[i].action;
7802 #if defined(NETWORK_AVALIABLE)
7803 if (network_playing)
7804 SendToServer_MovePlayer(summarized_player_action);
7807 if (!options.network && !setup.team_mode)
7808 local_player->effective_action = summarized_player_action;
7810 if (recorded_player_action != NULL)
7811 for (i = 0; i < MAX_PLAYERS; i++)
7812 stored_player[i].effective_action = recorded_player_action[i];
7814 for (i = 0; i < MAX_PLAYERS; i++)
7816 tape_action[i] = stored_player[i].effective_action;
7818 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7819 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7822 /* only save actions from input devices, but not programmed actions */
7824 TapeRecordAction(tape_action);
7826 for (i = 0; i < MAX_PLAYERS; i++)
7828 int actual_player_action = stored_player[i].effective_action;
7831 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
7832 - rnd_equinox_tetrachloride 048
7833 - rnd_equinox_tetrachloride_ii 096
7834 - rnd_emanuel_schmieg 002
7835 - doctor_sloan_ww 001, 020
7837 if (stored_player[i].MovPos == 0)
7838 CheckGravityMovement(&stored_player[i]);
7841 /* overwrite programmed action with tape action */
7842 if (stored_player[i].programmed_action)
7843 actual_player_action = stored_player[i].programmed_action;
7846 PlayerActions(&stored_player[i], actual_player_action);
7848 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7850 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7851 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7854 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7857 network_player_action_received = FALSE;
7859 ScrollScreen(NULL, SCROLL_GO_ON);
7861 /* for backwards compatibility, the following code emulates a fixed bug that
7862 occured when pushing elements (causing elements that just made their last
7863 pushing step to already (if possible) make their first falling step in the
7864 same game frame, which is bad); this code is also needed to use the famous
7865 "spring push bug" which is used in older levels and might be wanted to be
7866 used also in newer levels, but in this case the buggy pushing code is only
7867 affecting the "spring" element and no other elements */
7869 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7871 for (i = 0; i < MAX_PLAYERS; i++)
7873 struct PlayerInfo *player = &stored_player[i];
7877 if (player->active && player->is_pushing && player->is_moving &&
7879 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7880 Feld[x][y] == EL_SPRING))
7882 ContinueMoving(x, y);
7884 /* continue moving after pushing (this is actually a bug) */
7885 if (!IS_MOVING(x, y))
7893 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7895 Changed[x][y] = FALSE;
7896 ChangeEvent[x][y] = -1;
7898 /* this must be handled before main playfield loop */
7899 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
7902 if (MovDelay[x][y] <= 0)
7906 #if USE_NEW_SNAP_DELAY
7907 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
7910 if (MovDelay[x][y] <= 0)
7913 DrawLevelField(x, y);
7919 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7921 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7922 printf("GameActions(): This should never happen!\n");
7924 ChangePage[x][y] = -1;
7929 if (WasJustMoving[x][y] > 0)
7930 WasJustMoving[x][y]--;
7931 if (WasJustFalling[x][y] > 0)
7932 WasJustFalling[x][y]--;
7933 if (CheckCollision[x][y] > 0)
7934 CheckCollision[x][y]--;
7938 /* reset finished pushing action (not done in ContinueMoving() to allow
7939 continous pushing animation for elements with zero push delay) */
7940 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7942 ResetGfxAnimation(x, y);
7943 DrawLevelField(x, y);
7947 if (IS_BLOCKED(x, y))
7951 Blocked2Moving(x, y, &oldx, &oldy);
7952 if (!IS_MOVING(oldx, oldy))
7954 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7955 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7956 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7957 printf("GameActions(): This should never happen!\n");
7963 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7965 element = Feld[x][y];
7966 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7968 if (graphic_info[graphic].anim_global_sync)
7969 GfxFrame[x][y] = FrameCounter;
7971 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7972 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7973 ResetRandomAnimationValue(x, y);
7975 SetRandomAnimationValue(x, y);
7977 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7979 if (IS_INACTIVE(element))
7981 if (IS_ANIMATED(graphic))
7982 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7987 /* this may take place after moving, so 'element' may have changed */
7988 if (IS_CHANGING(x, y) &&
7989 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7991 int page = element_info[element].event_page_nr[CE_DELAY];
7993 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
7997 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8001 ChangeElement(x, y, page);
8003 if (CAN_CHANGE(element))
8004 ChangeElement(x, y, page);
8006 if (HAS_ACTION(element))
8007 ExecuteCustomElementAction(x, y, element, page);
8012 element = Feld[x][y];
8013 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8016 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8020 element = Feld[x][y];
8021 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8023 if (IS_ANIMATED(graphic) &&
8026 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8028 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8029 EdelsteinFunkeln(x, y);
8031 else if ((element == EL_ACID ||
8032 element == EL_EXIT_OPEN ||
8033 element == EL_SP_EXIT_OPEN ||
8034 element == EL_SP_TERMINAL ||
8035 element == EL_SP_TERMINAL_ACTIVE ||
8036 element == EL_EXTRA_TIME ||
8037 element == EL_SHIELD_NORMAL ||
8038 element == EL_SHIELD_DEADLY) &&
8039 IS_ANIMATED(graphic))
8040 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8041 else if (IS_MOVING(x, y))
8042 ContinueMoving(x, y);
8043 else if (IS_ACTIVE_BOMB(element))
8044 CheckDynamite(x, y);
8045 else if (element == EL_AMOEBA_GROWING)
8046 AmoebeWaechst(x, y);
8047 else if (element == EL_AMOEBA_SHRINKING)
8048 AmoebaDisappearing(x, y);
8050 #if !USE_NEW_AMOEBA_CODE
8051 else if (IS_AMOEBALIVE(element))
8052 AmoebeAbleger(x, y);
8055 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8057 else if (element == EL_EXIT_CLOSED)
8059 else if (element == EL_SP_EXIT_CLOSED)
8061 else if (element == EL_EXPANDABLE_WALL_GROWING)
8063 else if (element == EL_EXPANDABLE_WALL ||
8064 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8065 element == EL_EXPANDABLE_WALL_VERTICAL ||
8066 element == EL_EXPANDABLE_WALL_ANY)
8068 else if (element == EL_FLAMES)
8069 CheckForDragon(x, y);
8070 else if (element == EL_EXPLOSION)
8071 ; /* drawing of correct explosion animation is handled separately */
8072 else if (element == EL_ELEMENT_SNAPPING)
8075 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
8077 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8080 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8081 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8083 if (IS_BELT_ACTIVE(element))
8084 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8086 if (game.magic_wall_active)
8088 int jx = local_player->jx, jy = local_player->jy;
8090 /* play the element sound at the position nearest to the player */
8091 if ((element == EL_MAGIC_WALL_FULL ||
8092 element == EL_MAGIC_WALL_ACTIVE ||
8093 element == EL_MAGIC_WALL_EMPTYING ||
8094 element == EL_BD_MAGIC_WALL_FULL ||
8095 element == EL_BD_MAGIC_WALL_ACTIVE ||
8096 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8097 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8105 #if USE_NEW_AMOEBA_CODE
8106 /* new experimental amoeba growth stuff */
8107 if (!(FrameCounter % 8))
8109 static unsigned long random = 1684108901;
8111 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8113 x = RND(lev_fieldx);
8114 y = RND(lev_fieldy);
8115 element = Feld[x][y];
8117 if (!IS_PLAYER(x,y) &&
8118 (element == EL_EMPTY ||
8119 CAN_GROW_INTO(element) ||
8120 element == EL_QUICKSAND_EMPTY ||
8121 element == EL_ACID_SPLASH_LEFT ||
8122 element == EL_ACID_SPLASH_RIGHT))
8124 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8125 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8126 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8127 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8128 Feld[x][y] = EL_AMOEBA_DROP;
8131 random = random * 129 + 1;
8137 if (game.explosions_delayed)
8140 game.explosions_delayed = FALSE;
8142 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8144 element = Feld[x][y];
8146 if (ExplodeField[x][y])
8147 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8148 else if (element == EL_EXPLOSION)
8149 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8151 ExplodeField[x][y] = EX_TYPE_NONE;
8154 game.explosions_delayed = TRUE;
8157 if (game.magic_wall_active)
8159 if (!(game.magic_wall_time_left % 4))
8161 int element = Feld[magic_wall_x][magic_wall_y];
8163 if (element == EL_BD_MAGIC_WALL_FULL ||
8164 element == EL_BD_MAGIC_WALL_ACTIVE ||
8165 element == EL_BD_MAGIC_WALL_EMPTYING)
8166 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8168 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8171 if (game.magic_wall_time_left > 0)
8173 game.magic_wall_time_left--;
8174 if (!game.magic_wall_time_left)
8176 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8178 element = Feld[x][y];
8180 if (element == EL_MAGIC_WALL_ACTIVE ||
8181 element == EL_MAGIC_WALL_FULL)
8183 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8184 DrawLevelField(x, y);
8186 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8187 element == EL_BD_MAGIC_WALL_FULL)
8189 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8190 DrawLevelField(x, y);
8194 game.magic_wall_active = FALSE;
8199 if (game.light_time_left > 0)
8201 game.light_time_left--;
8203 if (game.light_time_left == 0)
8204 RedrawAllLightSwitchesAndInvisibleElements();
8207 if (game.timegate_time_left > 0)
8209 game.timegate_time_left--;
8211 if (game.timegate_time_left == 0)
8212 CloseAllOpenTimegates();
8215 for (i = 0; i < MAX_PLAYERS; i++)
8217 struct PlayerInfo *player = &stored_player[i];
8219 if (SHIELD_ON(player))
8221 if (player->shield_deadly_time_left)
8222 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8223 else if (player->shield_normal_time_left)
8224 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8228 if (TimeFrames >= FRAMES_PER_SECOND)
8233 for (i = 0; i < MAX_PLAYERS; i++)
8235 struct PlayerInfo *player = &stored_player[i];
8237 if (SHIELD_ON(player))
8239 player->shield_normal_time_left--;
8241 if (player->shield_deadly_time_left > 0)
8242 player->shield_deadly_time_left--;
8246 if (!level.use_step_counter)
8254 if (TimeLeft <= 10 && setup.time_limit)
8255 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8257 DrawGameValue_Time(TimeLeft);
8259 if (!TimeLeft && setup.time_limit)
8260 for (i = 0; i < MAX_PLAYERS; i++)
8261 KillPlayer(&stored_player[i]);
8263 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8264 DrawGameValue_Time(TimePlayed);
8267 if (tape.recording || tape.playing)
8268 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8272 PlayAllPlayersSound();
8274 if (options.debug) /* calculate frames per second */
8276 static unsigned long fps_counter = 0;
8277 static int fps_frames = 0;
8278 unsigned long fps_delay_ms = Counter() - fps_counter;
8282 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8284 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8287 fps_counter = Counter();
8290 redraw_mask |= REDRAW_FPS;
8293 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
8295 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8297 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8299 local_player->show_envelope = 0;
8302 /* use random number generator in every frame to make it less predictable */
8303 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8307 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8309 int min_x = x, min_y = y, max_x = x, max_y = y;
8312 for (i = 0; i < MAX_PLAYERS; i++)
8314 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8316 if (!stored_player[i].active || &stored_player[i] == player)
8319 min_x = MIN(min_x, jx);
8320 min_y = MIN(min_y, jy);
8321 max_x = MAX(max_x, jx);
8322 max_y = MAX(max_y, jy);
8325 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8328 static boolean AllPlayersInVisibleScreen()
8332 for (i = 0; i < MAX_PLAYERS; i++)
8334 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8336 if (!stored_player[i].active)
8339 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8346 void ScrollLevel(int dx, int dy)
8348 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8351 BlitBitmap(drawto_field, drawto_field,
8352 FX + TILEX * (dx == -1) - softscroll_offset,
8353 FY + TILEY * (dy == -1) - softscroll_offset,
8354 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8355 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8356 FX + TILEX * (dx == 1) - softscroll_offset,
8357 FY + TILEY * (dy == 1) - softscroll_offset);
8361 x = (dx == 1 ? BX1 : BX2);
8362 for (y = BY1; y <= BY2; y++)
8363 DrawScreenField(x, y);
8368 y = (dy == 1 ? BY1 : BY2);
8369 for (x = BX1; x <= BX2; x++)
8370 DrawScreenField(x, y);
8373 redraw_mask |= REDRAW_FIELD;
8376 static boolean canFallDown(struct PlayerInfo *player)
8378 int jx = player->jx, jy = player->jy;
8380 return (IN_LEV_FIELD(jx, jy + 1) &&
8381 (IS_FREE(jx, jy + 1) ||
8382 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8383 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8384 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8387 static boolean canPassField(int x, int y, int move_dir)
8389 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8390 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8391 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8394 int element = Feld[x][y];
8396 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8397 !CAN_MOVE(element) &&
8398 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8399 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8400 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8403 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8405 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8406 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8407 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8411 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8412 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
8413 (IS_DIGGABLE(Feld[newx][newy]) ||
8414 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8415 canPassField(newx, newy, move_dir)));
8418 static void CheckGravityMovement(struct PlayerInfo *player)
8420 if (game.gravity && !player->programmed_action)
8422 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8423 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8424 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8425 int jx = player->jx, jy = player->jy;
8426 boolean player_is_moving_to_valid_field =
8427 (!player_is_snapping &&
8428 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8429 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8430 boolean player_can_fall_down = canFallDown(player);
8432 if (player_can_fall_down &&
8433 !player_is_moving_to_valid_field)
8434 player->programmed_action = MV_DOWN;
8438 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8440 return CheckGravityMovement(player);
8442 if (game.gravity && !player->programmed_action)
8444 int jx = player->jx, jy = player->jy;
8445 boolean field_under_player_is_free =
8446 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8447 boolean player_is_standing_on_valid_field =
8448 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8449 (IS_WALKABLE(Feld[jx][jy]) &&
8450 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8452 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8453 player->programmed_action = MV_DOWN;
8459 -----------------------------------------------------------------------------
8460 dx, dy: direction (non-diagonal) to try to move the player to
8461 real_dx, real_dy: direction as read from input device (can be diagonal)
8464 boolean MovePlayerOneStep(struct PlayerInfo *player,
8465 int dx, int dy, int real_dx, int real_dy)
8467 int jx = player->jx, jy = player->jy;
8468 int new_jx = jx + dx, new_jy = jy + dy;
8472 if (!player->active || (!dx && !dy))
8473 return MF_NO_ACTION;
8475 player->MovDir = (dx < 0 ? MV_LEFT :
8478 dy > 0 ? MV_DOWN : MV_NONE);
8480 if (!IN_LEV_FIELD(new_jx, new_jy))
8481 return MF_NO_ACTION;
8483 if (player->cannot_move)
8485 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8486 SnapField(player, 0, 0);
8488 return MF_NO_ACTION;
8491 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8492 return MF_NO_ACTION;
8494 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8496 if (DONT_RUN_INTO(element))
8498 if (element == EL_ACID && dx == 0 && dy == 1)
8500 SplashAcid(new_jx, new_jy);
8501 Feld[jx][jy] = EL_PLAYER_1;
8502 InitMovingField(jx, jy, MV_DOWN);
8503 Store[jx][jy] = EL_ACID;
8504 ContinueMoving(jx, jy);
8508 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
8513 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8514 if (can_move != MF_MOVING)
8517 /* check if DigField() has caused relocation of the player */
8518 if (player->jx != jx || player->jy != jy)
8519 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
8521 StorePlayer[jx][jy] = 0;
8522 player->last_jx = jx;
8523 player->last_jy = jy;
8524 player->jx = new_jx;
8525 player->jy = new_jy;
8526 StorePlayer[new_jx][new_jy] = player->element_nr;
8528 if (player->move_delay_value_next != -1)
8530 player->move_delay_value = player->move_delay_value_next;
8531 player->move_delay_value_next = -1;
8535 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8537 player->step_counter++;
8539 PlayerVisit[jx][jy] = FrameCounter;
8541 ScrollPlayer(player, SCROLL_INIT);
8546 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8548 int jx = player->jx, jy = player->jy;
8549 int old_jx = jx, old_jy = jy;
8550 int moved = MF_NO_ACTION;
8552 if (!player->active)
8557 if (player->MovPos == 0)
8559 player->is_moving = FALSE;
8560 player->is_digging = FALSE;
8561 player->is_collecting = FALSE;
8562 player->is_snapping = FALSE;
8563 player->is_pushing = FALSE;
8569 if (player->move_delay > 0)
8572 player->move_delay = -1; /* set to "uninitialized" value */
8574 /* store if player is automatically moved to next field */
8575 player->is_auto_moving = (player->programmed_action != MV_NONE);
8577 /* remove the last programmed player action */
8578 player->programmed_action = 0;
8582 /* should only happen if pre-1.2 tape recordings are played */
8583 /* this is only for backward compatibility */
8585 int original_move_delay_value = player->move_delay_value;
8588 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8592 /* scroll remaining steps with finest movement resolution */
8593 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8595 while (player->MovPos)
8597 ScrollPlayer(player, SCROLL_GO_ON);
8598 ScrollScreen(NULL, SCROLL_GO_ON);
8600 AdvanceFrameAndPlayerCounters(player->index_nr);
8606 player->move_delay_value = original_move_delay_value;
8609 if (player->last_move_dir & MV_HORIZONTAL)
8611 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8612 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8616 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8617 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8623 if (moved & MF_MOVING && !ScreenMovPos &&
8624 (player == local_player || !options.network))
8626 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8627 int offset = (setup.scroll_delay ? 3 : 0);
8629 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8631 /* actual player has left the screen -- scroll in that direction */
8632 if (jx != old_jx) /* player has moved horizontally */
8633 scroll_x += (jx - old_jx);
8634 else /* player has moved vertically */
8635 scroll_y += (jy - old_jy);
8639 if (jx != old_jx) /* player has moved horizontally */
8641 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8642 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8643 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8645 /* don't scroll over playfield boundaries */
8646 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8647 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8649 /* don't scroll more than one field at a time */
8650 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8652 /* don't scroll against the player's moving direction */
8653 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8654 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8655 scroll_x = old_scroll_x;
8657 else /* player has moved vertically */
8659 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8660 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8661 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8663 /* don't scroll over playfield boundaries */
8664 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8665 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8667 /* don't scroll more than one field at a time */
8668 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8670 /* don't scroll against the player's moving direction */
8671 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8672 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8673 scroll_y = old_scroll_y;
8677 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8679 if (!options.network && !AllPlayersInVisibleScreen())
8681 scroll_x = old_scroll_x;
8682 scroll_y = old_scroll_y;
8686 ScrollScreen(player, SCROLL_INIT);
8687 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8692 player->StepFrame = 0;
8694 if (moved & MF_MOVING)
8696 if (old_jx != jx && old_jy == jy)
8697 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8698 else if (old_jx == jx && old_jy != jy)
8699 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8701 DrawLevelField(jx, jy); /* for "crumbled sand" */
8703 player->last_move_dir = player->MovDir;
8704 player->is_moving = TRUE;
8705 player->is_snapping = FALSE;
8706 player->is_switching = FALSE;
8707 player->is_dropping = FALSE;
8711 CheckGravityMovementWhenNotMoving(player);
8713 player->is_moving = FALSE;
8715 /* at this point, the player is allowed to move, but cannot move right now
8716 (e.g. because of something blocking the way) -- ensure that the player
8717 is also allowed to move in the next frame (in old versions before 3.1.1,
8718 the player was forced to wait again for eight frames before next try) */
8720 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8721 player->move_delay = 0; /* allow direct movement in the next frame */
8724 if (player->move_delay == -1) /* not yet initialized by DigField() */
8725 player->move_delay = player->move_delay_value;
8727 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8729 TestIfPlayerTouchesBadThing(jx, jy);
8730 TestIfPlayerTouchesCustomElement(jx, jy);
8733 if (!player->active)
8734 RemovePlayer(player);
8739 void ScrollPlayer(struct PlayerInfo *player, int mode)
8741 int jx = player->jx, jy = player->jy;
8742 int last_jx = player->last_jx, last_jy = player->last_jy;
8743 int move_stepsize = TILEX / player->move_delay_value;
8745 #if USE_NEW_PLAYER_SPEED
8746 if (!player->active)
8749 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
8752 if (!player->active || player->MovPos == 0)
8756 if (mode == SCROLL_INIT)
8758 player->actual_frame_counter = FrameCounter;
8759 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8761 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
8762 Feld[last_jx][last_jy] == EL_EMPTY)
8764 int last_field_block_delay = 0; /* start with no blocking at all */
8765 int block_delay_adjustment = player->block_delay_adjustment;
8767 /* if player blocks last field, add delay for exactly one move */
8768 if (player->block_last_field)
8770 last_field_block_delay += player->move_delay_value;
8772 /* when blocking enabled, prevent moving up despite gravity */
8773 if (game.gravity && player->MovDir == MV_UP)
8774 block_delay_adjustment = -1;
8777 /* add block delay adjustment (also possible when not blocking) */
8778 last_field_block_delay += block_delay_adjustment;
8780 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8781 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
8784 #if USE_NEW_PLAYER_SPEED
8785 if (player->MovPos != 0) /* player has not yet reached destination */
8791 else if (!FrameReached(&player->actual_frame_counter, 1))
8795 printf("::: player->MovPos: %d -> %d\n",
8797 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
8800 #if USE_NEW_PLAYER_SPEED
8801 if (player->MovPos != 0)
8803 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8804 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8806 /* before DrawPlayer() to draw correct player graphic for this case */
8807 if (player->MovPos == 0)
8808 CheckGravityMovement(player);
8811 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8812 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8814 /* before DrawPlayer() to draw correct player graphic for this case */
8815 if (player->MovPos == 0)
8816 CheckGravityMovement(player);
8819 if (player->MovPos == 0) /* player reached destination field */
8822 printf("::: player reached destination field\n");
8825 if (player->move_delay_reset_counter > 0)
8827 player->move_delay_reset_counter--;
8829 if (player->move_delay_reset_counter == 0)
8831 /* continue with normal speed after quickly moving through gate */
8832 HALVE_PLAYER_SPEED(player);
8834 /* be able to make the next move without delay */
8835 player->move_delay = 0;
8839 player->last_jx = jx;
8840 player->last_jy = jy;
8842 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8843 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8844 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8846 DrawPlayer(player); /* needed here only to cleanup last field */
8847 RemovePlayer(player);
8849 if (local_player->friends_still_needed == 0 ||
8850 IS_SP_ELEMENT(Feld[jx][jy]))
8851 player->LevelSolved = player->GameOver = TRUE;
8854 /* this breaks one level: "machine", level 000 */
8856 int move_direction = player->MovDir;
8857 int enter_side = MV_DIR_OPPOSITE(move_direction);
8858 int leave_side = move_direction;
8859 int old_jx = last_jx;
8860 int old_jy = last_jy;
8861 int old_element = Feld[old_jx][old_jy];
8862 int new_element = Feld[jx][jy];
8864 if (IS_CUSTOM_ELEMENT(old_element))
8865 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
8867 player->index_bit, leave_side);
8869 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
8871 player->index_bit, leave_side);
8873 if (IS_CUSTOM_ELEMENT(new_element))
8874 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
8875 player->index_bit, enter_side);
8877 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
8879 player->index_bit, enter_side);
8882 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8884 TestIfPlayerTouchesBadThing(jx, jy);
8885 TestIfPlayerTouchesCustomElement(jx, jy);
8887 /* needed because pushed element has not yet reached its destination,
8888 so it would trigger a change event at its previous field location */
8889 if (!player->is_pushing)
8890 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8892 if (!player->active)
8893 RemovePlayer(player);
8896 if (level.use_step_counter)
8906 if (TimeLeft <= 10 && setup.time_limit)
8907 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8909 DrawGameValue_Time(TimeLeft);
8911 if (!TimeLeft && setup.time_limit)
8912 for (i = 0; i < MAX_PLAYERS; i++)
8913 KillPlayer(&stored_player[i]);
8915 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8916 DrawGameValue_Time(TimePlayed);
8919 if (tape.single_step && tape.recording && !tape.pausing &&
8920 !player->programmed_action)
8921 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8925 void ScrollScreen(struct PlayerInfo *player, int mode)
8927 static unsigned long screen_frame_counter = 0;
8929 if (mode == SCROLL_INIT)
8931 /* set scrolling step size according to actual player's moving speed */
8932 ScrollStepSize = TILEX / player->move_delay_value;
8934 screen_frame_counter = FrameCounter;
8935 ScreenMovDir = player->MovDir;
8936 ScreenMovPos = player->MovPos;
8937 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8940 else if (!FrameReached(&screen_frame_counter, 1))
8945 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8946 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8947 redraw_mask |= REDRAW_FIELD;
8950 ScreenMovDir = MV_NONE;
8953 void TestIfPlayerTouchesCustomElement(int x, int y)
8955 static int xy[4][2] =
8962 static int trigger_sides[4][2] =
8964 /* center side border side */
8965 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8966 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8967 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8968 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8970 static int touch_dir[4] =
8977 int center_element = Feld[x][y]; /* should always be non-moving! */
8980 for (i = 0; i < NUM_DIRECTIONS; i++)
8982 int xx = x + xy[i][0];
8983 int yy = y + xy[i][1];
8984 int center_side = trigger_sides[i][0];
8985 int border_side = trigger_sides[i][1];
8988 if (!IN_LEV_FIELD(xx, yy))
8991 if (IS_PLAYER(x, y))
8993 struct PlayerInfo *player = PLAYERINFO(x, y);
8995 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8996 border_element = Feld[xx][yy]; /* may be moving! */
8997 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8998 border_element = Feld[xx][yy];
8999 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9000 border_element = MovingOrBlocked2Element(xx, yy);
9002 continue; /* center and border element do not touch */
9004 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9005 player->index_bit, border_side);
9006 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9007 CE_PLAYER_TOUCHES_X,
9008 player->index_bit, border_side);
9010 else if (IS_PLAYER(xx, yy))
9012 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9014 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9016 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9017 continue; /* center and border element do not touch */
9020 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9021 player->index_bit, center_side);
9022 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9023 CE_PLAYER_TOUCHES_X,
9024 player->index_bit, center_side);
9030 void TestIfElementTouchesCustomElement(int x, int y)
9032 static int xy[4][2] =
9039 static int trigger_sides[4][2] =
9041 /* center side border side */
9042 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9043 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9044 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9045 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9047 static int touch_dir[4] =
9054 boolean change_center_element = FALSE;
9055 int center_element = Feld[x][y]; /* should always be non-moving! */
9058 for (i = 0; i < NUM_DIRECTIONS; i++)
9060 int xx = x + xy[i][0];
9061 int yy = y + xy[i][1];
9062 int center_side = trigger_sides[i][0];
9063 int border_side = trigger_sides[i][1];
9066 if (!IN_LEV_FIELD(xx, yy))
9069 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9070 border_element = Feld[xx][yy]; /* may be moving! */
9071 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9072 border_element = Feld[xx][yy];
9073 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9074 border_element = MovingOrBlocked2Element(xx, yy);
9076 continue; /* center and border element do not touch */
9078 /* check for change of center element (but change it only once) */
9079 if (!change_center_element)
9080 change_center_element =
9081 CheckElementChangeBySide(x, y, center_element, border_element,
9082 CE_TOUCHING_X, border_side);
9084 /* check for change of border element */
9085 CheckElementChangeBySide(xx, yy, border_element, center_element,
9086 CE_TOUCHING_X, center_side);
9090 void TestIfElementHitsCustomElement(int x, int y, int direction)
9092 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9093 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9094 int hitx = x + dx, hity = y + dy;
9095 int hitting_element = Feld[x][y];
9096 int touched_element;
9098 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9101 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9102 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9104 if (IN_LEV_FIELD(hitx, hity))
9106 int opposite_direction = MV_DIR_OPPOSITE(direction);
9107 int hitting_side = direction;
9108 int touched_side = opposite_direction;
9109 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9110 MovDir[hitx][hity] != direction ||
9111 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9117 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9118 CE_HITTING_X, touched_side);
9120 CheckElementChangeBySide(hitx, hity, touched_element,
9121 hitting_element, CE_HIT_BY_X, hitting_side);
9123 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9124 CE_HIT_BY_SOMETHING, opposite_direction);
9128 /* "hitting something" is also true when hitting the playfield border */
9129 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9130 CE_HITTING_SOMETHING, direction);
9134 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9136 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9137 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9138 int hitx = x + dx, hity = y + dy;
9139 int hitting_element = Feld[x][y];
9140 int touched_element;
9142 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9143 !IS_FREE(hitx, hity) &&
9144 (!IS_MOVING(hitx, hity) ||
9145 MovDir[hitx][hity] != direction ||
9146 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9149 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9153 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9157 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9158 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9160 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9161 EP_CAN_SMASH_EVERYTHING, direction);
9163 if (IN_LEV_FIELD(hitx, hity))
9165 int opposite_direction = MV_DIR_OPPOSITE(direction);
9166 int hitting_side = direction;
9167 int touched_side = opposite_direction;
9169 int touched_element = MovingOrBlocked2Element(hitx, hity);
9172 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9173 MovDir[hitx][hity] != direction ||
9174 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9183 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9184 CE_SMASHED_BY_SOMETHING, opposite_direction);
9186 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9187 CE_OTHER_IS_SMASHING, touched_side);
9189 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9190 CE_OTHER_GETS_SMASHED, hitting_side);
9196 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9198 int i, kill_x = -1, kill_y = -1;
9199 int bad_element = -1;
9200 static int test_xy[4][2] =
9207 static int test_dir[4] =
9215 for (i = 0; i < NUM_DIRECTIONS; i++)
9217 int test_x, test_y, test_move_dir, test_element;
9219 test_x = good_x + test_xy[i][0];
9220 test_y = good_y + test_xy[i][1];
9222 if (!IN_LEV_FIELD(test_x, test_y))
9226 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9228 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9230 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9231 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9233 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9234 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9238 bad_element = test_element;
9244 if (kill_x != -1 || kill_y != -1)
9246 if (IS_PLAYER(good_x, good_y))
9248 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9250 if (player->shield_deadly_time_left > 0 &&
9251 !IS_INDESTRUCTIBLE(bad_element))
9252 Bang(kill_x, kill_y);
9253 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9257 Bang(good_x, good_y);
9261 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9263 int i, kill_x = -1, kill_y = -1;
9264 int bad_element = Feld[bad_x][bad_y];
9265 static int test_xy[4][2] =
9272 static int touch_dir[4] =
9279 static int test_dir[4] =
9287 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9290 for (i = 0; i < NUM_DIRECTIONS; i++)
9292 int test_x, test_y, test_move_dir, test_element;
9294 test_x = bad_x + test_xy[i][0];
9295 test_y = bad_y + test_xy[i][1];
9296 if (!IN_LEV_FIELD(test_x, test_y))
9300 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9302 test_element = Feld[test_x][test_y];
9304 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9305 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9307 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9308 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9310 /* good thing is player or penguin that does not move away */
9311 if (IS_PLAYER(test_x, test_y))
9313 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9315 if (bad_element == EL_ROBOT && player->is_moving)
9316 continue; /* robot does not kill player if he is moving */
9318 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9320 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9321 continue; /* center and border element do not touch */
9328 else if (test_element == EL_PENGUIN)
9337 if (kill_x != -1 || kill_y != -1)
9339 if (IS_PLAYER(kill_x, kill_y))
9341 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9343 if (player->shield_deadly_time_left > 0 &&
9344 !IS_INDESTRUCTIBLE(bad_element))
9346 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9350 Bang(kill_x, kill_y);
9354 void TestIfPlayerTouchesBadThing(int x, int y)
9356 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9359 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
9361 TestIfGoodThingHitsBadThing(x, y, move_dir);
9364 void TestIfBadThingTouchesPlayer(int x, int y)
9366 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9369 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
9371 TestIfBadThingHitsGoodThing(x, y, move_dir);
9374 void TestIfFriendTouchesBadThing(int x, int y)
9376 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9379 void TestIfBadThingTouchesFriend(int x, int y)
9381 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9384 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9386 int i, kill_x = bad_x, kill_y = bad_y;
9387 static int xy[4][2] =
9395 for (i = 0; i < NUM_DIRECTIONS; i++)
9399 x = bad_x + xy[i][0];
9400 y = bad_y + xy[i][1];
9401 if (!IN_LEV_FIELD(x, y))
9404 element = Feld[x][y];
9405 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9406 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9414 if (kill_x != bad_x || kill_y != bad_y)
9418 void KillPlayer(struct PlayerInfo *player)
9420 int jx = player->jx, jy = player->jy;
9422 if (!player->active)
9425 /* remove accessible field at the player's position */
9426 Feld[jx][jy] = EL_EMPTY;
9428 /* deactivate shield (else Bang()/Explode() would not work right) */
9429 player->shield_normal_time_left = 0;
9430 player->shield_deadly_time_left = 0;
9436 static void KillPlayerUnlessEnemyProtected(int x, int y)
9438 if (!PLAYER_ENEMY_PROTECTED(x, y))
9439 KillPlayer(PLAYERINFO(x, y));
9442 static void KillPlayerUnlessExplosionProtected(int x, int y)
9444 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9445 KillPlayer(PLAYERINFO(x, y));
9448 void BuryPlayer(struct PlayerInfo *player)
9450 int jx = player->jx, jy = player->jy;
9452 if (!player->active)
9455 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9456 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9458 player->GameOver = TRUE;
9459 RemovePlayer(player);
9462 void RemovePlayer(struct PlayerInfo *player)
9464 int jx = player->jx, jy = player->jy;
9465 int i, found = FALSE;
9467 player->present = FALSE;
9468 player->active = FALSE;
9470 if (!ExplodeField[jx][jy])
9471 StorePlayer[jx][jy] = 0;
9473 if (player->is_moving)
9474 DrawLevelField(player->last_jx, player->last_jy);
9476 for (i = 0; i < MAX_PLAYERS; i++)
9477 if (stored_player[i].active)
9481 AllPlayersGone = TRUE;
9487 #if USE_NEW_SNAP_DELAY
9488 static void setFieldForSnapping(int x, int y, int element, int direction)
9490 struct ElementInfo *ei = &element_info[element];
9491 int direction_bit = MV_DIR_BIT(direction);
9492 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
9493 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
9494 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
9496 Feld[x][y] = EL_ELEMENT_SNAPPING;
9497 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
9499 ResetGfxAnimation(x, y);
9501 GfxElement[x][y] = element;
9502 GfxAction[x][y] = action;
9503 GfxDir[x][y] = direction;
9504 GfxFrame[x][y] = -1;
9509 =============================================================================
9510 checkDiagonalPushing()
9511 -----------------------------------------------------------------------------
9512 check if diagonal input device direction results in pushing of object
9513 (by checking if the alternative direction is walkable, diggable, ...)
9514 =============================================================================
9517 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9518 int x, int y, int real_dx, int real_dy)
9520 int jx, jy, dx, dy, xx, yy;
9522 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9525 /* diagonal direction: check alternative direction */
9530 xx = jx + (dx == 0 ? real_dx : 0);
9531 yy = jy + (dy == 0 ? real_dy : 0);
9533 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9537 =============================================================================
9539 -----------------------------------------------------------------------------
9540 x, y: field next to player (non-diagonal) to try to dig to
9541 real_dx, real_dy: direction as read from input device (can be diagonal)
9542 =============================================================================
9545 int DigField(struct PlayerInfo *player,
9546 int oldx, int oldy, int x, int y,
9547 int real_dx, int real_dy, int mode)
9549 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
9550 boolean player_was_pushing = player->is_pushing;
9551 int jx = oldx, jy = oldy;
9552 int dx = x - jx, dy = y - jy;
9553 int nextx = x + dx, nexty = y + dy;
9554 int move_direction = (dx == -1 ? MV_LEFT :
9555 dx == +1 ? MV_RIGHT :
9557 dy == +1 ? MV_DOWN : MV_NONE);
9558 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9559 int dig_side = MV_DIR_OPPOSITE(move_direction);
9560 int old_element = Feld[jx][jy];
9564 if (is_player) /* function can also be called by EL_PENGUIN */
9566 if (player->MovPos == 0)
9568 player->is_digging = FALSE;
9569 player->is_collecting = FALSE;
9572 if (player->MovPos == 0) /* last pushing move finished */
9573 player->is_pushing = FALSE;
9575 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9577 player->is_switching = FALSE;
9578 player->push_delay = -1;
9580 return MF_NO_ACTION;
9584 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9585 return MF_NO_ACTION;
9587 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9588 old_element = Back[jx][jy];
9590 /* in case of element dropped at player position, check background */
9591 else if (Back[jx][jy] != EL_EMPTY &&
9592 game.engine_version >= VERSION_IDENT(2,2,0,0))
9593 old_element = Back[jx][jy];
9595 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
9596 return MF_NO_ACTION; /* field has no opening in this direction */
9598 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
9599 return MF_NO_ACTION; /* field has no opening in this direction */
9601 element = Feld[x][y];
9602 #if USE_NEW_CUSTOM_VALUE
9605 collect_count = element_info[element].collect_count_initial;
9607 collect_count = CustomValue[x][y];
9611 collect_count = element_info[element].collect_count_initial;
9615 if (element != EL_BLOCKED &&
9616 CustomValue[x][y] != element_info[element].collect_count_initial)
9617 printf("::: %d: %d != %d\n",
9620 element_info[element].collect_count_initial);
9623 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
9624 return MF_NO_ACTION;
9626 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9627 game.engine_version >= VERSION_IDENT(2,2,0,0))
9628 return MF_NO_ACTION;
9630 if (game.gravity && is_player && !player->is_auto_moving &&
9631 canFallDown(player) && move_direction != MV_DOWN &&
9632 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
9633 return MF_NO_ACTION; /* player cannot walk here due to gravity */
9635 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
9637 int sound_element = SND_ELEMENT(element);
9638 int sound_action = ACTION_WALKING;
9640 if (IS_RND_GATE(element))
9642 if (!player->key[RND_GATE_NR(element)])
9643 return MF_NO_ACTION;
9645 else if (IS_RND_GATE_GRAY(element))
9647 if (!player->key[RND_GATE_GRAY_NR(element)])
9648 return MF_NO_ACTION;
9650 else if (element == EL_EXIT_OPEN ||
9651 element == EL_SP_EXIT_OPEN ||
9652 element == EL_SP_EXIT_OPENING)
9654 sound_action = ACTION_PASSING; /* player is passing exit */
9656 else if (element == EL_EMPTY)
9658 sound_action = ACTION_MOVING; /* nothing to walk on */
9661 /* play sound from background or player, whatever is available */
9662 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
9663 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
9665 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9667 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
9669 if (!ACCESS_FROM(element, opposite_direction))
9670 return MF_NO_ACTION; /* field not accessible from this direction */
9672 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9673 return MF_NO_ACTION;
9675 if (IS_EM_GATE(element))
9677 if (!player->key[EM_GATE_NR(element)])
9678 return MF_NO_ACTION;
9680 else if (IS_EM_GATE_GRAY(element))
9682 if (!player->key[EM_GATE_GRAY_NR(element)])
9683 return MF_NO_ACTION;
9685 else if (IS_SP_PORT(element))
9687 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9688 element == EL_SP_GRAVITY_PORT_RIGHT ||
9689 element == EL_SP_GRAVITY_PORT_UP ||
9690 element == EL_SP_GRAVITY_PORT_DOWN)
9691 game.gravity = !game.gravity;
9692 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
9693 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
9694 element == EL_SP_GRAVITY_ON_PORT_UP ||
9695 element == EL_SP_GRAVITY_ON_PORT_DOWN)
9696 game.gravity = TRUE;
9697 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
9698 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
9699 element == EL_SP_GRAVITY_OFF_PORT_UP ||
9700 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
9701 game.gravity = FALSE;
9704 /* automatically move to the next field with double speed */
9705 player->programmed_action = move_direction;
9707 if (player->move_delay_reset_counter == 0)
9709 player->move_delay_reset_counter = 2; /* two double speed steps */
9711 DOUBLE_PLAYER_SPEED(player);
9714 PlayLevelSoundAction(x, y, ACTION_PASSING);
9716 else if (IS_DIGGABLE(element))
9720 if (mode != DF_SNAP)
9722 GfxElement[x][y] = GFX_ELEMENT(element);
9723 player->is_digging = TRUE;
9726 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9728 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
9729 player->index_bit, dig_side);
9731 if (mode == DF_SNAP)
9733 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9735 #if USE_NEW_SNAP_DELAY
9736 if (level.block_snap_field)
9737 setFieldForSnapping(x, y, element, move_direction);
9741 else if (IS_COLLECTIBLE(element))
9745 if (is_player && mode != DF_SNAP)
9747 GfxElement[x][y] = element;
9748 player->is_collecting = TRUE;
9751 if (element == EL_SPEED_PILL)
9753 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9755 else if (element == EL_EXTRA_TIME && level.time > 0)
9757 TimeLeft += level.extra_time;
9758 DrawGameValue_Time(TimeLeft);
9760 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9762 player->shield_normal_time_left += level.shield_normal_time;
9763 if (element == EL_SHIELD_DEADLY)
9764 player->shield_deadly_time_left += level.shield_deadly_time;
9766 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9768 if (player->inventory_size < MAX_INVENTORY_SIZE)
9769 player->inventory_element[player->inventory_size++] = element;
9771 DrawGameValue_Dynamite(local_player->inventory_size);
9773 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9775 player->dynabomb_count++;
9776 player->dynabombs_left++;
9778 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9780 player->dynabomb_size++;
9782 else if (element == EL_DYNABOMB_INCREASE_POWER)
9784 player->dynabomb_xl = TRUE;
9786 else if (IS_KEY(element))
9788 player->key[KEY_NR(element)] = TRUE;
9790 DrawGameValue_Keys(player->key);
9792 redraw_mask |= REDRAW_DOOR_1;
9794 else if (IS_ENVELOPE(element))
9796 player->show_envelope = element;
9798 else if (IS_DROPPABLE(element) ||
9799 IS_THROWABLE(element)) /* can be collected and dropped */
9803 if (collect_count == 0)
9804 player->inventory_infinite_element = element;
9806 for (i = 0; i < collect_count; i++)
9807 if (player->inventory_size < MAX_INVENTORY_SIZE)
9808 player->inventory_element[player->inventory_size++] = element;
9810 DrawGameValue_Dynamite(local_player->inventory_size);
9812 else if (collect_count > 0)
9814 local_player->gems_still_needed -= collect_count;
9815 if (local_player->gems_still_needed < 0)
9816 local_player->gems_still_needed = 0;
9818 DrawGameValue_Emeralds(local_player->gems_still_needed);
9821 RaiseScoreElement(element);
9822 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9825 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
9826 player->index_bit, dig_side);
9828 if (mode == DF_SNAP)
9830 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9832 #if USE_NEW_SNAP_DELAY
9833 if (level.block_snap_field)
9834 setFieldForSnapping(x, y, element, move_direction);
9838 else if (IS_PUSHABLE(element))
9840 if (mode == DF_SNAP && element != EL_BD_ROCK)
9841 return MF_NO_ACTION;
9843 if (CAN_FALL(element) && dy)
9844 return MF_NO_ACTION;
9846 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9847 !(element == EL_SPRING && level.use_spring_bug))
9848 return MF_NO_ACTION;
9850 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9851 ((move_direction & MV_VERTICAL &&
9852 ((element_info[element].move_pattern & MV_LEFT &&
9853 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9854 (element_info[element].move_pattern & MV_RIGHT &&
9855 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9856 (move_direction & MV_HORIZONTAL &&
9857 ((element_info[element].move_pattern & MV_UP &&
9858 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9859 (element_info[element].move_pattern & MV_DOWN &&
9860 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9861 return MF_NO_ACTION;
9863 /* do not push elements already moving away faster than player */
9864 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9865 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9866 return MF_NO_ACTION;
9868 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9870 if (player->push_delay_value == -1 || !player_was_pushing)
9871 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9873 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9875 if (player->push_delay_value == -1)
9876 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9878 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9880 if (!player->is_pushing)
9881 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9884 player->is_pushing = TRUE;
9886 if (!(IN_LEV_FIELD(nextx, nexty) &&
9887 (IS_FREE(nextx, nexty) ||
9888 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9889 IS_SB_ELEMENT(element)))))
9890 return MF_NO_ACTION;
9892 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9893 return MF_NO_ACTION;
9895 if (player->push_delay == -1) /* new pushing; restart delay */
9896 player->push_delay = 0;
9898 if (player->push_delay < player->push_delay_value &&
9899 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9900 element != EL_SPRING && element != EL_BALLOON)
9902 /* make sure that there is no move delay before next try to push */
9903 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9904 player->move_delay = 0;
9906 return MF_NO_ACTION;
9909 if (IS_SB_ELEMENT(element))
9911 if (element == EL_SOKOBAN_FIELD_FULL)
9913 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9914 local_player->sokobanfields_still_needed++;
9917 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9919 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9920 local_player->sokobanfields_still_needed--;
9923 Feld[x][y] = EL_SOKOBAN_OBJECT;
9925 if (Back[x][y] == Back[nextx][nexty])
9926 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9927 else if (Back[x][y] != 0)
9928 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9931 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9934 if (local_player->sokobanfields_still_needed == 0 &&
9935 game.emulation == EMU_SOKOBAN)
9937 player->LevelSolved = player->GameOver = TRUE;
9938 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9942 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9944 InitMovingField(x, y, move_direction);
9945 GfxAction[x][y] = ACTION_PUSHING;
9947 if (mode == DF_SNAP)
9948 ContinueMoving(x, y);
9950 MovPos[x][y] = (dx != 0 ? dx : dy);
9952 Pushed[x][y] = TRUE;
9953 Pushed[nextx][nexty] = TRUE;
9955 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9956 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9958 player->push_delay_value = -1; /* get new value later */
9960 /* check for element change _after_ element has been pushed */
9961 if (game.use_change_when_pushing_bug)
9963 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
9964 player->index_bit, dig_side);
9965 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
9966 player->index_bit, dig_side);
9969 else if (IS_SWITCHABLE(element))
9971 if (PLAYER_SWITCHING(player, x, y))
9973 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
9974 player->index_bit, dig_side);
9979 player->is_switching = TRUE;
9980 player->switch_x = x;
9981 player->switch_y = y;
9983 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9985 if (element == EL_ROBOT_WHEEL)
9987 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9991 DrawLevelField(x, y);
9993 else if (element == EL_SP_TERMINAL)
9997 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9999 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10001 else if (Feld[xx][yy] == EL_SP_TERMINAL)
10002 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10005 else if (IS_BELT_SWITCH(element))
10007 ToggleBeltSwitch(x, y);
10009 else if (element == EL_SWITCHGATE_SWITCH_UP ||
10010 element == EL_SWITCHGATE_SWITCH_DOWN)
10012 ToggleSwitchgateSwitch(x, y);
10014 else if (element == EL_LIGHT_SWITCH ||
10015 element == EL_LIGHT_SWITCH_ACTIVE)
10017 ToggleLightSwitch(x, y);
10019 else if (element == EL_TIMEGATE_SWITCH)
10021 ActivateTimegateSwitch(x, y);
10023 else if (element == EL_BALLOON_SWITCH_LEFT ||
10024 element == EL_BALLOON_SWITCH_RIGHT ||
10025 element == EL_BALLOON_SWITCH_UP ||
10026 element == EL_BALLOON_SWITCH_DOWN ||
10027 element == EL_BALLOON_SWITCH_NONE ||
10028 element == EL_BALLOON_SWITCH_ANY)
10030 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10031 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10032 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10033 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10034 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
10037 else if (element == EL_LAMP)
10039 Feld[x][y] = EL_LAMP_ACTIVE;
10040 local_player->lights_still_needed--;
10042 ResetGfxAnimation(x, y);
10043 DrawLevelField(x, y);
10045 else if (element == EL_TIME_ORB_FULL)
10047 Feld[x][y] = EL_TIME_ORB_EMPTY;
10049 if (level.time > 0 || level.use_time_orb_bug)
10051 TimeLeft += level.time_orb_time;
10052 DrawGameValue_Time(TimeLeft);
10055 ResetGfxAnimation(x, y);
10056 DrawLevelField(x, y);
10059 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10060 player->index_bit, dig_side);
10062 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10063 player->index_bit, dig_side);
10069 if (!PLAYER_SWITCHING(player, x, y))
10071 player->is_switching = TRUE;
10072 player->switch_x = x;
10073 player->switch_y = y;
10075 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
10076 player->index_bit, dig_side);
10077 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10078 player->index_bit, dig_side);
10081 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10082 player->index_bit, dig_side);
10083 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10084 player->index_bit, dig_side);
10086 return MF_NO_ACTION;
10089 player->push_delay = -1;
10091 if (is_player) /* function can also be called by EL_PENGUIN */
10093 if (Feld[x][y] != element) /* really digged/collected something */
10094 player->is_collecting = !player->is_digging;
10100 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10102 int jx = player->jx, jy = player->jy;
10103 int x = jx + dx, y = jy + dy;
10104 int snap_direction = (dx == -1 ? MV_LEFT :
10105 dx == +1 ? MV_RIGHT :
10107 dy == +1 ? MV_DOWN : MV_NONE);
10109 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
10112 if (!player->active || !IN_LEV_FIELD(x, y))
10120 if (player->MovPos == 0)
10121 player->is_pushing = FALSE;
10123 player->is_snapping = FALSE;
10125 if (player->MovPos == 0)
10127 player->is_moving = FALSE;
10128 player->is_digging = FALSE;
10129 player->is_collecting = FALSE;
10135 if (player->is_snapping)
10138 player->MovDir = snap_direction;
10140 if (player->MovPos == 0)
10142 player->is_moving = FALSE;
10143 player->is_digging = FALSE;
10144 player->is_collecting = FALSE;
10147 player->is_dropping = FALSE;
10149 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10152 player->is_snapping = TRUE;
10154 if (player->MovPos == 0)
10156 player->is_moving = FALSE;
10157 player->is_digging = FALSE;
10158 player->is_collecting = FALSE;
10161 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
10162 DrawLevelField(player->last_jx, player->last_jy);
10164 DrawLevelField(x, y);
10169 boolean DropElement(struct PlayerInfo *player)
10171 int old_element, new_element;
10172 int dropx = player->jx, dropy = player->jy;
10173 int drop_direction = player->MovDir;
10174 int drop_side = drop_direction;
10175 int drop_element = (player->inventory_size > 0 ?
10176 player->inventory_element[player->inventory_size - 1] :
10177 player->inventory_infinite_element != EL_UNDEFINED ?
10178 player->inventory_infinite_element :
10179 player->dynabombs_left > 0 ?
10180 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10183 /* do not drop an element on top of another element; when holding drop key
10184 pressed without moving, dropped element must move away before the next
10185 element can be dropped (this is especially important if the next element
10186 is dynamite, which can be placed on background for historical reasons) */
10187 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
10190 if (IS_THROWABLE(drop_element))
10192 dropx += GET_DX_FROM_DIR(drop_direction);
10193 dropy += GET_DY_FROM_DIR(drop_direction);
10195 if (!IN_LEV_FIELD(dropx, dropy))
10199 old_element = Feld[dropx][dropy]; /* old element at dropping position */
10200 new_element = drop_element; /* default: no change when dropping */
10202 /* check if player is active, not moving and ready to drop */
10203 if (!player->active || player->MovPos || player->drop_delay > 0)
10206 /* check if player has anything that can be dropped */
10207 if (new_element == EL_UNDEFINED)
10210 /* check if anything can be dropped at the current position */
10211 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10214 /* collected custom elements can only be dropped on empty fields */
10215 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10218 if (old_element != EL_EMPTY)
10219 Back[dropx][dropy] = old_element; /* store old element on this field */
10221 ResetGfxAnimation(dropx, dropy);
10222 ResetRandomAnimationValue(dropx, dropy);
10224 if (player->inventory_size > 0 ||
10225 player->inventory_infinite_element != EL_UNDEFINED)
10227 if (player->inventory_size > 0)
10229 player->inventory_size--;
10231 DrawGameValue_Dynamite(local_player->inventory_size);
10233 if (new_element == EL_DYNAMITE)
10234 new_element = EL_DYNAMITE_ACTIVE;
10235 else if (new_element == EL_SP_DISK_RED)
10236 new_element = EL_SP_DISK_RED_ACTIVE;
10239 Feld[dropx][dropy] = new_element;
10241 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10242 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10243 el2img(Feld[dropx][dropy]), 0);
10245 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10247 /* needed if previous element just changed to "empty" in the last frame */
10248 Changed[dropx][dropy] = FALSE; /* allow another change */
10250 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
10251 player->index_bit, drop_side);
10252 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
10254 player->index_bit, drop_side);
10256 TestIfElementTouchesCustomElement(dropx, dropy);
10258 else /* player is dropping a dyna bomb */
10260 player->dynabombs_left--;
10262 Feld[dropx][dropy] = new_element;
10264 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10265 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10266 el2img(Feld[dropx][dropy]), 0);
10268 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10271 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
10272 InitField_WithBug1(dropx, dropy, FALSE);
10274 new_element = Feld[dropx][dropy]; /* element might have changed */
10276 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10277 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10279 int move_direction, nextx, nexty;
10281 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10282 MovDir[dropx][dropy] = drop_direction;
10284 move_direction = MovDir[dropx][dropy];
10285 nextx = dropx + GET_DX_FROM_DIR(move_direction);
10286 nexty = dropy + GET_DY_FROM_DIR(move_direction);
10288 Changed[dropx][dropy] = FALSE; /* allow another change */
10289 CheckCollision[dropx][dropy] = 2;
10292 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
10293 player->is_dropping = TRUE;
10295 player->drop_x = dropx;
10296 player->drop_y = dropy;
10301 /* ------------------------------------------------------------------------- */
10302 /* game sound playing functions */
10303 /* ------------------------------------------------------------------------- */
10305 static int *loop_sound_frame = NULL;
10306 static int *loop_sound_volume = NULL;
10308 void InitPlayLevelSound()
10310 int num_sounds = getSoundListSize();
10312 checked_free(loop_sound_frame);
10313 checked_free(loop_sound_volume);
10315 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10316 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10319 static void PlayLevelSound(int x, int y, int nr)
10321 int sx = SCREENX(x), sy = SCREENY(y);
10322 int volume, stereo_position;
10323 int max_distance = 8;
10324 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10326 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10327 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10330 if (!IN_LEV_FIELD(x, y) ||
10331 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10332 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10335 volume = SOUND_MAX_VOLUME;
10337 if (!IN_SCR_FIELD(sx, sy))
10339 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10340 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10342 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10345 stereo_position = (SOUND_MAX_LEFT +
10346 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10347 (SCR_FIELDX + 2 * max_distance));
10349 if (IS_LOOP_SOUND(nr))
10351 /* This assures that quieter loop sounds do not overwrite louder ones,
10352 while restarting sound volume comparison with each new game frame. */
10354 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10357 loop_sound_volume[nr] = volume;
10358 loop_sound_frame[nr] = FrameCounter;
10361 PlaySoundExt(nr, volume, stereo_position, type);
10364 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10366 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10367 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10368 y < LEVELY(BY1) ? LEVELY(BY1) :
10369 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10373 static void PlayLevelSoundAction(int x, int y, int action)
10375 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10378 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10380 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10382 if (sound_effect != SND_UNDEFINED)
10383 PlayLevelSound(x, y, sound_effect);
10386 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10389 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10391 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10392 PlayLevelSound(x, y, sound_effect);
10395 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10397 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10399 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10400 PlayLevelSound(x, y, sound_effect);
10403 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10405 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10407 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10408 StopSound(sound_effect);
10411 static void PlayLevelMusic()
10413 if (levelset.music[level_nr] != MUS_UNDEFINED)
10414 PlayMusic(levelset.music[level_nr]); /* from config file */
10416 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10419 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
10421 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
10426 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
10430 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10434 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10438 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10442 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10446 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10450 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10453 case SAMPLE_android_clone:
10454 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10457 case SAMPLE_android_move:
10458 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10461 case SAMPLE_spring:
10462 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10466 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
10470 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
10473 case SAMPLE_eater_eat:
10474 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10478 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10481 case SAMPLE_collect:
10482 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10485 case SAMPLE_diamond:
10486 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10489 case SAMPLE_squash:
10490 /* !!! CHECK THIS !!! */
10492 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10494 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
10498 case SAMPLE_wonderfall:
10499 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
10503 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10507 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10511 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10515 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
10519 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10523 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
10526 case SAMPLE_wonder:
10527 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10531 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10534 case SAMPLE_exit_open:
10535 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
10538 case SAMPLE_exit_leave:
10539 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10542 case SAMPLE_dynamite:
10543 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10547 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10551 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10555 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10559 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
10563 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
10567 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10571 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
10576 void RaiseScore(int value)
10578 local_player->score += value;
10580 DrawGameValue_Score(local_player->score);
10583 void RaiseScoreElement(int element)
10588 case EL_BD_DIAMOND:
10589 case EL_EMERALD_YELLOW:
10590 case EL_EMERALD_RED:
10591 case EL_EMERALD_PURPLE:
10592 case EL_SP_INFOTRON:
10593 RaiseScore(level.score[SC_EMERALD]);
10596 RaiseScore(level.score[SC_DIAMOND]);
10599 RaiseScore(level.score[SC_CRYSTAL]);
10602 RaiseScore(level.score[SC_PEARL]);
10605 case EL_BD_BUTTERFLY:
10606 case EL_SP_ELECTRON:
10607 RaiseScore(level.score[SC_BUG]);
10610 case EL_BD_FIREFLY:
10611 case EL_SP_SNIKSNAK:
10612 RaiseScore(level.score[SC_SPACESHIP]);
10615 case EL_DARK_YAMYAM:
10616 RaiseScore(level.score[SC_YAMYAM]);
10619 RaiseScore(level.score[SC_ROBOT]);
10622 RaiseScore(level.score[SC_PACMAN]);
10625 RaiseScore(level.score[SC_NUT]);
10628 case EL_SP_DISK_RED:
10629 case EL_DYNABOMB_INCREASE_NUMBER:
10630 case EL_DYNABOMB_INCREASE_SIZE:
10631 case EL_DYNABOMB_INCREASE_POWER:
10632 RaiseScore(level.score[SC_DYNAMITE]);
10634 case EL_SHIELD_NORMAL:
10635 case EL_SHIELD_DEADLY:
10636 RaiseScore(level.score[SC_SHIELD]);
10638 case EL_EXTRA_TIME:
10639 RaiseScore(level.score[SC_TIME_BONUS]);
10653 RaiseScore(level.score[SC_KEY]);
10656 RaiseScore(element_info[element].collect_score);
10661 void RequestQuitGame(boolean ask_if_really_quit)
10663 if (AllPlayersGone ||
10664 !ask_if_really_quit ||
10665 level_editor_test_game ||
10666 Request("Do you really want to quit the game ?",
10667 REQ_ASK | REQ_STAY_CLOSED))
10669 #if defined(NETWORK_AVALIABLE)
10670 if (options.network)
10671 SendToServer_StopPlaying();
10675 game_status = GAME_MODE_MAIN;
10681 if (tape.playing && tape.deactivate_display)
10682 TapeDeactivateDisplayOff(TRUE);
10684 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10686 if (tape.playing && tape.deactivate_display)
10687 TapeDeactivateDisplayOn();
10692 /* ---------- new game button stuff ---------------------------------------- */
10694 /* graphic position values for game buttons */
10695 #define GAME_BUTTON_XSIZE 30
10696 #define GAME_BUTTON_YSIZE 30
10697 #define GAME_BUTTON_XPOS 5
10698 #define GAME_BUTTON_YPOS 215
10699 #define SOUND_BUTTON_XPOS 5
10700 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10702 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10703 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10704 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10705 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10706 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10707 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10714 } gamebutton_info[NUM_GAME_BUTTONS] =
10717 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10722 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10723 GAME_CTRL_ID_PAUSE,
10727 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10732 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10733 SOUND_CTRL_ID_MUSIC,
10734 "background music on/off"
10737 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10738 SOUND_CTRL_ID_LOOPS,
10739 "sound loops on/off"
10742 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10743 SOUND_CTRL_ID_SIMPLE,
10744 "normal sounds on/off"
10748 void CreateGameButtons()
10752 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10754 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10755 struct GadgetInfo *gi;
10758 unsigned long event_mask;
10759 int gd_xoffset, gd_yoffset;
10760 int gd_x1, gd_x2, gd_y1, gd_y2;
10763 gd_xoffset = gamebutton_info[i].x;
10764 gd_yoffset = gamebutton_info[i].y;
10765 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10766 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10768 if (id == GAME_CTRL_ID_STOP ||
10769 id == GAME_CTRL_ID_PAUSE ||
10770 id == GAME_CTRL_ID_PLAY)
10772 button_type = GD_TYPE_NORMAL_BUTTON;
10774 event_mask = GD_EVENT_RELEASED;
10775 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10776 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10780 button_type = GD_TYPE_CHECK_BUTTON;
10782 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10783 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10784 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10785 event_mask = GD_EVENT_PRESSED;
10786 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10787 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10790 gi = CreateGadget(GDI_CUSTOM_ID, id,
10791 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10792 GDI_X, DX + gd_xoffset,
10793 GDI_Y, DY + gd_yoffset,
10794 GDI_WIDTH, GAME_BUTTON_XSIZE,
10795 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10796 GDI_TYPE, button_type,
10797 GDI_STATE, GD_BUTTON_UNPRESSED,
10798 GDI_CHECKED, checked,
10799 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10800 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10801 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10802 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10803 GDI_EVENT_MASK, event_mask,
10804 GDI_CALLBACK_ACTION, HandleGameButtons,
10808 Error(ERR_EXIT, "cannot create gadget");
10810 game_gadget[id] = gi;
10814 void FreeGameButtons()
10818 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10819 FreeGadget(game_gadget[i]);
10822 static void MapGameButtons()
10826 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10827 MapGadget(game_gadget[i]);
10830 void UnmapGameButtons()
10834 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10835 UnmapGadget(game_gadget[i]);
10838 static void HandleGameButtons(struct GadgetInfo *gi)
10840 int id = gi->custom_id;
10842 if (game_status != GAME_MODE_PLAYING)
10847 case GAME_CTRL_ID_STOP:
10848 RequestQuitGame(TRUE);
10851 case GAME_CTRL_ID_PAUSE:
10852 if (options.network)
10854 #if defined(NETWORK_AVALIABLE)
10856 SendToServer_ContinuePlaying();
10858 SendToServer_PausePlaying();
10862 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10865 case GAME_CTRL_ID_PLAY:
10868 #if defined(NETWORK_AVALIABLE)
10869 if (options.network)
10870 SendToServer_ContinuePlaying();
10874 tape.pausing = FALSE;
10875 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10880 case SOUND_CTRL_ID_MUSIC:
10881 if (setup.sound_music)
10883 setup.sound_music = FALSE;
10886 else if (audio.music_available)
10888 setup.sound = setup.sound_music = TRUE;
10890 SetAudioMode(setup.sound);
10896 case SOUND_CTRL_ID_LOOPS:
10897 if (setup.sound_loops)
10898 setup.sound_loops = FALSE;
10899 else if (audio.loops_available)
10901 setup.sound = setup.sound_loops = TRUE;
10902 SetAudioMode(setup.sound);
10906 case SOUND_CTRL_ID_SIMPLE:
10907 if (setup.sound_simple)
10908 setup.sound_simple = FALSE;
10909 else if (audio.sound_available)
10911 setup.sound = setup.sound_simple = TRUE;
10912 SetAudioMode(setup.sound);