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 GET_VALID_PLAYER_ELEMENT(e) \
139 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
141 #define CAN_GROW_INTO(e) \
142 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
144 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
145 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
148 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
149 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
150 (CAN_MOVE_INTO_ACID(e) && \
151 Feld[x][y] == EL_ACID) || \
154 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
155 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
156 (CAN_MOVE_INTO_ACID(e) && \
157 Feld[x][y] == EL_ACID) || \
160 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
161 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
163 (CAN_MOVE_INTO_ACID(e) && \
164 Feld[x][y] == EL_ACID) || \
165 (DONT_COLLIDE_WITH(e) && \
167 !PLAYER_ENEMY_PROTECTED(x, y))))
169 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
170 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
172 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
173 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
175 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
176 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
178 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
179 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
181 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
182 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
184 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
185 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
187 #define PIG_CAN_ENTER_FIELD(e, x, y) \
188 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
190 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
191 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
192 IS_FOOD_PENGUIN(Feld[x][y])))
193 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
194 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
196 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
197 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
199 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
200 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
202 #define GROUP_NR(e) ((e) - EL_GROUP_START)
203 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
204 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
205 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
207 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
208 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
210 #define CE_ENTER_FIELD_COND(e, x, y) \
211 (!IS_PLAYER(x, y) && \
212 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
214 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
215 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
217 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
218 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
220 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
221 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
222 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
223 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
225 /* game button identifiers */
226 #define GAME_CTRL_ID_STOP 0
227 #define GAME_CTRL_ID_PAUSE 1
228 #define GAME_CTRL_ID_PLAY 2
229 #define SOUND_CTRL_ID_MUSIC 3
230 #define SOUND_CTRL_ID_LOOPS 4
231 #define SOUND_CTRL_ID_SIMPLE 5
233 #define NUM_GAME_BUTTONS 6
236 /* forward declaration for internal use */
238 static void AdvanceFrameAndPlayerCounters(int);
240 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
241 static boolean MovePlayer(struct PlayerInfo *, int, int);
242 static void ScrollPlayer(struct PlayerInfo *, int);
243 static void ScrollScreen(struct PlayerInfo *, int);
245 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
247 static void InitBeltMovement(void);
248 static void CloseAllOpenTimegates(void);
249 static void CheckGravityMovement(struct PlayerInfo *);
250 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
251 static void KillPlayerUnlessEnemyProtected(int, int);
252 static void KillPlayerUnlessExplosionProtected(int, int);
254 static void TestIfPlayerTouchesCustomElement(int, int);
255 static void TestIfElementTouchesCustomElement(int, int);
256 static void TestIfElementHitsCustomElement(int, int, int);
258 static void TestIfElementSmashesCustomElement(int, int, int);
261 static void ChangeElement(int, int, int);
263 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
264 #define CheckTriggeredElementChange(x, y, e, ev) \
265 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
266 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
267 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
268 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
269 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
270 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
271 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
273 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
274 #define CheckElementChange(x, y, e, te, ev) \
275 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
276 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
277 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
278 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
279 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
281 static void PlayLevelSound(int, int, int);
282 static void PlayLevelSoundNearest(int, int, int);
283 static void PlayLevelSoundAction(int, int, int);
284 static void PlayLevelSoundElementAction(int, int, int, int);
285 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
286 static void PlayLevelSoundActionIfLoop(int, int, int);
287 static void StopLevelSoundActionIfLoop(int, int, int);
288 static void PlayLevelMusic();
290 static void MapGameButtons();
291 static void HandleGameButtons(struct GadgetInfo *);
293 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
296 /* ------------------------------------------------------------------------- */
297 /* definition of elements that automatically change to other elements after */
298 /* a specified time, eventually calling a function when changing */
299 /* ------------------------------------------------------------------------- */
301 /* forward declaration for changer functions */
302 static void InitBuggyBase(int x, int y);
303 static void WarnBuggyBase(int x, int y);
305 static void InitTrap(int x, int y);
306 static void ActivateTrap(int x, int y);
307 static void ChangeActiveTrap(int x, int y);
309 static void InitRobotWheel(int x, int y);
310 static void RunRobotWheel(int x, int y);
311 static void StopRobotWheel(int x, int y);
313 static void InitTimegateWheel(int x, int y);
314 static void RunTimegateWheel(int x, int y);
316 struct ChangingElementInfo
321 void (*pre_change_function)(int x, int y);
322 void (*change_function)(int x, int y);
323 void (*post_change_function)(int x, int y);
326 static struct ChangingElementInfo change_delay_list[] =
377 EL_SWITCHGATE_OPENING,
385 EL_SWITCHGATE_CLOSING,
386 EL_SWITCHGATE_CLOSED,
418 EL_ACID_SPLASH_RIGHT,
427 EL_SP_BUGGY_BASE_ACTIVATING,
434 EL_SP_BUGGY_BASE_ACTIVATING,
435 EL_SP_BUGGY_BASE_ACTIVE,
442 EL_SP_BUGGY_BASE_ACTIVE,
466 EL_ROBOT_WHEEL_ACTIVE,
474 EL_TIMEGATE_SWITCH_ACTIVE,
495 int push_delay_fixed, push_delay_random;
500 { EL_BALLOON, 0, 0 },
502 { EL_SOKOBAN_OBJECT, 2, 0 },
503 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
504 { EL_SATELLITE, 2, 0 },
505 { EL_SP_DISK_YELLOW, 2, 0 },
507 { EL_UNDEFINED, 0, 0 },
515 move_stepsize_list[] =
517 { EL_AMOEBA_DROP, 2 },
518 { EL_AMOEBA_DROPPING, 2 },
519 { EL_QUICKSAND_FILLING, 1 },
520 { EL_QUICKSAND_EMPTYING, 1 },
521 { EL_MAGIC_WALL_FILLING, 2 },
522 { EL_BD_MAGIC_WALL_FILLING, 2 },
523 { EL_MAGIC_WALL_EMPTYING, 2 },
524 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
534 collect_count_list[] =
537 { EL_BD_DIAMOND, 1 },
538 { EL_EMERALD_YELLOW, 1 },
539 { EL_EMERALD_RED, 1 },
540 { EL_EMERALD_PURPLE, 1 },
542 { EL_SP_INFOTRON, 1 },
554 access_direction_list[] =
556 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
557 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
558 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
559 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
560 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
561 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
562 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
563 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
564 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
565 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
566 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
568 { EL_SP_PORT_LEFT, MV_RIGHT },
569 { EL_SP_PORT_RIGHT, MV_LEFT },
570 { EL_SP_PORT_UP, MV_DOWN },
571 { EL_SP_PORT_DOWN, MV_UP },
572 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
573 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
574 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
575 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
576 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
577 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
578 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
579 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
580 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
581 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
582 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
583 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
584 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
585 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
586 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
588 { EL_UNDEFINED, MV_NONE }
591 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
593 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
594 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
595 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
596 IS_JUST_CHANGING(x, y))
598 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
601 void GetPlayerConfig()
603 if (!audio.sound_available)
604 setup.sound_simple = FALSE;
606 if (!audio.loops_available)
607 setup.sound_loops = FALSE;
609 if (!audio.music_available)
610 setup.sound_music = FALSE;
612 if (!video.fullscreen_available)
613 setup.fullscreen = FALSE;
615 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
617 SetAudioMode(setup.sound);
621 static int getBeltNrFromBeltElement(int element)
623 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
624 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
625 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
628 static int getBeltNrFromBeltActiveElement(int element)
630 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
631 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
632 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
635 static int getBeltNrFromBeltSwitchElement(int element)
637 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
638 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
639 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
642 static int getBeltDirNrFromBeltSwitchElement(int element)
644 static int belt_base_element[4] =
646 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
647 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
648 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
649 EL_CONVEYOR_BELT_4_SWITCH_LEFT
652 int belt_nr = getBeltNrFromBeltSwitchElement(element);
653 int belt_dir_nr = element - belt_base_element[belt_nr];
655 return (belt_dir_nr % 3);
658 static int getBeltDirFromBeltSwitchElement(int element)
660 static int belt_move_dir[3] =
667 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
669 return belt_move_dir[belt_dir_nr];
672 static void InitPlayerField(int x, int y, int element, boolean init_game)
674 if (element == EL_SP_MURPHY)
678 if (stored_player[0].present)
680 Feld[x][y] = EL_SP_MURPHY_CLONE;
686 stored_player[0].use_murphy_graphic = TRUE;
689 Feld[x][y] = EL_PLAYER_1;
695 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
696 int jx = player->jx, jy = player->jy;
698 player->present = TRUE;
700 player->block_last_field = (element == EL_SP_MURPHY ?
701 level.sp_block_last_field :
702 level.block_last_field);
704 /* ---------- initialize player's last field block delay --------------- */
706 /* always start with reliable default value (no adjustment needed) */
707 player->block_delay_adjustment = 0;
709 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
710 if (player->block_last_field && element == EL_SP_MURPHY)
711 player->block_delay_adjustment = 1;
713 /* special case 2: in game engines before 3.1.1, blocking was different */
714 if (game.use_block_last_field_bug)
715 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
717 if (!options.network || player->connected)
719 player->active = TRUE;
721 /* remove potentially duplicate players */
722 if (StorePlayer[jx][jy] == Feld[x][y])
723 StorePlayer[jx][jy] = 0;
725 StorePlayer[x][y] = Feld[x][y];
729 printf("Player %d activated.\n", player->element_nr);
730 printf("[Local player is %d and currently %s.]\n",
731 local_player->element_nr,
732 local_player->active ? "active" : "not active");
736 Feld[x][y] = EL_EMPTY;
738 player->jx = player->last_jx = x;
739 player->jy = player->last_jy = y;
743 static void InitField(int x, int y, boolean init_game)
745 int element = Feld[x][y];
754 InitPlayerField(x, y, element, init_game);
757 case EL_SOKOBAN_FIELD_PLAYER:
758 element = Feld[x][y] = EL_PLAYER_1;
759 InitField(x, y, init_game);
761 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
762 InitField(x, y, init_game);
765 case EL_SOKOBAN_FIELD_EMPTY:
766 local_player->sokobanfields_still_needed++;
770 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
771 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
772 else if (x > 0 && Feld[x-1][y] == EL_ACID)
773 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
774 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
775 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
776 else if (y > 0 && Feld[x][y-1] == EL_ACID)
777 Feld[x][y] = EL_ACID_POOL_BOTTOM;
778 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
779 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
787 case EL_SPACESHIP_RIGHT:
788 case EL_SPACESHIP_UP:
789 case EL_SPACESHIP_LEFT:
790 case EL_SPACESHIP_DOWN:
792 case EL_BD_BUTTERFLY_RIGHT:
793 case EL_BD_BUTTERFLY_UP:
794 case EL_BD_BUTTERFLY_LEFT:
795 case EL_BD_BUTTERFLY_DOWN:
796 case EL_BD_BUTTERFLY:
797 case EL_BD_FIREFLY_RIGHT:
798 case EL_BD_FIREFLY_UP:
799 case EL_BD_FIREFLY_LEFT:
800 case EL_BD_FIREFLY_DOWN:
802 case EL_PACMAN_RIGHT:
826 if (y == lev_fieldy - 1)
828 Feld[x][y] = EL_AMOEBA_GROWING;
829 Store[x][y] = EL_AMOEBA_WET;
833 case EL_DYNAMITE_ACTIVE:
834 case EL_SP_DISK_RED_ACTIVE:
835 case EL_DYNABOMB_PLAYER_1_ACTIVE:
836 case EL_DYNABOMB_PLAYER_2_ACTIVE:
837 case EL_DYNABOMB_PLAYER_3_ACTIVE:
838 case EL_DYNABOMB_PLAYER_4_ACTIVE:
843 local_player->lights_still_needed++;
847 local_player->friends_still_needed++;
852 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
855 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
856 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
857 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
858 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
859 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
860 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
861 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
862 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
863 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
864 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
865 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
866 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
869 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
870 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
871 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
873 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
875 game.belt_dir[belt_nr] = belt_dir;
876 game.belt_dir_nr[belt_nr] = belt_dir_nr;
878 else /* more than one switch -- set it like the first switch */
880 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
885 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
887 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
890 case EL_LIGHT_SWITCH_ACTIVE:
892 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
897 if (IS_CUSTOM_ELEMENT(element))
899 if (CAN_MOVE(element))
902 #if USE_NEW_CUSTOM_VALUE
903 if (!element_info[element].use_last_ce_value)
904 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
908 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
911 else if (IS_GROUP_ELEMENT(element))
913 struct ElementGroupInfo *group = element_info[element].group;
914 int last_anim_random_frame = gfx.anim_random_frame;
917 if (group->choice_mode == ANIM_RANDOM)
918 gfx.anim_random_frame = RND(group->num_elements_resolved);
920 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
921 group->choice_mode, 0,
924 if (group->choice_mode == ANIM_RANDOM)
925 gfx.anim_random_frame = last_anim_random_frame;
929 Feld[x][y] = group->element_resolved[element_pos];
931 InitField(x, y, init_game);
938 #if USE_NEW_CUSTOM_VALUE
941 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
943 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
951 static inline void InitField_WithBug1(int x, int y, boolean init_game)
953 InitField(x, y, init_game);
955 /* not needed to call InitMovDir() -- already done by InitField()! */
956 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
957 CAN_MOVE(Feld[x][y]))
961 static inline void InitField_WithBug2(int x, int y, boolean init_game)
963 int old_element = Feld[x][y];
965 InitField(x, y, init_game);
967 /* not needed to call InitMovDir() -- already done by InitField()! */
968 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
969 CAN_MOVE(old_element) &&
970 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
973 /* this case is in fact a combination of not less than three bugs:
974 first, it calls InitMovDir() for elements that can move, although this is
975 already done by InitField(); then, it checks the element that was at this
976 field _before_ the call to InitField() (which can change it); lastly, it
977 was not called for "mole with direction" elements, which were treated as
978 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
982 inline void DrawGameValue_Emeralds(int value)
984 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
987 inline void DrawGameValue_Dynamite(int value)
989 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
992 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
996 /* currently only 4 of 8 possible keys are displayed */
997 for (i = 0; i < STD_NUM_KEYS; i++)
1000 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1001 el2edimg(EL_KEY_1 + i));
1003 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1004 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1005 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1009 inline void DrawGameValue_Score(int value)
1011 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1014 inline void DrawGameValue_Time(int value)
1017 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1019 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1022 inline void DrawGameValue_Level(int value)
1025 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1028 /* misuse area for displaying emeralds to draw bigger level number */
1029 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1030 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1032 /* now copy it to the area for displaying level number */
1033 BlitBitmap(drawto, drawto,
1034 DX_EMERALDS, DY_EMERALDS + 1,
1035 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1036 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1037 DX_LEVEL - 1, DY_LEVEL + 1);
1039 /* restore the area for displaying emeralds */
1040 DrawGameValue_Emeralds(local_player->gems_still_needed);
1042 /* yes, this is all really ugly :-) */
1046 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1049 int key[MAX_NUM_KEYS];
1052 for (i = 0; i < MAX_NUM_KEYS; i++)
1053 key[i] = key_bits & (1 << i);
1055 DrawGameValue_Level(level_nr);
1057 DrawGameValue_Emeralds(emeralds);
1058 DrawGameValue_Dynamite(dynamite);
1059 DrawGameValue_Score(score);
1060 DrawGameValue_Time(time);
1062 DrawGameValue_Keys(key);
1065 void DrawGameDoorValues()
1069 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1071 DrawGameDoorValues_EM();
1076 DrawGameValue_Level(level_nr);
1078 DrawGameValue_Emeralds(local_player->gems_still_needed);
1079 DrawGameValue_Dynamite(local_player->inventory_size);
1080 DrawGameValue_Score(local_player->score);
1081 DrawGameValue_Time(TimeLeft);
1083 for (i = 0; i < MAX_PLAYERS; i++)
1084 DrawGameValue_Keys(stored_player[i].key);
1087 static void resolve_group_element(int group_element, int recursion_depth)
1089 static int group_nr;
1090 static struct ElementGroupInfo *group;
1091 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1094 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1096 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1097 group_element - EL_GROUP_START + 1);
1099 /* replace element which caused too deep recursion by question mark */
1100 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1105 if (recursion_depth == 0) /* initialization */
1107 group = element_info[group_element].group;
1108 group_nr = group_element - EL_GROUP_START;
1110 group->num_elements_resolved = 0;
1111 group->choice_pos = 0;
1114 for (i = 0; i < actual_group->num_elements; i++)
1116 int element = actual_group->element[i];
1118 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1121 if (IS_GROUP_ELEMENT(element))
1122 resolve_group_element(element, recursion_depth + 1);
1125 group->element_resolved[group->num_elements_resolved++] = element;
1126 element_info[element].in_group[group_nr] = TRUE;
1133 =============================================================================
1135 -----------------------------------------------------------------------------
1136 initialize game engine due to level / tape version number
1137 =============================================================================
1140 static void InitGameEngine()
1144 /* set game engine from tape file when re-playing, else from level file */
1145 game.engine_version = (tape.playing ? tape.engine_version :
1146 level.game_version);
1148 /* ---------------------------------------------------------------------- */
1149 /* set flags for bugs and changes according to active game engine version */
1150 /* ---------------------------------------------------------------------- */
1153 Summary of bugfix/change:
1154 Fixed handling for custom elements that change when pushed by the player.
1156 Fixed/changed in version:
1160 Before 3.1.0, custom elements that "change when pushing" changed directly
1161 after the player started pushing them (until then handled in "DigField()").
1162 Since 3.1.0, these custom elements are not changed until the "pushing"
1163 move of the element is finished (now handled in "ContinueMoving()").
1165 Affected levels/tapes:
1166 The first condition is generally needed for all levels/tapes before version
1167 3.1.0, which might use the old behaviour before it was changed; known tapes
1168 that are affected are some tapes from the level set "Walpurgis Gardens" by
1170 The second condition is an exception from the above case and is needed for
1171 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1172 above (including some development versions of 3.1.0), but before it was
1173 known that this change would break tapes like the above and was fixed in
1174 3.1.1, so that the changed behaviour was active although the engine version
1175 while recording maybe was before 3.1.0. There is at least one tape that is
1176 affected by this exception, which is the tape for the one-level set "Bug
1177 Machine" by Juergen Bonhagen.
1180 game.use_change_when_pushing_bug =
1181 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1183 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1184 tape.game_version < VERSION_IDENT(3,1,1,0)));
1187 Summary of bugfix/change:
1188 Fixed handling for blocking the field the player leaves when moving.
1190 Fixed/changed in version:
1194 Before 3.1.1, when "block last field when moving" was enabled, the field
1195 the player is leaving when moving was blocked for the time of the move,
1196 and was directly unblocked afterwards. This resulted in the last field
1197 being blocked for exactly one less than the number of frames of one player
1198 move. Additionally, even when blocking was disabled, the last field was
1199 blocked for exactly one frame.
1200 Since 3.1.1, due to changes in player movement handling, the last field
1201 is not blocked at all when blocking is disabled. When blocking is enabled,
1202 the last field is blocked for exactly the number of frames of one player
1203 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1204 last field is blocked for exactly one more than the number of frames of
1207 Affected levels/tapes:
1208 (!!! yet to be determined -- probably many !!!)
1211 game.use_block_last_field_bug =
1212 (game.engine_version < VERSION_IDENT(3,1,1,0));
1214 /* ---------------------------------------------------------------------- */
1216 /* dynamically adjust element properties according to game engine version */
1217 InitElementPropertiesEngine(game.engine_version);
1220 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1221 printf(" tape version == %06d [%s] [file: %06d]\n",
1222 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1224 printf(" => game.engine_version == %06d\n", game.engine_version);
1227 /* ---------- recursively resolve group elements ------------------------- */
1229 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1230 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1231 element_info[i].in_group[j] = FALSE;
1233 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1234 resolve_group_element(EL_GROUP_START + i, 0);
1236 /* ---------- initialize player's initial move delay --------------------- */
1238 /* dynamically adjust player properties according to level information */
1239 game.initial_move_delay_value =
1240 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1242 /* dynamically adjust player properties according to game engine version */
1243 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1244 game.initial_move_delay_value : 0);
1246 /* ---------- initialize player's initial push delay --------------------- */
1248 /* dynamically adjust player properties according to game engine version */
1249 game.initial_push_delay_value =
1250 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1252 /* ---------- initialize changing elements ------------------------------- */
1254 /* initialize changing elements information */
1255 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1257 struct ElementInfo *ei = &element_info[i];
1259 /* this pointer might have been changed in the level editor */
1260 ei->change = &ei->change_page[0];
1262 if (!IS_CUSTOM_ELEMENT(i))
1264 ei->change->target_element = EL_EMPTY_SPACE;
1265 ei->change->delay_fixed = 0;
1266 ei->change->delay_random = 0;
1267 ei->change->delay_frames = 1;
1270 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1272 ei->has_change_event[j] = FALSE;
1274 ei->event_page_nr[j] = 0;
1275 ei->event_page[j] = &ei->change_page[0];
1279 /* add changing elements from pre-defined list */
1280 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1282 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1283 struct ElementInfo *ei = &element_info[ch_delay->element];
1285 ei->change->target_element = ch_delay->target_element;
1286 ei->change->delay_fixed = ch_delay->change_delay;
1288 ei->change->pre_change_function = ch_delay->pre_change_function;
1289 ei->change->change_function = ch_delay->change_function;
1290 ei->change->post_change_function = ch_delay->post_change_function;
1292 ei->change->can_change = TRUE;
1293 ei->change->can_change_or_has_action = TRUE;
1295 ei->has_change_event[CE_DELAY] = TRUE;
1297 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1298 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1301 /* ---------- initialize internal run-time variables ------------- */
1303 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1305 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1307 for (j = 0; j < ei->num_change_pages; j++)
1309 ei->change_page[j].can_change_or_has_action =
1310 (ei->change_page[j].can_change |
1311 ei->change_page[j].has_action);
1315 /* add change events from custom element configuration */
1316 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1318 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1320 for (j = 0; j < ei->num_change_pages; j++)
1322 if (!ei->change_page[j].can_change_or_has_action)
1325 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1327 /* only add event page for the first page found with this event */
1328 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1330 ei->has_change_event[k] = TRUE;
1332 ei->event_page_nr[k] = j;
1333 ei->event_page[k] = &ei->change_page[j];
1339 /* ---------- initialize run-time trigger player and element ------------- */
1341 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1343 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1345 for (j = 0; j < ei->num_change_pages; j++)
1347 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1348 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1349 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1350 ei->change_page[j].actual_trigger_ce_value = 0;
1354 /* ---------- initialize trigger events ---------------------------------- */
1356 /* initialize trigger events information */
1357 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1358 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1359 trigger_events[i][j] = FALSE;
1361 /* add trigger events from element change event properties */
1362 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1364 struct ElementInfo *ei = &element_info[i];
1366 for (j = 0; j < ei->num_change_pages; j++)
1368 if (!ei->change_page[j].can_change_or_has_action)
1371 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1373 int trigger_element = ei->change_page[j].trigger_element;
1375 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1377 if (ei->change_page[j].has_event[k])
1379 if (IS_GROUP_ELEMENT(trigger_element))
1381 struct ElementGroupInfo *group =
1382 element_info[trigger_element].group;
1384 for (l = 0; l < group->num_elements_resolved; l++)
1385 trigger_events[group->element_resolved[l]][k] = TRUE;
1388 trigger_events[trigger_element][k] = TRUE;
1395 /* ---------- initialize push delay -------------------------------------- */
1397 /* initialize push delay values to default */
1398 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1400 if (!IS_CUSTOM_ELEMENT(i))
1402 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1403 element_info[i].push_delay_random = game.default_push_delay_random;
1407 /* set push delay value for certain elements from pre-defined list */
1408 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1410 int e = push_delay_list[i].element;
1412 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1413 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1416 /* set push delay value for Supaplex elements for newer engine versions */
1417 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1419 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1421 if (IS_SP_ELEMENT(i))
1423 /* set SP push delay to just enough to push under a falling zonk */
1424 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1426 element_info[i].push_delay_fixed = delay;
1427 element_info[i].push_delay_random = 0;
1432 /* ---------- initialize move stepsize ----------------------------------- */
1434 /* initialize move stepsize values to default */
1435 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1436 if (!IS_CUSTOM_ELEMENT(i))
1437 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1439 /* set move stepsize value for certain elements from pre-defined list */
1440 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1442 int e = move_stepsize_list[i].element;
1444 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1447 /* ---------- initialize collect score ----------------------------------- */
1449 /* initialize collect score values for custom elements from initial value */
1450 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1451 if (IS_CUSTOM_ELEMENT(i))
1452 element_info[i].collect_score = element_info[i].collect_score_initial;
1454 /* ---------- initialize collect count ----------------------------------- */
1456 /* initialize collect count values for non-custom elements */
1457 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1458 if (!IS_CUSTOM_ELEMENT(i))
1459 element_info[i].collect_count_initial = 0;
1461 /* add collect count values for all elements from pre-defined list */
1462 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1463 element_info[collect_count_list[i].element].collect_count_initial =
1464 collect_count_list[i].count;
1466 /* ---------- initialize access direction -------------------------------- */
1468 /* initialize access direction values to default (access from every side) */
1469 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1470 if (!IS_CUSTOM_ELEMENT(i))
1471 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1473 /* set access direction value for certain elements from pre-defined list */
1474 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1475 element_info[access_direction_list[i].element].access_direction =
1476 access_direction_list[i].direction;
1481 =============================================================================
1483 -----------------------------------------------------------------------------
1484 initialize and start new game
1485 =============================================================================
1490 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1491 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1492 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1497 /* don't play tapes over network */
1498 network_playing = (options.network && !tape.playing);
1500 for (i = 0; i < MAX_PLAYERS; i++)
1502 struct PlayerInfo *player = &stored_player[i];
1504 player->index_nr = i;
1505 player->index_bit = (1 << i);
1506 player->element_nr = EL_PLAYER_1 + i;
1508 player->present = FALSE;
1509 player->active = FALSE;
1512 player->effective_action = 0;
1513 player->programmed_action = 0;
1516 player->gems_still_needed = level.gems_needed;
1517 player->sokobanfields_still_needed = 0;
1518 player->lights_still_needed = 0;
1519 player->friends_still_needed = 0;
1521 for (j = 0; j < MAX_NUM_KEYS; j++)
1522 player->key[j] = FALSE;
1524 player->dynabomb_count = 0;
1525 player->dynabomb_size = 1;
1526 player->dynabombs_left = 0;
1527 player->dynabomb_xl = FALSE;
1529 player->MovDir = MV_NONE;
1532 player->GfxDir = MV_NONE;
1533 player->GfxAction = ACTION_DEFAULT;
1535 player->StepFrame = 0;
1537 player->use_murphy_graphic = FALSE;
1539 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1540 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1542 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1544 player->actual_frame_counter = 0;
1546 player->step_counter = 0;
1548 player->last_move_dir = MV_NONE;
1550 player->is_waiting = FALSE;
1551 player->is_moving = FALSE;
1552 player->is_auto_moving = FALSE;
1553 player->is_digging = FALSE;
1554 player->is_snapping = FALSE;
1555 player->is_collecting = FALSE;
1556 player->is_pushing = FALSE;
1557 player->is_switching = FALSE;
1558 player->is_dropping = FALSE;
1560 player->is_bored = FALSE;
1561 player->is_sleeping = FALSE;
1563 player->cannot_move = FALSE;
1565 player->frame_counter_bored = -1;
1566 player->frame_counter_sleeping = -1;
1568 player->anim_delay_counter = 0;
1569 player->post_delay_counter = 0;
1571 player->action_waiting = ACTION_DEFAULT;
1572 player->last_action_waiting = ACTION_DEFAULT;
1573 player->special_action_bored = ACTION_DEFAULT;
1574 player->special_action_sleeping = ACTION_DEFAULT;
1576 player->num_special_action_bored = 0;
1577 player->num_special_action_sleeping = 0;
1579 /* determine number of special actions for bored and sleeping animation */
1580 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1582 boolean found = FALSE;
1584 for (k = 0; k < NUM_DIRECTIONS; k++)
1585 if (el_act_dir2img(player->element_nr, j, k) !=
1586 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1590 player->num_special_action_bored++;
1594 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1596 boolean found = FALSE;
1598 for (k = 0; k < NUM_DIRECTIONS; k++)
1599 if (el_act_dir2img(player->element_nr, j, k) !=
1600 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1604 player->num_special_action_sleeping++;
1609 player->switch_x = -1;
1610 player->switch_y = -1;
1612 player->drop_x = -1;
1613 player->drop_y = -1;
1615 player->show_envelope = 0;
1617 player->move_delay = game.initial_move_delay;
1618 player->move_delay_value = game.initial_move_delay_value;
1620 player->move_delay_value_next = -1;
1622 player->move_delay_reset_counter = 0;
1624 player->push_delay = -1; /* initialized when pushing starts */
1625 player->push_delay_value = game.initial_push_delay_value;
1627 player->drop_delay = 0;
1629 player->last_jx = player->last_jy = 0;
1630 player->jx = player->jy = 0;
1632 player->shield_normal_time_left = 0;
1633 player->shield_deadly_time_left = 0;
1635 player->inventory_infinite_element = EL_UNDEFINED;
1636 player->inventory_size = 0;
1638 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1639 SnapField(player, 0, 0);
1641 player->LevelSolved = FALSE;
1642 player->GameOver = FALSE;
1645 network_player_action_received = FALSE;
1647 #if defined(NETWORK_AVALIABLE)
1648 /* initial null action */
1649 if (network_playing)
1650 SendToServer_MovePlayer(MV_NONE);
1659 TimeLeft = level.time;
1662 ScreenMovDir = MV_NONE;
1666 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1668 AllPlayersGone = FALSE;
1670 game.yamyam_content_nr = 0;
1671 game.magic_wall_active = FALSE;
1672 game.magic_wall_time_left = 0;
1673 game.light_time_left = 0;
1674 game.timegate_time_left = 0;
1675 game.switchgate_pos = 0;
1676 game.wind_direction = level.wind_direction_initial;
1677 game.gravity = level.initial_gravity;
1678 game.explosions_delayed = TRUE;
1680 game.envelope_active = FALSE;
1682 for (i = 0; i < NUM_BELTS; i++)
1684 game.belt_dir[i] = MV_NONE;
1685 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1688 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1689 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1691 for (x = 0; x < lev_fieldx; x++)
1693 for (y = 0; y < lev_fieldy; y++)
1695 Feld[x][y] = level.field[x][y];
1696 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1697 ChangeDelay[x][y] = 0;
1698 ChangePage[x][y] = -1;
1699 #if USE_NEW_CUSTOM_VALUE
1700 CustomValue[x][y] = 0; /* initialized in InitField() */
1702 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1704 WasJustMoving[x][y] = 0;
1705 WasJustFalling[x][y] = 0;
1706 CheckCollision[x][y] = 0;
1708 Pushed[x][y] = FALSE;
1710 Changed[x][y] = FALSE;
1711 ChangeEvent[x][y] = -1;
1713 ExplodePhase[x][y] = 0;
1714 ExplodeDelay[x][y] = 0;
1715 ExplodeField[x][y] = EX_TYPE_NONE;
1717 RunnerVisit[x][y] = 0;
1718 PlayerVisit[x][y] = 0;
1721 GfxRandom[x][y] = INIT_GFX_RANDOM();
1722 GfxElement[x][y] = EL_UNDEFINED;
1723 GfxAction[x][y] = ACTION_DEFAULT;
1724 GfxDir[x][y] = MV_NONE;
1728 for (y = 0; y < lev_fieldy; y++)
1730 for (x = 0; x < lev_fieldx; x++)
1732 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1734 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1736 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1739 InitField(x, y, TRUE);
1745 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1746 emulate_sb ? EMU_SOKOBAN :
1747 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1749 #if USE_NEW_ALL_SLIPPERY
1750 /* initialize type of slippery elements */
1751 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1753 if (!IS_CUSTOM_ELEMENT(i))
1755 /* default: elements slip down either to the left or right randomly */
1756 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
1758 /* SP style elements prefer to slip down on the left side */
1759 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
1760 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1762 /* BD style elements prefer to slip down on the left side */
1763 if (game.emulation == EMU_BOULDERDASH)
1764 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1769 /* initialize explosion and ignition delay */
1770 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1772 if (!IS_CUSTOM_ELEMENT(i))
1775 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1776 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1777 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1778 int last_phase = (num_phase + 1) * delay;
1779 int half_phase = (num_phase / 2) * delay;
1781 element_info[i].explosion_delay = last_phase - 1;
1782 element_info[i].ignition_delay = half_phase;
1784 if (i == EL_BLACK_ORB)
1785 element_info[i].ignition_delay = 1;
1789 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1790 element_info[i].explosion_delay = 1;
1792 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1793 element_info[i].ignition_delay = 1;
1797 /* correct non-moving belts to start moving left */
1798 for (i = 0; i < NUM_BELTS; i++)
1799 if (game.belt_dir[i] == MV_NONE)
1800 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1802 /* check if any connected player was not found in playfield */
1803 for (i = 0; i < MAX_PLAYERS; i++)
1805 struct PlayerInfo *player = &stored_player[i];
1807 if (player->connected && !player->present)
1809 for (j = 0; j < MAX_PLAYERS; j++)
1811 struct PlayerInfo *some_player = &stored_player[j];
1812 int jx = some_player->jx, jy = some_player->jy;
1814 /* assign first free player found that is present in the playfield */
1815 if (some_player->present && !some_player->connected)
1817 player->present = TRUE;
1818 player->active = TRUE;
1820 some_player->present = FALSE;
1821 some_player->active = FALSE;
1824 player->element_nr = some_player->element_nr;
1827 player->block_last_field = some_player->block_last_field;
1828 player->block_delay_adjustment = some_player->block_delay_adjustment;
1830 StorePlayer[jx][jy] = player->element_nr;
1831 player->jx = player->last_jx = jx;
1832 player->jy = player->last_jy = jy;
1842 /* when playing a tape, eliminate all players which do not participate */
1844 for (i = 0; i < MAX_PLAYERS; i++)
1846 if (stored_player[i].active && !tape.player_participates[i])
1848 struct PlayerInfo *player = &stored_player[i];
1849 int jx = player->jx, jy = player->jy;
1851 player->active = FALSE;
1852 StorePlayer[jx][jy] = 0;
1853 Feld[jx][jy] = EL_EMPTY;
1857 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1859 /* when in single player mode, eliminate all but the first active player */
1861 for (i = 0; i < MAX_PLAYERS; i++)
1863 if (stored_player[i].active)
1865 for (j = i + 1; j < MAX_PLAYERS; j++)
1867 if (stored_player[j].active)
1869 struct PlayerInfo *player = &stored_player[j];
1870 int jx = player->jx, jy = player->jy;
1872 player->active = FALSE;
1873 player->present = FALSE;
1875 StorePlayer[jx][jy] = 0;
1876 Feld[jx][jy] = EL_EMPTY;
1883 /* when recording the game, store which players take part in the game */
1886 for (i = 0; i < MAX_PLAYERS; i++)
1887 if (stored_player[i].active)
1888 tape.player_participates[i] = TRUE;
1893 for (i = 0; i < MAX_PLAYERS; i++)
1895 struct PlayerInfo *player = &stored_player[i];
1897 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1902 if (local_player == player)
1903 printf("Player %d is local player.\n", i+1);
1907 if (BorderElement == EL_EMPTY)
1910 SBX_Right = lev_fieldx - SCR_FIELDX;
1912 SBY_Lower = lev_fieldy - SCR_FIELDY;
1917 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1919 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1922 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1923 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1925 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1926 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1928 /* if local player not found, look for custom element that might create
1929 the player (make some assumptions about the right custom element) */
1930 if (!local_player->present)
1932 int start_x = 0, start_y = 0;
1933 int found_rating = 0;
1934 int found_element = EL_UNDEFINED;
1936 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1938 int element = Feld[x][y];
1943 if (!IS_CUSTOM_ELEMENT(element))
1946 if (CAN_CHANGE(element))
1948 for (i = 0; i < element_info[element].num_change_pages; i++)
1950 content = element_info[element].change_page[i].target_element;
1951 is_player = ELEM_IS_PLAYER(content);
1953 if (is_player && (found_rating < 3 || element < found_element))
1959 found_element = element;
1964 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1966 content = element_info[element].content.e[xx][yy];
1967 is_player = ELEM_IS_PLAYER(content);
1969 if (is_player && (found_rating < 2 || element < found_element))
1971 start_x = x + xx - 1;
1972 start_y = y + yy - 1;
1975 found_element = element;
1978 if (!CAN_CHANGE(element))
1981 for (i = 0; i < element_info[element].num_change_pages; i++)
1984 element_info[element].change_page[i].target_content.e[xx][yy];
1986 is_player = ELEM_IS_PLAYER(content);
1988 if (is_player && (found_rating < 1 || element < found_element))
1990 start_x = x + xx - 1;
1991 start_y = y + yy - 1;
1994 found_element = element;
2000 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2001 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2004 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2005 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2010 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2011 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2012 local_player->jx - MIDPOSX);
2014 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2015 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2016 local_player->jy - MIDPOSY);
2019 if (!game.restart_level)
2020 CloseDoor(DOOR_CLOSE_1);
2022 /* !!! FIX THIS (START) !!! */
2023 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2025 InitGameEngine_EM();
2032 /* after drawing the level, correct some elements */
2033 if (game.timegate_time_left == 0)
2034 CloseAllOpenTimegates();
2036 if (setup.soft_scrolling)
2037 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2039 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2042 /* !!! FIX THIS (END) !!! */
2044 if (!game.restart_level)
2046 /* copy default game door content to main double buffer */
2047 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2048 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2051 DrawGameDoorValues();
2053 if (!game.restart_level)
2057 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2058 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2059 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2063 /* copy actual game door content to door double buffer for OpenDoor() */
2064 BlitBitmap(drawto, bitmap_db_door,
2065 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2067 OpenDoor(DOOR_OPEN_ALL);
2069 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2071 if (setup.sound_music)
2074 KeyboardAutoRepeatOffUnlessAutoplay();
2078 for (i = 0; i < MAX_PLAYERS; i++)
2079 printf("Player %d %sactive.\n",
2080 i + 1, (stored_player[i].active ? "" : "not "));
2084 game.restart_level = FALSE;
2087 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2089 /* this is used for non-R'n'D game engines to update certain engine values */
2091 /* needed to determine if sounds are played within the visible screen area */
2092 scroll_x = actual_scroll_x;
2093 scroll_y = actual_scroll_y;
2096 void InitMovDir(int x, int y)
2098 int i, element = Feld[x][y];
2099 static int xy[4][2] =
2106 static int direction[3][4] =
2108 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2109 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2110 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2119 Feld[x][y] = EL_BUG;
2120 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2123 case EL_SPACESHIP_RIGHT:
2124 case EL_SPACESHIP_UP:
2125 case EL_SPACESHIP_LEFT:
2126 case EL_SPACESHIP_DOWN:
2127 Feld[x][y] = EL_SPACESHIP;
2128 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2131 case EL_BD_BUTTERFLY_RIGHT:
2132 case EL_BD_BUTTERFLY_UP:
2133 case EL_BD_BUTTERFLY_LEFT:
2134 case EL_BD_BUTTERFLY_DOWN:
2135 Feld[x][y] = EL_BD_BUTTERFLY;
2136 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2139 case EL_BD_FIREFLY_RIGHT:
2140 case EL_BD_FIREFLY_UP:
2141 case EL_BD_FIREFLY_LEFT:
2142 case EL_BD_FIREFLY_DOWN:
2143 Feld[x][y] = EL_BD_FIREFLY;
2144 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2147 case EL_PACMAN_RIGHT:
2149 case EL_PACMAN_LEFT:
2150 case EL_PACMAN_DOWN:
2151 Feld[x][y] = EL_PACMAN;
2152 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2155 case EL_SP_SNIKSNAK:
2156 MovDir[x][y] = MV_UP;
2159 case EL_SP_ELECTRON:
2160 MovDir[x][y] = MV_LEFT;
2167 Feld[x][y] = EL_MOLE;
2168 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2172 if (IS_CUSTOM_ELEMENT(element))
2174 struct ElementInfo *ei = &element_info[element];
2175 int move_direction_initial = ei->move_direction_initial;
2176 int move_pattern = ei->move_pattern;
2178 if (move_direction_initial == MV_START_PREVIOUS)
2180 if (MovDir[x][y] != MV_NONE)
2183 move_direction_initial = MV_START_AUTOMATIC;
2186 if (move_direction_initial == MV_START_RANDOM)
2187 MovDir[x][y] = 1 << RND(4);
2188 else if (move_direction_initial & MV_ANY_DIRECTION)
2189 MovDir[x][y] = move_direction_initial;
2190 else if (move_pattern == MV_ALL_DIRECTIONS ||
2191 move_pattern == MV_TURNING_LEFT ||
2192 move_pattern == MV_TURNING_RIGHT ||
2193 move_pattern == MV_TURNING_LEFT_RIGHT ||
2194 move_pattern == MV_TURNING_RIGHT_LEFT ||
2195 move_pattern == MV_TURNING_RANDOM)
2196 MovDir[x][y] = 1 << RND(4);
2197 else if (move_pattern == MV_HORIZONTAL)
2198 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2199 else if (move_pattern == MV_VERTICAL)
2200 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2201 else if (move_pattern & MV_ANY_DIRECTION)
2202 MovDir[x][y] = element_info[element].move_pattern;
2203 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2204 move_pattern == MV_ALONG_RIGHT_SIDE)
2206 /* use random direction as default start direction */
2207 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2208 MovDir[x][y] = 1 << RND(4);
2210 for (i = 0; i < NUM_DIRECTIONS; i++)
2212 int x1 = x + xy[i][0];
2213 int y1 = y + xy[i][1];
2215 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2217 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2218 MovDir[x][y] = direction[0][i];
2220 MovDir[x][y] = direction[1][i];
2229 MovDir[x][y] = 1 << RND(4);
2231 if (element != EL_BUG &&
2232 element != EL_SPACESHIP &&
2233 element != EL_BD_BUTTERFLY &&
2234 element != EL_BD_FIREFLY)
2237 for (i = 0; i < NUM_DIRECTIONS; i++)
2239 int x1 = x + xy[i][0];
2240 int y1 = y + xy[i][1];
2242 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2244 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2246 MovDir[x][y] = direction[0][i];
2249 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2250 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2252 MovDir[x][y] = direction[1][i];
2261 GfxDir[x][y] = MovDir[x][y];
2264 void InitAmoebaNr(int x, int y)
2267 int group_nr = AmoebeNachbarNr(x, y);
2271 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2273 if (AmoebaCnt[i] == 0)
2281 AmoebaNr[x][y] = group_nr;
2282 AmoebaCnt[group_nr]++;
2283 AmoebaCnt2[group_nr]++;
2289 boolean raise_level = FALSE;
2291 if (local_player->MovPos)
2294 if (tape.auto_play) /* tape might already be stopped here */
2295 tape.auto_play_level_solved = TRUE;
2297 local_player->LevelSolved = FALSE;
2299 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2303 if (!tape.playing && setup.sound_loops)
2304 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2305 SND_CTRL_PLAY_LOOP);
2307 while (TimeLeft > 0)
2309 if (!tape.playing && !setup.sound_loops)
2310 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2311 if (TimeLeft > 0 && !(TimeLeft % 10))
2312 RaiseScore(level.score[SC_TIME_BONUS]);
2313 if (TimeLeft > 100 && !(TimeLeft % 10))
2318 DrawGameValue_Time(TimeLeft);
2326 if (!tape.playing && setup.sound_loops)
2327 StopSound(SND_GAME_LEVELTIME_BONUS);
2329 else if (level.time == 0) /* level without time limit */
2331 if (!tape.playing && setup.sound_loops)
2332 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2333 SND_CTRL_PLAY_LOOP);
2335 while (TimePlayed < 999)
2337 if (!tape.playing && !setup.sound_loops)
2338 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2339 if (TimePlayed < 999 && !(TimePlayed % 10))
2340 RaiseScore(level.score[SC_TIME_BONUS]);
2341 if (TimePlayed < 900 && !(TimePlayed % 10))
2346 DrawGameValue_Time(TimePlayed);
2354 if (!tape.playing && setup.sound_loops)
2355 StopSound(SND_GAME_LEVELTIME_BONUS);
2358 /* close exit door after last player */
2359 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2360 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2361 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2363 int element = Feld[ExitX][ExitY];
2365 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2366 EL_SP_EXIT_CLOSING);
2368 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2371 /* player disappears */
2372 if (ExitX >= 0 && ExitY >= 0)
2373 DrawLevelField(ExitX, ExitY);
2380 CloseDoor(DOOR_CLOSE_1);
2385 SaveTape(tape.level_nr); /* Ask to save tape */
2388 if (level_nr == leveldir_current->handicap_level)
2390 leveldir_current->handicap_level++;
2391 SaveLevelSetup_SeriesInfo();
2394 if (level_editor_test_game)
2395 local_player->score = -1; /* no highscore when playing from editor */
2396 else if (level_nr < leveldir_current->last_level)
2397 raise_level = TRUE; /* advance to next level */
2399 if ((hi_pos = NewHiScore()) >= 0)
2401 game_status = GAME_MODE_SCORES;
2402 DrawHallOfFame(hi_pos);
2411 game_status = GAME_MODE_MAIN;
2428 LoadScore(level_nr);
2430 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2431 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2434 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2436 if (local_player->score > highscore[k].Score)
2438 /* player has made it to the hall of fame */
2440 if (k < MAX_SCORE_ENTRIES - 1)
2442 int m = MAX_SCORE_ENTRIES - 1;
2445 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2446 if (!strcmp(setup.player_name, highscore[l].Name))
2448 if (m == k) /* player's new highscore overwrites his old one */
2452 for (l = m; l > k; l--)
2454 strcpy(highscore[l].Name, highscore[l - 1].Name);
2455 highscore[l].Score = highscore[l - 1].Score;
2462 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2463 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2464 highscore[k].Score = local_player->score;
2470 else if (!strncmp(setup.player_name, highscore[k].Name,
2471 MAX_PLAYER_NAME_LEN))
2472 break; /* player already there with a higher score */
2478 SaveScore(level_nr);
2483 inline static int getElementMoveStepsize(int x, int y)
2485 int element = Feld[x][y];
2486 int direction = MovDir[x][y];
2487 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2488 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2489 int horiz_move = (dx != 0);
2490 int sign = (horiz_move ? dx : dy);
2491 int step = sign * element_info[element].move_stepsize;
2493 /* special values for move stepsize for spring and things on conveyor belt */
2497 if (element == EL_SPRING)
2498 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2499 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2500 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2501 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2503 if (CAN_FALL(element) &&
2504 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2505 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2506 else if (element == EL_SPRING)
2507 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2514 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2516 if (player->GfxAction != action || player->GfxDir != dir)
2519 printf("Player frame reset! (%d => %d, %d => %d)\n",
2520 player->GfxAction, action, player->GfxDir, dir);
2523 player->GfxAction = action;
2524 player->GfxDir = dir;
2526 player->StepFrame = 0;
2530 static void ResetRandomAnimationValue(int x, int y)
2532 GfxRandom[x][y] = INIT_GFX_RANDOM();
2535 static void ResetGfxAnimation(int x, int y)
2538 GfxAction[x][y] = ACTION_DEFAULT;
2539 GfxDir[x][y] = MovDir[x][y];
2542 void InitMovingField(int x, int y, int direction)
2544 int element = Feld[x][y];
2545 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2546 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2550 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2551 ResetGfxAnimation(x, y);
2553 MovDir[x][y] = direction;
2554 GfxDir[x][y] = direction;
2555 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2556 ACTION_FALLING : ACTION_MOVING);
2558 /* this is needed for CEs with property "can move" / "not moving" */
2560 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2562 if (Feld[newx][newy] == EL_EMPTY)
2563 Feld[newx][newy] = EL_BLOCKED;
2565 MovDir[newx][newy] = MovDir[x][y];
2567 #if USE_NEW_CUSTOM_VALUE
2568 CustomValue[newx][newy] = CustomValue[x][y];
2571 GfxFrame[newx][newy] = GfxFrame[x][y];
2572 GfxRandom[newx][newy] = GfxRandom[x][y];
2573 GfxAction[newx][newy] = GfxAction[x][y];
2574 GfxDir[newx][newy] = GfxDir[x][y];
2578 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2580 int direction = MovDir[x][y];
2581 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2582 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2588 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2590 int oldx = x, oldy = y;
2591 int direction = MovDir[x][y];
2593 if (direction == MV_LEFT)
2595 else if (direction == MV_RIGHT)
2597 else if (direction == MV_UP)
2599 else if (direction == MV_DOWN)
2602 *comes_from_x = oldx;
2603 *comes_from_y = oldy;
2606 int MovingOrBlocked2Element(int x, int y)
2608 int element = Feld[x][y];
2610 if (element == EL_BLOCKED)
2614 Blocked2Moving(x, y, &oldx, &oldy);
2615 return Feld[oldx][oldy];
2621 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2623 /* like MovingOrBlocked2Element(), but if element is moving
2624 and (x,y) is the field the moving element is just leaving,
2625 return EL_BLOCKED instead of the element value */
2626 int element = Feld[x][y];
2628 if (IS_MOVING(x, y))
2630 if (element == EL_BLOCKED)
2634 Blocked2Moving(x, y, &oldx, &oldy);
2635 return Feld[oldx][oldy];
2644 static void RemoveField(int x, int y)
2646 Feld[x][y] = EL_EMPTY;
2652 #if USE_NEW_CUSTOM_VALUE
2653 CustomValue[x][y] = 0;
2657 ChangeDelay[x][y] = 0;
2658 ChangePage[x][y] = -1;
2659 Pushed[x][y] = FALSE;
2662 ExplodeField[x][y] = EX_TYPE_NONE;
2665 GfxElement[x][y] = EL_UNDEFINED;
2666 GfxAction[x][y] = ACTION_DEFAULT;
2667 GfxDir[x][y] = MV_NONE;
2670 void RemoveMovingField(int x, int y)
2672 int oldx = x, oldy = y, newx = x, newy = y;
2673 int element = Feld[x][y];
2674 int next_element = EL_UNDEFINED;
2676 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2679 if (IS_MOVING(x, y))
2681 Moving2Blocked(x, y, &newx, &newy);
2683 if (Feld[newx][newy] != EL_BLOCKED)
2685 /* element is moving, but target field is not free (blocked), but
2686 already occupied by something different (example: acid pool);
2687 in this case, only remove the moving field, but not the target */
2689 RemoveField(oldx, oldy);
2691 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2693 DrawLevelField(oldx, oldy);
2698 else if (element == EL_BLOCKED)
2700 Blocked2Moving(x, y, &oldx, &oldy);
2701 if (!IS_MOVING(oldx, oldy))
2705 if (element == EL_BLOCKED &&
2706 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2707 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2708 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2709 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2710 next_element = get_next_element(Feld[oldx][oldy]);
2712 RemoveField(oldx, oldy);
2713 RemoveField(newx, newy);
2715 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2717 if (next_element != EL_UNDEFINED)
2718 Feld[oldx][oldy] = next_element;
2720 DrawLevelField(oldx, oldy);
2721 DrawLevelField(newx, newy);
2724 void DrawDynamite(int x, int y)
2726 int sx = SCREENX(x), sy = SCREENY(y);
2727 int graphic = el2img(Feld[x][y]);
2730 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2733 if (IS_WALKABLE_INSIDE(Back[x][y]))
2737 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2738 else if (Store[x][y])
2739 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2741 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2743 if (Back[x][y] || Store[x][y])
2744 DrawGraphicThruMask(sx, sy, graphic, frame);
2746 DrawGraphic(sx, sy, graphic, frame);
2749 void CheckDynamite(int x, int y)
2751 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2755 if (MovDelay[x][y] != 0)
2758 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2764 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2769 void DrawRelocatePlayer(struct PlayerInfo *player)
2771 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2772 boolean no_delay = (tape.warp_forward);
2773 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2774 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2775 int jx = player->jx;
2776 int jy = player->jy;
2778 if (level.instant_relocation)
2780 int offset = (setup.scroll_delay ? 3 : 0);
2782 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2784 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2785 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2786 local_player->jx - MIDPOSX);
2788 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2789 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2790 local_player->jy - MIDPOSY);
2794 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2795 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2796 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2798 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2799 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2800 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2802 /* don't scroll over playfield boundaries */
2803 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2804 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2806 /* don't scroll over playfield boundaries */
2807 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2808 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2811 RedrawPlayfield(TRUE, 0,0,0,0);
2815 int scroll_xx = -999, scroll_yy = -999;
2817 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2819 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2822 int fx = FX, fy = FY;
2824 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2825 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2826 local_player->jx - MIDPOSX);
2828 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2829 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2830 local_player->jy - MIDPOSY);
2832 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2833 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2835 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2841 fx += dx * TILEX / 2;
2842 fy += dy * TILEY / 2;
2844 ScrollLevel(dx, dy);
2847 /* scroll in two steps of half tile size to make things smoother */
2848 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2850 Delay(wait_delay_value);
2852 /* scroll second step to align at full tile size */
2854 Delay(wait_delay_value);
2859 Delay(wait_delay_value);
2863 void RelocatePlayer(int jx, int jy, int el_player_raw)
2865 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
2866 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2867 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2868 boolean no_delay = (tape.warp_forward);
2869 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2870 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2871 int old_jx = player->jx;
2872 int old_jy = player->jy;
2873 int old_element = Feld[old_jx][old_jy];
2874 int element = Feld[jx][jy];
2875 boolean player_relocated = (old_jx != jx || old_jy != jy);
2877 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2878 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2879 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2880 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2881 int leave_side_horiz = move_dir_horiz;
2882 int leave_side_vert = move_dir_vert;
2883 int enter_side = enter_side_horiz | enter_side_vert;
2884 int leave_side = leave_side_horiz | leave_side_vert;
2886 if (player->GameOver) /* do not reanimate dead player */
2889 if (!player_relocated) /* no need to relocate the player */
2892 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2894 RemoveField(jx, jy); /* temporarily remove newly placed player */
2895 DrawLevelField(jx, jy);
2898 if (player->present)
2900 while (player->MovPos)
2902 ScrollPlayer(player, SCROLL_GO_ON);
2903 ScrollScreen(NULL, SCROLL_GO_ON);
2905 AdvanceFrameAndPlayerCounters(player->index_nr);
2910 Delay(wait_delay_value);
2913 DrawPlayer(player); /* needed here only to cleanup last field */
2914 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2916 player->is_moving = FALSE;
2919 if (IS_CUSTOM_ELEMENT(old_element))
2920 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
2922 player->index_bit, leave_side);
2924 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
2926 player->index_bit, leave_side);
2928 Feld[jx][jy] = el_player;
2929 InitPlayerField(jx, jy, el_player, TRUE);
2931 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
2933 Feld[jx][jy] = element;
2934 InitField(jx, jy, FALSE);
2937 if (player == local_player) /* only visually relocate local player */
2938 DrawRelocatePlayer(player);
2940 TestIfPlayerTouchesBadThing(jx, jy);
2941 TestIfPlayerTouchesCustomElement(jx, jy);
2943 if (IS_CUSTOM_ELEMENT(element))
2944 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
2945 player->index_bit, enter_side);
2947 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
2948 player->index_bit, enter_side);
2951 void Explode(int ex, int ey, int phase, int mode)
2957 /* !!! eliminate this variable !!! */
2958 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2960 if (game.explosions_delayed)
2962 ExplodeField[ex][ey] = mode;
2966 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2968 int center_element = Feld[ex][ey];
2971 /* --- This is only really needed (and now handled) in "Impact()". --- */
2972 /* do not explode moving elements that left the explode field in time */
2973 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2974 center_element == EL_EMPTY &&
2975 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
2979 if (mode == EX_TYPE_NORMAL ||
2980 mode == EX_TYPE_CENTER ||
2981 mode == EX_TYPE_CROSS)
2982 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2984 /* remove things displayed in background while burning dynamite */
2985 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2988 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2990 /* put moving element to center field (and let it explode there) */
2991 center_element = MovingOrBlocked2Element(ex, ey);
2992 RemoveMovingField(ex, ey);
2993 Feld[ex][ey] = center_element;
2996 last_phase = element_info[center_element].explosion_delay + 1;
2998 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3000 int xx = x - ex + 1;
3001 int yy = y - ey + 1;
3004 if (!IN_LEV_FIELD(x, y) ||
3005 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3006 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3009 element = Feld[x][y];
3011 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3013 element = MovingOrBlocked2Element(x, y);
3015 if (!IS_EXPLOSION_PROOF(element))
3016 RemoveMovingField(x, y);
3019 /* indestructible elements can only explode in center (but not flames) */
3020 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3021 mode == EX_TYPE_BORDER)) ||
3022 element == EL_FLAMES)
3025 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3026 behaviour, for example when touching a yamyam that explodes to rocks
3027 with active deadly shield, a rock is created under the player !!! */
3028 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3030 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3031 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3032 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3034 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3037 if (IS_ACTIVE_BOMB(element))
3039 /* re-activate things under the bomb like gate or penguin */
3040 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3047 /* save walkable background elements while explosion on same tile */
3048 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3049 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3050 Back[x][y] = element;
3052 /* ignite explodable elements reached by other explosion */
3053 if (element == EL_EXPLOSION)
3054 element = Store2[x][y];
3056 if (AmoebaNr[x][y] &&
3057 (element == EL_AMOEBA_FULL ||
3058 element == EL_BD_AMOEBA ||
3059 element == EL_AMOEBA_GROWING))
3061 AmoebaCnt[AmoebaNr[x][y]]--;
3062 AmoebaCnt2[AmoebaNr[x][y]]--;
3067 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3069 switch(StorePlayer[ex][ey])
3072 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3075 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3078 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3082 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3086 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3087 Store[x][y] = EL_EMPTY;
3089 else if (center_element == EL_MOLE)
3090 Store[x][y] = EL_EMERALD_RED;
3091 else if (center_element == EL_PENGUIN)
3092 Store[x][y] = EL_EMERALD_PURPLE;
3093 else if (center_element == EL_BUG)
3094 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3095 else if (center_element == EL_BD_BUTTERFLY)
3096 Store[x][y] = EL_BD_DIAMOND;
3097 else if (center_element == EL_SP_ELECTRON)
3098 Store[x][y] = EL_SP_INFOTRON;
3099 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3100 Store[x][y] = level.amoeba_content;
3101 else if (center_element == EL_YAMYAM)
3102 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3103 else if (IS_CUSTOM_ELEMENT(center_element) &&
3104 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3105 Store[x][y] = element_info[center_element].content.e[xx][yy];
3106 else if (element == EL_WALL_EMERALD)
3107 Store[x][y] = EL_EMERALD;
3108 else if (element == EL_WALL_DIAMOND)
3109 Store[x][y] = EL_DIAMOND;
3110 else if (element == EL_WALL_BD_DIAMOND)
3111 Store[x][y] = EL_BD_DIAMOND;
3112 else if (element == EL_WALL_EMERALD_YELLOW)
3113 Store[x][y] = EL_EMERALD_YELLOW;
3114 else if (element == EL_WALL_EMERALD_RED)
3115 Store[x][y] = EL_EMERALD_RED;
3116 else if (element == EL_WALL_EMERALD_PURPLE)
3117 Store[x][y] = EL_EMERALD_PURPLE;
3118 else if (element == EL_WALL_PEARL)
3119 Store[x][y] = EL_PEARL;
3120 else if (element == EL_WALL_CRYSTAL)
3121 Store[x][y] = EL_CRYSTAL;
3122 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3123 Store[x][y] = element_info[element].content.e[1][1];
3125 Store[x][y] = EL_EMPTY;
3127 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3128 center_element == EL_AMOEBA_TO_DIAMOND)
3129 Store2[x][y] = element;
3131 Feld[x][y] = EL_EXPLOSION;
3132 GfxElement[x][y] = center_element;
3134 ExplodePhase[x][y] = 1;
3135 ExplodeDelay[x][y] = last_phase;
3140 if (center_element == EL_YAMYAM)
3141 game.yamyam_content_nr =
3142 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3154 GfxFrame[x][y] = 0; /* restart explosion animation */
3156 last_phase = ExplodeDelay[x][y];
3158 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3162 /* activate this even in non-DEBUG version until cause for crash in
3163 getGraphicAnimationFrame() (see below) is found and eliminated */
3168 if (GfxElement[x][y] == EL_UNDEFINED)
3171 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3172 printf("Explode(): This should never happen!\n");
3175 GfxElement[x][y] = EL_EMPTY;
3179 border_element = Store2[x][y];
3180 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3181 border_element = StorePlayer[x][y];
3183 if (phase == element_info[border_element].ignition_delay ||
3184 phase == last_phase)
3186 boolean border_explosion = FALSE;
3188 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3189 !PLAYER_EXPLOSION_PROTECTED(x, y))
3191 KillPlayerUnlessExplosionProtected(x, y);
3192 border_explosion = TRUE;
3194 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3196 Feld[x][y] = Store2[x][y];
3199 border_explosion = TRUE;
3201 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3203 AmoebeUmwandeln(x, y);
3205 border_explosion = TRUE;
3208 /* if an element just explodes due to another explosion (chain-reaction),
3209 do not immediately end the new explosion when it was the last frame of
3210 the explosion (as it would be done in the following "if"-statement!) */
3211 if (border_explosion && phase == last_phase)
3215 if (phase == last_phase)
3219 element = Feld[x][y] = Store[x][y];
3220 Store[x][y] = Store2[x][y] = 0;
3221 GfxElement[x][y] = EL_UNDEFINED;
3223 /* player can escape from explosions and might therefore be still alive */
3224 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3225 element <= EL_PLAYER_IS_EXPLODING_4)
3226 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3228 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3229 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3230 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3233 /* restore probably existing indestructible background element */
3234 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3235 element = Feld[x][y] = Back[x][y];
3238 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3239 GfxDir[x][y] = MV_NONE;
3240 ChangeDelay[x][y] = 0;
3241 ChangePage[x][y] = -1;
3243 #if USE_NEW_CUSTOM_VALUE
3244 CustomValue[x][y] = 0;
3247 InitField_WithBug2(x, y, FALSE);
3249 DrawLevelField(x, y);
3251 TestIfElementTouchesCustomElement(x, y);
3253 if (GFX_CRUMBLED(element))
3254 DrawLevelFieldCrumbledSandNeighbours(x, y);
3256 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3257 StorePlayer[x][y] = 0;
3259 if (ELEM_IS_PLAYER(element))
3260 RelocatePlayer(x, y, element);
3262 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3264 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3265 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3268 DrawLevelFieldCrumbledSand(x, y);
3270 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3272 DrawLevelElement(x, y, Back[x][y]);
3273 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3275 else if (IS_WALKABLE_UNDER(Back[x][y]))
3277 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3278 DrawLevelElementThruMask(x, y, Back[x][y]);
3280 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3281 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3285 void DynaExplode(int ex, int ey)
3288 int dynabomb_element = Feld[ex][ey];
3289 int dynabomb_size = 1;
3290 boolean dynabomb_xl = FALSE;
3291 struct PlayerInfo *player;
3292 static int xy[4][2] =
3300 if (IS_ACTIVE_BOMB(dynabomb_element))
3302 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3303 dynabomb_size = player->dynabomb_size;
3304 dynabomb_xl = player->dynabomb_xl;
3305 player->dynabombs_left++;
3308 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3310 for (i = 0; i < NUM_DIRECTIONS; i++)
3312 for (j = 1; j <= dynabomb_size; j++)
3314 int x = ex + j * xy[i][0];
3315 int y = ey + j * xy[i][1];
3318 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3321 element = Feld[x][y];
3323 /* do not restart explosions of fields with active bombs */
3324 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3327 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3329 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3330 !IS_DIGGABLE(element) && !dynabomb_xl)
3336 void Bang(int x, int y)
3338 int element = MovingOrBlocked2Element(x, y);
3340 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3342 struct PlayerInfo *player = PLAYERINFO(x, y);
3344 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3345 player->element_nr);
3352 case EL_BD_BUTTERFLY:
3355 case EL_DARK_YAMYAM:
3359 RaiseScoreElement(element);
3360 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3362 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3363 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3364 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3365 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3366 case EL_DYNABOMB_INCREASE_NUMBER:
3367 case EL_DYNABOMB_INCREASE_SIZE:
3368 case EL_DYNABOMB_INCREASE_POWER:
3373 case EL_LAMP_ACTIVE:
3374 case EL_AMOEBA_TO_DIAMOND:
3375 if (IS_PLAYER(x, y))
3376 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3378 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3381 if (element_info[element].explosion_type == EXPLODES_CROSS)
3382 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3383 else if (element_info[element].explosion_type == EXPLODES_1X1)
3384 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3386 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3390 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3393 void SplashAcid(int x, int y)
3395 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3396 (!IN_LEV_FIELD(x - 1, y - 2) ||
3397 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3398 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3400 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3401 (!IN_LEV_FIELD(x + 1, y - 2) ||
3402 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3403 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3405 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3408 static void InitBeltMovement()
3410 static int belt_base_element[4] =
3412 EL_CONVEYOR_BELT_1_LEFT,
3413 EL_CONVEYOR_BELT_2_LEFT,
3414 EL_CONVEYOR_BELT_3_LEFT,
3415 EL_CONVEYOR_BELT_4_LEFT
3417 static int belt_base_active_element[4] =
3419 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3420 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3421 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3422 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3427 /* set frame order for belt animation graphic according to belt direction */
3428 for (i = 0; i < NUM_BELTS; i++)
3432 for (j = 0; j < NUM_BELT_PARTS; j++)
3434 int element = belt_base_active_element[belt_nr] + j;
3435 int graphic = el2img(element);
3437 if (game.belt_dir[i] == MV_LEFT)
3438 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3440 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3444 for (y = 0; y < lev_fieldy; y++)
3446 for (x = 0; x < lev_fieldx; x++)
3448 int element = Feld[x][y];
3450 for (i = 0; i < NUM_BELTS; i++)
3452 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3454 int e_belt_nr = getBeltNrFromBeltElement(element);
3457 if (e_belt_nr == belt_nr)
3459 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3461 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3469 static void ToggleBeltSwitch(int x, int y)
3471 static int belt_base_element[4] =
3473 EL_CONVEYOR_BELT_1_LEFT,
3474 EL_CONVEYOR_BELT_2_LEFT,
3475 EL_CONVEYOR_BELT_3_LEFT,
3476 EL_CONVEYOR_BELT_4_LEFT
3478 static int belt_base_active_element[4] =
3480 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3481 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3482 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3483 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3485 static int belt_base_switch_element[4] =
3487 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3488 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3489 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3490 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3492 static int belt_move_dir[4] =
3500 int element = Feld[x][y];
3501 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3502 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3503 int belt_dir = belt_move_dir[belt_dir_nr];
3506 if (!IS_BELT_SWITCH(element))
3509 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3510 game.belt_dir[belt_nr] = belt_dir;
3512 if (belt_dir_nr == 3)
3515 /* set frame order for belt animation graphic according to belt direction */
3516 for (i = 0; i < NUM_BELT_PARTS; i++)
3518 int element = belt_base_active_element[belt_nr] + i;
3519 int graphic = el2img(element);
3521 if (belt_dir == MV_LEFT)
3522 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3524 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3527 for (yy = 0; yy < lev_fieldy; yy++)
3529 for (xx = 0; xx < lev_fieldx; xx++)
3531 int element = Feld[xx][yy];
3533 if (IS_BELT_SWITCH(element))
3535 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3537 if (e_belt_nr == belt_nr)
3539 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3540 DrawLevelField(xx, yy);
3543 else if (IS_BELT(element) && belt_dir != MV_NONE)
3545 int e_belt_nr = getBeltNrFromBeltElement(element);
3547 if (e_belt_nr == belt_nr)
3549 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3551 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3552 DrawLevelField(xx, yy);
3555 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3557 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3559 if (e_belt_nr == belt_nr)
3561 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3563 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3564 DrawLevelField(xx, yy);
3571 static void ToggleSwitchgateSwitch(int x, int y)
3575 game.switchgate_pos = !game.switchgate_pos;
3577 for (yy = 0; yy < lev_fieldy; yy++)
3579 for (xx = 0; xx < lev_fieldx; xx++)
3581 int element = Feld[xx][yy];
3583 if (element == EL_SWITCHGATE_SWITCH_UP ||
3584 element == EL_SWITCHGATE_SWITCH_DOWN)
3586 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3587 DrawLevelField(xx, yy);
3589 else if (element == EL_SWITCHGATE_OPEN ||
3590 element == EL_SWITCHGATE_OPENING)
3592 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3594 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3596 else if (element == EL_SWITCHGATE_CLOSED ||
3597 element == EL_SWITCHGATE_CLOSING)
3599 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3601 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3607 static int getInvisibleActiveFromInvisibleElement(int element)
3609 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3610 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3611 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3615 static int getInvisibleFromInvisibleActiveElement(int element)
3617 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3618 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3619 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3623 static void RedrawAllLightSwitchesAndInvisibleElements()
3627 for (y = 0; y < lev_fieldy; y++)
3629 for (x = 0; x < lev_fieldx; x++)
3631 int element = Feld[x][y];
3633 if (element == EL_LIGHT_SWITCH &&
3634 game.light_time_left > 0)
3636 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3637 DrawLevelField(x, y);
3639 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3640 game.light_time_left == 0)
3642 Feld[x][y] = EL_LIGHT_SWITCH;
3643 DrawLevelField(x, y);
3645 else if (element == EL_INVISIBLE_STEELWALL ||
3646 element == EL_INVISIBLE_WALL ||
3647 element == EL_INVISIBLE_SAND)
3649 if (game.light_time_left > 0)
3650 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3652 DrawLevelField(x, y);
3654 /* uncrumble neighbour fields, if needed */
3655 if (element == EL_INVISIBLE_SAND)
3656 DrawLevelFieldCrumbledSandNeighbours(x, y);
3658 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3659 element == EL_INVISIBLE_WALL_ACTIVE ||
3660 element == EL_INVISIBLE_SAND_ACTIVE)
3662 if (game.light_time_left == 0)
3663 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3665 DrawLevelField(x, y);
3667 /* re-crumble neighbour fields, if needed */
3668 if (element == EL_INVISIBLE_SAND)
3669 DrawLevelFieldCrumbledSandNeighbours(x, y);
3675 static void ToggleLightSwitch(int x, int y)
3677 int element = Feld[x][y];
3679 game.light_time_left =
3680 (element == EL_LIGHT_SWITCH ?
3681 level.time_light * FRAMES_PER_SECOND : 0);
3683 RedrawAllLightSwitchesAndInvisibleElements();
3686 static void ActivateTimegateSwitch(int x, int y)
3690 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3692 for (yy = 0; yy < lev_fieldy; yy++)
3694 for (xx = 0; xx < lev_fieldx; xx++)
3696 int element = Feld[xx][yy];
3698 if (element == EL_TIMEGATE_CLOSED ||
3699 element == EL_TIMEGATE_CLOSING)
3701 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3702 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3706 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3708 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3709 DrawLevelField(xx, yy);
3716 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3719 void Impact(int x, int y)
3721 boolean last_line = (y == lev_fieldy - 1);
3722 boolean object_hit = FALSE;
3723 boolean impact = (last_line || object_hit);
3724 int element = Feld[x][y];
3725 int smashed = EL_STEELWALL;
3727 if (!last_line) /* check if element below was hit */
3729 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3732 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3733 MovDir[x][y + 1] != MV_DOWN ||
3734 MovPos[x][y + 1] <= TILEY / 2));
3736 /* do not smash moving elements that left the smashed field in time */
3737 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3738 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3742 smashed = MovingOrBlocked2Element(x, y + 1);
3744 impact = (last_line || object_hit);
3747 if (!last_line && smashed == EL_ACID) /* element falls into acid */
3749 SplashAcid(x, y + 1);
3753 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
3754 /* only reset graphic animation if graphic really changes after impact */
3756 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3758 ResetGfxAnimation(x, y);
3759 DrawLevelField(x, y);
3762 if (impact && CAN_EXPLODE_IMPACT(element))
3767 else if (impact && element == EL_PEARL)
3769 ResetGfxAnimation(x, y);
3771 Feld[x][y] = EL_PEARL_BREAKING;
3772 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3775 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
3777 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3782 if (impact && element == EL_AMOEBA_DROP)
3784 if (object_hit && IS_PLAYER(x, y + 1))
3785 KillPlayerUnlessEnemyProtected(x, y + 1);
3786 else if (object_hit && smashed == EL_PENGUIN)
3790 Feld[x][y] = EL_AMOEBA_GROWING;
3791 Store[x][y] = EL_AMOEBA_WET;
3793 ResetRandomAnimationValue(x, y);
3798 if (object_hit) /* check which object was hit */
3800 if (CAN_PASS_MAGIC_WALL(element) &&
3801 (smashed == EL_MAGIC_WALL ||
3802 smashed == EL_BD_MAGIC_WALL))
3805 int activated_magic_wall =
3806 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3807 EL_BD_MAGIC_WALL_ACTIVE);
3809 /* activate magic wall / mill */
3810 for (yy = 0; yy < lev_fieldy; yy++)
3811 for (xx = 0; xx < lev_fieldx; xx++)
3812 if (Feld[xx][yy] == smashed)
3813 Feld[xx][yy] = activated_magic_wall;
3815 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3816 game.magic_wall_active = TRUE;
3818 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3819 SND_MAGIC_WALL_ACTIVATING :
3820 SND_BD_MAGIC_WALL_ACTIVATING));
3823 if (IS_PLAYER(x, y + 1))
3825 if (CAN_SMASH_PLAYER(element))
3827 KillPlayerUnlessEnemyProtected(x, y + 1);
3831 else if (smashed == EL_PENGUIN)
3833 if (CAN_SMASH_PLAYER(element))
3839 else if (element == EL_BD_DIAMOND)
3841 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3847 else if (((element == EL_SP_INFOTRON ||
3848 element == EL_SP_ZONK) &&
3849 (smashed == EL_SP_SNIKSNAK ||
3850 smashed == EL_SP_ELECTRON ||
3851 smashed == EL_SP_DISK_ORANGE)) ||
3852 (element == EL_SP_INFOTRON &&
3853 smashed == EL_SP_DISK_YELLOW))
3858 else if (CAN_SMASH_EVERYTHING(element))
3860 if (IS_CLASSIC_ENEMY(smashed) ||
3861 CAN_EXPLODE_SMASHED(smashed))
3866 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3868 if (smashed == EL_LAMP ||
3869 smashed == EL_LAMP_ACTIVE)
3874 else if (smashed == EL_NUT)
3876 Feld[x][y + 1] = EL_NUT_BREAKING;
3877 PlayLevelSound(x, y, SND_NUT_BREAKING);
3878 RaiseScoreElement(EL_NUT);
3881 else if (smashed == EL_PEARL)
3883 ResetGfxAnimation(x, y);
3885 Feld[x][y + 1] = EL_PEARL_BREAKING;
3886 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3889 else if (smashed == EL_DIAMOND)
3891 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3892 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3895 else if (IS_BELT_SWITCH(smashed))
3897 ToggleBeltSwitch(x, y + 1);
3899 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3900 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3902 ToggleSwitchgateSwitch(x, y + 1);
3904 else if (smashed == EL_LIGHT_SWITCH ||
3905 smashed == EL_LIGHT_SWITCH_ACTIVE)
3907 ToggleLightSwitch(x, y + 1);
3912 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
3915 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
3917 CheckElementChangeBySide(x, y + 1, smashed, element,
3918 CE_SWITCHED, CH_SIDE_TOP);
3919 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
3925 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
3930 /* play sound of magic wall / mill */
3932 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3933 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3935 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3936 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3937 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3938 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3943 /* play sound of object that hits the ground */
3944 if (last_line || object_hit)
3945 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3948 inline static void TurnRoundExt(int x, int y)
3960 { 0, 0 }, { 0, 0 }, { 0, 0 },
3965 int left, right, back;
3969 { MV_DOWN, MV_UP, MV_RIGHT },
3970 { MV_UP, MV_DOWN, MV_LEFT },
3972 { MV_LEFT, MV_RIGHT, MV_DOWN },
3976 { MV_RIGHT, MV_LEFT, MV_UP }
3979 int element = Feld[x][y];
3980 int move_pattern = element_info[element].move_pattern;
3982 int old_move_dir = MovDir[x][y];
3983 int left_dir = turn[old_move_dir].left;
3984 int right_dir = turn[old_move_dir].right;
3985 int back_dir = turn[old_move_dir].back;
3987 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3988 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3989 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3990 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3992 int left_x = x + left_dx, left_y = y + left_dy;
3993 int right_x = x + right_dx, right_y = y + right_dy;
3994 int move_x = x + move_dx, move_y = y + move_dy;
3998 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4000 TestIfBadThingTouchesOtherBadThing(x, y);
4002 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4003 MovDir[x][y] = right_dir;
4004 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4005 MovDir[x][y] = left_dir;
4007 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4009 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4012 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4014 TestIfBadThingTouchesOtherBadThing(x, y);
4016 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4017 MovDir[x][y] = left_dir;
4018 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4019 MovDir[x][y] = right_dir;
4021 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4023 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4026 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4028 TestIfBadThingTouchesOtherBadThing(x, y);
4030 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4031 MovDir[x][y] = left_dir;
4032 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4033 MovDir[x][y] = right_dir;
4035 if (MovDir[x][y] != old_move_dir)
4038 else if (element == EL_YAMYAM)
4040 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4041 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4043 if (can_turn_left && can_turn_right)
4044 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4045 else if (can_turn_left)
4046 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4047 else if (can_turn_right)
4048 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4050 MovDir[x][y] = back_dir;
4052 MovDelay[x][y] = 16 + 16 * RND(3);
4054 else if (element == EL_DARK_YAMYAM)
4056 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4058 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4061 if (can_turn_left && can_turn_right)
4062 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4063 else if (can_turn_left)
4064 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4065 else if (can_turn_right)
4066 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4068 MovDir[x][y] = back_dir;
4070 MovDelay[x][y] = 16 + 16 * RND(3);
4072 else if (element == EL_PACMAN)
4074 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4075 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4077 if (can_turn_left && can_turn_right)
4078 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4079 else if (can_turn_left)
4080 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4081 else if (can_turn_right)
4082 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4084 MovDir[x][y] = back_dir;
4086 MovDelay[x][y] = 6 + RND(40);
4088 else if (element == EL_PIG)
4090 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4091 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4092 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4093 boolean should_turn_left, should_turn_right, should_move_on;
4095 int rnd = RND(rnd_value);
4097 should_turn_left = (can_turn_left &&
4099 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4100 y + back_dy + left_dy)));
4101 should_turn_right = (can_turn_right &&
4103 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4104 y + back_dy + right_dy)));
4105 should_move_on = (can_move_on &&
4108 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4109 y + move_dy + left_dy) ||
4110 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4111 y + move_dy + right_dy)));
4113 if (should_turn_left || should_turn_right || should_move_on)
4115 if (should_turn_left && should_turn_right && should_move_on)
4116 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4117 rnd < 2 * rnd_value / 3 ? right_dir :
4119 else if (should_turn_left && should_turn_right)
4120 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4121 else if (should_turn_left && should_move_on)
4122 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4123 else if (should_turn_right && should_move_on)
4124 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4125 else if (should_turn_left)
4126 MovDir[x][y] = left_dir;
4127 else if (should_turn_right)
4128 MovDir[x][y] = right_dir;
4129 else if (should_move_on)
4130 MovDir[x][y] = old_move_dir;
4132 else if (can_move_on && rnd > rnd_value / 8)
4133 MovDir[x][y] = old_move_dir;
4134 else if (can_turn_left && can_turn_right)
4135 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4136 else if (can_turn_left && rnd > rnd_value / 8)
4137 MovDir[x][y] = left_dir;
4138 else if (can_turn_right && rnd > rnd_value/8)
4139 MovDir[x][y] = right_dir;
4141 MovDir[x][y] = back_dir;
4143 xx = x + move_xy[MovDir[x][y]].x;
4144 yy = y + move_xy[MovDir[x][y]].y;
4146 if (!IN_LEV_FIELD(xx, yy) ||
4147 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4148 MovDir[x][y] = old_move_dir;
4152 else if (element == EL_DRAGON)
4154 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4155 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4156 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4158 int rnd = RND(rnd_value);
4160 if (can_move_on && rnd > rnd_value / 8)
4161 MovDir[x][y] = old_move_dir;
4162 else if (can_turn_left && can_turn_right)
4163 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4164 else if (can_turn_left && rnd > rnd_value / 8)
4165 MovDir[x][y] = left_dir;
4166 else if (can_turn_right && rnd > rnd_value / 8)
4167 MovDir[x][y] = right_dir;
4169 MovDir[x][y] = back_dir;
4171 xx = x + move_xy[MovDir[x][y]].x;
4172 yy = y + move_xy[MovDir[x][y]].y;
4174 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4175 MovDir[x][y] = old_move_dir;
4179 else if (element == EL_MOLE)
4181 boolean can_move_on =
4182 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4183 IS_AMOEBOID(Feld[move_x][move_y]) ||
4184 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4187 boolean can_turn_left =
4188 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4189 IS_AMOEBOID(Feld[left_x][left_y])));
4191 boolean can_turn_right =
4192 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4193 IS_AMOEBOID(Feld[right_x][right_y])));
4195 if (can_turn_left && can_turn_right)
4196 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4197 else if (can_turn_left)
4198 MovDir[x][y] = left_dir;
4200 MovDir[x][y] = right_dir;
4203 if (MovDir[x][y] != old_move_dir)
4206 else if (element == EL_BALLOON)
4208 MovDir[x][y] = game.wind_direction;
4211 else if (element == EL_SPRING)
4213 if (MovDir[x][y] & MV_HORIZONTAL &&
4214 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4215 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4216 MovDir[x][y] = MV_NONE;
4220 else if (element == EL_ROBOT ||
4221 element == EL_SATELLITE ||
4222 element == EL_PENGUIN)
4224 int attr_x = -1, attr_y = -1;
4235 for (i = 0; i < MAX_PLAYERS; i++)
4237 struct PlayerInfo *player = &stored_player[i];
4238 int jx = player->jx, jy = player->jy;
4240 if (!player->active)
4244 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4252 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4253 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4254 game.engine_version < VERSION_IDENT(3,1,0,0)))
4260 if (element == EL_PENGUIN)
4263 static int xy[4][2] =
4271 for (i = 0; i < NUM_DIRECTIONS; i++)
4273 int ex = x + xy[i][0];
4274 int ey = y + xy[i][1];
4276 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4285 MovDir[x][y] = MV_NONE;
4287 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4288 else if (attr_x > x)
4289 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4291 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4292 else if (attr_y > y)
4293 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4295 if (element == EL_ROBOT)
4299 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4300 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4301 Moving2Blocked(x, y, &newx, &newy);
4303 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4304 MovDelay[x][y] = 8 + 8 * !RND(3);
4306 MovDelay[x][y] = 16;
4308 else if (element == EL_PENGUIN)
4314 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4316 boolean first_horiz = RND(2);
4317 int new_move_dir = MovDir[x][y];
4320 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4321 Moving2Blocked(x, y, &newx, &newy);
4323 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4327 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4328 Moving2Blocked(x, y, &newx, &newy);
4330 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4333 MovDir[x][y] = old_move_dir;
4337 else /* (element == EL_SATELLITE) */
4343 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4345 boolean first_horiz = RND(2);
4346 int new_move_dir = MovDir[x][y];
4349 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4350 Moving2Blocked(x, y, &newx, &newy);
4352 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4356 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4357 Moving2Blocked(x, y, &newx, &newy);
4359 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4362 MovDir[x][y] = old_move_dir;
4367 else if (move_pattern == MV_TURNING_LEFT ||
4368 move_pattern == MV_TURNING_RIGHT ||
4369 move_pattern == MV_TURNING_LEFT_RIGHT ||
4370 move_pattern == MV_TURNING_RIGHT_LEFT ||
4371 move_pattern == MV_TURNING_RANDOM ||
4372 move_pattern == MV_ALL_DIRECTIONS)
4374 boolean can_turn_left =
4375 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4376 boolean can_turn_right =
4377 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4379 if (element_info[element].move_stepsize == 0) /* "not moving" */
4382 if (move_pattern == MV_TURNING_LEFT)
4383 MovDir[x][y] = left_dir;
4384 else if (move_pattern == MV_TURNING_RIGHT)
4385 MovDir[x][y] = right_dir;
4386 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4387 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4388 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4389 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4390 else if (move_pattern == MV_TURNING_RANDOM)
4391 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4392 can_turn_right && !can_turn_left ? right_dir :
4393 RND(2) ? left_dir : right_dir);
4394 else if (can_turn_left && can_turn_right)
4395 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4396 else if (can_turn_left)
4397 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4398 else if (can_turn_right)
4399 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4401 MovDir[x][y] = back_dir;
4403 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4405 else if (move_pattern == MV_HORIZONTAL ||
4406 move_pattern == MV_VERTICAL)
4408 if (move_pattern & old_move_dir)
4409 MovDir[x][y] = back_dir;
4410 else if (move_pattern == MV_HORIZONTAL)
4411 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4412 else if (move_pattern == MV_VERTICAL)
4413 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4415 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4417 else if (move_pattern & MV_ANY_DIRECTION)
4419 MovDir[x][y] = move_pattern;
4420 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4422 else if (move_pattern & MV_WIND_DIRECTION)
4424 MovDir[x][y] = game.wind_direction;
4425 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4427 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4429 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4430 MovDir[x][y] = left_dir;
4431 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4432 MovDir[x][y] = right_dir;
4434 if (MovDir[x][y] != old_move_dir)
4435 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4437 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4439 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4440 MovDir[x][y] = right_dir;
4441 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4442 MovDir[x][y] = left_dir;
4444 if (MovDir[x][y] != old_move_dir)
4445 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4447 else if (move_pattern == MV_TOWARDS_PLAYER ||
4448 move_pattern == MV_AWAY_FROM_PLAYER)
4450 int attr_x = -1, attr_y = -1;
4452 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4463 for (i = 0; i < MAX_PLAYERS; i++)
4465 struct PlayerInfo *player = &stored_player[i];
4466 int jx = player->jx, jy = player->jy;
4468 if (!player->active)
4472 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4480 MovDir[x][y] = MV_NONE;
4482 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4483 else if (attr_x > x)
4484 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4486 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4487 else if (attr_y > y)
4488 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4490 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4492 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4494 boolean first_horiz = RND(2);
4495 int new_move_dir = MovDir[x][y];
4497 if (element_info[element].move_stepsize == 0) /* "not moving" */
4499 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
4500 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4506 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4507 Moving2Blocked(x, y, &newx, &newy);
4509 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4513 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4514 Moving2Blocked(x, y, &newx, &newy);
4516 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4519 MovDir[x][y] = old_move_dir;
4522 else if (move_pattern == MV_WHEN_PUSHED ||
4523 move_pattern == MV_WHEN_DROPPED)
4525 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4526 MovDir[x][y] = MV_NONE;
4530 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4532 static int test_xy[7][2] =
4542 static int test_dir[7] =
4552 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4553 int move_preference = -1000000; /* start with very low preference */
4554 int new_move_dir = MV_NONE;
4555 int start_test = RND(4);
4558 for (i = 0; i < NUM_DIRECTIONS; i++)
4560 int move_dir = test_dir[start_test + i];
4561 int move_dir_preference;
4563 xx = x + test_xy[start_test + i][0];
4564 yy = y + test_xy[start_test + i][1];
4566 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4567 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4569 new_move_dir = move_dir;
4574 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4577 move_dir_preference = -1 * RunnerVisit[xx][yy];
4578 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4579 move_dir_preference = PlayerVisit[xx][yy];
4581 if (move_dir_preference > move_preference)
4583 /* prefer field that has not been visited for the longest time */
4584 move_preference = move_dir_preference;
4585 new_move_dir = move_dir;
4587 else if (move_dir_preference == move_preference &&
4588 move_dir == old_move_dir)
4590 /* prefer last direction when all directions are preferred equally */
4591 move_preference = move_dir_preference;
4592 new_move_dir = move_dir;
4596 MovDir[x][y] = new_move_dir;
4597 if (old_move_dir != new_move_dir)
4598 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4602 static void TurnRound(int x, int y)
4604 int direction = MovDir[x][y];
4608 GfxDir[x][y] = MovDir[x][y];
4610 if (direction != MovDir[x][y])
4614 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4617 static boolean JustBeingPushed(int x, int y)
4621 for (i = 0; i < MAX_PLAYERS; i++)
4623 struct PlayerInfo *player = &stored_player[i];
4625 if (player->active && player->is_pushing && player->MovPos)
4627 int next_jx = player->jx + (player->jx - player->last_jx);
4628 int next_jy = player->jy + (player->jy - player->last_jy);
4630 if (x == next_jx && y == next_jy)
4638 void StartMoving(int x, int y)
4640 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4641 int element = Feld[x][y];
4646 if (MovDelay[x][y] == 0)
4647 GfxAction[x][y] = ACTION_DEFAULT;
4649 if (CAN_FALL(element) && y < lev_fieldy - 1)
4651 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4652 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4653 if (JustBeingPushed(x, y))
4656 if (element == EL_QUICKSAND_FULL)
4658 if (IS_FREE(x, y + 1))
4660 InitMovingField(x, y, MV_DOWN);
4661 started_moving = TRUE;
4663 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4664 Store[x][y] = EL_ROCK;
4666 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4668 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4670 if (!MovDelay[x][y])
4671 MovDelay[x][y] = TILEY + 1;
4680 Feld[x][y] = EL_QUICKSAND_EMPTY;
4681 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4682 Store[x][y + 1] = Store[x][y];
4685 PlayLevelSoundAction(x, y, ACTION_FILLING);
4688 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4689 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4691 InitMovingField(x, y, MV_DOWN);
4692 started_moving = TRUE;
4694 Feld[x][y] = EL_QUICKSAND_FILLING;
4695 Store[x][y] = element;
4697 PlayLevelSoundAction(x, y, ACTION_FILLING);
4699 else if (element == EL_MAGIC_WALL_FULL)
4701 if (IS_FREE(x, y + 1))
4703 InitMovingField(x, y, MV_DOWN);
4704 started_moving = TRUE;
4706 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4707 Store[x][y] = EL_CHANGED(Store[x][y]);
4709 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4711 if (!MovDelay[x][y])
4712 MovDelay[x][y] = TILEY/4 + 1;
4721 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4722 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4723 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4727 else if (element == EL_BD_MAGIC_WALL_FULL)
4729 if (IS_FREE(x, y + 1))
4731 InitMovingField(x, y, MV_DOWN);
4732 started_moving = TRUE;
4734 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4735 Store[x][y] = EL_CHANGED2(Store[x][y]);
4737 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4739 if (!MovDelay[x][y])
4740 MovDelay[x][y] = TILEY/4 + 1;
4749 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4750 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4751 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4755 else if (CAN_PASS_MAGIC_WALL(element) &&
4756 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4757 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4759 InitMovingField(x, y, MV_DOWN);
4760 started_moving = TRUE;
4763 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4764 EL_BD_MAGIC_WALL_FILLING);
4765 Store[x][y] = element;
4767 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4769 SplashAcid(x, y + 1);
4771 InitMovingField(x, y, MV_DOWN);
4772 started_moving = TRUE;
4774 Store[x][y] = EL_ACID;
4776 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4777 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
4779 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4780 CAN_FALL(element) && WasJustFalling[x][y] &&
4781 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
4783 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4784 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4785 (Feld[x][y + 1] == EL_BLOCKED)))
4787 /* this is needed for a special case not covered by calling "Impact()"
4788 from "ContinueMoving()": if an element moves to a tile directly below
4789 another element which was just falling on that tile (which was empty
4790 in the previous frame), the falling element above would just stop
4791 instead of smashing the element below (in previous version, the above
4792 element was just checked for "moving" instead of "falling", resulting
4793 in incorrect smashes caused by horizontal movement of the above
4794 element; also, the case of the player being the element to smash was
4795 simply not covered here... :-/ ) */
4797 CheckCollision[x][y] = 0;
4801 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4803 if (MovDir[x][y] == MV_NONE)
4805 InitMovingField(x, y, MV_DOWN);
4806 started_moving = TRUE;
4809 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4811 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4812 MovDir[x][y] = MV_DOWN;
4814 InitMovingField(x, y, MV_DOWN);
4815 started_moving = TRUE;
4817 else if (element == EL_AMOEBA_DROP)
4819 Feld[x][y] = EL_AMOEBA_GROWING;
4820 Store[x][y] = EL_AMOEBA_WET;
4822 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4823 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4824 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4825 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4827 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4828 (IS_FREE(x - 1, y + 1) ||
4829 Feld[x - 1][y + 1] == EL_ACID));
4830 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4831 (IS_FREE(x + 1, y + 1) ||
4832 Feld[x + 1][y + 1] == EL_ACID));
4833 boolean can_fall_any = (can_fall_left || can_fall_right);
4834 boolean can_fall_both = (can_fall_left && can_fall_right);
4835 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4837 #if USE_NEW_ALL_SLIPPERY
4838 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
4840 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4841 can_fall_right = FALSE;
4842 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4843 can_fall_left = FALSE;
4844 else if (slippery_type == SLIPPERY_ONLY_LEFT)
4845 can_fall_right = FALSE;
4846 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4847 can_fall_left = FALSE;
4849 can_fall_any = (can_fall_left || can_fall_right);
4850 can_fall_both = FALSE;
4853 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4855 if (slippery_type == SLIPPERY_ONLY_LEFT)
4856 can_fall_right = FALSE;
4857 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4858 can_fall_left = FALSE;
4859 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4860 can_fall_right = FALSE;
4861 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4862 can_fall_left = FALSE;
4864 can_fall_any = (can_fall_left || can_fall_right);
4865 can_fall_both = (can_fall_left && can_fall_right);
4869 #if USE_NEW_ALL_SLIPPERY
4871 #if USE_NEW_SP_SLIPPERY
4872 /* !!! better use the same properties as for custom elements here !!! */
4873 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
4874 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
4876 can_fall_right = FALSE; /* slip down on left side */
4877 can_fall_both = FALSE;
4882 #if USE_NEW_ALL_SLIPPERY
4885 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
4886 can_fall_right = FALSE; /* slip down on left side */
4888 can_fall_left = !(can_fall_right = RND(2));
4890 can_fall_both = FALSE;
4895 if (game.emulation == EMU_BOULDERDASH ||
4896 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
4897 can_fall_right = FALSE; /* slip down on left side */
4899 can_fall_left = !(can_fall_right = RND(2));
4901 can_fall_both = FALSE;
4907 /* if not determined otherwise, prefer left side for slipping down */
4908 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4909 started_moving = TRUE;
4913 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4915 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4918 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4919 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4920 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4921 int belt_dir = game.belt_dir[belt_nr];
4923 if ((belt_dir == MV_LEFT && left_is_free) ||
4924 (belt_dir == MV_RIGHT && right_is_free))
4926 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4928 InitMovingField(x, y, belt_dir);
4929 started_moving = TRUE;
4931 Pushed[x][y] = TRUE;
4932 Pushed[nextx][y] = TRUE;
4934 GfxAction[x][y] = ACTION_DEFAULT;
4938 MovDir[x][y] = 0; /* if element was moving, stop it */
4943 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4945 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
4947 if (CAN_MOVE(element) && !started_moving)
4950 int move_pattern = element_info[element].move_pattern;
4955 if (MovDir[x][y] == MV_NONE)
4957 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
4958 x, y, element, element_info[element].token_name);
4959 printf("StartMoving(): This should never happen!\n");
4964 Moving2Blocked(x, y, &newx, &newy);
4966 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4969 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4970 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
4972 WasJustMoving[x][y] = 0;
4973 CheckCollision[x][y] = 0;
4975 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4977 if (Feld[x][y] != element) /* element has changed */
4981 if (!MovDelay[x][y]) /* start new movement phase */
4983 /* all objects that can change their move direction after each step
4984 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4986 if (element != EL_YAMYAM &&
4987 element != EL_DARK_YAMYAM &&
4988 element != EL_PACMAN &&
4989 !(move_pattern & MV_ANY_DIRECTION) &&
4990 move_pattern != MV_TURNING_LEFT &&
4991 move_pattern != MV_TURNING_RIGHT &&
4992 move_pattern != MV_TURNING_LEFT_RIGHT &&
4993 move_pattern != MV_TURNING_RIGHT_LEFT &&
4994 move_pattern != MV_TURNING_RANDOM)
4998 if (MovDelay[x][y] && (element == EL_BUG ||
4999 element == EL_SPACESHIP ||
5000 element == EL_SP_SNIKSNAK ||
5001 element == EL_SP_ELECTRON ||
5002 element == EL_MOLE))
5003 DrawLevelField(x, y);
5007 if (MovDelay[x][y]) /* wait some time before next movement */
5011 if (element == EL_ROBOT ||
5012 element == EL_YAMYAM ||
5013 element == EL_DARK_YAMYAM)
5015 DrawLevelElementAnimationIfNeeded(x, y, element);
5016 PlayLevelSoundAction(x, y, ACTION_WAITING);
5018 else if (element == EL_SP_ELECTRON)
5019 DrawLevelElementAnimationIfNeeded(x, y, element);
5020 else if (element == EL_DRAGON)
5023 int dir = MovDir[x][y];
5024 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5025 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5026 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5027 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5028 dir == MV_UP ? IMG_FLAMES_1_UP :
5029 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5030 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5032 GfxAction[x][y] = ACTION_ATTACKING;
5034 if (IS_PLAYER(x, y))
5035 DrawPlayerField(x, y);
5037 DrawLevelField(x, y);
5039 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5041 for (i = 1; i <= 3; i++)
5043 int xx = x + i * dx;
5044 int yy = y + i * dy;
5045 int sx = SCREENX(xx);
5046 int sy = SCREENY(yy);
5047 int flame_graphic = graphic + (i - 1);
5049 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5054 int flamed = MovingOrBlocked2Element(xx, yy);
5058 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5060 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5061 RemoveMovingField(xx, yy);
5063 RemoveField(xx, yy);
5065 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5068 RemoveMovingField(xx, yy);
5071 ChangeDelay[xx][yy] = 0;
5073 Feld[xx][yy] = EL_FLAMES;
5075 if (IN_SCR_FIELD(sx, sy))
5077 DrawLevelFieldCrumbledSand(xx, yy);
5078 DrawGraphic(sx, sy, flame_graphic, frame);
5083 if (Feld[xx][yy] == EL_FLAMES)
5084 Feld[xx][yy] = EL_EMPTY;
5085 DrawLevelField(xx, yy);
5090 if (MovDelay[x][y]) /* element still has to wait some time */
5092 PlayLevelSoundAction(x, y, ACTION_WAITING);
5098 /* now make next step */
5100 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5102 if (DONT_COLLIDE_WITH(element) &&
5103 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5104 !PLAYER_ENEMY_PROTECTED(newx, newy))
5106 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5111 else if (CAN_MOVE_INTO_ACID(element) &&
5112 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5113 (MovDir[x][y] == MV_DOWN ||
5114 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5116 SplashAcid(newx, newy);
5117 Store[x][y] = EL_ACID;
5119 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5121 if (Feld[newx][newy] == EL_EXIT_OPEN)
5124 DrawLevelField(x, y);
5126 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5127 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5128 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5130 local_player->friends_still_needed--;
5131 if (!local_player->friends_still_needed &&
5132 !local_player->GameOver && AllPlayersGone)
5133 local_player->LevelSolved = local_player->GameOver = TRUE;
5137 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5139 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5140 DrawLevelField(newx, newy);
5142 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5144 else if (!IS_FREE(newx, newy))
5146 GfxAction[x][y] = ACTION_WAITING;
5148 if (IS_PLAYER(x, y))
5149 DrawPlayerField(x, y);
5151 DrawLevelField(x, y);
5156 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5158 if (IS_FOOD_PIG(Feld[newx][newy]))
5160 if (IS_MOVING(newx, newy))
5161 RemoveMovingField(newx, newy);
5164 Feld[newx][newy] = EL_EMPTY;
5165 DrawLevelField(newx, newy);
5168 PlayLevelSound(x, y, SND_PIG_DIGGING);
5170 else if (!IS_FREE(newx, newy))
5172 if (IS_PLAYER(x, y))
5173 DrawPlayerField(x, y);
5175 DrawLevelField(x, y);
5180 else if (IS_CUSTOM_ELEMENT(element) &&
5181 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5183 int new_element = Feld[newx][newy];
5185 if (!IS_FREE(newx, newy))
5187 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5188 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5191 /* no element can dig solid indestructible elements */
5192 if (IS_INDESTRUCTIBLE(new_element) &&
5193 !IS_DIGGABLE(new_element) &&
5194 !IS_COLLECTIBLE(new_element))
5197 if (AmoebaNr[newx][newy] &&
5198 (new_element == EL_AMOEBA_FULL ||
5199 new_element == EL_BD_AMOEBA ||
5200 new_element == EL_AMOEBA_GROWING))
5202 AmoebaCnt[AmoebaNr[newx][newy]]--;
5203 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5206 if (IS_MOVING(newx, newy))
5207 RemoveMovingField(newx, newy);
5210 RemoveField(newx, newy);
5211 DrawLevelField(newx, newy);
5214 /* if digged element was about to explode, prevent the explosion */
5215 ExplodeField[newx][newy] = EX_TYPE_NONE;
5217 PlayLevelSoundAction(x, y, action);
5220 Store[newx][newy] = EL_EMPTY;
5221 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5223 int move_leave_element = element_info[element].move_leave_element;
5225 /* this makes it possible to leave the removed element again */
5226 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
5227 new_element : move_leave_element);
5230 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5232 RunnerVisit[x][y] = FrameCounter;
5233 PlayerVisit[x][y] /= 8; /* expire player visit path */
5236 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5238 if (!IS_FREE(newx, newy))
5240 if (IS_PLAYER(x, y))
5241 DrawPlayerField(x, y);
5243 DrawLevelField(x, y);
5249 boolean wanna_flame = !RND(10);
5250 int dx = newx - x, dy = newy - y;
5251 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5252 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5253 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5254 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5255 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5256 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5259 IS_CLASSIC_ENEMY(element1) ||
5260 IS_CLASSIC_ENEMY(element2)) &&
5261 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5262 element1 != EL_FLAMES && element2 != EL_FLAMES)
5264 ResetGfxAnimation(x, y);
5265 GfxAction[x][y] = ACTION_ATTACKING;
5267 if (IS_PLAYER(x, y))
5268 DrawPlayerField(x, y);
5270 DrawLevelField(x, y);
5272 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5274 MovDelay[x][y] = 50;
5278 RemoveField(newx, newy);
5280 Feld[newx][newy] = EL_FLAMES;
5281 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5284 RemoveField(newx1, newy1);
5286 Feld[newx1][newy1] = EL_FLAMES;
5288 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5291 RemoveField(newx2, newy2);
5293 Feld[newx2][newy2] = EL_FLAMES;
5300 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5301 Feld[newx][newy] == EL_DIAMOND)
5303 if (IS_MOVING(newx, newy))
5304 RemoveMovingField(newx, newy);
5307 Feld[newx][newy] = EL_EMPTY;
5308 DrawLevelField(newx, newy);
5311 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5313 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5314 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5316 if (AmoebaNr[newx][newy])
5318 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5319 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5320 Feld[newx][newy] == EL_BD_AMOEBA)
5321 AmoebaCnt[AmoebaNr[newx][newy]]--;
5326 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5328 RemoveMovingField(newx, newy);
5331 if (IS_MOVING(newx, newy))
5333 RemoveMovingField(newx, newy);
5338 Feld[newx][newy] = EL_EMPTY;
5339 DrawLevelField(newx, newy);
5342 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5344 else if ((element == EL_PACMAN || element == EL_MOLE)
5345 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5347 if (AmoebaNr[newx][newy])
5349 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5350 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5351 Feld[newx][newy] == EL_BD_AMOEBA)
5352 AmoebaCnt[AmoebaNr[newx][newy]]--;
5355 if (element == EL_MOLE)
5357 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5358 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5360 ResetGfxAnimation(x, y);
5361 GfxAction[x][y] = ACTION_DIGGING;
5362 DrawLevelField(x, y);
5364 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5366 return; /* wait for shrinking amoeba */
5368 else /* element == EL_PACMAN */
5370 Feld[newx][newy] = EL_EMPTY;
5371 DrawLevelField(newx, newy);
5372 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5375 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5376 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5377 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5379 /* wait for shrinking amoeba to completely disappear */
5382 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5384 /* object was running against a wall */
5389 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
5390 if (move_pattern & MV_ANY_DIRECTION &&
5391 move_pattern == MovDir[x][y])
5393 int blocking_element =
5394 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5396 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5399 element = Feld[x][y]; /* element might have changed */
5403 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5404 DrawLevelElementAnimation(x, y, element);
5406 if (DONT_TOUCH(element))
5407 TestIfBadThingTouchesPlayer(x, y);
5412 InitMovingField(x, y, MovDir[x][y]);
5414 PlayLevelSoundAction(x, y, ACTION_MOVING);
5418 ContinueMoving(x, y);
5421 void ContinueMoving(int x, int y)
5423 int element = Feld[x][y];
5424 int stored = Store[x][y];
5425 struct ElementInfo *ei = &element_info[element];
5426 int direction = MovDir[x][y];
5427 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5428 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5429 int newx = x + dx, newy = y + dy;
5430 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5431 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5432 boolean last_line = (newy == lev_fieldy - 1);
5434 MovPos[x][y] += getElementMoveStepsize(x, y);
5436 if (pushed_by_player) /* special case: moving object pushed by player */
5437 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5439 if (ABS(MovPos[x][y]) < TILEX)
5441 DrawLevelField(x, y);
5443 return; /* element is still moving */
5446 /* element reached destination field */
5448 Feld[x][y] = EL_EMPTY;
5449 Feld[newx][newy] = element;
5450 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5452 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
5454 element = Feld[newx][newy] = EL_ACID;
5456 else if (element == EL_MOLE)
5458 Feld[x][y] = EL_SAND;
5460 DrawLevelFieldCrumbledSandNeighbours(x, y);
5462 else if (element == EL_QUICKSAND_FILLING)
5464 element = Feld[newx][newy] = get_next_element(element);
5465 Store[newx][newy] = Store[x][y];
5467 else if (element == EL_QUICKSAND_EMPTYING)
5469 Feld[x][y] = get_next_element(element);
5470 element = Feld[newx][newy] = Store[x][y];
5472 else if (element == EL_MAGIC_WALL_FILLING)
5474 element = Feld[newx][newy] = get_next_element(element);
5475 if (!game.magic_wall_active)
5476 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5477 Store[newx][newy] = Store[x][y];
5479 else if (element == EL_MAGIC_WALL_EMPTYING)
5481 Feld[x][y] = get_next_element(element);
5482 if (!game.magic_wall_active)
5483 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5484 element = Feld[newx][newy] = Store[x][y];
5486 #if USE_NEW_CUSTOM_VALUE
5487 InitField(newx, newy, FALSE);
5490 else if (element == EL_BD_MAGIC_WALL_FILLING)
5492 element = Feld[newx][newy] = get_next_element(element);
5493 if (!game.magic_wall_active)
5494 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5495 Store[newx][newy] = Store[x][y];
5497 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5499 Feld[x][y] = get_next_element(element);
5500 if (!game.magic_wall_active)
5501 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5502 element = Feld[newx][newy] = Store[x][y];
5504 #if USE_NEW_CUSTOM_VALUE
5505 InitField(newx, newy, FALSE);
5508 else if (element == EL_AMOEBA_DROPPING)
5510 Feld[x][y] = get_next_element(element);
5511 element = Feld[newx][newy] = Store[x][y];
5513 else if (element == EL_SOKOBAN_OBJECT)
5516 Feld[x][y] = Back[x][y];
5518 if (Back[newx][newy])
5519 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5521 Back[x][y] = Back[newx][newy] = 0;
5524 Store[x][y] = EL_EMPTY;
5529 MovDelay[newx][newy] = 0;
5531 if (CAN_CHANGE(element))
5533 /* copy element change control values to new field */
5534 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5535 ChangePage[newx][newy] = ChangePage[x][y];
5536 Changed[newx][newy] = Changed[x][y];
5537 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5539 #if USE_NEW_CUSTOM_VALUE
5540 CustomValue[newx][newy] = CustomValue[x][y];
5544 ChangeDelay[x][y] = 0;
5545 ChangePage[x][y] = -1;
5546 Changed[x][y] = FALSE;
5547 ChangeEvent[x][y] = -1;
5549 #if USE_NEW_CUSTOM_VALUE
5550 CustomValue[x][y] = 0;
5553 /* copy animation control values to new field */
5554 GfxFrame[newx][newy] = GfxFrame[x][y];
5555 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5556 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5557 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5559 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5561 /* some elements can leave other elements behind after moving */
5562 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
5563 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
5564 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
5566 int move_leave_element = ei->move_leave_element;
5568 /* this makes it possible to leave the removed element again */
5569 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
5570 ei->move_leave_element == EL_TRIGGER_ELEMENT)
5571 move_leave_element = stored;
5573 Feld[x][y] = move_leave_element;
5575 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
5576 MovDir[x][y] = direction;
5578 InitField(x, y, FALSE);
5580 if (GFX_CRUMBLED(Feld[x][y]))
5581 DrawLevelFieldCrumbledSandNeighbours(x, y);
5583 if (ELEM_IS_PLAYER(move_leave_element))
5584 RelocatePlayer(x, y, move_leave_element);
5587 /* do this after checking for left-behind element */
5588 ResetGfxAnimation(x, y); /* reset animation values for old field */
5590 if (!CAN_MOVE(element) ||
5591 (CAN_FALL(element) && direction == MV_DOWN &&
5592 (element == EL_SPRING ||
5593 element_info[element].move_pattern == MV_WHEN_PUSHED ||
5594 element_info[element].move_pattern == MV_WHEN_DROPPED)))
5595 GfxDir[x][y] = MovDir[newx][newy] = 0;
5597 DrawLevelField(x, y);
5598 DrawLevelField(newx, newy);
5600 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5602 /* prevent pushed element from moving on in pushed direction */
5603 if (pushed_by_player && CAN_MOVE(element) &&
5604 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5605 !(element_info[element].move_pattern & direction))
5606 TurnRound(newx, newy);
5608 /* prevent elements on conveyor belt from moving on in last direction */
5609 if (pushed_by_conveyor && CAN_FALL(element) &&
5610 direction & MV_HORIZONTAL)
5611 MovDir[newx][newy] = 0;
5613 if (!pushed_by_player)
5615 int nextx = newx + dx, nexty = newy + dy;
5616 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
5618 WasJustMoving[newx][newy] = 3;
5620 if (CAN_FALL(element) && direction == MV_DOWN)
5621 WasJustFalling[newx][newy] = 3;
5623 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
5624 CheckCollision[newx][newy] = 2;
5627 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5629 TestIfBadThingTouchesPlayer(newx, newy);
5630 TestIfBadThingTouchesFriend(newx, newy);
5632 if (!IS_CUSTOM_ELEMENT(element))
5633 TestIfBadThingTouchesOtherBadThing(newx, newy);
5635 else if (element == EL_PENGUIN)
5636 TestIfFriendTouchesBadThing(newx, newy);
5638 /* give the player one last chance (one more frame) to move away */
5639 if (CAN_FALL(element) && direction == MV_DOWN &&
5640 (last_line || (!IS_FREE(x, newy + 1) &&
5641 (!IS_PLAYER(x, newy + 1) ||
5642 game.engine_version < VERSION_IDENT(3,1,1,0)))))
5645 if (pushed_by_player && !game.use_change_when_pushing_bug)
5647 int dig_side = MV_DIR_OPPOSITE(direction);
5648 struct PlayerInfo *player = PLAYERINFO(x, y);
5650 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
5651 player->index_bit, dig_side);
5652 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
5653 player->index_bit, dig_side);
5656 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5658 TestIfElementHitsCustomElement(newx, newy, direction);
5659 TestIfPlayerTouchesCustomElement(newx, newy);
5660 TestIfElementTouchesCustomElement(newx, newy);
5663 int AmoebeNachbarNr(int ax, int ay)
5666 int element = Feld[ax][ay];
5668 static int xy[4][2] =
5676 for (i = 0; i < NUM_DIRECTIONS; i++)
5678 int x = ax + xy[i][0];
5679 int y = ay + xy[i][1];
5681 if (!IN_LEV_FIELD(x, y))
5684 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5685 group_nr = AmoebaNr[x][y];
5691 void AmoebenVereinigen(int ax, int ay)
5693 int i, x, y, xx, yy;
5694 int new_group_nr = AmoebaNr[ax][ay];
5695 static int xy[4][2] =
5703 if (new_group_nr == 0)
5706 for (i = 0; i < NUM_DIRECTIONS; i++)
5711 if (!IN_LEV_FIELD(x, y))
5714 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5715 Feld[x][y] == EL_BD_AMOEBA ||
5716 Feld[x][y] == EL_AMOEBA_DEAD) &&
5717 AmoebaNr[x][y] != new_group_nr)
5719 int old_group_nr = AmoebaNr[x][y];
5721 if (old_group_nr == 0)
5724 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5725 AmoebaCnt[old_group_nr] = 0;
5726 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5727 AmoebaCnt2[old_group_nr] = 0;
5729 for (yy = 0; yy < lev_fieldy; yy++)
5731 for (xx = 0; xx < lev_fieldx; xx++)
5733 if (AmoebaNr[xx][yy] == old_group_nr)
5734 AmoebaNr[xx][yy] = new_group_nr;
5741 void AmoebeUmwandeln(int ax, int ay)
5745 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5747 int group_nr = AmoebaNr[ax][ay];
5752 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5753 printf("AmoebeUmwandeln(): This should never happen!\n");
5758 for (y = 0; y < lev_fieldy; y++)
5760 for (x = 0; x < lev_fieldx; x++)
5762 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5765 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5769 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5770 SND_AMOEBA_TURNING_TO_GEM :
5771 SND_AMOEBA_TURNING_TO_ROCK));
5776 static int xy[4][2] =
5784 for (i = 0; i < NUM_DIRECTIONS; i++)
5789 if (!IN_LEV_FIELD(x, y))
5792 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5794 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5795 SND_AMOEBA_TURNING_TO_GEM :
5796 SND_AMOEBA_TURNING_TO_ROCK));
5803 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5806 int group_nr = AmoebaNr[ax][ay];
5807 boolean done = FALSE;
5812 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5813 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5818 for (y = 0; y < lev_fieldy; y++)
5820 for (x = 0; x < lev_fieldx; x++)
5822 if (AmoebaNr[x][y] == group_nr &&
5823 (Feld[x][y] == EL_AMOEBA_DEAD ||
5824 Feld[x][y] == EL_BD_AMOEBA ||
5825 Feld[x][y] == EL_AMOEBA_GROWING))
5828 Feld[x][y] = new_element;
5829 InitField(x, y, FALSE);
5830 DrawLevelField(x, y);
5837 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5838 SND_BD_AMOEBA_TURNING_TO_ROCK :
5839 SND_BD_AMOEBA_TURNING_TO_GEM));
5842 void AmoebeWaechst(int x, int y)
5844 static unsigned long sound_delay = 0;
5845 static unsigned long sound_delay_value = 0;
5847 if (!MovDelay[x][y]) /* start new growing cycle */
5851 if (DelayReached(&sound_delay, sound_delay_value))
5853 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5854 sound_delay_value = 30;
5858 if (MovDelay[x][y]) /* wait some time before growing bigger */
5861 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5863 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5864 6 - MovDelay[x][y]);
5866 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5869 if (!MovDelay[x][y])
5871 Feld[x][y] = Store[x][y];
5873 DrawLevelField(x, y);
5878 void AmoebaDisappearing(int x, int y)
5880 static unsigned long sound_delay = 0;
5881 static unsigned long sound_delay_value = 0;
5883 if (!MovDelay[x][y]) /* start new shrinking cycle */
5887 if (DelayReached(&sound_delay, sound_delay_value))
5888 sound_delay_value = 30;
5891 if (MovDelay[x][y]) /* wait some time before shrinking */
5894 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5896 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5897 6 - MovDelay[x][y]);
5899 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5902 if (!MovDelay[x][y])
5904 Feld[x][y] = EL_EMPTY;
5905 DrawLevelField(x, y);
5907 /* don't let mole enter this field in this cycle;
5908 (give priority to objects falling to this field from above) */
5914 void AmoebeAbleger(int ax, int ay)
5917 int element = Feld[ax][ay];
5918 int graphic = el2img(element);
5919 int newax = ax, neway = ay;
5920 static int xy[4][2] =
5928 if (!level.amoeba_speed)
5930 Feld[ax][ay] = EL_AMOEBA_DEAD;
5931 DrawLevelField(ax, ay);
5935 if (IS_ANIMATED(graphic))
5936 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5938 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5939 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5941 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5944 if (MovDelay[ax][ay])
5948 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5951 int x = ax + xy[start][0];
5952 int y = ay + xy[start][1];
5954 if (!IN_LEV_FIELD(x, y))
5957 if (IS_FREE(x, y) ||
5958 CAN_GROW_INTO(Feld[x][y]) ||
5959 Feld[x][y] == EL_QUICKSAND_EMPTY)
5965 if (newax == ax && neway == ay)
5968 else /* normal or "filled" (BD style) amoeba */
5971 boolean waiting_for_player = FALSE;
5973 for (i = 0; i < NUM_DIRECTIONS; i++)
5975 int j = (start + i) % 4;
5976 int x = ax + xy[j][0];
5977 int y = ay + xy[j][1];
5979 if (!IN_LEV_FIELD(x, y))
5982 if (IS_FREE(x, y) ||
5983 CAN_GROW_INTO(Feld[x][y]) ||
5984 Feld[x][y] == EL_QUICKSAND_EMPTY)
5990 else if (IS_PLAYER(x, y))
5991 waiting_for_player = TRUE;
5994 if (newax == ax && neway == ay) /* amoeba cannot grow */
5996 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
5998 Feld[ax][ay] = EL_AMOEBA_DEAD;
5999 DrawLevelField(ax, ay);
6000 AmoebaCnt[AmoebaNr[ax][ay]]--;
6002 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6004 if (element == EL_AMOEBA_FULL)
6005 AmoebeUmwandeln(ax, ay);
6006 else if (element == EL_BD_AMOEBA)
6007 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6012 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6014 /* amoeba gets larger by growing in some direction */
6016 int new_group_nr = AmoebaNr[ax][ay];
6019 if (new_group_nr == 0)
6021 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6022 printf("AmoebeAbleger(): This should never happen!\n");
6027 AmoebaNr[newax][neway] = new_group_nr;
6028 AmoebaCnt[new_group_nr]++;
6029 AmoebaCnt2[new_group_nr]++;
6031 /* if amoeba touches other amoeba(s) after growing, unify them */
6032 AmoebenVereinigen(newax, neway);
6034 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6036 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6042 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6043 (neway == lev_fieldy - 1 && newax != ax))
6045 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6046 Store[newax][neway] = element;
6048 else if (neway == ay)
6050 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6052 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6056 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6057 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6058 Store[ax][ay] = EL_AMOEBA_DROP;
6059 ContinueMoving(ax, ay);
6063 DrawLevelField(newax, neway);
6066 void Life(int ax, int ay)
6070 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6073 int element = Feld[ax][ay];
6074 int graphic = el2img(element);
6075 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6077 boolean changed = FALSE;
6079 if (IS_ANIMATED(graphic))
6080 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6085 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6086 MovDelay[ax][ay] = life_time;
6088 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6091 if (MovDelay[ax][ay])
6095 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6097 int xx = ax+x1, yy = ay+y1;
6100 if (!IN_LEV_FIELD(xx, yy))
6103 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6105 int x = xx+x2, y = yy+y2;
6107 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6110 if (((Feld[x][y] == element ||
6111 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6113 (IS_FREE(x, y) && Stop[x][y]))
6117 if (xx == ax && yy == ay) /* field in the middle */
6119 if (nachbarn < life_parameter[0] ||
6120 nachbarn > life_parameter[1])
6122 Feld[xx][yy] = EL_EMPTY;
6124 DrawLevelField(xx, yy);
6125 Stop[xx][yy] = TRUE;
6129 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6130 { /* free border field */
6131 if (nachbarn >= life_parameter[2] &&
6132 nachbarn <= life_parameter[3])
6134 Feld[xx][yy] = element;
6135 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6137 DrawLevelField(xx, yy);
6138 Stop[xx][yy] = TRUE;
6145 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6146 SND_GAME_OF_LIFE_GROWING);
6149 static void InitRobotWheel(int x, int y)
6151 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6154 static void RunRobotWheel(int x, int y)
6156 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6159 static void StopRobotWheel(int x, int y)
6161 if (ZX == x && ZY == y)
6165 static void InitTimegateWheel(int x, int y)
6167 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6170 static void RunTimegateWheel(int x, int y)
6172 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6175 void CheckExit(int x, int y)
6177 if (local_player->gems_still_needed > 0 ||
6178 local_player->sokobanfields_still_needed > 0 ||
6179 local_player->lights_still_needed > 0)
6181 int element = Feld[x][y];
6182 int graphic = el2img(element);
6184 if (IS_ANIMATED(graphic))
6185 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6190 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6193 Feld[x][y] = EL_EXIT_OPENING;
6195 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6198 void CheckExitSP(int x, int y)
6200 if (local_player->gems_still_needed > 0)
6202 int element = Feld[x][y];
6203 int graphic = el2img(element);
6205 if (IS_ANIMATED(graphic))
6206 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6211 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6214 Feld[x][y] = EL_SP_EXIT_OPENING;
6216 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6219 static void CloseAllOpenTimegates()
6223 for (y = 0; y < lev_fieldy; y++)
6225 for (x = 0; x < lev_fieldx; x++)
6227 int element = Feld[x][y];
6229 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6231 Feld[x][y] = EL_TIMEGATE_CLOSING;
6233 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6239 void EdelsteinFunkeln(int x, int y)
6241 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6244 if (Feld[x][y] == EL_BD_DIAMOND)
6247 if (MovDelay[x][y] == 0) /* next animation frame */
6248 MovDelay[x][y] = 11 * !SimpleRND(500);
6250 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6254 if (setup.direct_draw && MovDelay[x][y])
6255 SetDrawtoField(DRAW_BUFFERED);
6257 DrawLevelElementAnimation(x, y, Feld[x][y]);
6259 if (MovDelay[x][y] != 0)
6261 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6262 10 - MovDelay[x][y]);
6264 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6266 if (setup.direct_draw)
6270 dest_x = FX + SCREENX(x) * TILEX;
6271 dest_y = FY + SCREENY(y) * TILEY;
6273 BlitBitmap(drawto_field, window,
6274 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6275 SetDrawtoField(DRAW_DIRECT);
6281 void MauerWaechst(int x, int y)
6285 if (!MovDelay[x][y]) /* next animation frame */
6286 MovDelay[x][y] = 3 * delay;
6288 if (MovDelay[x][y]) /* wait some time before next frame */
6292 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6294 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6295 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6297 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6300 if (!MovDelay[x][y])
6302 if (MovDir[x][y] == MV_LEFT)
6304 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6305 DrawLevelField(x - 1, y);
6307 else if (MovDir[x][y] == MV_RIGHT)
6309 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6310 DrawLevelField(x + 1, y);
6312 else if (MovDir[x][y] == MV_UP)
6314 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6315 DrawLevelField(x, y - 1);
6319 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6320 DrawLevelField(x, y + 1);
6323 Feld[x][y] = Store[x][y];
6325 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6326 DrawLevelField(x, y);
6331 void MauerAbleger(int ax, int ay)
6333 int element = Feld[ax][ay];
6334 int graphic = el2img(element);
6335 boolean oben_frei = FALSE, unten_frei = FALSE;
6336 boolean links_frei = FALSE, rechts_frei = FALSE;
6337 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6338 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6339 boolean new_wall = FALSE;
6341 if (IS_ANIMATED(graphic))
6342 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6344 if (!MovDelay[ax][ay]) /* start building new wall */
6345 MovDelay[ax][ay] = 6;
6347 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6350 if (MovDelay[ax][ay])
6354 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6356 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6358 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6360 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6363 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6364 element == EL_EXPANDABLE_WALL_ANY)
6368 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6369 Store[ax][ay-1] = element;
6370 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6371 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6372 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6373 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6378 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6379 Store[ax][ay+1] = element;
6380 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6381 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6382 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6383 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6388 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6389 element == EL_EXPANDABLE_WALL_ANY ||
6390 element == EL_EXPANDABLE_WALL)
6394 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6395 Store[ax-1][ay] = element;
6396 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6397 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6398 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6399 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6405 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6406 Store[ax+1][ay] = element;
6407 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6408 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6409 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6410 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6415 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6416 DrawLevelField(ax, ay);
6418 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6420 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6421 unten_massiv = TRUE;
6422 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6423 links_massiv = TRUE;
6424 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6425 rechts_massiv = TRUE;
6427 if (((oben_massiv && unten_massiv) ||
6428 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6429 element == EL_EXPANDABLE_WALL) &&
6430 ((links_massiv && rechts_massiv) ||
6431 element == EL_EXPANDABLE_WALL_VERTICAL))
6432 Feld[ax][ay] = EL_WALL;
6435 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6438 void CheckForDragon(int x, int y)
6441 boolean dragon_found = FALSE;
6442 static int xy[4][2] =
6450 for (i = 0; i < NUM_DIRECTIONS; i++)
6452 for (j = 0; j < 4; j++)
6454 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6456 if (IN_LEV_FIELD(xx, yy) &&
6457 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6459 if (Feld[xx][yy] == EL_DRAGON)
6460 dragon_found = TRUE;
6469 for (i = 0; i < NUM_DIRECTIONS; i++)
6471 for (j = 0; j < 3; j++)
6473 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6475 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6477 Feld[xx][yy] = EL_EMPTY;
6478 DrawLevelField(xx, yy);
6487 static void InitBuggyBase(int x, int y)
6489 int element = Feld[x][y];
6490 int activating_delay = FRAMES_PER_SECOND / 4;
6493 (element == EL_SP_BUGGY_BASE ?
6494 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6495 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6497 element == EL_SP_BUGGY_BASE_ACTIVE ?
6498 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6501 static void WarnBuggyBase(int x, int y)
6504 static int xy[4][2] =
6512 for (i = 0; i < NUM_DIRECTIONS; i++)
6514 int xx = x + xy[i][0], yy = y + xy[i][1];
6516 if (IS_PLAYER(xx, yy))
6518 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6525 static void InitTrap(int x, int y)
6527 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6530 static void ActivateTrap(int x, int y)
6532 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6535 static void ChangeActiveTrap(int x, int y)
6537 int graphic = IMG_TRAP_ACTIVE;
6539 /* if new animation frame was drawn, correct crumbled sand border */
6540 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6541 DrawLevelFieldCrumbledSand(x, y);
6544 static int getSpecialActionElement(int element, int number, int base_element)
6546 return (element != EL_EMPTY ? element :
6547 number != -1 ? base_element + number - 1 :
6551 static int getModifiedActionNumber(int value_old, int operator, int operand,
6552 int value_min, int value_max)
6554 int value_new = (operator == CA_MODE_SET ? operand :
6555 operator == CA_MODE_ADD ? value_old + operand :
6556 operator == CA_MODE_SUBTRACT ? value_old - operand :
6557 operator == CA_MODE_MULTIPLY ? value_old * operand :
6558 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
6559 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
6562 return (value_new < value_min ? value_min :
6563 value_new > value_max ? value_max :
6567 static void ExecuteCustomElementAction(int x, int y, int element, int page)
6569 struct ElementInfo *ei = &element_info[element];
6570 struct ElementChangeInfo *change = &ei->change_page[page];
6571 int action_type = change->action_type;
6572 int action_mode = change->action_mode;
6573 int action_arg = change->action_arg;
6576 if (!change->has_action)
6579 /* ---------- determine action paramater values ---------- */
6581 int action_arg_element =
6582 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
6583 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
6584 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
6587 int action_arg_direction =
6588 (action_arg >= CA_ARG_DIRECTION_LEFT &&
6589 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
6590 action_arg == CA_ARG_DIRECTION_TRIGGER ?
6591 change->actual_trigger_side :
6592 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
6593 MV_DIR_OPPOSITE(change->actual_trigger_side) :
6596 int action_arg_number_min =
6597 (action_type == CA_SET_SPEED ? MOVE_STEPSIZE_MIN :
6600 int action_arg_number_max =
6601 (action_type == CA_SET_SPEED ? MOVE_STEPSIZE_MAX :
6602 action_type == CA_SET_GEMS ? 999 :
6603 action_type == CA_SET_TIME ? 9999 :
6604 action_type == CA_SET_SCORE ? 99999 :
6605 action_type == CA_SET_CE_SCORE ? 9999 :
6606 action_type == CA_SET_CE_VALUE ? 9999 :
6609 int action_arg_number_reset =
6610 (action_type == CA_SET_SPEED ? TILEX / game.initial_move_delay_value :
6611 action_type == CA_SET_GEMS ? level.gems_needed :
6612 action_type == CA_SET_TIME ? level.time :
6613 action_type == CA_SET_SCORE ? 0 :
6614 action_type == CA_SET_CE_SCORE ? 0 :
6616 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
6618 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
6622 int action_arg_number =
6623 (action_arg <= CA_ARG_MAX ? action_arg :
6624 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
6625 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
6626 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
6627 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
6628 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
6629 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
6630 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
6631 #if USE_NEW_CUSTOM_VALUE
6632 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
6634 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
6636 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) :
6637 action_arg == CA_ARG_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
6638 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value :
6641 int action_arg_number_old =
6642 (action_type == CA_SET_GEMS ? local_player->gems_still_needed :
6643 action_type == CA_SET_TIME ? TimeLeft :
6644 action_type == CA_SET_SCORE ? local_player->score :
6645 action_type == CA_SET_CE_SCORE ? ei->collect_score :
6646 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
6649 int action_arg_number_new =
6650 getModifiedActionNumber(action_arg_number_old,
6651 action_mode, action_arg_number,
6652 action_arg_number_min, action_arg_number_max);
6654 int trigger_player_bits =
6655 (change->actual_trigger_player >= EL_PLAYER_1 &&
6656 change->actual_trigger_player <= EL_PLAYER_4 ?
6657 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
6660 int action_arg_player_bits =
6661 (action_arg >= CA_ARG_PLAYER_1 &&
6662 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
6663 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
6666 /* ---------- execute action ---------- */
6675 case CA_EXIT_PLAYER:
6677 for (i = 0; i < MAX_PLAYERS; i++)
6678 if (action_arg_player_bits & (1 << i))
6679 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
6684 case CA_KILL_PLAYER:
6686 for (i = 0; i < MAX_PLAYERS; i++)
6687 if (action_arg_player_bits & (1 << i))
6688 KillPlayer(&stored_player[i]);
6693 case CA_MOVE_PLAYER:
6695 /* automatically move to the next field in specified direction */
6696 for (i = 0; i < MAX_PLAYERS; i++)
6697 if (trigger_player_bits & (1 << i))
6698 stored_player[i].programmed_action = action_arg_direction;
6703 case CA_RESTART_LEVEL:
6705 game.restart_level = TRUE;
6710 case CA_SHOW_ENVELOPE:
6712 int element = getSpecialActionElement(action_arg_element,
6713 action_arg_number, EL_ENVELOPE_1);
6715 if (IS_ENVELOPE(element))
6716 local_player->show_envelope = element;
6723 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
6724 int element = getSpecialActionElement(action_arg_element,
6725 action_arg_number, EL_KEY_1);
6727 if (IS_KEY(element))
6729 for (i = 0; i < MAX_PLAYERS; i++)
6731 if (trigger_player_bits & (1 << i))
6733 stored_player[i].key[KEY_NR(element)] = key_state;
6735 DrawGameValue_Keys(stored_player[i].key);
6737 redraw_mask |= REDRAW_DOOR_1;
6747 for (i = 0; i < MAX_PLAYERS; i++)
6749 if (trigger_player_bits & (1 << i))
6751 int move_stepsize = TILEX / stored_player[i].move_delay_value;
6753 if (action_arg == CA_ARG_SPEED_SLOWER ||
6754 action_arg == CA_ARG_SPEED_FASTER)
6756 action_arg_number = 2;
6757 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
6762 getModifiedActionNumber(move_stepsize,
6765 action_arg_number_min,
6766 action_arg_number_max);
6768 /* make sure that value is power of 2 */
6769 move_stepsize = (1 << log_2(move_stepsize));
6771 /* do no immediately change -- the player might just be moving */
6772 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
6774 stored_player[i].cannot_move =
6775 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
6778 printf("::: move_delay_value == %d [%d]\n",
6779 stored_player[i].move_delay_value_next, action_arg_number);
6787 case CA_SET_GRAVITY:
6789 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
6790 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
6791 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
6798 game.wind_direction = action_arg_direction;
6805 local_player->gems_still_needed = action_arg_number_new;
6807 DrawGameValue_Emeralds(local_player->gems_still_needed);
6814 if (level.time > 0) /* only modify limited time value */
6816 TimeLeft = action_arg_number_new;
6818 DrawGameValue_Time(TimeLeft);
6820 if (!TimeLeft && setup.time_limit)
6821 for (i = 0; i < MAX_PLAYERS; i++)
6822 KillPlayer(&stored_player[i]);
6830 local_player->score = action_arg_number_new;
6832 DrawGameValue_Score(local_player->score);
6837 case CA_SET_CE_SCORE:
6839 ei->collect_score = action_arg_number_new;
6844 case CA_SET_CE_VALUE:
6846 #if USE_NEW_CUSTOM_VALUE
6847 int last_custom_value = CustomValue[x][y];
6849 CustomValue[x][y] = action_arg_number_new;
6852 printf("::: Count == %d\n", CustomValue[x][y]);
6855 if (CustomValue[x][y] == 0 && last_custom_value > 0)
6858 printf("::: CE_VALUE_GETS_ZERO\n");
6861 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
6862 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
6870 case CA_SET_DYNABOMB_NUMBER:
6872 printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n");
6877 case CA_SET_DYNABOMB_SIZE:
6879 printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n");
6884 case CA_SET_DYNABOMB_POWER:
6886 printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n");
6897 static void ChangeElementNowExt(struct ElementChangeInfo *change,
6898 int x, int y, int target_element)
6900 int previous_move_direction = MovDir[x][y];
6901 #if USE_NEW_CUSTOM_VALUE
6902 int last_ce_value = CustomValue[x][y];
6904 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
6905 IS_WALKABLE(Feld[x][y]));
6907 /* check if element under player changes from accessible to unaccessible
6908 (needed for special case of dropping element which then changes) */
6909 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6910 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6918 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6919 RemoveMovingField(x, y);
6923 Feld[x][y] = target_element;
6925 ResetGfxAnimation(x, y);
6926 ResetRandomAnimationValue(x, y);
6928 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6929 MovDir[x][y] = previous_move_direction;
6931 #if USE_NEW_CUSTOM_VALUE
6932 if (element_info[Feld[x][y]].use_last_ce_value)
6933 CustomValue[x][y] = last_ce_value;
6936 InitField_WithBug1(x, y, FALSE);
6938 DrawLevelField(x, y);
6940 if (GFX_CRUMBLED(Feld[x][y]))
6941 DrawLevelFieldCrumbledSandNeighbours(x, y);
6944 /* "Changed[][]" not set yet to allow "entered by player" change one time */
6945 if (ELEM_IS_PLAYER(target_element))
6946 RelocatePlayer(x, y, target_element);
6949 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
6951 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6954 TestIfBadThingTouchesPlayer(x, y);
6955 TestIfPlayerTouchesCustomElement(x, y);
6956 TestIfElementTouchesCustomElement(x, y);
6959 static boolean ChangeElementNow(int x, int y, int element, int page)
6961 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6963 int old_element = Feld[x][y];
6965 /* always use default change event to prevent running into a loop */
6966 if (ChangeEvent[x][y] == -1)
6967 ChangeEvent[x][y] = CE_DELAY;
6969 if (ChangeEvent[x][y] == CE_DELAY)
6971 /* reset actual trigger element, trigger player and action element */
6972 change->actual_trigger_element = EL_EMPTY;
6973 change->actual_trigger_player = EL_PLAYER_1;
6974 change->actual_trigger_side = CH_SIDE_NONE;
6975 change->actual_trigger_ce_value = 0;
6979 /* do not change any elements that have already changed in this frame */
6983 /* do not change already changed elements with same change event */
6984 if (Changed[x][y] & ChangeEvent[x][y])
6989 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
6991 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6994 if (change->explode)
7001 if (change->use_target_content)
7003 boolean complete_replace = TRUE;
7004 boolean can_replace[3][3];
7007 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7010 boolean is_walkable;
7011 boolean is_diggable;
7012 boolean is_collectible;
7013 boolean is_removable;
7014 boolean is_destructible;
7015 int ex = x + xx - 1;
7016 int ey = y + yy - 1;
7017 int content_element = change->target_content.e[xx][yy];
7020 can_replace[xx][yy] = TRUE;
7022 if (ex == x && ey == y) /* do not check changing element itself */
7025 if (content_element == EL_EMPTY_SPACE)
7027 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7032 if (!IN_LEV_FIELD(ex, ey))
7034 can_replace[xx][yy] = FALSE;
7035 complete_replace = FALSE;
7042 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7043 e = MovingOrBlocked2Element(ex, ey);
7045 is_empty = (IS_FREE(ex, ey) ||
7046 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7048 is_walkable = (is_empty || IS_WALKABLE(e));
7049 is_diggable = (is_empty || IS_DIGGABLE(e));
7050 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7051 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7052 is_removable = (is_diggable || is_collectible);
7054 can_replace[xx][yy] =
7055 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7056 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7057 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7058 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7059 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7060 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7061 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7063 if (!can_replace[xx][yy])
7064 complete_replace = FALSE;
7067 if (!change->only_if_complete || complete_replace)
7069 boolean something_has_changed = FALSE;
7071 if (change->only_if_complete && change->use_random_replace &&
7072 RND(100) < change->random_percentage)
7075 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7077 int ex = x + xx - 1;
7078 int ey = y + yy - 1;
7079 int content_element;
7081 if (can_replace[xx][yy] && (!change->use_random_replace ||
7082 RND(100) < change->random_percentage))
7084 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7085 RemoveMovingField(ex, ey);
7087 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7089 content_element = change->target_content.e[xx][yy];
7090 target_element = GET_TARGET_ELEMENT(content_element, change);
7092 ChangeElementNowExt(change, ex, ey, target_element);
7094 something_has_changed = TRUE;
7096 /* for symmetry reasons, freeze newly created border elements */
7097 if (ex != x || ey != y)
7098 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7102 if (something_has_changed)
7104 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7105 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7111 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7113 ChangeElementNowExt(change, x, y, target_element);
7115 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7116 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7119 /* this uses direct change before indirect change */
7120 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
7125 #if USE_NEW_DELAYED_ACTION
7127 static void ChangeElement(int x, int y, int page)
7129 int element = MovingOrBlocked2Element(x, y);
7130 struct ElementInfo *ei = &element_info[element];
7131 struct ElementChangeInfo *change = &ei->change_page[page];
7134 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
7135 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
7138 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7139 x, y, element, element_info[element].token_name);
7140 printf("ChangeElement(): This should never happen!\n");
7145 /* this can happen with classic bombs on walkable, changing elements */
7146 if (!CAN_CHANGE_OR_HAS_ACTION(element))
7149 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7150 ChangeDelay[x][y] = 0;
7156 if (ChangeDelay[x][y] == 0) /* initialize element change */
7158 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7160 if (change->can_change)
7162 ResetGfxAnimation(x, y);
7163 ResetRandomAnimationValue(x, y);
7165 if (change->pre_change_function)
7166 change->pre_change_function(x, y);
7170 ChangeDelay[x][y]--;
7172 if (ChangeDelay[x][y] != 0) /* continue element change */
7174 if (change->can_change)
7176 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7178 if (IS_ANIMATED(graphic))
7179 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7181 if (change->change_function)
7182 change->change_function(x, y);
7185 else /* finish element change */
7187 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7189 page = ChangePage[x][y];
7190 ChangePage[x][y] = -1;
7192 change = &ei->change_page[page];
7195 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7197 ChangeDelay[x][y] = 1; /* try change after next move step */
7198 ChangePage[x][y] = page; /* remember page to use for change */
7203 if (change->can_change)
7205 if (ChangeElementNow(x, y, element, page))
7207 if (change->post_change_function)
7208 change->post_change_function(x, y);
7212 if (change->has_action)
7213 ExecuteCustomElementAction(x, y, element, page);
7219 static void ChangeElement(int x, int y, int page)
7221 int element = MovingOrBlocked2Element(x, y);
7222 struct ElementInfo *ei = &element_info[element];
7223 struct ElementChangeInfo *change = &ei->change_page[page];
7226 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7229 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7230 x, y, element, element_info[element].token_name);
7231 printf("ChangeElement(): This should never happen!\n");
7236 /* this can happen with classic bombs on walkable, changing elements */
7237 if (!CAN_CHANGE(element))
7240 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7241 ChangeDelay[x][y] = 0;
7247 if (ChangeDelay[x][y] == 0) /* initialize element change */
7249 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7251 ResetGfxAnimation(x, y);
7252 ResetRandomAnimationValue(x, y);
7254 if (change->pre_change_function)
7255 change->pre_change_function(x, y);
7258 ChangeDelay[x][y]--;
7260 if (ChangeDelay[x][y] != 0) /* continue element change */
7262 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7264 if (IS_ANIMATED(graphic))
7265 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7267 if (change->change_function)
7268 change->change_function(x, y);
7270 else /* finish element change */
7272 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7274 page = ChangePage[x][y];
7275 ChangePage[x][y] = -1;
7277 change = &ei->change_page[page];
7280 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7282 ChangeDelay[x][y] = 1; /* try change after next move step */
7283 ChangePage[x][y] = page; /* remember page to use for change */
7288 if (ChangeElementNow(x, y, element, page))
7290 if (change->post_change_function)
7291 change->post_change_function(x, y);
7298 static boolean CheckTriggeredElementChangeExt(int x, int y,
7299 int trigger_element,
7305 boolean change_done_any = FALSE;
7306 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7309 if (!(trigger_events[trigger_element][trigger_event]))
7312 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7314 int element = EL_CUSTOM_START + i;
7315 boolean change_done = FALSE;
7318 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7319 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7322 for (p = 0; p < element_info[element].num_change_pages; p++)
7324 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7326 if (change->can_change_or_has_action &&
7327 change->has_event[trigger_event] &&
7328 change->trigger_side & trigger_side &&
7329 change->trigger_player & trigger_player &&
7330 change->trigger_page & trigger_page_bits &&
7331 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7333 change->actual_trigger_element = trigger_element;
7334 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7335 change->actual_trigger_side = trigger_side;
7336 change->actual_trigger_ce_value = CustomValue[x][y];
7338 if ((change->can_change && !change_done) || change->has_action)
7342 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7344 if (Feld[x][y] == element)
7346 if (change->can_change && !change_done)
7348 ChangeDelay[x][y] = 1;
7349 ChangeEvent[x][y] = trigger_event;
7350 ChangeElement(x, y, p);
7352 #if USE_NEW_DELAYED_ACTION
7353 else if (change->has_action)
7355 ExecuteCustomElementAction(x, y, element, p);
7356 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7359 if (change->has_action)
7361 ExecuteCustomElementAction(x, y, element, p);
7362 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7368 if (change->can_change)
7371 change_done_any = TRUE;
7378 return change_done_any;
7381 static boolean CheckElementChangeExt(int x, int y,
7383 int trigger_element,
7388 boolean change_done = FALSE;
7391 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7392 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7395 if (Feld[x][y] == EL_BLOCKED)
7397 Blocked2Moving(x, y, &x, &y);
7398 element = Feld[x][y];
7401 if (Feld[x][y] != element) /* check if element has already changed */
7404 for (p = 0; p < element_info[element].num_change_pages; p++)
7406 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7408 boolean check_trigger_element =
7409 (trigger_event == CE_TOUCHING_X ||
7410 trigger_event == CE_HITTING_X ||
7411 trigger_event == CE_HIT_BY_X);
7413 if (change->can_change_or_has_action &&
7414 change->has_event[trigger_event] &&
7415 change->trigger_side & trigger_side &&
7416 change->trigger_player & trigger_player &&
7417 (!check_trigger_element ||
7418 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
7420 change->actual_trigger_element = trigger_element;
7421 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7422 change->actual_trigger_side = trigger_side;
7423 change->actual_trigger_ce_value = CustomValue[x][y];
7425 if (change->can_change && !change_done)
7427 ChangeDelay[x][y] = 1;
7428 ChangeEvent[x][y] = trigger_event;
7429 ChangeElement(x, y, p);
7433 #if USE_NEW_DELAYED_ACTION
7434 else if (change->has_action)
7436 ExecuteCustomElementAction(x, y, element, p);
7437 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7440 if (change->has_action)
7442 ExecuteCustomElementAction(x, y, element, p);
7443 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7452 static void PlayPlayerSound(struct PlayerInfo *player)
7454 int jx = player->jx, jy = player->jy;
7455 int element = player->element_nr;
7456 int last_action = player->last_action_waiting;
7457 int action = player->action_waiting;
7459 if (player->is_waiting)
7461 if (action != last_action)
7462 PlayLevelSoundElementAction(jx, jy, element, action);
7464 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7468 if (action != last_action)
7469 StopSound(element_info[element].sound[last_action]);
7471 if (last_action == ACTION_SLEEPING)
7472 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7476 static void PlayAllPlayersSound()
7480 for (i = 0; i < MAX_PLAYERS; i++)
7481 if (stored_player[i].active)
7482 PlayPlayerSound(&stored_player[i]);
7485 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7487 boolean last_waiting = player->is_waiting;
7488 int move_dir = player->MovDir;
7490 player->last_action_waiting = player->action_waiting;
7494 if (!last_waiting) /* not waiting -> waiting */
7496 player->is_waiting = TRUE;
7498 player->frame_counter_bored =
7500 game.player_boring_delay_fixed +
7501 SimpleRND(game.player_boring_delay_random);
7502 player->frame_counter_sleeping =
7504 game.player_sleeping_delay_fixed +
7505 SimpleRND(game.player_sleeping_delay_random);
7507 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7510 if (game.player_sleeping_delay_fixed +
7511 game.player_sleeping_delay_random > 0 &&
7512 player->anim_delay_counter == 0 &&
7513 player->post_delay_counter == 0 &&
7514 FrameCounter >= player->frame_counter_sleeping)
7515 player->is_sleeping = TRUE;
7516 else if (game.player_boring_delay_fixed +
7517 game.player_boring_delay_random > 0 &&
7518 FrameCounter >= player->frame_counter_bored)
7519 player->is_bored = TRUE;
7521 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7522 player->is_bored ? ACTION_BORING :
7525 if (player->is_sleeping)
7527 if (player->num_special_action_sleeping > 0)
7529 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7531 int last_special_action = player->special_action_sleeping;
7532 int num_special_action = player->num_special_action_sleeping;
7533 int special_action =
7534 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7535 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7536 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7537 last_special_action + 1 : ACTION_SLEEPING);
7538 int special_graphic =
7539 el_act_dir2img(player->element_nr, special_action, move_dir);
7541 player->anim_delay_counter =
7542 graphic_info[special_graphic].anim_delay_fixed +
7543 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7544 player->post_delay_counter =
7545 graphic_info[special_graphic].post_delay_fixed +
7546 SimpleRND(graphic_info[special_graphic].post_delay_random);
7548 player->special_action_sleeping = special_action;
7551 if (player->anim_delay_counter > 0)
7553 player->action_waiting = player->special_action_sleeping;
7554 player->anim_delay_counter--;
7556 else if (player->post_delay_counter > 0)
7558 player->post_delay_counter--;
7562 else if (player->is_bored)
7564 if (player->num_special_action_bored > 0)
7566 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7568 int special_action =
7569 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7570 int special_graphic =
7571 el_act_dir2img(player->element_nr, special_action, move_dir);
7573 player->anim_delay_counter =
7574 graphic_info[special_graphic].anim_delay_fixed +
7575 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7576 player->post_delay_counter =
7577 graphic_info[special_graphic].post_delay_fixed +
7578 SimpleRND(graphic_info[special_graphic].post_delay_random);
7580 player->special_action_bored = special_action;
7583 if (player->anim_delay_counter > 0)
7585 player->action_waiting = player->special_action_bored;
7586 player->anim_delay_counter--;
7588 else if (player->post_delay_counter > 0)
7590 player->post_delay_counter--;
7595 else if (last_waiting) /* waiting -> not waiting */
7597 player->is_waiting = FALSE;
7598 player->is_bored = FALSE;
7599 player->is_sleeping = FALSE;
7601 player->frame_counter_bored = -1;
7602 player->frame_counter_sleeping = -1;
7604 player->anim_delay_counter = 0;
7605 player->post_delay_counter = 0;
7607 player->action_waiting = ACTION_DEFAULT;
7609 player->special_action_bored = ACTION_DEFAULT;
7610 player->special_action_sleeping = ACTION_DEFAULT;
7614 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7616 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7617 int left = player_action & JOY_LEFT;
7618 int right = player_action & JOY_RIGHT;
7619 int up = player_action & JOY_UP;
7620 int down = player_action & JOY_DOWN;
7621 int button1 = player_action & JOY_BUTTON_1;
7622 int button2 = player_action & JOY_BUTTON_2;
7623 int dx = (left ? -1 : right ? 1 : 0);
7624 int dy = (up ? -1 : down ? 1 : 0);
7626 if (!player->active || tape.pausing)
7632 snapped = SnapField(player, dx, dy);
7636 dropped = DropElement(player);
7638 moved = MovePlayer(player, dx, dy);
7641 if (tape.single_step && tape.recording && !tape.pausing)
7643 if (button1 || (dropped && !moved))
7645 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7646 SnapField(player, 0, 0); /* stop snapping */
7650 SetPlayerWaiting(player, FALSE);
7652 return player_action;
7656 /* no actions for this player (no input at player's configured device) */
7658 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7659 SnapField(player, 0, 0);
7660 CheckGravityMovementWhenNotMoving(player);
7662 if (player->MovPos == 0)
7663 SetPlayerWaiting(player, TRUE);
7665 if (player->MovPos == 0) /* needed for tape.playing */
7666 player->is_moving = FALSE;
7668 player->is_dropping = FALSE;
7674 void AdvanceFrameAndPlayerCounters(int player_nr)
7678 /* advance frame counters (global frame counter and time frame counter) */
7682 /* advance player counters (counters for move delay, move animation etc.) */
7683 for (i = 0; i < MAX_PLAYERS; i++)
7685 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
7686 int move_delay_value = stored_player[i].move_delay_value;
7687 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
7689 if (!advance_player_counters) /* not all players may be affected */
7692 #if USE_NEW_PLAYER_ANIM
7693 if (move_frames == 0) /* less than one move per game frame */
7695 int stepsize = TILEX / move_delay_value;
7696 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
7697 int count = (stored_player[i].is_moving ?
7698 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
7700 if (count % delay == 0)
7705 stored_player[i].Frame += move_frames;
7707 if (stored_player[i].MovPos != 0)
7708 stored_player[i].StepFrame += move_frames;
7710 if (stored_player[i].move_delay > 0)
7711 stored_player[i].move_delay--;
7713 /* due to bugs in previous versions, counter must count up, not down */
7714 if (stored_player[i].push_delay != -1)
7715 stored_player[i].push_delay++;
7717 if (stored_player[i].drop_delay > 0)
7718 stored_player[i].drop_delay--;
7724 static unsigned long game_frame_delay = 0;
7725 unsigned long game_frame_delay_value;
7726 int magic_wall_x = 0, magic_wall_y = 0;
7727 int i, x, y, element, graphic;
7728 byte *recorded_player_action;
7729 byte summarized_player_action = 0;
7730 byte tape_action[MAX_PLAYERS];
7732 if (game_status != GAME_MODE_PLAYING)
7735 game_frame_delay_value =
7736 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7738 if (tape.playing && tape.warp_forward && !tape.pausing)
7739 game_frame_delay_value = 0;
7741 /* ---------- main game synchronization point ---------- */
7743 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
7745 if (network_playing && !network_player_action_received)
7747 /* try to get network player actions in time */
7749 #if defined(NETWORK_AVALIABLE)
7750 /* last chance to get network player actions without main loop delay */
7754 /* game was quit by network peer */
7755 if (game_status != GAME_MODE_PLAYING)
7758 if (!network_player_action_received)
7759 return; /* failed to get network player actions in time */
7765 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7768 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
7769 if (recorded_player_action == NULL && tape.pausing)
7773 for (i = 0; i < MAX_PLAYERS; i++)
7775 summarized_player_action |= stored_player[i].action;
7777 if (!network_playing)
7778 stored_player[i].effective_action = stored_player[i].action;
7781 #if defined(NETWORK_AVALIABLE)
7782 if (network_playing)
7783 SendToServer_MovePlayer(summarized_player_action);
7786 if (!options.network && !setup.team_mode)
7787 local_player->effective_action = summarized_player_action;
7789 if (recorded_player_action != NULL)
7790 for (i = 0; i < MAX_PLAYERS; i++)
7791 stored_player[i].effective_action = recorded_player_action[i];
7793 for (i = 0; i < MAX_PLAYERS; i++)
7795 tape_action[i] = stored_player[i].effective_action;
7797 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7798 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7801 /* only save actions from input devices, but not programmed actions */
7803 TapeRecordAction(tape_action);
7805 for (i = 0; i < MAX_PLAYERS; i++)
7807 int actual_player_action = stored_player[i].effective_action;
7810 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
7811 - rnd_equinox_tetrachloride 048
7812 - rnd_equinox_tetrachloride_ii 096
7813 - rnd_emanuel_schmieg 002
7814 - doctor_sloan_ww 001, 020
7816 if (stored_player[i].MovPos == 0)
7817 CheckGravityMovement(&stored_player[i]);
7820 /* overwrite programmed action with tape action */
7821 if (stored_player[i].programmed_action)
7822 actual_player_action = stored_player[i].programmed_action;
7825 PlayerActions(&stored_player[i], actual_player_action);
7827 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7829 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7830 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7833 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7836 network_player_action_received = FALSE;
7838 ScrollScreen(NULL, SCROLL_GO_ON);
7840 /* for backwards compatibility, the following code emulates a fixed bug that
7841 occured when pushing elements (causing elements that just made their last
7842 pushing step to already (if possible) make their first falling step in the
7843 same game frame, which is bad); this code is also needed to use the famous
7844 "spring push bug" which is used in older levels and might be wanted to be
7845 used also in newer levels, but in this case the buggy pushing code is only
7846 affecting the "spring" element and no other elements */
7848 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7850 for (i = 0; i < MAX_PLAYERS; i++)
7852 struct PlayerInfo *player = &stored_player[i];
7856 if (player->active && player->is_pushing && player->is_moving &&
7858 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7859 Feld[x][y] == EL_SPRING))
7861 ContinueMoving(x, y);
7863 /* continue moving after pushing (this is actually a bug) */
7864 if (!IS_MOVING(x, y))
7872 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7874 Changed[x][y] = FALSE;
7875 ChangeEvent[x][y] = -1;
7877 /* this must be handled before main playfield loop */
7878 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
7881 if (MovDelay[x][y] <= 0)
7885 #if USE_NEW_SNAP_DELAY
7886 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
7889 if (MovDelay[x][y] <= 0)
7892 DrawLevelField(x, y);
7898 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7900 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7901 printf("GameActions(): This should never happen!\n");
7903 ChangePage[x][y] = -1;
7908 if (WasJustMoving[x][y] > 0)
7909 WasJustMoving[x][y]--;
7910 if (WasJustFalling[x][y] > 0)
7911 WasJustFalling[x][y]--;
7912 if (CheckCollision[x][y] > 0)
7913 CheckCollision[x][y]--;
7917 /* reset finished pushing action (not done in ContinueMoving() to allow
7918 continous pushing animation for elements with zero push delay) */
7919 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7921 ResetGfxAnimation(x, y);
7922 DrawLevelField(x, y);
7926 if (IS_BLOCKED(x, y))
7930 Blocked2Moving(x, y, &oldx, &oldy);
7931 if (!IS_MOVING(oldx, oldy))
7933 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7934 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7935 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7936 printf("GameActions(): This should never happen!\n");
7942 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7944 element = Feld[x][y];
7945 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7947 if (graphic_info[graphic].anim_global_sync)
7948 GfxFrame[x][y] = FrameCounter;
7950 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7951 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7952 ResetRandomAnimationValue(x, y);
7954 SetRandomAnimationValue(x, y);
7956 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7958 if (IS_INACTIVE(element))
7960 if (IS_ANIMATED(graphic))
7961 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7966 /* this may take place after moving, so 'element' may have changed */
7967 if (IS_CHANGING(x, y) &&
7968 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7970 int page = element_info[element].event_page_nr[CE_DELAY];
7972 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
7976 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
7980 ChangeElement(x, y, page);
7982 if (CAN_CHANGE(element))
7983 ChangeElement(x, y, page);
7985 if (HAS_ACTION(element))
7986 ExecuteCustomElementAction(x, y, element, page);
7991 element = Feld[x][y];
7992 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7995 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7999 element = Feld[x][y];
8000 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8002 if (IS_ANIMATED(graphic) &&
8005 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8007 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8008 EdelsteinFunkeln(x, y);
8010 else if ((element == EL_ACID ||
8011 element == EL_EXIT_OPEN ||
8012 element == EL_SP_EXIT_OPEN ||
8013 element == EL_SP_TERMINAL ||
8014 element == EL_SP_TERMINAL_ACTIVE ||
8015 element == EL_EXTRA_TIME ||
8016 element == EL_SHIELD_NORMAL ||
8017 element == EL_SHIELD_DEADLY) &&
8018 IS_ANIMATED(graphic))
8019 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8020 else if (IS_MOVING(x, y))
8021 ContinueMoving(x, y);
8022 else if (IS_ACTIVE_BOMB(element))
8023 CheckDynamite(x, y);
8024 else if (element == EL_AMOEBA_GROWING)
8025 AmoebeWaechst(x, y);
8026 else if (element == EL_AMOEBA_SHRINKING)
8027 AmoebaDisappearing(x, y);
8029 #if !USE_NEW_AMOEBA_CODE
8030 else if (IS_AMOEBALIVE(element))
8031 AmoebeAbleger(x, y);
8034 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8036 else if (element == EL_EXIT_CLOSED)
8038 else if (element == EL_SP_EXIT_CLOSED)
8040 else if (element == EL_EXPANDABLE_WALL_GROWING)
8042 else if (element == EL_EXPANDABLE_WALL ||
8043 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8044 element == EL_EXPANDABLE_WALL_VERTICAL ||
8045 element == EL_EXPANDABLE_WALL_ANY)
8047 else if (element == EL_FLAMES)
8048 CheckForDragon(x, y);
8049 else if (element == EL_EXPLOSION)
8050 ; /* drawing of correct explosion animation is handled separately */
8051 else if (element == EL_ELEMENT_SNAPPING)
8054 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
8056 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8059 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8060 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8062 if (IS_BELT_ACTIVE(element))
8063 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8065 if (game.magic_wall_active)
8067 int jx = local_player->jx, jy = local_player->jy;
8069 /* play the element sound at the position nearest to the player */
8070 if ((element == EL_MAGIC_WALL_FULL ||
8071 element == EL_MAGIC_WALL_ACTIVE ||
8072 element == EL_MAGIC_WALL_EMPTYING ||
8073 element == EL_BD_MAGIC_WALL_FULL ||
8074 element == EL_BD_MAGIC_WALL_ACTIVE ||
8075 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8076 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8084 #if USE_NEW_AMOEBA_CODE
8085 /* new experimental amoeba growth stuff */
8086 if (!(FrameCounter % 8))
8088 static unsigned long random = 1684108901;
8090 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8092 x = RND(lev_fieldx);
8093 y = RND(lev_fieldy);
8094 element = Feld[x][y];
8096 if (!IS_PLAYER(x,y) &&
8097 (element == EL_EMPTY ||
8098 CAN_GROW_INTO(element) ||
8099 element == EL_QUICKSAND_EMPTY ||
8100 element == EL_ACID_SPLASH_LEFT ||
8101 element == EL_ACID_SPLASH_RIGHT))
8103 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8104 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8105 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8106 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8107 Feld[x][y] = EL_AMOEBA_DROP;
8110 random = random * 129 + 1;
8116 if (game.explosions_delayed)
8119 game.explosions_delayed = FALSE;
8121 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8123 element = Feld[x][y];
8125 if (ExplodeField[x][y])
8126 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8127 else if (element == EL_EXPLOSION)
8128 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8130 ExplodeField[x][y] = EX_TYPE_NONE;
8133 game.explosions_delayed = TRUE;
8136 if (game.magic_wall_active)
8138 if (!(game.magic_wall_time_left % 4))
8140 int element = Feld[magic_wall_x][magic_wall_y];
8142 if (element == EL_BD_MAGIC_WALL_FULL ||
8143 element == EL_BD_MAGIC_WALL_ACTIVE ||
8144 element == EL_BD_MAGIC_WALL_EMPTYING)
8145 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8147 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8150 if (game.magic_wall_time_left > 0)
8152 game.magic_wall_time_left--;
8153 if (!game.magic_wall_time_left)
8155 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8157 element = Feld[x][y];
8159 if (element == EL_MAGIC_WALL_ACTIVE ||
8160 element == EL_MAGIC_WALL_FULL)
8162 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8163 DrawLevelField(x, y);
8165 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8166 element == EL_BD_MAGIC_WALL_FULL)
8168 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8169 DrawLevelField(x, y);
8173 game.magic_wall_active = FALSE;
8178 if (game.light_time_left > 0)
8180 game.light_time_left--;
8182 if (game.light_time_left == 0)
8183 RedrawAllLightSwitchesAndInvisibleElements();
8186 if (game.timegate_time_left > 0)
8188 game.timegate_time_left--;
8190 if (game.timegate_time_left == 0)
8191 CloseAllOpenTimegates();
8194 for (i = 0; i < MAX_PLAYERS; i++)
8196 struct PlayerInfo *player = &stored_player[i];
8198 if (SHIELD_ON(player))
8200 if (player->shield_deadly_time_left)
8201 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8202 else if (player->shield_normal_time_left)
8203 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8207 if (TimeFrames >= FRAMES_PER_SECOND)
8212 for (i = 0; i < MAX_PLAYERS; i++)
8214 struct PlayerInfo *player = &stored_player[i];
8216 if (SHIELD_ON(player))
8218 player->shield_normal_time_left--;
8220 if (player->shield_deadly_time_left > 0)
8221 player->shield_deadly_time_left--;
8225 if (!level.use_step_counter)
8233 if (TimeLeft <= 10 && setup.time_limit)
8234 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8236 DrawGameValue_Time(TimeLeft);
8238 if (!TimeLeft && setup.time_limit)
8239 for (i = 0; i < MAX_PLAYERS; i++)
8240 KillPlayer(&stored_player[i]);
8242 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8243 DrawGameValue_Time(TimePlayed);
8246 if (tape.recording || tape.playing)
8247 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8251 PlayAllPlayersSound();
8253 if (options.debug) /* calculate frames per second */
8255 static unsigned long fps_counter = 0;
8256 static int fps_frames = 0;
8257 unsigned long fps_delay_ms = Counter() - fps_counter;
8261 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8263 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8266 fps_counter = Counter();
8269 redraw_mask |= REDRAW_FPS;
8272 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
8274 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8276 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8278 local_player->show_envelope = 0;
8281 /* use random number generator in every frame to make it less predictable */
8282 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8286 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8288 int min_x = x, min_y = y, max_x = x, max_y = y;
8291 for (i = 0; i < MAX_PLAYERS; i++)
8293 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8295 if (!stored_player[i].active || &stored_player[i] == player)
8298 min_x = MIN(min_x, jx);
8299 min_y = MIN(min_y, jy);
8300 max_x = MAX(max_x, jx);
8301 max_y = MAX(max_y, jy);
8304 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8307 static boolean AllPlayersInVisibleScreen()
8311 for (i = 0; i < MAX_PLAYERS; i++)
8313 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8315 if (!stored_player[i].active)
8318 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8325 void ScrollLevel(int dx, int dy)
8327 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8330 BlitBitmap(drawto_field, drawto_field,
8331 FX + TILEX * (dx == -1) - softscroll_offset,
8332 FY + TILEY * (dy == -1) - softscroll_offset,
8333 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8334 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8335 FX + TILEX * (dx == 1) - softscroll_offset,
8336 FY + TILEY * (dy == 1) - softscroll_offset);
8340 x = (dx == 1 ? BX1 : BX2);
8341 for (y = BY1; y <= BY2; y++)
8342 DrawScreenField(x, y);
8347 y = (dy == 1 ? BY1 : BY2);
8348 for (x = BX1; x <= BX2; x++)
8349 DrawScreenField(x, y);
8352 redraw_mask |= REDRAW_FIELD;
8355 static boolean canFallDown(struct PlayerInfo *player)
8357 int jx = player->jx, jy = player->jy;
8359 return (IN_LEV_FIELD(jx, jy + 1) &&
8360 (IS_FREE(jx, jy + 1) ||
8361 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8362 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8363 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8366 static boolean canPassField(int x, int y, int move_dir)
8368 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8369 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8370 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8373 int element = Feld[x][y];
8375 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8376 !CAN_MOVE(element) &&
8377 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8378 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8379 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8382 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8384 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8385 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8386 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8390 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8391 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
8392 (IS_DIGGABLE(Feld[newx][newy]) ||
8393 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8394 canPassField(newx, newy, move_dir)));
8397 static void CheckGravityMovement(struct PlayerInfo *player)
8399 if (game.gravity && !player->programmed_action)
8401 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8402 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8403 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8404 int jx = player->jx, jy = player->jy;
8405 boolean player_is_moving_to_valid_field =
8406 (!player_is_snapping &&
8407 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8408 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8409 boolean player_can_fall_down = canFallDown(player);
8411 if (player_can_fall_down &&
8412 !player_is_moving_to_valid_field)
8413 player->programmed_action = MV_DOWN;
8417 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8419 return CheckGravityMovement(player);
8421 if (game.gravity && !player->programmed_action)
8423 int jx = player->jx, jy = player->jy;
8424 boolean field_under_player_is_free =
8425 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8426 boolean player_is_standing_on_valid_field =
8427 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8428 (IS_WALKABLE(Feld[jx][jy]) &&
8429 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8431 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8432 player->programmed_action = MV_DOWN;
8438 -----------------------------------------------------------------------------
8439 dx, dy: direction (non-diagonal) to try to move the player to
8440 real_dx, real_dy: direction as read from input device (can be diagonal)
8443 boolean MovePlayerOneStep(struct PlayerInfo *player,
8444 int dx, int dy, int real_dx, int real_dy)
8446 int jx = player->jx, jy = player->jy;
8447 int new_jx = jx + dx, new_jy = jy + dy;
8451 if (!player->active || (!dx && !dy))
8452 return MF_NO_ACTION;
8454 player->MovDir = (dx < 0 ? MV_LEFT :
8457 dy > 0 ? MV_DOWN : MV_NONE);
8459 if (!IN_LEV_FIELD(new_jx, new_jy))
8460 return MF_NO_ACTION;
8462 if (player->cannot_move)
8464 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8465 SnapField(player, 0, 0);
8467 return MF_NO_ACTION;
8470 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8471 return MF_NO_ACTION;
8473 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8475 if (DONT_RUN_INTO(element))
8477 if (element == EL_ACID && dx == 0 && dy == 1)
8479 SplashAcid(new_jx, new_jy);
8480 Feld[jx][jy] = EL_PLAYER_1;
8481 InitMovingField(jx, jy, MV_DOWN);
8482 Store[jx][jy] = EL_ACID;
8483 ContinueMoving(jx, jy);
8487 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
8492 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8493 if (can_move != MF_MOVING)
8496 /* check if DigField() has caused relocation of the player */
8497 if (player->jx != jx || player->jy != jy)
8498 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
8500 StorePlayer[jx][jy] = 0;
8501 player->last_jx = jx;
8502 player->last_jy = jy;
8503 player->jx = new_jx;
8504 player->jy = new_jy;
8505 StorePlayer[new_jx][new_jy] = player->element_nr;
8507 if (player->move_delay_value_next != -1)
8509 player->move_delay_value = player->move_delay_value_next;
8510 player->move_delay_value_next = -1;
8514 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8516 player->step_counter++;
8518 PlayerVisit[jx][jy] = FrameCounter;
8520 ScrollPlayer(player, SCROLL_INIT);
8525 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8527 int jx = player->jx, jy = player->jy;
8528 int old_jx = jx, old_jy = jy;
8529 int moved = MF_NO_ACTION;
8531 if (!player->active)
8536 if (player->MovPos == 0)
8538 player->is_moving = FALSE;
8539 player->is_digging = FALSE;
8540 player->is_collecting = FALSE;
8541 player->is_snapping = FALSE;
8542 player->is_pushing = FALSE;
8548 if (player->move_delay > 0)
8551 player->move_delay = -1; /* set to "uninitialized" value */
8553 /* store if player is automatically moved to next field */
8554 player->is_auto_moving = (player->programmed_action != MV_NONE);
8556 /* remove the last programmed player action */
8557 player->programmed_action = 0;
8561 /* should only happen if pre-1.2 tape recordings are played */
8562 /* this is only for backward compatibility */
8564 int original_move_delay_value = player->move_delay_value;
8567 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8571 /* scroll remaining steps with finest movement resolution */
8572 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8574 while (player->MovPos)
8576 ScrollPlayer(player, SCROLL_GO_ON);
8577 ScrollScreen(NULL, SCROLL_GO_ON);
8579 AdvanceFrameAndPlayerCounters(player->index_nr);
8585 player->move_delay_value = original_move_delay_value;
8588 if (player->last_move_dir & MV_HORIZONTAL)
8590 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8591 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8595 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8596 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8602 if (moved & MF_MOVING && !ScreenMovPos &&
8603 (player == local_player || !options.network))
8605 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8606 int offset = (setup.scroll_delay ? 3 : 0);
8608 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8610 /* actual player has left the screen -- scroll in that direction */
8611 if (jx != old_jx) /* player has moved horizontally */
8612 scroll_x += (jx - old_jx);
8613 else /* player has moved vertically */
8614 scroll_y += (jy - old_jy);
8618 if (jx != old_jx) /* player has moved horizontally */
8620 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8621 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8622 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8624 /* don't scroll over playfield boundaries */
8625 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8626 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8628 /* don't scroll more than one field at a time */
8629 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8631 /* don't scroll against the player's moving direction */
8632 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8633 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8634 scroll_x = old_scroll_x;
8636 else /* player has moved vertically */
8638 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8639 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8640 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8642 /* don't scroll over playfield boundaries */
8643 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8644 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8646 /* don't scroll more than one field at a time */
8647 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8649 /* don't scroll against the player's moving direction */
8650 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8651 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8652 scroll_y = old_scroll_y;
8656 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8658 if (!options.network && !AllPlayersInVisibleScreen())
8660 scroll_x = old_scroll_x;
8661 scroll_y = old_scroll_y;
8665 ScrollScreen(player, SCROLL_INIT);
8666 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8671 player->StepFrame = 0;
8673 if (moved & MF_MOVING)
8675 if (old_jx != jx && old_jy == jy)
8676 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8677 else if (old_jx == jx && old_jy != jy)
8678 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8680 DrawLevelField(jx, jy); /* for "crumbled sand" */
8682 player->last_move_dir = player->MovDir;
8683 player->is_moving = TRUE;
8684 player->is_snapping = FALSE;
8685 player->is_switching = FALSE;
8686 player->is_dropping = FALSE;
8690 CheckGravityMovementWhenNotMoving(player);
8692 player->is_moving = FALSE;
8694 /* at this point, the player is allowed to move, but cannot move right now
8695 (e.g. because of something blocking the way) -- ensure that the player
8696 is also allowed to move in the next frame (in old versions before 3.1.1,
8697 the player was forced to wait again for eight frames before next try) */
8699 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8700 player->move_delay = 0; /* allow direct movement in the next frame */
8703 if (player->move_delay == -1) /* not yet initialized by DigField() */
8704 player->move_delay = player->move_delay_value;
8706 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8708 TestIfPlayerTouchesBadThing(jx, jy);
8709 TestIfPlayerTouchesCustomElement(jx, jy);
8712 if (!player->active)
8713 RemovePlayer(player);
8718 void ScrollPlayer(struct PlayerInfo *player, int mode)
8720 int jx = player->jx, jy = player->jy;
8721 int last_jx = player->last_jx, last_jy = player->last_jy;
8722 int move_stepsize = TILEX / player->move_delay_value;
8724 #if USE_NEW_PLAYER_SPEED
8725 if (!player->active)
8728 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
8731 if (!player->active || player->MovPos == 0)
8735 if (mode == SCROLL_INIT)
8737 player->actual_frame_counter = FrameCounter;
8738 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8740 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
8741 Feld[last_jx][last_jy] == EL_EMPTY)
8743 int last_field_block_delay = 0; /* start with no blocking at all */
8744 int block_delay_adjustment = player->block_delay_adjustment;
8746 /* if player blocks last field, add delay for exactly one move */
8747 if (player->block_last_field)
8749 last_field_block_delay += player->move_delay_value;
8751 /* when blocking enabled, prevent moving up despite gravity */
8752 if (game.gravity && player->MovDir == MV_UP)
8753 block_delay_adjustment = -1;
8756 /* add block delay adjustment (also possible when not blocking) */
8757 last_field_block_delay += block_delay_adjustment;
8759 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8760 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
8763 #if USE_NEW_PLAYER_SPEED
8764 if (player->MovPos != 0) /* player has not yet reached destination */
8770 else if (!FrameReached(&player->actual_frame_counter, 1))
8774 printf("::: player->MovPos: %d -> %d\n",
8776 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
8779 #if USE_NEW_PLAYER_SPEED
8780 if (player->MovPos != 0)
8782 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8783 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8785 /* before DrawPlayer() to draw correct player graphic for this case */
8786 if (player->MovPos == 0)
8787 CheckGravityMovement(player);
8790 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8791 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8793 /* before DrawPlayer() to draw correct player graphic for this case */
8794 if (player->MovPos == 0)
8795 CheckGravityMovement(player);
8798 if (player->MovPos == 0) /* player reached destination field */
8801 printf("::: player reached destination field\n");
8804 if (player->move_delay_reset_counter > 0)
8806 player->move_delay_reset_counter--;
8808 if (player->move_delay_reset_counter == 0)
8810 /* continue with normal speed after quickly moving through gate */
8811 HALVE_PLAYER_SPEED(player);
8813 /* be able to make the next move without delay */
8814 player->move_delay = 0;
8818 player->last_jx = jx;
8819 player->last_jy = jy;
8821 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8822 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8823 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8825 DrawPlayer(player); /* needed here only to cleanup last field */
8826 RemovePlayer(player);
8828 if (local_player->friends_still_needed == 0 ||
8829 IS_SP_ELEMENT(Feld[jx][jy]))
8830 player->LevelSolved = player->GameOver = TRUE;
8833 /* this breaks one level: "machine", level 000 */
8835 int move_direction = player->MovDir;
8836 int enter_side = MV_DIR_OPPOSITE(move_direction);
8837 int leave_side = move_direction;
8838 int old_jx = last_jx;
8839 int old_jy = last_jy;
8840 int old_element = Feld[old_jx][old_jy];
8841 int new_element = Feld[jx][jy];
8843 if (IS_CUSTOM_ELEMENT(old_element))
8844 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
8846 player->index_bit, leave_side);
8848 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
8850 player->index_bit, leave_side);
8852 if (IS_CUSTOM_ELEMENT(new_element))
8853 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
8854 player->index_bit, enter_side);
8856 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
8858 player->index_bit, enter_side);
8861 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8863 TestIfPlayerTouchesBadThing(jx, jy);
8864 TestIfPlayerTouchesCustomElement(jx, jy);
8866 /* needed because pushed element has not yet reached its destination,
8867 so it would trigger a change event at its previous field location */
8868 if (!player->is_pushing)
8869 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8871 if (!player->active)
8872 RemovePlayer(player);
8875 if (level.use_step_counter)
8885 if (TimeLeft <= 10 && setup.time_limit)
8886 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8888 DrawGameValue_Time(TimeLeft);
8890 if (!TimeLeft && setup.time_limit)
8891 for (i = 0; i < MAX_PLAYERS; i++)
8892 KillPlayer(&stored_player[i]);
8894 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8895 DrawGameValue_Time(TimePlayed);
8898 if (tape.single_step && tape.recording && !tape.pausing &&
8899 !player->programmed_action)
8900 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8904 void ScrollScreen(struct PlayerInfo *player, int mode)
8906 static unsigned long screen_frame_counter = 0;
8908 if (mode == SCROLL_INIT)
8910 /* set scrolling step size according to actual player's moving speed */
8911 ScrollStepSize = TILEX / player->move_delay_value;
8913 screen_frame_counter = FrameCounter;
8914 ScreenMovDir = player->MovDir;
8915 ScreenMovPos = player->MovPos;
8916 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8919 else if (!FrameReached(&screen_frame_counter, 1))
8924 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8925 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8926 redraw_mask |= REDRAW_FIELD;
8929 ScreenMovDir = MV_NONE;
8932 void TestIfPlayerTouchesCustomElement(int x, int y)
8934 static int xy[4][2] =
8941 static int trigger_sides[4][2] =
8943 /* center side border side */
8944 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8945 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8946 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8947 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8949 static int touch_dir[4] =
8956 int center_element = Feld[x][y]; /* should always be non-moving! */
8959 for (i = 0; i < NUM_DIRECTIONS; i++)
8961 int xx = x + xy[i][0];
8962 int yy = y + xy[i][1];
8963 int center_side = trigger_sides[i][0];
8964 int border_side = trigger_sides[i][1];
8967 if (!IN_LEV_FIELD(xx, yy))
8970 if (IS_PLAYER(x, y))
8972 struct PlayerInfo *player = PLAYERINFO(x, y);
8974 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8975 border_element = Feld[xx][yy]; /* may be moving! */
8976 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8977 border_element = Feld[xx][yy];
8978 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8979 border_element = MovingOrBlocked2Element(xx, yy);
8981 continue; /* center and border element do not touch */
8983 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
8984 player->index_bit, border_side);
8985 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
8986 CE_PLAYER_TOUCHES_X,
8987 player->index_bit, border_side);
8989 else if (IS_PLAYER(xx, yy))
8991 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8993 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8995 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8996 continue; /* center and border element do not touch */
8999 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9000 player->index_bit, center_side);
9001 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9002 CE_PLAYER_TOUCHES_X,
9003 player->index_bit, center_side);
9009 void TestIfElementTouchesCustomElement(int x, int y)
9011 static int xy[4][2] =
9018 static int trigger_sides[4][2] =
9020 /* center side border side */
9021 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9022 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9023 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9024 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9026 static int touch_dir[4] =
9033 boolean change_center_element = FALSE;
9034 int center_element = Feld[x][y]; /* should always be non-moving! */
9037 for (i = 0; i < NUM_DIRECTIONS; i++)
9039 int xx = x + xy[i][0];
9040 int yy = y + xy[i][1];
9041 int center_side = trigger_sides[i][0];
9042 int border_side = trigger_sides[i][1];
9045 if (!IN_LEV_FIELD(xx, yy))
9048 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9049 border_element = Feld[xx][yy]; /* may be moving! */
9050 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9051 border_element = Feld[xx][yy];
9052 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9053 border_element = MovingOrBlocked2Element(xx, yy);
9055 continue; /* center and border element do not touch */
9057 /* check for change of center element (but change it only once) */
9058 if (!change_center_element)
9059 change_center_element =
9060 CheckElementChangeBySide(x, y, center_element, border_element,
9061 CE_TOUCHING_X, border_side);
9063 /* check for change of border element */
9064 CheckElementChangeBySide(xx, yy, border_element, center_element,
9065 CE_TOUCHING_X, center_side);
9069 void TestIfElementHitsCustomElement(int x, int y, int direction)
9071 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9072 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9073 int hitx = x + dx, hity = y + dy;
9074 int hitting_element = Feld[x][y];
9075 int touched_element;
9077 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9080 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9081 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9083 if (IN_LEV_FIELD(hitx, hity))
9085 int opposite_direction = MV_DIR_OPPOSITE(direction);
9086 int hitting_side = direction;
9087 int touched_side = opposite_direction;
9088 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9089 MovDir[hitx][hity] != direction ||
9090 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9096 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9097 CE_HITTING_X, touched_side);
9099 CheckElementChangeBySide(hitx, hity, touched_element,
9100 hitting_element, CE_HIT_BY_X, hitting_side);
9102 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9103 CE_HIT_BY_SOMETHING, opposite_direction);
9107 /* "hitting something" is also true when hitting the playfield border */
9108 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9109 CE_HITTING_SOMETHING, direction);
9113 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9115 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9116 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9117 int hitx = x + dx, hity = y + dy;
9118 int hitting_element = Feld[x][y];
9119 int touched_element;
9121 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9122 !IS_FREE(hitx, hity) &&
9123 (!IS_MOVING(hitx, hity) ||
9124 MovDir[hitx][hity] != direction ||
9125 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9128 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9132 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9136 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9137 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9139 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9140 EP_CAN_SMASH_EVERYTHING, direction);
9142 if (IN_LEV_FIELD(hitx, hity))
9144 int opposite_direction = MV_DIR_OPPOSITE(direction);
9145 int hitting_side = direction;
9146 int touched_side = opposite_direction;
9148 int touched_element = MovingOrBlocked2Element(hitx, hity);
9151 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9152 MovDir[hitx][hity] != direction ||
9153 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9162 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9163 CE_SMASHED_BY_SOMETHING, opposite_direction);
9165 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9166 CE_OTHER_IS_SMASHING, touched_side);
9168 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9169 CE_OTHER_GETS_SMASHED, hitting_side);
9175 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9177 int i, kill_x = -1, kill_y = -1;
9178 int bad_element = -1;
9179 static int test_xy[4][2] =
9186 static int test_dir[4] =
9194 for (i = 0; i < NUM_DIRECTIONS; i++)
9196 int test_x, test_y, test_move_dir, test_element;
9198 test_x = good_x + test_xy[i][0];
9199 test_y = good_y + test_xy[i][1];
9201 if (!IN_LEV_FIELD(test_x, test_y))
9205 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9207 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9209 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9210 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9212 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9213 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9217 bad_element = test_element;
9223 if (kill_x != -1 || kill_y != -1)
9225 if (IS_PLAYER(good_x, good_y))
9227 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9229 if (player->shield_deadly_time_left > 0 &&
9230 !IS_INDESTRUCTIBLE(bad_element))
9231 Bang(kill_x, kill_y);
9232 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9236 Bang(good_x, good_y);
9240 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9242 int i, kill_x = -1, kill_y = -1;
9243 int bad_element = Feld[bad_x][bad_y];
9244 static int test_xy[4][2] =
9251 static int touch_dir[4] =
9258 static int test_dir[4] =
9266 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9269 for (i = 0; i < NUM_DIRECTIONS; i++)
9271 int test_x, test_y, test_move_dir, test_element;
9273 test_x = bad_x + test_xy[i][0];
9274 test_y = bad_y + test_xy[i][1];
9275 if (!IN_LEV_FIELD(test_x, test_y))
9279 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9281 test_element = Feld[test_x][test_y];
9283 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9284 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9286 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9287 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9289 /* good thing is player or penguin that does not move away */
9290 if (IS_PLAYER(test_x, test_y))
9292 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9294 if (bad_element == EL_ROBOT && player->is_moving)
9295 continue; /* robot does not kill player if he is moving */
9297 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9299 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9300 continue; /* center and border element do not touch */
9307 else if (test_element == EL_PENGUIN)
9316 if (kill_x != -1 || kill_y != -1)
9318 if (IS_PLAYER(kill_x, kill_y))
9320 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9322 if (player->shield_deadly_time_left > 0 &&
9323 !IS_INDESTRUCTIBLE(bad_element))
9325 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9329 Bang(kill_x, kill_y);
9333 void TestIfPlayerTouchesBadThing(int x, int y)
9335 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9338 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
9340 TestIfGoodThingHitsBadThing(x, y, move_dir);
9343 void TestIfBadThingTouchesPlayer(int x, int y)
9345 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9348 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
9350 TestIfBadThingHitsGoodThing(x, y, move_dir);
9353 void TestIfFriendTouchesBadThing(int x, int y)
9355 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9358 void TestIfBadThingTouchesFriend(int x, int y)
9360 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9363 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9365 int i, kill_x = bad_x, kill_y = bad_y;
9366 static int xy[4][2] =
9374 for (i = 0; i < NUM_DIRECTIONS; i++)
9378 x = bad_x + xy[i][0];
9379 y = bad_y + xy[i][1];
9380 if (!IN_LEV_FIELD(x, y))
9383 element = Feld[x][y];
9384 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9385 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9393 if (kill_x != bad_x || kill_y != bad_y)
9397 void KillPlayer(struct PlayerInfo *player)
9399 int jx = player->jx, jy = player->jy;
9401 if (!player->active)
9404 /* remove accessible field at the player's position */
9405 Feld[jx][jy] = EL_EMPTY;
9407 /* deactivate shield (else Bang()/Explode() would not work right) */
9408 player->shield_normal_time_left = 0;
9409 player->shield_deadly_time_left = 0;
9415 static void KillPlayerUnlessEnemyProtected(int x, int y)
9417 if (!PLAYER_ENEMY_PROTECTED(x, y))
9418 KillPlayer(PLAYERINFO(x, y));
9421 static void KillPlayerUnlessExplosionProtected(int x, int y)
9423 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9424 KillPlayer(PLAYERINFO(x, y));
9427 void BuryPlayer(struct PlayerInfo *player)
9429 int jx = player->jx, jy = player->jy;
9431 if (!player->active)
9434 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9435 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9437 player->GameOver = TRUE;
9438 RemovePlayer(player);
9441 void RemovePlayer(struct PlayerInfo *player)
9443 int jx = player->jx, jy = player->jy;
9444 int i, found = FALSE;
9446 player->present = FALSE;
9447 player->active = FALSE;
9449 if (!ExplodeField[jx][jy])
9450 StorePlayer[jx][jy] = 0;
9452 if (player->is_moving)
9453 DrawLevelField(player->last_jx, player->last_jy);
9455 for (i = 0; i < MAX_PLAYERS; i++)
9456 if (stored_player[i].active)
9460 AllPlayersGone = TRUE;
9466 #if USE_NEW_SNAP_DELAY
9467 static void setFieldForSnapping(int x, int y, int element, int direction)
9469 struct ElementInfo *ei = &element_info[element];
9470 int direction_bit = MV_DIR_BIT(direction);
9471 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
9472 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
9473 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
9475 Feld[x][y] = EL_ELEMENT_SNAPPING;
9476 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
9478 ResetGfxAnimation(x, y);
9480 GfxElement[x][y] = element;
9481 GfxAction[x][y] = action;
9482 GfxDir[x][y] = direction;
9483 GfxFrame[x][y] = -1;
9488 =============================================================================
9489 checkDiagonalPushing()
9490 -----------------------------------------------------------------------------
9491 check if diagonal input device direction results in pushing of object
9492 (by checking if the alternative direction is walkable, diggable, ...)
9493 =============================================================================
9496 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9497 int x, int y, int real_dx, int real_dy)
9499 int jx, jy, dx, dy, xx, yy;
9501 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9504 /* diagonal direction: check alternative direction */
9509 xx = jx + (dx == 0 ? real_dx : 0);
9510 yy = jy + (dy == 0 ? real_dy : 0);
9512 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9516 =============================================================================
9518 -----------------------------------------------------------------------------
9519 x, y: field next to player (non-diagonal) to try to dig to
9520 real_dx, real_dy: direction as read from input device (can be diagonal)
9521 =============================================================================
9524 int DigField(struct PlayerInfo *player,
9525 int oldx, int oldy, int x, int y,
9526 int real_dx, int real_dy, int mode)
9528 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
9529 boolean player_was_pushing = player->is_pushing;
9530 int jx = oldx, jy = oldy;
9531 int dx = x - jx, dy = y - jy;
9532 int nextx = x + dx, nexty = y + dy;
9533 int move_direction = (dx == -1 ? MV_LEFT :
9534 dx == +1 ? MV_RIGHT :
9536 dy == +1 ? MV_DOWN : MV_NONE);
9537 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9538 int dig_side = MV_DIR_OPPOSITE(move_direction);
9539 int old_element = Feld[jx][jy];
9543 if (is_player) /* function can also be called by EL_PENGUIN */
9545 if (player->MovPos == 0)
9547 player->is_digging = FALSE;
9548 player->is_collecting = FALSE;
9551 if (player->MovPos == 0) /* last pushing move finished */
9552 player->is_pushing = FALSE;
9554 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9556 player->is_switching = FALSE;
9557 player->push_delay = -1;
9559 return MF_NO_ACTION;
9563 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9564 return MF_NO_ACTION;
9566 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9567 old_element = Back[jx][jy];
9569 /* in case of element dropped at player position, check background */
9570 else if (Back[jx][jy] != EL_EMPTY &&
9571 game.engine_version >= VERSION_IDENT(2,2,0,0))
9572 old_element = Back[jx][jy];
9574 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
9575 return MF_NO_ACTION; /* field has no opening in this direction */
9577 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
9578 return MF_NO_ACTION; /* field has no opening in this direction */
9580 element = Feld[x][y];
9581 #if USE_NEW_CUSTOM_VALUE
9584 collect_count = element_info[element].collect_count_initial;
9586 collect_count = CustomValue[x][y];
9590 collect_count = element_info[element].collect_count_initial;
9594 if (element != EL_BLOCKED &&
9595 CustomValue[x][y] != element_info[element].collect_count_initial)
9596 printf("::: %d: %d != %d\n",
9599 element_info[element].collect_count_initial);
9602 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
9603 return MF_NO_ACTION;
9605 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9606 game.engine_version >= VERSION_IDENT(2,2,0,0))
9607 return MF_NO_ACTION;
9609 if (game.gravity && is_player && !player->is_auto_moving &&
9610 canFallDown(player) && move_direction != MV_DOWN &&
9611 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
9612 return MF_NO_ACTION; /* player cannot walk here due to gravity */
9614 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
9616 int sound_element = SND_ELEMENT(element);
9617 int sound_action = ACTION_WALKING;
9619 if (IS_RND_GATE(element))
9621 if (!player->key[RND_GATE_NR(element)])
9622 return MF_NO_ACTION;
9624 else if (IS_RND_GATE_GRAY(element))
9626 if (!player->key[RND_GATE_GRAY_NR(element)])
9627 return MF_NO_ACTION;
9629 else if (element == EL_EXIT_OPEN ||
9630 element == EL_SP_EXIT_OPEN ||
9631 element == EL_SP_EXIT_OPENING)
9633 sound_action = ACTION_PASSING; /* player is passing exit */
9635 else if (element == EL_EMPTY)
9637 sound_action = ACTION_MOVING; /* nothing to walk on */
9640 /* play sound from background or player, whatever is available */
9641 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
9642 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
9644 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9646 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
9648 if (!ACCESS_FROM(element, opposite_direction))
9649 return MF_NO_ACTION; /* field not accessible from this direction */
9651 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9652 return MF_NO_ACTION;
9654 if (IS_EM_GATE(element))
9656 if (!player->key[EM_GATE_NR(element)])
9657 return MF_NO_ACTION;
9659 else if (IS_EM_GATE_GRAY(element))
9661 if (!player->key[EM_GATE_GRAY_NR(element)])
9662 return MF_NO_ACTION;
9664 else if (IS_SP_PORT(element))
9666 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9667 element == EL_SP_GRAVITY_PORT_RIGHT ||
9668 element == EL_SP_GRAVITY_PORT_UP ||
9669 element == EL_SP_GRAVITY_PORT_DOWN)
9670 game.gravity = !game.gravity;
9671 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
9672 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
9673 element == EL_SP_GRAVITY_ON_PORT_UP ||
9674 element == EL_SP_GRAVITY_ON_PORT_DOWN)
9675 game.gravity = TRUE;
9676 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
9677 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
9678 element == EL_SP_GRAVITY_OFF_PORT_UP ||
9679 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
9680 game.gravity = FALSE;
9683 /* automatically move to the next field with double speed */
9684 player->programmed_action = move_direction;
9686 if (player->move_delay_reset_counter == 0)
9688 player->move_delay_reset_counter = 2; /* two double speed steps */
9690 DOUBLE_PLAYER_SPEED(player);
9693 PlayLevelSoundAction(x, y, ACTION_PASSING);
9695 else if (IS_DIGGABLE(element))
9699 if (mode != DF_SNAP)
9701 GfxElement[x][y] = GFX_ELEMENT(element);
9702 player->is_digging = TRUE;
9705 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9707 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
9708 player->index_bit, dig_side);
9710 if (mode == DF_SNAP)
9712 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9714 #if USE_NEW_SNAP_DELAY
9715 if (level.block_snap_field)
9716 setFieldForSnapping(x, y, element, move_direction);
9720 else if (IS_COLLECTIBLE(element))
9724 if (is_player && mode != DF_SNAP)
9726 GfxElement[x][y] = element;
9727 player->is_collecting = TRUE;
9730 if (element == EL_SPEED_PILL)
9732 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9734 else if (element == EL_EXTRA_TIME && level.time > 0)
9736 TimeLeft += level.extra_time;
9737 DrawGameValue_Time(TimeLeft);
9739 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9741 player->shield_normal_time_left += level.shield_normal_time;
9742 if (element == EL_SHIELD_DEADLY)
9743 player->shield_deadly_time_left += level.shield_deadly_time;
9745 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9747 if (player->inventory_size < MAX_INVENTORY_SIZE)
9748 player->inventory_element[player->inventory_size++] = element;
9750 DrawGameValue_Dynamite(local_player->inventory_size);
9752 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9754 player->dynabomb_count++;
9755 player->dynabombs_left++;
9757 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9759 player->dynabomb_size++;
9761 else if (element == EL_DYNABOMB_INCREASE_POWER)
9763 player->dynabomb_xl = TRUE;
9765 else if (IS_KEY(element))
9767 player->key[KEY_NR(element)] = TRUE;
9769 DrawGameValue_Keys(player->key);
9771 redraw_mask |= REDRAW_DOOR_1;
9773 else if (IS_ENVELOPE(element))
9775 player->show_envelope = element;
9777 else if (IS_DROPPABLE(element) ||
9778 IS_THROWABLE(element)) /* can be collected and dropped */
9782 if (collect_count == 0)
9783 player->inventory_infinite_element = element;
9785 for (i = 0; i < collect_count; i++)
9786 if (player->inventory_size < MAX_INVENTORY_SIZE)
9787 player->inventory_element[player->inventory_size++] = element;
9789 DrawGameValue_Dynamite(local_player->inventory_size);
9791 else if (collect_count > 0)
9793 local_player->gems_still_needed -= collect_count;
9794 if (local_player->gems_still_needed < 0)
9795 local_player->gems_still_needed = 0;
9797 DrawGameValue_Emeralds(local_player->gems_still_needed);
9800 RaiseScoreElement(element);
9801 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9804 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
9805 player->index_bit, dig_side);
9807 if (mode == DF_SNAP)
9809 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9811 #if USE_NEW_SNAP_DELAY
9812 if (level.block_snap_field)
9813 setFieldForSnapping(x, y, element, move_direction);
9817 else if (IS_PUSHABLE(element))
9819 if (mode == DF_SNAP && element != EL_BD_ROCK)
9820 return MF_NO_ACTION;
9822 if (CAN_FALL(element) && dy)
9823 return MF_NO_ACTION;
9825 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9826 !(element == EL_SPRING && level.use_spring_bug))
9827 return MF_NO_ACTION;
9829 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9830 ((move_direction & MV_VERTICAL &&
9831 ((element_info[element].move_pattern & MV_LEFT &&
9832 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9833 (element_info[element].move_pattern & MV_RIGHT &&
9834 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9835 (move_direction & MV_HORIZONTAL &&
9836 ((element_info[element].move_pattern & MV_UP &&
9837 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9838 (element_info[element].move_pattern & MV_DOWN &&
9839 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9840 return MF_NO_ACTION;
9842 /* do not push elements already moving away faster than player */
9843 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9844 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9845 return MF_NO_ACTION;
9847 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9849 if (player->push_delay_value == -1 || !player_was_pushing)
9850 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9852 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9854 if (player->push_delay_value == -1)
9855 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9857 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9859 if (!player->is_pushing)
9860 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9863 player->is_pushing = TRUE;
9865 if (!(IN_LEV_FIELD(nextx, nexty) &&
9866 (IS_FREE(nextx, nexty) ||
9867 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9868 IS_SB_ELEMENT(element)))))
9869 return MF_NO_ACTION;
9871 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9872 return MF_NO_ACTION;
9874 if (player->push_delay == -1) /* new pushing; restart delay */
9875 player->push_delay = 0;
9877 if (player->push_delay < player->push_delay_value &&
9878 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9879 element != EL_SPRING && element != EL_BALLOON)
9881 /* make sure that there is no move delay before next try to push */
9882 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9883 player->move_delay = 0;
9885 return MF_NO_ACTION;
9888 if (IS_SB_ELEMENT(element))
9890 if (element == EL_SOKOBAN_FIELD_FULL)
9892 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9893 local_player->sokobanfields_still_needed++;
9896 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9898 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9899 local_player->sokobanfields_still_needed--;
9902 Feld[x][y] = EL_SOKOBAN_OBJECT;
9904 if (Back[x][y] == Back[nextx][nexty])
9905 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9906 else if (Back[x][y] != 0)
9907 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9910 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9913 if (local_player->sokobanfields_still_needed == 0 &&
9914 game.emulation == EMU_SOKOBAN)
9916 player->LevelSolved = player->GameOver = TRUE;
9917 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9921 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9923 InitMovingField(x, y, move_direction);
9924 GfxAction[x][y] = ACTION_PUSHING;
9926 if (mode == DF_SNAP)
9927 ContinueMoving(x, y);
9929 MovPos[x][y] = (dx != 0 ? dx : dy);
9931 Pushed[x][y] = TRUE;
9932 Pushed[nextx][nexty] = TRUE;
9934 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9935 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9937 player->push_delay_value = -1; /* get new value later */
9939 /* check for element change _after_ element has been pushed */
9940 if (game.use_change_when_pushing_bug)
9942 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
9943 player->index_bit, dig_side);
9944 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
9945 player->index_bit, dig_side);
9948 else if (IS_SWITCHABLE(element))
9950 if (PLAYER_SWITCHING(player, x, y))
9952 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
9953 player->index_bit, dig_side);
9958 player->is_switching = TRUE;
9959 player->switch_x = x;
9960 player->switch_y = y;
9962 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9964 if (element == EL_ROBOT_WHEEL)
9966 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9970 DrawLevelField(x, y);
9972 else if (element == EL_SP_TERMINAL)
9976 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9978 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9980 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9981 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9984 else if (IS_BELT_SWITCH(element))
9986 ToggleBeltSwitch(x, y);
9988 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9989 element == EL_SWITCHGATE_SWITCH_DOWN)
9991 ToggleSwitchgateSwitch(x, y);
9993 else if (element == EL_LIGHT_SWITCH ||
9994 element == EL_LIGHT_SWITCH_ACTIVE)
9996 ToggleLightSwitch(x, y);
9998 else if (element == EL_TIMEGATE_SWITCH)
10000 ActivateTimegateSwitch(x, y);
10002 else if (element == EL_BALLOON_SWITCH_LEFT ||
10003 element == EL_BALLOON_SWITCH_RIGHT ||
10004 element == EL_BALLOON_SWITCH_UP ||
10005 element == EL_BALLOON_SWITCH_DOWN ||
10006 element == EL_BALLOON_SWITCH_NONE ||
10007 element == EL_BALLOON_SWITCH_ANY)
10009 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10010 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10011 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10012 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10013 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
10016 else if (element == EL_LAMP)
10018 Feld[x][y] = EL_LAMP_ACTIVE;
10019 local_player->lights_still_needed--;
10021 ResetGfxAnimation(x, y);
10022 DrawLevelField(x, y);
10024 else if (element == EL_TIME_ORB_FULL)
10026 Feld[x][y] = EL_TIME_ORB_EMPTY;
10028 if (level.time > 0 || level.use_time_orb_bug)
10030 TimeLeft += level.time_orb_time;
10031 DrawGameValue_Time(TimeLeft);
10034 ResetGfxAnimation(x, y);
10035 DrawLevelField(x, y);
10038 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10039 player->index_bit, dig_side);
10041 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10042 player->index_bit, dig_side);
10048 if (!PLAYER_SWITCHING(player, x, y))
10050 player->is_switching = TRUE;
10051 player->switch_x = x;
10052 player->switch_y = y;
10054 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
10055 player->index_bit, dig_side);
10056 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10057 player->index_bit, dig_side);
10060 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10061 player->index_bit, dig_side);
10062 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10063 player->index_bit, dig_side);
10065 return MF_NO_ACTION;
10068 player->push_delay = -1;
10070 if (is_player) /* function can also be called by EL_PENGUIN */
10072 if (Feld[x][y] != element) /* really digged/collected something */
10073 player->is_collecting = !player->is_digging;
10079 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10081 int jx = player->jx, jy = player->jy;
10082 int x = jx + dx, y = jy + dy;
10083 int snap_direction = (dx == -1 ? MV_LEFT :
10084 dx == +1 ? MV_RIGHT :
10086 dy == +1 ? MV_DOWN : MV_NONE);
10088 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
10091 if (!player->active || !IN_LEV_FIELD(x, y))
10099 if (player->MovPos == 0)
10100 player->is_pushing = FALSE;
10102 player->is_snapping = FALSE;
10104 if (player->MovPos == 0)
10106 player->is_moving = FALSE;
10107 player->is_digging = FALSE;
10108 player->is_collecting = FALSE;
10114 if (player->is_snapping)
10117 player->MovDir = snap_direction;
10119 if (player->MovPos == 0)
10121 player->is_moving = FALSE;
10122 player->is_digging = FALSE;
10123 player->is_collecting = FALSE;
10126 player->is_dropping = FALSE;
10128 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10131 player->is_snapping = TRUE;
10133 if (player->MovPos == 0)
10135 player->is_moving = FALSE;
10136 player->is_digging = FALSE;
10137 player->is_collecting = FALSE;
10140 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
10141 DrawLevelField(player->last_jx, player->last_jy);
10143 DrawLevelField(x, y);
10148 boolean DropElement(struct PlayerInfo *player)
10150 int old_element, new_element;
10151 int dropx = player->jx, dropy = player->jy;
10152 int drop_direction = player->MovDir;
10153 int drop_side = drop_direction;
10154 int drop_element = (player->inventory_size > 0 ?
10155 player->inventory_element[player->inventory_size - 1] :
10156 player->inventory_infinite_element != EL_UNDEFINED ?
10157 player->inventory_infinite_element :
10158 player->dynabombs_left > 0 ?
10159 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10162 /* do not drop an element on top of another element; when holding drop key
10163 pressed without moving, dropped element must move away before the next
10164 element can be dropped (this is especially important if the next element
10165 is dynamite, which can be placed on background for historical reasons) */
10166 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
10169 if (IS_THROWABLE(drop_element))
10171 dropx += GET_DX_FROM_DIR(drop_direction);
10172 dropy += GET_DY_FROM_DIR(drop_direction);
10174 if (!IN_LEV_FIELD(dropx, dropy))
10178 old_element = Feld[dropx][dropy]; /* old element at dropping position */
10179 new_element = drop_element; /* default: no change when dropping */
10181 /* check if player is active, not moving and ready to drop */
10182 if (!player->active || player->MovPos || player->drop_delay > 0)
10185 /* check if player has anything that can be dropped */
10186 if (new_element == EL_UNDEFINED)
10189 /* check if anything can be dropped at the current position */
10190 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10193 /* collected custom elements can only be dropped on empty fields */
10194 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10197 if (old_element != EL_EMPTY)
10198 Back[dropx][dropy] = old_element; /* store old element on this field */
10200 ResetGfxAnimation(dropx, dropy);
10201 ResetRandomAnimationValue(dropx, dropy);
10203 if (player->inventory_size > 0 ||
10204 player->inventory_infinite_element != EL_UNDEFINED)
10206 if (player->inventory_size > 0)
10208 player->inventory_size--;
10210 DrawGameValue_Dynamite(local_player->inventory_size);
10212 if (new_element == EL_DYNAMITE)
10213 new_element = EL_DYNAMITE_ACTIVE;
10214 else if (new_element == EL_SP_DISK_RED)
10215 new_element = EL_SP_DISK_RED_ACTIVE;
10218 Feld[dropx][dropy] = new_element;
10220 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10221 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10222 el2img(Feld[dropx][dropy]), 0);
10224 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10226 /* needed if previous element just changed to "empty" in the last frame */
10227 Changed[dropx][dropy] = FALSE; /* allow another change */
10229 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
10230 player->index_bit, drop_side);
10231 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
10233 player->index_bit, drop_side);
10235 TestIfElementTouchesCustomElement(dropx, dropy);
10237 else /* player is dropping a dyna bomb */
10239 player->dynabombs_left--;
10241 Feld[dropx][dropy] = new_element;
10243 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10244 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10245 el2img(Feld[dropx][dropy]), 0);
10247 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10250 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
10251 InitField_WithBug1(dropx, dropy, FALSE);
10253 new_element = Feld[dropx][dropy]; /* element might have changed */
10255 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10256 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10258 int move_direction, nextx, nexty;
10260 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10261 MovDir[dropx][dropy] = drop_direction;
10263 move_direction = MovDir[dropx][dropy];
10264 nextx = dropx + GET_DX_FROM_DIR(move_direction);
10265 nexty = dropy + GET_DY_FROM_DIR(move_direction);
10267 Changed[dropx][dropy] = FALSE; /* allow another change */
10268 CheckCollision[dropx][dropy] = 2;
10271 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
10272 player->is_dropping = TRUE;
10274 player->drop_x = dropx;
10275 player->drop_y = dropy;
10280 /* ------------------------------------------------------------------------- */
10281 /* game sound playing functions */
10282 /* ------------------------------------------------------------------------- */
10284 static int *loop_sound_frame = NULL;
10285 static int *loop_sound_volume = NULL;
10287 void InitPlayLevelSound()
10289 int num_sounds = getSoundListSize();
10291 checked_free(loop_sound_frame);
10292 checked_free(loop_sound_volume);
10294 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10295 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10298 static void PlayLevelSound(int x, int y, int nr)
10300 int sx = SCREENX(x), sy = SCREENY(y);
10301 int volume, stereo_position;
10302 int max_distance = 8;
10303 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10305 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10306 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10309 if (!IN_LEV_FIELD(x, y) ||
10310 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10311 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10314 volume = SOUND_MAX_VOLUME;
10316 if (!IN_SCR_FIELD(sx, sy))
10318 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10319 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10321 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10324 stereo_position = (SOUND_MAX_LEFT +
10325 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10326 (SCR_FIELDX + 2 * max_distance));
10328 if (IS_LOOP_SOUND(nr))
10330 /* This assures that quieter loop sounds do not overwrite louder ones,
10331 while restarting sound volume comparison with each new game frame. */
10333 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10336 loop_sound_volume[nr] = volume;
10337 loop_sound_frame[nr] = FrameCounter;
10340 PlaySoundExt(nr, volume, stereo_position, type);
10343 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10345 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10346 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10347 y < LEVELY(BY1) ? LEVELY(BY1) :
10348 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10352 static void PlayLevelSoundAction(int x, int y, int action)
10354 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10357 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10359 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10361 if (sound_effect != SND_UNDEFINED)
10362 PlayLevelSound(x, y, sound_effect);
10365 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10368 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10370 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10371 PlayLevelSound(x, y, sound_effect);
10374 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10376 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10378 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10379 PlayLevelSound(x, y, sound_effect);
10382 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10384 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10386 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10387 StopSound(sound_effect);
10390 static void PlayLevelMusic()
10392 if (levelset.music[level_nr] != MUS_UNDEFINED)
10393 PlayMusic(levelset.music[level_nr]); /* from config file */
10395 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10398 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
10400 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
10405 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
10409 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10413 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10417 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10421 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10425 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10429 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10432 case SAMPLE_android_clone:
10433 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10436 case SAMPLE_android_move:
10437 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10440 case SAMPLE_spring:
10441 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10445 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
10449 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
10452 case SAMPLE_eater_eat:
10453 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10457 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10460 case SAMPLE_collect:
10461 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10464 case SAMPLE_diamond:
10465 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10468 case SAMPLE_squash:
10469 /* !!! CHECK THIS !!! */
10471 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10473 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
10477 case SAMPLE_wonderfall:
10478 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
10482 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10486 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10490 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10494 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
10498 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10502 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
10505 case SAMPLE_wonder:
10506 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10510 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10513 case SAMPLE_exit_open:
10514 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
10517 case SAMPLE_exit_leave:
10518 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10521 case SAMPLE_dynamite:
10522 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10526 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10530 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10534 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10538 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
10542 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
10546 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10550 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
10555 void RaiseScore(int value)
10557 local_player->score += value;
10559 DrawGameValue_Score(local_player->score);
10562 void RaiseScoreElement(int element)
10567 case EL_BD_DIAMOND:
10568 case EL_EMERALD_YELLOW:
10569 case EL_EMERALD_RED:
10570 case EL_EMERALD_PURPLE:
10571 case EL_SP_INFOTRON:
10572 RaiseScore(level.score[SC_EMERALD]);
10575 RaiseScore(level.score[SC_DIAMOND]);
10578 RaiseScore(level.score[SC_CRYSTAL]);
10581 RaiseScore(level.score[SC_PEARL]);
10584 case EL_BD_BUTTERFLY:
10585 case EL_SP_ELECTRON:
10586 RaiseScore(level.score[SC_BUG]);
10589 case EL_BD_FIREFLY:
10590 case EL_SP_SNIKSNAK:
10591 RaiseScore(level.score[SC_SPACESHIP]);
10594 case EL_DARK_YAMYAM:
10595 RaiseScore(level.score[SC_YAMYAM]);
10598 RaiseScore(level.score[SC_ROBOT]);
10601 RaiseScore(level.score[SC_PACMAN]);
10604 RaiseScore(level.score[SC_NUT]);
10607 case EL_SP_DISK_RED:
10608 case EL_DYNABOMB_INCREASE_NUMBER:
10609 case EL_DYNABOMB_INCREASE_SIZE:
10610 case EL_DYNABOMB_INCREASE_POWER:
10611 RaiseScore(level.score[SC_DYNAMITE]);
10613 case EL_SHIELD_NORMAL:
10614 case EL_SHIELD_DEADLY:
10615 RaiseScore(level.score[SC_SHIELD]);
10617 case EL_EXTRA_TIME:
10618 RaiseScore(level.score[SC_TIME_BONUS]);
10632 RaiseScore(level.score[SC_KEY]);
10635 RaiseScore(element_info[element].collect_score);
10640 void RequestQuitGame(boolean ask_if_really_quit)
10642 if (AllPlayersGone ||
10643 !ask_if_really_quit ||
10644 level_editor_test_game ||
10645 Request("Do you really want to quit the game ?",
10646 REQ_ASK | REQ_STAY_CLOSED))
10648 #if defined(NETWORK_AVALIABLE)
10649 if (options.network)
10650 SendToServer_StopPlaying();
10654 game_status = GAME_MODE_MAIN;
10660 if (tape.playing && tape.deactivate_display)
10661 TapeDeactivateDisplayOff(TRUE);
10663 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10665 if (tape.playing && tape.deactivate_display)
10666 TapeDeactivateDisplayOn();
10671 /* ---------- new game button stuff ---------------------------------------- */
10673 /* graphic position values for game buttons */
10674 #define GAME_BUTTON_XSIZE 30
10675 #define GAME_BUTTON_YSIZE 30
10676 #define GAME_BUTTON_XPOS 5
10677 #define GAME_BUTTON_YPOS 215
10678 #define SOUND_BUTTON_XPOS 5
10679 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10681 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10682 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10683 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10684 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10685 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10686 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10693 } gamebutton_info[NUM_GAME_BUTTONS] =
10696 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10701 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10702 GAME_CTRL_ID_PAUSE,
10706 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10711 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10712 SOUND_CTRL_ID_MUSIC,
10713 "background music on/off"
10716 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10717 SOUND_CTRL_ID_LOOPS,
10718 "sound loops on/off"
10721 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10722 SOUND_CTRL_ID_SIMPLE,
10723 "normal sounds on/off"
10727 void CreateGameButtons()
10731 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10733 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10734 struct GadgetInfo *gi;
10737 unsigned long event_mask;
10738 int gd_xoffset, gd_yoffset;
10739 int gd_x1, gd_x2, gd_y1, gd_y2;
10742 gd_xoffset = gamebutton_info[i].x;
10743 gd_yoffset = gamebutton_info[i].y;
10744 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10745 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10747 if (id == GAME_CTRL_ID_STOP ||
10748 id == GAME_CTRL_ID_PAUSE ||
10749 id == GAME_CTRL_ID_PLAY)
10751 button_type = GD_TYPE_NORMAL_BUTTON;
10753 event_mask = GD_EVENT_RELEASED;
10754 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10755 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10759 button_type = GD_TYPE_CHECK_BUTTON;
10761 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10762 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10763 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10764 event_mask = GD_EVENT_PRESSED;
10765 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10766 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10769 gi = CreateGadget(GDI_CUSTOM_ID, id,
10770 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10771 GDI_X, DX + gd_xoffset,
10772 GDI_Y, DY + gd_yoffset,
10773 GDI_WIDTH, GAME_BUTTON_XSIZE,
10774 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10775 GDI_TYPE, button_type,
10776 GDI_STATE, GD_BUTTON_UNPRESSED,
10777 GDI_CHECKED, checked,
10778 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10779 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10780 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10781 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10782 GDI_EVENT_MASK, event_mask,
10783 GDI_CALLBACK_ACTION, HandleGameButtons,
10787 Error(ERR_EXIT, "cannot create gadget");
10789 game_gadget[id] = gi;
10793 void FreeGameButtons()
10797 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10798 FreeGadget(game_gadget[i]);
10801 static void MapGameButtons()
10805 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10806 MapGadget(game_gadget[i]);
10809 void UnmapGameButtons()
10813 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10814 UnmapGadget(game_gadget[i]);
10817 static void HandleGameButtons(struct GadgetInfo *gi)
10819 int id = gi->custom_id;
10821 if (game_status != GAME_MODE_PLAYING)
10826 case GAME_CTRL_ID_STOP:
10827 RequestQuitGame(TRUE);
10830 case GAME_CTRL_ID_PAUSE:
10831 if (options.network)
10833 #if defined(NETWORK_AVALIABLE)
10835 SendToServer_ContinuePlaying();
10837 SendToServer_PausePlaying();
10841 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10844 case GAME_CTRL_ID_PLAY:
10847 #if defined(NETWORK_AVALIABLE)
10848 if (options.network)
10849 SendToServer_ContinuePlaying();
10853 tape.pausing = FALSE;
10854 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10859 case SOUND_CTRL_ID_MUSIC:
10860 if (setup.sound_music)
10862 setup.sound_music = FALSE;
10865 else if (audio.music_available)
10867 setup.sound = setup.sound_music = TRUE;
10869 SetAudioMode(setup.sound);
10875 case SOUND_CTRL_ID_LOOPS:
10876 if (setup.sound_loops)
10877 setup.sound_loops = FALSE;
10878 else if (audio.loops_available)
10880 setup.sound = setup.sound_loops = TRUE;
10881 SetAudioMode(setup.sound);
10885 case SOUND_CTRL_ID_SIMPLE:
10886 if (setup.sound_simple)
10887 setup.sound_simple = FALSE;
10888 else if (audio.sound_available)
10890 setup.sound = setup.sound_simple = TRUE;
10891 SetAudioMode(setup.sound);