1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
43 /* for MovePlayer() */
44 #define MF_NO_ACTION 0
48 /* for ScrollPlayer() */
50 #define SCROLL_GO_ON 1
53 #define EX_PHASE_START 0
54 #define EX_TYPE_NONE 0
55 #define EX_TYPE_NORMAL (1 << 0)
56 #define EX_TYPE_CENTER (1 << 1)
57 #define EX_TYPE_BORDER (1 << 2)
58 #define EX_TYPE_CROSS (1 << 3)
59 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
61 /* special positions in the game control window (relative to control window) */
64 #define XX_EMERALDS 29
65 #define YY_EMERALDS 54
66 #define XX_DYNAMITE 29
67 #define YY_DYNAMITE 89
76 /* special positions in the game control window (relative to main window) */
77 #define DX_LEVEL (DX + XX_LEVEL)
78 #define DY_LEVEL (DY + YY_LEVEL)
79 #define DX_EMERALDS (DX + XX_EMERALDS)
80 #define DY_EMERALDS (DY + YY_EMERALDS)
81 #define DX_DYNAMITE (DX + XX_DYNAMITE)
82 #define DY_DYNAMITE (DY + YY_DYNAMITE)
83 #define DX_KEYS (DX + XX_KEYS)
84 #define DY_KEYS (DY + YY_KEYS)
85 #define DX_SCORE (DX + XX_SCORE)
86 #define DY_SCORE (DY + YY_SCORE)
87 #define DX_TIME1 (DX + XX_TIME1)
88 #define DX_TIME2 (DX + XX_TIME2)
89 #define DY_TIME (DY + YY_TIME)
91 /* values for initial player move delay (initial delay counter value) */
92 #define INITIAL_MOVE_DELAY_OFF -1
93 #define INITIAL_MOVE_DELAY_ON 0
95 /* values for player movement speed (which is in fact a delay value) */
96 #define MOVE_DELAY_MIN_SPEED 32
97 #define MOVE_DELAY_NORMAL_SPEED 8
98 #define MOVE_DELAY_HIGH_SPEED 4
99 #define MOVE_DELAY_MAX_SPEED 1
102 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
103 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
105 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
106 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
108 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
109 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
111 /* values for other actions */
112 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
113 #define MOVE_STEPSIZE_MIN (1)
114 #define MOVE_STEPSIZE_MAX (TILEX)
116 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
117 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
119 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
121 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
122 RND(element_info[e].push_delay_random))
123 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
124 RND(element_info[e].drop_delay_random))
125 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
126 RND(element_info[e].move_delay_random))
127 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
128 (element_info[e].move_delay_random))
129 #define GET_NEW_CUSTOM_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
130 RND(element_info[e].ce_value_random_initial))
131 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
132 RND((c)->delay_random * (c)->delay_frames))
134 #define GET_TARGET_ELEMENT(e, ch) \
135 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
136 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
138 #define CAN_GROW_INTO(e) \
139 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
141 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
142 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
145 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
146 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
147 (CAN_MOVE_INTO_ACID(e) && \
148 Feld[x][y] == EL_ACID) || \
151 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
152 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
153 (CAN_MOVE_INTO_ACID(e) && \
154 Feld[x][y] == EL_ACID) || \
157 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
158 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
160 (CAN_MOVE_INTO_ACID(e) && \
161 Feld[x][y] == EL_ACID) || \
162 (DONT_COLLIDE_WITH(e) && \
164 !PLAYER_ENEMY_PROTECTED(x, y))))
166 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
167 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
169 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
170 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
172 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
173 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
175 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
176 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
178 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
179 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
181 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
182 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
184 #define PIG_CAN_ENTER_FIELD(e, x, y) \
185 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
187 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
188 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
189 IS_FOOD_PENGUIN(Feld[x][y])))
190 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
191 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
193 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
194 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
196 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
197 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
199 #define GROUP_NR(e) ((e) - EL_GROUP_START)
200 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
201 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
202 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
204 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
205 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
207 #define CE_ENTER_FIELD_COND(e, x, y) \
208 (!IS_PLAYER(x, y) && \
209 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
211 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
212 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
214 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
215 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
217 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
218 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
219 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
220 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
222 /* game button identifiers */
223 #define GAME_CTRL_ID_STOP 0
224 #define GAME_CTRL_ID_PAUSE 1
225 #define GAME_CTRL_ID_PLAY 2
226 #define SOUND_CTRL_ID_MUSIC 3
227 #define SOUND_CTRL_ID_LOOPS 4
228 #define SOUND_CTRL_ID_SIMPLE 5
230 #define NUM_GAME_BUTTONS 6
233 /* forward declaration for internal use */
235 static void AdvanceFrameAndPlayerCounters(int);
237 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
238 static boolean MovePlayer(struct PlayerInfo *, int, int);
239 static void ScrollPlayer(struct PlayerInfo *, int);
240 static void ScrollScreen(struct PlayerInfo *, int);
242 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
244 static void InitBeltMovement(void);
245 static void CloseAllOpenTimegates(void);
246 static void CheckGravityMovement(struct PlayerInfo *);
247 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
248 static void KillPlayerUnlessEnemyProtected(int, int);
249 static void KillPlayerUnlessExplosionProtected(int, int);
251 static void TestIfPlayerTouchesCustomElement(int, int);
252 static void TestIfElementTouchesCustomElement(int, int);
253 static void TestIfElementHitsCustomElement(int, int, int);
255 static void TestIfElementSmashesCustomElement(int, int, int);
258 static void ChangeElement(int, int, int);
260 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
261 #define CheckTriggeredElementChange(x, y, e, ev) \
262 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
263 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
264 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
265 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
266 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
267 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
268 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
270 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
271 #define CheckElementChange(x, y, e, te, ev) \
272 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
273 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
274 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
275 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
276 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
278 static void PlayLevelSound(int, int, int);
279 static void PlayLevelSoundNearest(int, int, int);
280 static void PlayLevelSoundAction(int, int, int);
281 static void PlayLevelSoundElementAction(int, int, int, int);
282 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
283 static void PlayLevelSoundActionIfLoop(int, int, int);
284 static void StopLevelSoundActionIfLoop(int, int, int);
285 static void PlayLevelMusic();
287 static void MapGameButtons();
288 static void HandleGameButtons(struct GadgetInfo *);
290 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
293 /* ------------------------------------------------------------------------- */
294 /* definition of elements that automatically change to other elements after */
295 /* a specified time, eventually calling a function when changing */
296 /* ------------------------------------------------------------------------- */
298 /* forward declaration for changer functions */
299 static void InitBuggyBase(int x, int y);
300 static void WarnBuggyBase(int x, int y);
302 static void InitTrap(int x, int y);
303 static void ActivateTrap(int x, int y);
304 static void ChangeActiveTrap(int x, int y);
306 static void InitRobotWheel(int x, int y);
307 static void RunRobotWheel(int x, int y);
308 static void StopRobotWheel(int x, int y);
310 static void InitTimegateWheel(int x, int y);
311 static void RunTimegateWheel(int x, int y);
313 struct ChangingElementInfo
318 void (*pre_change_function)(int x, int y);
319 void (*change_function)(int x, int y);
320 void (*post_change_function)(int x, int y);
323 static struct ChangingElementInfo change_delay_list[] =
374 EL_SWITCHGATE_OPENING,
382 EL_SWITCHGATE_CLOSING,
383 EL_SWITCHGATE_CLOSED,
415 EL_ACID_SPLASH_RIGHT,
424 EL_SP_BUGGY_BASE_ACTIVATING,
431 EL_SP_BUGGY_BASE_ACTIVATING,
432 EL_SP_BUGGY_BASE_ACTIVE,
439 EL_SP_BUGGY_BASE_ACTIVE,
463 EL_ROBOT_WHEEL_ACTIVE,
471 EL_TIMEGATE_SWITCH_ACTIVE,
492 int push_delay_fixed, push_delay_random;
497 { EL_BALLOON, 0, 0 },
499 { EL_SOKOBAN_OBJECT, 2, 0 },
500 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
501 { EL_SATELLITE, 2, 0 },
502 { EL_SP_DISK_YELLOW, 2, 0 },
504 { EL_UNDEFINED, 0, 0 },
512 move_stepsize_list[] =
514 { EL_AMOEBA_DROP, 2 },
515 { EL_AMOEBA_DROPPING, 2 },
516 { EL_QUICKSAND_FILLING, 1 },
517 { EL_QUICKSAND_EMPTYING, 1 },
518 { EL_MAGIC_WALL_FILLING, 2 },
519 { EL_BD_MAGIC_WALL_FILLING, 2 },
520 { EL_MAGIC_WALL_EMPTYING, 2 },
521 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
531 collect_count_list[] =
534 { EL_BD_DIAMOND, 1 },
535 { EL_EMERALD_YELLOW, 1 },
536 { EL_EMERALD_RED, 1 },
537 { EL_EMERALD_PURPLE, 1 },
539 { EL_SP_INFOTRON, 1 },
551 access_direction_list[] =
553 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
554 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
555 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
556 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
557 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
558 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
559 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
560 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
561 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
562 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
563 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
565 { EL_SP_PORT_LEFT, MV_RIGHT },
566 { EL_SP_PORT_RIGHT, MV_LEFT },
567 { EL_SP_PORT_UP, MV_DOWN },
568 { EL_SP_PORT_DOWN, MV_UP },
569 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
570 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
571 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
572 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
573 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
574 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
575 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
576 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
577 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
578 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
579 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
580 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
581 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
582 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
583 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
585 { EL_UNDEFINED, MV_NONE }
588 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
590 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
591 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
592 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
593 IS_JUST_CHANGING(x, y))
595 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
598 void GetPlayerConfig()
600 if (!audio.sound_available)
601 setup.sound_simple = FALSE;
603 if (!audio.loops_available)
604 setup.sound_loops = FALSE;
606 if (!audio.music_available)
607 setup.sound_music = FALSE;
609 if (!video.fullscreen_available)
610 setup.fullscreen = FALSE;
612 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
614 SetAudioMode(setup.sound);
618 static int getBeltNrFromBeltElement(int element)
620 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
621 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
622 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
625 static int getBeltNrFromBeltActiveElement(int element)
627 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
628 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
629 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
632 static int getBeltNrFromBeltSwitchElement(int element)
634 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
635 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
636 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
639 static int getBeltDirNrFromBeltSwitchElement(int element)
641 static int belt_base_element[4] =
643 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
644 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
645 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
646 EL_CONVEYOR_BELT_4_SWITCH_LEFT
649 int belt_nr = getBeltNrFromBeltSwitchElement(element);
650 int belt_dir_nr = element - belt_base_element[belt_nr];
652 return (belt_dir_nr % 3);
655 static int getBeltDirFromBeltSwitchElement(int element)
657 static int belt_move_dir[3] =
664 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
666 return belt_move_dir[belt_dir_nr];
669 static void InitPlayerField(int x, int y, int element, boolean init_game)
671 if (element == EL_SP_MURPHY)
675 if (stored_player[0].present)
677 Feld[x][y] = EL_SP_MURPHY_CLONE;
683 stored_player[0].use_murphy_graphic = TRUE;
686 Feld[x][y] = EL_PLAYER_1;
692 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
693 int jx = player->jx, jy = player->jy;
695 player->present = TRUE;
697 player->block_last_field = (element == EL_SP_MURPHY ?
698 level.sp_block_last_field :
699 level.block_last_field);
701 /* ---------- initialize player's last field block delay --------------- */
703 /* always start with reliable default value (no adjustment needed) */
704 player->block_delay_adjustment = 0;
706 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
707 if (player->block_last_field && element == EL_SP_MURPHY)
708 player->block_delay_adjustment = 1;
710 /* special case 2: in game engines before 3.1.1, blocking was different */
711 if (game.use_block_last_field_bug)
712 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
714 if (!options.network || player->connected)
716 player->active = TRUE;
718 /* remove potentially duplicate players */
719 if (StorePlayer[jx][jy] == Feld[x][y])
720 StorePlayer[jx][jy] = 0;
722 StorePlayer[x][y] = Feld[x][y];
726 printf("Player %d activated.\n", player->element_nr);
727 printf("[Local player is %d and currently %s.]\n",
728 local_player->element_nr,
729 local_player->active ? "active" : "not active");
733 Feld[x][y] = EL_EMPTY;
735 player->jx = player->last_jx = x;
736 player->jy = player->last_jy = y;
740 static void InitField(int x, int y, boolean init_game)
742 int element = Feld[x][y];
751 InitPlayerField(x, y, element, init_game);
754 case EL_SOKOBAN_FIELD_PLAYER:
755 element = Feld[x][y] = EL_PLAYER_1;
756 InitField(x, y, init_game);
758 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
759 InitField(x, y, init_game);
762 case EL_SOKOBAN_FIELD_EMPTY:
763 local_player->sokobanfields_still_needed++;
767 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
768 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
769 else if (x > 0 && Feld[x-1][y] == EL_ACID)
770 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
771 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
772 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
773 else if (y > 0 && Feld[x][y-1] == EL_ACID)
774 Feld[x][y] = EL_ACID_POOL_BOTTOM;
775 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
776 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
784 case EL_SPACESHIP_RIGHT:
785 case EL_SPACESHIP_UP:
786 case EL_SPACESHIP_LEFT:
787 case EL_SPACESHIP_DOWN:
789 case EL_BD_BUTTERFLY_RIGHT:
790 case EL_BD_BUTTERFLY_UP:
791 case EL_BD_BUTTERFLY_LEFT:
792 case EL_BD_BUTTERFLY_DOWN:
793 case EL_BD_BUTTERFLY:
794 case EL_BD_FIREFLY_RIGHT:
795 case EL_BD_FIREFLY_UP:
796 case EL_BD_FIREFLY_LEFT:
797 case EL_BD_FIREFLY_DOWN:
799 case EL_PACMAN_RIGHT:
823 if (y == lev_fieldy - 1)
825 Feld[x][y] = EL_AMOEBA_GROWING;
826 Store[x][y] = EL_AMOEBA_WET;
830 case EL_DYNAMITE_ACTIVE:
831 case EL_SP_DISK_RED_ACTIVE:
832 case EL_DYNABOMB_PLAYER_1_ACTIVE:
833 case EL_DYNABOMB_PLAYER_2_ACTIVE:
834 case EL_DYNABOMB_PLAYER_3_ACTIVE:
835 case EL_DYNABOMB_PLAYER_4_ACTIVE:
840 local_player->lights_still_needed++;
844 local_player->friends_still_needed++;
849 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
852 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
853 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
854 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
855 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
856 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
857 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
858 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
859 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
860 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
861 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
862 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
863 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
866 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
867 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
868 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
870 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
872 game.belt_dir[belt_nr] = belt_dir;
873 game.belt_dir_nr[belt_nr] = belt_dir_nr;
875 else /* more than one switch -- set it like the first switch */
877 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
882 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
884 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
887 case EL_LIGHT_SWITCH_ACTIVE:
889 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
894 if (IS_CUSTOM_ELEMENT(element))
896 if (CAN_MOVE(element))
899 #if USE_NEW_CUSTOM_VALUE
900 if (!element_info[element].use_last_ce_value)
901 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
905 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
908 else if (IS_GROUP_ELEMENT(element))
910 struct ElementGroupInfo *group = element_info[element].group;
911 int last_anim_random_frame = gfx.anim_random_frame;
914 if (group->choice_mode == ANIM_RANDOM)
915 gfx.anim_random_frame = RND(group->num_elements_resolved);
917 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
918 group->choice_mode, 0,
921 if (group->choice_mode == ANIM_RANDOM)
922 gfx.anim_random_frame = last_anim_random_frame;
926 Feld[x][y] = group->element_resolved[element_pos];
928 InitField(x, y, init_game);
935 #if USE_NEW_CUSTOM_VALUE
938 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
940 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
948 static inline void InitField_WithBug1(int x, int y, boolean init_game)
950 InitField(x, y, init_game);
952 /* not needed to call InitMovDir() -- already done by InitField()! */
953 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
954 CAN_MOVE(Feld[x][y]))
958 static inline void InitField_WithBug2(int x, int y, boolean init_game)
960 int old_element = Feld[x][y];
962 InitField(x, y, init_game);
964 /* not needed to call InitMovDir() -- already done by InitField()! */
965 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
966 CAN_MOVE(old_element) &&
967 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
970 /* this case is in fact a combination of not less than three bugs:
971 first, it calls InitMovDir() for elements that can move, although this is
972 already done by InitField(); then, it checks the element that was at this
973 field _before_ the call to InitField() (which can change it); lastly, it
974 was not called for "mole with direction" elements, which were treated as
975 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
979 inline void DrawGameValue_Emeralds(int value)
981 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
984 inline void DrawGameValue_Dynamite(int value)
986 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
989 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
993 /* currently only 4 of 8 possible keys are displayed */
994 for (i = 0; i < STD_NUM_KEYS; i++)
997 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
998 el2edimg(EL_KEY_1 + i));
1000 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1001 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1002 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1006 inline void DrawGameValue_Score(int value)
1008 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1011 inline void DrawGameValue_Time(int value)
1014 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1016 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1019 inline void DrawGameValue_Level(int value)
1022 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1025 /* misuse area for displaying emeralds to draw bigger level number */
1026 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1027 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1029 /* now copy it to the area for displaying level number */
1030 BlitBitmap(drawto, drawto,
1031 DX_EMERALDS, DY_EMERALDS + 1,
1032 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1033 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1034 DX_LEVEL - 1, DY_LEVEL + 1);
1036 /* restore the area for displaying emeralds */
1037 DrawGameValue_Emeralds(local_player->gems_still_needed);
1039 /* yes, this is all really ugly :-) */
1043 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1046 int key[MAX_NUM_KEYS];
1049 for (i = 0; i < MAX_NUM_KEYS; i++)
1050 key[i] = key_bits & (1 << i);
1052 DrawGameValue_Level(level_nr);
1054 DrawGameValue_Emeralds(emeralds);
1055 DrawGameValue_Dynamite(dynamite);
1056 DrawGameValue_Score(score);
1057 DrawGameValue_Time(time);
1059 DrawGameValue_Keys(key);
1062 void DrawGameDoorValues()
1066 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1068 DrawGameDoorValues_EM();
1073 DrawGameValue_Level(level_nr);
1075 DrawGameValue_Emeralds(local_player->gems_still_needed);
1076 DrawGameValue_Dynamite(local_player->inventory_size);
1077 DrawGameValue_Score(local_player->score);
1078 DrawGameValue_Time(TimeLeft);
1080 for (i = 0; i < MAX_PLAYERS; i++)
1081 DrawGameValue_Keys(stored_player[i].key);
1084 static void resolve_group_element(int group_element, int recursion_depth)
1086 static int group_nr;
1087 static struct ElementGroupInfo *group;
1088 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1091 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1093 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1094 group_element - EL_GROUP_START + 1);
1096 /* replace element which caused too deep recursion by question mark */
1097 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1102 if (recursion_depth == 0) /* initialization */
1104 group = element_info[group_element].group;
1105 group_nr = group_element - EL_GROUP_START;
1107 group->num_elements_resolved = 0;
1108 group->choice_pos = 0;
1111 for (i = 0; i < actual_group->num_elements; i++)
1113 int element = actual_group->element[i];
1115 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1118 if (IS_GROUP_ELEMENT(element))
1119 resolve_group_element(element, recursion_depth + 1);
1122 group->element_resolved[group->num_elements_resolved++] = element;
1123 element_info[element].in_group[group_nr] = TRUE;
1130 =============================================================================
1132 -----------------------------------------------------------------------------
1133 initialize game engine due to level / tape version number
1134 =============================================================================
1137 static void InitGameEngine()
1141 /* set game engine from tape file when re-playing, else from level file */
1142 game.engine_version = (tape.playing ? tape.engine_version :
1143 level.game_version);
1145 /* ---------------------------------------------------------------------- */
1146 /* set flags for bugs and changes according to active game engine version */
1147 /* ---------------------------------------------------------------------- */
1150 Summary of bugfix/change:
1151 Fixed handling for custom elements that change when pushed by the player.
1153 Fixed/changed in version:
1157 Before 3.1.0, custom elements that "change when pushing" changed directly
1158 after the player started pushing them (until then handled in "DigField()").
1159 Since 3.1.0, these custom elements are not changed until the "pushing"
1160 move of the element is finished (now handled in "ContinueMoving()").
1162 Affected levels/tapes:
1163 The first condition is generally needed for all levels/tapes before version
1164 3.1.0, which might use the old behaviour before it was changed; known tapes
1165 that are affected are some tapes from the level set "Walpurgis Gardens" by
1167 The second condition is an exception from the above case and is needed for
1168 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1169 above (including some development versions of 3.1.0), but before it was
1170 known that this change would break tapes like the above and was fixed in
1171 3.1.1, so that the changed behaviour was active although the engine version
1172 while recording maybe was before 3.1.0. There is at least one tape that is
1173 affected by this exception, which is the tape for the one-level set "Bug
1174 Machine" by Juergen Bonhagen.
1177 game.use_change_when_pushing_bug =
1178 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1180 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1181 tape.game_version < VERSION_IDENT(3,1,1,0)));
1184 Summary of bugfix/change:
1185 Fixed handling for blocking the field the player leaves when moving.
1187 Fixed/changed in version:
1191 Before 3.1.1, when "block last field when moving" was enabled, the field
1192 the player is leaving when moving was blocked for the time of the move,
1193 and was directly unblocked afterwards. This resulted in the last field
1194 being blocked for exactly one less than the number of frames of one player
1195 move. Additionally, even when blocking was disabled, the last field was
1196 blocked for exactly one frame.
1197 Since 3.1.1, due to changes in player movement handling, the last field
1198 is not blocked at all when blocking is disabled. When blocking is enabled,
1199 the last field is blocked for exactly the number of frames of one player
1200 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1201 last field is blocked for exactly one more than the number of frames of
1204 Affected levels/tapes:
1205 (!!! yet to be determined -- probably many !!!)
1208 game.use_block_last_field_bug =
1209 (game.engine_version < VERSION_IDENT(3,1,1,0));
1211 /* ---------------------------------------------------------------------- */
1213 /* dynamically adjust element properties according to game engine version */
1214 InitElementPropertiesEngine(game.engine_version);
1217 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1218 printf(" tape version == %06d [%s] [file: %06d]\n",
1219 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1221 printf(" => game.engine_version == %06d\n", game.engine_version);
1224 /* ---------- recursively resolve group elements ------------------------- */
1226 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1227 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1228 element_info[i].in_group[j] = FALSE;
1230 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1231 resolve_group_element(EL_GROUP_START + i, 0);
1233 /* ---------- initialize player's initial move delay --------------------- */
1235 /* dynamically adjust player properties according to level information */
1236 game.initial_move_delay_value =
1237 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1239 /* dynamically adjust player properties according to game engine version */
1240 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1241 game.initial_move_delay_value : 0);
1243 /* ---------- initialize player's initial push delay --------------------- */
1245 /* dynamically adjust player properties according to game engine version */
1246 game.initial_push_delay_value =
1247 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1249 /* ---------- initialize changing elements ------------------------------- */
1251 /* initialize changing elements information */
1252 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1254 struct ElementInfo *ei = &element_info[i];
1256 /* this pointer might have been changed in the level editor */
1257 ei->change = &ei->change_page[0];
1259 if (!IS_CUSTOM_ELEMENT(i))
1261 ei->change->target_element = EL_EMPTY_SPACE;
1262 ei->change->delay_fixed = 0;
1263 ei->change->delay_random = 0;
1264 ei->change->delay_frames = 1;
1267 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1269 ei->has_change_event[j] = FALSE;
1271 ei->event_page_nr[j] = 0;
1272 ei->event_page[j] = &ei->change_page[0];
1276 /* add changing elements from pre-defined list */
1277 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1279 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1280 struct ElementInfo *ei = &element_info[ch_delay->element];
1282 ei->change->target_element = ch_delay->target_element;
1283 ei->change->delay_fixed = ch_delay->change_delay;
1285 ei->change->pre_change_function = ch_delay->pre_change_function;
1286 ei->change->change_function = ch_delay->change_function;
1287 ei->change->post_change_function = ch_delay->post_change_function;
1289 ei->change->can_change = TRUE;
1290 ei->change->can_change_or_has_action = TRUE;
1292 ei->has_change_event[CE_DELAY] = TRUE;
1294 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1295 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1298 /* ---------- initialize internal run-time variables ------------- */
1300 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1302 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1304 for (j = 0; j < ei->num_change_pages; j++)
1306 ei->change_page[j].can_change_or_has_action =
1307 (ei->change_page[j].can_change |
1308 ei->change_page[j].has_action);
1312 /* add change events from custom element configuration */
1313 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1315 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1317 for (j = 0; j < ei->num_change_pages; j++)
1319 if (!ei->change_page[j].can_change_or_has_action)
1322 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1324 /* only add event page for the first page found with this event */
1325 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1327 ei->has_change_event[k] = TRUE;
1329 ei->event_page_nr[k] = j;
1330 ei->event_page[k] = &ei->change_page[j];
1336 /* ---------- initialize run-time trigger player and element ------------- */
1338 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1340 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1342 for (j = 0; j < ei->num_change_pages; j++)
1344 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1345 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1346 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1347 ei->change_page[j].actual_trigger_ce_value = 0;
1351 /* ---------- initialize trigger events ---------------------------------- */
1353 /* initialize trigger events information */
1354 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1355 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1356 trigger_events[i][j] = FALSE;
1358 /* add trigger events from element change event properties */
1359 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1361 struct ElementInfo *ei = &element_info[i];
1363 for (j = 0; j < ei->num_change_pages; j++)
1365 if (!ei->change_page[j].can_change_or_has_action)
1368 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1370 int trigger_element = ei->change_page[j].trigger_element;
1372 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1374 if (ei->change_page[j].has_event[k])
1376 if (IS_GROUP_ELEMENT(trigger_element))
1378 struct ElementGroupInfo *group =
1379 element_info[trigger_element].group;
1381 for (l = 0; l < group->num_elements_resolved; l++)
1382 trigger_events[group->element_resolved[l]][k] = TRUE;
1385 trigger_events[trigger_element][k] = TRUE;
1392 /* ---------- initialize push delay -------------------------------------- */
1394 /* initialize push delay values to default */
1395 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1397 if (!IS_CUSTOM_ELEMENT(i))
1399 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1400 element_info[i].push_delay_random = game.default_push_delay_random;
1404 /* set push delay value for certain elements from pre-defined list */
1405 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1407 int e = push_delay_list[i].element;
1409 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1410 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1413 /* set push delay value for Supaplex elements for newer engine versions */
1414 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1416 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1418 if (IS_SP_ELEMENT(i))
1420 /* set SP push delay to just enough to push under a falling zonk */
1421 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1423 element_info[i].push_delay_fixed = delay;
1424 element_info[i].push_delay_random = 0;
1429 /* ---------- initialize move stepsize ----------------------------------- */
1431 /* initialize move stepsize values to default */
1432 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1433 if (!IS_CUSTOM_ELEMENT(i))
1434 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1436 /* set move stepsize value for certain elements from pre-defined list */
1437 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1439 int e = move_stepsize_list[i].element;
1441 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1444 /* ---------- initialize collect score ----------------------------------- */
1446 /* initialize collect score values for custom elements from initial value */
1447 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1448 if (IS_CUSTOM_ELEMENT(i))
1449 element_info[i].collect_score = element_info[i].collect_score_initial;
1451 /* ---------- initialize collect count ----------------------------------- */
1453 /* initialize collect count values for non-custom elements */
1454 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1455 if (!IS_CUSTOM_ELEMENT(i))
1456 element_info[i].collect_count_initial = 0;
1458 /* add collect count values for all elements from pre-defined list */
1459 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1460 element_info[collect_count_list[i].element].collect_count_initial =
1461 collect_count_list[i].count;
1463 /* ---------- initialize access direction -------------------------------- */
1465 /* initialize access direction values to default (access from every side) */
1466 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1467 if (!IS_CUSTOM_ELEMENT(i))
1468 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1470 /* set access direction value for certain elements from pre-defined list */
1471 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1472 element_info[access_direction_list[i].element].access_direction =
1473 access_direction_list[i].direction;
1478 =============================================================================
1480 -----------------------------------------------------------------------------
1481 initialize and start new game
1482 =============================================================================
1487 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1488 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1489 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1494 /* don't play tapes over network */
1495 network_playing = (options.network && !tape.playing);
1497 for (i = 0; i < MAX_PLAYERS; i++)
1499 struct PlayerInfo *player = &stored_player[i];
1501 player->index_nr = i;
1502 player->index_bit = (1 << i);
1503 player->element_nr = EL_PLAYER_1 + i;
1505 player->present = FALSE;
1506 player->active = FALSE;
1509 player->effective_action = 0;
1510 player->programmed_action = 0;
1513 player->gems_still_needed = level.gems_needed;
1514 player->sokobanfields_still_needed = 0;
1515 player->lights_still_needed = 0;
1516 player->friends_still_needed = 0;
1518 for (j = 0; j < MAX_NUM_KEYS; j++)
1519 player->key[j] = FALSE;
1521 player->dynabomb_count = 0;
1522 player->dynabomb_size = 1;
1523 player->dynabombs_left = 0;
1524 player->dynabomb_xl = FALSE;
1526 player->MovDir = MV_NONE;
1529 player->GfxDir = MV_NONE;
1530 player->GfxAction = ACTION_DEFAULT;
1532 player->StepFrame = 0;
1534 player->use_murphy_graphic = FALSE;
1536 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1537 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1539 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1541 player->actual_frame_counter = 0;
1543 player->step_counter = 0;
1545 player->last_move_dir = MV_NONE;
1547 player->is_waiting = FALSE;
1548 player->is_moving = FALSE;
1549 player->is_auto_moving = FALSE;
1550 player->is_digging = FALSE;
1551 player->is_snapping = FALSE;
1552 player->is_collecting = FALSE;
1553 player->is_pushing = FALSE;
1554 player->is_switching = FALSE;
1555 player->is_dropping = FALSE;
1557 player->is_bored = FALSE;
1558 player->is_sleeping = FALSE;
1560 player->cannot_move = FALSE;
1562 player->frame_counter_bored = -1;
1563 player->frame_counter_sleeping = -1;
1565 player->anim_delay_counter = 0;
1566 player->post_delay_counter = 0;
1568 player->action_waiting = ACTION_DEFAULT;
1569 player->last_action_waiting = ACTION_DEFAULT;
1570 player->special_action_bored = ACTION_DEFAULT;
1571 player->special_action_sleeping = ACTION_DEFAULT;
1573 player->num_special_action_bored = 0;
1574 player->num_special_action_sleeping = 0;
1576 /* determine number of special actions for bored and sleeping animation */
1577 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1579 boolean found = FALSE;
1581 for (k = 0; k < NUM_DIRECTIONS; k++)
1582 if (el_act_dir2img(player->element_nr, j, k) !=
1583 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1587 player->num_special_action_bored++;
1591 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1593 boolean found = FALSE;
1595 for (k = 0; k < NUM_DIRECTIONS; k++)
1596 if (el_act_dir2img(player->element_nr, j, k) !=
1597 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1601 player->num_special_action_sleeping++;
1606 player->switch_x = -1;
1607 player->switch_y = -1;
1609 player->drop_x = -1;
1610 player->drop_y = -1;
1612 player->show_envelope = 0;
1614 player->move_delay = game.initial_move_delay;
1615 player->move_delay_value = game.initial_move_delay_value;
1617 player->move_delay_value_next = -1;
1619 player->move_delay_reset_counter = 0;
1621 player->push_delay = -1; /* initialized when pushing starts */
1622 player->push_delay_value = game.initial_push_delay_value;
1624 player->drop_delay = 0;
1626 player->last_jx = player->last_jy = 0;
1627 player->jx = player->jy = 0;
1629 player->shield_normal_time_left = 0;
1630 player->shield_deadly_time_left = 0;
1632 player->inventory_infinite_element = EL_UNDEFINED;
1633 player->inventory_size = 0;
1635 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1636 SnapField(player, 0, 0);
1638 player->LevelSolved = FALSE;
1639 player->GameOver = FALSE;
1642 network_player_action_received = FALSE;
1644 #if defined(NETWORK_AVALIABLE)
1645 /* initial null action */
1646 if (network_playing)
1647 SendToServer_MovePlayer(MV_NONE);
1656 TimeLeft = level.time;
1659 ScreenMovDir = MV_NONE;
1663 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1665 AllPlayersGone = FALSE;
1667 game.yamyam_content_nr = 0;
1668 game.magic_wall_active = FALSE;
1669 game.magic_wall_time_left = 0;
1670 game.light_time_left = 0;
1671 game.timegate_time_left = 0;
1672 game.switchgate_pos = 0;
1673 game.wind_direction = level.wind_direction_initial;
1674 game.gravity = level.initial_gravity;
1675 game.explosions_delayed = TRUE;
1677 game.envelope_active = FALSE;
1679 for (i = 0; i < NUM_BELTS; i++)
1681 game.belt_dir[i] = MV_NONE;
1682 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1685 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1686 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1688 for (x = 0; x < lev_fieldx; x++)
1690 for (y = 0; y < lev_fieldy; y++)
1692 Feld[x][y] = level.field[x][y];
1693 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1694 ChangeDelay[x][y] = 0;
1695 ChangePage[x][y] = -1;
1696 #if USE_NEW_CUSTOM_VALUE
1697 CustomValue[x][y] = 0; /* initialized in InitField() */
1699 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1701 WasJustMoving[x][y] = 0;
1702 WasJustFalling[x][y] = 0;
1703 CheckCollision[x][y] = 0;
1705 Pushed[x][y] = FALSE;
1707 Changed[x][y] = FALSE;
1708 ChangeEvent[x][y] = -1;
1710 ExplodePhase[x][y] = 0;
1711 ExplodeDelay[x][y] = 0;
1712 ExplodeField[x][y] = EX_TYPE_NONE;
1714 RunnerVisit[x][y] = 0;
1715 PlayerVisit[x][y] = 0;
1718 GfxRandom[x][y] = INIT_GFX_RANDOM();
1719 GfxElement[x][y] = EL_UNDEFINED;
1720 GfxAction[x][y] = ACTION_DEFAULT;
1721 GfxDir[x][y] = MV_NONE;
1725 for (y = 0; y < lev_fieldy; y++)
1727 for (x = 0; x < lev_fieldx; x++)
1729 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1731 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1733 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1736 InitField(x, y, TRUE);
1742 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1743 emulate_sb ? EMU_SOKOBAN :
1744 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1746 #if USE_NEW_ALL_SLIPPERY
1747 /* initialize type of slippery elements */
1748 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1750 if (!IS_CUSTOM_ELEMENT(i))
1752 /* default: elements slip down either to the left or right randomly */
1753 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
1755 /* SP style elements prefer to slip down on the left side */
1756 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
1757 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1759 /* BD style elements prefer to slip down on the left side */
1760 if (game.emulation == EMU_BOULDERDASH)
1761 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1766 /* initialize explosion and ignition delay */
1767 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1769 if (!IS_CUSTOM_ELEMENT(i))
1772 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1773 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1774 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1775 int last_phase = (num_phase + 1) * delay;
1776 int half_phase = (num_phase / 2) * delay;
1778 element_info[i].explosion_delay = last_phase - 1;
1779 element_info[i].ignition_delay = half_phase;
1781 if (i == EL_BLACK_ORB)
1782 element_info[i].ignition_delay = 1;
1786 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1787 element_info[i].explosion_delay = 1;
1789 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1790 element_info[i].ignition_delay = 1;
1794 /* correct non-moving belts to start moving left */
1795 for (i = 0; i < NUM_BELTS; i++)
1796 if (game.belt_dir[i] == MV_NONE)
1797 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1799 /* check if any connected player was not found in playfield */
1800 for (i = 0; i < MAX_PLAYERS; i++)
1802 struct PlayerInfo *player = &stored_player[i];
1804 if (player->connected && !player->present)
1806 for (j = 0; j < MAX_PLAYERS; j++)
1808 struct PlayerInfo *some_player = &stored_player[j];
1809 int jx = some_player->jx, jy = some_player->jy;
1811 /* assign first free player found that is present in the playfield */
1812 if (some_player->present && !some_player->connected)
1814 player->present = TRUE;
1815 player->active = TRUE;
1817 some_player->present = FALSE;
1818 some_player->active = FALSE;
1821 player->element_nr = some_player->element_nr;
1824 player->block_last_field = some_player->block_last_field;
1825 player->block_delay_adjustment = some_player->block_delay_adjustment;
1827 StorePlayer[jx][jy] = player->element_nr;
1828 player->jx = player->last_jx = jx;
1829 player->jy = player->last_jy = jy;
1839 /* when playing a tape, eliminate all players which do not participate */
1841 for (i = 0; i < MAX_PLAYERS; i++)
1843 if (stored_player[i].active && !tape.player_participates[i])
1845 struct PlayerInfo *player = &stored_player[i];
1846 int jx = player->jx, jy = player->jy;
1848 player->active = FALSE;
1849 StorePlayer[jx][jy] = 0;
1850 Feld[jx][jy] = EL_EMPTY;
1854 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1856 /* when in single player mode, eliminate all but the first active player */
1858 for (i = 0; i < MAX_PLAYERS; i++)
1860 if (stored_player[i].active)
1862 for (j = i + 1; j < MAX_PLAYERS; j++)
1864 if (stored_player[j].active)
1866 struct PlayerInfo *player = &stored_player[j];
1867 int jx = player->jx, jy = player->jy;
1869 player->active = FALSE;
1870 player->present = FALSE;
1872 StorePlayer[jx][jy] = 0;
1873 Feld[jx][jy] = EL_EMPTY;
1880 /* when recording the game, store which players take part in the game */
1883 for (i = 0; i < MAX_PLAYERS; i++)
1884 if (stored_player[i].active)
1885 tape.player_participates[i] = TRUE;
1890 for (i = 0; i < MAX_PLAYERS; i++)
1892 struct PlayerInfo *player = &stored_player[i];
1894 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1899 if (local_player == player)
1900 printf("Player %d is local player.\n", i+1);
1904 if (BorderElement == EL_EMPTY)
1907 SBX_Right = lev_fieldx - SCR_FIELDX;
1909 SBY_Lower = lev_fieldy - SCR_FIELDY;
1914 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1916 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1919 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1920 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1922 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1923 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1925 /* if local player not found, look for custom element that might create
1926 the player (make some assumptions about the right custom element) */
1927 if (!local_player->present)
1929 int start_x = 0, start_y = 0;
1930 int found_rating = 0;
1931 int found_element = EL_UNDEFINED;
1932 int player_nr = local_player->index_nr;
1934 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1936 int element = Feld[x][y];
1941 if (level.use_start_element[player_nr] &&
1942 level.start_element[player_nr] == element &&
1949 found_element = element;
1952 if (!IS_CUSTOM_ELEMENT(element))
1955 if (CAN_CHANGE(element))
1957 for (i = 0; i < element_info[element].num_change_pages; i++)
1959 /* check for player created from custom element as single target */
1960 content = element_info[element].change_page[i].target_element;
1961 is_player = ELEM_IS_PLAYER(content);
1963 if (is_player && (found_rating < 3 || element < found_element))
1969 found_element = element;
1974 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1976 /* check for player created from custom element as explosion content */
1977 content = element_info[element].content.e[xx][yy];
1978 is_player = ELEM_IS_PLAYER(content);
1980 if (is_player && (found_rating < 2 || element < found_element))
1982 start_x = x + xx - 1;
1983 start_y = y + yy - 1;
1986 found_element = element;
1989 if (!CAN_CHANGE(element))
1992 for (i = 0; i < element_info[element].num_change_pages; i++)
1994 /* check for player created from custom element as extended target */
1996 element_info[element].change_page[i].target_content.e[xx][yy];
1998 is_player = ELEM_IS_PLAYER(content);
2000 if (is_player && (found_rating < 1 || element < found_element))
2002 start_x = x + xx - 1;
2003 start_y = y + yy - 1;
2006 found_element = element;
2012 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2013 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2016 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2017 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2022 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2023 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2024 local_player->jx - MIDPOSX);
2026 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2027 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2028 local_player->jy - MIDPOSY);
2031 if (!game.restart_level)
2032 CloseDoor(DOOR_CLOSE_1);
2034 /* !!! FIX THIS (START) !!! */
2035 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2037 InitGameEngine_EM();
2044 /* after drawing the level, correct some elements */
2045 if (game.timegate_time_left == 0)
2046 CloseAllOpenTimegates();
2048 if (setup.soft_scrolling)
2049 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2051 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2054 /* !!! FIX THIS (END) !!! */
2056 if (!game.restart_level)
2058 /* copy default game door content to main double buffer */
2059 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2060 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2063 DrawGameDoorValues();
2065 if (!game.restart_level)
2069 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2070 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2071 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2075 /* copy actual game door content to door double buffer for OpenDoor() */
2076 BlitBitmap(drawto, bitmap_db_door,
2077 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2079 OpenDoor(DOOR_OPEN_ALL);
2081 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2083 if (setup.sound_music)
2086 KeyboardAutoRepeatOffUnlessAutoplay();
2090 for (i = 0; i < MAX_PLAYERS; i++)
2091 printf("Player %d %sactive.\n",
2092 i + 1, (stored_player[i].active ? "" : "not "));
2096 game.restart_level = FALSE;
2099 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2101 /* this is used for non-R'n'D game engines to update certain engine values */
2103 /* needed to determine if sounds are played within the visible screen area */
2104 scroll_x = actual_scroll_x;
2105 scroll_y = actual_scroll_y;
2108 void InitMovDir(int x, int y)
2110 int i, element = Feld[x][y];
2111 static int xy[4][2] =
2118 static int direction[3][4] =
2120 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2121 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2122 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2131 Feld[x][y] = EL_BUG;
2132 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2135 case EL_SPACESHIP_RIGHT:
2136 case EL_SPACESHIP_UP:
2137 case EL_SPACESHIP_LEFT:
2138 case EL_SPACESHIP_DOWN:
2139 Feld[x][y] = EL_SPACESHIP;
2140 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2143 case EL_BD_BUTTERFLY_RIGHT:
2144 case EL_BD_BUTTERFLY_UP:
2145 case EL_BD_BUTTERFLY_LEFT:
2146 case EL_BD_BUTTERFLY_DOWN:
2147 Feld[x][y] = EL_BD_BUTTERFLY;
2148 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2151 case EL_BD_FIREFLY_RIGHT:
2152 case EL_BD_FIREFLY_UP:
2153 case EL_BD_FIREFLY_LEFT:
2154 case EL_BD_FIREFLY_DOWN:
2155 Feld[x][y] = EL_BD_FIREFLY;
2156 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2159 case EL_PACMAN_RIGHT:
2161 case EL_PACMAN_LEFT:
2162 case EL_PACMAN_DOWN:
2163 Feld[x][y] = EL_PACMAN;
2164 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2167 case EL_SP_SNIKSNAK:
2168 MovDir[x][y] = MV_UP;
2171 case EL_SP_ELECTRON:
2172 MovDir[x][y] = MV_LEFT;
2179 Feld[x][y] = EL_MOLE;
2180 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2184 if (IS_CUSTOM_ELEMENT(element))
2186 struct ElementInfo *ei = &element_info[element];
2187 int move_direction_initial = ei->move_direction_initial;
2188 int move_pattern = ei->move_pattern;
2190 if (move_direction_initial == MV_START_PREVIOUS)
2192 if (MovDir[x][y] != MV_NONE)
2195 move_direction_initial = MV_START_AUTOMATIC;
2198 if (move_direction_initial == MV_START_RANDOM)
2199 MovDir[x][y] = 1 << RND(4);
2200 else if (move_direction_initial & MV_ANY_DIRECTION)
2201 MovDir[x][y] = move_direction_initial;
2202 else if (move_pattern == MV_ALL_DIRECTIONS ||
2203 move_pattern == MV_TURNING_LEFT ||
2204 move_pattern == MV_TURNING_RIGHT ||
2205 move_pattern == MV_TURNING_LEFT_RIGHT ||
2206 move_pattern == MV_TURNING_RIGHT_LEFT ||
2207 move_pattern == MV_TURNING_RANDOM)
2208 MovDir[x][y] = 1 << RND(4);
2209 else if (move_pattern == MV_HORIZONTAL)
2210 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2211 else if (move_pattern == MV_VERTICAL)
2212 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2213 else if (move_pattern & MV_ANY_DIRECTION)
2214 MovDir[x][y] = element_info[element].move_pattern;
2215 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2216 move_pattern == MV_ALONG_RIGHT_SIDE)
2218 /* use random direction as default start direction */
2219 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2220 MovDir[x][y] = 1 << RND(4);
2222 for (i = 0; i < NUM_DIRECTIONS; i++)
2224 int x1 = x + xy[i][0];
2225 int y1 = y + xy[i][1];
2227 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2229 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2230 MovDir[x][y] = direction[0][i];
2232 MovDir[x][y] = direction[1][i];
2241 MovDir[x][y] = 1 << RND(4);
2243 if (element != EL_BUG &&
2244 element != EL_SPACESHIP &&
2245 element != EL_BD_BUTTERFLY &&
2246 element != EL_BD_FIREFLY)
2249 for (i = 0; i < NUM_DIRECTIONS; i++)
2251 int x1 = x + xy[i][0];
2252 int y1 = y + xy[i][1];
2254 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2256 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2258 MovDir[x][y] = direction[0][i];
2261 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2262 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2264 MovDir[x][y] = direction[1][i];
2273 GfxDir[x][y] = MovDir[x][y];
2276 void InitAmoebaNr(int x, int y)
2279 int group_nr = AmoebeNachbarNr(x, y);
2283 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2285 if (AmoebaCnt[i] == 0)
2293 AmoebaNr[x][y] = group_nr;
2294 AmoebaCnt[group_nr]++;
2295 AmoebaCnt2[group_nr]++;
2301 boolean raise_level = FALSE;
2303 if (local_player->MovPos)
2306 if (tape.auto_play) /* tape might already be stopped here */
2307 tape.auto_play_level_solved = TRUE;
2309 local_player->LevelSolved = FALSE;
2311 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2315 if (!tape.playing && setup.sound_loops)
2316 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2317 SND_CTRL_PLAY_LOOP);
2319 while (TimeLeft > 0)
2321 if (!tape.playing && !setup.sound_loops)
2322 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2324 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2327 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2332 RaiseScore(level.score[SC_TIME_BONUS]);
2335 DrawGameValue_Time(TimeLeft);
2343 if (!tape.playing && setup.sound_loops)
2344 StopSound(SND_GAME_LEVELTIME_BONUS);
2346 else if (level.time == 0) /* level without time limit */
2348 if (!tape.playing && setup.sound_loops)
2349 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2350 SND_CTRL_PLAY_LOOP);
2352 while (TimePlayed < 999)
2354 if (!tape.playing && !setup.sound_loops)
2355 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2357 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2360 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2365 RaiseScore(level.score[SC_TIME_BONUS]);
2368 DrawGameValue_Time(TimePlayed);
2376 if (!tape.playing && setup.sound_loops)
2377 StopSound(SND_GAME_LEVELTIME_BONUS);
2380 /* close exit door after last player */
2381 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2382 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2383 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2385 int element = Feld[ExitX][ExitY];
2387 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2388 EL_SP_EXIT_CLOSING);
2390 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2393 /* player disappears */
2394 if (ExitX >= 0 && ExitY >= 0)
2395 DrawLevelField(ExitX, ExitY);
2402 CloseDoor(DOOR_CLOSE_1);
2407 SaveTape(tape.level_nr); /* Ask to save tape */
2410 if (level_nr == leveldir_current->handicap_level)
2412 leveldir_current->handicap_level++;
2413 SaveLevelSetup_SeriesInfo();
2416 if (level_editor_test_game)
2417 local_player->score = -1; /* no highscore when playing from editor */
2418 else if (level_nr < leveldir_current->last_level)
2419 raise_level = TRUE; /* advance to next level */
2421 if ((hi_pos = NewHiScore()) >= 0)
2423 game_status = GAME_MODE_SCORES;
2424 DrawHallOfFame(hi_pos);
2433 game_status = GAME_MODE_MAIN;
2450 LoadScore(level_nr);
2452 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2453 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2456 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2458 if (local_player->score > highscore[k].Score)
2460 /* player has made it to the hall of fame */
2462 if (k < MAX_SCORE_ENTRIES - 1)
2464 int m = MAX_SCORE_ENTRIES - 1;
2467 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2468 if (!strcmp(setup.player_name, highscore[l].Name))
2470 if (m == k) /* player's new highscore overwrites his old one */
2474 for (l = m; l > k; l--)
2476 strcpy(highscore[l].Name, highscore[l - 1].Name);
2477 highscore[l].Score = highscore[l - 1].Score;
2484 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2485 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2486 highscore[k].Score = local_player->score;
2492 else if (!strncmp(setup.player_name, highscore[k].Name,
2493 MAX_PLAYER_NAME_LEN))
2494 break; /* player already there with a higher score */
2500 SaveScore(level_nr);
2505 inline static int getElementMoveStepsize(int x, int y)
2507 int element = Feld[x][y];
2508 int direction = MovDir[x][y];
2509 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2510 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2511 int horiz_move = (dx != 0);
2512 int sign = (horiz_move ? dx : dy);
2513 int step = sign * element_info[element].move_stepsize;
2515 /* special values for move stepsize for spring and things on conveyor belt */
2519 if (element == EL_SPRING)
2520 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2521 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2522 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2523 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2525 if (CAN_FALL(element) &&
2526 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2527 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2528 else if (element == EL_SPRING)
2529 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2536 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2538 if (player->GfxAction != action || player->GfxDir != dir)
2541 printf("Player frame reset! (%d => %d, %d => %d)\n",
2542 player->GfxAction, action, player->GfxDir, dir);
2545 player->GfxAction = action;
2546 player->GfxDir = dir;
2548 player->StepFrame = 0;
2552 static void ResetRandomAnimationValue(int x, int y)
2554 GfxRandom[x][y] = INIT_GFX_RANDOM();
2557 static void ResetGfxAnimation(int x, int y)
2560 GfxAction[x][y] = ACTION_DEFAULT;
2561 GfxDir[x][y] = MovDir[x][y];
2564 void InitMovingField(int x, int y, int direction)
2566 int element = Feld[x][y];
2567 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2568 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2572 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2573 ResetGfxAnimation(x, y);
2575 MovDir[x][y] = direction;
2576 GfxDir[x][y] = direction;
2577 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2578 ACTION_FALLING : ACTION_MOVING);
2580 /* this is needed for CEs with property "can move" / "not moving" */
2582 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2584 if (Feld[newx][newy] == EL_EMPTY)
2585 Feld[newx][newy] = EL_BLOCKED;
2587 MovDir[newx][newy] = MovDir[x][y];
2589 #if USE_NEW_CUSTOM_VALUE
2590 CustomValue[newx][newy] = CustomValue[x][y];
2593 GfxFrame[newx][newy] = GfxFrame[x][y];
2594 GfxRandom[newx][newy] = GfxRandom[x][y];
2595 GfxAction[newx][newy] = GfxAction[x][y];
2596 GfxDir[newx][newy] = GfxDir[x][y];
2600 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2602 int direction = MovDir[x][y];
2603 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2604 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2610 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2612 int oldx = x, oldy = y;
2613 int direction = MovDir[x][y];
2615 if (direction == MV_LEFT)
2617 else if (direction == MV_RIGHT)
2619 else if (direction == MV_UP)
2621 else if (direction == MV_DOWN)
2624 *comes_from_x = oldx;
2625 *comes_from_y = oldy;
2628 int MovingOrBlocked2Element(int x, int y)
2630 int element = Feld[x][y];
2632 if (element == EL_BLOCKED)
2636 Blocked2Moving(x, y, &oldx, &oldy);
2637 return Feld[oldx][oldy];
2643 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2645 /* like MovingOrBlocked2Element(), but if element is moving
2646 and (x,y) is the field the moving element is just leaving,
2647 return EL_BLOCKED instead of the element value */
2648 int element = Feld[x][y];
2650 if (IS_MOVING(x, y))
2652 if (element == EL_BLOCKED)
2656 Blocked2Moving(x, y, &oldx, &oldy);
2657 return Feld[oldx][oldy];
2666 static void RemoveField(int x, int y)
2668 Feld[x][y] = EL_EMPTY;
2674 #if USE_NEW_CUSTOM_VALUE
2675 CustomValue[x][y] = 0;
2679 ChangeDelay[x][y] = 0;
2680 ChangePage[x][y] = -1;
2681 Pushed[x][y] = FALSE;
2684 ExplodeField[x][y] = EX_TYPE_NONE;
2687 GfxElement[x][y] = EL_UNDEFINED;
2688 GfxAction[x][y] = ACTION_DEFAULT;
2689 GfxDir[x][y] = MV_NONE;
2692 void RemoveMovingField(int x, int y)
2694 int oldx = x, oldy = y, newx = x, newy = y;
2695 int element = Feld[x][y];
2696 int next_element = EL_UNDEFINED;
2698 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2701 if (IS_MOVING(x, y))
2703 Moving2Blocked(x, y, &newx, &newy);
2705 if (Feld[newx][newy] != EL_BLOCKED)
2707 /* element is moving, but target field is not free (blocked), but
2708 already occupied by something different (example: acid pool);
2709 in this case, only remove the moving field, but not the target */
2711 RemoveField(oldx, oldy);
2713 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2715 DrawLevelField(oldx, oldy);
2720 else if (element == EL_BLOCKED)
2722 Blocked2Moving(x, y, &oldx, &oldy);
2723 if (!IS_MOVING(oldx, oldy))
2727 if (element == EL_BLOCKED &&
2728 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2729 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2730 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2731 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2732 next_element = get_next_element(Feld[oldx][oldy]);
2734 RemoveField(oldx, oldy);
2735 RemoveField(newx, newy);
2737 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2739 if (next_element != EL_UNDEFINED)
2740 Feld[oldx][oldy] = next_element;
2742 DrawLevelField(oldx, oldy);
2743 DrawLevelField(newx, newy);
2746 void DrawDynamite(int x, int y)
2748 int sx = SCREENX(x), sy = SCREENY(y);
2749 int graphic = el2img(Feld[x][y]);
2752 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2755 if (IS_WALKABLE_INSIDE(Back[x][y]))
2759 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2760 else if (Store[x][y])
2761 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2763 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2765 if (Back[x][y] || Store[x][y])
2766 DrawGraphicThruMask(sx, sy, graphic, frame);
2768 DrawGraphic(sx, sy, graphic, frame);
2771 void CheckDynamite(int x, int y)
2773 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2777 if (MovDelay[x][y] != 0)
2780 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2786 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2791 void DrawRelocatePlayer(struct PlayerInfo *player)
2793 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2794 boolean no_delay = (tape.warp_forward);
2795 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2796 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2797 int jx = player->jx;
2798 int jy = player->jy;
2800 if (level.instant_relocation)
2802 int offset = (setup.scroll_delay ? 3 : 0);
2804 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2806 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2807 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2808 local_player->jx - MIDPOSX);
2810 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2811 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2812 local_player->jy - MIDPOSY);
2816 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2817 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2818 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2820 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2821 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2822 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2824 /* don't scroll over playfield boundaries */
2825 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2826 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2828 /* don't scroll over playfield boundaries */
2829 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2830 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2833 RedrawPlayfield(TRUE, 0,0,0,0);
2837 int scroll_xx = -999, scroll_yy = -999;
2839 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2841 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2844 int fx = FX, fy = FY;
2846 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2847 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2848 local_player->jx - MIDPOSX);
2850 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2851 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2852 local_player->jy - MIDPOSY);
2854 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2855 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2857 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2863 fx += dx * TILEX / 2;
2864 fy += dy * TILEY / 2;
2866 ScrollLevel(dx, dy);
2869 /* scroll in two steps of half tile size to make things smoother */
2870 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2872 Delay(wait_delay_value);
2874 /* scroll second step to align at full tile size */
2876 Delay(wait_delay_value);
2881 Delay(wait_delay_value);
2885 void RelocatePlayer(int jx, int jy, int el_player_raw)
2887 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
2888 int player_nr = GET_PLAYER_NR(el_player);
2889 struct PlayerInfo *player = &stored_player[player_nr];
2890 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2891 boolean no_delay = (tape.warp_forward);
2892 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2893 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2894 int old_jx = player->jx;
2895 int old_jy = player->jy;
2896 int old_element = Feld[old_jx][old_jy];
2897 int element = Feld[jx][jy];
2898 boolean player_relocated = (old_jx != jx || old_jy != jy);
2900 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2901 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2902 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2903 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2904 int leave_side_horiz = move_dir_horiz;
2905 int leave_side_vert = move_dir_vert;
2906 int enter_side = enter_side_horiz | enter_side_vert;
2907 int leave_side = leave_side_horiz | leave_side_vert;
2909 if (player->GameOver) /* do not reanimate dead player */
2912 if (!player_relocated) /* no need to relocate the player */
2915 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2917 RemoveField(jx, jy); /* temporarily remove newly placed player */
2918 DrawLevelField(jx, jy);
2921 if (player->present)
2923 while (player->MovPos)
2925 ScrollPlayer(player, SCROLL_GO_ON);
2926 ScrollScreen(NULL, SCROLL_GO_ON);
2928 AdvanceFrameAndPlayerCounters(player->index_nr);
2933 Delay(wait_delay_value);
2936 DrawPlayer(player); /* needed here only to cleanup last field */
2937 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2939 player->is_moving = FALSE;
2942 if (IS_CUSTOM_ELEMENT(old_element))
2943 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
2945 player->index_bit, leave_side);
2947 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
2949 player->index_bit, leave_side);
2951 Feld[jx][jy] = el_player;
2952 InitPlayerField(jx, jy, el_player, TRUE);
2954 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
2956 Feld[jx][jy] = element;
2957 InitField(jx, jy, FALSE);
2960 if (player == local_player) /* only visually relocate local player */
2961 DrawRelocatePlayer(player);
2963 TestIfPlayerTouchesBadThing(jx, jy);
2964 TestIfPlayerTouchesCustomElement(jx, jy);
2966 if (IS_CUSTOM_ELEMENT(element))
2967 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
2968 player->index_bit, enter_side);
2970 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
2971 player->index_bit, enter_side);
2974 void Explode(int ex, int ey, int phase, int mode)
2980 /* !!! eliminate this variable !!! */
2981 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2983 if (game.explosions_delayed)
2985 ExplodeField[ex][ey] = mode;
2989 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2991 int center_element = Feld[ex][ey];
2994 /* --- This is only really needed (and now handled) in "Impact()". --- */
2995 /* do not explode moving elements that left the explode field in time */
2996 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2997 center_element == EL_EMPTY &&
2998 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3002 if (mode == EX_TYPE_NORMAL ||
3003 mode == EX_TYPE_CENTER ||
3004 mode == EX_TYPE_CROSS)
3005 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3007 /* remove things displayed in background while burning dynamite */
3008 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3011 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3013 /* put moving element to center field (and let it explode there) */
3014 center_element = MovingOrBlocked2Element(ex, ey);
3015 RemoveMovingField(ex, ey);
3016 Feld[ex][ey] = center_element;
3019 last_phase = element_info[center_element].explosion_delay + 1;
3021 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3023 int xx = x - ex + 1;
3024 int yy = y - ey + 1;
3027 if (!IN_LEV_FIELD(x, y) ||
3028 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3029 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3032 element = Feld[x][y];
3034 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3036 element = MovingOrBlocked2Element(x, y);
3038 if (!IS_EXPLOSION_PROOF(element))
3039 RemoveMovingField(x, y);
3042 /* indestructible elements can only explode in center (but not flames) */
3043 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3044 mode == EX_TYPE_BORDER)) ||
3045 element == EL_FLAMES)
3048 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3049 behaviour, for example when touching a yamyam that explodes to rocks
3050 with active deadly shield, a rock is created under the player !!! */
3051 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3053 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3054 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3055 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3057 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3060 if (IS_ACTIVE_BOMB(element))
3062 /* re-activate things under the bomb like gate or penguin */
3063 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3070 /* save walkable background elements while explosion on same tile */
3071 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3072 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3073 Back[x][y] = element;
3075 /* ignite explodable elements reached by other explosion */
3076 if (element == EL_EXPLOSION)
3077 element = Store2[x][y];
3079 if (AmoebaNr[x][y] &&
3080 (element == EL_AMOEBA_FULL ||
3081 element == EL_BD_AMOEBA ||
3082 element == EL_AMOEBA_GROWING))
3084 AmoebaCnt[AmoebaNr[x][y]]--;
3085 AmoebaCnt2[AmoebaNr[x][y]]--;
3090 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3092 switch(StorePlayer[ex][ey])
3095 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3098 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3101 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3105 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3109 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3110 Store[x][y] = EL_EMPTY;
3112 else if (center_element == EL_MOLE)
3113 Store[x][y] = EL_EMERALD_RED;
3114 else if (center_element == EL_PENGUIN)
3115 Store[x][y] = EL_EMERALD_PURPLE;
3116 else if (center_element == EL_BUG)
3117 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3118 else if (center_element == EL_BD_BUTTERFLY)
3119 Store[x][y] = EL_BD_DIAMOND;
3120 else if (center_element == EL_SP_ELECTRON)
3121 Store[x][y] = EL_SP_INFOTRON;
3122 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3123 Store[x][y] = level.amoeba_content;
3124 else if (center_element == EL_YAMYAM)
3125 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3126 else if (IS_CUSTOM_ELEMENT(center_element) &&
3127 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3128 Store[x][y] = element_info[center_element].content.e[xx][yy];
3129 else if (element == EL_WALL_EMERALD)
3130 Store[x][y] = EL_EMERALD;
3131 else if (element == EL_WALL_DIAMOND)
3132 Store[x][y] = EL_DIAMOND;
3133 else if (element == EL_WALL_BD_DIAMOND)
3134 Store[x][y] = EL_BD_DIAMOND;
3135 else if (element == EL_WALL_EMERALD_YELLOW)
3136 Store[x][y] = EL_EMERALD_YELLOW;
3137 else if (element == EL_WALL_EMERALD_RED)
3138 Store[x][y] = EL_EMERALD_RED;
3139 else if (element == EL_WALL_EMERALD_PURPLE)
3140 Store[x][y] = EL_EMERALD_PURPLE;
3141 else if (element == EL_WALL_PEARL)
3142 Store[x][y] = EL_PEARL;
3143 else if (element == EL_WALL_CRYSTAL)
3144 Store[x][y] = EL_CRYSTAL;
3145 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3146 Store[x][y] = element_info[element].content.e[1][1];
3148 Store[x][y] = EL_EMPTY;
3150 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3151 center_element == EL_AMOEBA_TO_DIAMOND)
3152 Store2[x][y] = element;
3154 Feld[x][y] = EL_EXPLOSION;
3155 GfxElement[x][y] = center_element;
3157 ExplodePhase[x][y] = 1;
3158 ExplodeDelay[x][y] = last_phase;
3163 if (center_element == EL_YAMYAM)
3164 game.yamyam_content_nr =
3165 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3177 GfxFrame[x][y] = 0; /* restart explosion animation */
3179 last_phase = ExplodeDelay[x][y];
3181 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3185 /* activate this even in non-DEBUG version until cause for crash in
3186 getGraphicAnimationFrame() (see below) is found and eliminated */
3191 if (GfxElement[x][y] == EL_UNDEFINED)
3194 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3195 printf("Explode(): This should never happen!\n");
3198 GfxElement[x][y] = EL_EMPTY;
3202 border_element = Store2[x][y];
3203 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3204 border_element = StorePlayer[x][y];
3206 if (phase == element_info[border_element].ignition_delay ||
3207 phase == last_phase)
3209 boolean border_explosion = FALSE;
3211 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3212 !PLAYER_EXPLOSION_PROTECTED(x, y))
3214 KillPlayerUnlessExplosionProtected(x, y);
3215 border_explosion = TRUE;
3217 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3219 Feld[x][y] = Store2[x][y];
3222 border_explosion = TRUE;
3224 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3226 AmoebeUmwandeln(x, y);
3228 border_explosion = TRUE;
3231 /* if an element just explodes due to another explosion (chain-reaction),
3232 do not immediately end the new explosion when it was the last frame of
3233 the explosion (as it would be done in the following "if"-statement!) */
3234 if (border_explosion && phase == last_phase)
3238 if (phase == last_phase)
3242 element = Feld[x][y] = Store[x][y];
3243 Store[x][y] = Store2[x][y] = 0;
3244 GfxElement[x][y] = EL_UNDEFINED;
3246 /* player can escape from explosions and might therefore be still alive */
3247 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3248 element <= EL_PLAYER_IS_EXPLODING_4)
3249 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3251 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3252 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3253 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3256 /* restore probably existing indestructible background element */
3257 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3258 element = Feld[x][y] = Back[x][y];
3261 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3262 GfxDir[x][y] = MV_NONE;
3263 ChangeDelay[x][y] = 0;
3264 ChangePage[x][y] = -1;
3266 #if USE_NEW_CUSTOM_VALUE
3267 CustomValue[x][y] = 0;
3270 InitField_WithBug2(x, y, FALSE);
3272 DrawLevelField(x, y);
3274 TestIfElementTouchesCustomElement(x, y);
3276 if (GFX_CRUMBLED(element))
3277 DrawLevelFieldCrumbledSandNeighbours(x, y);
3279 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3280 StorePlayer[x][y] = 0;
3282 if (ELEM_IS_PLAYER(element))
3283 RelocatePlayer(x, y, element);
3285 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3287 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3288 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3291 DrawLevelFieldCrumbledSand(x, y);
3293 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3295 DrawLevelElement(x, y, Back[x][y]);
3296 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3298 else if (IS_WALKABLE_UNDER(Back[x][y]))
3300 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3301 DrawLevelElementThruMask(x, y, Back[x][y]);
3303 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3304 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3308 void DynaExplode(int ex, int ey)
3311 int dynabomb_element = Feld[ex][ey];
3312 int dynabomb_size = 1;
3313 boolean dynabomb_xl = FALSE;
3314 struct PlayerInfo *player;
3315 static int xy[4][2] =
3323 if (IS_ACTIVE_BOMB(dynabomb_element))
3325 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3326 dynabomb_size = player->dynabomb_size;
3327 dynabomb_xl = player->dynabomb_xl;
3328 player->dynabombs_left++;
3331 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3333 for (i = 0; i < NUM_DIRECTIONS; i++)
3335 for (j = 1; j <= dynabomb_size; j++)
3337 int x = ex + j * xy[i][0];
3338 int y = ey + j * xy[i][1];
3341 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3344 element = Feld[x][y];
3346 /* do not restart explosions of fields with active bombs */
3347 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3350 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3352 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3353 !IS_DIGGABLE(element) && !dynabomb_xl)
3359 void Bang(int x, int y)
3361 int element = MovingOrBlocked2Element(x, y);
3363 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3365 struct PlayerInfo *player = PLAYERINFO(x, y);
3367 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3368 player->element_nr);
3375 case EL_BD_BUTTERFLY:
3378 case EL_DARK_YAMYAM:
3382 RaiseScoreElement(element);
3383 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3385 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3386 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3387 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3388 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3389 case EL_DYNABOMB_INCREASE_NUMBER:
3390 case EL_DYNABOMB_INCREASE_SIZE:
3391 case EL_DYNABOMB_INCREASE_POWER:
3396 case EL_LAMP_ACTIVE:
3397 case EL_AMOEBA_TO_DIAMOND:
3398 if (IS_PLAYER(x, y))
3399 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3401 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3404 if (element_info[element].explosion_type == EXPLODES_CROSS)
3405 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3406 else if (element_info[element].explosion_type == EXPLODES_1X1)
3407 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3409 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3413 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3416 void SplashAcid(int x, int y)
3418 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3419 (!IN_LEV_FIELD(x - 1, y - 2) ||
3420 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3421 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3423 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3424 (!IN_LEV_FIELD(x + 1, y - 2) ||
3425 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3426 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3428 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3431 static void InitBeltMovement()
3433 static int belt_base_element[4] =
3435 EL_CONVEYOR_BELT_1_LEFT,
3436 EL_CONVEYOR_BELT_2_LEFT,
3437 EL_CONVEYOR_BELT_3_LEFT,
3438 EL_CONVEYOR_BELT_4_LEFT
3440 static int belt_base_active_element[4] =
3442 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3443 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3444 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3445 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3450 /* set frame order for belt animation graphic according to belt direction */
3451 for (i = 0; i < NUM_BELTS; i++)
3455 for (j = 0; j < NUM_BELT_PARTS; j++)
3457 int element = belt_base_active_element[belt_nr] + j;
3458 int graphic = el2img(element);
3460 if (game.belt_dir[i] == MV_LEFT)
3461 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3463 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3467 for (y = 0; y < lev_fieldy; y++)
3469 for (x = 0; x < lev_fieldx; x++)
3471 int element = Feld[x][y];
3473 for (i = 0; i < NUM_BELTS; i++)
3475 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3477 int e_belt_nr = getBeltNrFromBeltElement(element);
3480 if (e_belt_nr == belt_nr)
3482 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3484 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3492 static void ToggleBeltSwitch(int x, int y)
3494 static int belt_base_element[4] =
3496 EL_CONVEYOR_BELT_1_LEFT,
3497 EL_CONVEYOR_BELT_2_LEFT,
3498 EL_CONVEYOR_BELT_3_LEFT,
3499 EL_CONVEYOR_BELT_4_LEFT
3501 static int belt_base_active_element[4] =
3503 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3504 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3505 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3506 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3508 static int belt_base_switch_element[4] =
3510 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3511 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3512 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3513 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3515 static int belt_move_dir[4] =
3523 int element = Feld[x][y];
3524 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3525 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3526 int belt_dir = belt_move_dir[belt_dir_nr];
3529 if (!IS_BELT_SWITCH(element))
3532 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3533 game.belt_dir[belt_nr] = belt_dir;
3535 if (belt_dir_nr == 3)
3538 /* set frame order for belt animation graphic according to belt direction */
3539 for (i = 0; i < NUM_BELT_PARTS; i++)
3541 int element = belt_base_active_element[belt_nr] + i;
3542 int graphic = el2img(element);
3544 if (belt_dir == MV_LEFT)
3545 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3547 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3550 for (yy = 0; yy < lev_fieldy; yy++)
3552 for (xx = 0; xx < lev_fieldx; xx++)
3554 int element = Feld[xx][yy];
3556 if (IS_BELT_SWITCH(element))
3558 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3560 if (e_belt_nr == belt_nr)
3562 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3563 DrawLevelField(xx, yy);
3566 else if (IS_BELT(element) && belt_dir != MV_NONE)
3568 int e_belt_nr = getBeltNrFromBeltElement(element);
3570 if (e_belt_nr == belt_nr)
3572 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3574 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3575 DrawLevelField(xx, yy);
3578 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3580 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3582 if (e_belt_nr == belt_nr)
3584 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3586 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3587 DrawLevelField(xx, yy);
3594 static void ToggleSwitchgateSwitch(int x, int y)
3598 game.switchgate_pos = !game.switchgate_pos;
3600 for (yy = 0; yy < lev_fieldy; yy++)
3602 for (xx = 0; xx < lev_fieldx; xx++)
3604 int element = Feld[xx][yy];
3606 if (element == EL_SWITCHGATE_SWITCH_UP ||
3607 element == EL_SWITCHGATE_SWITCH_DOWN)
3609 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3610 DrawLevelField(xx, yy);
3612 else if (element == EL_SWITCHGATE_OPEN ||
3613 element == EL_SWITCHGATE_OPENING)
3615 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3617 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3619 else if (element == EL_SWITCHGATE_CLOSED ||
3620 element == EL_SWITCHGATE_CLOSING)
3622 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3624 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3630 static int getInvisibleActiveFromInvisibleElement(int element)
3632 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3633 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3634 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3638 static int getInvisibleFromInvisibleActiveElement(int element)
3640 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3641 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3642 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3646 static void RedrawAllLightSwitchesAndInvisibleElements()
3650 for (y = 0; y < lev_fieldy; y++)
3652 for (x = 0; x < lev_fieldx; x++)
3654 int element = Feld[x][y];
3656 if (element == EL_LIGHT_SWITCH &&
3657 game.light_time_left > 0)
3659 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3660 DrawLevelField(x, y);
3662 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3663 game.light_time_left == 0)
3665 Feld[x][y] = EL_LIGHT_SWITCH;
3666 DrawLevelField(x, y);
3668 else if (element == EL_INVISIBLE_STEELWALL ||
3669 element == EL_INVISIBLE_WALL ||
3670 element == EL_INVISIBLE_SAND)
3672 if (game.light_time_left > 0)
3673 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3675 DrawLevelField(x, y);
3677 /* uncrumble neighbour fields, if needed */
3678 if (element == EL_INVISIBLE_SAND)
3679 DrawLevelFieldCrumbledSandNeighbours(x, y);
3681 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3682 element == EL_INVISIBLE_WALL_ACTIVE ||
3683 element == EL_INVISIBLE_SAND_ACTIVE)
3685 if (game.light_time_left == 0)
3686 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3688 DrawLevelField(x, y);
3690 /* re-crumble neighbour fields, if needed */
3691 if (element == EL_INVISIBLE_SAND)
3692 DrawLevelFieldCrumbledSandNeighbours(x, y);
3698 static void ToggleLightSwitch(int x, int y)
3700 int element = Feld[x][y];
3702 game.light_time_left =
3703 (element == EL_LIGHT_SWITCH ?
3704 level.time_light * FRAMES_PER_SECOND : 0);
3706 RedrawAllLightSwitchesAndInvisibleElements();
3709 static void ActivateTimegateSwitch(int x, int y)
3713 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3715 for (yy = 0; yy < lev_fieldy; yy++)
3717 for (xx = 0; xx < lev_fieldx; xx++)
3719 int element = Feld[xx][yy];
3721 if (element == EL_TIMEGATE_CLOSED ||
3722 element == EL_TIMEGATE_CLOSING)
3724 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3725 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3729 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3731 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3732 DrawLevelField(xx, yy);
3739 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3742 void Impact(int x, int y)
3744 boolean last_line = (y == lev_fieldy - 1);
3745 boolean object_hit = FALSE;
3746 boolean impact = (last_line || object_hit);
3747 int element = Feld[x][y];
3748 int smashed = EL_STEELWALL;
3750 if (!last_line) /* check if element below was hit */
3752 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3755 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3756 MovDir[x][y + 1] != MV_DOWN ||
3757 MovPos[x][y + 1] <= TILEY / 2));
3759 /* do not smash moving elements that left the smashed field in time */
3760 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3761 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3765 smashed = MovingOrBlocked2Element(x, y + 1);
3767 impact = (last_line || object_hit);
3770 if (!last_line && smashed == EL_ACID) /* element falls into acid */
3772 SplashAcid(x, y + 1);
3776 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
3777 /* only reset graphic animation if graphic really changes after impact */
3779 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3781 ResetGfxAnimation(x, y);
3782 DrawLevelField(x, y);
3785 if (impact && CAN_EXPLODE_IMPACT(element))
3790 else if (impact && element == EL_PEARL)
3792 ResetGfxAnimation(x, y);
3794 Feld[x][y] = EL_PEARL_BREAKING;
3795 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3798 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
3800 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3805 if (impact && element == EL_AMOEBA_DROP)
3807 if (object_hit && IS_PLAYER(x, y + 1))
3808 KillPlayerUnlessEnemyProtected(x, y + 1);
3809 else if (object_hit && smashed == EL_PENGUIN)
3813 Feld[x][y] = EL_AMOEBA_GROWING;
3814 Store[x][y] = EL_AMOEBA_WET;
3816 ResetRandomAnimationValue(x, y);
3821 if (object_hit) /* check which object was hit */
3823 if (CAN_PASS_MAGIC_WALL(element) &&
3824 (smashed == EL_MAGIC_WALL ||
3825 smashed == EL_BD_MAGIC_WALL))
3828 int activated_magic_wall =
3829 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3830 EL_BD_MAGIC_WALL_ACTIVE);
3832 /* activate magic wall / mill */
3833 for (yy = 0; yy < lev_fieldy; yy++)
3834 for (xx = 0; xx < lev_fieldx; xx++)
3835 if (Feld[xx][yy] == smashed)
3836 Feld[xx][yy] = activated_magic_wall;
3838 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3839 game.magic_wall_active = TRUE;
3841 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3842 SND_MAGIC_WALL_ACTIVATING :
3843 SND_BD_MAGIC_WALL_ACTIVATING));
3846 if (IS_PLAYER(x, y + 1))
3848 if (CAN_SMASH_PLAYER(element))
3850 KillPlayerUnlessEnemyProtected(x, y + 1);
3854 else if (smashed == EL_PENGUIN)
3856 if (CAN_SMASH_PLAYER(element))
3862 else if (element == EL_BD_DIAMOND)
3864 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3870 else if (((element == EL_SP_INFOTRON ||
3871 element == EL_SP_ZONK) &&
3872 (smashed == EL_SP_SNIKSNAK ||
3873 smashed == EL_SP_ELECTRON ||
3874 smashed == EL_SP_DISK_ORANGE)) ||
3875 (element == EL_SP_INFOTRON &&
3876 smashed == EL_SP_DISK_YELLOW))
3881 else if (CAN_SMASH_EVERYTHING(element))
3883 if (IS_CLASSIC_ENEMY(smashed) ||
3884 CAN_EXPLODE_SMASHED(smashed))
3889 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3891 if (smashed == EL_LAMP ||
3892 smashed == EL_LAMP_ACTIVE)
3897 else if (smashed == EL_NUT)
3899 Feld[x][y + 1] = EL_NUT_BREAKING;
3900 PlayLevelSound(x, y, SND_NUT_BREAKING);
3901 RaiseScoreElement(EL_NUT);
3904 else if (smashed == EL_PEARL)
3906 ResetGfxAnimation(x, y);
3908 Feld[x][y + 1] = EL_PEARL_BREAKING;
3909 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3912 else if (smashed == EL_DIAMOND)
3914 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3915 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3918 else if (IS_BELT_SWITCH(smashed))
3920 ToggleBeltSwitch(x, y + 1);
3922 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3923 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3925 ToggleSwitchgateSwitch(x, y + 1);
3927 else if (smashed == EL_LIGHT_SWITCH ||
3928 smashed == EL_LIGHT_SWITCH_ACTIVE)
3930 ToggleLightSwitch(x, y + 1);
3935 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
3938 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
3940 CheckElementChangeBySide(x, y + 1, smashed, element,
3941 CE_SWITCHED, CH_SIDE_TOP);
3942 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
3948 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
3953 /* play sound of magic wall / mill */
3955 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3956 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3958 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3959 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3960 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3961 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3966 /* play sound of object that hits the ground */
3967 if (last_line || object_hit)
3968 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3971 inline static void TurnRoundExt(int x, int y)
3983 { 0, 0 }, { 0, 0 }, { 0, 0 },
3988 int left, right, back;
3992 { MV_DOWN, MV_UP, MV_RIGHT },
3993 { MV_UP, MV_DOWN, MV_LEFT },
3995 { MV_LEFT, MV_RIGHT, MV_DOWN },
3999 { MV_RIGHT, MV_LEFT, MV_UP }
4002 int element = Feld[x][y];
4003 int move_pattern = element_info[element].move_pattern;
4005 int old_move_dir = MovDir[x][y];
4006 int left_dir = turn[old_move_dir].left;
4007 int right_dir = turn[old_move_dir].right;
4008 int back_dir = turn[old_move_dir].back;
4010 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4011 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4012 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4013 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4015 int left_x = x + left_dx, left_y = y + left_dy;
4016 int right_x = x + right_dx, right_y = y + right_dy;
4017 int move_x = x + move_dx, move_y = y + move_dy;
4021 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4023 TestIfBadThingTouchesOtherBadThing(x, y);
4025 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4026 MovDir[x][y] = right_dir;
4027 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4028 MovDir[x][y] = left_dir;
4030 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4032 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4035 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4037 TestIfBadThingTouchesOtherBadThing(x, y);
4039 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4040 MovDir[x][y] = left_dir;
4041 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4042 MovDir[x][y] = right_dir;
4044 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4046 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4049 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4051 TestIfBadThingTouchesOtherBadThing(x, y);
4053 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4054 MovDir[x][y] = left_dir;
4055 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4056 MovDir[x][y] = right_dir;
4058 if (MovDir[x][y] != old_move_dir)
4061 else if (element == EL_YAMYAM)
4063 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4064 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4066 if (can_turn_left && can_turn_right)
4067 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4068 else if (can_turn_left)
4069 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4070 else if (can_turn_right)
4071 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4073 MovDir[x][y] = back_dir;
4075 MovDelay[x][y] = 16 + 16 * RND(3);
4077 else if (element == EL_DARK_YAMYAM)
4079 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4081 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4084 if (can_turn_left && can_turn_right)
4085 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4086 else if (can_turn_left)
4087 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4088 else if (can_turn_right)
4089 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4091 MovDir[x][y] = back_dir;
4093 MovDelay[x][y] = 16 + 16 * RND(3);
4095 else if (element == EL_PACMAN)
4097 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4098 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4100 if (can_turn_left && can_turn_right)
4101 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4102 else if (can_turn_left)
4103 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4104 else if (can_turn_right)
4105 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4107 MovDir[x][y] = back_dir;
4109 MovDelay[x][y] = 6 + RND(40);
4111 else if (element == EL_PIG)
4113 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4114 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4115 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4116 boolean should_turn_left, should_turn_right, should_move_on;
4118 int rnd = RND(rnd_value);
4120 should_turn_left = (can_turn_left &&
4122 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4123 y + back_dy + left_dy)));
4124 should_turn_right = (can_turn_right &&
4126 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4127 y + back_dy + right_dy)));
4128 should_move_on = (can_move_on &&
4131 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4132 y + move_dy + left_dy) ||
4133 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4134 y + move_dy + right_dy)));
4136 if (should_turn_left || should_turn_right || should_move_on)
4138 if (should_turn_left && should_turn_right && should_move_on)
4139 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4140 rnd < 2 * rnd_value / 3 ? right_dir :
4142 else if (should_turn_left && should_turn_right)
4143 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4144 else if (should_turn_left && should_move_on)
4145 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4146 else if (should_turn_right && should_move_on)
4147 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4148 else if (should_turn_left)
4149 MovDir[x][y] = left_dir;
4150 else if (should_turn_right)
4151 MovDir[x][y] = right_dir;
4152 else if (should_move_on)
4153 MovDir[x][y] = old_move_dir;
4155 else if (can_move_on && rnd > rnd_value / 8)
4156 MovDir[x][y] = old_move_dir;
4157 else if (can_turn_left && can_turn_right)
4158 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4159 else if (can_turn_left && rnd > rnd_value / 8)
4160 MovDir[x][y] = left_dir;
4161 else if (can_turn_right && rnd > rnd_value/8)
4162 MovDir[x][y] = right_dir;
4164 MovDir[x][y] = back_dir;
4166 xx = x + move_xy[MovDir[x][y]].x;
4167 yy = y + move_xy[MovDir[x][y]].y;
4169 if (!IN_LEV_FIELD(xx, yy) ||
4170 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4171 MovDir[x][y] = old_move_dir;
4175 else if (element == EL_DRAGON)
4177 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4178 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4179 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4181 int rnd = RND(rnd_value);
4183 if (can_move_on && rnd > rnd_value / 8)
4184 MovDir[x][y] = old_move_dir;
4185 else if (can_turn_left && can_turn_right)
4186 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4187 else if (can_turn_left && rnd > rnd_value / 8)
4188 MovDir[x][y] = left_dir;
4189 else if (can_turn_right && rnd > rnd_value / 8)
4190 MovDir[x][y] = right_dir;
4192 MovDir[x][y] = back_dir;
4194 xx = x + move_xy[MovDir[x][y]].x;
4195 yy = y + move_xy[MovDir[x][y]].y;
4197 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4198 MovDir[x][y] = old_move_dir;
4202 else if (element == EL_MOLE)
4204 boolean can_move_on =
4205 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4206 IS_AMOEBOID(Feld[move_x][move_y]) ||
4207 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4210 boolean can_turn_left =
4211 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4212 IS_AMOEBOID(Feld[left_x][left_y])));
4214 boolean can_turn_right =
4215 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4216 IS_AMOEBOID(Feld[right_x][right_y])));
4218 if (can_turn_left && can_turn_right)
4219 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4220 else if (can_turn_left)
4221 MovDir[x][y] = left_dir;
4223 MovDir[x][y] = right_dir;
4226 if (MovDir[x][y] != old_move_dir)
4229 else if (element == EL_BALLOON)
4231 MovDir[x][y] = game.wind_direction;
4234 else if (element == EL_SPRING)
4236 if (MovDir[x][y] & MV_HORIZONTAL &&
4237 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4238 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4239 MovDir[x][y] = MV_NONE;
4243 else if (element == EL_ROBOT ||
4244 element == EL_SATELLITE ||
4245 element == EL_PENGUIN)
4247 int attr_x = -1, attr_y = -1;
4258 for (i = 0; i < MAX_PLAYERS; i++)
4260 struct PlayerInfo *player = &stored_player[i];
4261 int jx = player->jx, jy = player->jy;
4263 if (!player->active)
4267 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4275 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4276 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4277 game.engine_version < VERSION_IDENT(3,1,0,0)))
4283 if (element == EL_PENGUIN)
4286 static int xy[4][2] =
4294 for (i = 0; i < NUM_DIRECTIONS; i++)
4296 int ex = x + xy[i][0];
4297 int ey = y + xy[i][1];
4299 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4308 MovDir[x][y] = MV_NONE;
4310 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4311 else if (attr_x > x)
4312 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4314 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4315 else if (attr_y > y)
4316 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4318 if (element == EL_ROBOT)
4322 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4323 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4324 Moving2Blocked(x, y, &newx, &newy);
4326 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4327 MovDelay[x][y] = 8 + 8 * !RND(3);
4329 MovDelay[x][y] = 16;
4331 else if (element == EL_PENGUIN)
4337 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4339 boolean first_horiz = RND(2);
4340 int new_move_dir = MovDir[x][y];
4343 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4344 Moving2Blocked(x, y, &newx, &newy);
4346 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4350 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4351 Moving2Blocked(x, y, &newx, &newy);
4353 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4356 MovDir[x][y] = old_move_dir;
4360 else /* (element == EL_SATELLITE) */
4366 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4368 boolean first_horiz = RND(2);
4369 int new_move_dir = MovDir[x][y];
4372 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4373 Moving2Blocked(x, y, &newx, &newy);
4375 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4379 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4380 Moving2Blocked(x, y, &newx, &newy);
4382 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4385 MovDir[x][y] = old_move_dir;
4390 else if (move_pattern == MV_TURNING_LEFT ||
4391 move_pattern == MV_TURNING_RIGHT ||
4392 move_pattern == MV_TURNING_LEFT_RIGHT ||
4393 move_pattern == MV_TURNING_RIGHT_LEFT ||
4394 move_pattern == MV_TURNING_RANDOM ||
4395 move_pattern == MV_ALL_DIRECTIONS)
4397 boolean can_turn_left =
4398 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4399 boolean can_turn_right =
4400 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4402 if (element_info[element].move_stepsize == 0) /* "not moving" */
4405 if (move_pattern == MV_TURNING_LEFT)
4406 MovDir[x][y] = left_dir;
4407 else if (move_pattern == MV_TURNING_RIGHT)
4408 MovDir[x][y] = right_dir;
4409 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4410 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4411 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4412 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4413 else if (move_pattern == MV_TURNING_RANDOM)
4414 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4415 can_turn_right && !can_turn_left ? right_dir :
4416 RND(2) ? left_dir : right_dir);
4417 else if (can_turn_left && can_turn_right)
4418 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4419 else if (can_turn_left)
4420 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4421 else if (can_turn_right)
4422 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4424 MovDir[x][y] = back_dir;
4426 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4428 else if (move_pattern == MV_HORIZONTAL ||
4429 move_pattern == MV_VERTICAL)
4431 if (move_pattern & old_move_dir)
4432 MovDir[x][y] = back_dir;
4433 else if (move_pattern == MV_HORIZONTAL)
4434 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4435 else if (move_pattern == MV_VERTICAL)
4436 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4438 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4440 else if (move_pattern & MV_ANY_DIRECTION)
4442 MovDir[x][y] = move_pattern;
4443 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4445 else if (move_pattern & MV_WIND_DIRECTION)
4447 MovDir[x][y] = game.wind_direction;
4448 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4450 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4452 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4453 MovDir[x][y] = left_dir;
4454 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4455 MovDir[x][y] = right_dir;
4457 if (MovDir[x][y] != old_move_dir)
4458 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4460 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4462 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4463 MovDir[x][y] = right_dir;
4464 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4465 MovDir[x][y] = left_dir;
4467 if (MovDir[x][y] != old_move_dir)
4468 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4470 else if (move_pattern == MV_TOWARDS_PLAYER ||
4471 move_pattern == MV_AWAY_FROM_PLAYER)
4473 int attr_x = -1, attr_y = -1;
4475 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4486 for (i = 0; i < MAX_PLAYERS; i++)
4488 struct PlayerInfo *player = &stored_player[i];
4489 int jx = player->jx, jy = player->jy;
4491 if (!player->active)
4495 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4503 MovDir[x][y] = MV_NONE;
4505 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4506 else if (attr_x > x)
4507 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4509 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4510 else if (attr_y > y)
4511 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4513 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4515 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4517 boolean first_horiz = RND(2);
4518 int new_move_dir = MovDir[x][y];
4520 if (element_info[element].move_stepsize == 0) /* "not moving" */
4522 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
4523 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4529 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4530 Moving2Blocked(x, y, &newx, &newy);
4532 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4536 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4537 Moving2Blocked(x, y, &newx, &newy);
4539 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4542 MovDir[x][y] = old_move_dir;
4545 else if (move_pattern == MV_WHEN_PUSHED ||
4546 move_pattern == MV_WHEN_DROPPED)
4548 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4549 MovDir[x][y] = MV_NONE;
4553 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4555 static int test_xy[7][2] =
4565 static int test_dir[7] =
4575 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4576 int move_preference = -1000000; /* start with very low preference */
4577 int new_move_dir = MV_NONE;
4578 int start_test = RND(4);
4581 for (i = 0; i < NUM_DIRECTIONS; i++)
4583 int move_dir = test_dir[start_test + i];
4584 int move_dir_preference;
4586 xx = x + test_xy[start_test + i][0];
4587 yy = y + test_xy[start_test + i][1];
4589 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4590 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4592 new_move_dir = move_dir;
4597 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4600 move_dir_preference = -1 * RunnerVisit[xx][yy];
4601 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4602 move_dir_preference = PlayerVisit[xx][yy];
4604 if (move_dir_preference > move_preference)
4606 /* prefer field that has not been visited for the longest time */
4607 move_preference = move_dir_preference;
4608 new_move_dir = move_dir;
4610 else if (move_dir_preference == move_preference &&
4611 move_dir == old_move_dir)
4613 /* prefer last direction when all directions are preferred equally */
4614 move_preference = move_dir_preference;
4615 new_move_dir = move_dir;
4619 MovDir[x][y] = new_move_dir;
4620 if (old_move_dir != new_move_dir)
4621 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4625 static void TurnRound(int x, int y)
4627 int direction = MovDir[x][y];
4631 GfxDir[x][y] = MovDir[x][y];
4633 if (direction != MovDir[x][y])
4637 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4640 static boolean JustBeingPushed(int x, int y)
4644 for (i = 0; i < MAX_PLAYERS; i++)
4646 struct PlayerInfo *player = &stored_player[i];
4648 if (player->active && player->is_pushing && player->MovPos)
4650 int next_jx = player->jx + (player->jx - player->last_jx);
4651 int next_jy = player->jy + (player->jy - player->last_jy);
4653 if (x == next_jx && y == next_jy)
4661 void StartMoving(int x, int y)
4663 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4664 int element = Feld[x][y];
4669 if (MovDelay[x][y] == 0)
4670 GfxAction[x][y] = ACTION_DEFAULT;
4672 if (CAN_FALL(element) && y < lev_fieldy - 1)
4674 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4675 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4676 if (JustBeingPushed(x, y))
4679 if (element == EL_QUICKSAND_FULL)
4681 if (IS_FREE(x, y + 1))
4683 InitMovingField(x, y, MV_DOWN);
4684 started_moving = TRUE;
4686 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4687 Store[x][y] = EL_ROCK;
4689 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4691 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4693 if (!MovDelay[x][y])
4694 MovDelay[x][y] = TILEY + 1;
4703 Feld[x][y] = EL_QUICKSAND_EMPTY;
4704 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4705 Store[x][y + 1] = Store[x][y];
4708 PlayLevelSoundAction(x, y, ACTION_FILLING);
4711 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4712 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4714 InitMovingField(x, y, MV_DOWN);
4715 started_moving = TRUE;
4717 Feld[x][y] = EL_QUICKSAND_FILLING;
4718 Store[x][y] = element;
4720 PlayLevelSoundAction(x, y, ACTION_FILLING);
4722 else if (element == EL_MAGIC_WALL_FULL)
4724 if (IS_FREE(x, y + 1))
4726 InitMovingField(x, y, MV_DOWN);
4727 started_moving = TRUE;
4729 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4730 Store[x][y] = EL_CHANGED(Store[x][y]);
4732 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4734 if (!MovDelay[x][y])
4735 MovDelay[x][y] = TILEY/4 + 1;
4744 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4745 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4746 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4750 else if (element == EL_BD_MAGIC_WALL_FULL)
4752 if (IS_FREE(x, y + 1))
4754 InitMovingField(x, y, MV_DOWN);
4755 started_moving = TRUE;
4757 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4758 Store[x][y] = EL_CHANGED2(Store[x][y]);
4760 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4762 if (!MovDelay[x][y])
4763 MovDelay[x][y] = TILEY/4 + 1;
4772 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4773 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4774 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4778 else if (CAN_PASS_MAGIC_WALL(element) &&
4779 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4780 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4782 InitMovingField(x, y, MV_DOWN);
4783 started_moving = TRUE;
4786 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4787 EL_BD_MAGIC_WALL_FILLING);
4788 Store[x][y] = element;
4790 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4792 SplashAcid(x, y + 1);
4794 InitMovingField(x, y, MV_DOWN);
4795 started_moving = TRUE;
4797 Store[x][y] = EL_ACID;
4799 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4800 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
4802 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4803 CAN_FALL(element) && WasJustFalling[x][y] &&
4804 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
4806 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4807 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4808 (Feld[x][y + 1] == EL_BLOCKED)))
4810 /* this is needed for a special case not covered by calling "Impact()"
4811 from "ContinueMoving()": if an element moves to a tile directly below
4812 another element which was just falling on that tile (which was empty
4813 in the previous frame), the falling element above would just stop
4814 instead of smashing the element below (in previous version, the above
4815 element was just checked for "moving" instead of "falling", resulting
4816 in incorrect smashes caused by horizontal movement of the above
4817 element; also, the case of the player being the element to smash was
4818 simply not covered here... :-/ ) */
4820 CheckCollision[x][y] = 0;
4824 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4826 if (MovDir[x][y] == MV_NONE)
4828 InitMovingField(x, y, MV_DOWN);
4829 started_moving = TRUE;
4832 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4834 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4835 MovDir[x][y] = MV_DOWN;
4837 InitMovingField(x, y, MV_DOWN);
4838 started_moving = TRUE;
4840 else if (element == EL_AMOEBA_DROP)
4842 Feld[x][y] = EL_AMOEBA_GROWING;
4843 Store[x][y] = EL_AMOEBA_WET;
4845 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4846 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4847 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4848 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4850 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4851 (IS_FREE(x - 1, y + 1) ||
4852 Feld[x - 1][y + 1] == EL_ACID));
4853 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4854 (IS_FREE(x + 1, y + 1) ||
4855 Feld[x + 1][y + 1] == EL_ACID));
4856 boolean can_fall_any = (can_fall_left || can_fall_right);
4857 boolean can_fall_both = (can_fall_left && can_fall_right);
4858 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4860 #if USE_NEW_ALL_SLIPPERY
4861 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
4863 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4864 can_fall_right = FALSE;
4865 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4866 can_fall_left = FALSE;
4867 else if (slippery_type == SLIPPERY_ONLY_LEFT)
4868 can_fall_right = FALSE;
4869 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4870 can_fall_left = FALSE;
4872 can_fall_any = (can_fall_left || can_fall_right);
4873 can_fall_both = FALSE;
4876 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4878 if (slippery_type == SLIPPERY_ONLY_LEFT)
4879 can_fall_right = FALSE;
4880 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4881 can_fall_left = FALSE;
4882 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4883 can_fall_right = FALSE;
4884 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4885 can_fall_left = FALSE;
4887 can_fall_any = (can_fall_left || can_fall_right);
4888 can_fall_both = (can_fall_left && can_fall_right);
4892 #if USE_NEW_ALL_SLIPPERY
4894 #if USE_NEW_SP_SLIPPERY
4895 /* !!! better use the same properties as for custom elements here !!! */
4896 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
4897 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
4899 can_fall_right = FALSE; /* slip down on left side */
4900 can_fall_both = FALSE;
4905 #if USE_NEW_ALL_SLIPPERY
4908 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
4909 can_fall_right = FALSE; /* slip down on left side */
4911 can_fall_left = !(can_fall_right = RND(2));
4913 can_fall_both = FALSE;
4918 if (game.emulation == EMU_BOULDERDASH ||
4919 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
4920 can_fall_right = FALSE; /* slip down on left side */
4922 can_fall_left = !(can_fall_right = RND(2));
4924 can_fall_both = FALSE;
4930 /* if not determined otherwise, prefer left side for slipping down */
4931 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4932 started_moving = TRUE;
4936 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4938 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4941 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4942 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4943 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4944 int belt_dir = game.belt_dir[belt_nr];
4946 if ((belt_dir == MV_LEFT && left_is_free) ||
4947 (belt_dir == MV_RIGHT && right_is_free))
4949 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4951 InitMovingField(x, y, belt_dir);
4952 started_moving = TRUE;
4954 Pushed[x][y] = TRUE;
4955 Pushed[nextx][y] = TRUE;
4957 GfxAction[x][y] = ACTION_DEFAULT;
4961 MovDir[x][y] = 0; /* if element was moving, stop it */
4966 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4968 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
4970 if (CAN_MOVE(element) && !started_moving)
4973 int move_pattern = element_info[element].move_pattern;
4978 if (MovDir[x][y] == MV_NONE)
4980 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
4981 x, y, element, element_info[element].token_name);
4982 printf("StartMoving(): This should never happen!\n");
4987 Moving2Blocked(x, y, &newx, &newy);
4989 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4992 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4993 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
4995 WasJustMoving[x][y] = 0;
4996 CheckCollision[x][y] = 0;
4998 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5000 if (Feld[x][y] != element) /* element has changed */
5004 if (!MovDelay[x][y]) /* start new movement phase */
5006 /* all objects that can change their move direction after each step
5007 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5009 if (element != EL_YAMYAM &&
5010 element != EL_DARK_YAMYAM &&
5011 element != EL_PACMAN &&
5012 !(move_pattern & MV_ANY_DIRECTION) &&
5013 move_pattern != MV_TURNING_LEFT &&
5014 move_pattern != MV_TURNING_RIGHT &&
5015 move_pattern != MV_TURNING_LEFT_RIGHT &&
5016 move_pattern != MV_TURNING_RIGHT_LEFT &&
5017 move_pattern != MV_TURNING_RANDOM)
5021 if (MovDelay[x][y] && (element == EL_BUG ||
5022 element == EL_SPACESHIP ||
5023 element == EL_SP_SNIKSNAK ||
5024 element == EL_SP_ELECTRON ||
5025 element == EL_MOLE))
5026 DrawLevelField(x, y);
5030 if (MovDelay[x][y]) /* wait some time before next movement */
5034 if (element == EL_ROBOT ||
5035 element == EL_YAMYAM ||
5036 element == EL_DARK_YAMYAM)
5038 DrawLevelElementAnimationIfNeeded(x, y, element);
5039 PlayLevelSoundAction(x, y, ACTION_WAITING);
5041 else if (element == EL_SP_ELECTRON)
5042 DrawLevelElementAnimationIfNeeded(x, y, element);
5043 else if (element == EL_DRAGON)
5046 int dir = MovDir[x][y];
5047 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5048 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5049 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5050 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5051 dir == MV_UP ? IMG_FLAMES_1_UP :
5052 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5053 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5055 GfxAction[x][y] = ACTION_ATTACKING;
5057 if (IS_PLAYER(x, y))
5058 DrawPlayerField(x, y);
5060 DrawLevelField(x, y);
5062 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5064 for (i = 1; i <= 3; i++)
5066 int xx = x + i * dx;
5067 int yy = y + i * dy;
5068 int sx = SCREENX(xx);
5069 int sy = SCREENY(yy);
5070 int flame_graphic = graphic + (i - 1);
5072 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5077 int flamed = MovingOrBlocked2Element(xx, yy);
5081 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5083 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5084 RemoveMovingField(xx, yy);
5086 RemoveField(xx, yy);
5088 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5091 RemoveMovingField(xx, yy);
5094 ChangeDelay[xx][yy] = 0;
5096 Feld[xx][yy] = EL_FLAMES;
5098 if (IN_SCR_FIELD(sx, sy))
5100 DrawLevelFieldCrumbledSand(xx, yy);
5101 DrawGraphic(sx, sy, flame_graphic, frame);
5106 if (Feld[xx][yy] == EL_FLAMES)
5107 Feld[xx][yy] = EL_EMPTY;
5108 DrawLevelField(xx, yy);
5113 if (MovDelay[x][y]) /* element still has to wait some time */
5115 PlayLevelSoundAction(x, y, ACTION_WAITING);
5121 /* now make next step */
5123 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5125 if (DONT_COLLIDE_WITH(element) &&
5126 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5127 !PLAYER_ENEMY_PROTECTED(newx, newy))
5129 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5134 else if (CAN_MOVE_INTO_ACID(element) &&
5135 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5136 (MovDir[x][y] == MV_DOWN ||
5137 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5139 SplashAcid(newx, newy);
5140 Store[x][y] = EL_ACID;
5142 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5144 if (Feld[newx][newy] == EL_EXIT_OPEN)
5147 DrawLevelField(x, y);
5149 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5150 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5151 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5153 local_player->friends_still_needed--;
5154 if (!local_player->friends_still_needed &&
5155 !local_player->GameOver && AllPlayersGone)
5156 local_player->LevelSolved = local_player->GameOver = TRUE;
5160 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5162 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5163 DrawLevelField(newx, newy);
5165 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5167 else if (!IS_FREE(newx, newy))
5169 GfxAction[x][y] = ACTION_WAITING;
5171 if (IS_PLAYER(x, y))
5172 DrawPlayerField(x, y);
5174 DrawLevelField(x, y);
5179 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5181 if (IS_FOOD_PIG(Feld[newx][newy]))
5183 if (IS_MOVING(newx, newy))
5184 RemoveMovingField(newx, newy);
5187 Feld[newx][newy] = EL_EMPTY;
5188 DrawLevelField(newx, newy);
5191 PlayLevelSound(x, y, SND_PIG_DIGGING);
5193 else if (!IS_FREE(newx, newy))
5195 if (IS_PLAYER(x, y))
5196 DrawPlayerField(x, y);
5198 DrawLevelField(x, y);
5203 else if (IS_CUSTOM_ELEMENT(element) &&
5204 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5206 int new_element = Feld[newx][newy];
5208 if (!IS_FREE(newx, newy))
5210 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5211 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5214 /* no element can dig solid indestructible elements */
5215 if (IS_INDESTRUCTIBLE(new_element) &&
5216 !IS_DIGGABLE(new_element) &&
5217 !IS_COLLECTIBLE(new_element))
5220 if (AmoebaNr[newx][newy] &&
5221 (new_element == EL_AMOEBA_FULL ||
5222 new_element == EL_BD_AMOEBA ||
5223 new_element == EL_AMOEBA_GROWING))
5225 AmoebaCnt[AmoebaNr[newx][newy]]--;
5226 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5229 if (IS_MOVING(newx, newy))
5230 RemoveMovingField(newx, newy);
5233 RemoveField(newx, newy);
5234 DrawLevelField(newx, newy);
5237 /* if digged element was about to explode, prevent the explosion */
5238 ExplodeField[newx][newy] = EX_TYPE_NONE;
5240 PlayLevelSoundAction(x, y, action);
5243 Store[newx][newy] = EL_EMPTY;
5244 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5246 int move_leave_element = element_info[element].move_leave_element;
5248 /* this makes it possible to leave the removed element again */
5249 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
5250 new_element : move_leave_element);
5253 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5255 RunnerVisit[x][y] = FrameCounter;
5256 PlayerVisit[x][y] /= 8; /* expire player visit path */
5259 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5261 if (!IS_FREE(newx, newy))
5263 if (IS_PLAYER(x, y))
5264 DrawPlayerField(x, y);
5266 DrawLevelField(x, y);
5272 boolean wanna_flame = !RND(10);
5273 int dx = newx - x, dy = newy - y;
5274 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5275 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5276 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5277 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5278 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5279 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5282 IS_CLASSIC_ENEMY(element1) ||
5283 IS_CLASSIC_ENEMY(element2)) &&
5284 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5285 element1 != EL_FLAMES && element2 != EL_FLAMES)
5287 ResetGfxAnimation(x, y);
5288 GfxAction[x][y] = ACTION_ATTACKING;
5290 if (IS_PLAYER(x, y))
5291 DrawPlayerField(x, y);
5293 DrawLevelField(x, y);
5295 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5297 MovDelay[x][y] = 50;
5301 RemoveField(newx, newy);
5303 Feld[newx][newy] = EL_FLAMES;
5304 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5307 RemoveField(newx1, newy1);
5309 Feld[newx1][newy1] = EL_FLAMES;
5311 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5314 RemoveField(newx2, newy2);
5316 Feld[newx2][newy2] = EL_FLAMES;
5323 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5324 Feld[newx][newy] == EL_DIAMOND)
5326 if (IS_MOVING(newx, newy))
5327 RemoveMovingField(newx, newy);
5330 Feld[newx][newy] = EL_EMPTY;
5331 DrawLevelField(newx, newy);
5334 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5336 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5337 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5339 if (AmoebaNr[newx][newy])
5341 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5342 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5343 Feld[newx][newy] == EL_BD_AMOEBA)
5344 AmoebaCnt[AmoebaNr[newx][newy]]--;
5349 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5351 RemoveMovingField(newx, newy);
5354 if (IS_MOVING(newx, newy))
5356 RemoveMovingField(newx, newy);
5361 Feld[newx][newy] = EL_EMPTY;
5362 DrawLevelField(newx, newy);
5365 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5367 else if ((element == EL_PACMAN || element == EL_MOLE)
5368 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5370 if (AmoebaNr[newx][newy])
5372 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5373 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5374 Feld[newx][newy] == EL_BD_AMOEBA)
5375 AmoebaCnt[AmoebaNr[newx][newy]]--;
5378 if (element == EL_MOLE)
5380 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5381 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5383 ResetGfxAnimation(x, y);
5384 GfxAction[x][y] = ACTION_DIGGING;
5385 DrawLevelField(x, y);
5387 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5389 return; /* wait for shrinking amoeba */
5391 else /* element == EL_PACMAN */
5393 Feld[newx][newy] = EL_EMPTY;
5394 DrawLevelField(newx, newy);
5395 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5398 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5399 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5400 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5402 /* wait for shrinking amoeba to completely disappear */
5405 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5407 /* object was running against a wall */
5412 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
5413 if (move_pattern & MV_ANY_DIRECTION &&
5414 move_pattern == MovDir[x][y])
5416 int blocking_element =
5417 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5419 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5422 element = Feld[x][y]; /* element might have changed */
5426 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5427 DrawLevelElementAnimation(x, y, element);
5429 if (DONT_TOUCH(element))
5430 TestIfBadThingTouchesPlayer(x, y);
5435 InitMovingField(x, y, MovDir[x][y]);
5437 PlayLevelSoundAction(x, y, ACTION_MOVING);
5441 ContinueMoving(x, y);
5444 void ContinueMoving(int x, int y)
5446 int element = Feld[x][y];
5447 int stored = Store[x][y];
5448 struct ElementInfo *ei = &element_info[element];
5449 int direction = MovDir[x][y];
5450 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5451 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5452 int newx = x + dx, newy = y + dy;
5453 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5454 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5455 boolean last_line = (newy == lev_fieldy - 1);
5457 MovPos[x][y] += getElementMoveStepsize(x, y);
5459 if (pushed_by_player) /* special case: moving object pushed by player */
5460 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5462 if (ABS(MovPos[x][y]) < TILEX)
5464 DrawLevelField(x, y);
5466 return; /* element is still moving */
5469 /* element reached destination field */
5471 Feld[x][y] = EL_EMPTY;
5472 Feld[newx][newy] = element;
5473 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5475 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
5477 element = Feld[newx][newy] = EL_ACID;
5479 else if (element == EL_MOLE)
5481 Feld[x][y] = EL_SAND;
5483 DrawLevelFieldCrumbledSandNeighbours(x, y);
5485 else if (element == EL_QUICKSAND_FILLING)
5487 element = Feld[newx][newy] = get_next_element(element);
5488 Store[newx][newy] = Store[x][y];
5490 else if (element == EL_QUICKSAND_EMPTYING)
5492 Feld[x][y] = get_next_element(element);
5493 element = Feld[newx][newy] = Store[x][y];
5495 else if (element == EL_MAGIC_WALL_FILLING)
5497 element = Feld[newx][newy] = get_next_element(element);
5498 if (!game.magic_wall_active)
5499 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5500 Store[newx][newy] = Store[x][y];
5502 else if (element == EL_MAGIC_WALL_EMPTYING)
5504 Feld[x][y] = get_next_element(element);
5505 if (!game.magic_wall_active)
5506 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5507 element = Feld[newx][newy] = Store[x][y];
5509 #if USE_NEW_CUSTOM_VALUE
5510 InitField(newx, newy, FALSE);
5513 else if (element == EL_BD_MAGIC_WALL_FILLING)
5515 element = Feld[newx][newy] = get_next_element(element);
5516 if (!game.magic_wall_active)
5517 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5518 Store[newx][newy] = Store[x][y];
5520 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5522 Feld[x][y] = get_next_element(element);
5523 if (!game.magic_wall_active)
5524 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5525 element = Feld[newx][newy] = Store[x][y];
5527 #if USE_NEW_CUSTOM_VALUE
5528 InitField(newx, newy, FALSE);
5531 else if (element == EL_AMOEBA_DROPPING)
5533 Feld[x][y] = get_next_element(element);
5534 element = Feld[newx][newy] = Store[x][y];
5536 else if (element == EL_SOKOBAN_OBJECT)
5539 Feld[x][y] = Back[x][y];
5541 if (Back[newx][newy])
5542 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5544 Back[x][y] = Back[newx][newy] = 0;
5547 Store[x][y] = EL_EMPTY;
5552 MovDelay[newx][newy] = 0;
5554 if (CAN_CHANGE(element))
5556 /* copy element change control values to new field */
5557 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5558 ChangePage[newx][newy] = ChangePage[x][y];
5559 Changed[newx][newy] = Changed[x][y];
5560 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5562 #if USE_NEW_CUSTOM_VALUE
5563 CustomValue[newx][newy] = CustomValue[x][y];
5567 ChangeDelay[x][y] = 0;
5568 ChangePage[x][y] = -1;
5569 Changed[x][y] = FALSE;
5570 ChangeEvent[x][y] = -1;
5572 #if USE_NEW_CUSTOM_VALUE
5573 CustomValue[x][y] = 0;
5576 /* copy animation control values to new field */
5577 GfxFrame[newx][newy] = GfxFrame[x][y];
5578 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5579 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5580 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5582 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5584 /* some elements can leave other elements behind after moving */
5585 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
5586 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
5587 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
5589 int move_leave_element = ei->move_leave_element;
5591 /* this makes it possible to leave the removed element again */
5592 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
5593 ei->move_leave_element == EL_TRIGGER_ELEMENT)
5594 move_leave_element = stored;
5596 Feld[x][y] = move_leave_element;
5598 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
5599 MovDir[x][y] = direction;
5601 InitField(x, y, FALSE);
5603 if (GFX_CRUMBLED(Feld[x][y]))
5604 DrawLevelFieldCrumbledSandNeighbours(x, y);
5606 if (ELEM_IS_PLAYER(move_leave_element))
5607 RelocatePlayer(x, y, move_leave_element);
5610 /* do this after checking for left-behind element */
5611 ResetGfxAnimation(x, y); /* reset animation values for old field */
5613 if (!CAN_MOVE(element) ||
5614 (CAN_FALL(element) && direction == MV_DOWN &&
5615 (element == EL_SPRING ||
5616 element_info[element].move_pattern == MV_WHEN_PUSHED ||
5617 element_info[element].move_pattern == MV_WHEN_DROPPED)))
5618 GfxDir[x][y] = MovDir[newx][newy] = 0;
5620 DrawLevelField(x, y);
5621 DrawLevelField(newx, newy);
5623 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5625 /* prevent pushed element from moving on in pushed direction */
5626 if (pushed_by_player && CAN_MOVE(element) &&
5627 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5628 !(element_info[element].move_pattern & direction))
5629 TurnRound(newx, newy);
5631 /* prevent elements on conveyor belt from moving on in last direction */
5632 if (pushed_by_conveyor && CAN_FALL(element) &&
5633 direction & MV_HORIZONTAL)
5634 MovDir[newx][newy] = 0;
5636 if (!pushed_by_player)
5638 int nextx = newx + dx, nexty = newy + dy;
5639 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
5641 WasJustMoving[newx][newy] = 3;
5643 if (CAN_FALL(element) && direction == MV_DOWN)
5644 WasJustFalling[newx][newy] = 3;
5646 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
5647 CheckCollision[newx][newy] = 2;
5650 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5652 TestIfBadThingTouchesPlayer(newx, newy);
5653 TestIfBadThingTouchesFriend(newx, newy);
5655 if (!IS_CUSTOM_ELEMENT(element))
5656 TestIfBadThingTouchesOtherBadThing(newx, newy);
5658 else if (element == EL_PENGUIN)
5659 TestIfFriendTouchesBadThing(newx, newy);
5661 /* give the player one last chance (one more frame) to move away */
5662 if (CAN_FALL(element) && direction == MV_DOWN &&
5663 (last_line || (!IS_FREE(x, newy + 1) &&
5664 (!IS_PLAYER(x, newy + 1) ||
5665 game.engine_version < VERSION_IDENT(3,1,1,0)))))
5668 if (pushed_by_player && !game.use_change_when_pushing_bug)
5670 int dig_side = MV_DIR_OPPOSITE(direction);
5671 struct PlayerInfo *player = PLAYERINFO(x, y);
5673 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
5674 player->index_bit, dig_side);
5675 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
5676 player->index_bit, dig_side);
5679 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5681 TestIfElementHitsCustomElement(newx, newy, direction);
5682 TestIfPlayerTouchesCustomElement(newx, newy);
5683 TestIfElementTouchesCustomElement(newx, newy);
5686 int AmoebeNachbarNr(int ax, int ay)
5689 int element = Feld[ax][ay];
5691 static int xy[4][2] =
5699 for (i = 0; i < NUM_DIRECTIONS; i++)
5701 int x = ax + xy[i][0];
5702 int y = ay + xy[i][1];
5704 if (!IN_LEV_FIELD(x, y))
5707 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5708 group_nr = AmoebaNr[x][y];
5714 void AmoebenVereinigen(int ax, int ay)
5716 int i, x, y, xx, yy;
5717 int new_group_nr = AmoebaNr[ax][ay];
5718 static int xy[4][2] =
5726 if (new_group_nr == 0)
5729 for (i = 0; i < NUM_DIRECTIONS; i++)
5734 if (!IN_LEV_FIELD(x, y))
5737 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5738 Feld[x][y] == EL_BD_AMOEBA ||
5739 Feld[x][y] == EL_AMOEBA_DEAD) &&
5740 AmoebaNr[x][y] != new_group_nr)
5742 int old_group_nr = AmoebaNr[x][y];
5744 if (old_group_nr == 0)
5747 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5748 AmoebaCnt[old_group_nr] = 0;
5749 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5750 AmoebaCnt2[old_group_nr] = 0;
5752 for (yy = 0; yy < lev_fieldy; yy++)
5754 for (xx = 0; xx < lev_fieldx; xx++)
5756 if (AmoebaNr[xx][yy] == old_group_nr)
5757 AmoebaNr[xx][yy] = new_group_nr;
5764 void AmoebeUmwandeln(int ax, int ay)
5768 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5770 int group_nr = AmoebaNr[ax][ay];
5775 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5776 printf("AmoebeUmwandeln(): This should never happen!\n");
5781 for (y = 0; y < lev_fieldy; y++)
5783 for (x = 0; x < lev_fieldx; x++)
5785 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5788 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5792 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5793 SND_AMOEBA_TURNING_TO_GEM :
5794 SND_AMOEBA_TURNING_TO_ROCK));
5799 static int xy[4][2] =
5807 for (i = 0; i < NUM_DIRECTIONS; i++)
5812 if (!IN_LEV_FIELD(x, y))
5815 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5817 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5818 SND_AMOEBA_TURNING_TO_GEM :
5819 SND_AMOEBA_TURNING_TO_ROCK));
5826 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5829 int group_nr = AmoebaNr[ax][ay];
5830 boolean done = FALSE;
5835 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5836 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5841 for (y = 0; y < lev_fieldy; y++)
5843 for (x = 0; x < lev_fieldx; x++)
5845 if (AmoebaNr[x][y] == group_nr &&
5846 (Feld[x][y] == EL_AMOEBA_DEAD ||
5847 Feld[x][y] == EL_BD_AMOEBA ||
5848 Feld[x][y] == EL_AMOEBA_GROWING))
5851 Feld[x][y] = new_element;
5852 InitField(x, y, FALSE);
5853 DrawLevelField(x, y);
5860 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5861 SND_BD_AMOEBA_TURNING_TO_ROCK :
5862 SND_BD_AMOEBA_TURNING_TO_GEM));
5865 void AmoebeWaechst(int x, int y)
5867 static unsigned long sound_delay = 0;
5868 static unsigned long sound_delay_value = 0;
5870 if (!MovDelay[x][y]) /* start new growing cycle */
5874 if (DelayReached(&sound_delay, sound_delay_value))
5876 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5877 sound_delay_value = 30;
5881 if (MovDelay[x][y]) /* wait some time before growing bigger */
5884 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5886 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5887 6 - MovDelay[x][y]);
5889 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5892 if (!MovDelay[x][y])
5894 Feld[x][y] = Store[x][y];
5896 DrawLevelField(x, y);
5901 void AmoebaDisappearing(int x, int y)
5903 static unsigned long sound_delay = 0;
5904 static unsigned long sound_delay_value = 0;
5906 if (!MovDelay[x][y]) /* start new shrinking cycle */
5910 if (DelayReached(&sound_delay, sound_delay_value))
5911 sound_delay_value = 30;
5914 if (MovDelay[x][y]) /* wait some time before shrinking */
5917 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5919 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5920 6 - MovDelay[x][y]);
5922 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5925 if (!MovDelay[x][y])
5927 Feld[x][y] = EL_EMPTY;
5928 DrawLevelField(x, y);
5930 /* don't let mole enter this field in this cycle;
5931 (give priority to objects falling to this field from above) */
5937 void AmoebeAbleger(int ax, int ay)
5940 int element = Feld[ax][ay];
5941 int graphic = el2img(element);
5942 int newax = ax, neway = ay;
5943 static int xy[4][2] =
5951 if (!level.amoeba_speed)
5953 Feld[ax][ay] = EL_AMOEBA_DEAD;
5954 DrawLevelField(ax, ay);
5958 if (IS_ANIMATED(graphic))
5959 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5961 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5962 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5964 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5967 if (MovDelay[ax][ay])
5971 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5974 int x = ax + xy[start][0];
5975 int y = ay + xy[start][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 if (newax == ax && neway == ay)
5991 else /* normal or "filled" (BD style) amoeba */
5994 boolean waiting_for_player = FALSE;
5996 for (i = 0; i < NUM_DIRECTIONS; i++)
5998 int j = (start + i) % 4;
5999 int x = ax + xy[j][0];
6000 int y = ay + xy[j][1];
6002 if (!IN_LEV_FIELD(x, y))
6005 if (IS_FREE(x, y) ||
6006 CAN_GROW_INTO(Feld[x][y]) ||
6007 Feld[x][y] == EL_QUICKSAND_EMPTY)
6013 else if (IS_PLAYER(x, y))
6014 waiting_for_player = TRUE;
6017 if (newax == ax && neway == ay) /* amoeba cannot grow */
6019 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6021 Feld[ax][ay] = EL_AMOEBA_DEAD;
6022 DrawLevelField(ax, ay);
6023 AmoebaCnt[AmoebaNr[ax][ay]]--;
6025 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6027 if (element == EL_AMOEBA_FULL)
6028 AmoebeUmwandeln(ax, ay);
6029 else if (element == EL_BD_AMOEBA)
6030 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6035 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6037 /* amoeba gets larger by growing in some direction */
6039 int new_group_nr = AmoebaNr[ax][ay];
6042 if (new_group_nr == 0)
6044 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6045 printf("AmoebeAbleger(): This should never happen!\n");
6050 AmoebaNr[newax][neway] = new_group_nr;
6051 AmoebaCnt[new_group_nr]++;
6052 AmoebaCnt2[new_group_nr]++;
6054 /* if amoeba touches other amoeba(s) after growing, unify them */
6055 AmoebenVereinigen(newax, neway);
6057 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6059 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6065 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6066 (neway == lev_fieldy - 1 && newax != ax))
6068 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6069 Store[newax][neway] = element;
6071 else if (neway == ay)
6073 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6075 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6079 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6080 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6081 Store[ax][ay] = EL_AMOEBA_DROP;
6082 ContinueMoving(ax, ay);
6086 DrawLevelField(newax, neway);
6089 void Life(int ax, int ay)
6093 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6096 int element = Feld[ax][ay];
6097 int graphic = el2img(element);
6098 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6100 boolean changed = FALSE;
6102 if (IS_ANIMATED(graphic))
6103 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6108 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6109 MovDelay[ax][ay] = life_time;
6111 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6114 if (MovDelay[ax][ay])
6118 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6120 int xx = ax+x1, yy = ay+y1;
6123 if (!IN_LEV_FIELD(xx, yy))
6126 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6128 int x = xx+x2, y = yy+y2;
6130 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6133 if (((Feld[x][y] == element ||
6134 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6136 (IS_FREE(x, y) && Stop[x][y]))
6140 if (xx == ax && yy == ay) /* field in the middle */
6142 if (nachbarn < life_parameter[0] ||
6143 nachbarn > life_parameter[1])
6145 Feld[xx][yy] = EL_EMPTY;
6147 DrawLevelField(xx, yy);
6148 Stop[xx][yy] = TRUE;
6152 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6153 { /* free border field */
6154 if (nachbarn >= life_parameter[2] &&
6155 nachbarn <= life_parameter[3])
6157 Feld[xx][yy] = element;
6158 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6160 DrawLevelField(xx, yy);
6161 Stop[xx][yy] = TRUE;
6168 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6169 SND_GAME_OF_LIFE_GROWING);
6172 static void InitRobotWheel(int x, int y)
6174 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6177 static void RunRobotWheel(int x, int y)
6179 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6182 static void StopRobotWheel(int x, int y)
6184 if (ZX == x && ZY == y)
6188 static void InitTimegateWheel(int x, int y)
6190 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6193 static void RunTimegateWheel(int x, int y)
6195 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6198 void CheckExit(int x, int y)
6200 if (local_player->gems_still_needed > 0 ||
6201 local_player->sokobanfields_still_needed > 0 ||
6202 local_player->lights_still_needed > 0)
6204 int element = Feld[x][y];
6205 int graphic = el2img(element);
6207 if (IS_ANIMATED(graphic))
6208 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6213 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6216 Feld[x][y] = EL_EXIT_OPENING;
6218 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6221 void CheckExitSP(int x, int y)
6223 if (local_player->gems_still_needed > 0)
6225 int element = Feld[x][y];
6226 int graphic = el2img(element);
6228 if (IS_ANIMATED(graphic))
6229 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6234 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6237 Feld[x][y] = EL_SP_EXIT_OPENING;
6239 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6242 static void CloseAllOpenTimegates()
6246 for (y = 0; y < lev_fieldy; y++)
6248 for (x = 0; x < lev_fieldx; x++)
6250 int element = Feld[x][y];
6252 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6254 Feld[x][y] = EL_TIMEGATE_CLOSING;
6256 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6262 void EdelsteinFunkeln(int x, int y)
6264 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6267 if (Feld[x][y] == EL_BD_DIAMOND)
6270 if (MovDelay[x][y] == 0) /* next animation frame */
6271 MovDelay[x][y] = 11 * !SimpleRND(500);
6273 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6277 if (setup.direct_draw && MovDelay[x][y])
6278 SetDrawtoField(DRAW_BUFFERED);
6280 DrawLevelElementAnimation(x, y, Feld[x][y]);
6282 if (MovDelay[x][y] != 0)
6284 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6285 10 - MovDelay[x][y]);
6287 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6289 if (setup.direct_draw)
6293 dest_x = FX + SCREENX(x) * TILEX;
6294 dest_y = FY + SCREENY(y) * TILEY;
6296 BlitBitmap(drawto_field, window,
6297 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6298 SetDrawtoField(DRAW_DIRECT);
6304 void MauerWaechst(int x, int y)
6308 if (!MovDelay[x][y]) /* next animation frame */
6309 MovDelay[x][y] = 3 * delay;
6311 if (MovDelay[x][y]) /* wait some time before next frame */
6315 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6317 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6318 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6320 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6323 if (!MovDelay[x][y])
6325 if (MovDir[x][y] == MV_LEFT)
6327 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6328 DrawLevelField(x - 1, y);
6330 else if (MovDir[x][y] == MV_RIGHT)
6332 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6333 DrawLevelField(x + 1, y);
6335 else if (MovDir[x][y] == MV_UP)
6337 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6338 DrawLevelField(x, y - 1);
6342 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6343 DrawLevelField(x, y + 1);
6346 Feld[x][y] = Store[x][y];
6348 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6349 DrawLevelField(x, y);
6354 void MauerAbleger(int ax, int ay)
6356 int element = Feld[ax][ay];
6357 int graphic = el2img(element);
6358 boolean oben_frei = FALSE, unten_frei = FALSE;
6359 boolean links_frei = FALSE, rechts_frei = FALSE;
6360 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6361 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6362 boolean new_wall = FALSE;
6364 if (IS_ANIMATED(graphic))
6365 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6367 if (!MovDelay[ax][ay]) /* start building new wall */
6368 MovDelay[ax][ay] = 6;
6370 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6373 if (MovDelay[ax][ay])
6377 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6379 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6381 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6383 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6386 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6387 element == EL_EXPANDABLE_WALL_ANY)
6391 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6392 Store[ax][ay-1] = element;
6393 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6394 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6395 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6396 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6401 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6402 Store[ax][ay+1] = element;
6403 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6404 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6405 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6406 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6411 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6412 element == EL_EXPANDABLE_WALL_ANY ||
6413 element == EL_EXPANDABLE_WALL)
6417 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6418 Store[ax-1][ay] = element;
6419 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6420 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6421 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6422 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6428 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6429 Store[ax+1][ay] = element;
6430 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6431 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6432 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6433 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6438 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6439 DrawLevelField(ax, ay);
6441 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6443 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6444 unten_massiv = TRUE;
6445 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6446 links_massiv = TRUE;
6447 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6448 rechts_massiv = TRUE;
6450 if (((oben_massiv && unten_massiv) ||
6451 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6452 element == EL_EXPANDABLE_WALL) &&
6453 ((links_massiv && rechts_massiv) ||
6454 element == EL_EXPANDABLE_WALL_VERTICAL))
6455 Feld[ax][ay] = EL_WALL;
6458 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6461 void CheckForDragon(int x, int y)
6464 boolean dragon_found = FALSE;
6465 static int xy[4][2] =
6473 for (i = 0; i < NUM_DIRECTIONS; i++)
6475 for (j = 0; j < 4; j++)
6477 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6479 if (IN_LEV_FIELD(xx, yy) &&
6480 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6482 if (Feld[xx][yy] == EL_DRAGON)
6483 dragon_found = TRUE;
6492 for (i = 0; i < NUM_DIRECTIONS; i++)
6494 for (j = 0; j < 3; j++)
6496 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6498 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6500 Feld[xx][yy] = EL_EMPTY;
6501 DrawLevelField(xx, yy);
6510 static void InitBuggyBase(int x, int y)
6512 int element = Feld[x][y];
6513 int activating_delay = FRAMES_PER_SECOND / 4;
6516 (element == EL_SP_BUGGY_BASE ?
6517 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6518 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6520 element == EL_SP_BUGGY_BASE_ACTIVE ?
6521 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6524 static void WarnBuggyBase(int x, int y)
6527 static int xy[4][2] =
6535 for (i = 0; i < NUM_DIRECTIONS; i++)
6537 int xx = x + xy[i][0], yy = y + xy[i][1];
6539 if (IS_PLAYER(xx, yy))
6541 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6548 static void InitTrap(int x, int y)
6550 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6553 static void ActivateTrap(int x, int y)
6555 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6558 static void ChangeActiveTrap(int x, int y)
6560 int graphic = IMG_TRAP_ACTIVE;
6562 /* if new animation frame was drawn, correct crumbled sand border */
6563 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6564 DrawLevelFieldCrumbledSand(x, y);
6567 static int getSpecialActionElement(int element, int number, int base_element)
6569 return (element != EL_EMPTY ? element :
6570 number != -1 ? base_element + number - 1 :
6574 static int getModifiedActionNumber(int value_old, int operator, int operand,
6575 int value_min, int value_max)
6577 int value_new = (operator == CA_MODE_SET ? operand :
6578 operator == CA_MODE_ADD ? value_old + operand :
6579 operator == CA_MODE_SUBTRACT ? value_old - operand :
6580 operator == CA_MODE_MULTIPLY ? value_old * operand :
6581 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
6582 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
6585 return (value_new < value_min ? value_min :
6586 value_new > value_max ? value_max :
6590 static void ExecuteCustomElementAction(int x, int y, int element, int page)
6592 struct ElementInfo *ei = &element_info[element];
6593 struct ElementChangeInfo *change = &ei->change_page[page];
6594 int action_type = change->action_type;
6595 int action_mode = change->action_mode;
6596 int action_arg = change->action_arg;
6599 if (!change->has_action)
6602 /* ---------- determine action paramater values -------------------------- */
6604 int level_time_value =
6605 (level.time > 0 ? TimeLeft :
6608 int action_arg_element =
6609 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
6610 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
6611 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
6614 int action_arg_direction =
6615 (action_arg >= CA_ARG_DIRECTION_LEFT &&
6616 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
6617 action_arg == CA_ARG_DIRECTION_TRIGGER ?
6618 change->actual_trigger_side :
6619 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
6620 MV_DIR_OPPOSITE(change->actual_trigger_side) :
6623 int action_arg_number_min =
6624 (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MIN :
6627 int action_arg_number_max =
6628 (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MAX :
6629 action_type == CA_SET_LEVEL_GEMS ? 999 :
6630 action_type == CA_SET_LEVEL_TIME ? 9999 :
6631 action_type == CA_SET_LEVEL_SCORE ? 99999 :
6632 action_type == CA_SET_CE_SCORE ? 9999 :
6633 action_type == CA_SET_CE_VALUE ? 9999 :
6636 int action_arg_number_reset =
6637 (action_type == CA_SET_PLAYER_SPEED ? TILEX/game.initial_move_delay_value :
6638 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
6639 action_type == CA_SET_LEVEL_TIME ? level.time :
6640 action_type == CA_SET_LEVEL_SCORE ? 0 :
6641 action_type == CA_SET_CE_SCORE ? 0 :
6643 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
6645 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
6649 int action_arg_number =
6650 (action_arg <= CA_ARG_MAX ? action_arg :
6651 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
6652 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
6653 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
6654 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
6655 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
6656 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
6657 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
6658 #if USE_NEW_CUSTOM_VALUE
6659 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
6661 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
6663 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) :
6664 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
6665 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
6666 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
6667 action_arg == CA_ARG_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
6668 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value :
6671 int action_arg_number_old =
6672 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
6673 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
6674 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
6675 action_type == CA_SET_CE_SCORE ? ei->collect_score :
6676 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
6679 int action_arg_number_new =
6680 getModifiedActionNumber(action_arg_number_old,
6681 action_mode, action_arg_number,
6682 action_arg_number_min, action_arg_number_max);
6684 int trigger_player_bits =
6685 (change->actual_trigger_player >= EL_PLAYER_1 &&
6686 change->actual_trigger_player <= EL_PLAYER_4 ?
6687 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
6690 int action_arg_player_bits =
6691 (action_arg >= CA_ARG_PLAYER_1 &&
6692 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
6693 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
6696 /* ---------- execute action -------------------------------------------- */
6705 /* ---------- level actions ------------------------------------------- */
6707 case CA_RESTART_LEVEL:
6709 game.restart_level = TRUE;
6714 case CA_SHOW_ENVELOPE:
6716 int element = getSpecialActionElement(action_arg_element,
6717 action_arg_number, EL_ENVELOPE_1);
6719 if (IS_ENVELOPE(element))
6720 local_player->show_envelope = element;
6725 case CA_SET_LEVEL_TIME:
6727 if (level.time > 0) /* only modify limited time value */
6729 TimeLeft = action_arg_number_new;
6731 DrawGameValue_Time(TimeLeft);
6733 if (!TimeLeft && setup.time_limit)
6734 for (i = 0; i < MAX_PLAYERS; i++)
6735 KillPlayer(&stored_player[i]);
6741 case CA_SET_LEVEL_SCORE:
6743 local_player->score = action_arg_number_new;
6745 DrawGameValue_Score(local_player->score);
6750 case CA_SET_LEVEL_GEMS:
6752 local_player->gems_still_needed = action_arg_number_new;
6754 DrawGameValue_Emeralds(local_player->gems_still_needed);
6759 case CA_SET_LEVEL_GRAVITY:
6761 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
6762 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
6763 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
6768 case CA_SET_LEVEL_WIND:
6770 game.wind_direction = action_arg_direction;
6775 /* ---------- player actions ------------------------------------------ */
6777 case CA_MOVE_PLAYER:
6779 /* automatically move to the next field in specified direction */
6780 for (i = 0; i < MAX_PLAYERS; i++)
6781 if (trigger_player_bits & (1 << i))
6782 stored_player[i].programmed_action = action_arg_direction;
6787 case CA_EXIT_PLAYER:
6789 for (i = 0; i < MAX_PLAYERS; i++)
6790 if (action_arg_player_bits & (1 << i))
6791 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
6796 case CA_KILL_PLAYER:
6798 for (i = 0; i < MAX_PLAYERS; i++)
6799 if (action_arg_player_bits & (1 << i))
6800 KillPlayer(&stored_player[i]);
6805 case CA_SET_PLAYER_KEYS:
6807 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
6808 int element = getSpecialActionElement(action_arg_element,
6809 action_arg_number, EL_KEY_1);
6811 if (IS_KEY(element))
6813 for (i = 0; i < MAX_PLAYERS; i++)
6815 if (trigger_player_bits & (1 << i))
6817 stored_player[i].key[KEY_NR(element)] = key_state;
6819 DrawGameValue_Keys(stored_player[i].key);
6821 redraw_mask |= REDRAW_DOOR_1;
6829 case CA_SET_PLAYER_SPEED:
6831 for (i = 0; i < MAX_PLAYERS; i++)
6833 if (trigger_player_bits & (1 << i))
6835 int move_stepsize = TILEX / stored_player[i].move_delay_value;
6837 if (action_arg == CA_ARG_SPEED_SLOWER ||
6838 action_arg == CA_ARG_SPEED_FASTER)
6840 action_arg_number = 2;
6841 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
6846 getModifiedActionNumber(move_stepsize,
6849 action_arg_number_min,
6850 action_arg_number_max);
6852 /* make sure that value is power of 2 */
6853 move_stepsize = (1 << log_2(move_stepsize));
6855 /* do no immediately change -- the player might just be moving */
6856 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
6858 stored_player[i].cannot_move =
6859 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
6866 case CA_SET_PLAYER_SHIELD:
6868 for (i = 0; i < MAX_PLAYERS; i++)
6870 if (trigger_player_bits & (1 << i))
6872 if (action_arg == CA_ARG_SHIELD_OFF)
6874 stored_player[i].shield_normal_time_left = 0;
6875 stored_player[i].shield_deadly_time_left = 0;
6877 else if (action_arg == CA_ARG_SHIELD_NORMAL)
6879 stored_player[i].shield_normal_time_left = 999999;
6881 else if (action_arg == CA_ARG_SHIELD_DEADLY)
6883 stored_player[i].shield_normal_time_left = 999999;
6884 stored_player[i].shield_deadly_time_left = 999999;
6892 case CA_SET_PLAYER_ARTWORK:
6894 for (i = 0; i < MAX_PLAYERS; i++)
6896 int element = action_arg_element;
6902 /* ---------- CE actions ---------------------------------------------- */
6904 case CA_SET_CE_SCORE:
6906 ei->collect_score = action_arg_number_new;
6911 case CA_SET_CE_VALUE:
6913 #if USE_NEW_CUSTOM_VALUE
6914 int last_custom_value = CustomValue[x][y];
6916 CustomValue[x][y] = action_arg_number_new;
6919 printf("::: Count == %d\n", CustomValue[x][y]);
6922 if (CustomValue[x][y] == 0 && last_custom_value > 0)
6925 printf("::: CE_VALUE_GETS_ZERO\n");
6928 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
6929 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
6941 static void ChangeElementNowExt(struct ElementChangeInfo *change,
6942 int x, int y, int target_element)
6944 int previous_move_direction = MovDir[x][y];
6945 #if USE_NEW_CUSTOM_VALUE
6946 int last_ce_value = CustomValue[x][y];
6948 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
6949 IS_WALKABLE(Feld[x][y]));
6951 /* check if element under player changes from accessible to unaccessible
6952 (needed for special case of dropping element which then changes) */
6953 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6954 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6962 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6963 RemoveMovingField(x, y);
6967 Feld[x][y] = target_element;
6969 ResetGfxAnimation(x, y);
6970 ResetRandomAnimationValue(x, y);
6972 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6973 MovDir[x][y] = previous_move_direction;
6975 #if USE_NEW_CUSTOM_VALUE
6976 if (element_info[Feld[x][y]].use_last_ce_value)
6977 CustomValue[x][y] = last_ce_value;
6980 InitField_WithBug1(x, y, FALSE);
6982 DrawLevelField(x, y);
6984 if (GFX_CRUMBLED(Feld[x][y]))
6985 DrawLevelFieldCrumbledSandNeighbours(x, y);
6988 /* "Changed[][]" not set yet to allow "entered by player" change one time */
6989 if (ELEM_IS_PLAYER(target_element))
6990 RelocatePlayer(x, y, target_element);
6993 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
6995 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6998 TestIfBadThingTouchesPlayer(x, y);
6999 TestIfPlayerTouchesCustomElement(x, y);
7000 TestIfElementTouchesCustomElement(x, y);
7003 static boolean ChangeElementNow(int x, int y, int element, int page)
7005 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7007 int old_element = Feld[x][y];
7009 /* always use default change event to prevent running into a loop */
7010 if (ChangeEvent[x][y] == -1)
7011 ChangeEvent[x][y] = CE_DELAY;
7013 if (ChangeEvent[x][y] == CE_DELAY)
7015 /* reset actual trigger element, trigger player and action element */
7016 change->actual_trigger_element = EL_EMPTY;
7017 change->actual_trigger_player = EL_PLAYER_1;
7018 change->actual_trigger_side = CH_SIDE_NONE;
7019 change->actual_trigger_ce_value = 0;
7023 /* do not change any elements that have already changed in this frame */
7027 /* do not change already changed elements with same change event */
7028 if (Changed[x][y] & ChangeEvent[x][y])
7033 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7035 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7038 if (change->explode)
7045 if (change->use_target_content)
7047 boolean complete_replace = TRUE;
7048 boolean can_replace[3][3];
7051 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7054 boolean is_walkable;
7055 boolean is_diggable;
7056 boolean is_collectible;
7057 boolean is_removable;
7058 boolean is_destructible;
7059 int ex = x + xx - 1;
7060 int ey = y + yy - 1;
7061 int content_element = change->target_content.e[xx][yy];
7064 can_replace[xx][yy] = TRUE;
7066 if (ex == x && ey == y) /* do not check changing element itself */
7069 if (content_element == EL_EMPTY_SPACE)
7071 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7076 if (!IN_LEV_FIELD(ex, ey))
7078 can_replace[xx][yy] = FALSE;
7079 complete_replace = FALSE;
7086 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7087 e = MovingOrBlocked2Element(ex, ey);
7089 is_empty = (IS_FREE(ex, ey) ||
7090 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7092 is_walkable = (is_empty || IS_WALKABLE(e));
7093 is_diggable = (is_empty || IS_DIGGABLE(e));
7094 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7095 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7096 is_removable = (is_diggable || is_collectible);
7098 can_replace[xx][yy] =
7099 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7100 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7101 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7102 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7103 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7104 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7105 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7107 if (!can_replace[xx][yy])
7108 complete_replace = FALSE;
7111 if (!change->only_if_complete || complete_replace)
7113 boolean something_has_changed = FALSE;
7115 if (change->only_if_complete && change->use_random_replace &&
7116 RND(100) < change->random_percentage)
7119 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7121 int ex = x + xx - 1;
7122 int ey = y + yy - 1;
7123 int content_element;
7125 if (can_replace[xx][yy] && (!change->use_random_replace ||
7126 RND(100) < change->random_percentage))
7128 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7129 RemoveMovingField(ex, ey);
7131 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7133 content_element = change->target_content.e[xx][yy];
7134 target_element = GET_TARGET_ELEMENT(content_element, change);
7136 ChangeElementNowExt(change, ex, ey, target_element);
7138 something_has_changed = TRUE;
7140 /* for symmetry reasons, freeze newly created border elements */
7141 if (ex != x || ey != y)
7142 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7146 if (something_has_changed)
7148 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7149 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7155 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7157 ChangeElementNowExt(change, x, y, target_element);
7159 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7160 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7163 /* this uses direct change before indirect change */
7164 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
7169 #if USE_NEW_DELAYED_ACTION
7171 static void ChangeElement(int x, int y, int page)
7173 int element = MovingOrBlocked2Element(x, y);
7174 struct ElementInfo *ei = &element_info[element];
7175 struct ElementChangeInfo *change = &ei->change_page[page];
7178 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
7179 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
7182 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7183 x, y, element, element_info[element].token_name);
7184 printf("ChangeElement(): This should never happen!\n");
7189 /* this can happen with classic bombs on walkable, changing elements */
7190 if (!CAN_CHANGE_OR_HAS_ACTION(element))
7193 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7194 ChangeDelay[x][y] = 0;
7200 if (ChangeDelay[x][y] == 0) /* initialize element change */
7202 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7204 if (change->can_change)
7206 ResetGfxAnimation(x, y);
7207 ResetRandomAnimationValue(x, y);
7209 if (change->pre_change_function)
7210 change->pre_change_function(x, y);
7214 ChangeDelay[x][y]--;
7216 if (ChangeDelay[x][y] != 0) /* continue element change */
7218 if (change->can_change)
7220 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7222 if (IS_ANIMATED(graphic))
7223 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7225 if (change->change_function)
7226 change->change_function(x, y);
7229 else /* finish element change */
7231 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7233 page = ChangePage[x][y];
7234 ChangePage[x][y] = -1;
7236 change = &ei->change_page[page];
7239 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7241 ChangeDelay[x][y] = 1; /* try change after next move step */
7242 ChangePage[x][y] = page; /* remember page to use for change */
7247 if (change->can_change)
7249 if (ChangeElementNow(x, y, element, page))
7251 if (change->post_change_function)
7252 change->post_change_function(x, y);
7256 if (change->has_action)
7257 ExecuteCustomElementAction(x, y, element, page);
7263 static void ChangeElement(int x, int y, int page)
7265 int element = MovingOrBlocked2Element(x, y);
7266 struct ElementInfo *ei = &element_info[element];
7267 struct ElementChangeInfo *change = &ei->change_page[page];
7270 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7273 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7274 x, y, element, element_info[element].token_name);
7275 printf("ChangeElement(): This should never happen!\n");
7280 /* this can happen with classic bombs on walkable, changing elements */
7281 if (!CAN_CHANGE(element))
7284 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7285 ChangeDelay[x][y] = 0;
7291 if (ChangeDelay[x][y] == 0) /* initialize element change */
7293 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7295 ResetGfxAnimation(x, y);
7296 ResetRandomAnimationValue(x, y);
7298 if (change->pre_change_function)
7299 change->pre_change_function(x, y);
7302 ChangeDelay[x][y]--;
7304 if (ChangeDelay[x][y] != 0) /* continue element change */
7306 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7308 if (IS_ANIMATED(graphic))
7309 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7311 if (change->change_function)
7312 change->change_function(x, y);
7314 else /* finish element change */
7316 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7318 page = ChangePage[x][y];
7319 ChangePage[x][y] = -1;
7321 change = &ei->change_page[page];
7324 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7326 ChangeDelay[x][y] = 1; /* try change after next move step */
7327 ChangePage[x][y] = page; /* remember page to use for change */
7332 if (ChangeElementNow(x, y, element, page))
7334 if (change->post_change_function)
7335 change->post_change_function(x, y);
7342 static boolean CheckTriggeredElementChangeExt(int x, int y,
7343 int trigger_element,
7349 boolean change_done_any = FALSE;
7350 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7353 if (!(trigger_events[trigger_element][trigger_event]))
7356 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7358 int element = EL_CUSTOM_START + i;
7359 boolean change_done = FALSE;
7362 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7363 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7366 for (p = 0; p < element_info[element].num_change_pages; p++)
7368 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7370 if (change->can_change_or_has_action &&
7371 change->has_event[trigger_event] &&
7372 change->trigger_side & trigger_side &&
7373 change->trigger_player & trigger_player &&
7374 change->trigger_page & trigger_page_bits &&
7375 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7377 change->actual_trigger_element = trigger_element;
7378 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7379 change->actual_trigger_side = trigger_side;
7380 change->actual_trigger_ce_value = CustomValue[x][y];
7382 if ((change->can_change && !change_done) || change->has_action)
7386 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7388 if (Feld[x][y] == element)
7390 if (change->can_change && !change_done)
7392 ChangeDelay[x][y] = 1;
7393 ChangeEvent[x][y] = trigger_event;
7394 ChangeElement(x, y, p);
7396 #if USE_NEW_DELAYED_ACTION
7397 else if (change->has_action)
7399 ExecuteCustomElementAction(x, y, element, p);
7400 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7403 if (change->has_action)
7405 ExecuteCustomElementAction(x, y, element, p);
7406 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7412 if (change->can_change)
7415 change_done_any = TRUE;
7422 return change_done_any;
7425 static boolean CheckElementChangeExt(int x, int y,
7427 int trigger_element,
7432 boolean change_done = FALSE;
7435 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7436 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7439 if (Feld[x][y] == EL_BLOCKED)
7441 Blocked2Moving(x, y, &x, &y);
7442 element = Feld[x][y];
7445 if (Feld[x][y] != element) /* check if element has already changed */
7448 for (p = 0; p < element_info[element].num_change_pages; p++)
7450 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7452 boolean check_trigger_element =
7453 (trigger_event == CE_TOUCHING_X ||
7454 trigger_event == CE_HITTING_X ||
7455 trigger_event == CE_HIT_BY_X);
7457 if (change->can_change_or_has_action &&
7458 change->has_event[trigger_event] &&
7459 change->trigger_side & trigger_side &&
7460 change->trigger_player & trigger_player &&
7461 (!check_trigger_element ||
7462 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
7464 change->actual_trigger_element = trigger_element;
7465 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7466 change->actual_trigger_side = trigger_side;
7467 change->actual_trigger_ce_value = CustomValue[x][y];
7469 if (change->can_change && !change_done)
7471 ChangeDelay[x][y] = 1;
7472 ChangeEvent[x][y] = trigger_event;
7473 ChangeElement(x, y, p);
7477 #if USE_NEW_DELAYED_ACTION
7478 else if (change->has_action)
7480 ExecuteCustomElementAction(x, y, element, p);
7481 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7484 if (change->has_action)
7486 ExecuteCustomElementAction(x, y, element, p);
7487 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7496 static void PlayPlayerSound(struct PlayerInfo *player)
7498 int jx = player->jx, jy = player->jy;
7499 int element = player->element_nr;
7500 int last_action = player->last_action_waiting;
7501 int action = player->action_waiting;
7503 if (player->is_waiting)
7505 if (action != last_action)
7506 PlayLevelSoundElementAction(jx, jy, element, action);
7508 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7512 if (action != last_action)
7513 StopSound(element_info[element].sound[last_action]);
7515 if (last_action == ACTION_SLEEPING)
7516 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7520 static void PlayAllPlayersSound()
7524 for (i = 0; i < MAX_PLAYERS; i++)
7525 if (stored_player[i].active)
7526 PlayPlayerSound(&stored_player[i]);
7529 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7531 boolean last_waiting = player->is_waiting;
7532 int move_dir = player->MovDir;
7534 player->last_action_waiting = player->action_waiting;
7538 if (!last_waiting) /* not waiting -> waiting */
7540 player->is_waiting = TRUE;
7542 player->frame_counter_bored =
7544 game.player_boring_delay_fixed +
7545 SimpleRND(game.player_boring_delay_random);
7546 player->frame_counter_sleeping =
7548 game.player_sleeping_delay_fixed +
7549 SimpleRND(game.player_sleeping_delay_random);
7551 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7554 if (game.player_sleeping_delay_fixed +
7555 game.player_sleeping_delay_random > 0 &&
7556 player->anim_delay_counter == 0 &&
7557 player->post_delay_counter == 0 &&
7558 FrameCounter >= player->frame_counter_sleeping)
7559 player->is_sleeping = TRUE;
7560 else if (game.player_boring_delay_fixed +
7561 game.player_boring_delay_random > 0 &&
7562 FrameCounter >= player->frame_counter_bored)
7563 player->is_bored = TRUE;
7565 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7566 player->is_bored ? ACTION_BORING :
7569 if (player->is_sleeping)
7571 if (player->num_special_action_sleeping > 0)
7573 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7575 int last_special_action = player->special_action_sleeping;
7576 int num_special_action = player->num_special_action_sleeping;
7577 int special_action =
7578 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7579 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7580 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7581 last_special_action + 1 : ACTION_SLEEPING);
7582 int special_graphic =
7583 el_act_dir2img(player->element_nr, special_action, move_dir);
7585 player->anim_delay_counter =
7586 graphic_info[special_graphic].anim_delay_fixed +
7587 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7588 player->post_delay_counter =
7589 graphic_info[special_graphic].post_delay_fixed +
7590 SimpleRND(graphic_info[special_graphic].post_delay_random);
7592 player->special_action_sleeping = special_action;
7595 if (player->anim_delay_counter > 0)
7597 player->action_waiting = player->special_action_sleeping;
7598 player->anim_delay_counter--;
7600 else if (player->post_delay_counter > 0)
7602 player->post_delay_counter--;
7606 else if (player->is_bored)
7608 if (player->num_special_action_bored > 0)
7610 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7612 int special_action =
7613 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7614 int special_graphic =
7615 el_act_dir2img(player->element_nr, special_action, move_dir);
7617 player->anim_delay_counter =
7618 graphic_info[special_graphic].anim_delay_fixed +
7619 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7620 player->post_delay_counter =
7621 graphic_info[special_graphic].post_delay_fixed +
7622 SimpleRND(graphic_info[special_graphic].post_delay_random);
7624 player->special_action_bored = special_action;
7627 if (player->anim_delay_counter > 0)
7629 player->action_waiting = player->special_action_bored;
7630 player->anim_delay_counter--;
7632 else if (player->post_delay_counter > 0)
7634 player->post_delay_counter--;
7639 else if (last_waiting) /* waiting -> not waiting */
7641 player->is_waiting = FALSE;
7642 player->is_bored = FALSE;
7643 player->is_sleeping = FALSE;
7645 player->frame_counter_bored = -1;
7646 player->frame_counter_sleeping = -1;
7648 player->anim_delay_counter = 0;
7649 player->post_delay_counter = 0;
7651 player->action_waiting = ACTION_DEFAULT;
7653 player->special_action_bored = ACTION_DEFAULT;
7654 player->special_action_sleeping = ACTION_DEFAULT;
7658 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7660 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7661 int left = player_action & JOY_LEFT;
7662 int right = player_action & JOY_RIGHT;
7663 int up = player_action & JOY_UP;
7664 int down = player_action & JOY_DOWN;
7665 int button1 = player_action & JOY_BUTTON_1;
7666 int button2 = player_action & JOY_BUTTON_2;
7667 int dx = (left ? -1 : right ? 1 : 0);
7668 int dy = (up ? -1 : down ? 1 : 0);
7670 if (!player->active || tape.pausing)
7676 snapped = SnapField(player, dx, dy);
7680 dropped = DropElement(player);
7682 moved = MovePlayer(player, dx, dy);
7685 if (tape.single_step && tape.recording && !tape.pausing)
7687 if (button1 || (dropped && !moved))
7689 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7690 SnapField(player, 0, 0); /* stop snapping */
7694 SetPlayerWaiting(player, FALSE);
7696 return player_action;
7700 /* no actions for this player (no input at player's configured device) */
7702 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7703 SnapField(player, 0, 0);
7704 CheckGravityMovementWhenNotMoving(player);
7706 if (player->MovPos == 0)
7707 SetPlayerWaiting(player, TRUE);
7709 if (player->MovPos == 0) /* needed for tape.playing */
7710 player->is_moving = FALSE;
7712 player->is_dropping = FALSE;
7718 void AdvanceFrameAndPlayerCounters(int player_nr)
7722 /* advance frame counters (global frame counter and time frame counter) */
7726 /* advance player counters (counters for move delay, move animation etc.) */
7727 for (i = 0; i < MAX_PLAYERS; i++)
7729 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
7730 int move_delay_value = stored_player[i].move_delay_value;
7731 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
7733 if (!advance_player_counters) /* not all players may be affected */
7736 #if USE_NEW_PLAYER_ANIM
7737 if (move_frames == 0) /* less than one move per game frame */
7739 int stepsize = TILEX / move_delay_value;
7740 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
7741 int count = (stored_player[i].is_moving ?
7742 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
7744 if (count % delay == 0)
7749 stored_player[i].Frame += move_frames;
7751 if (stored_player[i].MovPos != 0)
7752 stored_player[i].StepFrame += move_frames;
7754 if (stored_player[i].move_delay > 0)
7755 stored_player[i].move_delay--;
7757 /* due to bugs in previous versions, counter must count up, not down */
7758 if (stored_player[i].push_delay != -1)
7759 stored_player[i].push_delay++;
7761 if (stored_player[i].drop_delay > 0)
7762 stored_player[i].drop_delay--;
7768 static unsigned long game_frame_delay = 0;
7769 unsigned long game_frame_delay_value;
7770 int magic_wall_x = 0, magic_wall_y = 0;
7771 int i, x, y, element, graphic;
7772 byte *recorded_player_action;
7773 byte summarized_player_action = 0;
7774 byte tape_action[MAX_PLAYERS];
7776 if (game_status != GAME_MODE_PLAYING)
7779 game_frame_delay_value =
7780 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7782 if (tape.playing && tape.warp_forward && !tape.pausing)
7783 game_frame_delay_value = 0;
7785 /* ---------- main game synchronization point ---------- */
7787 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
7789 if (network_playing && !network_player_action_received)
7791 /* try to get network player actions in time */
7793 #if defined(NETWORK_AVALIABLE)
7794 /* last chance to get network player actions without main loop delay */
7798 /* game was quit by network peer */
7799 if (game_status != GAME_MODE_PLAYING)
7802 if (!network_player_action_received)
7803 return; /* failed to get network player actions in time */
7809 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7812 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
7813 if (recorded_player_action == NULL && tape.pausing)
7817 for (i = 0; i < MAX_PLAYERS; i++)
7819 summarized_player_action |= stored_player[i].action;
7821 if (!network_playing)
7822 stored_player[i].effective_action = stored_player[i].action;
7825 #if defined(NETWORK_AVALIABLE)
7826 if (network_playing)
7827 SendToServer_MovePlayer(summarized_player_action);
7830 if (!options.network && !setup.team_mode)
7831 local_player->effective_action = summarized_player_action;
7833 if (recorded_player_action != NULL)
7834 for (i = 0; i < MAX_PLAYERS; i++)
7835 stored_player[i].effective_action = recorded_player_action[i];
7837 for (i = 0; i < MAX_PLAYERS; i++)
7839 tape_action[i] = stored_player[i].effective_action;
7841 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7842 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7845 /* only save actions from input devices, but not programmed actions */
7847 TapeRecordAction(tape_action);
7849 for (i = 0; i < MAX_PLAYERS; i++)
7851 int actual_player_action = stored_player[i].effective_action;
7854 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
7855 - rnd_equinox_tetrachloride 048
7856 - rnd_equinox_tetrachloride_ii 096
7857 - rnd_emanuel_schmieg 002
7858 - doctor_sloan_ww 001, 020
7860 if (stored_player[i].MovPos == 0)
7861 CheckGravityMovement(&stored_player[i]);
7864 /* overwrite programmed action with tape action */
7865 if (stored_player[i].programmed_action)
7866 actual_player_action = stored_player[i].programmed_action;
7869 PlayerActions(&stored_player[i], actual_player_action);
7871 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7873 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7874 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7877 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7880 network_player_action_received = FALSE;
7882 ScrollScreen(NULL, SCROLL_GO_ON);
7884 /* for backwards compatibility, the following code emulates a fixed bug that
7885 occured when pushing elements (causing elements that just made their last
7886 pushing step to already (if possible) make their first falling step in the
7887 same game frame, which is bad); this code is also needed to use the famous
7888 "spring push bug" which is used in older levels and might be wanted to be
7889 used also in newer levels, but in this case the buggy pushing code is only
7890 affecting the "spring" element and no other elements */
7892 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7894 for (i = 0; i < MAX_PLAYERS; i++)
7896 struct PlayerInfo *player = &stored_player[i];
7900 if (player->active && player->is_pushing && player->is_moving &&
7902 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7903 Feld[x][y] == EL_SPRING))
7905 ContinueMoving(x, y);
7907 /* continue moving after pushing (this is actually a bug) */
7908 if (!IS_MOVING(x, y))
7916 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7918 Changed[x][y] = FALSE;
7919 ChangeEvent[x][y] = -1;
7921 /* this must be handled before main playfield loop */
7922 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
7925 if (MovDelay[x][y] <= 0)
7929 #if USE_NEW_SNAP_DELAY
7930 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
7933 if (MovDelay[x][y] <= 0)
7936 DrawLevelField(x, y);
7942 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7944 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7945 printf("GameActions(): This should never happen!\n");
7947 ChangePage[x][y] = -1;
7952 if (WasJustMoving[x][y] > 0)
7953 WasJustMoving[x][y]--;
7954 if (WasJustFalling[x][y] > 0)
7955 WasJustFalling[x][y]--;
7956 if (CheckCollision[x][y] > 0)
7957 CheckCollision[x][y]--;
7961 /* reset finished pushing action (not done in ContinueMoving() to allow
7962 continous pushing animation for elements with zero push delay) */
7963 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7965 ResetGfxAnimation(x, y);
7966 DrawLevelField(x, y);
7970 if (IS_BLOCKED(x, y))
7974 Blocked2Moving(x, y, &oldx, &oldy);
7975 if (!IS_MOVING(oldx, oldy))
7977 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7978 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7979 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7980 printf("GameActions(): This should never happen!\n");
7986 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7988 element = Feld[x][y];
7989 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7991 if (graphic_info[graphic].anim_global_sync)
7992 GfxFrame[x][y] = FrameCounter;
7994 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7995 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7996 ResetRandomAnimationValue(x, y);
7998 SetRandomAnimationValue(x, y);
8000 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8002 if (IS_INACTIVE(element))
8004 if (IS_ANIMATED(graphic))
8005 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8010 /* this may take place after moving, so 'element' may have changed */
8011 if (IS_CHANGING(x, y) &&
8012 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8014 int page = element_info[element].event_page_nr[CE_DELAY];
8016 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
8020 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8024 ChangeElement(x, y, page);
8026 if (CAN_CHANGE(element))
8027 ChangeElement(x, y, page);
8029 if (HAS_ACTION(element))
8030 ExecuteCustomElementAction(x, y, element, page);
8035 element = Feld[x][y];
8036 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8039 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8043 element = Feld[x][y];
8044 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8046 if (IS_ANIMATED(graphic) &&
8049 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8051 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8052 EdelsteinFunkeln(x, y);
8054 else if ((element == EL_ACID ||
8055 element == EL_EXIT_OPEN ||
8056 element == EL_SP_EXIT_OPEN ||
8057 element == EL_SP_TERMINAL ||
8058 element == EL_SP_TERMINAL_ACTIVE ||
8059 element == EL_EXTRA_TIME ||
8060 element == EL_SHIELD_NORMAL ||
8061 element == EL_SHIELD_DEADLY) &&
8062 IS_ANIMATED(graphic))
8063 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8064 else if (IS_MOVING(x, y))
8065 ContinueMoving(x, y);
8066 else if (IS_ACTIVE_BOMB(element))
8067 CheckDynamite(x, y);
8068 else if (element == EL_AMOEBA_GROWING)
8069 AmoebeWaechst(x, y);
8070 else if (element == EL_AMOEBA_SHRINKING)
8071 AmoebaDisappearing(x, y);
8073 #if !USE_NEW_AMOEBA_CODE
8074 else if (IS_AMOEBALIVE(element))
8075 AmoebeAbleger(x, y);
8078 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8080 else if (element == EL_EXIT_CLOSED)
8082 else if (element == EL_SP_EXIT_CLOSED)
8084 else if (element == EL_EXPANDABLE_WALL_GROWING)
8086 else if (element == EL_EXPANDABLE_WALL ||
8087 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8088 element == EL_EXPANDABLE_WALL_VERTICAL ||
8089 element == EL_EXPANDABLE_WALL_ANY)
8091 else if (element == EL_FLAMES)
8092 CheckForDragon(x, y);
8093 else if (element == EL_EXPLOSION)
8094 ; /* drawing of correct explosion animation is handled separately */
8095 else if (element == EL_ELEMENT_SNAPPING)
8098 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
8100 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8103 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8104 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8106 if (IS_BELT_ACTIVE(element))
8107 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8109 if (game.magic_wall_active)
8111 int jx = local_player->jx, jy = local_player->jy;
8113 /* play the element sound at the position nearest to the player */
8114 if ((element == EL_MAGIC_WALL_FULL ||
8115 element == EL_MAGIC_WALL_ACTIVE ||
8116 element == EL_MAGIC_WALL_EMPTYING ||
8117 element == EL_BD_MAGIC_WALL_FULL ||
8118 element == EL_BD_MAGIC_WALL_ACTIVE ||
8119 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8120 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8128 #if USE_NEW_AMOEBA_CODE
8129 /* new experimental amoeba growth stuff */
8130 if (!(FrameCounter % 8))
8132 static unsigned long random = 1684108901;
8134 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8136 x = RND(lev_fieldx);
8137 y = RND(lev_fieldy);
8138 element = Feld[x][y];
8140 if (!IS_PLAYER(x,y) &&
8141 (element == EL_EMPTY ||
8142 CAN_GROW_INTO(element) ||
8143 element == EL_QUICKSAND_EMPTY ||
8144 element == EL_ACID_SPLASH_LEFT ||
8145 element == EL_ACID_SPLASH_RIGHT))
8147 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8148 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8149 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8150 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8151 Feld[x][y] = EL_AMOEBA_DROP;
8154 random = random * 129 + 1;
8160 if (game.explosions_delayed)
8163 game.explosions_delayed = FALSE;
8165 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8167 element = Feld[x][y];
8169 if (ExplodeField[x][y])
8170 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8171 else if (element == EL_EXPLOSION)
8172 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8174 ExplodeField[x][y] = EX_TYPE_NONE;
8177 game.explosions_delayed = TRUE;
8180 if (game.magic_wall_active)
8182 if (!(game.magic_wall_time_left % 4))
8184 int element = Feld[magic_wall_x][magic_wall_y];
8186 if (element == EL_BD_MAGIC_WALL_FULL ||
8187 element == EL_BD_MAGIC_WALL_ACTIVE ||
8188 element == EL_BD_MAGIC_WALL_EMPTYING)
8189 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8191 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8194 if (game.magic_wall_time_left > 0)
8196 game.magic_wall_time_left--;
8197 if (!game.magic_wall_time_left)
8199 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8201 element = Feld[x][y];
8203 if (element == EL_MAGIC_WALL_ACTIVE ||
8204 element == EL_MAGIC_WALL_FULL)
8206 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8207 DrawLevelField(x, y);
8209 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8210 element == EL_BD_MAGIC_WALL_FULL)
8212 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8213 DrawLevelField(x, y);
8217 game.magic_wall_active = FALSE;
8222 if (game.light_time_left > 0)
8224 game.light_time_left--;
8226 if (game.light_time_left == 0)
8227 RedrawAllLightSwitchesAndInvisibleElements();
8230 if (game.timegate_time_left > 0)
8232 game.timegate_time_left--;
8234 if (game.timegate_time_left == 0)
8235 CloseAllOpenTimegates();
8238 for (i = 0; i < MAX_PLAYERS; i++)
8240 struct PlayerInfo *player = &stored_player[i];
8242 if (SHIELD_ON(player))
8244 if (player->shield_deadly_time_left)
8245 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8246 else if (player->shield_normal_time_left)
8247 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8251 if (TimeFrames >= FRAMES_PER_SECOND)
8256 for (i = 0; i < MAX_PLAYERS; i++)
8258 struct PlayerInfo *player = &stored_player[i];
8260 if (SHIELD_ON(player))
8262 player->shield_normal_time_left--;
8264 if (player->shield_deadly_time_left > 0)
8265 player->shield_deadly_time_left--;
8269 if (!level.use_step_counter)
8277 if (TimeLeft <= 10 && setup.time_limit)
8278 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8280 DrawGameValue_Time(TimeLeft);
8282 if (!TimeLeft && setup.time_limit)
8283 for (i = 0; i < MAX_PLAYERS; i++)
8284 KillPlayer(&stored_player[i]);
8286 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8287 DrawGameValue_Time(TimePlayed);
8290 if (tape.recording || tape.playing)
8291 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8295 PlayAllPlayersSound();
8297 if (options.debug) /* calculate frames per second */
8299 static unsigned long fps_counter = 0;
8300 static int fps_frames = 0;
8301 unsigned long fps_delay_ms = Counter() - fps_counter;
8305 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8307 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8310 fps_counter = Counter();
8313 redraw_mask |= REDRAW_FPS;
8316 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
8318 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8320 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8322 local_player->show_envelope = 0;
8325 /* use random number generator in every frame to make it less predictable */
8326 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8330 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8332 int min_x = x, min_y = y, max_x = x, max_y = y;
8335 for (i = 0; i < MAX_PLAYERS; i++)
8337 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8339 if (!stored_player[i].active || &stored_player[i] == player)
8342 min_x = MIN(min_x, jx);
8343 min_y = MIN(min_y, jy);
8344 max_x = MAX(max_x, jx);
8345 max_y = MAX(max_y, jy);
8348 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8351 static boolean AllPlayersInVisibleScreen()
8355 for (i = 0; i < MAX_PLAYERS; i++)
8357 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8359 if (!stored_player[i].active)
8362 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8369 void ScrollLevel(int dx, int dy)
8371 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8374 BlitBitmap(drawto_field, drawto_field,
8375 FX + TILEX * (dx == -1) - softscroll_offset,
8376 FY + TILEY * (dy == -1) - softscroll_offset,
8377 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8378 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8379 FX + TILEX * (dx == 1) - softscroll_offset,
8380 FY + TILEY * (dy == 1) - softscroll_offset);
8384 x = (dx == 1 ? BX1 : BX2);
8385 for (y = BY1; y <= BY2; y++)
8386 DrawScreenField(x, y);
8391 y = (dy == 1 ? BY1 : BY2);
8392 for (x = BX1; x <= BX2; x++)
8393 DrawScreenField(x, y);
8396 redraw_mask |= REDRAW_FIELD;
8399 static boolean canFallDown(struct PlayerInfo *player)
8401 int jx = player->jx, jy = player->jy;
8403 return (IN_LEV_FIELD(jx, jy + 1) &&
8404 (IS_FREE(jx, jy + 1) ||
8405 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8406 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8407 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8410 static boolean canPassField(int x, int y, int move_dir)
8412 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8413 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8414 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8417 int element = Feld[x][y];
8419 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8420 !CAN_MOVE(element) &&
8421 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8422 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8423 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8426 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8428 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8429 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8430 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8434 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8435 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
8436 (IS_DIGGABLE(Feld[newx][newy]) ||
8437 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8438 canPassField(newx, newy, move_dir)));
8441 static void CheckGravityMovement(struct PlayerInfo *player)
8443 if (game.gravity && !player->programmed_action)
8445 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8446 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8447 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8448 int jx = player->jx, jy = player->jy;
8449 boolean player_is_moving_to_valid_field =
8450 (!player_is_snapping &&
8451 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8452 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8453 boolean player_can_fall_down = canFallDown(player);
8455 if (player_can_fall_down &&
8456 !player_is_moving_to_valid_field)
8457 player->programmed_action = MV_DOWN;
8461 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8463 return CheckGravityMovement(player);
8465 if (game.gravity && !player->programmed_action)
8467 int jx = player->jx, jy = player->jy;
8468 boolean field_under_player_is_free =
8469 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8470 boolean player_is_standing_on_valid_field =
8471 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8472 (IS_WALKABLE(Feld[jx][jy]) &&
8473 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8475 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8476 player->programmed_action = MV_DOWN;
8482 -----------------------------------------------------------------------------
8483 dx, dy: direction (non-diagonal) to try to move the player to
8484 real_dx, real_dy: direction as read from input device (can be diagonal)
8487 boolean MovePlayerOneStep(struct PlayerInfo *player,
8488 int dx, int dy, int real_dx, int real_dy)
8490 int jx = player->jx, jy = player->jy;
8491 int new_jx = jx + dx, new_jy = jy + dy;
8495 if (!player->active || (!dx && !dy))
8496 return MF_NO_ACTION;
8498 player->MovDir = (dx < 0 ? MV_LEFT :
8501 dy > 0 ? MV_DOWN : MV_NONE);
8503 if (!IN_LEV_FIELD(new_jx, new_jy))
8504 return MF_NO_ACTION;
8506 if (player->cannot_move)
8508 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8509 SnapField(player, 0, 0);
8511 return MF_NO_ACTION;
8514 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8515 return MF_NO_ACTION;
8517 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8519 if (DONT_RUN_INTO(element))
8521 if (element == EL_ACID && dx == 0 && dy == 1)
8523 SplashAcid(new_jx, new_jy);
8524 Feld[jx][jy] = EL_PLAYER_1;
8525 InitMovingField(jx, jy, MV_DOWN);
8526 Store[jx][jy] = EL_ACID;
8527 ContinueMoving(jx, jy);
8531 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
8536 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8537 if (can_move != MF_MOVING)
8540 /* check if DigField() has caused relocation of the player */
8541 if (player->jx != jx || player->jy != jy)
8542 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
8544 StorePlayer[jx][jy] = 0;
8545 player->last_jx = jx;
8546 player->last_jy = jy;
8547 player->jx = new_jx;
8548 player->jy = new_jy;
8549 StorePlayer[new_jx][new_jy] = player->element_nr;
8551 if (player->move_delay_value_next != -1)
8553 player->move_delay_value = player->move_delay_value_next;
8554 player->move_delay_value_next = -1;
8558 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8560 player->step_counter++;
8562 PlayerVisit[jx][jy] = FrameCounter;
8564 ScrollPlayer(player, SCROLL_INIT);
8569 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8571 int jx = player->jx, jy = player->jy;
8572 int old_jx = jx, old_jy = jy;
8573 int moved = MF_NO_ACTION;
8575 if (!player->active)
8580 if (player->MovPos == 0)
8582 player->is_moving = FALSE;
8583 player->is_digging = FALSE;
8584 player->is_collecting = FALSE;
8585 player->is_snapping = FALSE;
8586 player->is_pushing = FALSE;
8592 if (player->move_delay > 0)
8595 player->move_delay = -1; /* set to "uninitialized" value */
8597 /* store if player is automatically moved to next field */
8598 player->is_auto_moving = (player->programmed_action != MV_NONE);
8600 /* remove the last programmed player action */
8601 player->programmed_action = 0;
8605 /* should only happen if pre-1.2 tape recordings are played */
8606 /* this is only for backward compatibility */
8608 int original_move_delay_value = player->move_delay_value;
8611 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8615 /* scroll remaining steps with finest movement resolution */
8616 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8618 while (player->MovPos)
8620 ScrollPlayer(player, SCROLL_GO_ON);
8621 ScrollScreen(NULL, SCROLL_GO_ON);
8623 AdvanceFrameAndPlayerCounters(player->index_nr);
8629 player->move_delay_value = original_move_delay_value;
8632 if (player->last_move_dir & MV_HORIZONTAL)
8634 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8635 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8639 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8640 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8646 if (moved & MF_MOVING && !ScreenMovPos &&
8647 (player == local_player || !options.network))
8649 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8650 int offset = (setup.scroll_delay ? 3 : 0);
8652 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8654 /* actual player has left the screen -- scroll in that direction */
8655 if (jx != old_jx) /* player has moved horizontally */
8656 scroll_x += (jx - old_jx);
8657 else /* player has moved vertically */
8658 scroll_y += (jy - old_jy);
8662 if (jx != old_jx) /* player has moved horizontally */
8664 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8665 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8666 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8668 /* don't scroll over playfield boundaries */
8669 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8670 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8672 /* don't scroll more than one field at a time */
8673 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8675 /* don't scroll against the player's moving direction */
8676 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8677 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8678 scroll_x = old_scroll_x;
8680 else /* player has moved vertically */
8682 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8683 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8684 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8686 /* don't scroll over playfield boundaries */
8687 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8688 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8690 /* don't scroll more than one field at a time */
8691 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8693 /* don't scroll against the player's moving direction */
8694 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8695 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8696 scroll_y = old_scroll_y;
8700 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8702 if (!options.network && !AllPlayersInVisibleScreen())
8704 scroll_x = old_scroll_x;
8705 scroll_y = old_scroll_y;
8709 ScrollScreen(player, SCROLL_INIT);
8710 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8715 player->StepFrame = 0;
8717 if (moved & MF_MOVING)
8719 if (old_jx != jx && old_jy == jy)
8720 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8721 else if (old_jx == jx && old_jy != jy)
8722 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8724 DrawLevelField(jx, jy); /* for "crumbled sand" */
8726 player->last_move_dir = player->MovDir;
8727 player->is_moving = TRUE;
8728 player->is_snapping = FALSE;
8729 player->is_switching = FALSE;
8730 player->is_dropping = FALSE;
8734 CheckGravityMovementWhenNotMoving(player);
8736 player->is_moving = FALSE;
8738 /* at this point, the player is allowed to move, but cannot move right now
8739 (e.g. because of something blocking the way) -- ensure that the player
8740 is also allowed to move in the next frame (in old versions before 3.1.1,
8741 the player was forced to wait again for eight frames before next try) */
8743 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8744 player->move_delay = 0; /* allow direct movement in the next frame */
8747 if (player->move_delay == -1) /* not yet initialized by DigField() */
8748 player->move_delay = player->move_delay_value;
8750 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8752 TestIfPlayerTouchesBadThing(jx, jy);
8753 TestIfPlayerTouchesCustomElement(jx, jy);
8756 if (!player->active)
8757 RemovePlayer(player);
8762 void ScrollPlayer(struct PlayerInfo *player, int mode)
8764 int jx = player->jx, jy = player->jy;
8765 int last_jx = player->last_jx, last_jy = player->last_jy;
8766 int move_stepsize = TILEX / player->move_delay_value;
8768 #if USE_NEW_PLAYER_SPEED
8769 if (!player->active)
8772 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
8775 if (!player->active || player->MovPos == 0)
8779 if (mode == SCROLL_INIT)
8781 player->actual_frame_counter = FrameCounter;
8782 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8784 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
8785 Feld[last_jx][last_jy] == EL_EMPTY)
8787 int last_field_block_delay = 0; /* start with no blocking at all */
8788 int block_delay_adjustment = player->block_delay_adjustment;
8790 /* if player blocks last field, add delay for exactly one move */
8791 if (player->block_last_field)
8793 last_field_block_delay += player->move_delay_value;
8795 /* when blocking enabled, prevent moving up despite gravity */
8796 if (game.gravity && player->MovDir == MV_UP)
8797 block_delay_adjustment = -1;
8800 /* add block delay adjustment (also possible when not blocking) */
8801 last_field_block_delay += block_delay_adjustment;
8803 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8804 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
8807 #if USE_NEW_PLAYER_SPEED
8808 if (player->MovPos != 0) /* player has not yet reached destination */
8814 else if (!FrameReached(&player->actual_frame_counter, 1))
8818 printf("::: player->MovPos: %d -> %d\n",
8820 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
8823 #if USE_NEW_PLAYER_SPEED
8824 if (player->MovPos != 0)
8826 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8827 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8829 /* before DrawPlayer() to draw correct player graphic for this case */
8830 if (player->MovPos == 0)
8831 CheckGravityMovement(player);
8834 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8835 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8837 /* before DrawPlayer() to draw correct player graphic for this case */
8838 if (player->MovPos == 0)
8839 CheckGravityMovement(player);
8842 if (player->MovPos == 0) /* player reached destination field */
8845 printf("::: player reached destination field\n");
8848 if (player->move_delay_reset_counter > 0)
8850 player->move_delay_reset_counter--;
8852 if (player->move_delay_reset_counter == 0)
8854 /* continue with normal speed after quickly moving through gate */
8855 HALVE_PLAYER_SPEED(player);
8857 /* be able to make the next move without delay */
8858 player->move_delay = 0;
8862 player->last_jx = jx;
8863 player->last_jy = jy;
8865 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8866 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8867 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8869 DrawPlayer(player); /* needed here only to cleanup last field */
8870 RemovePlayer(player);
8872 if (local_player->friends_still_needed == 0 ||
8873 IS_SP_ELEMENT(Feld[jx][jy]))
8874 player->LevelSolved = player->GameOver = TRUE;
8877 /* this breaks one level: "machine", level 000 */
8879 int move_direction = player->MovDir;
8880 int enter_side = MV_DIR_OPPOSITE(move_direction);
8881 int leave_side = move_direction;
8882 int old_jx = last_jx;
8883 int old_jy = last_jy;
8884 int old_element = Feld[old_jx][old_jy];
8885 int new_element = Feld[jx][jy];
8887 if (IS_CUSTOM_ELEMENT(old_element))
8888 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
8890 player->index_bit, leave_side);
8892 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
8894 player->index_bit, leave_side);
8896 if (IS_CUSTOM_ELEMENT(new_element))
8897 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
8898 player->index_bit, enter_side);
8900 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
8902 player->index_bit, enter_side);
8905 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8907 TestIfPlayerTouchesBadThing(jx, jy);
8908 TestIfPlayerTouchesCustomElement(jx, jy);
8910 /* needed because pushed element has not yet reached its destination,
8911 so it would trigger a change event at its previous field location */
8912 if (!player->is_pushing)
8913 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8915 if (!player->active)
8916 RemovePlayer(player);
8919 if (level.use_step_counter)
8929 if (TimeLeft <= 10 && setup.time_limit)
8930 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8932 DrawGameValue_Time(TimeLeft);
8934 if (!TimeLeft && setup.time_limit)
8935 for (i = 0; i < MAX_PLAYERS; i++)
8936 KillPlayer(&stored_player[i]);
8938 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8939 DrawGameValue_Time(TimePlayed);
8942 if (tape.single_step && tape.recording && !tape.pausing &&
8943 !player->programmed_action)
8944 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8948 void ScrollScreen(struct PlayerInfo *player, int mode)
8950 static unsigned long screen_frame_counter = 0;
8952 if (mode == SCROLL_INIT)
8954 /* set scrolling step size according to actual player's moving speed */
8955 ScrollStepSize = TILEX / player->move_delay_value;
8957 screen_frame_counter = FrameCounter;
8958 ScreenMovDir = player->MovDir;
8959 ScreenMovPos = player->MovPos;
8960 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8963 else if (!FrameReached(&screen_frame_counter, 1))
8968 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8969 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8970 redraw_mask |= REDRAW_FIELD;
8973 ScreenMovDir = MV_NONE;
8976 void TestIfPlayerTouchesCustomElement(int x, int y)
8978 static int xy[4][2] =
8985 static int trigger_sides[4][2] =
8987 /* center side border side */
8988 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8989 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8990 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8991 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8993 static int touch_dir[4] =
9000 int center_element = Feld[x][y]; /* should always be non-moving! */
9003 for (i = 0; i < NUM_DIRECTIONS; i++)
9005 int xx = x + xy[i][0];
9006 int yy = y + xy[i][1];
9007 int center_side = trigger_sides[i][0];
9008 int border_side = trigger_sides[i][1];
9011 if (!IN_LEV_FIELD(xx, yy))
9014 if (IS_PLAYER(x, y))
9016 struct PlayerInfo *player = PLAYERINFO(x, y);
9018 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9019 border_element = Feld[xx][yy]; /* may be moving! */
9020 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9021 border_element = Feld[xx][yy];
9022 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9023 border_element = MovingOrBlocked2Element(xx, yy);
9025 continue; /* center and border element do not touch */
9027 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9028 player->index_bit, border_side);
9029 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9030 CE_PLAYER_TOUCHES_X,
9031 player->index_bit, border_side);
9033 else if (IS_PLAYER(xx, yy))
9035 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9037 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9039 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9040 continue; /* center and border element do not touch */
9043 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9044 player->index_bit, center_side);
9045 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9046 CE_PLAYER_TOUCHES_X,
9047 player->index_bit, center_side);
9053 void TestIfElementTouchesCustomElement(int x, int y)
9055 static int xy[4][2] =
9062 static int trigger_sides[4][2] =
9064 /* center side border side */
9065 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9066 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9067 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9068 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9070 static int touch_dir[4] =
9077 boolean change_center_element = FALSE;
9078 int center_element = Feld[x][y]; /* should always be non-moving! */
9081 for (i = 0; i < NUM_DIRECTIONS; i++)
9083 int xx = x + xy[i][0];
9084 int yy = y + xy[i][1];
9085 int center_side = trigger_sides[i][0];
9086 int border_side = trigger_sides[i][1];
9089 if (!IN_LEV_FIELD(xx, yy))
9092 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9093 border_element = Feld[xx][yy]; /* may be moving! */
9094 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9095 border_element = Feld[xx][yy];
9096 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9097 border_element = MovingOrBlocked2Element(xx, yy);
9099 continue; /* center and border element do not touch */
9101 /* check for change of center element (but change it only once) */
9102 if (!change_center_element)
9103 change_center_element =
9104 CheckElementChangeBySide(x, y, center_element, border_element,
9105 CE_TOUCHING_X, border_side);
9107 /* check for change of border element */
9108 CheckElementChangeBySide(xx, yy, border_element, center_element,
9109 CE_TOUCHING_X, center_side);
9113 void TestIfElementHitsCustomElement(int x, int y, int direction)
9115 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9116 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9117 int hitx = x + dx, hity = y + dy;
9118 int hitting_element = Feld[x][y];
9119 int touched_element;
9121 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9124 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9125 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9127 if (IN_LEV_FIELD(hitx, hity))
9129 int opposite_direction = MV_DIR_OPPOSITE(direction);
9130 int hitting_side = direction;
9131 int touched_side = opposite_direction;
9132 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9133 MovDir[hitx][hity] != direction ||
9134 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9140 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9141 CE_HITTING_X, touched_side);
9143 CheckElementChangeBySide(hitx, hity, touched_element,
9144 hitting_element, CE_HIT_BY_X, hitting_side);
9146 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9147 CE_HIT_BY_SOMETHING, opposite_direction);
9151 /* "hitting something" is also true when hitting the playfield border */
9152 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9153 CE_HITTING_SOMETHING, direction);
9157 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9159 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9160 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9161 int hitx = x + dx, hity = y + dy;
9162 int hitting_element = Feld[x][y];
9163 int touched_element;
9165 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9166 !IS_FREE(hitx, hity) &&
9167 (!IS_MOVING(hitx, hity) ||
9168 MovDir[hitx][hity] != direction ||
9169 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9172 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9176 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9180 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9181 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9183 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9184 EP_CAN_SMASH_EVERYTHING, direction);
9186 if (IN_LEV_FIELD(hitx, hity))
9188 int opposite_direction = MV_DIR_OPPOSITE(direction);
9189 int hitting_side = direction;
9190 int touched_side = opposite_direction;
9192 int touched_element = MovingOrBlocked2Element(hitx, hity);
9195 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9196 MovDir[hitx][hity] != direction ||
9197 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9206 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9207 CE_SMASHED_BY_SOMETHING, opposite_direction);
9209 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9210 CE_OTHER_IS_SMASHING, touched_side);
9212 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9213 CE_OTHER_GETS_SMASHED, hitting_side);
9219 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9221 int i, kill_x = -1, kill_y = -1;
9222 int bad_element = -1;
9223 static int test_xy[4][2] =
9230 static int test_dir[4] =
9238 for (i = 0; i < NUM_DIRECTIONS; i++)
9240 int test_x, test_y, test_move_dir, test_element;
9242 test_x = good_x + test_xy[i][0];
9243 test_y = good_y + test_xy[i][1];
9245 if (!IN_LEV_FIELD(test_x, test_y))
9249 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9251 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9253 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9254 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9256 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9257 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9261 bad_element = test_element;
9267 if (kill_x != -1 || kill_y != -1)
9269 if (IS_PLAYER(good_x, good_y))
9271 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9273 if (player->shield_deadly_time_left > 0 &&
9274 !IS_INDESTRUCTIBLE(bad_element))
9275 Bang(kill_x, kill_y);
9276 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9280 Bang(good_x, good_y);
9284 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9286 int i, kill_x = -1, kill_y = -1;
9287 int bad_element = Feld[bad_x][bad_y];
9288 static int test_xy[4][2] =
9295 static int touch_dir[4] =
9302 static int test_dir[4] =
9310 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9313 for (i = 0; i < NUM_DIRECTIONS; i++)
9315 int test_x, test_y, test_move_dir, test_element;
9317 test_x = bad_x + test_xy[i][0];
9318 test_y = bad_y + test_xy[i][1];
9319 if (!IN_LEV_FIELD(test_x, test_y))
9323 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9325 test_element = Feld[test_x][test_y];
9327 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9328 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9330 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9331 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9333 /* good thing is player or penguin that does not move away */
9334 if (IS_PLAYER(test_x, test_y))
9336 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9338 if (bad_element == EL_ROBOT && player->is_moving)
9339 continue; /* robot does not kill player if he is moving */
9341 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9343 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9344 continue; /* center and border element do not touch */
9351 else if (test_element == EL_PENGUIN)
9360 if (kill_x != -1 || kill_y != -1)
9362 if (IS_PLAYER(kill_x, kill_y))
9364 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9366 if (player->shield_deadly_time_left > 0 &&
9367 !IS_INDESTRUCTIBLE(bad_element))
9369 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9373 Bang(kill_x, kill_y);
9377 void TestIfPlayerTouchesBadThing(int x, int y)
9379 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9382 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
9384 TestIfGoodThingHitsBadThing(x, y, move_dir);
9387 void TestIfBadThingTouchesPlayer(int x, int y)
9389 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9392 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
9394 TestIfBadThingHitsGoodThing(x, y, move_dir);
9397 void TestIfFriendTouchesBadThing(int x, int y)
9399 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9402 void TestIfBadThingTouchesFriend(int x, int y)
9404 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9407 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9409 int i, kill_x = bad_x, kill_y = bad_y;
9410 static int xy[4][2] =
9418 for (i = 0; i < NUM_DIRECTIONS; i++)
9422 x = bad_x + xy[i][0];
9423 y = bad_y + xy[i][1];
9424 if (!IN_LEV_FIELD(x, y))
9427 element = Feld[x][y];
9428 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9429 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9437 if (kill_x != bad_x || kill_y != bad_y)
9441 void KillPlayer(struct PlayerInfo *player)
9443 int jx = player->jx, jy = player->jy;
9445 if (!player->active)
9448 /* remove accessible field at the player's position */
9449 Feld[jx][jy] = EL_EMPTY;
9451 /* deactivate shield (else Bang()/Explode() would not work right) */
9452 player->shield_normal_time_left = 0;
9453 player->shield_deadly_time_left = 0;
9459 static void KillPlayerUnlessEnemyProtected(int x, int y)
9461 if (!PLAYER_ENEMY_PROTECTED(x, y))
9462 KillPlayer(PLAYERINFO(x, y));
9465 static void KillPlayerUnlessExplosionProtected(int x, int y)
9467 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9468 KillPlayer(PLAYERINFO(x, y));
9471 void BuryPlayer(struct PlayerInfo *player)
9473 int jx = player->jx, jy = player->jy;
9475 if (!player->active)
9478 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9479 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9481 player->GameOver = TRUE;
9482 RemovePlayer(player);
9485 void RemovePlayer(struct PlayerInfo *player)
9487 int jx = player->jx, jy = player->jy;
9488 int i, found = FALSE;
9490 player->present = FALSE;
9491 player->active = FALSE;
9493 if (!ExplodeField[jx][jy])
9494 StorePlayer[jx][jy] = 0;
9496 if (player->is_moving)
9497 DrawLevelField(player->last_jx, player->last_jy);
9499 for (i = 0; i < MAX_PLAYERS; i++)
9500 if (stored_player[i].active)
9504 AllPlayersGone = TRUE;
9510 #if USE_NEW_SNAP_DELAY
9511 static void setFieldForSnapping(int x, int y, int element, int direction)
9513 struct ElementInfo *ei = &element_info[element];
9514 int direction_bit = MV_DIR_BIT(direction);
9515 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
9516 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
9517 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
9519 Feld[x][y] = EL_ELEMENT_SNAPPING;
9520 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
9522 ResetGfxAnimation(x, y);
9524 GfxElement[x][y] = element;
9525 GfxAction[x][y] = action;
9526 GfxDir[x][y] = direction;
9527 GfxFrame[x][y] = -1;
9532 =============================================================================
9533 checkDiagonalPushing()
9534 -----------------------------------------------------------------------------
9535 check if diagonal input device direction results in pushing of object
9536 (by checking if the alternative direction is walkable, diggable, ...)
9537 =============================================================================
9540 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9541 int x, int y, int real_dx, int real_dy)
9543 int jx, jy, dx, dy, xx, yy;
9545 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9548 /* diagonal direction: check alternative direction */
9553 xx = jx + (dx == 0 ? real_dx : 0);
9554 yy = jy + (dy == 0 ? real_dy : 0);
9556 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9560 =============================================================================
9562 -----------------------------------------------------------------------------
9563 x, y: field next to player (non-diagonal) to try to dig to
9564 real_dx, real_dy: direction as read from input device (can be diagonal)
9565 =============================================================================
9568 int DigField(struct PlayerInfo *player,
9569 int oldx, int oldy, int x, int y,
9570 int real_dx, int real_dy, int mode)
9572 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
9573 boolean player_was_pushing = player->is_pushing;
9574 int jx = oldx, jy = oldy;
9575 int dx = x - jx, dy = y - jy;
9576 int nextx = x + dx, nexty = y + dy;
9577 int move_direction = (dx == -1 ? MV_LEFT :
9578 dx == +1 ? MV_RIGHT :
9580 dy == +1 ? MV_DOWN : MV_NONE);
9581 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9582 int dig_side = MV_DIR_OPPOSITE(move_direction);
9583 int old_element = Feld[jx][jy];
9587 if (is_player) /* function can also be called by EL_PENGUIN */
9589 if (player->MovPos == 0)
9591 player->is_digging = FALSE;
9592 player->is_collecting = FALSE;
9595 if (player->MovPos == 0) /* last pushing move finished */
9596 player->is_pushing = FALSE;
9598 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9600 player->is_switching = FALSE;
9601 player->push_delay = -1;
9603 return MF_NO_ACTION;
9607 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9608 return MF_NO_ACTION;
9610 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9611 old_element = Back[jx][jy];
9613 /* in case of element dropped at player position, check background */
9614 else if (Back[jx][jy] != EL_EMPTY &&
9615 game.engine_version >= VERSION_IDENT(2,2,0,0))
9616 old_element = Back[jx][jy];
9618 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
9619 return MF_NO_ACTION; /* field has no opening in this direction */
9621 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
9622 return MF_NO_ACTION; /* field has no opening in this direction */
9624 element = Feld[x][y];
9625 #if USE_NEW_CUSTOM_VALUE
9628 collect_count = element_info[element].collect_count_initial;
9630 collect_count = CustomValue[x][y];
9634 collect_count = element_info[element].collect_count_initial;
9638 if (element != EL_BLOCKED &&
9639 CustomValue[x][y] != element_info[element].collect_count_initial)
9640 printf("::: %d: %d != %d\n",
9643 element_info[element].collect_count_initial);
9646 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
9647 return MF_NO_ACTION;
9649 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9650 game.engine_version >= VERSION_IDENT(2,2,0,0))
9651 return MF_NO_ACTION;
9653 if (game.gravity && is_player && !player->is_auto_moving &&
9654 canFallDown(player) && move_direction != MV_DOWN &&
9655 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
9656 return MF_NO_ACTION; /* player cannot walk here due to gravity */
9658 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
9660 int sound_element = SND_ELEMENT(element);
9661 int sound_action = ACTION_WALKING;
9663 if (IS_RND_GATE(element))
9665 if (!player->key[RND_GATE_NR(element)])
9666 return MF_NO_ACTION;
9668 else if (IS_RND_GATE_GRAY(element))
9670 if (!player->key[RND_GATE_GRAY_NR(element)])
9671 return MF_NO_ACTION;
9673 else if (element == EL_EXIT_OPEN ||
9674 element == EL_SP_EXIT_OPEN ||
9675 element == EL_SP_EXIT_OPENING)
9677 sound_action = ACTION_PASSING; /* player is passing exit */
9679 else if (element == EL_EMPTY)
9681 sound_action = ACTION_MOVING; /* nothing to walk on */
9684 /* play sound from background or player, whatever is available */
9685 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
9686 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
9688 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9690 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
9692 if (!ACCESS_FROM(element, opposite_direction))
9693 return MF_NO_ACTION; /* field not accessible from this direction */
9695 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9696 return MF_NO_ACTION;
9698 if (IS_EM_GATE(element))
9700 if (!player->key[EM_GATE_NR(element)])
9701 return MF_NO_ACTION;
9703 else if (IS_EM_GATE_GRAY(element))
9705 if (!player->key[EM_GATE_GRAY_NR(element)])
9706 return MF_NO_ACTION;
9708 else if (IS_SP_PORT(element))
9710 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9711 element == EL_SP_GRAVITY_PORT_RIGHT ||
9712 element == EL_SP_GRAVITY_PORT_UP ||
9713 element == EL_SP_GRAVITY_PORT_DOWN)
9714 game.gravity = !game.gravity;
9715 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
9716 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
9717 element == EL_SP_GRAVITY_ON_PORT_UP ||
9718 element == EL_SP_GRAVITY_ON_PORT_DOWN)
9719 game.gravity = TRUE;
9720 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
9721 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
9722 element == EL_SP_GRAVITY_OFF_PORT_UP ||
9723 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
9724 game.gravity = FALSE;
9727 /* automatically move to the next field with double speed */
9728 player->programmed_action = move_direction;
9730 if (player->move_delay_reset_counter == 0)
9732 player->move_delay_reset_counter = 2; /* two double speed steps */
9734 DOUBLE_PLAYER_SPEED(player);
9737 PlayLevelSoundAction(x, y, ACTION_PASSING);
9739 else if (IS_DIGGABLE(element))
9743 if (mode != DF_SNAP)
9745 GfxElement[x][y] = GFX_ELEMENT(element);
9746 player->is_digging = TRUE;
9749 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9751 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
9752 player->index_bit, dig_side);
9754 if (mode == DF_SNAP)
9756 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9758 #if USE_NEW_SNAP_DELAY
9759 if (level.block_snap_field)
9760 setFieldForSnapping(x, y, element, move_direction);
9764 else if (IS_COLLECTIBLE(element))
9768 if (is_player && mode != DF_SNAP)
9770 GfxElement[x][y] = element;
9771 player->is_collecting = TRUE;
9774 if (element == EL_SPEED_PILL)
9776 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9778 else if (element == EL_EXTRA_TIME && level.time > 0)
9780 TimeLeft += level.extra_time;
9781 DrawGameValue_Time(TimeLeft);
9783 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9785 player->shield_normal_time_left += level.shield_normal_time;
9786 if (element == EL_SHIELD_DEADLY)
9787 player->shield_deadly_time_left += level.shield_deadly_time;
9789 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9791 if (player->inventory_size < MAX_INVENTORY_SIZE)
9792 player->inventory_element[player->inventory_size++] = element;
9794 DrawGameValue_Dynamite(local_player->inventory_size);
9796 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9798 player->dynabomb_count++;
9799 player->dynabombs_left++;
9801 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9803 player->dynabomb_size++;
9805 else if (element == EL_DYNABOMB_INCREASE_POWER)
9807 player->dynabomb_xl = TRUE;
9809 else if (IS_KEY(element))
9811 player->key[KEY_NR(element)] = TRUE;
9813 DrawGameValue_Keys(player->key);
9815 redraw_mask |= REDRAW_DOOR_1;
9817 else if (IS_ENVELOPE(element))
9819 player->show_envelope = element;
9821 else if (IS_DROPPABLE(element) ||
9822 IS_THROWABLE(element)) /* can be collected and dropped */
9826 if (collect_count == 0)
9827 player->inventory_infinite_element = element;
9829 for (i = 0; i < collect_count; i++)
9830 if (player->inventory_size < MAX_INVENTORY_SIZE)
9831 player->inventory_element[player->inventory_size++] = element;
9833 DrawGameValue_Dynamite(local_player->inventory_size);
9835 else if (collect_count > 0)
9837 local_player->gems_still_needed -= collect_count;
9838 if (local_player->gems_still_needed < 0)
9839 local_player->gems_still_needed = 0;
9841 DrawGameValue_Emeralds(local_player->gems_still_needed);
9844 RaiseScoreElement(element);
9845 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9848 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
9849 player->index_bit, dig_side);
9851 if (mode == DF_SNAP)
9853 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9855 #if USE_NEW_SNAP_DELAY
9856 if (level.block_snap_field)
9857 setFieldForSnapping(x, y, element, move_direction);
9861 else if (IS_PUSHABLE(element))
9863 if (mode == DF_SNAP && element != EL_BD_ROCK)
9864 return MF_NO_ACTION;
9866 if (CAN_FALL(element) && dy)
9867 return MF_NO_ACTION;
9869 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9870 !(element == EL_SPRING && level.use_spring_bug))
9871 return MF_NO_ACTION;
9873 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9874 ((move_direction & MV_VERTICAL &&
9875 ((element_info[element].move_pattern & MV_LEFT &&
9876 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9877 (element_info[element].move_pattern & MV_RIGHT &&
9878 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9879 (move_direction & MV_HORIZONTAL &&
9880 ((element_info[element].move_pattern & MV_UP &&
9881 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9882 (element_info[element].move_pattern & MV_DOWN &&
9883 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9884 return MF_NO_ACTION;
9886 /* do not push elements already moving away faster than player */
9887 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9888 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9889 return MF_NO_ACTION;
9891 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9893 if (player->push_delay_value == -1 || !player_was_pushing)
9894 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9896 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9898 if (player->push_delay_value == -1)
9899 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9901 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9903 if (!player->is_pushing)
9904 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9907 player->is_pushing = TRUE;
9909 if (!(IN_LEV_FIELD(nextx, nexty) &&
9910 (IS_FREE(nextx, nexty) ||
9911 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9912 IS_SB_ELEMENT(element)))))
9913 return MF_NO_ACTION;
9915 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9916 return MF_NO_ACTION;
9918 if (player->push_delay == -1) /* new pushing; restart delay */
9919 player->push_delay = 0;
9921 if (player->push_delay < player->push_delay_value &&
9922 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9923 element != EL_SPRING && element != EL_BALLOON)
9925 /* make sure that there is no move delay before next try to push */
9926 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9927 player->move_delay = 0;
9929 return MF_NO_ACTION;
9932 if (IS_SB_ELEMENT(element))
9934 if (element == EL_SOKOBAN_FIELD_FULL)
9936 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9937 local_player->sokobanfields_still_needed++;
9940 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9942 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9943 local_player->sokobanfields_still_needed--;
9946 Feld[x][y] = EL_SOKOBAN_OBJECT;
9948 if (Back[x][y] == Back[nextx][nexty])
9949 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9950 else if (Back[x][y] != 0)
9951 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9954 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9957 if (local_player->sokobanfields_still_needed == 0 &&
9958 game.emulation == EMU_SOKOBAN)
9960 player->LevelSolved = player->GameOver = TRUE;
9961 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9965 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9967 InitMovingField(x, y, move_direction);
9968 GfxAction[x][y] = ACTION_PUSHING;
9970 if (mode == DF_SNAP)
9971 ContinueMoving(x, y);
9973 MovPos[x][y] = (dx != 0 ? dx : dy);
9975 Pushed[x][y] = TRUE;
9976 Pushed[nextx][nexty] = TRUE;
9978 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9979 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9981 player->push_delay_value = -1; /* get new value later */
9983 /* check for element change _after_ element has been pushed */
9984 if (game.use_change_when_pushing_bug)
9986 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
9987 player->index_bit, dig_side);
9988 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
9989 player->index_bit, dig_side);
9992 else if (IS_SWITCHABLE(element))
9994 if (PLAYER_SWITCHING(player, x, y))
9996 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
9997 player->index_bit, dig_side);
10002 player->is_switching = TRUE;
10003 player->switch_x = x;
10004 player->switch_y = y;
10006 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10008 if (element == EL_ROBOT_WHEEL)
10010 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10014 DrawLevelField(x, y);
10016 else if (element == EL_SP_TERMINAL)
10020 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
10022 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10024 else if (Feld[xx][yy] == EL_SP_TERMINAL)
10025 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10028 else if (IS_BELT_SWITCH(element))
10030 ToggleBeltSwitch(x, y);
10032 else if (element == EL_SWITCHGATE_SWITCH_UP ||
10033 element == EL_SWITCHGATE_SWITCH_DOWN)
10035 ToggleSwitchgateSwitch(x, y);
10037 else if (element == EL_LIGHT_SWITCH ||
10038 element == EL_LIGHT_SWITCH_ACTIVE)
10040 ToggleLightSwitch(x, y);
10042 else if (element == EL_TIMEGATE_SWITCH)
10044 ActivateTimegateSwitch(x, y);
10046 else if (element == EL_BALLOON_SWITCH_LEFT ||
10047 element == EL_BALLOON_SWITCH_RIGHT ||
10048 element == EL_BALLOON_SWITCH_UP ||
10049 element == EL_BALLOON_SWITCH_DOWN ||
10050 element == EL_BALLOON_SWITCH_NONE ||
10051 element == EL_BALLOON_SWITCH_ANY)
10053 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10054 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10055 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10056 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10057 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
10060 else if (element == EL_LAMP)
10062 Feld[x][y] = EL_LAMP_ACTIVE;
10063 local_player->lights_still_needed--;
10065 ResetGfxAnimation(x, y);
10066 DrawLevelField(x, y);
10068 else if (element == EL_TIME_ORB_FULL)
10070 Feld[x][y] = EL_TIME_ORB_EMPTY;
10072 if (level.time > 0 || level.use_time_orb_bug)
10074 TimeLeft += level.time_orb_time;
10075 DrawGameValue_Time(TimeLeft);
10078 ResetGfxAnimation(x, y);
10079 DrawLevelField(x, y);
10082 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10083 player->index_bit, dig_side);
10085 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10086 player->index_bit, dig_side);
10092 if (!PLAYER_SWITCHING(player, x, y))
10094 player->is_switching = TRUE;
10095 player->switch_x = x;
10096 player->switch_y = y;
10098 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
10099 player->index_bit, dig_side);
10100 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10101 player->index_bit, dig_side);
10104 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10105 player->index_bit, dig_side);
10106 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10107 player->index_bit, dig_side);
10109 return MF_NO_ACTION;
10112 player->push_delay = -1;
10114 if (is_player) /* function can also be called by EL_PENGUIN */
10116 if (Feld[x][y] != element) /* really digged/collected something */
10117 player->is_collecting = !player->is_digging;
10123 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10125 int jx = player->jx, jy = player->jy;
10126 int x = jx + dx, y = jy + dy;
10127 int snap_direction = (dx == -1 ? MV_LEFT :
10128 dx == +1 ? MV_RIGHT :
10130 dy == +1 ? MV_DOWN : MV_NONE);
10132 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
10135 if (!player->active || !IN_LEV_FIELD(x, y))
10143 if (player->MovPos == 0)
10144 player->is_pushing = FALSE;
10146 player->is_snapping = FALSE;
10148 if (player->MovPos == 0)
10150 player->is_moving = FALSE;
10151 player->is_digging = FALSE;
10152 player->is_collecting = FALSE;
10158 if (player->is_snapping)
10161 player->MovDir = snap_direction;
10163 if (player->MovPos == 0)
10165 player->is_moving = FALSE;
10166 player->is_digging = FALSE;
10167 player->is_collecting = FALSE;
10170 player->is_dropping = FALSE;
10172 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10175 player->is_snapping = TRUE;
10177 if (player->MovPos == 0)
10179 player->is_moving = FALSE;
10180 player->is_digging = FALSE;
10181 player->is_collecting = FALSE;
10184 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
10185 DrawLevelField(player->last_jx, player->last_jy);
10187 DrawLevelField(x, y);
10192 boolean DropElement(struct PlayerInfo *player)
10194 int old_element, new_element;
10195 int dropx = player->jx, dropy = player->jy;
10196 int drop_direction = player->MovDir;
10197 int drop_side = drop_direction;
10198 int drop_element = (player->inventory_size > 0 ?
10199 player->inventory_element[player->inventory_size - 1] :
10200 player->inventory_infinite_element != EL_UNDEFINED ?
10201 player->inventory_infinite_element :
10202 player->dynabombs_left > 0 ?
10203 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10206 /* do not drop an element on top of another element; when holding drop key
10207 pressed without moving, dropped element must move away before the next
10208 element can be dropped (this is especially important if the next element
10209 is dynamite, which can be placed on background for historical reasons) */
10210 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
10213 if (IS_THROWABLE(drop_element))
10215 dropx += GET_DX_FROM_DIR(drop_direction);
10216 dropy += GET_DY_FROM_DIR(drop_direction);
10218 if (!IN_LEV_FIELD(dropx, dropy))
10222 old_element = Feld[dropx][dropy]; /* old element at dropping position */
10223 new_element = drop_element; /* default: no change when dropping */
10225 /* check if player is active, not moving and ready to drop */
10226 if (!player->active || player->MovPos || player->drop_delay > 0)
10229 /* check if player has anything that can be dropped */
10230 if (new_element == EL_UNDEFINED)
10233 /* check if anything can be dropped at the current position */
10234 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10237 /* collected custom elements can only be dropped on empty fields */
10238 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10241 if (old_element != EL_EMPTY)
10242 Back[dropx][dropy] = old_element; /* store old element on this field */
10244 ResetGfxAnimation(dropx, dropy);
10245 ResetRandomAnimationValue(dropx, dropy);
10247 if (player->inventory_size > 0 ||
10248 player->inventory_infinite_element != EL_UNDEFINED)
10250 if (player->inventory_size > 0)
10252 player->inventory_size--;
10254 DrawGameValue_Dynamite(local_player->inventory_size);
10256 if (new_element == EL_DYNAMITE)
10257 new_element = EL_DYNAMITE_ACTIVE;
10258 else if (new_element == EL_SP_DISK_RED)
10259 new_element = EL_SP_DISK_RED_ACTIVE;
10262 Feld[dropx][dropy] = new_element;
10264 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10265 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10266 el2img(Feld[dropx][dropy]), 0);
10268 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10270 /* needed if previous element just changed to "empty" in the last frame */
10271 Changed[dropx][dropy] = FALSE; /* allow another change */
10273 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
10274 player->index_bit, drop_side);
10275 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
10277 player->index_bit, drop_side);
10279 TestIfElementTouchesCustomElement(dropx, dropy);
10281 else /* player is dropping a dyna bomb */
10283 player->dynabombs_left--;
10285 Feld[dropx][dropy] = new_element;
10287 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10288 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10289 el2img(Feld[dropx][dropy]), 0);
10291 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10294 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
10295 InitField_WithBug1(dropx, dropy, FALSE);
10297 new_element = Feld[dropx][dropy]; /* element might have changed */
10299 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10300 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10302 int move_direction, nextx, nexty;
10304 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10305 MovDir[dropx][dropy] = drop_direction;
10307 move_direction = MovDir[dropx][dropy];
10308 nextx = dropx + GET_DX_FROM_DIR(move_direction);
10309 nexty = dropy + GET_DY_FROM_DIR(move_direction);
10311 Changed[dropx][dropy] = FALSE; /* allow another change */
10312 CheckCollision[dropx][dropy] = 2;
10315 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
10316 player->is_dropping = TRUE;
10318 player->drop_x = dropx;
10319 player->drop_y = dropy;
10324 /* ------------------------------------------------------------------------- */
10325 /* game sound playing functions */
10326 /* ------------------------------------------------------------------------- */
10328 static int *loop_sound_frame = NULL;
10329 static int *loop_sound_volume = NULL;
10331 void InitPlayLevelSound()
10333 int num_sounds = getSoundListSize();
10335 checked_free(loop_sound_frame);
10336 checked_free(loop_sound_volume);
10338 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10339 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10342 static void PlayLevelSound(int x, int y, int nr)
10344 int sx = SCREENX(x), sy = SCREENY(y);
10345 int volume, stereo_position;
10346 int max_distance = 8;
10347 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10349 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10350 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10353 if (!IN_LEV_FIELD(x, y) ||
10354 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10355 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10358 volume = SOUND_MAX_VOLUME;
10360 if (!IN_SCR_FIELD(sx, sy))
10362 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10363 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10365 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10368 stereo_position = (SOUND_MAX_LEFT +
10369 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10370 (SCR_FIELDX + 2 * max_distance));
10372 if (IS_LOOP_SOUND(nr))
10374 /* This assures that quieter loop sounds do not overwrite louder ones,
10375 while restarting sound volume comparison with each new game frame. */
10377 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10380 loop_sound_volume[nr] = volume;
10381 loop_sound_frame[nr] = FrameCounter;
10384 PlaySoundExt(nr, volume, stereo_position, type);
10387 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10389 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10390 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10391 y < LEVELY(BY1) ? LEVELY(BY1) :
10392 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10396 static void PlayLevelSoundAction(int x, int y, int action)
10398 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10401 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10403 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10405 if (sound_effect != SND_UNDEFINED)
10406 PlayLevelSound(x, y, sound_effect);
10409 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10412 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10414 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10415 PlayLevelSound(x, y, sound_effect);
10418 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10420 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10422 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10423 PlayLevelSound(x, y, sound_effect);
10426 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10428 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10430 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10431 StopSound(sound_effect);
10434 static void PlayLevelMusic()
10436 if (levelset.music[level_nr] != MUS_UNDEFINED)
10437 PlayMusic(levelset.music[level_nr]); /* from config file */
10439 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10442 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
10444 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
10449 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
10453 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10457 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10461 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10465 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10469 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10473 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10476 case SAMPLE_android_clone:
10477 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10480 case SAMPLE_android_move:
10481 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10484 case SAMPLE_spring:
10485 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10489 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
10493 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
10496 case SAMPLE_eater_eat:
10497 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10501 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10504 case SAMPLE_collect:
10505 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10508 case SAMPLE_diamond:
10509 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10512 case SAMPLE_squash:
10513 /* !!! CHECK THIS !!! */
10515 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10517 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
10521 case SAMPLE_wonderfall:
10522 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
10526 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10530 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10534 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10538 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
10542 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10546 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
10549 case SAMPLE_wonder:
10550 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10554 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10557 case SAMPLE_exit_open:
10558 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
10561 case SAMPLE_exit_leave:
10562 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10565 case SAMPLE_dynamite:
10566 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10570 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10574 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10578 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10582 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
10586 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
10590 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10594 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
10599 void RaiseScore(int value)
10601 local_player->score += value;
10603 DrawGameValue_Score(local_player->score);
10606 void RaiseScoreElement(int element)
10611 case EL_BD_DIAMOND:
10612 case EL_EMERALD_YELLOW:
10613 case EL_EMERALD_RED:
10614 case EL_EMERALD_PURPLE:
10615 case EL_SP_INFOTRON:
10616 RaiseScore(level.score[SC_EMERALD]);
10619 RaiseScore(level.score[SC_DIAMOND]);
10622 RaiseScore(level.score[SC_CRYSTAL]);
10625 RaiseScore(level.score[SC_PEARL]);
10628 case EL_BD_BUTTERFLY:
10629 case EL_SP_ELECTRON:
10630 RaiseScore(level.score[SC_BUG]);
10633 case EL_BD_FIREFLY:
10634 case EL_SP_SNIKSNAK:
10635 RaiseScore(level.score[SC_SPACESHIP]);
10638 case EL_DARK_YAMYAM:
10639 RaiseScore(level.score[SC_YAMYAM]);
10642 RaiseScore(level.score[SC_ROBOT]);
10645 RaiseScore(level.score[SC_PACMAN]);
10648 RaiseScore(level.score[SC_NUT]);
10651 case EL_SP_DISK_RED:
10652 case EL_DYNABOMB_INCREASE_NUMBER:
10653 case EL_DYNABOMB_INCREASE_SIZE:
10654 case EL_DYNABOMB_INCREASE_POWER:
10655 RaiseScore(level.score[SC_DYNAMITE]);
10657 case EL_SHIELD_NORMAL:
10658 case EL_SHIELD_DEADLY:
10659 RaiseScore(level.score[SC_SHIELD]);
10661 case EL_EXTRA_TIME:
10662 RaiseScore(level.extra_time_score);
10676 RaiseScore(level.score[SC_KEY]);
10679 RaiseScore(element_info[element].collect_score);
10684 void RequestQuitGame(boolean ask_if_really_quit)
10686 if (AllPlayersGone ||
10687 !ask_if_really_quit ||
10688 level_editor_test_game ||
10689 Request("Do you really want to quit the game ?",
10690 REQ_ASK | REQ_STAY_CLOSED))
10692 #if defined(NETWORK_AVALIABLE)
10693 if (options.network)
10694 SendToServer_StopPlaying();
10698 game_status = GAME_MODE_MAIN;
10704 if (tape.playing && tape.deactivate_display)
10705 TapeDeactivateDisplayOff(TRUE);
10707 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10709 if (tape.playing && tape.deactivate_display)
10710 TapeDeactivateDisplayOn();
10715 /* ---------- new game button stuff ---------------------------------------- */
10717 /* graphic position values for game buttons */
10718 #define GAME_BUTTON_XSIZE 30
10719 #define GAME_BUTTON_YSIZE 30
10720 #define GAME_BUTTON_XPOS 5
10721 #define GAME_BUTTON_YPOS 215
10722 #define SOUND_BUTTON_XPOS 5
10723 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10725 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10726 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10727 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10728 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10729 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10730 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10737 } gamebutton_info[NUM_GAME_BUTTONS] =
10740 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10745 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10746 GAME_CTRL_ID_PAUSE,
10750 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10755 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10756 SOUND_CTRL_ID_MUSIC,
10757 "background music on/off"
10760 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10761 SOUND_CTRL_ID_LOOPS,
10762 "sound loops on/off"
10765 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10766 SOUND_CTRL_ID_SIMPLE,
10767 "normal sounds on/off"
10771 void CreateGameButtons()
10775 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10777 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10778 struct GadgetInfo *gi;
10781 unsigned long event_mask;
10782 int gd_xoffset, gd_yoffset;
10783 int gd_x1, gd_x2, gd_y1, gd_y2;
10786 gd_xoffset = gamebutton_info[i].x;
10787 gd_yoffset = gamebutton_info[i].y;
10788 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10789 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10791 if (id == GAME_CTRL_ID_STOP ||
10792 id == GAME_CTRL_ID_PAUSE ||
10793 id == GAME_CTRL_ID_PLAY)
10795 button_type = GD_TYPE_NORMAL_BUTTON;
10797 event_mask = GD_EVENT_RELEASED;
10798 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10799 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10803 button_type = GD_TYPE_CHECK_BUTTON;
10805 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10806 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10807 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10808 event_mask = GD_EVENT_PRESSED;
10809 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10810 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10813 gi = CreateGadget(GDI_CUSTOM_ID, id,
10814 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10815 GDI_X, DX + gd_xoffset,
10816 GDI_Y, DY + gd_yoffset,
10817 GDI_WIDTH, GAME_BUTTON_XSIZE,
10818 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10819 GDI_TYPE, button_type,
10820 GDI_STATE, GD_BUTTON_UNPRESSED,
10821 GDI_CHECKED, checked,
10822 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10823 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10824 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10825 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10826 GDI_EVENT_MASK, event_mask,
10827 GDI_CALLBACK_ACTION, HandleGameButtons,
10831 Error(ERR_EXIT, "cannot create gadget");
10833 game_gadget[id] = gi;
10837 void FreeGameButtons()
10841 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10842 FreeGadget(game_gadget[i]);
10845 static void MapGameButtons()
10849 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10850 MapGadget(game_gadget[i]);
10853 void UnmapGameButtons()
10857 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10858 UnmapGadget(game_gadget[i]);
10861 static void HandleGameButtons(struct GadgetInfo *gi)
10863 int id = gi->custom_id;
10865 if (game_status != GAME_MODE_PLAYING)
10870 case GAME_CTRL_ID_STOP:
10871 RequestQuitGame(TRUE);
10874 case GAME_CTRL_ID_PAUSE:
10875 if (options.network)
10877 #if defined(NETWORK_AVALIABLE)
10879 SendToServer_ContinuePlaying();
10881 SendToServer_PausePlaying();
10885 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10888 case GAME_CTRL_ID_PLAY:
10891 #if defined(NETWORK_AVALIABLE)
10892 if (options.network)
10893 SendToServer_ContinuePlaying();
10897 tape.pausing = FALSE;
10898 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10903 case SOUND_CTRL_ID_MUSIC:
10904 if (setup.sound_music)
10906 setup.sound_music = FALSE;
10909 else if (audio.music_available)
10911 setup.sound = setup.sound_music = TRUE;
10913 SetAudioMode(setup.sound);
10919 case SOUND_CTRL_ID_LOOPS:
10920 if (setup.sound_loops)
10921 setup.sound_loops = FALSE;
10922 else if (audio.loops_available)
10924 setup.sound = setup.sound_loops = TRUE;
10925 SetAudioMode(setup.sound);
10929 case SOUND_CTRL_ID_SIMPLE:
10930 if (setup.sound_simple)
10931 setup.sound_simple = FALSE;
10932 else if (audio.sound_available)
10934 setup.sound = setup.sound_simple = TRUE;
10935 SetAudioMode(setup.sound);