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);
6782 case CA_SET_GRAVITY:
6784 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
6785 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
6786 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
6793 game.wind_direction = action_arg_direction;
6800 local_player->gems_still_needed = action_arg_number_new;
6802 DrawGameValue_Emeralds(local_player->gems_still_needed);
6809 if (level.time > 0) /* only modify limited time value */
6811 TimeLeft = action_arg_number_new;
6813 DrawGameValue_Time(TimeLeft);
6815 if (!TimeLeft && setup.time_limit)
6816 for (i = 0; i < MAX_PLAYERS; i++)
6817 KillPlayer(&stored_player[i]);
6825 local_player->score = action_arg_number_new;
6827 DrawGameValue_Score(local_player->score);
6832 case CA_SET_CE_SCORE:
6834 ei->collect_score = action_arg_number_new;
6839 case CA_SET_CE_VALUE:
6841 #if USE_NEW_CUSTOM_VALUE
6842 int last_custom_value = CustomValue[x][y];
6844 CustomValue[x][y] = action_arg_number_new;
6847 printf("::: Count == %d\n", CustomValue[x][y]);
6850 if (CustomValue[x][y] == 0 && last_custom_value > 0)
6853 printf("::: CE_VALUE_GETS_ZERO\n");
6856 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
6857 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
6865 case CA_SET_DYNABOMB_NUMBER:
6867 printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n");
6872 case CA_SET_DYNABOMB_SIZE:
6874 printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n");
6879 case CA_SET_DYNABOMB_POWER:
6881 printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n");
6892 static void ChangeElementNowExt(struct ElementChangeInfo *change,
6893 int x, int y, int target_element)
6895 int previous_move_direction = MovDir[x][y];
6896 #if USE_NEW_CUSTOM_VALUE
6897 int last_ce_value = CustomValue[x][y];
6899 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
6900 IS_WALKABLE(Feld[x][y]));
6902 /* check if element under player changes from accessible to unaccessible
6903 (needed for special case of dropping element which then changes) */
6904 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6905 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6913 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6914 RemoveMovingField(x, y);
6918 Feld[x][y] = target_element;
6920 ResetGfxAnimation(x, y);
6921 ResetRandomAnimationValue(x, y);
6923 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6924 MovDir[x][y] = previous_move_direction;
6926 #if USE_NEW_CUSTOM_VALUE
6927 if (element_info[Feld[x][y]].use_last_ce_value)
6928 CustomValue[x][y] = last_ce_value;
6931 InitField_WithBug1(x, y, FALSE);
6933 DrawLevelField(x, y);
6935 if (GFX_CRUMBLED(Feld[x][y]))
6936 DrawLevelFieldCrumbledSandNeighbours(x, y);
6939 /* "Changed[][]" not set yet to allow "entered by player" change one time */
6940 if (ELEM_IS_PLAYER(target_element))
6941 RelocatePlayer(x, y, target_element);
6944 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
6946 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6949 TestIfBadThingTouchesPlayer(x, y);
6950 TestIfPlayerTouchesCustomElement(x, y);
6951 TestIfElementTouchesCustomElement(x, y);
6954 static boolean ChangeElementNow(int x, int y, int element, int page)
6956 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6958 int old_element = Feld[x][y];
6960 /* always use default change event to prevent running into a loop */
6961 if (ChangeEvent[x][y] == -1)
6962 ChangeEvent[x][y] = CE_DELAY;
6964 if (ChangeEvent[x][y] == CE_DELAY)
6966 /* reset actual trigger element, trigger player and action element */
6967 change->actual_trigger_element = EL_EMPTY;
6968 change->actual_trigger_player = EL_PLAYER_1;
6969 change->actual_trigger_side = CH_SIDE_NONE;
6970 change->actual_trigger_ce_value = 0;
6974 /* do not change any elements that have already changed in this frame */
6978 /* do not change already changed elements with same change event */
6979 if (Changed[x][y] & ChangeEvent[x][y])
6984 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
6986 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6989 if (change->explode)
6996 if (change->use_target_content)
6998 boolean complete_replace = TRUE;
6999 boolean can_replace[3][3];
7002 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7005 boolean is_walkable;
7006 boolean is_diggable;
7007 boolean is_collectible;
7008 boolean is_removable;
7009 boolean is_destructible;
7010 int ex = x + xx - 1;
7011 int ey = y + yy - 1;
7012 int content_element = change->target_content.e[xx][yy];
7015 can_replace[xx][yy] = TRUE;
7017 if (ex == x && ey == y) /* do not check changing element itself */
7020 if (content_element == EL_EMPTY_SPACE)
7022 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7027 if (!IN_LEV_FIELD(ex, ey))
7029 can_replace[xx][yy] = FALSE;
7030 complete_replace = FALSE;
7037 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7038 e = MovingOrBlocked2Element(ex, ey);
7040 is_empty = (IS_FREE(ex, ey) ||
7041 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7043 is_walkable = (is_empty || IS_WALKABLE(e));
7044 is_diggable = (is_empty || IS_DIGGABLE(e));
7045 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7046 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7047 is_removable = (is_diggable || is_collectible);
7049 can_replace[xx][yy] =
7050 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7051 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7052 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7053 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7054 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7055 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7056 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7058 if (!can_replace[xx][yy])
7059 complete_replace = FALSE;
7062 if (!change->only_if_complete || complete_replace)
7064 boolean something_has_changed = FALSE;
7066 if (change->only_if_complete && change->use_random_replace &&
7067 RND(100) < change->random_percentage)
7070 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7072 int ex = x + xx - 1;
7073 int ey = y + yy - 1;
7074 int content_element;
7076 if (can_replace[xx][yy] && (!change->use_random_replace ||
7077 RND(100) < change->random_percentage))
7079 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7080 RemoveMovingField(ex, ey);
7082 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7084 content_element = change->target_content.e[xx][yy];
7085 target_element = GET_TARGET_ELEMENT(content_element, change);
7087 ChangeElementNowExt(change, ex, ey, target_element);
7089 something_has_changed = TRUE;
7091 /* for symmetry reasons, freeze newly created border elements */
7092 if (ex != x || ey != y)
7093 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7097 if (something_has_changed)
7099 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7100 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7106 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7108 ChangeElementNowExt(change, x, y, target_element);
7110 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7111 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7114 /* this uses direct change before indirect change */
7115 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
7120 #if USE_NEW_DELAYED_ACTION
7122 static void ChangeElement(int x, int y, int page)
7124 int element = MovingOrBlocked2Element(x, y);
7125 struct ElementInfo *ei = &element_info[element];
7126 struct ElementChangeInfo *change = &ei->change_page[page];
7129 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
7130 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
7133 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7134 x, y, element, element_info[element].token_name);
7135 printf("ChangeElement(): This should never happen!\n");
7140 /* this can happen with classic bombs on walkable, changing elements */
7141 if (!CAN_CHANGE_OR_HAS_ACTION(element))
7144 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7145 ChangeDelay[x][y] = 0;
7151 if (ChangeDelay[x][y] == 0) /* initialize element change */
7153 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7155 if (change->can_change)
7157 ResetGfxAnimation(x, y);
7158 ResetRandomAnimationValue(x, y);
7160 if (change->pre_change_function)
7161 change->pre_change_function(x, y);
7165 ChangeDelay[x][y]--;
7167 if (ChangeDelay[x][y] != 0) /* continue element change */
7169 if (change->can_change)
7171 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7173 if (IS_ANIMATED(graphic))
7174 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7176 if (change->change_function)
7177 change->change_function(x, y);
7180 else /* finish element change */
7182 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7184 page = ChangePage[x][y];
7185 ChangePage[x][y] = -1;
7187 change = &ei->change_page[page];
7190 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7192 ChangeDelay[x][y] = 1; /* try change after next move step */
7193 ChangePage[x][y] = page; /* remember page to use for change */
7198 if (change->can_change)
7200 if (ChangeElementNow(x, y, element, page))
7202 if (change->post_change_function)
7203 change->post_change_function(x, y);
7207 if (change->has_action)
7208 ExecuteCustomElementAction(x, y, element, page);
7214 static void ChangeElement(int x, int y, int page)
7216 int element = MovingOrBlocked2Element(x, y);
7217 struct ElementInfo *ei = &element_info[element];
7218 struct ElementChangeInfo *change = &ei->change_page[page];
7221 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7224 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7225 x, y, element, element_info[element].token_name);
7226 printf("ChangeElement(): This should never happen!\n");
7231 /* this can happen with classic bombs on walkable, changing elements */
7232 if (!CAN_CHANGE(element))
7235 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7236 ChangeDelay[x][y] = 0;
7242 if (ChangeDelay[x][y] == 0) /* initialize element change */
7244 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7246 ResetGfxAnimation(x, y);
7247 ResetRandomAnimationValue(x, y);
7249 if (change->pre_change_function)
7250 change->pre_change_function(x, y);
7253 ChangeDelay[x][y]--;
7255 if (ChangeDelay[x][y] != 0) /* continue element change */
7257 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7259 if (IS_ANIMATED(graphic))
7260 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7262 if (change->change_function)
7263 change->change_function(x, y);
7265 else /* finish element change */
7267 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7269 page = ChangePage[x][y];
7270 ChangePage[x][y] = -1;
7272 change = &ei->change_page[page];
7275 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7277 ChangeDelay[x][y] = 1; /* try change after next move step */
7278 ChangePage[x][y] = page; /* remember page to use for change */
7283 if (ChangeElementNow(x, y, element, page))
7285 if (change->post_change_function)
7286 change->post_change_function(x, y);
7293 static boolean CheckTriggeredElementChangeExt(int x, int y,
7294 int trigger_element,
7300 boolean change_done_any = FALSE;
7301 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7304 if (!(trigger_events[trigger_element][trigger_event]))
7307 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7309 int element = EL_CUSTOM_START + i;
7310 boolean change_done = FALSE;
7313 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7314 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7317 for (p = 0; p < element_info[element].num_change_pages; p++)
7319 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7321 if (change->can_change_or_has_action &&
7322 change->has_event[trigger_event] &&
7323 change->trigger_side & trigger_side &&
7324 change->trigger_player & trigger_player &&
7325 change->trigger_page & trigger_page_bits &&
7326 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7328 change->actual_trigger_element = trigger_element;
7329 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7330 change->actual_trigger_side = trigger_side;
7331 change->actual_trigger_ce_value = CustomValue[x][y];
7333 if ((change->can_change && !change_done) || change->has_action)
7337 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7339 if (Feld[x][y] == element)
7341 if (change->can_change && !change_done)
7343 ChangeDelay[x][y] = 1;
7344 ChangeEvent[x][y] = trigger_event;
7345 ChangeElement(x, y, p);
7347 #if USE_NEW_DELAYED_ACTION
7348 else if (change->has_action)
7350 ExecuteCustomElementAction(x, y, element, p);
7351 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7354 if (change->has_action)
7356 ExecuteCustomElementAction(x, y, element, p);
7357 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7363 if (change->can_change)
7366 change_done_any = TRUE;
7373 return change_done_any;
7376 static boolean CheckElementChangeExt(int x, int y,
7378 int trigger_element,
7383 boolean change_done = FALSE;
7386 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7387 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7390 if (Feld[x][y] == EL_BLOCKED)
7392 Blocked2Moving(x, y, &x, &y);
7393 element = Feld[x][y];
7396 if (Feld[x][y] != element) /* check if element has already changed */
7399 for (p = 0; p < element_info[element].num_change_pages; p++)
7401 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7403 boolean check_trigger_element =
7404 (trigger_event == CE_TOUCHING_X ||
7405 trigger_event == CE_HITTING_X ||
7406 trigger_event == CE_HIT_BY_X);
7408 if (change->can_change_or_has_action &&
7409 change->has_event[trigger_event] &&
7410 change->trigger_side & trigger_side &&
7411 change->trigger_player & trigger_player &&
7412 (!check_trigger_element ||
7413 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
7415 change->actual_trigger_element = trigger_element;
7416 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7417 change->actual_trigger_side = trigger_side;
7418 change->actual_trigger_ce_value = CustomValue[x][y];
7420 if (change->can_change && !change_done)
7422 ChangeDelay[x][y] = 1;
7423 ChangeEvent[x][y] = trigger_event;
7424 ChangeElement(x, y, p);
7428 #if USE_NEW_DELAYED_ACTION
7429 else if (change->has_action)
7431 ExecuteCustomElementAction(x, y, element, p);
7432 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7435 if (change->has_action)
7437 ExecuteCustomElementAction(x, y, element, p);
7438 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7447 static void PlayPlayerSound(struct PlayerInfo *player)
7449 int jx = player->jx, jy = player->jy;
7450 int element = player->element_nr;
7451 int last_action = player->last_action_waiting;
7452 int action = player->action_waiting;
7454 if (player->is_waiting)
7456 if (action != last_action)
7457 PlayLevelSoundElementAction(jx, jy, element, action);
7459 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7463 if (action != last_action)
7464 StopSound(element_info[element].sound[last_action]);
7466 if (last_action == ACTION_SLEEPING)
7467 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7471 static void PlayAllPlayersSound()
7475 for (i = 0; i < MAX_PLAYERS; i++)
7476 if (stored_player[i].active)
7477 PlayPlayerSound(&stored_player[i]);
7480 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7482 boolean last_waiting = player->is_waiting;
7483 int move_dir = player->MovDir;
7485 player->last_action_waiting = player->action_waiting;
7489 if (!last_waiting) /* not waiting -> waiting */
7491 player->is_waiting = TRUE;
7493 player->frame_counter_bored =
7495 game.player_boring_delay_fixed +
7496 SimpleRND(game.player_boring_delay_random);
7497 player->frame_counter_sleeping =
7499 game.player_sleeping_delay_fixed +
7500 SimpleRND(game.player_sleeping_delay_random);
7502 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7505 if (game.player_sleeping_delay_fixed +
7506 game.player_sleeping_delay_random > 0 &&
7507 player->anim_delay_counter == 0 &&
7508 player->post_delay_counter == 0 &&
7509 FrameCounter >= player->frame_counter_sleeping)
7510 player->is_sleeping = TRUE;
7511 else if (game.player_boring_delay_fixed +
7512 game.player_boring_delay_random > 0 &&
7513 FrameCounter >= player->frame_counter_bored)
7514 player->is_bored = TRUE;
7516 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7517 player->is_bored ? ACTION_BORING :
7520 if (player->is_sleeping)
7522 if (player->num_special_action_sleeping > 0)
7524 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7526 int last_special_action = player->special_action_sleeping;
7527 int num_special_action = player->num_special_action_sleeping;
7528 int special_action =
7529 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7530 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7531 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7532 last_special_action + 1 : ACTION_SLEEPING);
7533 int special_graphic =
7534 el_act_dir2img(player->element_nr, special_action, move_dir);
7536 player->anim_delay_counter =
7537 graphic_info[special_graphic].anim_delay_fixed +
7538 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7539 player->post_delay_counter =
7540 graphic_info[special_graphic].post_delay_fixed +
7541 SimpleRND(graphic_info[special_graphic].post_delay_random);
7543 player->special_action_sleeping = special_action;
7546 if (player->anim_delay_counter > 0)
7548 player->action_waiting = player->special_action_sleeping;
7549 player->anim_delay_counter--;
7551 else if (player->post_delay_counter > 0)
7553 player->post_delay_counter--;
7557 else if (player->is_bored)
7559 if (player->num_special_action_bored > 0)
7561 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7563 int special_action =
7564 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7565 int special_graphic =
7566 el_act_dir2img(player->element_nr, special_action, move_dir);
7568 player->anim_delay_counter =
7569 graphic_info[special_graphic].anim_delay_fixed +
7570 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7571 player->post_delay_counter =
7572 graphic_info[special_graphic].post_delay_fixed +
7573 SimpleRND(graphic_info[special_graphic].post_delay_random);
7575 player->special_action_bored = special_action;
7578 if (player->anim_delay_counter > 0)
7580 player->action_waiting = player->special_action_bored;
7581 player->anim_delay_counter--;
7583 else if (player->post_delay_counter > 0)
7585 player->post_delay_counter--;
7590 else if (last_waiting) /* waiting -> not waiting */
7592 player->is_waiting = FALSE;
7593 player->is_bored = FALSE;
7594 player->is_sleeping = FALSE;
7596 player->frame_counter_bored = -1;
7597 player->frame_counter_sleeping = -1;
7599 player->anim_delay_counter = 0;
7600 player->post_delay_counter = 0;
7602 player->action_waiting = ACTION_DEFAULT;
7604 player->special_action_bored = ACTION_DEFAULT;
7605 player->special_action_sleeping = ACTION_DEFAULT;
7609 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7611 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7612 int left = player_action & JOY_LEFT;
7613 int right = player_action & JOY_RIGHT;
7614 int up = player_action & JOY_UP;
7615 int down = player_action & JOY_DOWN;
7616 int button1 = player_action & JOY_BUTTON_1;
7617 int button2 = player_action & JOY_BUTTON_2;
7618 int dx = (left ? -1 : right ? 1 : 0);
7619 int dy = (up ? -1 : down ? 1 : 0);
7621 if (!player->active || tape.pausing)
7627 snapped = SnapField(player, dx, dy);
7631 dropped = DropElement(player);
7633 moved = MovePlayer(player, dx, dy);
7636 if (tape.single_step && tape.recording && !tape.pausing)
7638 if (button1 || (dropped && !moved))
7640 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7641 SnapField(player, 0, 0); /* stop snapping */
7645 SetPlayerWaiting(player, FALSE);
7647 return player_action;
7651 /* no actions for this player (no input at player's configured device) */
7653 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7654 SnapField(player, 0, 0);
7655 CheckGravityMovementWhenNotMoving(player);
7657 if (player->MovPos == 0)
7658 SetPlayerWaiting(player, TRUE);
7660 if (player->MovPos == 0) /* needed for tape.playing */
7661 player->is_moving = FALSE;
7663 player->is_dropping = FALSE;
7669 void AdvanceFrameAndPlayerCounters(int player_nr)
7673 /* advance frame counters (global frame counter and time frame counter) */
7677 /* advance player counters (counters for move delay, move animation etc.) */
7678 for (i = 0; i < MAX_PLAYERS; i++)
7680 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
7681 int move_delay_value = stored_player[i].move_delay_value;
7682 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
7684 if (!advance_player_counters) /* not all players may be affected */
7687 #if USE_NEW_PLAYER_ANIM
7688 if (move_frames == 0) /* less than one move per game frame */
7690 int stepsize = TILEX / move_delay_value;
7691 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
7692 int count = (stored_player[i].is_moving ?
7693 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
7695 if (count % delay == 0)
7700 stored_player[i].Frame += move_frames;
7702 if (stored_player[i].MovPos != 0)
7703 stored_player[i].StepFrame += move_frames;
7705 if (stored_player[i].move_delay > 0)
7706 stored_player[i].move_delay--;
7708 /* due to bugs in previous versions, counter must count up, not down */
7709 if (stored_player[i].push_delay != -1)
7710 stored_player[i].push_delay++;
7712 if (stored_player[i].drop_delay > 0)
7713 stored_player[i].drop_delay--;
7719 static unsigned long game_frame_delay = 0;
7720 unsigned long game_frame_delay_value;
7721 int magic_wall_x = 0, magic_wall_y = 0;
7722 int i, x, y, element, graphic;
7723 byte *recorded_player_action;
7724 byte summarized_player_action = 0;
7725 byte tape_action[MAX_PLAYERS];
7727 if (game_status != GAME_MODE_PLAYING)
7730 game_frame_delay_value =
7731 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7733 if (tape.playing && tape.warp_forward && !tape.pausing)
7734 game_frame_delay_value = 0;
7736 /* ---------- main game synchronization point ---------- */
7738 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
7740 if (network_playing && !network_player_action_received)
7742 /* try to get network player actions in time */
7744 #if defined(NETWORK_AVALIABLE)
7745 /* last chance to get network player actions without main loop delay */
7749 /* game was quit by network peer */
7750 if (game_status != GAME_MODE_PLAYING)
7753 if (!network_player_action_received)
7754 return; /* failed to get network player actions in time */
7760 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7763 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
7764 if (recorded_player_action == NULL && tape.pausing)
7768 for (i = 0; i < MAX_PLAYERS; i++)
7770 summarized_player_action |= stored_player[i].action;
7772 if (!network_playing)
7773 stored_player[i].effective_action = stored_player[i].action;
7776 #if defined(NETWORK_AVALIABLE)
7777 if (network_playing)
7778 SendToServer_MovePlayer(summarized_player_action);
7781 if (!options.network && !setup.team_mode)
7782 local_player->effective_action = summarized_player_action;
7784 if (recorded_player_action != NULL)
7785 for (i = 0; i < MAX_PLAYERS; i++)
7786 stored_player[i].effective_action = recorded_player_action[i];
7788 for (i = 0; i < MAX_PLAYERS; i++)
7790 tape_action[i] = stored_player[i].effective_action;
7792 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7793 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7796 /* only save actions from input devices, but not programmed actions */
7798 TapeRecordAction(tape_action);
7800 for (i = 0; i < MAX_PLAYERS; i++)
7802 int actual_player_action = stored_player[i].effective_action;
7805 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
7806 - rnd_equinox_tetrachloride 048
7807 - rnd_equinox_tetrachloride_ii 096
7808 - rnd_emanuel_schmieg 002
7809 - doctor_sloan_ww 001, 020
7811 if (stored_player[i].MovPos == 0)
7812 CheckGravityMovement(&stored_player[i]);
7815 /* overwrite programmed action with tape action */
7816 if (stored_player[i].programmed_action)
7817 actual_player_action = stored_player[i].programmed_action;
7820 PlayerActions(&stored_player[i], actual_player_action);
7822 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7824 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7825 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7828 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7831 network_player_action_received = FALSE;
7833 ScrollScreen(NULL, SCROLL_GO_ON);
7835 /* for backwards compatibility, the following code emulates a fixed bug that
7836 occured when pushing elements (causing elements that just made their last
7837 pushing step to already (if possible) make their first falling step in the
7838 same game frame, which is bad); this code is also needed to use the famous
7839 "spring push bug" which is used in older levels and might be wanted to be
7840 used also in newer levels, but in this case the buggy pushing code is only
7841 affecting the "spring" element and no other elements */
7843 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7845 for (i = 0; i < MAX_PLAYERS; i++)
7847 struct PlayerInfo *player = &stored_player[i];
7851 if (player->active && player->is_pushing && player->is_moving &&
7853 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7854 Feld[x][y] == EL_SPRING))
7856 ContinueMoving(x, y);
7858 /* continue moving after pushing (this is actually a bug) */
7859 if (!IS_MOVING(x, y))
7867 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7869 Changed[x][y] = FALSE;
7870 ChangeEvent[x][y] = -1;
7872 /* this must be handled before main playfield loop */
7873 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
7876 if (MovDelay[x][y] <= 0)
7880 #if USE_NEW_SNAP_DELAY
7881 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
7884 if (MovDelay[x][y] <= 0)
7887 DrawLevelField(x, y);
7893 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7895 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7896 printf("GameActions(): This should never happen!\n");
7898 ChangePage[x][y] = -1;
7903 if (WasJustMoving[x][y] > 0)
7904 WasJustMoving[x][y]--;
7905 if (WasJustFalling[x][y] > 0)
7906 WasJustFalling[x][y]--;
7907 if (CheckCollision[x][y] > 0)
7908 CheckCollision[x][y]--;
7912 /* reset finished pushing action (not done in ContinueMoving() to allow
7913 continous pushing animation for elements with zero push delay) */
7914 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7916 ResetGfxAnimation(x, y);
7917 DrawLevelField(x, y);
7921 if (IS_BLOCKED(x, y))
7925 Blocked2Moving(x, y, &oldx, &oldy);
7926 if (!IS_MOVING(oldx, oldy))
7928 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7929 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7930 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7931 printf("GameActions(): This should never happen!\n");
7937 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7939 element = Feld[x][y];
7940 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7942 if (graphic_info[graphic].anim_global_sync)
7943 GfxFrame[x][y] = FrameCounter;
7945 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7946 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7947 ResetRandomAnimationValue(x, y);
7949 SetRandomAnimationValue(x, y);
7951 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7953 if (IS_INACTIVE(element))
7955 if (IS_ANIMATED(graphic))
7956 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7961 /* this may take place after moving, so 'element' may have changed */
7962 if (IS_CHANGING(x, y) &&
7963 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7965 int page = element_info[element].event_page_nr[CE_DELAY];
7967 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
7971 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
7975 ChangeElement(x, y, page);
7977 if (CAN_CHANGE(element))
7978 ChangeElement(x, y, page);
7980 if (HAS_ACTION(element))
7981 ExecuteCustomElementAction(x, y, element, page);
7986 element = Feld[x][y];
7987 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7990 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7994 element = Feld[x][y];
7995 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7997 if (IS_ANIMATED(graphic) &&
8000 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8002 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8003 EdelsteinFunkeln(x, y);
8005 else if ((element == EL_ACID ||
8006 element == EL_EXIT_OPEN ||
8007 element == EL_SP_EXIT_OPEN ||
8008 element == EL_SP_TERMINAL ||
8009 element == EL_SP_TERMINAL_ACTIVE ||
8010 element == EL_EXTRA_TIME ||
8011 element == EL_SHIELD_NORMAL ||
8012 element == EL_SHIELD_DEADLY) &&
8013 IS_ANIMATED(graphic))
8014 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8015 else if (IS_MOVING(x, y))
8016 ContinueMoving(x, y);
8017 else if (IS_ACTIVE_BOMB(element))
8018 CheckDynamite(x, y);
8019 else if (element == EL_AMOEBA_GROWING)
8020 AmoebeWaechst(x, y);
8021 else if (element == EL_AMOEBA_SHRINKING)
8022 AmoebaDisappearing(x, y);
8024 #if !USE_NEW_AMOEBA_CODE
8025 else if (IS_AMOEBALIVE(element))
8026 AmoebeAbleger(x, y);
8029 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8031 else if (element == EL_EXIT_CLOSED)
8033 else if (element == EL_SP_EXIT_CLOSED)
8035 else if (element == EL_EXPANDABLE_WALL_GROWING)
8037 else if (element == EL_EXPANDABLE_WALL ||
8038 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8039 element == EL_EXPANDABLE_WALL_VERTICAL ||
8040 element == EL_EXPANDABLE_WALL_ANY)
8042 else if (element == EL_FLAMES)
8043 CheckForDragon(x, y);
8044 else if (element == EL_EXPLOSION)
8045 ; /* drawing of correct explosion animation is handled separately */
8046 else if (element == EL_ELEMENT_SNAPPING)
8049 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
8051 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8054 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8055 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8057 if (IS_BELT_ACTIVE(element))
8058 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8060 if (game.magic_wall_active)
8062 int jx = local_player->jx, jy = local_player->jy;
8064 /* play the element sound at the position nearest to the player */
8065 if ((element == EL_MAGIC_WALL_FULL ||
8066 element == EL_MAGIC_WALL_ACTIVE ||
8067 element == EL_MAGIC_WALL_EMPTYING ||
8068 element == EL_BD_MAGIC_WALL_FULL ||
8069 element == EL_BD_MAGIC_WALL_ACTIVE ||
8070 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8071 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8079 #if USE_NEW_AMOEBA_CODE
8080 /* new experimental amoeba growth stuff */
8081 if (!(FrameCounter % 8))
8083 static unsigned long random = 1684108901;
8085 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8087 x = RND(lev_fieldx);
8088 y = RND(lev_fieldy);
8089 element = Feld[x][y];
8091 if (!IS_PLAYER(x,y) &&
8092 (element == EL_EMPTY ||
8093 CAN_GROW_INTO(element) ||
8094 element == EL_QUICKSAND_EMPTY ||
8095 element == EL_ACID_SPLASH_LEFT ||
8096 element == EL_ACID_SPLASH_RIGHT))
8098 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8099 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8100 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8101 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8102 Feld[x][y] = EL_AMOEBA_DROP;
8105 random = random * 129 + 1;
8111 if (game.explosions_delayed)
8114 game.explosions_delayed = FALSE;
8116 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8118 element = Feld[x][y];
8120 if (ExplodeField[x][y])
8121 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8122 else if (element == EL_EXPLOSION)
8123 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8125 ExplodeField[x][y] = EX_TYPE_NONE;
8128 game.explosions_delayed = TRUE;
8131 if (game.magic_wall_active)
8133 if (!(game.magic_wall_time_left % 4))
8135 int element = Feld[magic_wall_x][magic_wall_y];
8137 if (element == EL_BD_MAGIC_WALL_FULL ||
8138 element == EL_BD_MAGIC_WALL_ACTIVE ||
8139 element == EL_BD_MAGIC_WALL_EMPTYING)
8140 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8142 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8145 if (game.magic_wall_time_left > 0)
8147 game.magic_wall_time_left--;
8148 if (!game.magic_wall_time_left)
8150 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8152 element = Feld[x][y];
8154 if (element == EL_MAGIC_WALL_ACTIVE ||
8155 element == EL_MAGIC_WALL_FULL)
8157 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8158 DrawLevelField(x, y);
8160 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8161 element == EL_BD_MAGIC_WALL_FULL)
8163 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8164 DrawLevelField(x, y);
8168 game.magic_wall_active = FALSE;
8173 if (game.light_time_left > 0)
8175 game.light_time_left--;
8177 if (game.light_time_left == 0)
8178 RedrawAllLightSwitchesAndInvisibleElements();
8181 if (game.timegate_time_left > 0)
8183 game.timegate_time_left--;
8185 if (game.timegate_time_left == 0)
8186 CloseAllOpenTimegates();
8189 for (i = 0; i < MAX_PLAYERS; i++)
8191 struct PlayerInfo *player = &stored_player[i];
8193 if (SHIELD_ON(player))
8195 if (player->shield_deadly_time_left)
8196 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8197 else if (player->shield_normal_time_left)
8198 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8202 if (TimeFrames >= FRAMES_PER_SECOND)
8207 for (i = 0; i < MAX_PLAYERS; i++)
8209 struct PlayerInfo *player = &stored_player[i];
8211 if (SHIELD_ON(player))
8213 player->shield_normal_time_left--;
8215 if (player->shield_deadly_time_left > 0)
8216 player->shield_deadly_time_left--;
8220 if (!level.use_step_counter)
8228 if (TimeLeft <= 10 && setup.time_limit)
8229 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8231 DrawGameValue_Time(TimeLeft);
8233 if (!TimeLeft && setup.time_limit)
8234 for (i = 0; i < MAX_PLAYERS; i++)
8235 KillPlayer(&stored_player[i]);
8237 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8238 DrawGameValue_Time(TimePlayed);
8241 if (tape.recording || tape.playing)
8242 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8246 PlayAllPlayersSound();
8248 if (options.debug) /* calculate frames per second */
8250 static unsigned long fps_counter = 0;
8251 static int fps_frames = 0;
8252 unsigned long fps_delay_ms = Counter() - fps_counter;
8256 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8258 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8261 fps_counter = Counter();
8264 redraw_mask |= REDRAW_FPS;
8267 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
8269 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8271 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8273 local_player->show_envelope = 0;
8276 /* use random number generator in every frame to make it less predictable */
8277 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8281 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8283 int min_x = x, min_y = y, max_x = x, max_y = y;
8286 for (i = 0; i < MAX_PLAYERS; i++)
8288 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8290 if (!stored_player[i].active || &stored_player[i] == player)
8293 min_x = MIN(min_x, jx);
8294 min_y = MIN(min_y, jy);
8295 max_x = MAX(max_x, jx);
8296 max_y = MAX(max_y, jy);
8299 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8302 static boolean AllPlayersInVisibleScreen()
8306 for (i = 0; i < MAX_PLAYERS; i++)
8308 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8310 if (!stored_player[i].active)
8313 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8320 void ScrollLevel(int dx, int dy)
8322 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8325 BlitBitmap(drawto_field, drawto_field,
8326 FX + TILEX * (dx == -1) - softscroll_offset,
8327 FY + TILEY * (dy == -1) - softscroll_offset,
8328 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8329 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8330 FX + TILEX * (dx == 1) - softscroll_offset,
8331 FY + TILEY * (dy == 1) - softscroll_offset);
8335 x = (dx == 1 ? BX1 : BX2);
8336 for (y = BY1; y <= BY2; y++)
8337 DrawScreenField(x, y);
8342 y = (dy == 1 ? BY1 : BY2);
8343 for (x = BX1; x <= BX2; x++)
8344 DrawScreenField(x, y);
8347 redraw_mask |= REDRAW_FIELD;
8350 static boolean canFallDown(struct PlayerInfo *player)
8352 int jx = player->jx, jy = player->jy;
8354 return (IN_LEV_FIELD(jx, jy + 1) &&
8355 (IS_FREE(jx, jy + 1) ||
8356 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8357 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8358 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8361 static boolean canPassField(int x, int y, int move_dir)
8363 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8364 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8365 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8368 int element = Feld[x][y];
8370 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8371 !CAN_MOVE(element) &&
8372 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8373 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8374 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8377 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8379 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8380 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8381 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8385 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8386 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
8387 (IS_DIGGABLE(Feld[newx][newy]) ||
8388 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8389 canPassField(newx, newy, move_dir)));
8392 static void CheckGravityMovement(struct PlayerInfo *player)
8394 if (game.gravity && !player->programmed_action)
8396 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8397 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8398 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8399 int jx = player->jx, jy = player->jy;
8400 boolean player_is_moving_to_valid_field =
8401 (!player_is_snapping &&
8402 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8403 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8404 boolean player_can_fall_down = canFallDown(player);
8406 if (player_can_fall_down &&
8407 !player_is_moving_to_valid_field)
8408 player->programmed_action = MV_DOWN;
8412 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8414 return CheckGravityMovement(player);
8416 if (game.gravity && !player->programmed_action)
8418 int jx = player->jx, jy = player->jy;
8419 boolean field_under_player_is_free =
8420 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8421 boolean player_is_standing_on_valid_field =
8422 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8423 (IS_WALKABLE(Feld[jx][jy]) &&
8424 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8426 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8427 player->programmed_action = MV_DOWN;
8433 -----------------------------------------------------------------------------
8434 dx, dy: direction (non-diagonal) to try to move the player to
8435 real_dx, real_dy: direction as read from input device (can be diagonal)
8438 boolean MovePlayerOneStep(struct PlayerInfo *player,
8439 int dx, int dy, int real_dx, int real_dy)
8441 int jx = player->jx, jy = player->jy;
8442 int new_jx = jx + dx, new_jy = jy + dy;
8446 if (!player->active || (!dx && !dy))
8447 return MF_NO_ACTION;
8449 player->MovDir = (dx < 0 ? MV_LEFT :
8452 dy > 0 ? MV_DOWN : MV_NONE);
8454 if (!IN_LEV_FIELD(new_jx, new_jy))
8455 return MF_NO_ACTION;
8457 if (player->cannot_move)
8459 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8460 SnapField(player, 0, 0);
8462 return MF_NO_ACTION;
8465 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8466 return MF_NO_ACTION;
8468 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8470 if (DONT_RUN_INTO(element))
8472 if (element == EL_ACID && dx == 0 && dy == 1)
8474 SplashAcid(new_jx, new_jy);
8475 Feld[jx][jy] = EL_PLAYER_1;
8476 InitMovingField(jx, jy, MV_DOWN);
8477 Store[jx][jy] = EL_ACID;
8478 ContinueMoving(jx, jy);
8482 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
8487 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8488 if (can_move != MF_MOVING)
8491 /* check if DigField() has caused relocation of the player */
8492 if (player->jx != jx || player->jy != jy)
8493 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
8495 StorePlayer[jx][jy] = 0;
8496 player->last_jx = jx;
8497 player->last_jy = jy;
8498 player->jx = new_jx;
8499 player->jy = new_jy;
8500 StorePlayer[new_jx][new_jy] = player->element_nr;
8502 if (player->move_delay_value_next != -1)
8504 player->move_delay_value = player->move_delay_value_next;
8505 player->move_delay_value_next = -1;
8509 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8511 player->step_counter++;
8513 PlayerVisit[jx][jy] = FrameCounter;
8515 ScrollPlayer(player, SCROLL_INIT);
8520 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8522 int jx = player->jx, jy = player->jy;
8523 int old_jx = jx, old_jy = jy;
8524 int moved = MF_NO_ACTION;
8526 if (!player->active)
8531 if (player->MovPos == 0)
8533 player->is_moving = FALSE;
8534 player->is_digging = FALSE;
8535 player->is_collecting = FALSE;
8536 player->is_snapping = FALSE;
8537 player->is_pushing = FALSE;
8543 if (player->move_delay > 0)
8546 player->move_delay = -1; /* set to "uninitialized" value */
8548 /* store if player is automatically moved to next field */
8549 player->is_auto_moving = (player->programmed_action != MV_NONE);
8551 /* remove the last programmed player action */
8552 player->programmed_action = 0;
8556 /* should only happen if pre-1.2 tape recordings are played */
8557 /* this is only for backward compatibility */
8559 int original_move_delay_value = player->move_delay_value;
8562 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8566 /* scroll remaining steps with finest movement resolution */
8567 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8569 while (player->MovPos)
8571 ScrollPlayer(player, SCROLL_GO_ON);
8572 ScrollScreen(NULL, SCROLL_GO_ON);
8574 AdvanceFrameAndPlayerCounters(player->index_nr);
8580 player->move_delay_value = original_move_delay_value;
8583 if (player->last_move_dir & MV_HORIZONTAL)
8585 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8586 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8590 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8591 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8597 if (moved & MF_MOVING && !ScreenMovPos &&
8598 (player == local_player || !options.network))
8600 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8601 int offset = (setup.scroll_delay ? 3 : 0);
8603 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8605 /* actual player has left the screen -- scroll in that direction */
8606 if (jx != old_jx) /* player has moved horizontally */
8607 scroll_x += (jx - old_jx);
8608 else /* player has moved vertically */
8609 scroll_y += (jy - old_jy);
8613 if (jx != old_jx) /* player has moved horizontally */
8615 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8616 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8617 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8619 /* don't scroll over playfield boundaries */
8620 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8621 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8623 /* don't scroll more than one field at a time */
8624 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8626 /* don't scroll against the player's moving direction */
8627 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8628 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8629 scroll_x = old_scroll_x;
8631 else /* player has moved vertically */
8633 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8634 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8635 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8637 /* don't scroll over playfield boundaries */
8638 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8639 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8641 /* don't scroll more than one field at a time */
8642 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8644 /* don't scroll against the player's moving direction */
8645 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8646 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8647 scroll_y = old_scroll_y;
8651 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8653 if (!options.network && !AllPlayersInVisibleScreen())
8655 scroll_x = old_scroll_x;
8656 scroll_y = old_scroll_y;
8660 ScrollScreen(player, SCROLL_INIT);
8661 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8666 player->StepFrame = 0;
8668 if (moved & MF_MOVING)
8670 if (old_jx != jx && old_jy == jy)
8671 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8672 else if (old_jx == jx && old_jy != jy)
8673 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8675 DrawLevelField(jx, jy); /* for "crumbled sand" */
8677 player->last_move_dir = player->MovDir;
8678 player->is_moving = TRUE;
8679 player->is_snapping = FALSE;
8680 player->is_switching = FALSE;
8681 player->is_dropping = FALSE;
8685 CheckGravityMovementWhenNotMoving(player);
8687 player->is_moving = FALSE;
8689 /* at this point, the player is allowed to move, but cannot move right now
8690 (e.g. because of something blocking the way) -- ensure that the player
8691 is also allowed to move in the next frame (in old versions before 3.1.1,
8692 the player was forced to wait again for eight frames before next try) */
8694 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8695 player->move_delay = 0; /* allow direct movement in the next frame */
8698 if (player->move_delay == -1) /* not yet initialized by DigField() */
8699 player->move_delay = player->move_delay_value;
8701 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8703 TestIfPlayerTouchesBadThing(jx, jy);
8704 TestIfPlayerTouchesCustomElement(jx, jy);
8707 if (!player->active)
8708 RemovePlayer(player);
8713 void ScrollPlayer(struct PlayerInfo *player, int mode)
8715 int jx = player->jx, jy = player->jy;
8716 int last_jx = player->last_jx, last_jy = player->last_jy;
8717 int move_stepsize = TILEX / player->move_delay_value;
8719 #if USE_NEW_PLAYER_SPEED
8720 if (!player->active)
8723 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
8726 if (!player->active || player->MovPos == 0)
8730 if (mode == SCROLL_INIT)
8732 player->actual_frame_counter = FrameCounter;
8733 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8735 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
8736 Feld[last_jx][last_jy] == EL_EMPTY)
8738 int last_field_block_delay = 0; /* start with no blocking at all */
8739 int block_delay_adjustment = player->block_delay_adjustment;
8741 /* if player blocks last field, add delay for exactly one move */
8742 if (player->block_last_field)
8744 last_field_block_delay += player->move_delay_value;
8746 /* when blocking enabled, prevent moving up despite gravity */
8747 if (game.gravity && player->MovDir == MV_UP)
8748 block_delay_adjustment = -1;
8751 /* add block delay adjustment (also possible when not blocking) */
8752 last_field_block_delay += block_delay_adjustment;
8754 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8755 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
8758 #if USE_NEW_PLAYER_SPEED
8759 if (player->MovPos != 0) /* player has not yet reached destination */
8765 else if (!FrameReached(&player->actual_frame_counter, 1))
8769 printf("::: player->MovPos: %d -> %d\n",
8771 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
8774 #if USE_NEW_PLAYER_SPEED
8775 if (player->MovPos != 0)
8777 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8778 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8780 /* before DrawPlayer() to draw correct player graphic for this case */
8781 if (player->MovPos == 0)
8782 CheckGravityMovement(player);
8785 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8786 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8788 /* before DrawPlayer() to draw correct player graphic for this case */
8789 if (player->MovPos == 0)
8790 CheckGravityMovement(player);
8793 if (player->MovPos == 0) /* player reached destination field */
8796 printf("::: player reached destination field\n");
8799 if (player->move_delay_reset_counter > 0)
8801 player->move_delay_reset_counter--;
8803 if (player->move_delay_reset_counter == 0)
8805 /* continue with normal speed after quickly moving through gate */
8806 HALVE_PLAYER_SPEED(player);
8808 /* be able to make the next move without delay */
8809 player->move_delay = 0;
8813 player->last_jx = jx;
8814 player->last_jy = jy;
8816 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8817 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8818 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8820 DrawPlayer(player); /* needed here only to cleanup last field */
8821 RemovePlayer(player);
8823 if (local_player->friends_still_needed == 0 ||
8824 IS_SP_ELEMENT(Feld[jx][jy]))
8825 player->LevelSolved = player->GameOver = TRUE;
8828 /* this breaks one level: "machine", level 000 */
8830 int move_direction = player->MovDir;
8831 int enter_side = MV_DIR_OPPOSITE(move_direction);
8832 int leave_side = move_direction;
8833 int old_jx = last_jx;
8834 int old_jy = last_jy;
8835 int old_element = Feld[old_jx][old_jy];
8836 int new_element = Feld[jx][jy];
8838 if (IS_CUSTOM_ELEMENT(old_element))
8839 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
8841 player->index_bit, leave_side);
8843 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
8845 player->index_bit, leave_side);
8847 if (IS_CUSTOM_ELEMENT(new_element))
8848 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
8849 player->index_bit, enter_side);
8851 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
8853 player->index_bit, enter_side);
8856 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8858 TestIfPlayerTouchesBadThing(jx, jy);
8859 TestIfPlayerTouchesCustomElement(jx, jy);
8861 /* needed because pushed element has not yet reached its destination,
8862 so it would trigger a change event at its previous field location */
8863 if (!player->is_pushing)
8864 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8866 if (!player->active)
8867 RemovePlayer(player);
8870 if (level.use_step_counter)
8880 if (TimeLeft <= 10 && setup.time_limit)
8881 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8883 DrawGameValue_Time(TimeLeft);
8885 if (!TimeLeft && setup.time_limit)
8886 for (i = 0; i < MAX_PLAYERS; i++)
8887 KillPlayer(&stored_player[i]);
8889 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8890 DrawGameValue_Time(TimePlayed);
8893 if (tape.single_step && tape.recording && !tape.pausing &&
8894 !player->programmed_action)
8895 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8899 void ScrollScreen(struct PlayerInfo *player, int mode)
8901 static unsigned long screen_frame_counter = 0;
8903 if (mode == SCROLL_INIT)
8905 /* set scrolling step size according to actual player's moving speed */
8906 ScrollStepSize = TILEX / player->move_delay_value;
8908 screen_frame_counter = FrameCounter;
8909 ScreenMovDir = player->MovDir;
8910 ScreenMovPos = player->MovPos;
8911 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8914 else if (!FrameReached(&screen_frame_counter, 1))
8919 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8920 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8921 redraw_mask |= REDRAW_FIELD;
8924 ScreenMovDir = MV_NONE;
8927 void TestIfPlayerTouchesCustomElement(int x, int y)
8929 static int xy[4][2] =
8936 static int trigger_sides[4][2] =
8938 /* center side border side */
8939 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8940 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8941 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8942 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8944 static int touch_dir[4] =
8951 int center_element = Feld[x][y]; /* should always be non-moving! */
8954 for (i = 0; i < NUM_DIRECTIONS; i++)
8956 int xx = x + xy[i][0];
8957 int yy = y + xy[i][1];
8958 int center_side = trigger_sides[i][0];
8959 int border_side = trigger_sides[i][1];
8962 if (!IN_LEV_FIELD(xx, yy))
8965 if (IS_PLAYER(x, y))
8967 struct PlayerInfo *player = PLAYERINFO(x, y);
8969 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8970 border_element = Feld[xx][yy]; /* may be moving! */
8971 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8972 border_element = Feld[xx][yy];
8973 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8974 border_element = MovingOrBlocked2Element(xx, yy);
8976 continue; /* center and border element do not touch */
8978 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
8979 player->index_bit, border_side);
8980 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
8981 CE_PLAYER_TOUCHES_X,
8982 player->index_bit, border_side);
8984 else if (IS_PLAYER(xx, yy))
8986 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8988 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8990 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8991 continue; /* center and border element do not touch */
8994 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
8995 player->index_bit, center_side);
8996 CheckTriggeredElementChangeByPlayer(x, y, center_element,
8997 CE_PLAYER_TOUCHES_X,
8998 player->index_bit, center_side);
9004 void TestIfElementTouchesCustomElement(int x, int y)
9006 static int xy[4][2] =
9013 static int trigger_sides[4][2] =
9015 /* center side border side */
9016 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9017 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9018 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9019 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9021 static int touch_dir[4] =
9028 boolean change_center_element = FALSE;
9029 int center_element = Feld[x][y]; /* should always be non-moving! */
9032 for (i = 0; i < NUM_DIRECTIONS; i++)
9034 int xx = x + xy[i][0];
9035 int yy = y + xy[i][1];
9036 int center_side = trigger_sides[i][0];
9037 int border_side = trigger_sides[i][1];
9040 if (!IN_LEV_FIELD(xx, yy))
9043 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9044 border_element = Feld[xx][yy]; /* may be moving! */
9045 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9046 border_element = Feld[xx][yy];
9047 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9048 border_element = MovingOrBlocked2Element(xx, yy);
9050 continue; /* center and border element do not touch */
9052 /* check for change of center element (but change it only once) */
9053 if (!change_center_element)
9054 change_center_element =
9055 CheckElementChangeBySide(x, y, center_element, border_element,
9056 CE_TOUCHING_X, border_side);
9058 /* check for change of border element */
9059 CheckElementChangeBySide(xx, yy, border_element, center_element,
9060 CE_TOUCHING_X, center_side);
9064 void TestIfElementHitsCustomElement(int x, int y, int direction)
9066 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9067 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9068 int hitx = x + dx, hity = y + dy;
9069 int hitting_element = Feld[x][y];
9070 int touched_element;
9072 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9075 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9076 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9078 if (IN_LEV_FIELD(hitx, hity))
9080 int opposite_direction = MV_DIR_OPPOSITE(direction);
9081 int hitting_side = direction;
9082 int touched_side = opposite_direction;
9083 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9084 MovDir[hitx][hity] != direction ||
9085 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9091 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9092 CE_HITTING_X, touched_side);
9094 CheckElementChangeBySide(hitx, hity, touched_element,
9095 hitting_element, CE_HIT_BY_X, hitting_side);
9097 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9098 CE_HIT_BY_SOMETHING, opposite_direction);
9102 /* "hitting something" is also true when hitting the playfield border */
9103 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9104 CE_HITTING_SOMETHING, direction);
9108 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9110 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9111 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9112 int hitx = x + dx, hity = y + dy;
9113 int hitting_element = Feld[x][y];
9114 int touched_element;
9116 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9117 !IS_FREE(hitx, hity) &&
9118 (!IS_MOVING(hitx, hity) ||
9119 MovDir[hitx][hity] != direction ||
9120 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9123 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9127 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9131 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9132 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9134 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9135 EP_CAN_SMASH_EVERYTHING, direction);
9137 if (IN_LEV_FIELD(hitx, hity))
9139 int opposite_direction = MV_DIR_OPPOSITE(direction);
9140 int hitting_side = direction;
9141 int touched_side = opposite_direction;
9143 int touched_element = MovingOrBlocked2Element(hitx, hity);
9146 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9147 MovDir[hitx][hity] != direction ||
9148 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9157 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9158 CE_SMASHED_BY_SOMETHING, opposite_direction);
9160 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9161 CE_OTHER_IS_SMASHING, touched_side);
9163 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9164 CE_OTHER_GETS_SMASHED, hitting_side);
9170 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9172 int i, kill_x = -1, kill_y = -1;
9173 int bad_element = -1;
9174 static int test_xy[4][2] =
9181 static int test_dir[4] =
9189 for (i = 0; i < NUM_DIRECTIONS; i++)
9191 int test_x, test_y, test_move_dir, test_element;
9193 test_x = good_x + test_xy[i][0];
9194 test_y = good_y + test_xy[i][1];
9196 if (!IN_LEV_FIELD(test_x, test_y))
9200 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9202 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9204 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9205 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9207 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9208 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9212 bad_element = test_element;
9218 if (kill_x != -1 || kill_y != -1)
9220 if (IS_PLAYER(good_x, good_y))
9222 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9224 if (player->shield_deadly_time_left > 0 &&
9225 !IS_INDESTRUCTIBLE(bad_element))
9226 Bang(kill_x, kill_y);
9227 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9231 Bang(good_x, good_y);
9235 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9237 int i, kill_x = -1, kill_y = -1;
9238 int bad_element = Feld[bad_x][bad_y];
9239 static int test_xy[4][2] =
9246 static int touch_dir[4] =
9253 static int test_dir[4] =
9261 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9264 for (i = 0; i < NUM_DIRECTIONS; i++)
9266 int test_x, test_y, test_move_dir, test_element;
9268 test_x = bad_x + test_xy[i][0];
9269 test_y = bad_y + test_xy[i][1];
9270 if (!IN_LEV_FIELD(test_x, test_y))
9274 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9276 test_element = Feld[test_x][test_y];
9278 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9279 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9281 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9282 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9284 /* good thing is player or penguin that does not move away */
9285 if (IS_PLAYER(test_x, test_y))
9287 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9289 if (bad_element == EL_ROBOT && player->is_moving)
9290 continue; /* robot does not kill player if he is moving */
9292 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9294 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9295 continue; /* center and border element do not touch */
9302 else if (test_element == EL_PENGUIN)
9311 if (kill_x != -1 || kill_y != -1)
9313 if (IS_PLAYER(kill_x, kill_y))
9315 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9317 if (player->shield_deadly_time_left > 0 &&
9318 !IS_INDESTRUCTIBLE(bad_element))
9320 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9324 Bang(kill_x, kill_y);
9328 void TestIfPlayerTouchesBadThing(int x, int y)
9330 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9333 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
9335 TestIfGoodThingHitsBadThing(x, y, move_dir);
9338 void TestIfBadThingTouchesPlayer(int x, int y)
9340 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9343 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
9345 TestIfBadThingHitsGoodThing(x, y, move_dir);
9348 void TestIfFriendTouchesBadThing(int x, int y)
9350 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9353 void TestIfBadThingTouchesFriend(int x, int y)
9355 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9358 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9360 int i, kill_x = bad_x, kill_y = bad_y;
9361 static int xy[4][2] =
9369 for (i = 0; i < NUM_DIRECTIONS; i++)
9373 x = bad_x + xy[i][0];
9374 y = bad_y + xy[i][1];
9375 if (!IN_LEV_FIELD(x, y))
9378 element = Feld[x][y];
9379 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9380 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9388 if (kill_x != bad_x || kill_y != bad_y)
9392 void KillPlayer(struct PlayerInfo *player)
9394 int jx = player->jx, jy = player->jy;
9396 if (!player->active)
9399 /* remove accessible field at the player's position */
9400 Feld[jx][jy] = EL_EMPTY;
9402 /* deactivate shield (else Bang()/Explode() would not work right) */
9403 player->shield_normal_time_left = 0;
9404 player->shield_deadly_time_left = 0;
9410 static void KillPlayerUnlessEnemyProtected(int x, int y)
9412 if (!PLAYER_ENEMY_PROTECTED(x, y))
9413 KillPlayer(PLAYERINFO(x, y));
9416 static void KillPlayerUnlessExplosionProtected(int x, int y)
9418 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9419 KillPlayer(PLAYERINFO(x, y));
9422 void BuryPlayer(struct PlayerInfo *player)
9424 int jx = player->jx, jy = player->jy;
9426 if (!player->active)
9429 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9430 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9432 player->GameOver = TRUE;
9433 RemovePlayer(player);
9436 void RemovePlayer(struct PlayerInfo *player)
9438 int jx = player->jx, jy = player->jy;
9439 int i, found = FALSE;
9441 player->present = FALSE;
9442 player->active = FALSE;
9444 if (!ExplodeField[jx][jy])
9445 StorePlayer[jx][jy] = 0;
9447 if (player->is_moving)
9448 DrawLevelField(player->last_jx, player->last_jy);
9450 for (i = 0; i < MAX_PLAYERS; i++)
9451 if (stored_player[i].active)
9455 AllPlayersGone = TRUE;
9461 #if USE_NEW_SNAP_DELAY
9462 static void setFieldForSnapping(int x, int y, int element, int direction)
9464 struct ElementInfo *ei = &element_info[element];
9465 int direction_bit = MV_DIR_BIT(direction);
9466 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
9467 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
9468 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
9470 Feld[x][y] = EL_ELEMENT_SNAPPING;
9471 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
9473 ResetGfxAnimation(x, y);
9475 GfxElement[x][y] = element;
9476 GfxAction[x][y] = action;
9477 GfxDir[x][y] = direction;
9478 GfxFrame[x][y] = -1;
9483 =============================================================================
9484 checkDiagonalPushing()
9485 -----------------------------------------------------------------------------
9486 check if diagonal input device direction results in pushing of object
9487 (by checking if the alternative direction is walkable, diggable, ...)
9488 =============================================================================
9491 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9492 int x, int y, int real_dx, int real_dy)
9494 int jx, jy, dx, dy, xx, yy;
9496 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9499 /* diagonal direction: check alternative direction */
9504 xx = jx + (dx == 0 ? real_dx : 0);
9505 yy = jy + (dy == 0 ? real_dy : 0);
9507 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9511 =============================================================================
9513 -----------------------------------------------------------------------------
9514 x, y: field next to player (non-diagonal) to try to dig to
9515 real_dx, real_dy: direction as read from input device (can be diagonal)
9516 =============================================================================
9519 int DigField(struct PlayerInfo *player,
9520 int oldx, int oldy, int x, int y,
9521 int real_dx, int real_dy, int mode)
9523 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
9524 boolean player_was_pushing = player->is_pushing;
9525 int jx = oldx, jy = oldy;
9526 int dx = x - jx, dy = y - jy;
9527 int nextx = x + dx, nexty = y + dy;
9528 int move_direction = (dx == -1 ? MV_LEFT :
9529 dx == +1 ? MV_RIGHT :
9531 dy == +1 ? MV_DOWN : MV_NONE);
9532 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9533 int dig_side = MV_DIR_OPPOSITE(move_direction);
9534 int old_element = Feld[jx][jy];
9538 if (is_player) /* function can also be called by EL_PENGUIN */
9540 if (player->MovPos == 0)
9542 player->is_digging = FALSE;
9543 player->is_collecting = FALSE;
9546 if (player->MovPos == 0) /* last pushing move finished */
9547 player->is_pushing = FALSE;
9549 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9551 player->is_switching = FALSE;
9552 player->push_delay = -1;
9554 return MF_NO_ACTION;
9558 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9559 return MF_NO_ACTION;
9561 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9562 old_element = Back[jx][jy];
9564 /* in case of element dropped at player position, check background */
9565 else if (Back[jx][jy] != EL_EMPTY &&
9566 game.engine_version >= VERSION_IDENT(2,2,0,0))
9567 old_element = Back[jx][jy];
9569 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
9570 return MF_NO_ACTION; /* field has no opening in this direction */
9572 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
9573 return MF_NO_ACTION; /* field has no opening in this direction */
9575 element = Feld[x][y];
9576 #if USE_NEW_CUSTOM_VALUE
9579 collect_count = element_info[element].collect_count_initial;
9581 collect_count = CustomValue[x][y];
9585 collect_count = element_info[element].collect_count_initial;
9589 if (element != EL_BLOCKED &&
9590 CustomValue[x][y] != element_info[element].collect_count_initial)
9591 printf("::: %d: %d != %d\n",
9594 element_info[element].collect_count_initial);
9597 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
9598 return MF_NO_ACTION;
9600 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9601 game.engine_version >= VERSION_IDENT(2,2,0,0))
9602 return MF_NO_ACTION;
9604 if (game.gravity && is_player && !player->is_auto_moving &&
9605 canFallDown(player) && move_direction != MV_DOWN &&
9606 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
9607 return MF_NO_ACTION; /* player cannot walk here due to gravity */
9609 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
9611 int sound_element = SND_ELEMENT(element);
9612 int sound_action = ACTION_WALKING;
9614 if (IS_RND_GATE(element))
9616 if (!player->key[RND_GATE_NR(element)])
9617 return MF_NO_ACTION;
9619 else if (IS_RND_GATE_GRAY(element))
9621 if (!player->key[RND_GATE_GRAY_NR(element)])
9622 return MF_NO_ACTION;
9624 else if (element == EL_EXIT_OPEN ||
9625 element == EL_SP_EXIT_OPEN ||
9626 element == EL_SP_EXIT_OPENING)
9628 sound_action = ACTION_PASSING; /* player is passing exit */
9630 else if (element == EL_EMPTY)
9632 sound_action = ACTION_MOVING; /* nothing to walk on */
9635 /* play sound from background or player, whatever is available */
9636 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
9637 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
9639 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9641 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
9643 if (!ACCESS_FROM(element, opposite_direction))
9644 return MF_NO_ACTION; /* field not accessible from this direction */
9646 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9647 return MF_NO_ACTION;
9649 if (IS_EM_GATE(element))
9651 if (!player->key[EM_GATE_NR(element)])
9652 return MF_NO_ACTION;
9654 else if (IS_EM_GATE_GRAY(element))
9656 if (!player->key[EM_GATE_GRAY_NR(element)])
9657 return MF_NO_ACTION;
9659 else if (IS_SP_PORT(element))
9661 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9662 element == EL_SP_GRAVITY_PORT_RIGHT ||
9663 element == EL_SP_GRAVITY_PORT_UP ||
9664 element == EL_SP_GRAVITY_PORT_DOWN)
9665 game.gravity = !game.gravity;
9666 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
9667 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
9668 element == EL_SP_GRAVITY_ON_PORT_UP ||
9669 element == EL_SP_GRAVITY_ON_PORT_DOWN)
9670 game.gravity = TRUE;
9671 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
9672 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
9673 element == EL_SP_GRAVITY_OFF_PORT_UP ||
9674 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
9675 game.gravity = FALSE;
9678 /* automatically move to the next field with double speed */
9679 player->programmed_action = move_direction;
9681 if (player->move_delay_reset_counter == 0)
9683 player->move_delay_reset_counter = 2; /* two double speed steps */
9685 DOUBLE_PLAYER_SPEED(player);
9688 PlayLevelSoundAction(x, y, ACTION_PASSING);
9690 else if (IS_DIGGABLE(element))
9694 if (mode != DF_SNAP)
9696 GfxElement[x][y] = GFX_ELEMENT(element);
9697 player->is_digging = TRUE;
9700 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9702 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
9703 player->index_bit, dig_side);
9705 if (mode == DF_SNAP)
9707 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9709 #if USE_NEW_SNAP_DELAY
9710 if (level.block_snap_field)
9711 setFieldForSnapping(x, y, element, move_direction);
9715 else if (IS_COLLECTIBLE(element))
9719 if (is_player && mode != DF_SNAP)
9721 GfxElement[x][y] = element;
9722 player->is_collecting = TRUE;
9725 if (element == EL_SPEED_PILL)
9727 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9729 else if (element == EL_EXTRA_TIME && level.time > 0)
9731 TimeLeft += level.extra_time;
9732 DrawGameValue_Time(TimeLeft);
9734 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9736 player->shield_normal_time_left += level.shield_normal_time;
9737 if (element == EL_SHIELD_DEADLY)
9738 player->shield_deadly_time_left += level.shield_deadly_time;
9740 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9742 if (player->inventory_size < MAX_INVENTORY_SIZE)
9743 player->inventory_element[player->inventory_size++] = element;
9745 DrawGameValue_Dynamite(local_player->inventory_size);
9747 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9749 player->dynabomb_count++;
9750 player->dynabombs_left++;
9752 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9754 player->dynabomb_size++;
9756 else if (element == EL_DYNABOMB_INCREASE_POWER)
9758 player->dynabomb_xl = TRUE;
9760 else if (IS_KEY(element))
9762 player->key[KEY_NR(element)] = TRUE;
9764 DrawGameValue_Keys(player->key);
9766 redraw_mask |= REDRAW_DOOR_1;
9768 else if (IS_ENVELOPE(element))
9770 player->show_envelope = element;
9772 else if (IS_DROPPABLE(element) ||
9773 IS_THROWABLE(element)) /* can be collected and dropped */
9777 if (collect_count == 0)
9778 player->inventory_infinite_element = element;
9780 for (i = 0; i < collect_count; i++)
9781 if (player->inventory_size < MAX_INVENTORY_SIZE)
9782 player->inventory_element[player->inventory_size++] = element;
9784 DrawGameValue_Dynamite(local_player->inventory_size);
9786 else if (collect_count > 0)
9788 local_player->gems_still_needed -= collect_count;
9789 if (local_player->gems_still_needed < 0)
9790 local_player->gems_still_needed = 0;
9792 DrawGameValue_Emeralds(local_player->gems_still_needed);
9795 RaiseScoreElement(element);
9796 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9799 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
9800 player->index_bit, dig_side);
9802 if (mode == DF_SNAP)
9804 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9806 #if USE_NEW_SNAP_DELAY
9807 if (level.block_snap_field)
9808 setFieldForSnapping(x, y, element, move_direction);
9812 else if (IS_PUSHABLE(element))
9814 if (mode == DF_SNAP && element != EL_BD_ROCK)
9815 return MF_NO_ACTION;
9817 if (CAN_FALL(element) && dy)
9818 return MF_NO_ACTION;
9820 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9821 !(element == EL_SPRING && level.use_spring_bug))
9822 return MF_NO_ACTION;
9824 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9825 ((move_direction & MV_VERTICAL &&
9826 ((element_info[element].move_pattern & MV_LEFT &&
9827 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9828 (element_info[element].move_pattern & MV_RIGHT &&
9829 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9830 (move_direction & MV_HORIZONTAL &&
9831 ((element_info[element].move_pattern & MV_UP &&
9832 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9833 (element_info[element].move_pattern & MV_DOWN &&
9834 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9835 return MF_NO_ACTION;
9837 /* do not push elements already moving away faster than player */
9838 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9839 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9840 return MF_NO_ACTION;
9842 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9844 if (player->push_delay_value == -1 || !player_was_pushing)
9845 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9847 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9849 if (player->push_delay_value == -1)
9850 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9852 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9854 if (!player->is_pushing)
9855 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9858 player->is_pushing = TRUE;
9860 if (!(IN_LEV_FIELD(nextx, nexty) &&
9861 (IS_FREE(nextx, nexty) ||
9862 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9863 IS_SB_ELEMENT(element)))))
9864 return MF_NO_ACTION;
9866 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9867 return MF_NO_ACTION;
9869 if (player->push_delay == -1) /* new pushing; restart delay */
9870 player->push_delay = 0;
9872 if (player->push_delay < player->push_delay_value &&
9873 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9874 element != EL_SPRING && element != EL_BALLOON)
9876 /* make sure that there is no move delay before next try to push */
9877 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9878 player->move_delay = 0;
9880 return MF_NO_ACTION;
9883 if (IS_SB_ELEMENT(element))
9885 if (element == EL_SOKOBAN_FIELD_FULL)
9887 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9888 local_player->sokobanfields_still_needed++;
9891 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9893 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9894 local_player->sokobanfields_still_needed--;
9897 Feld[x][y] = EL_SOKOBAN_OBJECT;
9899 if (Back[x][y] == Back[nextx][nexty])
9900 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9901 else if (Back[x][y] != 0)
9902 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9905 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9908 if (local_player->sokobanfields_still_needed == 0 &&
9909 game.emulation == EMU_SOKOBAN)
9911 player->LevelSolved = player->GameOver = TRUE;
9912 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9916 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9918 InitMovingField(x, y, move_direction);
9919 GfxAction[x][y] = ACTION_PUSHING;
9921 if (mode == DF_SNAP)
9922 ContinueMoving(x, y);
9924 MovPos[x][y] = (dx != 0 ? dx : dy);
9926 Pushed[x][y] = TRUE;
9927 Pushed[nextx][nexty] = TRUE;
9929 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9930 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9932 player->push_delay_value = -1; /* get new value later */
9934 /* check for element change _after_ element has been pushed */
9935 if (game.use_change_when_pushing_bug)
9937 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
9938 player->index_bit, dig_side);
9939 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
9940 player->index_bit, dig_side);
9943 else if (IS_SWITCHABLE(element))
9945 if (PLAYER_SWITCHING(player, x, y))
9947 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
9948 player->index_bit, dig_side);
9953 player->is_switching = TRUE;
9954 player->switch_x = x;
9955 player->switch_y = y;
9957 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9959 if (element == EL_ROBOT_WHEEL)
9961 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9965 DrawLevelField(x, y);
9967 else if (element == EL_SP_TERMINAL)
9971 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9973 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9975 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9976 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9979 else if (IS_BELT_SWITCH(element))
9981 ToggleBeltSwitch(x, y);
9983 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9984 element == EL_SWITCHGATE_SWITCH_DOWN)
9986 ToggleSwitchgateSwitch(x, y);
9988 else if (element == EL_LIGHT_SWITCH ||
9989 element == EL_LIGHT_SWITCH_ACTIVE)
9991 ToggleLightSwitch(x, y);
9993 else if (element == EL_TIMEGATE_SWITCH)
9995 ActivateTimegateSwitch(x, y);
9997 else if (element == EL_BALLOON_SWITCH_LEFT ||
9998 element == EL_BALLOON_SWITCH_RIGHT ||
9999 element == EL_BALLOON_SWITCH_UP ||
10000 element == EL_BALLOON_SWITCH_DOWN ||
10001 element == EL_BALLOON_SWITCH_NONE ||
10002 element == EL_BALLOON_SWITCH_ANY)
10004 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10005 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10006 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10007 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10008 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
10011 else if (element == EL_LAMP)
10013 Feld[x][y] = EL_LAMP_ACTIVE;
10014 local_player->lights_still_needed--;
10016 ResetGfxAnimation(x, y);
10017 DrawLevelField(x, y);
10019 else if (element == EL_TIME_ORB_FULL)
10021 Feld[x][y] = EL_TIME_ORB_EMPTY;
10023 if (level.time > 0 || level.use_time_orb_bug)
10025 TimeLeft += level.time_orb_time;
10026 DrawGameValue_Time(TimeLeft);
10029 ResetGfxAnimation(x, y);
10030 DrawLevelField(x, y);
10033 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10034 player->index_bit, dig_side);
10036 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10037 player->index_bit, dig_side);
10043 if (!PLAYER_SWITCHING(player, x, y))
10045 player->is_switching = TRUE;
10046 player->switch_x = x;
10047 player->switch_y = y;
10049 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
10050 player->index_bit, dig_side);
10051 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10052 player->index_bit, dig_side);
10055 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10056 player->index_bit, dig_side);
10057 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10058 player->index_bit, dig_side);
10060 return MF_NO_ACTION;
10063 player->push_delay = -1;
10065 if (is_player) /* function can also be called by EL_PENGUIN */
10067 if (Feld[x][y] != element) /* really digged/collected something */
10068 player->is_collecting = !player->is_digging;
10074 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10076 int jx = player->jx, jy = player->jy;
10077 int x = jx + dx, y = jy + dy;
10078 int snap_direction = (dx == -1 ? MV_LEFT :
10079 dx == +1 ? MV_RIGHT :
10081 dy == +1 ? MV_DOWN : MV_NONE);
10083 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
10086 if (!player->active || !IN_LEV_FIELD(x, y))
10094 if (player->MovPos == 0)
10095 player->is_pushing = FALSE;
10097 player->is_snapping = FALSE;
10099 if (player->MovPos == 0)
10101 player->is_moving = FALSE;
10102 player->is_digging = FALSE;
10103 player->is_collecting = FALSE;
10109 if (player->is_snapping)
10112 player->MovDir = snap_direction;
10114 if (player->MovPos == 0)
10116 player->is_moving = FALSE;
10117 player->is_digging = FALSE;
10118 player->is_collecting = FALSE;
10121 player->is_dropping = FALSE;
10123 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10126 player->is_snapping = TRUE;
10128 if (player->MovPos == 0)
10130 player->is_moving = FALSE;
10131 player->is_digging = FALSE;
10132 player->is_collecting = FALSE;
10135 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
10136 DrawLevelField(player->last_jx, player->last_jy);
10138 DrawLevelField(x, y);
10143 boolean DropElement(struct PlayerInfo *player)
10145 int old_element, new_element;
10146 int dropx = player->jx, dropy = player->jy;
10147 int drop_direction = player->MovDir;
10148 int drop_side = drop_direction;
10149 int drop_element = (player->inventory_size > 0 ?
10150 player->inventory_element[player->inventory_size - 1] :
10151 player->inventory_infinite_element != EL_UNDEFINED ?
10152 player->inventory_infinite_element :
10153 player->dynabombs_left > 0 ?
10154 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10157 /* do not drop an element on top of another element; when holding drop key
10158 pressed without moving, dropped element must move away before the next
10159 element can be dropped (this is especially important if the next element
10160 is dynamite, which can be placed on background for historical reasons) */
10161 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
10164 if (IS_THROWABLE(drop_element))
10166 dropx += GET_DX_FROM_DIR(drop_direction);
10167 dropy += GET_DY_FROM_DIR(drop_direction);
10169 if (!IN_LEV_FIELD(dropx, dropy))
10173 old_element = Feld[dropx][dropy]; /* old element at dropping position */
10174 new_element = drop_element; /* default: no change when dropping */
10176 /* check if player is active, not moving and ready to drop */
10177 if (!player->active || player->MovPos || player->drop_delay > 0)
10180 /* check if player has anything that can be dropped */
10181 if (new_element == EL_UNDEFINED)
10184 /* check if anything can be dropped at the current position */
10185 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10188 /* collected custom elements can only be dropped on empty fields */
10189 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10192 if (old_element != EL_EMPTY)
10193 Back[dropx][dropy] = old_element; /* store old element on this field */
10195 ResetGfxAnimation(dropx, dropy);
10196 ResetRandomAnimationValue(dropx, dropy);
10198 if (player->inventory_size > 0 ||
10199 player->inventory_infinite_element != EL_UNDEFINED)
10201 if (player->inventory_size > 0)
10203 player->inventory_size--;
10205 DrawGameValue_Dynamite(local_player->inventory_size);
10207 if (new_element == EL_DYNAMITE)
10208 new_element = EL_DYNAMITE_ACTIVE;
10209 else if (new_element == EL_SP_DISK_RED)
10210 new_element = EL_SP_DISK_RED_ACTIVE;
10213 Feld[dropx][dropy] = new_element;
10215 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10216 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10217 el2img(Feld[dropx][dropy]), 0);
10219 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10221 /* needed if previous element just changed to "empty" in the last frame */
10222 Changed[dropx][dropy] = FALSE; /* allow another change */
10224 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
10225 player->index_bit, drop_side);
10226 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
10228 player->index_bit, drop_side);
10230 TestIfElementTouchesCustomElement(dropx, dropy);
10232 else /* player is dropping a dyna bomb */
10234 player->dynabombs_left--;
10236 Feld[dropx][dropy] = new_element;
10238 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10239 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10240 el2img(Feld[dropx][dropy]), 0);
10242 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10245 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
10246 InitField_WithBug1(dropx, dropy, FALSE);
10248 new_element = Feld[dropx][dropy]; /* element might have changed */
10250 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10251 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10253 int move_direction, nextx, nexty;
10255 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10256 MovDir[dropx][dropy] = drop_direction;
10258 move_direction = MovDir[dropx][dropy];
10259 nextx = dropx + GET_DX_FROM_DIR(move_direction);
10260 nexty = dropy + GET_DY_FROM_DIR(move_direction);
10262 Changed[dropx][dropy] = FALSE; /* allow another change */
10263 CheckCollision[dropx][dropy] = 2;
10266 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
10267 player->is_dropping = TRUE;
10269 player->drop_x = dropx;
10270 player->drop_y = dropy;
10275 /* ------------------------------------------------------------------------- */
10276 /* game sound playing functions */
10277 /* ------------------------------------------------------------------------- */
10279 static int *loop_sound_frame = NULL;
10280 static int *loop_sound_volume = NULL;
10282 void InitPlayLevelSound()
10284 int num_sounds = getSoundListSize();
10286 checked_free(loop_sound_frame);
10287 checked_free(loop_sound_volume);
10289 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10290 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10293 static void PlayLevelSound(int x, int y, int nr)
10295 int sx = SCREENX(x), sy = SCREENY(y);
10296 int volume, stereo_position;
10297 int max_distance = 8;
10298 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10300 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10301 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10304 if (!IN_LEV_FIELD(x, y) ||
10305 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10306 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10309 volume = SOUND_MAX_VOLUME;
10311 if (!IN_SCR_FIELD(sx, sy))
10313 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10314 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10316 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10319 stereo_position = (SOUND_MAX_LEFT +
10320 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10321 (SCR_FIELDX + 2 * max_distance));
10323 if (IS_LOOP_SOUND(nr))
10325 /* This assures that quieter loop sounds do not overwrite louder ones,
10326 while restarting sound volume comparison with each new game frame. */
10328 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10331 loop_sound_volume[nr] = volume;
10332 loop_sound_frame[nr] = FrameCounter;
10335 PlaySoundExt(nr, volume, stereo_position, type);
10338 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10340 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10341 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10342 y < LEVELY(BY1) ? LEVELY(BY1) :
10343 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10347 static void PlayLevelSoundAction(int x, int y, int action)
10349 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10352 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10354 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10356 if (sound_effect != SND_UNDEFINED)
10357 PlayLevelSound(x, y, sound_effect);
10360 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10363 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10365 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10366 PlayLevelSound(x, y, sound_effect);
10369 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10371 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10373 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10374 PlayLevelSound(x, y, sound_effect);
10377 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10379 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10381 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10382 StopSound(sound_effect);
10385 static void PlayLevelMusic()
10387 if (levelset.music[level_nr] != MUS_UNDEFINED)
10388 PlayMusic(levelset.music[level_nr]); /* from config file */
10390 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10393 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
10395 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
10400 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
10404 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10408 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10412 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10416 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10420 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10424 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10427 case SAMPLE_android_clone:
10428 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10431 case SAMPLE_android_move:
10432 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10435 case SAMPLE_spring:
10436 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10440 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
10444 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
10447 case SAMPLE_eater_eat:
10448 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10452 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10455 case SAMPLE_collect:
10456 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10459 case SAMPLE_diamond:
10460 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10463 case SAMPLE_squash:
10464 /* !!! CHECK THIS !!! */
10466 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10468 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
10472 case SAMPLE_wonderfall:
10473 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
10477 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10481 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10485 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10489 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
10493 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10497 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
10500 case SAMPLE_wonder:
10501 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10505 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10508 case SAMPLE_exit_open:
10509 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
10512 case SAMPLE_exit_leave:
10513 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10516 case SAMPLE_dynamite:
10517 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10521 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10525 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10529 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10533 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
10537 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
10541 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10545 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
10550 void RaiseScore(int value)
10552 local_player->score += value;
10554 DrawGameValue_Score(local_player->score);
10557 void RaiseScoreElement(int element)
10562 case EL_BD_DIAMOND:
10563 case EL_EMERALD_YELLOW:
10564 case EL_EMERALD_RED:
10565 case EL_EMERALD_PURPLE:
10566 case EL_SP_INFOTRON:
10567 RaiseScore(level.score[SC_EMERALD]);
10570 RaiseScore(level.score[SC_DIAMOND]);
10573 RaiseScore(level.score[SC_CRYSTAL]);
10576 RaiseScore(level.score[SC_PEARL]);
10579 case EL_BD_BUTTERFLY:
10580 case EL_SP_ELECTRON:
10581 RaiseScore(level.score[SC_BUG]);
10584 case EL_BD_FIREFLY:
10585 case EL_SP_SNIKSNAK:
10586 RaiseScore(level.score[SC_SPACESHIP]);
10589 case EL_DARK_YAMYAM:
10590 RaiseScore(level.score[SC_YAMYAM]);
10593 RaiseScore(level.score[SC_ROBOT]);
10596 RaiseScore(level.score[SC_PACMAN]);
10599 RaiseScore(level.score[SC_NUT]);
10602 case EL_SP_DISK_RED:
10603 case EL_DYNABOMB_INCREASE_NUMBER:
10604 case EL_DYNABOMB_INCREASE_SIZE:
10605 case EL_DYNABOMB_INCREASE_POWER:
10606 RaiseScore(level.score[SC_DYNAMITE]);
10608 case EL_SHIELD_NORMAL:
10609 case EL_SHIELD_DEADLY:
10610 RaiseScore(level.score[SC_SHIELD]);
10612 case EL_EXTRA_TIME:
10613 RaiseScore(level.score[SC_TIME_BONUS]);
10627 RaiseScore(level.score[SC_KEY]);
10630 RaiseScore(element_info[element].collect_score);
10635 void RequestQuitGame(boolean ask_if_really_quit)
10637 if (AllPlayersGone ||
10638 !ask_if_really_quit ||
10639 level_editor_test_game ||
10640 Request("Do you really want to quit the game ?",
10641 REQ_ASK | REQ_STAY_CLOSED))
10643 #if defined(NETWORK_AVALIABLE)
10644 if (options.network)
10645 SendToServer_StopPlaying();
10649 game_status = GAME_MODE_MAIN;
10655 if (tape.playing && tape.deactivate_display)
10656 TapeDeactivateDisplayOff(TRUE);
10658 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10660 if (tape.playing && tape.deactivate_display)
10661 TapeDeactivateDisplayOn();
10666 /* ---------- new game button stuff ---------------------------------------- */
10668 /* graphic position values for game buttons */
10669 #define GAME_BUTTON_XSIZE 30
10670 #define GAME_BUTTON_YSIZE 30
10671 #define GAME_BUTTON_XPOS 5
10672 #define GAME_BUTTON_YPOS 215
10673 #define SOUND_BUTTON_XPOS 5
10674 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10676 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10677 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10678 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10679 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10680 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10681 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10688 } gamebutton_info[NUM_GAME_BUTTONS] =
10691 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10696 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10697 GAME_CTRL_ID_PAUSE,
10701 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10706 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10707 SOUND_CTRL_ID_MUSIC,
10708 "background music on/off"
10711 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10712 SOUND_CTRL_ID_LOOPS,
10713 "sound loops on/off"
10716 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10717 SOUND_CTRL_ID_SIMPLE,
10718 "normal sounds on/off"
10722 void CreateGameButtons()
10726 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10728 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10729 struct GadgetInfo *gi;
10732 unsigned long event_mask;
10733 int gd_xoffset, gd_yoffset;
10734 int gd_x1, gd_x2, gd_y1, gd_y2;
10737 gd_xoffset = gamebutton_info[i].x;
10738 gd_yoffset = gamebutton_info[i].y;
10739 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10740 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10742 if (id == GAME_CTRL_ID_STOP ||
10743 id == GAME_CTRL_ID_PAUSE ||
10744 id == GAME_CTRL_ID_PLAY)
10746 button_type = GD_TYPE_NORMAL_BUTTON;
10748 event_mask = GD_EVENT_RELEASED;
10749 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10750 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10754 button_type = GD_TYPE_CHECK_BUTTON;
10756 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10757 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10758 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10759 event_mask = GD_EVENT_PRESSED;
10760 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10761 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10764 gi = CreateGadget(GDI_CUSTOM_ID, id,
10765 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10766 GDI_X, DX + gd_xoffset,
10767 GDI_Y, DY + gd_yoffset,
10768 GDI_WIDTH, GAME_BUTTON_XSIZE,
10769 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10770 GDI_TYPE, button_type,
10771 GDI_STATE, GD_BUTTON_UNPRESSED,
10772 GDI_CHECKED, checked,
10773 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10774 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10775 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10776 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10777 GDI_EVENT_MASK, event_mask,
10778 GDI_CALLBACK_ACTION, HandleGameButtons,
10782 Error(ERR_EXIT, "cannot create gadget");
10784 game_gadget[id] = gi;
10788 void FreeGameButtons()
10792 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10793 FreeGadget(game_gadget[i]);
10796 static void MapGameButtons()
10800 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10801 MapGadget(game_gadget[i]);
10804 void UnmapGameButtons()
10808 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10809 UnmapGadget(game_gadget[i]);
10812 static void HandleGameButtons(struct GadgetInfo *gi)
10814 int id = gi->custom_id;
10816 if (game_status != GAME_MODE_PLAYING)
10821 case GAME_CTRL_ID_STOP:
10822 RequestQuitGame(TRUE);
10825 case GAME_CTRL_ID_PAUSE:
10826 if (options.network)
10828 #if defined(NETWORK_AVALIABLE)
10830 SendToServer_ContinuePlaying();
10832 SendToServer_PausePlaying();
10836 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10839 case GAME_CTRL_ID_PLAY:
10842 #if defined(NETWORK_AVALIABLE)
10843 if (options.network)
10844 SendToServer_ContinuePlaying();
10848 tape.pausing = FALSE;
10849 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10854 case SOUND_CTRL_ID_MUSIC:
10855 if (setup.sound_music)
10857 setup.sound_music = FALSE;
10860 else if (audio.music_available)
10862 setup.sound = setup.sound_music = TRUE;
10864 SetAudioMode(setup.sound);
10870 case SOUND_CTRL_ID_LOOPS:
10871 if (setup.sound_loops)
10872 setup.sound_loops = FALSE;
10873 else if (audio.loops_available)
10875 setup.sound = setup.sound_loops = TRUE;
10876 SetAudioMode(setup.sound);
10880 case SOUND_CTRL_ID_SIMPLE:
10881 if (setup.sound_simple)
10882 setup.sound_simple = FALSE;
10883 else if (audio.sound_available)
10885 setup.sound = setup.sound_simple = TRUE;
10886 SetAudioMode(setup.sound);