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->frame_counter_bored = -1;
1564 player->frame_counter_sleeping = -1;
1566 player->anim_delay_counter = 0;
1567 player->post_delay_counter = 0;
1569 player->action_waiting = ACTION_DEFAULT;
1570 player->last_action_waiting = ACTION_DEFAULT;
1571 player->special_action_bored = ACTION_DEFAULT;
1572 player->special_action_sleeping = ACTION_DEFAULT;
1574 player->num_special_action_bored = 0;
1575 player->num_special_action_sleeping = 0;
1577 /* determine number of special actions for bored and sleeping animation */
1578 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1580 boolean found = FALSE;
1582 for (k = 0; k < NUM_DIRECTIONS; k++)
1583 if (el_act_dir2img(player->element_nr, j, k) !=
1584 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1588 player->num_special_action_bored++;
1592 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1594 boolean found = FALSE;
1596 for (k = 0; k < NUM_DIRECTIONS; k++)
1597 if (el_act_dir2img(player->element_nr, j, k) !=
1598 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1602 player->num_special_action_sleeping++;
1607 player->switch_x = -1;
1608 player->switch_y = -1;
1610 player->drop_x = -1;
1611 player->drop_y = -1;
1613 player->show_envelope = 0;
1615 player->move_delay = game.initial_move_delay;
1616 player->move_delay_value = game.initial_move_delay_value;
1618 player->move_delay_value_next = -1;
1620 player->move_delay_reset_counter = 0;
1622 player->push_delay = -1; /* initialized when pushing starts */
1623 player->push_delay_value = game.initial_push_delay_value;
1625 player->drop_delay = 0;
1627 player->last_jx = player->last_jy = 0;
1628 player->jx = player->jy = 0;
1630 player->shield_normal_time_left = 0;
1631 player->shield_deadly_time_left = 0;
1633 player->inventory_infinite_element = EL_UNDEFINED;
1634 player->inventory_size = 0;
1636 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1637 SnapField(player, 0, 0);
1639 player->LevelSolved = FALSE;
1640 player->GameOver = FALSE;
1643 network_player_action_received = FALSE;
1645 #if defined(NETWORK_AVALIABLE)
1646 /* initial null action */
1647 if (network_playing)
1648 SendToServer_MovePlayer(MV_NONE);
1657 TimeLeft = level.time;
1660 ScreenMovDir = MV_NONE;
1664 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1666 AllPlayersGone = FALSE;
1668 game.yamyam_content_nr = 0;
1669 game.magic_wall_active = FALSE;
1670 game.magic_wall_time_left = 0;
1671 game.light_time_left = 0;
1672 game.timegate_time_left = 0;
1673 game.switchgate_pos = 0;
1674 game.wind_direction = level.wind_direction_initial;
1675 game.gravity = level.initial_gravity;
1676 game.explosions_delayed = TRUE;
1678 game.envelope_active = FALSE;
1680 for (i = 0; i < NUM_BELTS; i++)
1682 game.belt_dir[i] = MV_NONE;
1683 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1686 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1687 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1689 for (x = 0; x < lev_fieldx; x++)
1691 for (y = 0; y < lev_fieldy; y++)
1693 Feld[x][y] = level.field[x][y];
1694 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1695 ChangeDelay[x][y] = 0;
1696 ChangePage[x][y] = -1;
1697 #if USE_NEW_CUSTOM_VALUE
1698 CustomValue[x][y] = 0; /* initialized in InitField() */
1700 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1702 WasJustMoving[x][y] = 0;
1703 WasJustFalling[x][y] = 0;
1704 CheckCollision[x][y] = 0;
1706 Pushed[x][y] = FALSE;
1708 Changed[x][y] = FALSE;
1709 ChangeEvent[x][y] = -1;
1711 ExplodePhase[x][y] = 0;
1712 ExplodeDelay[x][y] = 0;
1713 ExplodeField[x][y] = EX_TYPE_NONE;
1715 RunnerVisit[x][y] = 0;
1716 PlayerVisit[x][y] = 0;
1719 GfxRandom[x][y] = INIT_GFX_RANDOM();
1720 GfxElement[x][y] = EL_UNDEFINED;
1721 GfxAction[x][y] = ACTION_DEFAULT;
1722 GfxDir[x][y] = MV_NONE;
1726 for (y = 0; y < lev_fieldy; y++)
1728 for (x = 0; x < lev_fieldx; x++)
1730 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1732 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1734 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1737 InitField(x, y, TRUE);
1743 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1744 emulate_sb ? EMU_SOKOBAN :
1745 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1747 #if USE_NEW_ALL_SLIPPERY
1748 /* initialize type of slippery elements */
1749 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1751 if (!IS_CUSTOM_ELEMENT(i))
1753 /* default: elements slip down either to the left or right randomly */
1754 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
1756 /* SP style elements prefer to slip down on the left side */
1757 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
1758 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1760 /* BD style elements prefer to slip down on the left side */
1761 if (game.emulation == EMU_BOULDERDASH)
1762 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1767 /* initialize explosion and ignition delay */
1768 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1770 if (!IS_CUSTOM_ELEMENT(i))
1773 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1774 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1775 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1776 int last_phase = (num_phase + 1) * delay;
1777 int half_phase = (num_phase / 2) * delay;
1779 element_info[i].explosion_delay = last_phase - 1;
1780 element_info[i].ignition_delay = half_phase;
1782 if (i == EL_BLACK_ORB)
1783 element_info[i].ignition_delay = 1;
1787 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1788 element_info[i].explosion_delay = 1;
1790 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1791 element_info[i].ignition_delay = 1;
1795 /* correct non-moving belts to start moving left */
1796 for (i = 0; i < NUM_BELTS; i++)
1797 if (game.belt_dir[i] == MV_NONE)
1798 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1800 /* check if any connected player was not found in playfield */
1801 for (i = 0; i < MAX_PLAYERS; i++)
1803 struct PlayerInfo *player = &stored_player[i];
1805 if (player->connected && !player->present)
1807 for (j = 0; j < MAX_PLAYERS; j++)
1809 struct PlayerInfo *some_player = &stored_player[j];
1810 int jx = some_player->jx, jy = some_player->jy;
1812 /* assign first free player found that is present in the playfield */
1813 if (some_player->present && !some_player->connected)
1815 player->present = TRUE;
1816 player->active = TRUE;
1818 some_player->present = FALSE;
1819 some_player->active = FALSE;
1822 player->element_nr = some_player->element_nr;
1825 player->block_last_field = some_player->block_last_field;
1826 player->block_delay_adjustment = some_player->block_delay_adjustment;
1828 StorePlayer[jx][jy] = player->element_nr;
1829 player->jx = player->last_jx = jx;
1830 player->jy = player->last_jy = jy;
1840 /* when playing a tape, eliminate all players which do not participate */
1842 for (i = 0; i < MAX_PLAYERS; i++)
1844 if (stored_player[i].active && !tape.player_participates[i])
1846 struct PlayerInfo *player = &stored_player[i];
1847 int jx = player->jx, jy = player->jy;
1849 player->active = FALSE;
1850 StorePlayer[jx][jy] = 0;
1851 Feld[jx][jy] = EL_EMPTY;
1855 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1857 /* when in single player mode, eliminate all but the first active player */
1859 for (i = 0; i < MAX_PLAYERS; i++)
1861 if (stored_player[i].active)
1863 for (j = i + 1; j < MAX_PLAYERS; j++)
1865 if (stored_player[j].active)
1867 struct PlayerInfo *player = &stored_player[j];
1868 int jx = player->jx, jy = player->jy;
1870 player->active = FALSE;
1871 player->present = FALSE;
1873 StorePlayer[jx][jy] = 0;
1874 Feld[jx][jy] = EL_EMPTY;
1881 /* when recording the game, store which players take part in the game */
1884 for (i = 0; i < MAX_PLAYERS; i++)
1885 if (stored_player[i].active)
1886 tape.player_participates[i] = TRUE;
1891 for (i = 0; i < MAX_PLAYERS; i++)
1893 struct PlayerInfo *player = &stored_player[i];
1895 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1900 if (local_player == player)
1901 printf("Player %d is local player.\n", i+1);
1905 if (BorderElement == EL_EMPTY)
1908 SBX_Right = lev_fieldx - SCR_FIELDX;
1910 SBY_Lower = lev_fieldy - SCR_FIELDY;
1915 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1917 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1920 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1921 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1923 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1924 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1926 /* if local player not found, look for custom element that might create
1927 the player (make some assumptions about the right custom element) */
1928 if (!local_player->present)
1930 int start_x = 0, start_y = 0;
1931 int found_rating = 0;
1932 int found_element = EL_UNDEFINED;
1934 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1936 int element = Feld[x][y];
1941 if (!IS_CUSTOM_ELEMENT(element))
1944 if (CAN_CHANGE(element))
1946 for (i = 0; i < element_info[element].num_change_pages; i++)
1948 content = element_info[element].change_page[i].target_element;
1949 is_player = ELEM_IS_PLAYER(content);
1951 if (is_player && (found_rating < 3 || element < found_element))
1957 found_element = element;
1962 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1964 content = element_info[element].content.e[xx][yy];
1965 is_player = ELEM_IS_PLAYER(content);
1967 if (is_player && (found_rating < 2 || element < found_element))
1969 start_x = x + xx - 1;
1970 start_y = y + yy - 1;
1973 found_element = element;
1976 if (!CAN_CHANGE(element))
1979 for (i = 0; i < element_info[element].num_change_pages; i++)
1982 element_info[element].change_page[i].target_content.e[xx][yy];
1984 is_player = ELEM_IS_PLAYER(content);
1986 if (is_player && (found_rating < 1 || element < found_element))
1988 start_x = x + xx - 1;
1989 start_y = y + yy - 1;
1992 found_element = element;
1998 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1999 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2002 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2003 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2008 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2009 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2010 local_player->jx - MIDPOSX);
2012 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2013 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2014 local_player->jy - MIDPOSY);
2017 if (!game.restart_level)
2018 CloseDoor(DOOR_CLOSE_1);
2020 /* !!! FIX THIS (START) !!! */
2021 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2023 InitGameEngine_EM();
2030 /* after drawing the level, correct some elements */
2031 if (game.timegate_time_left == 0)
2032 CloseAllOpenTimegates();
2034 if (setup.soft_scrolling)
2035 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2037 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2040 /* !!! FIX THIS (END) !!! */
2042 if (!game.restart_level)
2044 /* copy default game door content to main double buffer */
2045 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2046 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2049 DrawGameDoorValues();
2051 if (!game.restart_level)
2055 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2056 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2057 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2061 /* copy actual game door content to door double buffer for OpenDoor() */
2062 BlitBitmap(drawto, bitmap_db_door,
2063 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2065 OpenDoor(DOOR_OPEN_ALL);
2067 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2069 if (setup.sound_music)
2072 KeyboardAutoRepeatOffUnlessAutoplay();
2076 for (i = 0; i < MAX_PLAYERS; i++)
2077 printf("Player %d %sactive.\n",
2078 i + 1, (stored_player[i].active ? "" : "not "));
2082 game.restart_level = FALSE;
2085 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2087 /* this is used for non-R'n'D game engines to update certain engine values */
2089 /* needed to determine if sounds are played within the visible screen area */
2090 scroll_x = actual_scroll_x;
2091 scroll_y = actual_scroll_y;
2094 void InitMovDir(int x, int y)
2096 int i, element = Feld[x][y];
2097 static int xy[4][2] =
2104 static int direction[3][4] =
2106 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2107 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2108 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2117 Feld[x][y] = EL_BUG;
2118 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2121 case EL_SPACESHIP_RIGHT:
2122 case EL_SPACESHIP_UP:
2123 case EL_SPACESHIP_LEFT:
2124 case EL_SPACESHIP_DOWN:
2125 Feld[x][y] = EL_SPACESHIP;
2126 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2129 case EL_BD_BUTTERFLY_RIGHT:
2130 case EL_BD_BUTTERFLY_UP:
2131 case EL_BD_BUTTERFLY_LEFT:
2132 case EL_BD_BUTTERFLY_DOWN:
2133 Feld[x][y] = EL_BD_BUTTERFLY;
2134 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2137 case EL_BD_FIREFLY_RIGHT:
2138 case EL_BD_FIREFLY_UP:
2139 case EL_BD_FIREFLY_LEFT:
2140 case EL_BD_FIREFLY_DOWN:
2141 Feld[x][y] = EL_BD_FIREFLY;
2142 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2145 case EL_PACMAN_RIGHT:
2147 case EL_PACMAN_LEFT:
2148 case EL_PACMAN_DOWN:
2149 Feld[x][y] = EL_PACMAN;
2150 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2153 case EL_SP_SNIKSNAK:
2154 MovDir[x][y] = MV_UP;
2157 case EL_SP_ELECTRON:
2158 MovDir[x][y] = MV_LEFT;
2165 Feld[x][y] = EL_MOLE;
2166 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2170 if (IS_CUSTOM_ELEMENT(element))
2172 struct ElementInfo *ei = &element_info[element];
2173 int move_direction_initial = ei->move_direction_initial;
2174 int move_pattern = ei->move_pattern;
2176 if (move_direction_initial == MV_START_PREVIOUS)
2178 if (MovDir[x][y] != MV_NONE)
2181 move_direction_initial = MV_START_AUTOMATIC;
2184 if (move_direction_initial == MV_START_RANDOM)
2185 MovDir[x][y] = 1 << RND(4);
2186 else if (move_direction_initial & MV_ANY_DIRECTION)
2187 MovDir[x][y] = move_direction_initial;
2188 else if (move_pattern == MV_ALL_DIRECTIONS ||
2189 move_pattern == MV_TURNING_LEFT ||
2190 move_pattern == MV_TURNING_RIGHT ||
2191 move_pattern == MV_TURNING_LEFT_RIGHT ||
2192 move_pattern == MV_TURNING_RIGHT_LEFT ||
2193 move_pattern == MV_TURNING_RANDOM)
2194 MovDir[x][y] = 1 << RND(4);
2195 else if (move_pattern == MV_HORIZONTAL)
2196 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2197 else if (move_pattern == MV_VERTICAL)
2198 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2199 else if (move_pattern & MV_ANY_DIRECTION)
2200 MovDir[x][y] = element_info[element].move_pattern;
2201 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2202 move_pattern == MV_ALONG_RIGHT_SIDE)
2204 /* use random direction as default start direction */
2205 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2206 MovDir[x][y] = 1 << RND(4);
2208 for (i = 0; i < NUM_DIRECTIONS; i++)
2210 int x1 = x + xy[i][0];
2211 int y1 = y + xy[i][1];
2213 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2215 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2216 MovDir[x][y] = direction[0][i];
2218 MovDir[x][y] = direction[1][i];
2227 MovDir[x][y] = 1 << RND(4);
2229 if (element != EL_BUG &&
2230 element != EL_SPACESHIP &&
2231 element != EL_BD_BUTTERFLY &&
2232 element != EL_BD_FIREFLY)
2235 for (i = 0; i < NUM_DIRECTIONS; i++)
2237 int x1 = x + xy[i][0];
2238 int y1 = y + xy[i][1];
2240 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2242 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2244 MovDir[x][y] = direction[0][i];
2247 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2248 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2250 MovDir[x][y] = direction[1][i];
2259 GfxDir[x][y] = MovDir[x][y];
2262 void InitAmoebaNr(int x, int y)
2265 int group_nr = AmoebeNachbarNr(x, y);
2269 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2271 if (AmoebaCnt[i] == 0)
2279 AmoebaNr[x][y] = group_nr;
2280 AmoebaCnt[group_nr]++;
2281 AmoebaCnt2[group_nr]++;
2287 boolean raise_level = FALSE;
2289 if (local_player->MovPos)
2292 if (tape.auto_play) /* tape might already be stopped here */
2293 tape.auto_play_level_solved = TRUE;
2295 local_player->LevelSolved = FALSE;
2297 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2301 if (!tape.playing && setup.sound_loops)
2302 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2303 SND_CTRL_PLAY_LOOP);
2305 while (TimeLeft > 0)
2307 if (!tape.playing && !setup.sound_loops)
2308 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2309 if (TimeLeft > 0 && !(TimeLeft % 10))
2310 RaiseScore(level.score[SC_TIME_BONUS]);
2311 if (TimeLeft > 100 && !(TimeLeft % 10))
2316 DrawGameValue_Time(TimeLeft);
2324 if (!tape.playing && setup.sound_loops)
2325 StopSound(SND_GAME_LEVELTIME_BONUS);
2327 else if (level.time == 0) /* level without time limit */
2329 if (!tape.playing && setup.sound_loops)
2330 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2331 SND_CTRL_PLAY_LOOP);
2333 while (TimePlayed < 999)
2335 if (!tape.playing && !setup.sound_loops)
2336 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2337 if (TimePlayed < 999 && !(TimePlayed % 10))
2338 RaiseScore(level.score[SC_TIME_BONUS]);
2339 if (TimePlayed < 900 && !(TimePlayed % 10))
2344 DrawGameValue_Time(TimePlayed);
2352 if (!tape.playing && setup.sound_loops)
2353 StopSound(SND_GAME_LEVELTIME_BONUS);
2356 /* close exit door after last player */
2357 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2358 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2359 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2361 int element = Feld[ExitX][ExitY];
2363 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2364 EL_SP_EXIT_CLOSING);
2366 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2369 /* player disappears */
2370 if (ExitX >= 0 && ExitY >= 0)
2371 DrawLevelField(ExitX, ExitY);
2378 CloseDoor(DOOR_CLOSE_1);
2383 SaveTape(tape.level_nr); /* Ask to save tape */
2386 if (level_nr == leveldir_current->handicap_level)
2388 leveldir_current->handicap_level++;
2389 SaveLevelSetup_SeriesInfo();
2392 if (level_editor_test_game)
2393 local_player->score = -1; /* no highscore when playing from editor */
2394 else if (level_nr < leveldir_current->last_level)
2395 raise_level = TRUE; /* advance to next level */
2397 if ((hi_pos = NewHiScore()) >= 0)
2399 game_status = GAME_MODE_SCORES;
2400 DrawHallOfFame(hi_pos);
2409 game_status = GAME_MODE_MAIN;
2426 LoadScore(level_nr);
2428 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2429 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2432 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2434 if (local_player->score > highscore[k].Score)
2436 /* player has made it to the hall of fame */
2438 if (k < MAX_SCORE_ENTRIES - 1)
2440 int m = MAX_SCORE_ENTRIES - 1;
2443 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2444 if (!strcmp(setup.player_name, highscore[l].Name))
2446 if (m == k) /* player's new highscore overwrites his old one */
2450 for (l = m; l > k; l--)
2452 strcpy(highscore[l].Name, highscore[l - 1].Name);
2453 highscore[l].Score = highscore[l - 1].Score;
2460 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2461 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2462 highscore[k].Score = local_player->score;
2468 else if (!strncmp(setup.player_name, highscore[k].Name,
2469 MAX_PLAYER_NAME_LEN))
2470 break; /* player already there with a higher score */
2476 SaveScore(level_nr);
2481 inline static int getElementMoveStepsize(int x, int y)
2483 int element = Feld[x][y];
2484 int direction = MovDir[x][y];
2485 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2486 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2487 int horiz_move = (dx != 0);
2488 int sign = (horiz_move ? dx : dy);
2489 int step = sign * element_info[element].move_stepsize;
2491 /* special values for move stepsize for spring and things on conveyor belt */
2495 if (element == EL_SPRING)
2496 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2497 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2498 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2499 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2501 if (CAN_FALL(element) &&
2502 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2503 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2504 else if (element == EL_SPRING)
2505 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2512 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2514 if (player->GfxAction != action || player->GfxDir != dir)
2517 printf("Player frame reset! (%d => %d, %d => %d)\n",
2518 player->GfxAction, action, player->GfxDir, dir);
2521 player->GfxAction = action;
2522 player->GfxDir = dir;
2524 player->StepFrame = 0;
2528 static void ResetRandomAnimationValue(int x, int y)
2530 GfxRandom[x][y] = INIT_GFX_RANDOM();
2533 static void ResetGfxAnimation(int x, int y)
2536 GfxAction[x][y] = ACTION_DEFAULT;
2537 GfxDir[x][y] = MovDir[x][y];
2540 void InitMovingField(int x, int y, int direction)
2542 int element = Feld[x][y];
2543 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2544 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2548 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2549 ResetGfxAnimation(x, y);
2551 MovDir[x][y] = direction;
2552 GfxDir[x][y] = direction;
2553 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2554 ACTION_FALLING : ACTION_MOVING);
2556 /* this is needed for CEs with property "can move" / "not moving" */
2558 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2560 if (Feld[newx][newy] == EL_EMPTY)
2561 Feld[newx][newy] = EL_BLOCKED;
2563 MovDir[newx][newy] = MovDir[x][y];
2565 #if USE_NEW_CUSTOM_VALUE
2566 CustomValue[newx][newy] = CustomValue[x][y];
2569 GfxFrame[newx][newy] = GfxFrame[x][y];
2570 GfxRandom[newx][newy] = GfxRandom[x][y];
2571 GfxAction[newx][newy] = GfxAction[x][y];
2572 GfxDir[newx][newy] = GfxDir[x][y];
2576 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2578 int direction = MovDir[x][y];
2579 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2580 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2586 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2588 int oldx = x, oldy = y;
2589 int direction = MovDir[x][y];
2591 if (direction == MV_LEFT)
2593 else if (direction == MV_RIGHT)
2595 else if (direction == MV_UP)
2597 else if (direction == MV_DOWN)
2600 *comes_from_x = oldx;
2601 *comes_from_y = oldy;
2604 int MovingOrBlocked2Element(int x, int y)
2606 int element = Feld[x][y];
2608 if (element == EL_BLOCKED)
2612 Blocked2Moving(x, y, &oldx, &oldy);
2613 return Feld[oldx][oldy];
2619 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2621 /* like MovingOrBlocked2Element(), but if element is moving
2622 and (x,y) is the field the moving element is just leaving,
2623 return EL_BLOCKED instead of the element value */
2624 int element = Feld[x][y];
2626 if (IS_MOVING(x, y))
2628 if (element == EL_BLOCKED)
2632 Blocked2Moving(x, y, &oldx, &oldy);
2633 return Feld[oldx][oldy];
2642 static void RemoveField(int x, int y)
2644 Feld[x][y] = EL_EMPTY;
2650 #if USE_NEW_CUSTOM_VALUE
2651 CustomValue[x][y] = 0;
2655 ChangeDelay[x][y] = 0;
2656 ChangePage[x][y] = -1;
2657 Pushed[x][y] = FALSE;
2660 ExplodeField[x][y] = EX_TYPE_NONE;
2663 GfxElement[x][y] = EL_UNDEFINED;
2664 GfxAction[x][y] = ACTION_DEFAULT;
2665 GfxDir[x][y] = MV_NONE;
2668 void RemoveMovingField(int x, int y)
2670 int oldx = x, oldy = y, newx = x, newy = y;
2671 int element = Feld[x][y];
2672 int next_element = EL_UNDEFINED;
2674 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2677 if (IS_MOVING(x, y))
2679 Moving2Blocked(x, y, &newx, &newy);
2681 if (Feld[newx][newy] != EL_BLOCKED)
2683 /* element is moving, but target field is not free (blocked), but
2684 already occupied by something different (example: acid pool);
2685 in this case, only remove the moving field, but not the target */
2687 RemoveField(oldx, oldy);
2689 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2691 DrawLevelField(oldx, oldy);
2696 else if (element == EL_BLOCKED)
2698 Blocked2Moving(x, y, &oldx, &oldy);
2699 if (!IS_MOVING(oldx, oldy))
2703 if (element == EL_BLOCKED &&
2704 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2705 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2706 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2707 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2708 next_element = get_next_element(Feld[oldx][oldy]);
2710 RemoveField(oldx, oldy);
2711 RemoveField(newx, newy);
2713 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2715 if (next_element != EL_UNDEFINED)
2716 Feld[oldx][oldy] = next_element;
2718 DrawLevelField(oldx, oldy);
2719 DrawLevelField(newx, newy);
2722 void DrawDynamite(int x, int y)
2724 int sx = SCREENX(x), sy = SCREENY(y);
2725 int graphic = el2img(Feld[x][y]);
2728 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2731 if (IS_WALKABLE_INSIDE(Back[x][y]))
2735 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2736 else if (Store[x][y])
2737 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2739 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2741 if (Back[x][y] || Store[x][y])
2742 DrawGraphicThruMask(sx, sy, graphic, frame);
2744 DrawGraphic(sx, sy, graphic, frame);
2747 void CheckDynamite(int x, int y)
2749 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2753 if (MovDelay[x][y] != 0)
2756 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2762 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2767 void DrawRelocatePlayer(struct PlayerInfo *player)
2769 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2770 boolean no_delay = (tape.warp_forward);
2771 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2772 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2773 int jx = player->jx;
2774 int jy = player->jy;
2776 if (level.instant_relocation)
2778 int offset = (setup.scroll_delay ? 3 : 0);
2780 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2782 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2783 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2784 local_player->jx - MIDPOSX);
2786 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2787 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2788 local_player->jy - MIDPOSY);
2792 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2793 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2794 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2796 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2797 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2798 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2800 /* don't scroll over playfield boundaries */
2801 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2802 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2804 /* don't scroll over playfield boundaries */
2805 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2806 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2809 RedrawPlayfield(TRUE, 0,0,0,0);
2813 int scroll_xx = -999, scroll_yy = -999;
2815 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2817 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2820 int fx = FX, fy = FY;
2822 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2823 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2824 local_player->jx - MIDPOSX);
2826 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2827 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2828 local_player->jy - MIDPOSY);
2830 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2831 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2833 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2839 fx += dx * TILEX / 2;
2840 fy += dy * TILEY / 2;
2842 ScrollLevel(dx, dy);
2845 /* scroll in two steps of half tile size to make things smoother */
2846 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2848 Delay(wait_delay_value);
2850 /* scroll second step to align at full tile size */
2852 Delay(wait_delay_value);
2857 Delay(wait_delay_value);
2861 void RelocatePlayer(int jx, int jy, int el_player_raw)
2863 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
2864 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2865 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2866 boolean no_delay = (tape.warp_forward);
2867 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2868 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2869 int old_jx = player->jx;
2870 int old_jy = player->jy;
2871 int old_element = Feld[old_jx][old_jy];
2872 int element = Feld[jx][jy];
2873 boolean player_relocated = (old_jx != jx || old_jy != jy);
2875 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2876 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2877 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2878 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2879 int leave_side_horiz = move_dir_horiz;
2880 int leave_side_vert = move_dir_vert;
2881 int enter_side = enter_side_horiz | enter_side_vert;
2882 int leave_side = leave_side_horiz | leave_side_vert;
2884 if (player->GameOver) /* do not reanimate dead player */
2887 if (!player_relocated) /* no need to relocate the player */
2890 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2892 RemoveField(jx, jy); /* temporarily remove newly placed player */
2893 DrawLevelField(jx, jy);
2896 if (player->present)
2898 while (player->MovPos)
2900 ScrollPlayer(player, SCROLL_GO_ON);
2901 ScrollScreen(NULL, SCROLL_GO_ON);
2903 AdvanceFrameAndPlayerCounters(player->index_nr);
2908 Delay(wait_delay_value);
2911 DrawPlayer(player); /* needed here only to cleanup last field */
2912 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2914 player->is_moving = FALSE;
2917 if (IS_CUSTOM_ELEMENT(old_element))
2918 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
2920 player->index_bit, leave_side);
2922 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
2924 player->index_bit, leave_side);
2926 Feld[jx][jy] = el_player;
2927 InitPlayerField(jx, jy, el_player, TRUE);
2929 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
2931 Feld[jx][jy] = element;
2932 InitField(jx, jy, FALSE);
2935 if (player == local_player) /* only visually relocate local player */
2936 DrawRelocatePlayer(player);
2938 TestIfPlayerTouchesBadThing(jx, jy);
2939 TestIfPlayerTouchesCustomElement(jx, jy);
2941 if (IS_CUSTOM_ELEMENT(element))
2942 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
2943 player->index_bit, enter_side);
2945 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
2946 player->index_bit, enter_side);
2949 void Explode(int ex, int ey, int phase, int mode)
2955 /* !!! eliminate this variable !!! */
2956 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2958 if (game.explosions_delayed)
2960 ExplodeField[ex][ey] = mode;
2964 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2966 int center_element = Feld[ex][ey];
2969 /* --- This is only really needed (and now handled) in "Impact()". --- */
2970 /* do not explode moving elements that left the explode field in time */
2971 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2972 center_element == EL_EMPTY &&
2973 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
2977 if (mode == EX_TYPE_NORMAL ||
2978 mode == EX_TYPE_CENTER ||
2979 mode == EX_TYPE_CROSS)
2980 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2982 /* remove things displayed in background while burning dynamite */
2983 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2986 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2988 /* put moving element to center field (and let it explode there) */
2989 center_element = MovingOrBlocked2Element(ex, ey);
2990 RemoveMovingField(ex, ey);
2991 Feld[ex][ey] = center_element;
2994 last_phase = element_info[center_element].explosion_delay + 1;
2996 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2998 int xx = x - ex + 1;
2999 int yy = y - ey + 1;
3002 if (!IN_LEV_FIELD(x, y) ||
3003 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3004 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3007 element = Feld[x][y];
3009 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3011 element = MovingOrBlocked2Element(x, y);
3013 if (!IS_EXPLOSION_PROOF(element))
3014 RemoveMovingField(x, y);
3017 /* indestructible elements can only explode in center (but not flames) */
3018 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3019 mode == EX_TYPE_BORDER)) ||
3020 element == EL_FLAMES)
3023 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3024 behaviour, for example when touching a yamyam that explodes to rocks
3025 with active deadly shield, a rock is created under the player !!! */
3026 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3028 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3029 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3030 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3032 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3035 if (IS_ACTIVE_BOMB(element))
3037 /* re-activate things under the bomb like gate or penguin */
3038 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3045 /* save walkable background elements while explosion on same tile */
3046 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3047 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3048 Back[x][y] = element;
3050 /* ignite explodable elements reached by other explosion */
3051 if (element == EL_EXPLOSION)
3052 element = Store2[x][y];
3054 if (AmoebaNr[x][y] &&
3055 (element == EL_AMOEBA_FULL ||
3056 element == EL_BD_AMOEBA ||
3057 element == EL_AMOEBA_GROWING))
3059 AmoebaCnt[AmoebaNr[x][y]]--;
3060 AmoebaCnt2[AmoebaNr[x][y]]--;
3065 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3067 switch(StorePlayer[ex][ey])
3070 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3073 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3076 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3080 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3084 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3085 Store[x][y] = EL_EMPTY;
3087 else if (center_element == EL_MOLE)
3088 Store[x][y] = EL_EMERALD_RED;
3089 else if (center_element == EL_PENGUIN)
3090 Store[x][y] = EL_EMERALD_PURPLE;
3091 else if (center_element == EL_BUG)
3092 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3093 else if (center_element == EL_BD_BUTTERFLY)
3094 Store[x][y] = EL_BD_DIAMOND;
3095 else if (center_element == EL_SP_ELECTRON)
3096 Store[x][y] = EL_SP_INFOTRON;
3097 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3098 Store[x][y] = level.amoeba_content;
3099 else if (center_element == EL_YAMYAM)
3100 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3101 else if (IS_CUSTOM_ELEMENT(center_element) &&
3102 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3103 Store[x][y] = element_info[center_element].content.e[xx][yy];
3104 else if (element == EL_WALL_EMERALD)
3105 Store[x][y] = EL_EMERALD;
3106 else if (element == EL_WALL_DIAMOND)
3107 Store[x][y] = EL_DIAMOND;
3108 else if (element == EL_WALL_BD_DIAMOND)
3109 Store[x][y] = EL_BD_DIAMOND;
3110 else if (element == EL_WALL_EMERALD_YELLOW)
3111 Store[x][y] = EL_EMERALD_YELLOW;
3112 else if (element == EL_WALL_EMERALD_RED)
3113 Store[x][y] = EL_EMERALD_RED;
3114 else if (element == EL_WALL_EMERALD_PURPLE)
3115 Store[x][y] = EL_EMERALD_PURPLE;
3116 else if (element == EL_WALL_PEARL)
3117 Store[x][y] = EL_PEARL;
3118 else if (element == EL_WALL_CRYSTAL)
3119 Store[x][y] = EL_CRYSTAL;
3120 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3121 Store[x][y] = element_info[element].content.e[1][1];
3123 Store[x][y] = EL_EMPTY;
3125 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3126 center_element == EL_AMOEBA_TO_DIAMOND)
3127 Store2[x][y] = element;
3129 Feld[x][y] = EL_EXPLOSION;
3130 GfxElement[x][y] = center_element;
3132 ExplodePhase[x][y] = 1;
3133 ExplodeDelay[x][y] = last_phase;
3138 if (center_element == EL_YAMYAM)
3139 game.yamyam_content_nr =
3140 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3152 GfxFrame[x][y] = 0; /* restart explosion animation */
3154 last_phase = ExplodeDelay[x][y];
3156 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3160 /* activate this even in non-DEBUG version until cause for crash in
3161 getGraphicAnimationFrame() (see below) is found and eliminated */
3166 if (GfxElement[x][y] == EL_UNDEFINED)
3169 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3170 printf("Explode(): This should never happen!\n");
3173 GfxElement[x][y] = EL_EMPTY;
3177 border_element = Store2[x][y];
3178 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3179 border_element = StorePlayer[x][y];
3181 if (phase == element_info[border_element].ignition_delay ||
3182 phase == last_phase)
3184 boolean border_explosion = FALSE;
3186 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3187 !PLAYER_EXPLOSION_PROTECTED(x, y))
3189 KillPlayerUnlessExplosionProtected(x, y);
3190 border_explosion = TRUE;
3192 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3194 Feld[x][y] = Store2[x][y];
3197 border_explosion = TRUE;
3199 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3201 AmoebeUmwandeln(x, y);
3203 border_explosion = TRUE;
3206 /* if an element just explodes due to another explosion (chain-reaction),
3207 do not immediately end the new explosion when it was the last frame of
3208 the explosion (as it would be done in the following "if"-statement!) */
3209 if (border_explosion && phase == last_phase)
3213 if (phase == last_phase)
3217 element = Feld[x][y] = Store[x][y];
3218 Store[x][y] = Store2[x][y] = 0;
3219 GfxElement[x][y] = EL_UNDEFINED;
3221 /* player can escape from explosions and might therefore be still alive */
3222 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3223 element <= EL_PLAYER_IS_EXPLODING_4)
3224 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3226 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3227 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3228 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3231 /* restore probably existing indestructible background element */
3232 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3233 element = Feld[x][y] = Back[x][y];
3236 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3237 GfxDir[x][y] = MV_NONE;
3238 ChangeDelay[x][y] = 0;
3239 ChangePage[x][y] = -1;
3241 #if USE_NEW_CUSTOM_VALUE
3242 CustomValue[x][y] = 0;
3245 InitField_WithBug2(x, y, FALSE);
3247 DrawLevelField(x, y);
3249 TestIfElementTouchesCustomElement(x, y);
3251 if (GFX_CRUMBLED(element))
3252 DrawLevelFieldCrumbledSandNeighbours(x, y);
3254 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3255 StorePlayer[x][y] = 0;
3257 if (ELEM_IS_PLAYER(element))
3258 RelocatePlayer(x, y, element);
3260 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3262 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3263 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3266 DrawLevelFieldCrumbledSand(x, y);
3268 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3270 DrawLevelElement(x, y, Back[x][y]);
3271 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3273 else if (IS_WALKABLE_UNDER(Back[x][y]))
3275 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3276 DrawLevelElementThruMask(x, y, Back[x][y]);
3278 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3279 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3283 void DynaExplode(int ex, int ey)
3286 int dynabomb_element = Feld[ex][ey];
3287 int dynabomb_size = 1;
3288 boolean dynabomb_xl = FALSE;
3289 struct PlayerInfo *player;
3290 static int xy[4][2] =
3298 if (IS_ACTIVE_BOMB(dynabomb_element))
3300 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3301 dynabomb_size = player->dynabomb_size;
3302 dynabomb_xl = player->dynabomb_xl;
3303 player->dynabombs_left++;
3306 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3308 for (i = 0; i < NUM_DIRECTIONS; i++)
3310 for (j = 1; j <= dynabomb_size; j++)
3312 int x = ex + j * xy[i][0];
3313 int y = ey + j * xy[i][1];
3316 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3319 element = Feld[x][y];
3321 /* do not restart explosions of fields with active bombs */
3322 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3325 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3327 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3328 !IS_DIGGABLE(element) && !dynabomb_xl)
3334 void Bang(int x, int y)
3336 int element = MovingOrBlocked2Element(x, y);
3338 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3340 struct PlayerInfo *player = PLAYERINFO(x, y);
3342 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3343 player->element_nr);
3350 case EL_BD_BUTTERFLY:
3353 case EL_DARK_YAMYAM:
3357 RaiseScoreElement(element);
3358 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3360 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3361 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3362 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3363 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3364 case EL_DYNABOMB_INCREASE_NUMBER:
3365 case EL_DYNABOMB_INCREASE_SIZE:
3366 case EL_DYNABOMB_INCREASE_POWER:
3371 case EL_LAMP_ACTIVE:
3372 case EL_AMOEBA_TO_DIAMOND:
3373 if (IS_PLAYER(x, y))
3374 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3376 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3379 if (element_info[element].explosion_type == EXPLODES_CROSS)
3380 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3381 else if (element_info[element].explosion_type == EXPLODES_1X1)
3382 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3384 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3388 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3391 void SplashAcid(int x, int y)
3393 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3394 (!IN_LEV_FIELD(x - 1, y - 2) ||
3395 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3396 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3398 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3399 (!IN_LEV_FIELD(x + 1, y - 2) ||
3400 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3401 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3403 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3406 static void InitBeltMovement()
3408 static int belt_base_element[4] =
3410 EL_CONVEYOR_BELT_1_LEFT,
3411 EL_CONVEYOR_BELT_2_LEFT,
3412 EL_CONVEYOR_BELT_3_LEFT,
3413 EL_CONVEYOR_BELT_4_LEFT
3415 static int belt_base_active_element[4] =
3417 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3418 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3419 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3420 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3425 /* set frame order for belt animation graphic according to belt direction */
3426 for (i = 0; i < NUM_BELTS; i++)
3430 for (j = 0; j < NUM_BELT_PARTS; j++)
3432 int element = belt_base_active_element[belt_nr] + j;
3433 int graphic = el2img(element);
3435 if (game.belt_dir[i] == MV_LEFT)
3436 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3438 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3442 for (y = 0; y < lev_fieldy; y++)
3444 for (x = 0; x < lev_fieldx; x++)
3446 int element = Feld[x][y];
3448 for (i = 0; i < NUM_BELTS; i++)
3450 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3452 int e_belt_nr = getBeltNrFromBeltElement(element);
3455 if (e_belt_nr == belt_nr)
3457 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3459 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3467 static void ToggleBeltSwitch(int x, int y)
3469 static int belt_base_element[4] =
3471 EL_CONVEYOR_BELT_1_LEFT,
3472 EL_CONVEYOR_BELT_2_LEFT,
3473 EL_CONVEYOR_BELT_3_LEFT,
3474 EL_CONVEYOR_BELT_4_LEFT
3476 static int belt_base_active_element[4] =
3478 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3479 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3480 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3481 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3483 static int belt_base_switch_element[4] =
3485 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3486 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3487 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3488 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3490 static int belt_move_dir[4] =
3498 int element = Feld[x][y];
3499 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3500 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3501 int belt_dir = belt_move_dir[belt_dir_nr];
3504 if (!IS_BELT_SWITCH(element))
3507 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3508 game.belt_dir[belt_nr] = belt_dir;
3510 if (belt_dir_nr == 3)
3513 /* set frame order for belt animation graphic according to belt direction */
3514 for (i = 0; i < NUM_BELT_PARTS; i++)
3516 int element = belt_base_active_element[belt_nr] + i;
3517 int graphic = el2img(element);
3519 if (belt_dir == MV_LEFT)
3520 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3522 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3525 for (yy = 0; yy < lev_fieldy; yy++)
3527 for (xx = 0; xx < lev_fieldx; xx++)
3529 int element = Feld[xx][yy];
3531 if (IS_BELT_SWITCH(element))
3533 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3535 if (e_belt_nr == belt_nr)
3537 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3538 DrawLevelField(xx, yy);
3541 else if (IS_BELT(element) && belt_dir != MV_NONE)
3543 int e_belt_nr = getBeltNrFromBeltElement(element);
3545 if (e_belt_nr == belt_nr)
3547 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3549 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3550 DrawLevelField(xx, yy);
3553 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3555 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3557 if (e_belt_nr == belt_nr)
3559 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3561 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3562 DrawLevelField(xx, yy);
3569 static void ToggleSwitchgateSwitch(int x, int y)
3573 game.switchgate_pos = !game.switchgate_pos;
3575 for (yy = 0; yy < lev_fieldy; yy++)
3577 for (xx = 0; xx < lev_fieldx; xx++)
3579 int element = Feld[xx][yy];
3581 if (element == EL_SWITCHGATE_SWITCH_UP ||
3582 element == EL_SWITCHGATE_SWITCH_DOWN)
3584 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3585 DrawLevelField(xx, yy);
3587 else if (element == EL_SWITCHGATE_OPEN ||
3588 element == EL_SWITCHGATE_OPENING)
3590 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3592 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3594 else if (element == EL_SWITCHGATE_CLOSED ||
3595 element == EL_SWITCHGATE_CLOSING)
3597 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3599 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3605 static int getInvisibleActiveFromInvisibleElement(int element)
3607 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3608 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3609 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3613 static int getInvisibleFromInvisibleActiveElement(int element)
3615 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3616 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3617 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3621 static void RedrawAllLightSwitchesAndInvisibleElements()
3625 for (y = 0; y < lev_fieldy; y++)
3627 for (x = 0; x < lev_fieldx; x++)
3629 int element = Feld[x][y];
3631 if (element == EL_LIGHT_SWITCH &&
3632 game.light_time_left > 0)
3634 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3635 DrawLevelField(x, y);
3637 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3638 game.light_time_left == 0)
3640 Feld[x][y] = EL_LIGHT_SWITCH;
3641 DrawLevelField(x, y);
3643 else if (element == EL_INVISIBLE_STEELWALL ||
3644 element == EL_INVISIBLE_WALL ||
3645 element == EL_INVISIBLE_SAND)
3647 if (game.light_time_left > 0)
3648 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3650 DrawLevelField(x, y);
3652 /* uncrumble neighbour fields, if needed */
3653 if (element == EL_INVISIBLE_SAND)
3654 DrawLevelFieldCrumbledSandNeighbours(x, y);
3656 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3657 element == EL_INVISIBLE_WALL_ACTIVE ||
3658 element == EL_INVISIBLE_SAND_ACTIVE)
3660 if (game.light_time_left == 0)
3661 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3663 DrawLevelField(x, y);
3665 /* re-crumble neighbour fields, if needed */
3666 if (element == EL_INVISIBLE_SAND)
3667 DrawLevelFieldCrumbledSandNeighbours(x, y);
3673 static void ToggleLightSwitch(int x, int y)
3675 int element = Feld[x][y];
3677 game.light_time_left =
3678 (element == EL_LIGHT_SWITCH ?
3679 level.time_light * FRAMES_PER_SECOND : 0);
3681 RedrawAllLightSwitchesAndInvisibleElements();
3684 static void ActivateTimegateSwitch(int x, int y)
3688 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3690 for (yy = 0; yy < lev_fieldy; yy++)
3692 for (xx = 0; xx < lev_fieldx; xx++)
3694 int element = Feld[xx][yy];
3696 if (element == EL_TIMEGATE_CLOSED ||
3697 element == EL_TIMEGATE_CLOSING)
3699 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3700 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3704 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3706 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3707 DrawLevelField(xx, yy);
3714 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3717 void Impact(int x, int y)
3719 boolean last_line = (y == lev_fieldy - 1);
3720 boolean object_hit = FALSE;
3721 boolean impact = (last_line || object_hit);
3722 int element = Feld[x][y];
3723 int smashed = EL_STEELWALL;
3725 if (!last_line) /* check if element below was hit */
3727 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3730 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3731 MovDir[x][y + 1] != MV_DOWN ||
3732 MovPos[x][y + 1] <= TILEY / 2));
3734 /* do not smash moving elements that left the smashed field in time */
3735 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3736 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3740 smashed = MovingOrBlocked2Element(x, y + 1);
3742 impact = (last_line || object_hit);
3745 if (!last_line && smashed == EL_ACID) /* element falls into acid */
3747 SplashAcid(x, y + 1);
3751 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
3752 /* only reset graphic animation if graphic really changes after impact */
3754 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3756 ResetGfxAnimation(x, y);
3757 DrawLevelField(x, y);
3760 if (impact && CAN_EXPLODE_IMPACT(element))
3765 else if (impact && element == EL_PEARL)
3767 ResetGfxAnimation(x, y);
3769 Feld[x][y] = EL_PEARL_BREAKING;
3770 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3773 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
3775 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3780 if (impact && element == EL_AMOEBA_DROP)
3782 if (object_hit && IS_PLAYER(x, y + 1))
3783 KillPlayerUnlessEnemyProtected(x, y + 1);
3784 else if (object_hit && smashed == EL_PENGUIN)
3788 Feld[x][y] = EL_AMOEBA_GROWING;
3789 Store[x][y] = EL_AMOEBA_WET;
3791 ResetRandomAnimationValue(x, y);
3796 if (object_hit) /* check which object was hit */
3798 if (CAN_PASS_MAGIC_WALL(element) &&
3799 (smashed == EL_MAGIC_WALL ||
3800 smashed == EL_BD_MAGIC_WALL))
3803 int activated_magic_wall =
3804 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3805 EL_BD_MAGIC_WALL_ACTIVE);
3807 /* activate magic wall / mill */
3808 for (yy = 0; yy < lev_fieldy; yy++)
3809 for (xx = 0; xx < lev_fieldx; xx++)
3810 if (Feld[xx][yy] == smashed)
3811 Feld[xx][yy] = activated_magic_wall;
3813 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3814 game.magic_wall_active = TRUE;
3816 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3817 SND_MAGIC_WALL_ACTIVATING :
3818 SND_BD_MAGIC_WALL_ACTIVATING));
3821 if (IS_PLAYER(x, y + 1))
3823 if (CAN_SMASH_PLAYER(element))
3825 KillPlayerUnlessEnemyProtected(x, y + 1);
3829 else if (smashed == EL_PENGUIN)
3831 if (CAN_SMASH_PLAYER(element))
3837 else if (element == EL_BD_DIAMOND)
3839 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3845 else if (((element == EL_SP_INFOTRON ||
3846 element == EL_SP_ZONK) &&
3847 (smashed == EL_SP_SNIKSNAK ||
3848 smashed == EL_SP_ELECTRON ||
3849 smashed == EL_SP_DISK_ORANGE)) ||
3850 (element == EL_SP_INFOTRON &&
3851 smashed == EL_SP_DISK_YELLOW))
3856 else if (CAN_SMASH_EVERYTHING(element))
3858 if (IS_CLASSIC_ENEMY(smashed) ||
3859 CAN_EXPLODE_SMASHED(smashed))
3864 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3866 if (smashed == EL_LAMP ||
3867 smashed == EL_LAMP_ACTIVE)
3872 else if (smashed == EL_NUT)
3874 Feld[x][y + 1] = EL_NUT_BREAKING;
3875 PlayLevelSound(x, y, SND_NUT_BREAKING);
3876 RaiseScoreElement(EL_NUT);
3879 else if (smashed == EL_PEARL)
3881 ResetGfxAnimation(x, y);
3883 Feld[x][y + 1] = EL_PEARL_BREAKING;
3884 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3887 else if (smashed == EL_DIAMOND)
3889 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3890 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3893 else if (IS_BELT_SWITCH(smashed))
3895 ToggleBeltSwitch(x, y + 1);
3897 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3898 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3900 ToggleSwitchgateSwitch(x, y + 1);
3902 else if (smashed == EL_LIGHT_SWITCH ||
3903 smashed == EL_LIGHT_SWITCH_ACTIVE)
3905 ToggleLightSwitch(x, y + 1);
3910 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
3913 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
3915 CheckElementChangeBySide(x, y + 1, smashed, element,
3916 CE_SWITCHED, CH_SIDE_TOP);
3917 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
3923 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
3928 /* play sound of magic wall / mill */
3930 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3931 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3933 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3934 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3935 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3936 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3941 /* play sound of object that hits the ground */
3942 if (last_line || object_hit)
3943 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3946 inline static void TurnRoundExt(int x, int y)
3958 { 0, 0 }, { 0, 0 }, { 0, 0 },
3963 int left, right, back;
3967 { MV_DOWN, MV_UP, MV_RIGHT },
3968 { MV_UP, MV_DOWN, MV_LEFT },
3970 { MV_LEFT, MV_RIGHT, MV_DOWN },
3974 { MV_RIGHT, MV_LEFT, MV_UP }
3977 int element = Feld[x][y];
3978 int move_pattern = element_info[element].move_pattern;
3980 int old_move_dir = MovDir[x][y];
3981 int left_dir = turn[old_move_dir].left;
3982 int right_dir = turn[old_move_dir].right;
3983 int back_dir = turn[old_move_dir].back;
3985 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3986 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3987 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3988 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3990 int left_x = x + left_dx, left_y = y + left_dy;
3991 int right_x = x + right_dx, right_y = y + right_dy;
3992 int move_x = x + move_dx, move_y = y + move_dy;
3996 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3998 TestIfBadThingTouchesOtherBadThing(x, y);
4000 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4001 MovDir[x][y] = right_dir;
4002 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4003 MovDir[x][y] = left_dir;
4005 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4007 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4010 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4012 TestIfBadThingTouchesOtherBadThing(x, y);
4014 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4015 MovDir[x][y] = left_dir;
4016 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4017 MovDir[x][y] = right_dir;
4019 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4021 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4024 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4026 TestIfBadThingTouchesOtherBadThing(x, y);
4028 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4029 MovDir[x][y] = left_dir;
4030 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4031 MovDir[x][y] = right_dir;
4033 if (MovDir[x][y] != old_move_dir)
4036 else if (element == EL_YAMYAM)
4038 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4039 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4041 if (can_turn_left && can_turn_right)
4042 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4043 else if (can_turn_left)
4044 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4045 else if (can_turn_right)
4046 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4048 MovDir[x][y] = back_dir;
4050 MovDelay[x][y] = 16 + 16 * RND(3);
4052 else if (element == EL_DARK_YAMYAM)
4054 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4056 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4059 if (can_turn_left && can_turn_right)
4060 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4061 else if (can_turn_left)
4062 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4063 else if (can_turn_right)
4064 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4066 MovDir[x][y] = back_dir;
4068 MovDelay[x][y] = 16 + 16 * RND(3);
4070 else if (element == EL_PACMAN)
4072 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4073 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4075 if (can_turn_left && can_turn_right)
4076 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4077 else if (can_turn_left)
4078 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4079 else if (can_turn_right)
4080 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4082 MovDir[x][y] = back_dir;
4084 MovDelay[x][y] = 6 + RND(40);
4086 else if (element == EL_PIG)
4088 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4089 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4090 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4091 boolean should_turn_left, should_turn_right, should_move_on;
4093 int rnd = RND(rnd_value);
4095 should_turn_left = (can_turn_left &&
4097 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4098 y + back_dy + left_dy)));
4099 should_turn_right = (can_turn_right &&
4101 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4102 y + back_dy + right_dy)));
4103 should_move_on = (can_move_on &&
4106 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4107 y + move_dy + left_dy) ||
4108 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4109 y + move_dy + right_dy)));
4111 if (should_turn_left || should_turn_right || should_move_on)
4113 if (should_turn_left && should_turn_right && should_move_on)
4114 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4115 rnd < 2 * rnd_value / 3 ? right_dir :
4117 else if (should_turn_left && should_turn_right)
4118 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4119 else if (should_turn_left && should_move_on)
4120 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4121 else if (should_turn_right && should_move_on)
4122 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4123 else if (should_turn_left)
4124 MovDir[x][y] = left_dir;
4125 else if (should_turn_right)
4126 MovDir[x][y] = right_dir;
4127 else if (should_move_on)
4128 MovDir[x][y] = old_move_dir;
4130 else if (can_move_on && rnd > rnd_value / 8)
4131 MovDir[x][y] = old_move_dir;
4132 else if (can_turn_left && can_turn_right)
4133 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4134 else if (can_turn_left && rnd > rnd_value / 8)
4135 MovDir[x][y] = left_dir;
4136 else if (can_turn_right && rnd > rnd_value/8)
4137 MovDir[x][y] = right_dir;
4139 MovDir[x][y] = back_dir;
4141 xx = x + move_xy[MovDir[x][y]].x;
4142 yy = y + move_xy[MovDir[x][y]].y;
4144 if (!IN_LEV_FIELD(xx, yy) ||
4145 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4146 MovDir[x][y] = old_move_dir;
4150 else if (element == EL_DRAGON)
4152 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4153 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4154 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4156 int rnd = RND(rnd_value);
4158 if (can_move_on && rnd > rnd_value / 8)
4159 MovDir[x][y] = old_move_dir;
4160 else if (can_turn_left && can_turn_right)
4161 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4162 else if (can_turn_left && rnd > rnd_value / 8)
4163 MovDir[x][y] = left_dir;
4164 else if (can_turn_right && rnd > rnd_value / 8)
4165 MovDir[x][y] = right_dir;
4167 MovDir[x][y] = back_dir;
4169 xx = x + move_xy[MovDir[x][y]].x;
4170 yy = y + move_xy[MovDir[x][y]].y;
4172 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4173 MovDir[x][y] = old_move_dir;
4177 else if (element == EL_MOLE)
4179 boolean can_move_on =
4180 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4181 IS_AMOEBOID(Feld[move_x][move_y]) ||
4182 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4185 boolean can_turn_left =
4186 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4187 IS_AMOEBOID(Feld[left_x][left_y])));
4189 boolean can_turn_right =
4190 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4191 IS_AMOEBOID(Feld[right_x][right_y])));
4193 if (can_turn_left && can_turn_right)
4194 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4195 else if (can_turn_left)
4196 MovDir[x][y] = left_dir;
4198 MovDir[x][y] = right_dir;
4201 if (MovDir[x][y] != old_move_dir)
4204 else if (element == EL_BALLOON)
4206 MovDir[x][y] = game.wind_direction;
4209 else if (element == EL_SPRING)
4211 if (MovDir[x][y] & MV_HORIZONTAL &&
4212 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4213 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4214 MovDir[x][y] = MV_NONE;
4218 else if (element == EL_ROBOT ||
4219 element == EL_SATELLITE ||
4220 element == EL_PENGUIN)
4222 int attr_x = -1, attr_y = -1;
4233 for (i = 0; i < MAX_PLAYERS; i++)
4235 struct PlayerInfo *player = &stored_player[i];
4236 int jx = player->jx, jy = player->jy;
4238 if (!player->active)
4242 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4250 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4251 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4252 game.engine_version < VERSION_IDENT(3,1,0,0)))
4258 if (element == EL_PENGUIN)
4261 static int xy[4][2] =
4269 for (i = 0; i < NUM_DIRECTIONS; i++)
4271 int ex = x + xy[i][0];
4272 int ey = y + xy[i][1];
4274 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4283 MovDir[x][y] = MV_NONE;
4285 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4286 else if (attr_x > x)
4287 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4289 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4290 else if (attr_y > y)
4291 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4293 if (element == EL_ROBOT)
4297 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4298 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4299 Moving2Blocked(x, y, &newx, &newy);
4301 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4302 MovDelay[x][y] = 8 + 8 * !RND(3);
4304 MovDelay[x][y] = 16;
4306 else if (element == EL_PENGUIN)
4312 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4314 boolean first_horiz = RND(2);
4315 int new_move_dir = MovDir[x][y];
4318 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4319 Moving2Blocked(x, y, &newx, &newy);
4321 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4325 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4326 Moving2Blocked(x, y, &newx, &newy);
4328 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4331 MovDir[x][y] = old_move_dir;
4335 else /* (element == EL_SATELLITE) */
4341 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4343 boolean first_horiz = RND(2);
4344 int new_move_dir = MovDir[x][y];
4347 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4348 Moving2Blocked(x, y, &newx, &newy);
4350 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4354 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4355 Moving2Blocked(x, y, &newx, &newy);
4357 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4360 MovDir[x][y] = old_move_dir;
4365 else if (move_pattern == MV_TURNING_LEFT ||
4366 move_pattern == MV_TURNING_RIGHT ||
4367 move_pattern == MV_TURNING_LEFT_RIGHT ||
4368 move_pattern == MV_TURNING_RIGHT_LEFT ||
4369 move_pattern == MV_TURNING_RANDOM ||
4370 move_pattern == MV_ALL_DIRECTIONS)
4372 boolean can_turn_left =
4373 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4374 boolean can_turn_right =
4375 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4377 if (element_info[element].move_stepsize == 0) /* "not moving" */
4380 if (move_pattern == MV_TURNING_LEFT)
4381 MovDir[x][y] = left_dir;
4382 else if (move_pattern == MV_TURNING_RIGHT)
4383 MovDir[x][y] = right_dir;
4384 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4385 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4386 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4387 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4388 else if (move_pattern == MV_TURNING_RANDOM)
4389 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4390 can_turn_right && !can_turn_left ? right_dir :
4391 RND(2) ? left_dir : right_dir);
4392 else if (can_turn_left && can_turn_right)
4393 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4394 else if (can_turn_left)
4395 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4396 else if (can_turn_right)
4397 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4399 MovDir[x][y] = back_dir;
4401 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4403 else if (move_pattern == MV_HORIZONTAL ||
4404 move_pattern == MV_VERTICAL)
4406 if (move_pattern & old_move_dir)
4407 MovDir[x][y] = back_dir;
4408 else if (move_pattern == MV_HORIZONTAL)
4409 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4410 else if (move_pattern == MV_VERTICAL)
4411 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4413 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4415 else if (move_pattern & MV_ANY_DIRECTION)
4417 MovDir[x][y] = move_pattern;
4418 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4420 else if (move_pattern & MV_WIND_DIRECTION)
4422 MovDir[x][y] = game.wind_direction;
4423 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4425 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4427 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4428 MovDir[x][y] = left_dir;
4429 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4430 MovDir[x][y] = right_dir;
4432 if (MovDir[x][y] != old_move_dir)
4433 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4435 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4437 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4438 MovDir[x][y] = right_dir;
4439 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4440 MovDir[x][y] = left_dir;
4442 if (MovDir[x][y] != old_move_dir)
4443 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4445 else if (move_pattern == MV_TOWARDS_PLAYER ||
4446 move_pattern == MV_AWAY_FROM_PLAYER)
4448 int attr_x = -1, attr_y = -1;
4450 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4461 for (i = 0; i < MAX_PLAYERS; i++)
4463 struct PlayerInfo *player = &stored_player[i];
4464 int jx = player->jx, jy = player->jy;
4466 if (!player->active)
4470 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4478 MovDir[x][y] = MV_NONE;
4480 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4481 else if (attr_x > x)
4482 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4484 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4485 else if (attr_y > y)
4486 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4488 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4490 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4492 boolean first_horiz = RND(2);
4493 int new_move_dir = MovDir[x][y];
4495 if (element_info[element].move_stepsize == 0) /* "not moving" */
4497 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
4498 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4504 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4505 Moving2Blocked(x, y, &newx, &newy);
4507 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4511 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4512 Moving2Blocked(x, y, &newx, &newy);
4514 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4517 MovDir[x][y] = old_move_dir;
4520 else if (move_pattern == MV_WHEN_PUSHED ||
4521 move_pattern == MV_WHEN_DROPPED)
4523 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4524 MovDir[x][y] = MV_NONE;
4528 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4530 static int test_xy[7][2] =
4540 static int test_dir[7] =
4550 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4551 int move_preference = -1000000; /* start with very low preference */
4552 int new_move_dir = MV_NONE;
4553 int start_test = RND(4);
4556 for (i = 0; i < NUM_DIRECTIONS; i++)
4558 int move_dir = test_dir[start_test + i];
4559 int move_dir_preference;
4561 xx = x + test_xy[start_test + i][0];
4562 yy = y + test_xy[start_test + i][1];
4564 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4565 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4567 new_move_dir = move_dir;
4572 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4575 move_dir_preference = -1 * RunnerVisit[xx][yy];
4576 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4577 move_dir_preference = PlayerVisit[xx][yy];
4579 if (move_dir_preference > move_preference)
4581 /* prefer field that has not been visited for the longest time */
4582 move_preference = move_dir_preference;
4583 new_move_dir = move_dir;
4585 else if (move_dir_preference == move_preference &&
4586 move_dir == old_move_dir)
4588 /* prefer last direction when all directions are preferred equally */
4589 move_preference = move_dir_preference;
4590 new_move_dir = move_dir;
4594 MovDir[x][y] = new_move_dir;
4595 if (old_move_dir != new_move_dir)
4596 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4600 static void TurnRound(int x, int y)
4602 int direction = MovDir[x][y];
4606 GfxDir[x][y] = MovDir[x][y];
4608 if (direction != MovDir[x][y])
4612 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4615 static boolean JustBeingPushed(int x, int y)
4619 for (i = 0; i < MAX_PLAYERS; i++)
4621 struct PlayerInfo *player = &stored_player[i];
4623 if (player->active && player->is_pushing && player->MovPos)
4625 int next_jx = player->jx + (player->jx - player->last_jx);
4626 int next_jy = player->jy + (player->jy - player->last_jy);
4628 if (x == next_jx && y == next_jy)
4636 void StartMoving(int x, int y)
4638 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4639 int element = Feld[x][y];
4644 if (MovDelay[x][y] == 0)
4645 GfxAction[x][y] = ACTION_DEFAULT;
4647 if (CAN_FALL(element) && y < lev_fieldy - 1)
4649 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4650 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4651 if (JustBeingPushed(x, y))
4654 if (element == EL_QUICKSAND_FULL)
4656 if (IS_FREE(x, y + 1))
4658 InitMovingField(x, y, MV_DOWN);
4659 started_moving = TRUE;
4661 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4662 Store[x][y] = EL_ROCK;
4664 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4666 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4668 if (!MovDelay[x][y])
4669 MovDelay[x][y] = TILEY + 1;
4678 Feld[x][y] = EL_QUICKSAND_EMPTY;
4679 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4680 Store[x][y + 1] = Store[x][y];
4683 PlayLevelSoundAction(x, y, ACTION_FILLING);
4686 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4687 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4689 InitMovingField(x, y, MV_DOWN);
4690 started_moving = TRUE;
4692 Feld[x][y] = EL_QUICKSAND_FILLING;
4693 Store[x][y] = element;
4695 PlayLevelSoundAction(x, y, ACTION_FILLING);
4697 else if (element == EL_MAGIC_WALL_FULL)
4699 if (IS_FREE(x, y + 1))
4701 InitMovingField(x, y, MV_DOWN);
4702 started_moving = TRUE;
4704 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4705 Store[x][y] = EL_CHANGED(Store[x][y]);
4707 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4709 if (!MovDelay[x][y])
4710 MovDelay[x][y] = TILEY/4 + 1;
4719 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4720 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4721 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4725 else if (element == EL_BD_MAGIC_WALL_FULL)
4727 if (IS_FREE(x, y + 1))
4729 InitMovingField(x, y, MV_DOWN);
4730 started_moving = TRUE;
4732 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4733 Store[x][y] = EL_CHANGED2(Store[x][y]);
4735 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4737 if (!MovDelay[x][y])
4738 MovDelay[x][y] = TILEY/4 + 1;
4747 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4748 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4749 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4753 else if (CAN_PASS_MAGIC_WALL(element) &&
4754 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4755 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4757 InitMovingField(x, y, MV_DOWN);
4758 started_moving = TRUE;
4761 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4762 EL_BD_MAGIC_WALL_FILLING);
4763 Store[x][y] = element;
4765 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4767 SplashAcid(x, y + 1);
4769 InitMovingField(x, y, MV_DOWN);
4770 started_moving = TRUE;
4772 Store[x][y] = EL_ACID;
4774 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4775 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
4777 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4778 CAN_FALL(element) && WasJustFalling[x][y] &&
4779 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
4781 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4782 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4783 (Feld[x][y + 1] == EL_BLOCKED)))
4785 /* this is needed for a special case not covered by calling "Impact()"
4786 from "ContinueMoving()": if an element moves to a tile directly below
4787 another element which was just falling on that tile (which was empty
4788 in the previous frame), the falling element above would just stop
4789 instead of smashing the element below (in previous version, the above
4790 element was just checked for "moving" instead of "falling", resulting
4791 in incorrect smashes caused by horizontal movement of the above
4792 element; also, the case of the player being the element to smash was
4793 simply not covered here... :-/ ) */
4795 CheckCollision[x][y] = 0;
4799 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4801 if (MovDir[x][y] == MV_NONE)
4803 InitMovingField(x, y, MV_DOWN);
4804 started_moving = TRUE;
4807 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4809 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4810 MovDir[x][y] = MV_DOWN;
4812 InitMovingField(x, y, MV_DOWN);
4813 started_moving = TRUE;
4815 else if (element == EL_AMOEBA_DROP)
4817 Feld[x][y] = EL_AMOEBA_GROWING;
4818 Store[x][y] = EL_AMOEBA_WET;
4820 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4821 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4822 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4823 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4825 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4826 (IS_FREE(x - 1, y + 1) ||
4827 Feld[x - 1][y + 1] == EL_ACID));
4828 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4829 (IS_FREE(x + 1, y + 1) ||
4830 Feld[x + 1][y + 1] == EL_ACID));
4831 boolean can_fall_any = (can_fall_left || can_fall_right);
4832 boolean can_fall_both = (can_fall_left && can_fall_right);
4833 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4835 #if USE_NEW_ALL_SLIPPERY
4836 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
4838 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4839 can_fall_right = FALSE;
4840 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4841 can_fall_left = FALSE;
4842 else if (slippery_type == SLIPPERY_ONLY_LEFT)
4843 can_fall_right = FALSE;
4844 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4845 can_fall_left = FALSE;
4847 can_fall_any = (can_fall_left || can_fall_right);
4848 can_fall_both = FALSE;
4851 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4853 if (slippery_type == SLIPPERY_ONLY_LEFT)
4854 can_fall_right = FALSE;
4855 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4856 can_fall_left = FALSE;
4857 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4858 can_fall_right = FALSE;
4859 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4860 can_fall_left = FALSE;
4862 can_fall_any = (can_fall_left || can_fall_right);
4863 can_fall_both = (can_fall_left && can_fall_right);
4867 #if USE_NEW_ALL_SLIPPERY
4869 #if USE_NEW_SP_SLIPPERY
4870 /* !!! better use the same properties as for custom elements here !!! */
4871 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
4872 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
4874 can_fall_right = FALSE; /* slip down on left side */
4875 can_fall_both = FALSE;
4880 #if USE_NEW_ALL_SLIPPERY
4883 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
4884 can_fall_right = FALSE; /* slip down on left side */
4886 can_fall_left = !(can_fall_right = RND(2));
4888 can_fall_both = FALSE;
4893 if (game.emulation == EMU_BOULDERDASH ||
4894 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
4895 can_fall_right = FALSE; /* slip down on left side */
4897 can_fall_left = !(can_fall_right = RND(2));
4899 can_fall_both = FALSE;
4905 /* if not determined otherwise, prefer left side for slipping down */
4906 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4907 started_moving = TRUE;
4911 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4913 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4916 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4917 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4918 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4919 int belt_dir = game.belt_dir[belt_nr];
4921 if ((belt_dir == MV_LEFT && left_is_free) ||
4922 (belt_dir == MV_RIGHT && right_is_free))
4924 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4926 InitMovingField(x, y, belt_dir);
4927 started_moving = TRUE;
4929 Pushed[x][y] = TRUE;
4930 Pushed[nextx][y] = TRUE;
4932 GfxAction[x][y] = ACTION_DEFAULT;
4936 MovDir[x][y] = 0; /* if element was moving, stop it */
4941 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4943 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
4945 if (CAN_MOVE(element) && !started_moving)
4948 int move_pattern = element_info[element].move_pattern;
4953 if (MovDir[x][y] == MV_NONE)
4955 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
4956 x, y, element, element_info[element].token_name);
4957 printf("StartMoving(): This should never happen!\n");
4962 Moving2Blocked(x, y, &newx, &newy);
4964 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4967 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4968 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
4970 WasJustMoving[x][y] = 0;
4971 CheckCollision[x][y] = 0;
4973 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4975 if (Feld[x][y] != element) /* element has changed */
4979 if (!MovDelay[x][y]) /* start new movement phase */
4981 /* all objects that can change their move direction after each step
4982 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4984 if (element != EL_YAMYAM &&
4985 element != EL_DARK_YAMYAM &&
4986 element != EL_PACMAN &&
4987 !(move_pattern & MV_ANY_DIRECTION) &&
4988 move_pattern != MV_TURNING_LEFT &&
4989 move_pattern != MV_TURNING_RIGHT &&
4990 move_pattern != MV_TURNING_LEFT_RIGHT &&
4991 move_pattern != MV_TURNING_RIGHT_LEFT &&
4992 move_pattern != MV_TURNING_RANDOM)
4996 if (MovDelay[x][y] && (element == EL_BUG ||
4997 element == EL_SPACESHIP ||
4998 element == EL_SP_SNIKSNAK ||
4999 element == EL_SP_ELECTRON ||
5000 element == EL_MOLE))
5001 DrawLevelField(x, y);
5005 if (MovDelay[x][y]) /* wait some time before next movement */
5009 if (element == EL_ROBOT ||
5010 element == EL_YAMYAM ||
5011 element == EL_DARK_YAMYAM)
5013 DrawLevelElementAnimationIfNeeded(x, y, element);
5014 PlayLevelSoundAction(x, y, ACTION_WAITING);
5016 else if (element == EL_SP_ELECTRON)
5017 DrawLevelElementAnimationIfNeeded(x, y, element);
5018 else if (element == EL_DRAGON)
5021 int dir = MovDir[x][y];
5022 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5023 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5024 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5025 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5026 dir == MV_UP ? IMG_FLAMES_1_UP :
5027 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5028 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5030 GfxAction[x][y] = ACTION_ATTACKING;
5032 if (IS_PLAYER(x, y))
5033 DrawPlayerField(x, y);
5035 DrawLevelField(x, y);
5037 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5039 for (i = 1; i <= 3; i++)
5041 int xx = x + i * dx;
5042 int yy = y + i * dy;
5043 int sx = SCREENX(xx);
5044 int sy = SCREENY(yy);
5045 int flame_graphic = graphic + (i - 1);
5047 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5052 int flamed = MovingOrBlocked2Element(xx, yy);
5056 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5058 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5059 RemoveMovingField(xx, yy);
5061 RemoveField(xx, yy);
5063 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5066 RemoveMovingField(xx, yy);
5069 ChangeDelay[xx][yy] = 0;
5071 Feld[xx][yy] = EL_FLAMES;
5073 if (IN_SCR_FIELD(sx, sy))
5075 DrawLevelFieldCrumbledSand(xx, yy);
5076 DrawGraphic(sx, sy, flame_graphic, frame);
5081 if (Feld[xx][yy] == EL_FLAMES)
5082 Feld[xx][yy] = EL_EMPTY;
5083 DrawLevelField(xx, yy);
5088 if (MovDelay[x][y]) /* element still has to wait some time */
5090 PlayLevelSoundAction(x, y, ACTION_WAITING);
5096 /* now make next step */
5098 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5100 if (DONT_COLLIDE_WITH(element) &&
5101 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5102 !PLAYER_ENEMY_PROTECTED(newx, newy))
5104 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5109 else if (CAN_MOVE_INTO_ACID(element) &&
5110 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5111 (MovDir[x][y] == MV_DOWN ||
5112 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5114 SplashAcid(newx, newy);
5115 Store[x][y] = EL_ACID;
5117 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5119 if (Feld[newx][newy] == EL_EXIT_OPEN)
5122 DrawLevelField(x, y);
5124 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5125 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5126 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5128 local_player->friends_still_needed--;
5129 if (!local_player->friends_still_needed &&
5130 !local_player->GameOver && AllPlayersGone)
5131 local_player->LevelSolved = local_player->GameOver = TRUE;
5135 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5137 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5138 DrawLevelField(newx, newy);
5140 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5142 else if (!IS_FREE(newx, newy))
5144 GfxAction[x][y] = ACTION_WAITING;
5146 if (IS_PLAYER(x, y))
5147 DrawPlayerField(x, y);
5149 DrawLevelField(x, y);
5154 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5156 if (IS_FOOD_PIG(Feld[newx][newy]))
5158 if (IS_MOVING(newx, newy))
5159 RemoveMovingField(newx, newy);
5162 Feld[newx][newy] = EL_EMPTY;
5163 DrawLevelField(newx, newy);
5166 PlayLevelSound(x, y, SND_PIG_DIGGING);
5168 else if (!IS_FREE(newx, newy))
5170 if (IS_PLAYER(x, y))
5171 DrawPlayerField(x, y);
5173 DrawLevelField(x, y);
5178 else if (IS_CUSTOM_ELEMENT(element) &&
5179 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5181 int new_element = Feld[newx][newy];
5183 if (!IS_FREE(newx, newy))
5185 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5186 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5189 /* no element can dig solid indestructible elements */
5190 if (IS_INDESTRUCTIBLE(new_element) &&
5191 !IS_DIGGABLE(new_element) &&
5192 !IS_COLLECTIBLE(new_element))
5195 if (AmoebaNr[newx][newy] &&
5196 (new_element == EL_AMOEBA_FULL ||
5197 new_element == EL_BD_AMOEBA ||
5198 new_element == EL_AMOEBA_GROWING))
5200 AmoebaCnt[AmoebaNr[newx][newy]]--;
5201 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5204 if (IS_MOVING(newx, newy))
5205 RemoveMovingField(newx, newy);
5208 RemoveField(newx, newy);
5209 DrawLevelField(newx, newy);
5212 /* if digged element was about to explode, prevent the explosion */
5213 ExplodeField[newx][newy] = EX_TYPE_NONE;
5215 PlayLevelSoundAction(x, y, action);
5218 Store[newx][newy] = EL_EMPTY;
5219 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5221 int move_leave_element = element_info[element].move_leave_element;
5223 /* this makes it possible to leave the removed element again */
5224 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
5225 new_element : move_leave_element);
5228 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5230 RunnerVisit[x][y] = FrameCounter;
5231 PlayerVisit[x][y] /= 8; /* expire player visit path */
5234 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5236 if (!IS_FREE(newx, newy))
5238 if (IS_PLAYER(x, y))
5239 DrawPlayerField(x, y);
5241 DrawLevelField(x, y);
5247 boolean wanna_flame = !RND(10);
5248 int dx = newx - x, dy = newy - y;
5249 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5250 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5251 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5252 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5253 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5254 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5257 IS_CLASSIC_ENEMY(element1) ||
5258 IS_CLASSIC_ENEMY(element2)) &&
5259 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5260 element1 != EL_FLAMES && element2 != EL_FLAMES)
5262 ResetGfxAnimation(x, y);
5263 GfxAction[x][y] = ACTION_ATTACKING;
5265 if (IS_PLAYER(x, y))
5266 DrawPlayerField(x, y);
5268 DrawLevelField(x, y);
5270 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5272 MovDelay[x][y] = 50;
5276 RemoveField(newx, newy);
5278 Feld[newx][newy] = EL_FLAMES;
5279 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5282 RemoveField(newx1, newy1);
5284 Feld[newx1][newy1] = EL_FLAMES;
5286 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5289 RemoveField(newx2, newy2);
5291 Feld[newx2][newy2] = EL_FLAMES;
5298 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5299 Feld[newx][newy] == EL_DIAMOND)
5301 if (IS_MOVING(newx, newy))
5302 RemoveMovingField(newx, newy);
5305 Feld[newx][newy] = EL_EMPTY;
5306 DrawLevelField(newx, newy);
5309 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5311 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5312 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5314 if (AmoebaNr[newx][newy])
5316 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5317 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5318 Feld[newx][newy] == EL_BD_AMOEBA)
5319 AmoebaCnt[AmoebaNr[newx][newy]]--;
5324 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5326 RemoveMovingField(newx, newy);
5329 if (IS_MOVING(newx, newy))
5331 RemoveMovingField(newx, newy);
5336 Feld[newx][newy] = EL_EMPTY;
5337 DrawLevelField(newx, newy);
5340 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5342 else if ((element == EL_PACMAN || element == EL_MOLE)
5343 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5345 if (AmoebaNr[newx][newy])
5347 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5348 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5349 Feld[newx][newy] == EL_BD_AMOEBA)
5350 AmoebaCnt[AmoebaNr[newx][newy]]--;
5353 if (element == EL_MOLE)
5355 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5356 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5358 ResetGfxAnimation(x, y);
5359 GfxAction[x][y] = ACTION_DIGGING;
5360 DrawLevelField(x, y);
5362 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5364 return; /* wait for shrinking amoeba */
5366 else /* element == EL_PACMAN */
5368 Feld[newx][newy] = EL_EMPTY;
5369 DrawLevelField(newx, newy);
5370 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5373 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5374 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5375 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5377 /* wait for shrinking amoeba to completely disappear */
5380 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5382 /* object was running against a wall */
5387 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
5388 if (move_pattern & MV_ANY_DIRECTION &&
5389 move_pattern == MovDir[x][y])
5391 int blocking_element =
5392 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5394 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5397 element = Feld[x][y]; /* element might have changed */
5401 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5402 DrawLevelElementAnimation(x, y, element);
5404 if (DONT_TOUCH(element))
5405 TestIfBadThingTouchesPlayer(x, y);
5410 InitMovingField(x, y, MovDir[x][y]);
5412 PlayLevelSoundAction(x, y, ACTION_MOVING);
5416 ContinueMoving(x, y);
5419 void ContinueMoving(int x, int y)
5421 int element = Feld[x][y];
5422 int stored = Store[x][y];
5423 struct ElementInfo *ei = &element_info[element];
5424 int direction = MovDir[x][y];
5425 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5426 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5427 int newx = x + dx, newy = y + dy;
5428 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5429 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5430 boolean last_line = (newy == lev_fieldy - 1);
5432 MovPos[x][y] += getElementMoveStepsize(x, y);
5434 if (pushed_by_player) /* special case: moving object pushed by player */
5435 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5437 if (ABS(MovPos[x][y]) < TILEX)
5439 DrawLevelField(x, y);
5441 return; /* element is still moving */
5444 /* element reached destination field */
5446 Feld[x][y] = EL_EMPTY;
5447 Feld[newx][newy] = element;
5448 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5450 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
5452 element = Feld[newx][newy] = EL_ACID;
5454 else if (element == EL_MOLE)
5456 Feld[x][y] = EL_SAND;
5458 DrawLevelFieldCrumbledSandNeighbours(x, y);
5460 else if (element == EL_QUICKSAND_FILLING)
5462 element = Feld[newx][newy] = get_next_element(element);
5463 Store[newx][newy] = Store[x][y];
5465 else if (element == EL_QUICKSAND_EMPTYING)
5467 Feld[x][y] = get_next_element(element);
5468 element = Feld[newx][newy] = Store[x][y];
5470 else if (element == EL_MAGIC_WALL_FILLING)
5472 element = Feld[newx][newy] = get_next_element(element);
5473 if (!game.magic_wall_active)
5474 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5475 Store[newx][newy] = Store[x][y];
5477 else if (element == EL_MAGIC_WALL_EMPTYING)
5479 Feld[x][y] = get_next_element(element);
5480 if (!game.magic_wall_active)
5481 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5482 element = Feld[newx][newy] = Store[x][y];
5484 #if USE_NEW_CUSTOM_VALUE
5485 InitField(newx, newy, FALSE);
5488 else if (element == EL_BD_MAGIC_WALL_FILLING)
5490 element = Feld[newx][newy] = get_next_element(element);
5491 if (!game.magic_wall_active)
5492 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5493 Store[newx][newy] = Store[x][y];
5495 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5497 Feld[x][y] = get_next_element(element);
5498 if (!game.magic_wall_active)
5499 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5500 element = Feld[newx][newy] = Store[x][y];
5502 #if USE_NEW_CUSTOM_VALUE
5503 InitField(newx, newy, FALSE);
5506 else if (element == EL_AMOEBA_DROPPING)
5508 Feld[x][y] = get_next_element(element);
5509 element = Feld[newx][newy] = Store[x][y];
5511 else if (element == EL_SOKOBAN_OBJECT)
5514 Feld[x][y] = Back[x][y];
5516 if (Back[newx][newy])
5517 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5519 Back[x][y] = Back[newx][newy] = 0;
5522 Store[x][y] = EL_EMPTY;
5527 MovDelay[newx][newy] = 0;
5529 if (CAN_CHANGE(element))
5531 /* copy element change control values to new field */
5532 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5533 ChangePage[newx][newy] = ChangePage[x][y];
5534 Changed[newx][newy] = Changed[x][y];
5535 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5537 #if USE_NEW_CUSTOM_VALUE
5538 CustomValue[newx][newy] = CustomValue[x][y];
5542 ChangeDelay[x][y] = 0;
5543 ChangePage[x][y] = -1;
5544 Changed[x][y] = FALSE;
5545 ChangeEvent[x][y] = -1;
5547 #if USE_NEW_CUSTOM_VALUE
5548 CustomValue[x][y] = 0;
5551 /* copy animation control values to new field */
5552 GfxFrame[newx][newy] = GfxFrame[x][y];
5553 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5554 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5555 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5557 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5559 /* some elements can leave other elements behind after moving */
5560 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
5561 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
5562 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
5564 int move_leave_element = ei->move_leave_element;
5566 /* this makes it possible to leave the removed element again */
5567 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
5568 ei->move_leave_element == EL_TRIGGER_ELEMENT)
5569 move_leave_element = stored;
5571 Feld[x][y] = move_leave_element;
5573 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
5574 MovDir[x][y] = direction;
5576 InitField(x, y, FALSE);
5578 if (GFX_CRUMBLED(Feld[x][y]))
5579 DrawLevelFieldCrumbledSandNeighbours(x, y);
5581 if (ELEM_IS_PLAYER(move_leave_element))
5582 RelocatePlayer(x, y, move_leave_element);
5585 /* do this after checking for left-behind element */
5586 ResetGfxAnimation(x, y); /* reset animation values for old field */
5588 if (!CAN_MOVE(element) ||
5589 (CAN_FALL(element) && direction == MV_DOWN &&
5590 (element == EL_SPRING ||
5591 element_info[element].move_pattern == MV_WHEN_PUSHED ||
5592 element_info[element].move_pattern == MV_WHEN_DROPPED)))
5593 GfxDir[x][y] = MovDir[newx][newy] = 0;
5595 DrawLevelField(x, y);
5596 DrawLevelField(newx, newy);
5598 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5600 /* prevent pushed element from moving on in pushed direction */
5601 if (pushed_by_player && CAN_MOVE(element) &&
5602 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5603 !(element_info[element].move_pattern & direction))
5604 TurnRound(newx, newy);
5606 /* prevent elements on conveyor belt from moving on in last direction */
5607 if (pushed_by_conveyor && CAN_FALL(element) &&
5608 direction & MV_HORIZONTAL)
5609 MovDir[newx][newy] = 0;
5611 if (!pushed_by_player)
5613 int nextx = newx + dx, nexty = newy + dy;
5614 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
5616 WasJustMoving[newx][newy] = 3;
5618 if (CAN_FALL(element) && direction == MV_DOWN)
5619 WasJustFalling[newx][newy] = 3;
5621 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
5622 CheckCollision[newx][newy] = 2;
5625 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5627 TestIfBadThingTouchesPlayer(newx, newy);
5628 TestIfBadThingTouchesFriend(newx, newy);
5630 if (!IS_CUSTOM_ELEMENT(element))
5631 TestIfBadThingTouchesOtherBadThing(newx, newy);
5633 else if (element == EL_PENGUIN)
5634 TestIfFriendTouchesBadThing(newx, newy);
5636 /* give the player one last chance (one more frame) to move away */
5637 if (CAN_FALL(element) && direction == MV_DOWN &&
5638 (last_line || (!IS_FREE(x, newy + 1) &&
5639 (!IS_PLAYER(x, newy + 1) ||
5640 game.engine_version < VERSION_IDENT(3,1,1,0)))))
5643 if (pushed_by_player && !game.use_change_when_pushing_bug)
5645 int dig_side = MV_DIR_OPPOSITE(direction);
5646 struct PlayerInfo *player = PLAYERINFO(x, y);
5648 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
5649 player->index_bit, dig_side);
5650 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
5651 player->index_bit, dig_side);
5654 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5656 TestIfElementHitsCustomElement(newx, newy, direction);
5657 TestIfPlayerTouchesCustomElement(newx, newy);
5658 TestIfElementTouchesCustomElement(newx, newy);
5661 int AmoebeNachbarNr(int ax, int ay)
5664 int element = Feld[ax][ay];
5666 static int xy[4][2] =
5674 for (i = 0; i < NUM_DIRECTIONS; i++)
5676 int x = ax + xy[i][0];
5677 int y = ay + xy[i][1];
5679 if (!IN_LEV_FIELD(x, y))
5682 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5683 group_nr = AmoebaNr[x][y];
5689 void AmoebenVereinigen(int ax, int ay)
5691 int i, x, y, xx, yy;
5692 int new_group_nr = AmoebaNr[ax][ay];
5693 static int xy[4][2] =
5701 if (new_group_nr == 0)
5704 for (i = 0; i < NUM_DIRECTIONS; i++)
5709 if (!IN_LEV_FIELD(x, y))
5712 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5713 Feld[x][y] == EL_BD_AMOEBA ||
5714 Feld[x][y] == EL_AMOEBA_DEAD) &&
5715 AmoebaNr[x][y] != new_group_nr)
5717 int old_group_nr = AmoebaNr[x][y];
5719 if (old_group_nr == 0)
5722 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5723 AmoebaCnt[old_group_nr] = 0;
5724 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5725 AmoebaCnt2[old_group_nr] = 0;
5727 for (yy = 0; yy < lev_fieldy; yy++)
5729 for (xx = 0; xx < lev_fieldx; xx++)
5731 if (AmoebaNr[xx][yy] == old_group_nr)
5732 AmoebaNr[xx][yy] = new_group_nr;
5739 void AmoebeUmwandeln(int ax, int ay)
5743 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5745 int group_nr = AmoebaNr[ax][ay];
5750 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5751 printf("AmoebeUmwandeln(): This should never happen!\n");
5756 for (y = 0; y < lev_fieldy; y++)
5758 for (x = 0; x < lev_fieldx; x++)
5760 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5763 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5767 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5768 SND_AMOEBA_TURNING_TO_GEM :
5769 SND_AMOEBA_TURNING_TO_ROCK));
5774 static int xy[4][2] =
5782 for (i = 0; i < NUM_DIRECTIONS; i++)
5787 if (!IN_LEV_FIELD(x, y))
5790 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5792 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5793 SND_AMOEBA_TURNING_TO_GEM :
5794 SND_AMOEBA_TURNING_TO_ROCK));
5801 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5804 int group_nr = AmoebaNr[ax][ay];
5805 boolean done = FALSE;
5810 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5811 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5816 for (y = 0; y < lev_fieldy; y++)
5818 for (x = 0; x < lev_fieldx; x++)
5820 if (AmoebaNr[x][y] == group_nr &&
5821 (Feld[x][y] == EL_AMOEBA_DEAD ||
5822 Feld[x][y] == EL_BD_AMOEBA ||
5823 Feld[x][y] == EL_AMOEBA_GROWING))
5826 Feld[x][y] = new_element;
5827 InitField(x, y, FALSE);
5828 DrawLevelField(x, y);
5835 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5836 SND_BD_AMOEBA_TURNING_TO_ROCK :
5837 SND_BD_AMOEBA_TURNING_TO_GEM));
5840 void AmoebeWaechst(int x, int y)
5842 static unsigned long sound_delay = 0;
5843 static unsigned long sound_delay_value = 0;
5845 if (!MovDelay[x][y]) /* start new growing cycle */
5849 if (DelayReached(&sound_delay, sound_delay_value))
5851 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5852 sound_delay_value = 30;
5856 if (MovDelay[x][y]) /* wait some time before growing bigger */
5859 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5861 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5862 6 - MovDelay[x][y]);
5864 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5867 if (!MovDelay[x][y])
5869 Feld[x][y] = Store[x][y];
5871 DrawLevelField(x, y);
5876 void AmoebaDisappearing(int x, int y)
5878 static unsigned long sound_delay = 0;
5879 static unsigned long sound_delay_value = 0;
5881 if (!MovDelay[x][y]) /* start new shrinking cycle */
5885 if (DelayReached(&sound_delay, sound_delay_value))
5886 sound_delay_value = 30;
5889 if (MovDelay[x][y]) /* wait some time before shrinking */
5892 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5894 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5895 6 - MovDelay[x][y]);
5897 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5900 if (!MovDelay[x][y])
5902 Feld[x][y] = EL_EMPTY;
5903 DrawLevelField(x, y);
5905 /* don't let mole enter this field in this cycle;
5906 (give priority to objects falling to this field from above) */
5912 void AmoebeAbleger(int ax, int ay)
5915 int element = Feld[ax][ay];
5916 int graphic = el2img(element);
5917 int newax = ax, neway = ay;
5918 static int xy[4][2] =
5926 if (!level.amoeba_speed)
5928 Feld[ax][ay] = EL_AMOEBA_DEAD;
5929 DrawLevelField(ax, ay);
5933 if (IS_ANIMATED(graphic))
5934 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5936 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5937 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5939 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5942 if (MovDelay[ax][ay])
5946 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5949 int x = ax + xy[start][0];
5950 int y = ay + xy[start][1];
5952 if (!IN_LEV_FIELD(x, y))
5955 if (IS_FREE(x, y) ||
5956 CAN_GROW_INTO(Feld[x][y]) ||
5957 Feld[x][y] == EL_QUICKSAND_EMPTY)
5963 if (newax == ax && neway == ay)
5966 else /* normal or "filled" (BD style) amoeba */
5969 boolean waiting_for_player = FALSE;
5971 for (i = 0; i < NUM_DIRECTIONS; i++)
5973 int j = (start + i) % 4;
5974 int x = ax + xy[j][0];
5975 int y = ay + xy[j][1];
5977 if (!IN_LEV_FIELD(x, y))
5980 if (IS_FREE(x, y) ||
5981 CAN_GROW_INTO(Feld[x][y]) ||
5982 Feld[x][y] == EL_QUICKSAND_EMPTY)
5988 else if (IS_PLAYER(x, y))
5989 waiting_for_player = TRUE;
5992 if (newax == ax && neway == ay) /* amoeba cannot grow */
5994 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
5996 Feld[ax][ay] = EL_AMOEBA_DEAD;
5997 DrawLevelField(ax, ay);
5998 AmoebaCnt[AmoebaNr[ax][ay]]--;
6000 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6002 if (element == EL_AMOEBA_FULL)
6003 AmoebeUmwandeln(ax, ay);
6004 else if (element == EL_BD_AMOEBA)
6005 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6010 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6012 /* amoeba gets larger by growing in some direction */
6014 int new_group_nr = AmoebaNr[ax][ay];
6017 if (new_group_nr == 0)
6019 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6020 printf("AmoebeAbleger(): This should never happen!\n");
6025 AmoebaNr[newax][neway] = new_group_nr;
6026 AmoebaCnt[new_group_nr]++;
6027 AmoebaCnt2[new_group_nr]++;
6029 /* if amoeba touches other amoeba(s) after growing, unify them */
6030 AmoebenVereinigen(newax, neway);
6032 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6034 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6040 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6041 (neway == lev_fieldy - 1 && newax != ax))
6043 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6044 Store[newax][neway] = element;
6046 else if (neway == ay)
6048 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6050 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6054 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6055 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6056 Store[ax][ay] = EL_AMOEBA_DROP;
6057 ContinueMoving(ax, ay);
6061 DrawLevelField(newax, neway);
6064 void Life(int ax, int ay)
6068 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6071 int element = Feld[ax][ay];
6072 int graphic = el2img(element);
6073 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6075 boolean changed = FALSE;
6077 if (IS_ANIMATED(graphic))
6078 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6083 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6084 MovDelay[ax][ay] = life_time;
6086 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6089 if (MovDelay[ax][ay])
6093 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6095 int xx = ax+x1, yy = ay+y1;
6098 if (!IN_LEV_FIELD(xx, yy))
6101 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6103 int x = xx+x2, y = yy+y2;
6105 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6108 if (((Feld[x][y] == element ||
6109 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6111 (IS_FREE(x, y) && Stop[x][y]))
6115 if (xx == ax && yy == ay) /* field in the middle */
6117 if (nachbarn < life_parameter[0] ||
6118 nachbarn > life_parameter[1])
6120 Feld[xx][yy] = EL_EMPTY;
6122 DrawLevelField(xx, yy);
6123 Stop[xx][yy] = TRUE;
6127 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6128 { /* free border field */
6129 if (nachbarn >= life_parameter[2] &&
6130 nachbarn <= life_parameter[3])
6132 Feld[xx][yy] = element;
6133 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6135 DrawLevelField(xx, yy);
6136 Stop[xx][yy] = TRUE;
6143 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6144 SND_GAME_OF_LIFE_GROWING);
6147 static void InitRobotWheel(int x, int y)
6149 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6152 static void RunRobotWheel(int x, int y)
6154 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6157 static void StopRobotWheel(int x, int y)
6159 if (ZX == x && ZY == y)
6163 static void InitTimegateWheel(int x, int y)
6165 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6168 static void RunTimegateWheel(int x, int y)
6170 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6173 void CheckExit(int x, int y)
6175 if (local_player->gems_still_needed > 0 ||
6176 local_player->sokobanfields_still_needed > 0 ||
6177 local_player->lights_still_needed > 0)
6179 int element = Feld[x][y];
6180 int graphic = el2img(element);
6182 if (IS_ANIMATED(graphic))
6183 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6188 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6191 Feld[x][y] = EL_EXIT_OPENING;
6193 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6196 void CheckExitSP(int x, int y)
6198 if (local_player->gems_still_needed > 0)
6200 int element = Feld[x][y];
6201 int graphic = el2img(element);
6203 if (IS_ANIMATED(graphic))
6204 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6209 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6212 Feld[x][y] = EL_SP_EXIT_OPENING;
6214 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6217 static void CloseAllOpenTimegates()
6221 for (y = 0; y < lev_fieldy; y++)
6223 for (x = 0; x < lev_fieldx; x++)
6225 int element = Feld[x][y];
6227 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6229 Feld[x][y] = EL_TIMEGATE_CLOSING;
6231 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6237 void EdelsteinFunkeln(int x, int y)
6239 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6242 if (Feld[x][y] == EL_BD_DIAMOND)
6245 if (MovDelay[x][y] == 0) /* next animation frame */
6246 MovDelay[x][y] = 11 * !SimpleRND(500);
6248 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6252 if (setup.direct_draw && MovDelay[x][y])
6253 SetDrawtoField(DRAW_BUFFERED);
6255 DrawLevelElementAnimation(x, y, Feld[x][y]);
6257 if (MovDelay[x][y] != 0)
6259 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6260 10 - MovDelay[x][y]);
6262 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6264 if (setup.direct_draw)
6268 dest_x = FX + SCREENX(x) * TILEX;
6269 dest_y = FY + SCREENY(y) * TILEY;
6271 BlitBitmap(drawto_field, window,
6272 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6273 SetDrawtoField(DRAW_DIRECT);
6279 void MauerWaechst(int x, int y)
6283 if (!MovDelay[x][y]) /* next animation frame */
6284 MovDelay[x][y] = 3 * delay;
6286 if (MovDelay[x][y]) /* wait some time before next frame */
6290 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6292 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6293 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6295 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6298 if (!MovDelay[x][y])
6300 if (MovDir[x][y] == MV_LEFT)
6302 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6303 DrawLevelField(x - 1, y);
6305 else if (MovDir[x][y] == MV_RIGHT)
6307 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6308 DrawLevelField(x + 1, y);
6310 else if (MovDir[x][y] == MV_UP)
6312 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6313 DrawLevelField(x, y - 1);
6317 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6318 DrawLevelField(x, y + 1);
6321 Feld[x][y] = Store[x][y];
6323 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6324 DrawLevelField(x, y);
6329 void MauerAbleger(int ax, int ay)
6331 int element = Feld[ax][ay];
6332 int graphic = el2img(element);
6333 boolean oben_frei = FALSE, unten_frei = FALSE;
6334 boolean links_frei = FALSE, rechts_frei = FALSE;
6335 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6336 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6337 boolean new_wall = FALSE;
6339 if (IS_ANIMATED(graphic))
6340 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6342 if (!MovDelay[ax][ay]) /* start building new wall */
6343 MovDelay[ax][ay] = 6;
6345 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6348 if (MovDelay[ax][ay])
6352 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6354 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6356 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6358 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6361 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6362 element == EL_EXPANDABLE_WALL_ANY)
6366 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6367 Store[ax][ay-1] = element;
6368 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6369 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6370 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6371 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6376 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6377 Store[ax][ay+1] = element;
6378 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6379 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6380 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6381 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6386 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6387 element == EL_EXPANDABLE_WALL_ANY ||
6388 element == EL_EXPANDABLE_WALL)
6392 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6393 Store[ax-1][ay] = element;
6394 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6395 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6396 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6397 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6403 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6404 Store[ax+1][ay] = element;
6405 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6406 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6407 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6408 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6413 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6414 DrawLevelField(ax, ay);
6416 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6418 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6419 unten_massiv = TRUE;
6420 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6421 links_massiv = TRUE;
6422 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6423 rechts_massiv = TRUE;
6425 if (((oben_massiv && unten_massiv) ||
6426 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6427 element == EL_EXPANDABLE_WALL) &&
6428 ((links_massiv && rechts_massiv) ||
6429 element == EL_EXPANDABLE_WALL_VERTICAL))
6430 Feld[ax][ay] = EL_WALL;
6433 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6436 void CheckForDragon(int x, int y)
6439 boolean dragon_found = FALSE;
6440 static int xy[4][2] =
6448 for (i = 0; i < NUM_DIRECTIONS; i++)
6450 for (j = 0; j < 4; j++)
6452 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6454 if (IN_LEV_FIELD(xx, yy) &&
6455 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6457 if (Feld[xx][yy] == EL_DRAGON)
6458 dragon_found = TRUE;
6467 for (i = 0; i < NUM_DIRECTIONS; i++)
6469 for (j = 0; j < 3; j++)
6471 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6473 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6475 Feld[xx][yy] = EL_EMPTY;
6476 DrawLevelField(xx, yy);
6485 static void InitBuggyBase(int x, int y)
6487 int element = Feld[x][y];
6488 int activating_delay = FRAMES_PER_SECOND / 4;
6491 (element == EL_SP_BUGGY_BASE ?
6492 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6493 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6495 element == EL_SP_BUGGY_BASE_ACTIVE ?
6496 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6499 static void WarnBuggyBase(int x, int y)
6502 static int xy[4][2] =
6510 for (i = 0; i < NUM_DIRECTIONS; i++)
6512 int xx = x + xy[i][0], yy = y + xy[i][1];
6514 if (IS_PLAYER(xx, yy))
6516 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6523 static void InitTrap(int x, int y)
6525 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6528 static void ActivateTrap(int x, int y)
6530 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6533 static void ChangeActiveTrap(int x, int y)
6535 int graphic = IMG_TRAP_ACTIVE;
6537 /* if new animation frame was drawn, correct crumbled sand border */
6538 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6539 DrawLevelFieldCrumbledSand(x, y);
6542 static int getSpecialActionElement(int element, int number, int base_element)
6544 return (element != EL_EMPTY ? element :
6545 number != -1 ? base_element + number - 1 :
6549 static int getModifiedActionNumber(int value_old, int operator, int operand,
6550 int value_min, int value_max)
6552 int value_new = (operator == CA_MODE_SET ? operand :
6553 operator == CA_MODE_ADD ? value_old + operand :
6554 operator == CA_MODE_SUBTRACT ? value_old - operand :
6555 operator == CA_MODE_MULTIPLY ? value_old * operand :
6556 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
6557 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
6560 return (value_new < value_min ? value_min :
6561 value_new > value_max ? value_max :
6565 static void ExecuteCustomElementAction(int x, int y, int element, int page)
6567 struct ElementInfo *ei = &element_info[element];
6568 struct ElementChangeInfo *change = &ei->change_page[page];
6569 int action_type = change->action_type;
6570 int action_mode = change->action_mode;
6571 int action_arg = change->action_arg;
6574 if (!change->has_action)
6577 /* ---------- determine action paramater values ---------- */
6579 int action_arg_element =
6580 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
6581 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
6582 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
6585 int action_arg_number_min =
6586 (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MIN :
6589 int action_arg_number_max =
6590 (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MAX :
6591 action_type == CA_SET_LEVEL_GEMS ? 999 :
6592 action_type == CA_SET_LEVEL_TIME ? 9999 :
6593 action_type == CA_SET_LEVEL_SCORE ? 99999 :
6594 action_type == CA_SET_CE_SCORE ? 9999 :
6595 action_type == CA_SET_CE_VALUE ? 9999 :
6598 int action_arg_number_reset =
6599 (action_type == CA_SET_PLAYER_SPEED ? TILEX/game.initial_move_delay_value :
6600 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
6601 action_type == CA_SET_LEVEL_TIME ? level.time :
6602 action_type == CA_SET_LEVEL_SCORE ? 0 :
6603 action_type == CA_SET_CE_SCORE ? 0 :
6605 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
6607 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
6611 int action_arg_number =
6612 (action_arg <= CA_ARG_MAX ? action_arg :
6613 action_arg >= CA_ARG_SPEED_VERY_SLOW &&
6614 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
6615 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
6616 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
6617 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
6618 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
6619 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
6620 #if USE_NEW_CUSTOM_VALUE
6621 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
6623 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
6625 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) :
6626 action_arg == CA_ARG_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
6627 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value :
6630 int action_arg_number_old =
6631 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
6632 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
6633 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
6634 action_type == CA_SET_CE_SCORE ? ei->collect_score :
6635 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
6638 int action_arg_number_new =
6639 getModifiedActionNumber(action_arg_number_old,
6640 action_mode, action_arg_number,
6641 action_arg_number_min, action_arg_number_max);
6643 int action_arg_player_bits =
6644 (action_arg == CA_ARG_PLAYER_ANY ? PLAYER_BITS_ANY :
6645 action_arg >= CA_ARG_PLAYER_1 &&
6646 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
6647 action_arg >= CA_ARG_1 &&
6648 action_arg <= CA_ARG_PLAYER_4 ? (1 << (action_arg - CA_ARG_1)) :
6649 action_arg_element >= EL_PLAYER_1 &&
6650 action_arg_element <= EL_PLAYER_4 ?
6651 (1 << (action_arg_element - EL_PLAYER_1)) :
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 /* ---------- execute action ---------- */
6669 case CA_EXIT_PLAYER:
6671 for (i = 0; i < MAX_PLAYERS; i++)
6672 if (action_arg_player_bits & (1 << i))
6673 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
6678 case CA_KILL_PLAYER:
6680 for (i = 0; i < MAX_PLAYERS; i++)
6681 if (action_arg_player_bits & (1 << i))
6682 KillPlayer(&stored_player[i]);
6687 case CA_RESTART_LEVEL:
6689 game.restart_level = TRUE;
6694 case CA_SHOW_ENVELOPE:
6696 int element = getSpecialActionElement(action_arg_element,
6697 action_arg_number, EL_ENVELOPE_1);
6699 if (IS_ENVELOPE(element))
6700 local_player->show_envelope = element;
6707 int element = getSpecialActionElement(action_arg_element,
6708 action_arg_number, EL_KEY_1);
6710 if (IS_KEY(element))
6712 for (i = 0; i < MAX_PLAYERS; i++)
6714 if (trigger_player_bits & (1 << i))
6716 stored_player[i].key[KEY_NR(element)] = TRUE;
6718 DrawGameValue_Keys(stored_player[i].key);
6720 redraw_mask |= REDRAW_DOOR_1;
6730 int element = getSpecialActionElement(action_arg_element,
6731 action_arg_number, EL_KEY_1);
6733 if (IS_KEY(element))
6735 for (i = 0; i < MAX_PLAYERS; i++)
6737 if (trigger_player_bits & (1 << i))
6739 stored_player[i].key[KEY_NR(element)] = FALSE;
6741 DrawGameValue_Keys(stored_player[i].key);
6743 redraw_mask |= REDRAW_DOOR_1;
6752 case CA_SET_PLAYER_SPEED:
6754 for (i = 0; i < MAX_PLAYERS; i++)
6756 if (trigger_player_bits & (1 << i))
6758 int move_stepsize = TILEX / stored_player[i].move_delay_value;
6760 if (action_arg == CA_ARG_SPEED_SLOWER ||
6761 action_arg == CA_ARG_SPEED_FASTER)
6763 action_arg_number = 2;
6764 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
6769 getModifiedActionNumber(move_stepsize,
6772 action_arg_number_min,
6773 action_arg_number_max);
6775 /* make sure that value is power of 2 */
6776 move_stepsize = (1 << log_2(move_stepsize));
6778 /* do no immediately change -- the player might just be moving */
6779 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
6782 printf("::: move_delay_value == %d [%d]\n",
6783 stored_player[i].move_delay_value_next, action_arg_number);
6791 case CA_SET_PLAYER_SPEED:
6793 for (i = 0; i < MAX_PLAYERS; i++)
6795 if (trigger_player_bits & (1 << i))
6797 int move_stepsize = TILEX / stored_player[i].move_delay_value;
6799 if (action_mode == CA_MODE_ADD || action_mode == CA_MODE_SUBTRACT)
6801 /* translate "+" and "-" to "*" and "/" with powers of two */
6802 action_arg_number = 1 << action_arg_number;
6803 action_mode = (action_mode == CA_MODE_ADD ? CA_MODE_MULTIPLY :
6808 getModifiedActionNumber(move_stepsize,
6811 action_arg_number_min,
6812 action_arg_number_max);
6814 /* make sure that value is power of 2 */
6815 move_stepsize = (1 << log_2(move_stepsize));
6817 /* do no immediately change -- the player might just be moving */
6818 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
6821 printf("::: move_delay_value == %d [%d]\n",
6822 stored_player[i].move_delay_value_next, action_arg_number);
6831 case CA_SET_PLAYER_GRAVITY:
6833 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
6834 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
6835 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
6840 case CA_SET_WIND_DIRECTION:
6842 game.wind_direction = (action_arg >= CA_ARG_DIRECTION_NONE &&
6843 action_arg <= CA_ARG_DIRECTION_DOWN ?
6844 action_arg - CA_ARG_DIRECTION :
6845 action_arg == CA_ARG_DIRECTION_TRIGGER ?
6846 MV_DIR_OPPOSITE(change->actual_trigger_side) :
6847 game.wind_direction);
6852 case CA_SET_LEVEL_GEMS:
6854 local_player->gems_still_needed = action_arg_number_new;
6856 DrawGameValue_Emeralds(local_player->gems_still_needed);
6861 case CA_SET_LEVEL_TIME:
6863 if (level.time > 0) /* only modify limited time value */
6865 TimeLeft = action_arg_number_new;
6867 DrawGameValue_Time(TimeLeft);
6869 if (!TimeLeft && setup.time_limit)
6870 for (i = 0; i < MAX_PLAYERS; i++)
6871 KillPlayer(&stored_player[i]);
6877 case CA_SET_LEVEL_SCORE:
6879 local_player->score = action_arg_number_new;
6881 DrawGameValue_Score(local_player->score);
6886 case CA_SET_CE_SCORE:
6888 ei->collect_score = action_arg_number_new;
6893 case CA_SET_CE_VALUE:
6895 #if USE_NEW_CUSTOM_VALUE
6896 int last_custom_value = CustomValue[x][y];
6898 CustomValue[x][y] = action_arg_number_new;
6901 printf("::: Count == %d\n", CustomValue[x][y]);
6904 if (CustomValue[x][y] == 0 && last_custom_value > 0)
6907 printf("::: CE_VALUE_GETS_ZERO\n");
6910 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
6911 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
6919 case CA_SET_DYNABOMB_NUMBER:
6921 printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n");
6926 case CA_SET_DYNABOMB_SIZE:
6928 printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n");
6933 case CA_SET_DYNABOMB_POWER:
6935 printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n");
6946 static void ChangeElementNowExt(struct ElementChangeInfo *change,
6947 int x, int y, int target_element)
6949 int previous_move_direction = MovDir[x][y];
6950 #if USE_NEW_CUSTOM_VALUE
6951 int last_ce_value = CustomValue[x][y];
6953 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
6954 IS_WALKABLE(Feld[x][y]));
6956 /* check if element under player changes from accessible to unaccessible
6957 (needed for special case of dropping element which then changes) */
6958 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6959 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6967 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6968 RemoveMovingField(x, y);
6972 Feld[x][y] = target_element;
6974 ResetGfxAnimation(x, y);
6975 ResetRandomAnimationValue(x, y);
6977 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6978 MovDir[x][y] = previous_move_direction;
6980 #if USE_NEW_CUSTOM_VALUE
6981 if (element_info[Feld[x][y]].use_last_ce_value)
6982 CustomValue[x][y] = last_ce_value;
6985 InitField_WithBug1(x, y, FALSE);
6987 DrawLevelField(x, y);
6989 if (GFX_CRUMBLED(Feld[x][y]))
6990 DrawLevelFieldCrumbledSandNeighbours(x, y);
6993 /* "Changed[][]" not set yet to allow "entered by player" change one time */
6994 if (ELEM_IS_PLAYER(target_element))
6995 RelocatePlayer(x, y, target_element);
6998 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7000 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7003 TestIfBadThingTouchesPlayer(x, y);
7004 TestIfPlayerTouchesCustomElement(x, y);
7005 TestIfElementTouchesCustomElement(x, y);
7008 static boolean ChangeElementNow(int x, int y, int element, int page)
7010 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7012 int old_element = Feld[x][y];
7014 /* always use default change event to prevent running into a loop */
7015 if (ChangeEvent[x][y] == -1)
7016 ChangeEvent[x][y] = CE_DELAY;
7018 if (ChangeEvent[x][y] == CE_DELAY)
7020 /* reset actual trigger element, trigger player and action element */
7021 change->actual_trigger_element = EL_EMPTY;
7022 change->actual_trigger_player = EL_PLAYER_1;
7023 change->actual_trigger_side = CH_SIDE_NONE;
7024 change->actual_trigger_ce_value = 0;
7028 /* do not change any elements that have already changed in this frame */
7032 /* do not change already changed elements with same change event */
7033 if (Changed[x][y] & ChangeEvent[x][y])
7038 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7040 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7043 if (change->explode)
7050 if (change->use_target_content)
7052 boolean complete_replace = TRUE;
7053 boolean can_replace[3][3];
7056 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7059 boolean is_walkable;
7060 boolean is_diggable;
7061 boolean is_collectible;
7062 boolean is_removable;
7063 boolean is_destructible;
7064 int ex = x + xx - 1;
7065 int ey = y + yy - 1;
7066 int content_element = change->target_content.e[xx][yy];
7069 can_replace[xx][yy] = TRUE;
7071 if (ex == x && ey == y) /* do not check changing element itself */
7074 if (content_element == EL_EMPTY_SPACE)
7076 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7081 if (!IN_LEV_FIELD(ex, ey))
7083 can_replace[xx][yy] = FALSE;
7084 complete_replace = FALSE;
7091 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7092 e = MovingOrBlocked2Element(ex, ey);
7094 is_empty = (IS_FREE(ex, ey) ||
7095 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7097 is_walkable = (is_empty || IS_WALKABLE(e));
7098 is_diggable = (is_empty || IS_DIGGABLE(e));
7099 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7100 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7101 is_removable = (is_diggable || is_collectible);
7103 can_replace[xx][yy] =
7104 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7105 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7106 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7107 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7108 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7109 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7110 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7112 if (!can_replace[xx][yy])
7113 complete_replace = FALSE;
7116 if (!change->only_if_complete || complete_replace)
7118 boolean something_has_changed = FALSE;
7120 if (change->only_if_complete && change->use_random_replace &&
7121 RND(100) < change->random_percentage)
7124 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7126 int ex = x + xx - 1;
7127 int ey = y + yy - 1;
7128 int content_element;
7130 if (can_replace[xx][yy] && (!change->use_random_replace ||
7131 RND(100) < change->random_percentage))
7133 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7134 RemoveMovingField(ex, ey);
7136 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7138 content_element = change->target_content.e[xx][yy];
7139 target_element = GET_TARGET_ELEMENT(content_element, change);
7141 ChangeElementNowExt(change, ex, ey, target_element);
7143 something_has_changed = TRUE;
7145 /* for symmetry reasons, freeze newly created border elements */
7146 if (ex != x || ey != y)
7147 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7151 if (something_has_changed)
7153 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7154 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7160 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7162 ChangeElementNowExt(change, x, y, target_element);
7164 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7165 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7168 /* this uses direct change before indirect change */
7169 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
7174 #if USE_NEW_DELAYED_ACTION
7176 static void ChangeElement(int x, int y, int page)
7178 int element = MovingOrBlocked2Element(x, y);
7179 struct ElementInfo *ei = &element_info[element];
7180 struct ElementChangeInfo *change = &ei->change_page[page];
7183 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
7184 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
7187 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7188 x, y, element, element_info[element].token_name);
7189 printf("ChangeElement(): This should never happen!\n");
7194 /* this can happen with classic bombs on walkable, changing elements */
7195 if (!CAN_CHANGE_OR_HAS_ACTION(element))
7198 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7199 ChangeDelay[x][y] = 0;
7205 if (ChangeDelay[x][y] == 0) /* initialize element change */
7207 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7209 if (change->can_change)
7211 ResetGfxAnimation(x, y);
7212 ResetRandomAnimationValue(x, y);
7214 if (change->pre_change_function)
7215 change->pre_change_function(x, y);
7219 ChangeDelay[x][y]--;
7221 if (ChangeDelay[x][y] != 0) /* continue element change */
7223 if (change->can_change)
7225 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7227 if (IS_ANIMATED(graphic))
7228 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7230 if (change->change_function)
7231 change->change_function(x, y);
7234 else /* finish element change */
7236 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7238 page = ChangePage[x][y];
7239 ChangePage[x][y] = -1;
7241 change = &ei->change_page[page];
7244 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7246 ChangeDelay[x][y] = 1; /* try change after next move step */
7247 ChangePage[x][y] = page; /* remember page to use for change */
7252 if (change->can_change)
7254 if (ChangeElementNow(x, y, element, page))
7256 if (change->post_change_function)
7257 change->post_change_function(x, y);
7261 if (change->has_action)
7262 ExecuteCustomElementAction(x, y, element, page);
7268 static void ChangeElement(int x, int y, int page)
7270 int element = MovingOrBlocked2Element(x, y);
7271 struct ElementInfo *ei = &element_info[element];
7272 struct ElementChangeInfo *change = &ei->change_page[page];
7275 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7278 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7279 x, y, element, element_info[element].token_name);
7280 printf("ChangeElement(): This should never happen!\n");
7285 /* this can happen with classic bombs on walkable, changing elements */
7286 if (!CAN_CHANGE(element))
7289 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7290 ChangeDelay[x][y] = 0;
7296 if (ChangeDelay[x][y] == 0) /* initialize element change */
7298 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7300 ResetGfxAnimation(x, y);
7301 ResetRandomAnimationValue(x, y);
7303 if (change->pre_change_function)
7304 change->pre_change_function(x, y);
7307 ChangeDelay[x][y]--;
7309 if (ChangeDelay[x][y] != 0) /* continue element change */
7311 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7313 if (IS_ANIMATED(graphic))
7314 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7316 if (change->change_function)
7317 change->change_function(x, y);
7319 else /* finish element change */
7321 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7323 page = ChangePage[x][y];
7324 ChangePage[x][y] = -1;
7326 change = &ei->change_page[page];
7329 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7331 ChangeDelay[x][y] = 1; /* try change after next move step */
7332 ChangePage[x][y] = page; /* remember page to use for change */
7337 if (ChangeElementNow(x, y, element, page))
7339 if (change->post_change_function)
7340 change->post_change_function(x, y);
7347 static boolean CheckTriggeredElementChangeExt(int x, int y,
7348 int trigger_element,
7354 boolean change_done_any = FALSE;
7355 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7358 if (!(trigger_events[trigger_element][trigger_event]))
7361 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7363 int element = EL_CUSTOM_START + i;
7364 boolean change_done = FALSE;
7367 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7368 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7371 for (p = 0; p < element_info[element].num_change_pages; p++)
7373 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7375 if (change->can_change_or_has_action &&
7376 change->has_event[trigger_event] &&
7377 change->trigger_side & trigger_side &&
7378 change->trigger_player & trigger_player &&
7379 change->trigger_page & trigger_page_bits &&
7380 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7382 change->actual_trigger_element = trigger_element;
7383 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7384 change->actual_trigger_side = trigger_side;
7385 change->actual_trigger_ce_value = CustomValue[x][y];
7387 if ((change->can_change && !change_done) || change->has_action)
7391 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7393 if (Feld[x][y] == element)
7395 if (change->can_change && !change_done)
7397 ChangeDelay[x][y] = 1;
7398 ChangeEvent[x][y] = trigger_event;
7399 ChangeElement(x, y, p);
7401 #if USE_NEW_DELAYED_ACTION
7402 else if (change->has_action)
7404 ExecuteCustomElementAction(x, y, element, p);
7405 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7408 if (change->has_action)
7410 ExecuteCustomElementAction(x, y, element, p);
7411 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7417 if (change->can_change)
7420 change_done_any = TRUE;
7427 return change_done_any;
7430 static boolean CheckElementChangeExt(int x, int y,
7432 int trigger_element,
7437 boolean change_done = FALSE;
7440 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7441 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7444 if (Feld[x][y] == EL_BLOCKED)
7446 Blocked2Moving(x, y, &x, &y);
7447 element = Feld[x][y];
7450 if (Feld[x][y] != element) /* check if element has already changed */
7453 for (p = 0; p < element_info[element].num_change_pages; p++)
7455 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7457 boolean check_trigger_element =
7458 (trigger_event == CE_TOUCHING_X ||
7459 trigger_event == CE_HITTING_X ||
7460 trigger_event == CE_HIT_BY_X);
7462 if (change->can_change_or_has_action &&
7463 change->has_event[trigger_event] &&
7464 change->trigger_side & trigger_side &&
7465 change->trigger_player & trigger_player &&
7466 (!check_trigger_element ||
7467 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
7469 change->actual_trigger_element = trigger_element;
7470 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7471 change->actual_trigger_side = trigger_side;
7472 change->actual_trigger_ce_value = CustomValue[x][y];
7474 if (change->can_change && !change_done)
7476 ChangeDelay[x][y] = 1;
7477 ChangeEvent[x][y] = trigger_event;
7478 ChangeElement(x, y, p);
7482 #if USE_NEW_DELAYED_ACTION
7483 else if (change->has_action)
7485 ExecuteCustomElementAction(x, y, element, p);
7486 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7489 if (change->has_action)
7491 ExecuteCustomElementAction(x, y, element, p);
7492 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7501 static void PlayPlayerSound(struct PlayerInfo *player)
7503 int jx = player->jx, jy = player->jy;
7504 int element = player->element_nr;
7505 int last_action = player->last_action_waiting;
7506 int action = player->action_waiting;
7508 if (player->is_waiting)
7510 if (action != last_action)
7511 PlayLevelSoundElementAction(jx, jy, element, action);
7513 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7517 if (action != last_action)
7518 StopSound(element_info[element].sound[last_action]);
7520 if (last_action == ACTION_SLEEPING)
7521 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7525 static void PlayAllPlayersSound()
7529 for (i = 0; i < MAX_PLAYERS; i++)
7530 if (stored_player[i].active)
7531 PlayPlayerSound(&stored_player[i]);
7534 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7536 boolean last_waiting = player->is_waiting;
7537 int move_dir = player->MovDir;
7539 player->last_action_waiting = player->action_waiting;
7543 if (!last_waiting) /* not waiting -> waiting */
7545 player->is_waiting = TRUE;
7547 player->frame_counter_bored =
7549 game.player_boring_delay_fixed +
7550 SimpleRND(game.player_boring_delay_random);
7551 player->frame_counter_sleeping =
7553 game.player_sleeping_delay_fixed +
7554 SimpleRND(game.player_sleeping_delay_random);
7556 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7559 if (game.player_sleeping_delay_fixed +
7560 game.player_sleeping_delay_random > 0 &&
7561 player->anim_delay_counter == 0 &&
7562 player->post_delay_counter == 0 &&
7563 FrameCounter >= player->frame_counter_sleeping)
7564 player->is_sleeping = TRUE;
7565 else if (game.player_boring_delay_fixed +
7566 game.player_boring_delay_random > 0 &&
7567 FrameCounter >= player->frame_counter_bored)
7568 player->is_bored = TRUE;
7570 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7571 player->is_bored ? ACTION_BORING :
7574 if (player->is_sleeping)
7576 if (player->num_special_action_sleeping > 0)
7578 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7580 int last_special_action = player->special_action_sleeping;
7581 int num_special_action = player->num_special_action_sleeping;
7582 int special_action =
7583 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7584 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7585 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7586 last_special_action + 1 : ACTION_SLEEPING);
7587 int special_graphic =
7588 el_act_dir2img(player->element_nr, special_action, move_dir);
7590 player->anim_delay_counter =
7591 graphic_info[special_graphic].anim_delay_fixed +
7592 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7593 player->post_delay_counter =
7594 graphic_info[special_graphic].post_delay_fixed +
7595 SimpleRND(graphic_info[special_graphic].post_delay_random);
7597 player->special_action_sleeping = special_action;
7600 if (player->anim_delay_counter > 0)
7602 player->action_waiting = player->special_action_sleeping;
7603 player->anim_delay_counter--;
7605 else if (player->post_delay_counter > 0)
7607 player->post_delay_counter--;
7611 else if (player->is_bored)
7613 if (player->num_special_action_bored > 0)
7615 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7617 int special_action =
7618 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7619 int special_graphic =
7620 el_act_dir2img(player->element_nr, special_action, move_dir);
7622 player->anim_delay_counter =
7623 graphic_info[special_graphic].anim_delay_fixed +
7624 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7625 player->post_delay_counter =
7626 graphic_info[special_graphic].post_delay_fixed +
7627 SimpleRND(graphic_info[special_graphic].post_delay_random);
7629 player->special_action_bored = special_action;
7632 if (player->anim_delay_counter > 0)
7634 player->action_waiting = player->special_action_bored;
7635 player->anim_delay_counter--;
7637 else if (player->post_delay_counter > 0)
7639 player->post_delay_counter--;
7644 else if (last_waiting) /* waiting -> not waiting */
7646 player->is_waiting = FALSE;
7647 player->is_bored = FALSE;
7648 player->is_sleeping = FALSE;
7650 player->frame_counter_bored = -1;
7651 player->frame_counter_sleeping = -1;
7653 player->anim_delay_counter = 0;
7654 player->post_delay_counter = 0;
7656 player->action_waiting = ACTION_DEFAULT;
7658 player->special_action_bored = ACTION_DEFAULT;
7659 player->special_action_sleeping = ACTION_DEFAULT;
7663 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7665 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7666 int left = player_action & JOY_LEFT;
7667 int right = player_action & JOY_RIGHT;
7668 int up = player_action & JOY_UP;
7669 int down = player_action & JOY_DOWN;
7670 int button1 = player_action & JOY_BUTTON_1;
7671 int button2 = player_action & JOY_BUTTON_2;
7672 int dx = (left ? -1 : right ? 1 : 0);
7673 int dy = (up ? -1 : down ? 1 : 0);
7675 if (!player->active || tape.pausing)
7681 snapped = SnapField(player, dx, dy);
7685 dropped = DropElement(player);
7687 moved = MovePlayer(player, dx, dy);
7690 if (tape.single_step && tape.recording && !tape.pausing)
7692 if (button1 || (dropped && !moved))
7694 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7695 SnapField(player, 0, 0); /* stop snapping */
7699 SetPlayerWaiting(player, FALSE);
7701 return player_action;
7705 /* no actions for this player (no input at player's configured device) */
7707 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7708 SnapField(player, 0, 0);
7709 CheckGravityMovementWhenNotMoving(player);
7711 if (player->MovPos == 0)
7712 SetPlayerWaiting(player, TRUE);
7714 if (player->MovPos == 0) /* needed for tape.playing */
7715 player->is_moving = FALSE;
7717 player->is_dropping = FALSE;
7723 void AdvanceFrameAndPlayerCounters(int player_nr)
7727 /* advance frame counters (global frame counter and time frame counter) */
7731 /* advance player counters (counters for move delay, move animation etc.) */
7732 for (i = 0; i < MAX_PLAYERS; i++)
7734 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
7735 int move_delay_value = stored_player[i].move_delay_value;
7736 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
7738 if (!advance_player_counters) /* not all players may be affected */
7741 #if USE_NEW_PLAYER_ANIM
7742 if (move_frames == 0) /* less than one move per game frame */
7744 int stepsize = TILEX / move_delay_value;
7745 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
7746 int count = (stored_player[i].is_moving ?
7747 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
7749 if (count % delay == 0)
7754 stored_player[i].Frame += move_frames;
7756 if (stored_player[i].MovPos != 0)
7757 stored_player[i].StepFrame += move_frames;
7759 if (stored_player[i].move_delay > 0)
7760 stored_player[i].move_delay--;
7762 /* due to bugs in previous versions, counter must count up, not down */
7763 if (stored_player[i].push_delay != -1)
7764 stored_player[i].push_delay++;
7766 if (stored_player[i].drop_delay > 0)
7767 stored_player[i].drop_delay--;
7773 static unsigned long game_frame_delay = 0;
7774 unsigned long game_frame_delay_value;
7775 int magic_wall_x = 0, magic_wall_y = 0;
7776 int i, x, y, element, graphic;
7777 byte *recorded_player_action;
7778 byte summarized_player_action = 0;
7779 byte tape_action[MAX_PLAYERS];
7781 if (game_status != GAME_MODE_PLAYING)
7784 game_frame_delay_value =
7785 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7787 if (tape.playing && tape.warp_forward && !tape.pausing)
7788 game_frame_delay_value = 0;
7790 /* ---------- main game synchronization point ---------- */
7792 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
7794 if (network_playing && !network_player_action_received)
7796 /* try to get network player actions in time */
7798 #if defined(NETWORK_AVALIABLE)
7799 /* last chance to get network player actions without main loop delay */
7803 /* game was quit by network peer */
7804 if (game_status != GAME_MODE_PLAYING)
7807 if (!network_player_action_received)
7808 return; /* failed to get network player actions in time */
7814 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7817 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
7818 if (recorded_player_action == NULL && tape.pausing)
7822 for (i = 0; i < MAX_PLAYERS; i++)
7824 summarized_player_action |= stored_player[i].action;
7826 if (!network_playing)
7827 stored_player[i].effective_action = stored_player[i].action;
7830 #if defined(NETWORK_AVALIABLE)
7831 if (network_playing)
7832 SendToServer_MovePlayer(summarized_player_action);
7835 if (!options.network && !setup.team_mode)
7836 local_player->effective_action = summarized_player_action;
7838 if (recorded_player_action != NULL)
7839 for (i = 0; i < MAX_PLAYERS; i++)
7840 stored_player[i].effective_action = recorded_player_action[i];
7842 for (i = 0; i < MAX_PLAYERS; i++)
7844 tape_action[i] = stored_player[i].effective_action;
7846 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7847 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7850 /* only save actions from input devices, but not programmed actions */
7852 TapeRecordAction(tape_action);
7854 for (i = 0; i < MAX_PLAYERS; i++)
7856 int actual_player_action = stored_player[i].effective_action;
7859 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
7860 - rnd_equinox_tetrachloride 048
7861 - rnd_equinox_tetrachloride_ii 096
7862 - rnd_emanuel_schmieg 002
7863 - doctor_sloan_ww 001, 020
7865 if (stored_player[i].MovPos == 0)
7866 CheckGravityMovement(&stored_player[i]);
7869 /* overwrite programmed action with tape action */
7870 if (stored_player[i].programmed_action)
7871 actual_player_action = stored_player[i].programmed_action;
7874 PlayerActions(&stored_player[i], actual_player_action);
7876 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7878 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7879 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7882 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7885 network_player_action_received = FALSE;
7887 ScrollScreen(NULL, SCROLL_GO_ON);
7889 /* for backwards compatibility, the following code emulates a fixed bug that
7890 occured when pushing elements (causing elements that just made their last
7891 pushing step to already (if possible) make their first falling step in the
7892 same game frame, which is bad); this code is also needed to use the famous
7893 "spring push bug" which is used in older levels and might be wanted to be
7894 used also in newer levels, but in this case the buggy pushing code is only
7895 affecting the "spring" element and no other elements */
7897 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7899 for (i = 0; i < MAX_PLAYERS; i++)
7901 struct PlayerInfo *player = &stored_player[i];
7905 if (player->active && player->is_pushing && player->is_moving &&
7907 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7908 Feld[x][y] == EL_SPRING))
7910 ContinueMoving(x, y);
7912 /* continue moving after pushing (this is actually a bug) */
7913 if (!IS_MOVING(x, y))
7921 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7923 Changed[x][y] = FALSE;
7924 ChangeEvent[x][y] = -1;
7926 /* this must be handled before main playfield loop */
7927 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
7930 if (MovDelay[x][y] <= 0)
7934 #if USE_NEW_SNAP_DELAY
7935 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
7938 if (MovDelay[x][y] <= 0)
7941 DrawLevelField(x, y);
7947 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7949 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7950 printf("GameActions(): This should never happen!\n");
7952 ChangePage[x][y] = -1;
7957 if (WasJustMoving[x][y] > 0)
7958 WasJustMoving[x][y]--;
7959 if (WasJustFalling[x][y] > 0)
7960 WasJustFalling[x][y]--;
7961 if (CheckCollision[x][y] > 0)
7962 CheckCollision[x][y]--;
7966 /* reset finished pushing action (not done in ContinueMoving() to allow
7967 continous pushing animation for elements with zero push delay) */
7968 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7970 ResetGfxAnimation(x, y);
7971 DrawLevelField(x, y);
7975 if (IS_BLOCKED(x, y))
7979 Blocked2Moving(x, y, &oldx, &oldy);
7980 if (!IS_MOVING(oldx, oldy))
7982 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7983 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7984 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7985 printf("GameActions(): This should never happen!\n");
7991 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7993 element = Feld[x][y];
7994 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7996 if (graphic_info[graphic].anim_global_sync)
7997 GfxFrame[x][y] = FrameCounter;
7999 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8000 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8001 ResetRandomAnimationValue(x, y);
8003 SetRandomAnimationValue(x, y);
8005 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8007 if (IS_INACTIVE(element))
8009 if (IS_ANIMATED(graphic))
8010 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8015 /* this may take place after moving, so 'element' may have changed */
8016 if (IS_CHANGING(x, y) &&
8017 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8019 int page = element_info[element].event_page_nr[CE_DELAY];
8021 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
8025 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8029 ChangeElement(x, y, page);
8031 if (CAN_CHANGE(element))
8032 ChangeElement(x, y, page);
8034 if (HAS_ACTION(element))
8035 ExecuteCustomElementAction(x, y, element, page);
8040 element = Feld[x][y];
8041 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8044 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8048 element = Feld[x][y];
8049 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8051 if (IS_ANIMATED(graphic) &&
8054 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8056 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8057 EdelsteinFunkeln(x, y);
8059 else if ((element == EL_ACID ||
8060 element == EL_EXIT_OPEN ||
8061 element == EL_SP_EXIT_OPEN ||
8062 element == EL_SP_TERMINAL ||
8063 element == EL_SP_TERMINAL_ACTIVE ||
8064 element == EL_EXTRA_TIME ||
8065 element == EL_SHIELD_NORMAL ||
8066 element == EL_SHIELD_DEADLY) &&
8067 IS_ANIMATED(graphic))
8068 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8069 else if (IS_MOVING(x, y))
8070 ContinueMoving(x, y);
8071 else if (IS_ACTIVE_BOMB(element))
8072 CheckDynamite(x, y);
8073 else if (element == EL_AMOEBA_GROWING)
8074 AmoebeWaechst(x, y);
8075 else if (element == EL_AMOEBA_SHRINKING)
8076 AmoebaDisappearing(x, y);
8078 #if !USE_NEW_AMOEBA_CODE
8079 else if (IS_AMOEBALIVE(element))
8080 AmoebeAbleger(x, y);
8083 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8085 else if (element == EL_EXIT_CLOSED)
8087 else if (element == EL_SP_EXIT_CLOSED)
8089 else if (element == EL_EXPANDABLE_WALL_GROWING)
8091 else if (element == EL_EXPANDABLE_WALL ||
8092 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8093 element == EL_EXPANDABLE_WALL_VERTICAL ||
8094 element == EL_EXPANDABLE_WALL_ANY)
8096 else if (element == EL_FLAMES)
8097 CheckForDragon(x, y);
8098 else if (element == EL_EXPLOSION)
8099 ; /* drawing of correct explosion animation is handled separately */
8100 else if (element == EL_ELEMENT_SNAPPING)
8103 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
8105 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8108 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8109 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8111 if (IS_BELT_ACTIVE(element))
8112 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8114 if (game.magic_wall_active)
8116 int jx = local_player->jx, jy = local_player->jy;
8118 /* play the element sound at the position nearest to the player */
8119 if ((element == EL_MAGIC_WALL_FULL ||
8120 element == EL_MAGIC_WALL_ACTIVE ||
8121 element == EL_MAGIC_WALL_EMPTYING ||
8122 element == EL_BD_MAGIC_WALL_FULL ||
8123 element == EL_BD_MAGIC_WALL_ACTIVE ||
8124 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8125 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8133 #if USE_NEW_AMOEBA_CODE
8134 /* new experimental amoeba growth stuff */
8135 if (!(FrameCounter % 8))
8137 static unsigned long random = 1684108901;
8139 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8141 x = RND(lev_fieldx);
8142 y = RND(lev_fieldy);
8143 element = Feld[x][y];
8145 if (!IS_PLAYER(x,y) &&
8146 (element == EL_EMPTY ||
8147 CAN_GROW_INTO(element) ||
8148 element == EL_QUICKSAND_EMPTY ||
8149 element == EL_ACID_SPLASH_LEFT ||
8150 element == EL_ACID_SPLASH_RIGHT))
8152 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8153 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8154 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8155 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8156 Feld[x][y] = EL_AMOEBA_DROP;
8159 random = random * 129 + 1;
8165 if (game.explosions_delayed)
8168 game.explosions_delayed = FALSE;
8170 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8172 element = Feld[x][y];
8174 if (ExplodeField[x][y])
8175 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8176 else if (element == EL_EXPLOSION)
8177 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8179 ExplodeField[x][y] = EX_TYPE_NONE;
8182 game.explosions_delayed = TRUE;
8185 if (game.magic_wall_active)
8187 if (!(game.magic_wall_time_left % 4))
8189 int element = Feld[magic_wall_x][magic_wall_y];
8191 if (element == EL_BD_MAGIC_WALL_FULL ||
8192 element == EL_BD_MAGIC_WALL_ACTIVE ||
8193 element == EL_BD_MAGIC_WALL_EMPTYING)
8194 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8196 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8199 if (game.magic_wall_time_left > 0)
8201 game.magic_wall_time_left--;
8202 if (!game.magic_wall_time_left)
8204 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8206 element = Feld[x][y];
8208 if (element == EL_MAGIC_WALL_ACTIVE ||
8209 element == EL_MAGIC_WALL_FULL)
8211 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8212 DrawLevelField(x, y);
8214 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8215 element == EL_BD_MAGIC_WALL_FULL)
8217 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8218 DrawLevelField(x, y);
8222 game.magic_wall_active = FALSE;
8227 if (game.light_time_left > 0)
8229 game.light_time_left--;
8231 if (game.light_time_left == 0)
8232 RedrawAllLightSwitchesAndInvisibleElements();
8235 if (game.timegate_time_left > 0)
8237 game.timegate_time_left--;
8239 if (game.timegate_time_left == 0)
8240 CloseAllOpenTimegates();
8243 for (i = 0; i < MAX_PLAYERS; i++)
8245 struct PlayerInfo *player = &stored_player[i];
8247 if (SHIELD_ON(player))
8249 if (player->shield_deadly_time_left)
8250 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8251 else if (player->shield_normal_time_left)
8252 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8256 if (TimeFrames >= FRAMES_PER_SECOND)
8261 for (i = 0; i < MAX_PLAYERS; i++)
8263 struct PlayerInfo *player = &stored_player[i];
8265 if (SHIELD_ON(player))
8267 player->shield_normal_time_left--;
8269 if (player->shield_deadly_time_left > 0)
8270 player->shield_deadly_time_left--;
8274 if (!level.use_step_counter)
8282 if (TimeLeft <= 10 && setup.time_limit)
8283 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8285 DrawGameValue_Time(TimeLeft);
8287 if (!TimeLeft && setup.time_limit)
8288 for (i = 0; i < MAX_PLAYERS; i++)
8289 KillPlayer(&stored_player[i]);
8291 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8292 DrawGameValue_Time(TimePlayed);
8295 if (tape.recording || tape.playing)
8296 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8300 PlayAllPlayersSound();
8302 if (options.debug) /* calculate frames per second */
8304 static unsigned long fps_counter = 0;
8305 static int fps_frames = 0;
8306 unsigned long fps_delay_ms = Counter() - fps_counter;
8310 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8312 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8315 fps_counter = Counter();
8318 redraw_mask |= REDRAW_FPS;
8321 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
8323 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8325 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8327 local_player->show_envelope = 0;
8330 /* use random number generator in every frame to make it less predictable */
8331 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8335 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8337 int min_x = x, min_y = y, max_x = x, max_y = y;
8340 for (i = 0; i < MAX_PLAYERS; i++)
8342 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8344 if (!stored_player[i].active || &stored_player[i] == player)
8347 min_x = MIN(min_x, jx);
8348 min_y = MIN(min_y, jy);
8349 max_x = MAX(max_x, jx);
8350 max_y = MAX(max_y, jy);
8353 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8356 static boolean AllPlayersInVisibleScreen()
8360 for (i = 0; i < MAX_PLAYERS; i++)
8362 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8364 if (!stored_player[i].active)
8367 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8374 void ScrollLevel(int dx, int dy)
8376 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8379 BlitBitmap(drawto_field, drawto_field,
8380 FX + TILEX * (dx == -1) - softscroll_offset,
8381 FY + TILEY * (dy == -1) - softscroll_offset,
8382 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8383 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8384 FX + TILEX * (dx == 1) - softscroll_offset,
8385 FY + TILEY * (dy == 1) - softscroll_offset);
8389 x = (dx == 1 ? BX1 : BX2);
8390 for (y = BY1; y <= BY2; y++)
8391 DrawScreenField(x, y);
8396 y = (dy == 1 ? BY1 : BY2);
8397 for (x = BX1; x <= BX2; x++)
8398 DrawScreenField(x, y);
8401 redraw_mask |= REDRAW_FIELD;
8404 static boolean canFallDown(struct PlayerInfo *player)
8406 int jx = player->jx, jy = player->jy;
8408 return (IN_LEV_FIELD(jx, jy + 1) &&
8409 (IS_FREE(jx, jy + 1) ||
8410 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8411 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8412 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8415 static boolean canPassField(int x, int y, int move_dir)
8417 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8418 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8419 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8422 int element = Feld[x][y];
8424 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8425 !CAN_MOVE(element) &&
8426 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8427 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8428 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8431 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8433 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8434 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8435 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8439 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8440 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
8441 (IS_DIGGABLE(Feld[newx][newy]) ||
8442 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8443 canPassField(newx, newy, move_dir)));
8446 static void CheckGravityMovement(struct PlayerInfo *player)
8448 if (game.gravity && !player->programmed_action)
8450 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8451 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8452 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8453 int jx = player->jx, jy = player->jy;
8454 boolean player_is_moving_to_valid_field =
8455 (!player_is_snapping &&
8456 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8457 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8458 boolean player_can_fall_down = canFallDown(player);
8460 if (player_can_fall_down &&
8461 !player_is_moving_to_valid_field)
8462 player->programmed_action = MV_DOWN;
8466 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8468 return CheckGravityMovement(player);
8470 if (game.gravity && !player->programmed_action)
8472 int jx = player->jx, jy = player->jy;
8473 boolean field_under_player_is_free =
8474 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8475 boolean player_is_standing_on_valid_field =
8476 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8477 (IS_WALKABLE(Feld[jx][jy]) &&
8478 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8480 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8481 player->programmed_action = MV_DOWN;
8487 -----------------------------------------------------------------------------
8488 dx, dy: direction (non-diagonal) to try to move the player to
8489 real_dx, real_dy: direction as read from input device (can be diagonal)
8492 boolean MovePlayerOneStep(struct PlayerInfo *player,
8493 int dx, int dy, int real_dx, int real_dy)
8495 int jx = player->jx, jy = player->jy;
8496 int new_jx = jx + dx, new_jy = jy + dy;
8500 if (!player->active || (!dx && !dy))
8501 return MF_NO_ACTION;
8503 player->MovDir = (dx < 0 ? MV_LEFT :
8506 dy > 0 ? MV_DOWN : MV_NONE);
8508 if (!IN_LEV_FIELD(new_jx, new_jy))
8509 return MF_NO_ACTION;
8511 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8512 return MF_NO_ACTION;
8514 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8516 if (DONT_RUN_INTO(element))
8518 if (element == EL_ACID && dx == 0 && dy == 1)
8520 SplashAcid(new_jx, new_jy);
8521 Feld[jx][jy] = EL_PLAYER_1;
8522 InitMovingField(jx, jy, MV_DOWN);
8523 Store[jx][jy] = EL_ACID;
8524 ContinueMoving(jx, jy);
8528 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
8533 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8534 if (can_move != MF_MOVING)
8537 /* check if DigField() has caused relocation of the player */
8538 if (player->jx != jx || player->jy != jy)
8539 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
8541 StorePlayer[jx][jy] = 0;
8542 player->last_jx = jx;
8543 player->last_jy = jy;
8544 player->jx = new_jx;
8545 player->jy = new_jy;
8546 StorePlayer[new_jx][new_jy] = player->element_nr;
8548 if (player->move_delay_value_next != -1)
8550 player->move_delay_value = player->move_delay_value_next;
8551 player->move_delay_value_next = -1;
8555 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8557 player->step_counter++;
8559 PlayerVisit[jx][jy] = FrameCounter;
8561 ScrollPlayer(player, SCROLL_INIT);
8566 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8568 int jx = player->jx, jy = player->jy;
8569 int old_jx = jx, old_jy = jy;
8570 int moved = MF_NO_ACTION;
8572 if (!player->active)
8577 if (player->MovPos == 0)
8579 player->is_moving = FALSE;
8580 player->is_digging = FALSE;
8581 player->is_collecting = FALSE;
8582 player->is_snapping = FALSE;
8583 player->is_pushing = FALSE;
8589 if (player->move_delay > 0)
8592 player->move_delay = -1; /* set to "uninitialized" value */
8594 /* store if player is automatically moved to next field */
8595 player->is_auto_moving = (player->programmed_action != MV_NONE);
8597 /* remove the last programmed player action */
8598 player->programmed_action = 0;
8602 /* should only happen if pre-1.2 tape recordings are played */
8603 /* this is only for backward compatibility */
8605 int original_move_delay_value = player->move_delay_value;
8608 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8612 /* scroll remaining steps with finest movement resolution */
8613 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8615 while (player->MovPos)
8617 ScrollPlayer(player, SCROLL_GO_ON);
8618 ScrollScreen(NULL, SCROLL_GO_ON);
8620 AdvanceFrameAndPlayerCounters(player->index_nr);
8626 player->move_delay_value = original_move_delay_value;
8629 if (player->last_move_dir & MV_HORIZONTAL)
8631 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8632 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8636 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8637 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8643 if (moved & MF_MOVING && !ScreenMovPos &&
8644 (player == local_player || !options.network))
8646 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8647 int offset = (setup.scroll_delay ? 3 : 0);
8649 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8651 /* actual player has left the screen -- scroll in that direction */
8652 if (jx != old_jx) /* player has moved horizontally */
8653 scroll_x += (jx - old_jx);
8654 else /* player has moved vertically */
8655 scroll_y += (jy - old_jy);
8659 if (jx != old_jx) /* player has moved horizontally */
8661 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8662 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8663 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8665 /* don't scroll over playfield boundaries */
8666 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8667 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8669 /* don't scroll more than one field at a time */
8670 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8672 /* don't scroll against the player's moving direction */
8673 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8674 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8675 scroll_x = old_scroll_x;
8677 else /* player has moved vertically */
8679 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8680 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8681 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8683 /* don't scroll over playfield boundaries */
8684 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8685 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8687 /* don't scroll more than one field at a time */
8688 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8690 /* don't scroll against the player's moving direction */
8691 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8692 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8693 scroll_y = old_scroll_y;
8697 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8699 if (!options.network && !AllPlayersInVisibleScreen())
8701 scroll_x = old_scroll_x;
8702 scroll_y = old_scroll_y;
8706 ScrollScreen(player, SCROLL_INIT);
8707 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8712 player->StepFrame = 0;
8714 if (moved & MF_MOVING)
8716 if (old_jx != jx && old_jy == jy)
8717 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8718 else if (old_jx == jx && old_jy != jy)
8719 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8721 DrawLevelField(jx, jy); /* for "crumbled sand" */
8723 player->last_move_dir = player->MovDir;
8724 player->is_moving = TRUE;
8725 player->is_snapping = FALSE;
8726 player->is_switching = FALSE;
8727 player->is_dropping = FALSE;
8731 CheckGravityMovementWhenNotMoving(player);
8733 player->is_moving = FALSE;
8735 /* at this point, the player is allowed to move, but cannot move right now
8736 (e.g. because of something blocking the way) -- ensure that the player
8737 is also allowed to move in the next frame (in old versions before 3.1.1,
8738 the player was forced to wait again for eight frames before next try) */
8740 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8741 player->move_delay = 0; /* allow direct movement in the next frame */
8744 if (player->move_delay == -1) /* not yet initialized by DigField() */
8745 player->move_delay = player->move_delay_value;
8747 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8749 TestIfPlayerTouchesBadThing(jx, jy);
8750 TestIfPlayerTouchesCustomElement(jx, jy);
8753 if (!player->active)
8754 RemovePlayer(player);
8759 void ScrollPlayer(struct PlayerInfo *player, int mode)
8761 int jx = player->jx, jy = player->jy;
8762 int last_jx = player->last_jx, last_jy = player->last_jy;
8763 int move_stepsize = TILEX / player->move_delay_value;
8765 #if USE_NEW_PLAYER_SPEED
8766 if (!player->active)
8769 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
8772 if (!player->active || player->MovPos == 0)
8776 if (mode == SCROLL_INIT)
8778 player->actual_frame_counter = FrameCounter;
8779 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8781 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
8782 Feld[last_jx][last_jy] == EL_EMPTY)
8784 int last_field_block_delay = 0; /* start with no blocking at all */
8785 int block_delay_adjustment = player->block_delay_adjustment;
8787 /* if player blocks last field, add delay for exactly one move */
8788 if (player->block_last_field)
8790 last_field_block_delay += player->move_delay_value;
8792 /* when blocking enabled, prevent moving up despite gravity */
8793 if (game.gravity && player->MovDir == MV_UP)
8794 block_delay_adjustment = -1;
8797 /* add block delay adjustment (also possible when not blocking) */
8798 last_field_block_delay += block_delay_adjustment;
8800 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8801 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
8804 #if USE_NEW_PLAYER_SPEED
8805 if (player->MovPos != 0) /* player has not yet reached destination */
8811 else if (!FrameReached(&player->actual_frame_counter, 1))
8815 printf("::: player->MovPos: %d -> %d\n",
8817 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
8820 #if USE_NEW_PLAYER_SPEED
8821 if (player->MovPos != 0)
8823 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8824 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8826 /* before DrawPlayer() to draw correct player graphic for this case */
8827 if (player->MovPos == 0)
8828 CheckGravityMovement(player);
8831 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8832 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8834 /* before DrawPlayer() to draw correct player graphic for this case */
8835 if (player->MovPos == 0)
8836 CheckGravityMovement(player);
8839 if (player->MovPos == 0) /* player reached destination field */
8842 printf("::: player reached destination field\n");
8845 if (player->move_delay_reset_counter > 0)
8847 player->move_delay_reset_counter--;
8849 if (player->move_delay_reset_counter == 0)
8851 /* continue with normal speed after quickly moving through gate */
8852 HALVE_PLAYER_SPEED(player);
8854 /* be able to make the next move without delay */
8855 player->move_delay = 0;
8859 player->last_jx = jx;
8860 player->last_jy = jy;
8862 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8863 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8864 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8866 DrawPlayer(player); /* needed here only to cleanup last field */
8867 RemovePlayer(player);
8869 if (local_player->friends_still_needed == 0 ||
8870 IS_SP_ELEMENT(Feld[jx][jy]))
8871 player->LevelSolved = player->GameOver = TRUE;
8874 /* this breaks one level: "machine", level 000 */
8876 int move_direction = player->MovDir;
8877 int enter_side = MV_DIR_OPPOSITE(move_direction);
8878 int leave_side = move_direction;
8879 int old_jx = last_jx;
8880 int old_jy = last_jy;
8881 int old_element = Feld[old_jx][old_jy];
8882 int new_element = Feld[jx][jy];
8884 if (IS_CUSTOM_ELEMENT(old_element))
8885 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
8887 player->index_bit, leave_side);
8889 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
8891 player->index_bit, leave_side);
8893 if (IS_CUSTOM_ELEMENT(new_element))
8894 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
8895 player->index_bit, enter_side);
8897 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
8899 player->index_bit, enter_side);
8902 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8904 TestIfPlayerTouchesBadThing(jx, jy);
8905 TestIfPlayerTouchesCustomElement(jx, jy);
8907 /* needed because pushed element has not yet reached its destination,
8908 so it would trigger a change event at its previous field location */
8909 if (!player->is_pushing)
8910 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8912 if (!player->active)
8913 RemovePlayer(player);
8916 if (level.use_step_counter)
8926 if (TimeLeft <= 10 && setup.time_limit)
8927 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8929 DrawGameValue_Time(TimeLeft);
8931 if (!TimeLeft && setup.time_limit)
8932 for (i = 0; i < MAX_PLAYERS; i++)
8933 KillPlayer(&stored_player[i]);
8935 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8936 DrawGameValue_Time(TimePlayed);
8939 if (tape.single_step && tape.recording && !tape.pausing &&
8940 !player->programmed_action)
8941 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8945 void ScrollScreen(struct PlayerInfo *player, int mode)
8947 static unsigned long screen_frame_counter = 0;
8949 if (mode == SCROLL_INIT)
8951 /* set scrolling step size according to actual player's moving speed */
8952 ScrollStepSize = TILEX / player->move_delay_value;
8954 screen_frame_counter = FrameCounter;
8955 ScreenMovDir = player->MovDir;
8956 ScreenMovPos = player->MovPos;
8957 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8960 else if (!FrameReached(&screen_frame_counter, 1))
8965 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8966 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8967 redraw_mask |= REDRAW_FIELD;
8970 ScreenMovDir = MV_NONE;
8973 void TestIfPlayerTouchesCustomElement(int x, int y)
8975 static int xy[4][2] =
8982 static int trigger_sides[4][2] =
8984 /* center side border side */
8985 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8986 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8987 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8988 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8990 static int touch_dir[4] =
8997 int center_element = Feld[x][y]; /* should always be non-moving! */
9000 for (i = 0; i < NUM_DIRECTIONS; i++)
9002 int xx = x + xy[i][0];
9003 int yy = y + xy[i][1];
9004 int center_side = trigger_sides[i][0];
9005 int border_side = trigger_sides[i][1];
9008 if (!IN_LEV_FIELD(xx, yy))
9011 if (IS_PLAYER(x, y))
9013 struct PlayerInfo *player = PLAYERINFO(x, y);
9015 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9016 border_element = Feld[xx][yy]; /* may be moving! */
9017 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9018 border_element = Feld[xx][yy];
9019 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9020 border_element = MovingOrBlocked2Element(xx, yy);
9022 continue; /* center and border element do not touch */
9024 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9025 player->index_bit, border_side);
9026 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9027 CE_PLAYER_TOUCHES_X,
9028 player->index_bit, border_side);
9030 else if (IS_PLAYER(xx, yy))
9032 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9034 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9036 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9037 continue; /* center and border element do not touch */
9040 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9041 player->index_bit, center_side);
9042 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9043 CE_PLAYER_TOUCHES_X,
9044 player->index_bit, center_side);
9050 void TestIfElementTouchesCustomElement(int x, int y)
9052 static int xy[4][2] =
9059 static int trigger_sides[4][2] =
9061 /* center side border side */
9062 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9063 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9064 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9065 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9067 static int touch_dir[4] =
9074 boolean change_center_element = FALSE;
9075 int center_element = Feld[x][y]; /* should always be non-moving! */
9078 for (i = 0; i < NUM_DIRECTIONS; i++)
9080 int xx = x + xy[i][0];
9081 int yy = y + xy[i][1];
9082 int center_side = trigger_sides[i][0];
9083 int border_side = trigger_sides[i][1];
9086 if (!IN_LEV_FIELD(xx, yy))
9089 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9090 border_element = Feld[xx][yy]; /* may be moving! */
9091 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9092 border_element = Feld[xx][yy];
9093 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9094 border_element = MovingOrBlocked2Element(xx, yy);
9096 continue; /* center and border element do not touch */
9098 /* check for change of center element (but change it only once) */
9099 if (!change_center_element)
9100 change_center_element =
9101 CheckElementChangeBySide(x, y, center_element, border_element,
9102 CE_TOUCHING_X, border_side);
9104 /* check for change of border element */
9105 CheckElementChangeBySide(xx, yy, border_element, center_element,
9106 CE_TOUCHING_X, center_side);
9110 void TestIfElementHitsCustomElement(int x, int y, int direction)
9112 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9113 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9114 int hitx = x + dx, hity = y + dy;
9115 int hitting_element = Feld[x][y];
9116 int touched_element;
9118 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9121 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9122 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9124 if (IN_LEV_FIELD(hitx, hity))
9126 int opposite_direction = MV_DIR_OPPOSITE(direction);
9127 int hitting_side = direction;
9128 int touched_side = opposite_direction;
9129 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9130 MovDir[hitx][hity] != direction ||
9131 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9137 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9138 CE_HITTING_X, touched_side);
9140 CheckElementChangeBySide(hitx, hity, touched_element,
9141 hitting_element, CE_HIT_BY_X, hitting_side);
9143 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9144 CE_HIT_BY_SOMETHING, opposite_direction);
9148 /* "hitting something" is also true when hitting the playfield border */
9149 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9150 CE_HITTING_SOMETHING, direction);
9154 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9156 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9157 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9158 int hitx = x + dx, hity = y + dy;
9159 int hitting_element = Feld[x][y];
9160 int touched_element;
9162 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9163 !IS_FREE(hitx, hity) &&
9164 (!IS_MOVING(hitx, hity) ||
9165 MovDir[hitx][hity] != direction ||
9166 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9169 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9173 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9177 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9178 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9180 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9181 EP_CAN_SMASH_EVERYTHING, direction);
9183 if (IN_LEV_FIELD(hitx, hity))
9185 int opposite_direction = MV_DIR_OPPOSITE(direction);
9186 int hitting_side = direction;
9187 int touched_side = opposite_direction;
9189 int touched_element = MovingOrBlocked2Element(hitx, hity);
9192 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9193 MovDir[hitx][hity] != direction ||
9194 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9203 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9204 CE_SMASHED_BY_SOMETHING, opposite_direction);
9206 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9207 CE_OTHER_IS_SMASHING, touched_side);
9209 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9210 CE_OTHER_GETS_SMASHED, hitting_side);
9216 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9218 int i, kill_x = -1, kill_y = -1;
9219 int bad_element = -1;
9220 static int test_xy[4][2] =
9227 static int test_dir[4] =
9235 for (i = 0; i < NUM_DIRECTIONS; i++)
9237 int test_x, test_y, test_move_dir, test_element;
9239 test_x = good_x + test_xy[i][0];
9240 test_y = good_y + test_xy[i][1];
9242 if (!IN_LEV_FIELD(test_x, test_y))
9246 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9248 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9250 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9251 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9253 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9254 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9258 bad_element = test_element;
9264 if (kill_x != -1 || kill_y != -1)
9266 if (IS_PLAYER(good_x, good_y))
9268 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9270 if (player->shield_deadly_time_left > 0 &&
9271 !IS_INDESTRUCTIBLE(bad_element))
9272 Bang(kill_x, kill_y);
9273 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9277 Bang(good_x, good_y);
9281 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9283 int i, kill_x = -1, kill_y = -1;
9284 int bad_element = Feld[bad_x][bad_y];
9285 static int test_xy[4][2] =
9292 static int touch_dir[4] =
9299 static int test_dir[4] =
9307 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9310 for (i = 0; i < NUM_DIRECTIONS; i++)
9312 int test_x, test_y, test_move_dir, test_element;
9314 test_x = bad_x + test_xy[i][0];
9315 test_y = bad_y + test_xy[i][1];
9316 if (!IN_LEV_FIELD(test_x, test_y))
9320 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9322 test_element = Feld[test_x][test_y];
9324 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9325 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9327 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9328 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9330 /* good thing is player or penguin that does not move away */
9331 if (IS_PLAYER(test_x, test_y))
9333 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9335 if (bad_element == EL_ROBOT && player->is_moving)
9336 continue; /* robot does not kill player if he is moving */
9338 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9340 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9341 continue; /* center and border element do not touch */
9348 else if (test_element == EL_PENGUIN)
9357 if (kill_x != -1 || kill_y != -1)
9359 if (IS_PLAYER(kill_x, kill_y))
9361 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9363 if (player->shield_deadly_time_left > 0 &&
9364 !IS_INDESTRUCTIBLE(bad_element))
9366 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9370 Bang(kill_x, kill_y);
9374 void TestIfPlayerTouchesBadThing(int x, int y)
9376 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9379 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
9381 TestIfGoodThingHitsBadThing(x, y, move_dir);
9384 void TestIfBadThingTouchesPlayer(int x, int y)
9386 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9389 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
9391 TestIfBadThingHitsGoodThing(x, y, move_dir);
9394 void TestIfFriendTouchesBadThing(int x, int y)
9396 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9399 void TestIfBadThingTouchesFriend(int x, int y)
9401 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9404 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9406 int i, kill_x = bad_x, kill_y = bad_y;
9407 static int xy[4][2] =
9415 for (i = 0; i < NUM_DIRECTIONS; i++)
9419 x = bad_x + xy[i][0];
9420 y = bad_y + xy[i][1];
9421 if (!IN_LEV_FIELD(x, y))
9424 element = Feld[x][y];
9425 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9426 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9434 if (kill_x != bad_x || kill_y != bad_y)
9438 void KillPlayer(struct PlayerInfo *player)
9440 int jx = player->jx, jy = player->jy;
9442 if (!player->active)
9445 /* remove accessible field at the player's position */
9446 Feld[jx][jy] = EL_EMPTY;
9448 /* deactivate shield (else Bang()/Explode() would not work right) */
9449 player->shield_normal_time_left = 0;
9450 player->shield_deadly_time_left = 0;
9456 static void KillPlayerUnlessEnemyProtected(int x, int y)
9458 if (!PLAYER_ENEMY_PROTECTED(x, y))
9459 KillPlayer(PLAYERINFO(x, y));
9462 static void KillPlayerUnlessExplosionProtected(int x, int y)
9464 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9465 KillPlayer(PLAYERINFO(x, y));
9468 void BuryPlayer(struct PlayerInfo *player)
9470 int jx = player->jx, jy = player->jy;
9472 if (!player->active)
9475 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9476 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9478 player->GameOver = TRUE;
9479 RemovePlayer(player);
9482 void RemovePlayer(struct PlayerInfo *player)
9484 int jx = player->jx, jy = player->jy;
9485 int i, found = FALSE;
9487 player->present = FALSE;
9488 player->active = FALSE;
9490 if (!ExplodeField[jx][jy])
9491 StorePlayer[jx][jy] = 0;
9493 if (player->is_moving)
9494 DrawLevelField(player->last_jx, player->last_jy);
9496 for (i = 0; i < MAX_PLAYERS; i++)
9497 if (stored_player[i].active)
9501 AllPlayersGone = TRUE;
9507 #if USE_NEW_SNAP_DELAY
9508 static void setFieldForSnapping(int x, int y, int element, int direction)
9510 struct ElementInfo *ei = &element_info[element];
9511 int direction_bit = MV_DIR_BIT(direction);
9512 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
9513 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
9514 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
9516 Feld[x][y] = EL_ELEMENT_SNAPPING;
9517 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
9519 ResetGfxAnimation(x, y);
9521 GfxElement[x][y] = element;
9522 GfxAction[x][y] = action;
9523 GfxDir[x][y] = direction;
9524 GfxFrame[x][y] = -1;
9529 =============================================================================
9530 checkDiagonalPushing()
9531 -----------------------------------------------------------------------------
9532 check if diagonal input device direction results in pushing of object
9533 (by checking if the alternative direction is walkable, diggable, ...)
9534 =============================================================================
9537 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9538 int x, int y, int real_dx, int real_dy)
9540 int jx, jy, dx, dy, xx, yy;
9542 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9545 /* diagonal direction: check alternative direction */
9550 xx = jx + (dx == 0 ? real_dx : 0);
9551 yy = jy + (dy == 0 ? real_dy : 0);
9553 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9557 =============================================================================
9559 -----------------------------------------------------------------------------
9560 x, y: field next to player (non-diagonal) to try to dig to
9561 real_dx, real_dy: direction as read from input device (can be diagonal)
9562 =============================================================================
9565 int DigField(struct PlayerInfo *player,
9566 int oldx, int oldy, int x, int y,
9567 int real_dx, int real_dy, int mode)
9569 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
9570 boolean player_was_pushing = player->is_pushing;
9571 int jx = oldx, jy = oldy;
9572 int dx = x - jx, dy = y - jy;
9573 int nextx = x + dx, nexty = y + dy;
9574 int move_direction = (dx == -1 ? MV_LEFT :
9575 dx == +1 ? MV_RIGHT :
9577 dy == +1 ? MV_DOWN : MV_NONE);
9578 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9579 int dig_side = MV_DIR_OPPOSITE(move_direction);
9580 int old_element = Feld[jx][jy];
9584 if (is_player) /* function can also be called by EL_PENGUIN */
9586 if (player->MovPos == 0)
9588 player->is_digging = FALSE;
9589 player->is_collecting = FALSE;
9592 if (player->MovPos == 0) /* last pushing move finished */
9593 player->is_pushing = FALSE;
9595 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9597 player->is_switching = FALSE;
9598 player->push_delay = -1;
9600 return MF_NO_ACTION;
9604 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9605 return MF_NO_ACTION;
9607 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9608 old_element = Back[jx][jy];
9610 /* in case of element dropped at player position, check background */
9611 else if (Back[jx][jy] != EL_EMPTY &&
9612 game.engine_version >= VERSION_IDENT(2,2,0,0))
9613 old_element = Back[jx][jy];
9615 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
9616 return MF_NO_ACTION; /* field has no opening in this direction */
9618 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
9619 return MF_NO_ACTION; /* field has no opening in this direction */
9621 element = Feld[x][y];
9622 #if USE_NEW_CUSTOM_VALUE
9625 collect_count = element_info[element].collect_count_initial;
9627 collect_count = CustomValue[x][y];
9631 collect_count = element_info[element].collect_count_initial;
9635 if (element != EL_BLOCKED &&
9636 CustomValue[x][y] != element_info[element].collect_count_initial)
9637 printf("::: %d: %d != %d\n",
9640 element_info[element].collect_count_initial);
9643 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
9644 return MF_NO_ACTION;
9646 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9647 game.engine_version >= VERSION_IDENT(2,2,0,0))
9648 return MF_NO_ACTION;
9650 if (game.gravity && is_player && !player->is_auto_moving &&
9651 canFallDown(player) && move_direction != MV_DOWN &&
9652 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
9653 return MF_NO_ACTION; /* player cannot walk here due to gravity */
9655 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
9657 int sound_element = SND_ELEMENT(element);
9658 int sound_action = ACTION_WALKING;
9660 if (IS_RND_GATE(element))
9662 if (!player->key[RND_GATE_NR(element)])
9663 return MF_NO_ACTION;
9665 else if (IS_RND_GATE_GRAY(element))
9667 if (!player->key[RND_GATE_GRAY_NR(element)])
9668 return MF_NO_ACTION;
9670 else if (element == EL_EXIT_OPEN ||
9671 element == EL_SP_EXIT_OPEN ||
9672 element == EL_SP_EXIT_OPENING)
9674 sound_action = ACTION_PASSING; /* player is passing exit */
9676 else if (element == EL_EMPTY)
9678 sound_action = ACTION_MOVING; /* nothing to walk on */
9681 /* play sound from background or player, whatever is available */
9682 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
9683 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
9685 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9687 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
9689 if (!ACCESS_FROM(element, opposite_direction))
9690 return MF_NO_ACTION; /* field not accessible from this direction */
9692 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9693 return MF_NO_ACTION;
9695 if (IS_EM_GATE(element))
9697 if (!player->key[EM_GATE_NR(element)])
9698 return MF_NO_ACTION;
9700 else if (IS_EM_GATE_GRAY(element))
9702 if (!player->key[EM_GATE_GRAY_NR(element)])
9703 return MF_NO_ACTION;
9705 else if (IS_SP_PORT(element))
9707 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9708 element == EL_SP_GRAVITY_PORT_RIGHT ||
9709 element == EL_SP_GRAVITY_PORT_UP ||
9710 element == EL_SP_GRAVITY_PORT_DOWN)
9711 game.gravity = !game.gravity;
9712 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
9713 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
9714 element == EL_SP_GRAVITY_ON_PORT_UP ||
9715 element == EL_SP_GRAVITY_ON_PORT_DOWN)
9716 game.gravity = TRUE;
9717 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
9718 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
9719 element == EL_SP_GRAVITY_OFF_PORT_UP ||
9720 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
9721 game.gravity = FALSE;
9724 /* automatically move to the next field with double speed */
9725 player->programmed_action = move_direction;
9727 if (player->move_delay_reset_counter == 0)
9729 player->move_delay_reset_counter = 2; /* two double speed steps */
9731 DOUBLE_PLAYER_SPEED(player);
9734 PlayLevelSoundAction(x, y, ACTION_PASSING);
9736 else if (IS_DIGGABLE(element))
9740 if (mode != DF_SNAP)
9742 GfxElement[x][y] = GFX_ELEMENT(element);
9743 player->is_digging = TRUE;
9746 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9748 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
9749 player->index_bit, dig_side);
9751 if (mode == DF_SNAP)
9753 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9755 #if USE_NEW_SNAP_DELAY
9756 if (level.block_snap_field)
9757 setFieldForSnapping(x, y, element, move_direction);
9761 else if (IS_COLLECTIBLE(element))
9765 if (is_player && mode != DF_SNAP)
9767 GfxElement[x][y] = element;
9768 player->is_collecting = TRUE;
9771 if (element == EL_SPEED_PILL)
9773 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9775 else if (element == EL_EXTRA_TIME && level.time > 0)
9777 TimeLeft += level.extra_time;
9778 DrawGameValue_Time(TimeLeft);
9780 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9782 player->shield_normal_time_left += level.shield_normal_time;
9783 if (element == EL_SHIELD_DEADLY)
9784 player->shield_deadly_time_left += level.shield_deadly_time;
9786 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9788 if (player->inventory_size < MAX_INVENTORY_SIZE)
9789 player->inventory_element[player->inventory_size++] = element;
9791 DrawGameValue_Dynamite(local_player->inventory_size);
9793 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9795 player->dynabomb_count++;
9796 player->dynabombs_left++;
9798 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9800 player->dynabomb_size++;
9802 else if (element == EL_DYNABOMB_INCREASE_POWER)
9804 player->dynabomb_xl = TRUE;
9806 else if (IS_KEY(element))
9808 player->key[KEY_NR(element)] = TRUE;
9810 DrawGameValue_Keys(player->key);
9812 redraw_mask |= REDRAW_DOOR_1;
9814 else if (IS_ENVELOPE(element))
9816 player->show_envelope = element;
9818 else if (IS_DROPPABLE(element) ||
9819 IS_THROWABLE(element)) /* can be collected and dropped */
9823 if (collect_count == 0)
9824 player->inventory_infinite_element = element;
9826 for (i = 0; i < collect_count; i++)
9827 if (player->inventory_size < MAX_INVENTORY_SIZE)
9828 player->inventory_element[player->inventory_size++] = element;
9830 DrawGameValue_Dynamite(local_player->inventory_size);
9832 else if (collect_count > 0)
9834 local_player->gems_still_needed -= collect_count;
9835 if (local_player->gems_still_needed < 0)
9836 local_player->gems_still_needed = 0;
9838 DrawGameValue_Emeralds(local_player->gems_still_needed);
9841 RaiseScoreElement(element);
9842 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9845 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
9846 player->index_bit, dig_side);
9848 if (mode == DF_SNAP)
9850 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9852 #if USE_NEW_SNAP_DELAY
9853 if (level.block_snap_field)
9854 setFieldForSnapping(x, y, element, move_direction);
9858 else if (IS_PUSHABLE(element))
9860 if (mode == DF_SNAP && element != EL_BD_ROCK)
9861 return MF_NO_ACTION;
9863 if (CAN_FALL(element) && dy)
9864 return MF_NO_ACTION;
9866 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9867 !(element == EL_SPRING && level.use_spring_bug))
9868 return MF_NO_ACTION;
9870 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9871 ((move_direction & MV_VERTICAL &&
9872 ((element_info[element].move_pattern & MV_LEFT &&
9873 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9874 (element_info[element].move_pattern & MV_RIGHT &&
9875 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9876 (move_direction & MV_HORIZONTAL &&
9877 ((element_info[element].move_pattern & MV_UP &&
9878 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9879 (element_info[element].move_pattern & MV_DOWN &&
9880 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9881 return MF_NO_ACTION;
9883 /* do not push elements already moving away faster than player */
9884 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9885 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9886 return MF_NO_ACTION;
9888 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9890 if (player->push_delay_value == -1 || !player_was_pushing)
9891 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9893 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9895 if (player->push_delay_value == -1)
9896 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9898 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9900 if (!player->is_pushing)
9901 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9904 player->is_pushing = TRUE;
9906 if (!(IN_LEV_FIELD(nextx, nexty) &&
9907 (IS_FREE(nextx, nexty) ||
9908 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9909 IS_SB_ELEMENT(element)))))
9910 return MF_NO_ACTION;
9912 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9913 return MF_NO_ACTION;
9915 if (player->push_delay == -1) /* new pushing; restart delay */
9916 player->push_delay = 0;
9918 if (player->push_delay < player->push_delay_value &&
9919 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9920 element != EL_SPRING && element != EL_BALLOON)
9922 /* make sure that there is no move delay before next try to push */
9923 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9924 player->move_delay = 0;
9926 return MF_NO_ACTION;
9929 if (IS_SB_ELEMENT(element))
9931 if (element == EL_SOKOBAN_FIELD_FULL)
9933 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9934 local_player->sokobanfields_still_needed++;
9937 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9939 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9940 local_player->sokobanfields_still_needed--;
9943 Feld[x][y] = EL_SOKOBAN_OBJECT;
9945 if (Back[x][y] == Back[nextx][nexty])
9946 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9947 else if (Back[x][y] != 0)
9948 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9951 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9954 if (local_player->sokobanfields_still_needed == 0 &&
9955 game.emulation == EMU_SOKOBAN)
9957 player->LevelSolved = player->GameOver = TRUE;
9958 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9962 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9964 InitMovingField(x, y, move_direction);
9965 GfxAction[x][y] = ACTION_PUSHING;
9967 if (mode == DF_SNAP)
9968 ContinueMoving(x, y);
9970 MovPos[x][y] = (dx != 0 ? dx : dy);
9972 Pushed[x][y] = TRUE;
9973 Pushed[nextx][nexty] = TRUE;
9975 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9976 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9978 player->push_delay_value = -1; /* get new value later */
9980 /* check for element change _after_ element has been pushed */
9981 if (game.use_change_when_pushing_bug)
9983 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
9984 player->index_bit, dig_side);
9985 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
9986 player->index_bit, dig_side);
9989 else if (IS_SWITCHABLE(element))
9991 if (PLAYER_SWITCHING(player, x, y))
9993 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
9994 player->index_bit, dig_side);
9999 player->is_switching = TRUE;
10000 player->switch_x = x;
10001 player->switch_y = y;
10003 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10005 if (element == EL_ROBOT_WHEEL)
10007 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10011 DrawLevelField(x, y);
10013 else if (element == EL_SP_TERMINAL)
10017 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
10019 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10021 else if (Feld[xx][yy] == EL_SP_TERMINAL)
10022 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10025 else if (IS_BELT_SWITCH(element))
10027 ToggleBeltSwitch(x, y);
10029 else if (element == EL_SWITCHGATE_SWITCH_UP ||
10030 element == EL_SWITCHGATE_SWITCH_DOWN)
10032 ToggleSwitchgateSwitch(x, y);
10034 else if (element == EL_LIGHT_SWITCH ||
10035 element == EL_LIGHT_SWITCH_ACTIVE)
10037 ToggleLightSwitch(x, y);
10039 else if (element == EL_TIMEGATE_SWITCH)
10041 ActivateTimegateSwitch(x, y);
10043 else if (element == EL_BALLOON_SWITCH_LEFT ||
10044 element == EL_BALLOON_SWITCH_RIGHT ||
10045 element == EL_BALLOON_SWITCH_UP ||
10046 element == EL_BALLOON_SWITCH_DOWN ||
10047 element == EL_BALLOON_SWITCH_NONE ||
10048 element == EL_BALLOON_SWITCH_ANY)
10050 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10051 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10052 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10053 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10054 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
10057 else if (element == EL_LAMP)
10059 Feld[x][y] = EL_LAMP_ACTIVE;
10060 local_player->lights_still_needed--;
10062 ResetGfxAnimation(x, y);
10063 DrawLevelField(x, y);
10065 else if (element == EL_TIME_ORB_FULL)
10067 Feld[x][y] = EL_TIME_ORB_EMPTY;
10069 if (level.time > 0 || level.use_time_orb_bug)
10071 TimeLeft += level.time_orb_time;
10072 DrawGameValue_Time(TimeLeft);
10075 ResetGfxAnimation(x, y);
10076 DrawLevelField(x, y);
10079 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10080 player->index_bit, dig_side);
10082 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10083 player->index_bit, dig_side);
10089 if (!PLAYER_SWITCHING(player, x, y))
10091 player->is_switching = TRUE;
10092 player->switch_x = x;
10093 player->switch_y = y;
10095 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
10096 player->index_bit, dig_side);
10097 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10098 player->index_bit, dig_side);
10101 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10102 player->index_bit, dig_side);
10103 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10104 player->index_bit, dig_side);
10106 return MF_NO_ACTION;
10109 player->push_delay = -1;
10111 if (is_player) /* function can also be called by EL_PENGUIN */
10113 if (Feld[x][y] != element) /* really digged/collected something */
10114 player->is_collecting = !player->is_digging;
10120 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10122 int jx = player->jx, jy = player->jy;
10123 int x = jx + dx, y = jy + dy;
10124 int snap_direction = (dx == -1 ? MV_LEFT :
10125 dx == +1 ? MV_RIGHT :
10127 dy == +1 ? MV_DOWN : MV_NONE);
10129 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
10132 if (!player->active || !IN_LEV_FIELD(x, y))
10140 if (player->MovPos == 0)
10141 player->is_pushing = FALSE;
10143 player->is_snapping = FALSE;
10145 if (player->MovPos == 0)
10147 player->is_moving = FALSE;
10148 player->is_digging = FALSE;
10149 player->is_collecting = FALSE;
10155 if (player->is_snapping)
10158 player->MovDir = snap_direction;
10160 if (player->MovPos == 0)
10162 player->is_moving = FALSE;
10163 player->is_digging = FALSE;
10164 player->is_collecting = FALSE;
10167 player->is_dropping = FALSE;
10169 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10172 player->is_snapping = TRUE;
10174 if (player->MovPos == 0)
10176 player->is_moving = FALSE;
10177 player->is_digging = FALSE;
10178 player->is_collecting = FALSE;
10181 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
10182 DrawLevelField(player->last_jx, player->last_jy);
10184 DrawLevelField(x, y);
10189 boolean DropElement(struct PlayerInfo *player)
10191 int old_element, new_element;
10192 int dropx = player->jx, dropy = player->jy;
10193 int drop_direction = player->MovDir;
10194 int drop_side = drop_direction;
10195 int drop_element = (player->inventory_size > 0 ?
10196 player->inventory_element[player->inventory_size - 1] :
10197 player->inventory_infinite_element != EL_UNDEFINED ?
10198 player->inventory_infinite_element :
10199 player->dynabombs_left > 0 ?
10200 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10203 /* do not drop an element on top of another element; when holding drop key
10204 pressed without moving, dropped element must move away before the next
10205 element can be dropped (this is especially important if the next element
10206 is dynamite, which can be placed on background for historical reasons) */
10207 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
10210 if (IS_THROWABLE(drop_element))
10212 dropx += GET_DX_FROM_DIR(drop_direction);
10213 dropy += GET_DY_FROM_DIR(drop_direction);
10215 if (!IN_LEV_FIELD(dropx, dropy))
10219 old_element = Feld[dropx][dropy]; /* old element at dropping position */
10220 new_element = drop_element; /* default: no change when dropping */
10222 /* check if player is active, not moving and ready to drop */
10223 if (!player->active || player->MovPos || player->drop_delay > 0)
10226 /* check if player has anything that can be dropped */
10227 if (new_element == EL_UNDEFINED)
10230 /* check if anything can be dropped at the current position */
10231 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10234 /* collected custom elements can only be dropped on empty fields */
10235 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10238 if (old_element != EL_EMPTY)
10239 Back[dropx][dropy] = old_element; /* store old element on this field */
10241 ResetGfxAnimation(dropx, dropy);
10242 ResetRandomAnimationValue(dropx, dropy);
10244 if (player->inventory_size > 0 ||
10245 player->inventory_infinite_element != EL_UNDEFINED)
10247 if (player->inventory_size > 0)
10249 player->inventory_size--;
10251 DrawGameValue_Dynamite(local_player->inventory_size);
10253 if (new_element == EL_DYNAMITE)
10254 new_element = EL_DYNAMITE_ACTIVE;
10255 else if (new_element == EL_SP_DISK_RED)
10256 new_element = EL_SP_DISK_RED_ACTIVE;
10259 Feld[dropx][dropy] = new_element;
10261 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10262 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10263 el2img(Feld[dropx][dropy]), 0);
10265 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10267 /* needed if previous element just changed to "empty" in the last frame */
10268 Changed[dropx][dropy] = FALSE; /* allow another change */
10270 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
10271 player->index_bit, drop_side);
10272 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
10274 player->index_bit, drop_side);
10276 TestIfElementTouchesCustomElement(dropx, dropy);
10278 else /* player is dropping a dyna bomb */
10280 player->dynabombs_left--;
10282 Feld[dropx][dropy] = new_element;
10284 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10285 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10286 el2img(Feld[dropx][dropy]), 0);
10288 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10291 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
10292 InitField_WithBug1(dropx, dropy, FALSE);
10294 new_element = Feld[dropx][dropy]; /* element might have changed */
10296 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10297 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10299 int move_direction, nextx, nexty;
10301 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10302 MovDir[dropx][dropy] = drop_direction;
10304 move_direction = MovDir[dropx][dropy];
10305 nextx = dropx + GET_DX_FROM_DIR(move_direction);
10306 nexty = dropy + GET_DY_FROM_DIR(move_direction);
10308 Changed[dropx][dropy] = FALSE; /* allow another change */
10309 CheckCollision[dropx][dropy] = 2;
10312 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
10313 player->is_dropping = TRUE;
10315 player->drop_x = dropx;
10316 player->drop_y = dropy;
10321 /* ------------------------------------------------------------------------- */
10322 /* game sound playing functions */
10323 /* ------------------------------------------------------------------------- */
10325 static int *loop_sound_frame = NULL;
10326 static int *loop_sound_volume = NULL;
10328 void InitPlayLevelSound()
10330 int num_sounds = getSoundListSize();
10332 checked_free(loop_sound_frame);
10333 checked_free(loop_sound_volume);
10335 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10336 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10339 static void PlayLevelSound(int x, int y, int nr)
10341 int sx = SCREENX(x), sy = SCREENY(y);
10342 int volume, stereo_position;
10343 int max_distance = 8;
10344 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10346 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10347 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10350 if (!IN_LEV_FIELD(x, y) ||
10351 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10352 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10355 volume = SOUND_MAX_VOLUME;
10357 if (!IN_SCR_FIELD(sx, sy))
10359 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10360 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10362 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10365 stereo_position = (SOUND_MAX_LEFT +
10366 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10367 (SCR_FIELDX + 2 * max_distance));
10369 if (IS_LOOP_SOUND(nr))
10371 /* This assures that quieter loop sounds do not overwrite louder ones,
10372 while restarting sound volume comparison with each new game frame. */
10374 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10377 loop_sound_volume[nr] = volume;
10378 loop_sound_frame[nr] = FrameCounter;
10381 PlaySoundExt(nr, volume, stereo_position, type);
10384 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10386 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10387 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10388 y < LEVELY(BY1) ? LEVELY(BY1) :
10389 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10393 static void PlayLevelSoundAction(int x, int y, int action)
10395 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10398 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10400 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10402 if (sound_effect != SND_UNDEFINED)
10403 PlayLevelSound(x, y, sound_effect);
10406 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10409 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10411 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10412 PlayLevelSound(x, y, sound_effect);
10415 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10417 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10419 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10420 PlayLevelSound(x, y, sound_effect);
10423 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10425 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10427 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10428 StopSound(sound_effect);
10431 static void PlayLevelMusic()
10433 if (levelset.music[level_nr] != MUS_UNDEFINED)
10434 PlayMusic(levelset.music[level_nr]); /* from config file */
10436 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10439 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
10441 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
10446 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
10450 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10454 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10458 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10462 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10466 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10470 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10473 case SAMPLE_android_clone:
10474 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10477 case SAMPLE_android_move:
10478 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10481 case SAMPLE_spring:
10482 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10486 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
10490 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
10493 case SAMPLE_eater_eat:
10494 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10498 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10501 case SAMPLE_collect:
10502 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10505 case SAMPLE_diamond:
10506 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10509 case SAMPLE_squash:
10510 /* !!! CHECK THIS !!! */
10512 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10514 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
10518 case SAMPLE_wonderfall:
10519 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
10523 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10527 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10531 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10535 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
10539 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10543 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
10546 case SAMPLE_wonder:
10547 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10551 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10554 case SAMPLE_exit_open:
10555 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
10558 case SAMPLE_exit_leave:
10559 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10562 case SAMPLE_dynamite:
10563 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10567 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10571 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10575 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10579 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
10583 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
10587 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10591 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
10596 void RaiseScore(int value)
10598 local_player->score += value;
10600 DrawGameValue_Score(local_player->score);
10603 void RaiseScoreElement(int element)
10608 case EL_BD_DIAMOND:
10609 case EL_EMERALD_YELLOW:
10610 case EL_EMERALD_RED:
10611 case EL_EMERALD_PURPLE:
10612 case EL_SP_INFOTRON:
10613 RaiseScore(level.score[SC_EMERALD]);
10616 RaiseScore(level.score[SC_DIAMOND]);
10619 RaiseScore(level.score[SC_CRYSTAL]);
10622 RaiseScore(level.score[SC_PEARL]);
10625 case EL_BD_BUTTERFLY:
10626 case EL_SP_ELECTRON:
10627 RaiseScore(level.score[SC_BUG]);
10630 case EL_BD_FIREFLY:
10631 case EL_SP_SNIKSNAK:
10632 RaiseScore(level.score[SC_SPACESHIP]);
10635 case EL_DARK_YAMYAM:
10636 RaiseScore(level.score[SC_YAMYAM]);
10639 RaiseScore(level.score[SC_ROBOT]);
10642 RaiseScore(level.score[SC_PACMAN]);
10645 RaiseScore(level.score[SC_NUT]);
10648 case EL_SP_DISK_RED:
10649 case EL_DYNABOMB_INCREASE_NUMBER:
10650 case EL_DYNABOMB_INCREASE_SIZE:
10651 case EL_DYNABOMB_INCREASE_POWER:
10652 RaiseScore(level.score[SC_DYNAMITE]);
10654 case EL_SHIELD_NORMAL:
10655 case EL_SHIELD_DEADLY:
10656 RaiseScore(level.score[SC_SHIELD]);
10658 case EL_EXTRA_TIME:
10659 RaiseScore(level.score[SC_TIME_BONUS]);
10673 RaiseScore(level.score[SC_KEY]);
10676 RaiseScore(element_info[element].collect_score);
10681 void RequestQuitGame(boolean ask_if_really_quit)
10683 if (AllPlayersGone ||
10684 !ask_if_really_quit ||
10685 level_editor_test_game ||
10686 Request("Do you really want to quit the game ?",
10687 REQ_ASK | REQ_STAY_CLOSED))
10689 #if defined(NETWORK_AVALIABLE)
10690 if (options.network)
10691 SendToServer_StopPlaying();
10695 game_status = GAME_MODE_MAIN;
10701 if (tape.playing && tape.deactivate_display)
10702 TapeDeactivateDisplayOff(TRUE);
10704 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10706 if (tape.playing && tape.deactivate_display)
10707 TapeDeactivateDisplayOn();
10712 /* ---------- new game button stuff ---------------------------------------- */
10714 /* graphic position values for game buttons */
10715 #define GAME_BUTTON_XSIZE 30
10716 #define GAME_BUTTON_YSIZE 30
10717 #define GAME_BUTTON_XPOS 5
10718 #define GAME_BUTTON_YPOS 215
10719 #define SOUND_BUTTON_XPOS 5
10720 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10722 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10723 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10724 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10725 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10726 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10727 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10734 } gamebutton_info[NUM_GAME_BUTTONS] =
10737 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10742 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10743 GAME_CTRL_ID_PAUSE,
10747 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10752 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10753 SOUND_CTRL_ID_MUSIC,
10754 "background music on/off"
10757 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10758 SOUND_CTRL_ID_LOOPS,
10759 "sound loops on/off"
10762 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10763 SOUND_CTRL_ID_SIMPLE,
10764 "normal sounds on/off"
10768 void CreateGameButtons()
10772 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10774 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10775 struct GadgetInfo *gi;
10778 unsigned long event_mask;
10779 int gd_xoffset, gd_yoffset;
10780 int gd_x1, gd_x2, gd_y1, gd_y2;
10783 gd_xoffset = gamebutton_info[i].x;
10784 gd_yoffset = gamebutton_info[i].y;
10785 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10786 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10788 if (id == GAME_CTRL_ID_STOP ||
10789 id == GAME_CTRL_ID_PAUSE ||
10790 id == GAME_CTRL_ID_PLAY)
10792 button_type = GD_TYPE_NORMAL_BUTTON;
10794 event_mask = GD_EVENT_RELEASED;
10795 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10796 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10800 button_type = GD_TYPE_CHECK_BUTTON;
10802 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10803 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10804 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10805 event_mask = GD_EVENT_PRESSED;
10806 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10807 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10810 gi = CreateGadget(GDI_CUSTOM_ID, id,
10811 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10812 GDI_X, DX + gd_xoffset,
10813 GDI_Y, DY + gd_yoffset,
10814 GDI_WIDTH, GAME_BUTTON_XSIZE,
10815 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10816 GDI_TYPE, button_type,
10817 GDI_STATE, GD_BUTTON_UNPRESSED,
10818 GDI_CHECKED, checked,
10819 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10820 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10821 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10822 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10823 GDI_EVENT_MASK, event_mask,
10824 GDI_CALLBACK_ACTION, HandleGameButtons,
10828 Error(ERR_EXIT, "cannot create gadget");
10830 game_gadget[id] = gi;
10834 void FreeGameButtons()
10838 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10839 FreeGadget(game_gadget[i]);
10842 static void MapGameButtons()
10846 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10847 MapGadget(game_gadget[i]);
10850 void UnmapGameButtons()
10854 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10855 UnmapGadget(game_gadget[i]);
10858 static void HandleGameButtons(struct GadgetInfo *gi)
10860 int id = gi->custom_id;
10862 if (game_status != GAME_MODE_PLAYING)
10867 case GAME_CTRL_ID_STOP:
10868 RequestQuitGame(TRUE);
10871 case GAME_CTRL_ID_PAUSE:
10872 if (options.network)
10874 #if defined(NETWORK_AVALIABLE)
10876 SendToServer_ContinuePlaying();
10878 SendToServer_PausePlaying();
10882 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10885 case GAME_CTRL_ID_PLAY:
10888 #if defined(NETWORK_AVALIABLE)
10889 if (options.network)
10890 SendToServer_ContinuePlaying();
10894 tape.pausing = FALSE;
10895 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10900 case SOUND_CTRL_ID_MUSIC:
10901 if (setup.sound_music)
10903 setup.sound_music = FALSE;
10906 else if (audio.music_available)
10908 setup.sound = setup.sound_music = TRUE;
10910 SetAudioMode(setup.sound);
10916 case SOUND_CTRL_ID_LOOPS:
10917 if (setup.sound_loops)
10918 setup.sound_loops = FALSE;
10919 else if (audio.loops_available)
10921 setup.sound = setup.sound_loops = TRUE;
10922 SetAudioMode(setup.sound);
10926 case SOUND_CTRL_ID_SIMPLE:
10927 if (setup.sound_simple)
10928 setup.sound_simple = FALSE;
10929 else if (audio.sound_available)
10931 setup.sound = setup.sound_simple = TRUE;
10932 SetAudioMode(setup.sound);