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 SetPlayerWaiting(struct PlayerInfo *, boolean);
236 static void AdvanceFrameAndPlayerCounters(int);
238 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
239 static boolean MovePlayer(struct PlayerInfo *, int, int);
240 static void ScrollPlayer(struct PlayerInfo *, int);
241 static void ScrollScreen(struct PlayerInfo *, int);
243 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
245 static void InitBeltMovement(void);
246 static void CloseAllOpenTimegates(void);
247 static void CheckGravityMovement(struct PlayerInfo *);
248 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
249 static void KillPlayerUnlessEnemyProtected(int, int);
250 static void KillPlayerUnlessExplosionProtected(int, int);
252 static void TestIfPlayerTouchesCustomElement(int, int);
253 static void TestIfElementTouchesCustomElement(int, int);
254 static void TestIfElementHitsCustomElement(int, int, int);
256 static void TestIfElementSmashesCustomElement(int, int, int);
259 static void ChangeElement(int, int, int);
261 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
262 #define CheckTriggeredElementChange(x, y, e, ev) \
263 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
264 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
265 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
266 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
267 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
268 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
269 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
271 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
272 #define CheckElementChange(x, y, e, te, ev) \
273 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
274 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
275 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
276 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
277 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
279 static void PlayLevelSound(int, int, int);
280 static void PlayLevelSoundNearest(int, int, int);
281 static void PlayLevelSoundAction(int, int, int);
282 static void PlayLevelSoundElementAction(int, int, int, int);
283 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
284 static void PlayLevelSoundActionIfLoop(int, int, int);
285 static void StopLevelSoundActionIfLoop(int, int, int);
286 static void PlayLevelMusic();
288 static void MapGameButtons();
289 static void HandleGameButtons(struct GadgetInfo *);
291 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
294 /* ------------------------------------------------------------------------- */
295 /* definition of elements that automatically change to other elements after */
296 /* a specified time, eventually calling a function when changing */
297 /* ------------------------------------------------------------------------- */
299 /* forward declaration for changer functions */
300 static void InitBuggyBase(int x, int y);
301 static void WarnBuggyBase(int x, int y);
303 static void InitTrap(int x, int y);
304 static void ActivateTrap(int x, int y);
305 static void ChangeActiveTrap(int x, int y);
307 static void InitRobotWheel(int x, int y);
308 static void RunRobotWheel(int x, int y);
309 static void StopRobotWheel(int x, int y);
311 static void InitTimegateWheel(int x, int y);
312 static void RunTimegateWheel(int x, int y);
314 struct ChangingElementInfo
319 void (*pre_change_function)(int x, int y);
320 void (*change_function)(int x, int y);
321 void (*post_change_function)(int x, int y);
324 static struct ChangingElementInfo change_delay_list[] =
375 EL_SWITCHGATE_OPENING,
383 EL_SWITCHGATE_CLOSING,
384 EL_SWITCHGATE_CLOSED,
416 EL_ACID_SPLASH_RIGHT,
425 EL_SP_BUGGY_BASE_ACTIVATING,
432 EL_SP_BUGGY_BASE_ACTIVATING,
433 EL_SP_BUGGY_BASE_ACTIVE,
440 EL_SP_BUGGY_BASE_ACTIVE,
464 EL_ROBOT_WHEEL_ACTIVE,
472 EL_TIMEGATE_SWITCH_ACTIVE,
493 int push_delay_fixed, push_delay_random;
498 { EL_BALLOON, 0, 0 },
500 { EL_SOKOBAN_OBJECT, 2, 0 },
501 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
502 { EL_SATELLITE, 2, 0 },
503 { EL_SP_DISK_YELLOW, 2, 0 },
505 { EL_UNDEFINED, 0, 0 },
513 move_stepsize_list[] =
515 { EL_AMOEBA_DROP, 2 },
516 { EL_AMOEBA_DROPPING, 2 },
517 { EL_QUICKSAND_FILLING, 1 },
518 { EL_QUICKSAND_EMPTYING, 1 },
519 { EL_MAGIC_WALL_FILLING, 2 },
520 { EL_BD_MAGIC_WALL_FILLING, 2 },
521 { EL_MAGIC_WALL_EMPTYING, 2 },
522 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
532 collect_count_list[] =
535 { EL_BD_DIAMOND, 1 },
536 { EL_EMERALD_YELLOW, 1 },
537 { EL_EMERALD_RED, 1 },
538 { EL_EMERALD_PURPLE, 1 },
540 { EL_SP_INFOTRON, 1 },
552 access_direction_list[] =
554 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
555 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
556 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
557 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
558 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
559 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
560 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
561 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
562 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
563 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
564 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
566 { EL_SP_PORT_LEFT, MV_RIGHT },
567 { EL_SP_PORT_RIGHT, MV_LEFT },
568 { EL_SP_PORT_UP, MV_DOWN },
569 { EL_SP_PORT_DOWN, MV_UP },
570 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
571 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
572 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
573 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
574 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
575 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
576 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
577 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
578 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
579 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
580 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
581 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
582 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
583 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
584 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
586 { EL_UNDEFINED, MV_NONE }
589 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
591 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
592 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
593 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
594 IS_JUST_CHANGING(x, y))
596 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
599 void GetPlayerConfig()
601 if (!audio.sound_available)
602 setup.sound_simple = FALSE;
604 if (!audio.loops_available)
605 setup.sound_loops = FALSE;
607 if (!audio.music_available)
608 setup.sound_music = FALSE;
610 if (!video.fullscreen_available)
611 setup.fullscreen = FALSE;
613 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
615 SetAudioMode(setup.sound);
619 static int getBeltNrFromBeltElement(int element)
621 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
622 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
623 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
626 static int getBeltNrFromBeltActiveElement(int element)
628 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
629 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
630 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
633 static int getBeltNrFromBeltSwitchElement(int element)
635 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
636 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
637 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
640 static int getBeltDirNrFromBeltSwitchElement(int element)
642 static int belt_base_element[4] =
644 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
645 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
646 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
647 EL_CONVEYOR_BELT_4_SWITCH_LEFT
650 int belt_nr = getBeltNrFromBeltSwitchElement(element);
651 int belt_dir_nr = element - belt_base_element[belt_nr];
653 return (belt_dir_nr % 3);
656 static int getBeltDirFromBeltSwitchElement(int element)
658 static int belt_move_dir[3] =
665 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
667 return belt_move_dir[belt_dir_nr];
670 static void InitPlayerField(int x, int y, int element, boolean init_game)
672 if (element == EL_SP_MURPHY)
676 if (stored_player[0].present)
678 Feld[x][y] = EL_SP_MURPHY_CLONE;
684 stored_player[0].use_murphy = TRUE;
687 Feld[x][y] = EL_PLAYER_1;
693 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
694 int jx = player->jx, jy = player->jy;
696 player->present = TRUE;
698 player->block_last_field = (element == EL_SP_MURPHY ?
699 level.sp_block_last_field :
700 level.block_last_field);
702 /* ---------- initialize player's last field block delay --------------- */
704 /* always start with reliable default value (no adjustment needed) */
705 player->block_delay_adjustment = 0;
707 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
708 if (player->block_last_field && element == EL_SP_MURPHY)
709 player->block_delay_adjustment = 1;
711 /* special case 2: in game engines before 3.1.1, blocking was different */
712 if (game.use_block_last_field_bug)
713 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
715 if (!options.network || player->connected)
717 player->active = TRUE;
719 /* remove potentially duplicate players */
720 if (StorePlayer[jx][jy] == Feld[x][y])
721 StorePlayer[jx][jy] = 0;
723 StorePlayer[x][y] = Feld[x][y];
727 printf("Player %d activated.\n", player->element_nr);
728 printf("[Local player is %d and currently %s.]\n",
729 local_player->element_nr,
730 local_player->active ? "active" : "not active");
734 Feld[x][y] = EL_EMPTY;
736 player->jx = player->last_jx = x;
737 player->jy = player->last_jy = y;
741 static void InitField(int x, int y, boolean init_game)
743 int element = Feld[x][y];
752 InitPlayerField(x, y, element, init_game);
755 case EL_SOKOBAN_FIELD_PLAYER:
756 element = Feld[x][y] = EL_PLAYER_1;
757 InitField(x, y, init_game);
759 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
760 InitField(x, y, init_game);
763 case EL_SOKOBAN_FIELD_EMPTY:
764 local_player->sokobanfields_still_needed++;
768 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
769 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
770 else if (x > 0 && Feld[x-1][y] == EL_ACID)
771 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
772 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
773 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
774 else if (y > 0 && Feld[x][y-1] == EL_ACID)
775 Feld[x][y] = EL_ACID_POOL_BOTTOM;
776 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
777 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
785 case EL_SPACESHIP_RIGHT:
786 case EL_SPACESHIP_UP:
787 case EL_SPACESHIP_LEFT:
788 case EL_SPACESHIP_DOWN:
790 case EL_BD_BUTTERFLY_RIGHT:
791 case EL_BD_BUTTERFLY_UP:
792 case EL_BD_BUTTERFLY_LEFT:
793 case EL_BD_BUTTERFLY_DOWN:
794 case EL_BD_BUTTERFLY:
795 case EL_BD_FIREFLY_RIGHT:
796 case EL_BD_FIREFLY_UP:
797 case EL_BD_FIREFLY_LEFT:
798 case EL_BD_FIREFLY_DOWN:
800 case EL_PACMAN_RIGHT:
824 if (y == lev_fieldy - 1)
826 Feld[x][y] = EL_AMOEBA_GROWING;
827 Store[x][y] = EL_AMOEBA_WET;
831 case EL_DYNAMITE_ACTIVE:
832 case EL_SP_DISK_RED_ACTIVE:
833 case EL_DYNABOMB_PLAYER_1_ACTIVE:
834 case EL_DYNABOMB_PLAYER_2_ACTIVE:
835 case EL_DYNABOMB_PLAYER_3_ACTIVE:
836 case EL_DYNABOMB_PLAYER_4_ACTIVE:
841 local_player->lights_still_needed++;
845 local_player->friends_still_needed++;
850 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
853 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
854 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
855 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
856 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
857 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
858 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
859 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
860 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
861 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
862 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
863 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
864 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
867 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
868 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
869 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
871 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
873 game.belt_dir[belt_nr] = belt_dir;
874 game.belt_dir_nr[belt_nr] = belt_dir_nr;
876 else /* more than one switch -- set it like the first switch */
878 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
883 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
885 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
888 case EL_LIGHT_SWITCH_ACTIVE:
890 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
895 if (IS_CUSTOM_ELEMENT(element))
897 if (CAN_MOVE(element))
900 #if USE_NEW_CUSTOM_VALUE
901 if (!element_info[element].use_last_ce_value || init_game)
902 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
906 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
909 else if (IS_GROUP_ELEMENT(element))
911 struct ElementGroupInfo *group = element_info[element].group;
912 int last_anim_random_frame = gfx.anim_random_frame;
915 if (group->choice_mode == ANIM_RANDOM)
916 gfx.anim_random_frame = RND(group->num_elements_resolved);
918 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
919 group->choice_mode, 0,
922 if (group->choice_mode == ANIM_RANDOM)
923 gfx.anim_random_frame = last_anim_random_frame;
927 Feld[x][y] = group->element_resolved[element_pos];
929 InitField(x, y, init_game);
936 #if USE_NEW_CUSTOM_VALUE
939 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
941 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
949 static inline void InitField_WithBug1(int x, int y, boolean init_game)
951 InitField(x, y, init_game);
953 /* not needed to call InitMovDir() -- already done by InitField()! */
954 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
955 CAN_MOVE(Feld[x][y]))
959 static inline void InitField_WithBug2(int x, int y, boolean init_game)
961 int old_element = Feld[x][y];
963 InitField(x, y, init_game);
965 /* not needed to call InitMovDir() -- already done by InitField()! */
966 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
967 CAN_MOVE(old_element) &&
968 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
971 /* this case is in fact a combination of not less than three bugs:
972 first, it calls InitMovDir() for elements that can move, although this is
973 already done by InitField(); then, it checks the element that was at this
974 field _before_ the call to InitField() (which can change it); lastly, it
975 was not called for "mole with direction" elements, which were treated as
976 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
980 inline void DrawGameValue_Emeralds(int value)
982 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
985 inline void DrawGameValue_Dynamite(int value)
987 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
990 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
994 /* currently only 4 of 8 possible keys are displayed */
995 for (i = 0; i < STD_NUM_KEYS; i++)
998 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
999 el2edimg(EL_KEY_1 + i));
1001 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1002 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1003 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1007 inline void DrawGameValue_Score(int value)
1009 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1012 inline void DrawGameValue_Time(int value)
1015 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1017 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1020 inline void DrawGameValue_Level(int value)
1023 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1026 /* misuse area for displaying emeralds to draw bigger level number */
1027 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1028 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1030 /* now copy it to the area for displaying level number */
1031 BlitBitmap(drawto, drawto,
1032 DX_EMERALDS, DY_EMERALDS + 1,
1033 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1034 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1035 DX_LEVEL - 1, DY_LEVEL + 1);
1037 /* restore the area for displaying emeralds */
1038 DrawGameValue_Emeralds(local_player->gems_still_needed);
1040 /* yes, this is all really ugly :-) */
1044 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1047 int key[MAX_NUM_KEYS];
1050 for (i = 0; i < MAX_NUM_KEYS; i++)
1051 key[i] = key_bits & (1 << i);
1053 DrawGameValue_Level(level_nr);
1055 DrawGameValue_Emeralds(emeralds);
1056 DrawGameValue_Dynamite(dynamite);
1057 DrawGameValue_Score(score);
1058 DrawGameValue_Time(time);
1060 DrawGameValue_Keys(key);
1063 void DrawGameDoorValues()
1067 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1069 DrawGameDoorValues_EM();
1074 DrawGameValue_Level(level_nr);
1076 DrawGameValue_Emeralds(local_player->gems_still_needed);
1077 DrawGameValue_Dynamite(local_player->inventory_size);
1078 DrawGameValue_Score(local_player->score);
1079 DrawGameValue_Time(TimeLeft);
1081 for (i = 0; i < MAX_PLAYERS; i++)
1082 DrawGameValue_Keys(stored_player[i].key);
1085 static void resolve_group_element(int group_element, int recursion_depth)
1087 static int group_nr;
1088 static struct ElementGroupInfo *group;
1089 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1092 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1094 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1095 group_element - EL_GROUP_START + 1);
1097 /* replace element which caused too deep recursion by question mark */
1098 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1103 if (recursion_depth == 0) /* initialization */
1105 group = element_info[group_element].group;
1106 group_nr = group_element - EL_GROUP_START;
1108 group->num_elements_resolved = 0;
1109 group->choice_pos = 0;
1112 for (i = 0; i < actual_group->num_elements; i++)
1114 int element = actual_group->element[i];
1116 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1119 if (IS_GROUP_ELEMENT(element))
1120 resolve_group_element(element, recursion_depth + 1);
1123 group->element_resolved[group->num_elements_resolved++] = element;
1124 element_info[element].in_group[group_nr] = TRUE;
1131 =============================================================================
1133 -----------------------------------------------------------------------------
1134 initialize game engine due to level / tape version number
1135 =============================================================================
1138 static void InitGameEngine()
1142 /* set game engine from tape file when re-playing, else from level file */
1143 game.engine_version = (tape.playing ? tape.engine_version :
1144 level.game_version);
1146 /* ---------------------------------------------------------------------- */
1147 /* set flags for bugs and changes according to active game engine version */
1148 /* ---------------------------------------------------------------------- */
1151 Summary of bugfix/change:
1152 Fixed handling for custom elements that change when pushed by the player.
1154 Fixed/changed in version:
1158 Before 3.1.0, custom elements that "change when pushing" changed directly
1159 after the player started pushing them (until then handled in "DigField()").
1160 Since 3.1.0, these custom elements are not changed until the "pushing"
1161 move of the element is finished (now handled in "ContinueMoving()").
1163 Affected levels/tapes:
1164 The first condition is generally needed for all levels/tapes before version
1165 3.1.0, which might use the old behaviour before it was changed; known tapes
1166 that are affected are some tapes from the level set "Walpurgis Gardens" by
1168 The second condition is an exception from the above case and is needed for
1169 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1170 above (including some development versions of 3.1.0), but before it was
1171 known that this change would break tapes like the above and was fixed in
1172 3.1.1, so that the changed behaviour was active although the engine version
1173 while recording maybe was before 3.1.0. There is at least one tape that is
1174 affected by this exception, which is the tape for the one-level set "Bug
1175 Machine" by Juergen Bonhagen.
1178 game.use_change_when_pushing_bug =
1179 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1181 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1182 tape.game_version < VERSION_IDENT(3,1,1,0)));
1185 Summary of bugfix/change:
1186 Fixed handling for blocking the field the player leaves when moving.
1188 Fixed/changed in version:
1192 Before 3.1.1, when "block last field when moving" was enabled, the field
1193 the player is leaving when moving was blocked for the time of the move,
1194 and was directly unblocked afterwards. This resulted in the last field
1195 being blocked for exactly one less than the number of frames of one player
1196 move. Additionally, even when blocking was disabled, the last field was
1197 blocked for exactly one frame.
1198 Since 3.1.1, due to changes in player movement handling, the last field
1199 is not blocked at all when blocking is disabled. When blocking is enabled,
1200 the last field is blocked for exactly the number of frames of one player
1201 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1202 last field is blocked for exactly one more than the number of frames of
1205 Affected levels/tapes:
1206 (!!! yet to be determined -- probably many !!!)
1209 game.use_block_last_field_bug =
1210 (game.engine_version < VERSION_IDENT(3,1,1,0));
1212 /* ---------------------------------------------------------------------- */
1214 /* dynamically adjust element properties according to game engine version */
1215 InitElementPropertiesEngine(game.engine_version);
1218 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1219 printf(" tape version == %06d [%s] [file: %06d]\n",
1220 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1222 printf(" => game.engine_version == %06d\n", game.engine_version);
1225 /* ---------- recursively resolve group elements ------------------------- */
1227 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1228 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1229 element_info[i].in_group[j] = FALSE;
1231 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1232 resolve_group_element(EL_GROUP_START + i, 0);
1234 /* ---------- initialize player's initial move delay --------------------- */
1236 /* dynamically adjust player properties according to level information */
1237 game.initial_move_delay_value =
1238 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1240 /* dynamically adjust player properties according to game engine version */
1241 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1242 game.initial_move_delay_value : 0);
1244 /* ---------- initialize player's initial push delay --------------------- */
1246 /* dynamically adjust player properties according to game engine version */
1247 game.initial_push_delay_value =
1248 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1250 /* ---------- initialize changing elements ------------------------------- */
1252 /* initialize changing elements information */
1253 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1255 struct ElementInfo *ei = &element_info[i];
1257 /* this pointer might have been changed in the level editor */
1258 ei->change = &ei->change_page[0];
1260 if (!IS_CUSTOM_ELEMENT(i))
1262 ei->change->target_element = EL_EMPTY_SPACE;
1263 ei->change->delay_fixed = 0;
1264 ei->change->delay_random = 0;
1265 ei->change->delay_frames = 1;
1268 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1270 ei->has_change_event[j] = FALSE;
1272 ei->event_page_nr[j] = 0;
1273 ei->event_page[j] = &ei->change_page[0];
1277 /* add changing elements from pre-defined list */
1278 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1280 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1281 struct ElementInfo *ei = &element_info[ch_delay->element];
1283 ei->change->target_element = ch_delay->target_element;
1284 ei->change->delay_fixed = ch_delay->change_delay;
1286 ei->change->pre_change_function = ch_delay->pre_change_function;
1287 ei->change->change_function = ch_delay->change_function;
1288 ei->change->post_change_function = ch_delay->post_change_function;
1290 ei->change->can_change = TRUE;
1291 ei->change->can_change_or_has_action = TRUE;
1293 ei->has_change_event[CE_DELAY] = TRUE;
1295 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1296 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1299 /* ---------- initialize internal run-time variables ------------- */
1301 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1303 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1305 for (j = 0; j < ei->num_change_pages; j++)
1307 ei->change_page[j].can_change_or_has_action =
1308 (ei->change_page[j].can_change |
1309 ei->change_page[j].has_action);
1313 /* add change events from custom element configuration */
1314 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1316 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1318 for (j = 0; j < ei->num_change_pages; j++)
1320 if (!ei->change_page[j].can_change_or_has_action)
1323 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1325 /* only add event page for the first page found with this event */
1326 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1328 ei->has_change_event[k] = TRUE;
1330 ei->event_page_nr[k] = j;
1331 ei->event_page[k] = &ei->change_page[j];
1337 /* ---------- initialize run-time trigger player and element ------------- */
1339 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1341 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1343 for (j = 0; j < ei->num_change_pages; j++)
1345 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1346 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1347 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1348 ei->change_page[j].actual_trigger_ce_value = 0;
1352 /* ---------- initialize trigger events ---------------------------------- */
1354 /* initialize trigger events information */
1355 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1356 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1357 trigger_events[i][j] = FALSE;
1359 /* add trigger events from element change event properties */
1360 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1362 struct ElementInfo *ei = &element_info[i];
1364 for (j = 0; j < ei->num_change_pages; j++)
1366 if (!ei->change_page[j].can_change_or_has_action)
1369 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1371 int trigger_element = ei->change_page[j].trigger_element;
1373 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1375 if (ei->change_page[j].has_event[k])
1377 if (IS_GROUP_ELEMENT(trigger_element))
1379 struct ElementGroupInfo *group =
1380 element_info[trigger_element].group;
1382 for (l = 0; l < group->num_elements_resolved; l++)
1383 trigger_events[group->element_resolved[l]][k] = TRUE;
1386 trigger_events[trigger_element][k] = TRUE;
1393 /* ---------- initialize push delay -------------------------------------- */
1395 /* initialize push delay values to default */
1396 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1398 if (!IS_CUSTOM_ELEMENT(i))
1400 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1401 element_info[i].push_delay_random = game.default_push_delay_random;
1405 /* set push delay value for certain elements from pre-defined list */
1406 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1408 int e = push_delay_list[i].element;
1410 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1411 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1414 /* set push delay value for Supaplex elements for newer engine versions */
1415 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1417 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1419 if (IS_SP_ELEMENT(i))
1421 /* set SP push delay to just enough to push under a falling zonk */
1422 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1424 element_info[i].push_delay_fixed = delay;
1425 element_info[i].push_delay_random = 0;
1430 /* ---------- initialize move stepsize ----------------------------------- */
1432 /* initialize move stepsize values to default */
1433 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1434 if (!IS_CUSTOM_ELEMENT(i))
1435 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1437 /* set move stepsize value for certain elements from pre-defined list */
1438 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1440 int e = move_stepsize_list[i].element;
1442 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1445 /* ---------- initialize collect score ----------------------------------- */
1447 /* initialize collect score values for custom elements from initial value */
1448 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1449 if (IS_CUSTOM_ELEMENT(i))
1450 element_info[i].collect_score = element_info[i].collect_score_initial;
1452 /* ---------- initialize collect count ----------------------------------- */
1454 /* initialize collect count values for non-custom elements */
1455 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1456 if (!IS_CUSTOM_ELEMENT(i))
1457 element_info[i].collect_count_initial = 0;
1459 /* add collect count values for all elements from pre-defined list */
1460 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1461 element_info[collect_count_list[i].element].collect_count_initial =
1462 collect_count_list[i].count;
1464 /* ---------- initialize access direction -------------------------------- */
1466 /* initialize access direction values to default (access from every side) */
1467 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1468 if (!IS_CUSTOM_ELEMENT(i))
1469 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1471 /* set access direction value for certain elements from pre-defined list */
1472 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1473 element_info[access_direction_list[i].element].access_direction =
1474 access_direction_list[i].direction;
1477 int get_num_special_action(int element, int action_first, int action_last)
1479 int num_special_action = 0;
1482 for (i = action_first; i <= action_last; i++)
1484 boolean found = FALSE;
1486 for (j = 0; j < NUM_DIRECTIONS; j++)
1487 if (el_act_dir2img(element, i, j) !=
1488 el_act_dir2img(element, ACTION_DEFAULT, j))
1492 num_special_action++;
1497 return num_special_action;
1501 =============================================================================
1503 -----------------------------------------------------------------------------
1504 initialize and start new game
1505 =============================================================================
1510 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1511 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1512 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1517 /* don't play tapes over network */
1518 network_playing = (options.network && !tape.playing);
1520 for (i = 0; i < MAX_PLAYERS; i++)
1522 struct PlayerInfo *player = &stored_player[i];
1524 player->index_nr = i;
1525 player->index_bit = (1 << i);
1526 player->element_nr = EL_PLAYER_1 + i;
1528 player->present = FALSE;
1529 player->active = FALSE;
1532 player->effective_action = 0;
1533 player->programmed_action = 0;
1536 player->gems_still_needed = level.gems_needed;
1537 player->sokobanfields_still_needed = 0;
1538 player->lights_still_needed = 0;
1539 player->friends_still_needed = 0;
1541 for (j = 0; j < MAX_NUM_KEYS; j++)
1542 player->key[j] = FALSE;
1544 player->dynabomb_count = 0;
1545 player->dynabomb_size = 1;
1546 player->dynabombs_left = 0;
1547 player->dynabomb_xl = FALSE;
1549 player->MovDir = MV_NONE;
1552 player->GfxDir = MV_NONE;
1553 player->GfxAction = ACTION_DEFAULT;
1555 player->StepFrame = 0;
1557 player->use_murphy = FALSE;
1558 player->artwork_element = player->element_nr;
1560 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1561 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1563 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1565 player->actual_frame_counter = 0;
1567 player->step_counter = 0;
1569 player->last_move_dir = MV_NONE;
1571 player->is_waiting = FALSE;
1572 player->is_moving = FALSE;
1573 player->is_auto_moving = FALSE;
1574 player->is_digging = FALSE;
1575 player->is_snapping = FALSE;
1576 player->is_collecting = FALSE;
1577 player->is_pushing = FALSE;
1578 player->is_switching = FALSE;
1579 player->is_dropping = FALSE;
1581 player->is_bored = FALSE;
1582 player->is_sleeping = FALSE;
1584 player->cannot_move = FALSE;
1586 player->frame_counter_bored = -1;
1587 player->frame_counter_sleeping = -1;
1589 player->anim_delay_counter = 0;
1590 player->post_delay_counter = 0;
1592 player->action_waiting = ACTION_DEFAULT;
1593 player->last_action_waiting = ACTION_DEFAULT;
1594 player->special_action_bored = ACTION_DEFAULT;
1595 player->special_action_sleeping = ACTION_DEFAULT;
1597 /* set number of special actions for bored and sleeping animation */
1598 player->num_special_action_bored =
1599 get_num_special_action(player->artwork_element,
1600 ACTION_BORING_1, ACTION_BORING_LAST);
1601 player->num_special_action_sleeping =
1602 get_num_special_action(player->artwork_element,
1603 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
1605 player->switch_x = -1;
1606 player->switch_y = -1;
1608 player->drop_x = -1;
1609 player->drop_y = -1;
1611 player->show_envelope = 0;
1613 player->move_delay = game.initial_move_delay;
1614 player->move_delay_value = game.initial_move_delay_value;
1616 player->move_delay_value_next = -1;
1618 player->move_delay_reset_counter = 0;
1620 player->push_delay = -1; /* initialized when pushing starts */
1621 player->push_delay_value = game.initial_push_delay_value;
1623 player->drop_delay = 0;
1625 player->last_jx = player->last_jy = 0;
1626 player->jx = player->jy = 0;
1628 player->shield_normal_time_left = 0;
1629 player->shield_deadly_time_left = 0;
1631 player->inventory_infinite_element = EL_UNDEFINED;
1632 player->inventory_size = 0;
1634 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1635 SnapField(player, 0, 0);
1637 player->LevelSolved = FALSE;
1638 player->GameOver = FALSE;
1641 network_player_action_received = FALSE;
1643 #if defined(NETWORK_AVALIABLE)
1644 /* initial null action */
1645 if (network_playing)
1646 SendToServer_MovePlayer(MV_NONE);
1655 TimeLeft = level.time;
1658 ScreenMovDir = MV_NONE;
1662 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1664 AllPlayersGone = FALSE;
1666 game.yamyam_content_nr = 0;
1667 game.magic_wall_active = FALSE;
1668 game.magic_wall_time_left = 0;
1669 game.light_time_left = 0;
1670 game.timegate_time_left = 0;
1671 game.switchgate_pos = 0;
1672 game.wind_direction = level.wind_direction_initial;
1673 game.gravity = level.initial_gravity;
1674 game.explosions_delayed = TRUE;
1676 game.lenses_time_left = 0;
1677 game.magnify_time_left = 0;
1679 game.envelope_active = FALSE;
1681 for (i = 0; i < NUM_BELTS; i++)
1683 game.belt_dir[i] = MV_NONE;
1684 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1687 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1688 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1690 for (x = 0; x < lev_fieldx; x++)
1692 for (y = 0; y < lev_fieldy; y++)
1694 Feld[x][y] = level.field[x][y];
1695 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1696 ChangeDelay[x][y] = 0;
1697 ChangePage[x][y] = -1;
1698 #if USE_NEW_CUSTOM_VALUE
1699 CustomValue[x][y] = 0; /* initialized in InitField() */
1701 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1703 WasJustMoving[x][y] = 0;
1704 WasJustFalling[x][y] = 0;
1705 CheckCollision[x][y] = 0;
1707 Pushed[x][y] = FALSE;
1709 Changed[x][y] = FALSE;
1710 ChangeEvent[x][y] = -1;
1712 ExplodePhase[x][y] = 0;
1713 ExplodeDelay[x][y] = 0;
1714 ExplodeField[x][y] = EX_TYPE_NONE;
1716 RunnerVisit[x][y] = 0;
1717 PlayerVisit[x][y] = 0;
1720 GfxRandom[x][y] = INIT_GFX_RANDOM();
1721 GfxElement[x][y] = EL_UNDEFINED;
1722 GfxAction[x][y] = ACTION_DEFAULT;
1723 GfxDir[x][y] = MV_NONE;
1727 for (y = 0; y < lev_fieldy; y++)
1729 for (x = 0; x < lev_fieldx; x++)
1731 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1733 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1735 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1738 InitField(x, y, TRUE);
1744 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1745 emulate_sb ? EMU_SOKOBAN :
1746 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1748 #if USE_NEW_ALL_SLIPPERY
1749 /* initialize type of slippery elements */
1750 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1752 if (!IS_CUSTOM_ELEMENT(i))
1754 /* default: elements slip down either to the left or right randomly */
1755 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
1757 /* SP style elements prefer to slip down on the left side */
1758 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
1759 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1761 /* BD style elements prefer to slip down on the left side */
1762 if (game.emulation == EMU_BOULDERDASH)
1763 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1768 /* initialize explosion and ignition delay */
1769 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1771 if (!IS_CUSTOM_ELEMENT(i))
1774 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1775 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1776 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1777 int last_phase = (num_phase + 1) * delay;
1778 int half_phase = (num_phase / 2) * delay;
1780 element_info[i].explosion_delay = last_phase - 1;
1781 element_info[i].ignition_delay = half_phase;
1783 if (i == EL_BLACK_ORB)
1784 element_info[i].ignition_delay = 1;
1788 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1789 element_info[i].explosion_delay = 1;
1791 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1792 element_info[i].ignition_delay = 1;
1796 /* correct non-moving belts to start moving left */
1797 for (i = 0; i < NUM_BELTS; i++)
1798 if (game.belt_dir[i] == MV_NONE)
1799 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1801 /* check if any connected player was not found in playfield */
1802 for (i = 0; i < MAX_PLAYERS; i++)
1804 struct PlayerInfo *player = &stored_player[i];
1806 if (player->connected && !player->present)
1808 for (j = 0; j < MAX_PLAYERS; j++)
1810 struct PlayerInfo *some_player = &stored_player[j];
1811 int jx = some_player->jx, jy = some_player->jy;
1813 /* assign first free player found that is present in the playfield */
1814 if (some_player->present && !some_player->connected)
1816 player->present = TRUE;
1817 player->active = TRUE;
1819 some_player->present = FALSE;
1820 some_player->active = FALSE;
1823 player->element_nr = some_player->element_nr;
1826 player->artwork_element = some_player->artwork_element;
1828 player->block_last_field = some_player->block_last_field;
1829 player->block_delay_adjustment = some_player->block_delay_adjustment;
1831 StorePlayer[jx][jy] = player->element_nr;
1832 player->jx = player->last_jx = jx;
1833 player->jy = player->last_jy = jy;
1843 /* when playing a tape, eliminate all players which do not participate */
1845 for (i = 0; i < MAX_PLAYERS; i++)
1847 if (stored_player[i].active && !tape.player_participates[i])
1849 struct PlayerInfo *player = &stored_player[i];
1850 int jx = player->jx, jy = player->jy;
1852 player->active = FALSE;
1853 StorePlayer[jx][jy] = 0;
1854 Feld[jx][jy] = EL_EMPTY;
1858 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1860 /* when in single player mode, eliminate all but the first active player */
1862 for (i = 0; i < MAX_PLAYERS; i++)
1864 if (stored_player[i].active)
1866 for (j = i + 1; j < MAX_PLAYERS; j++)
1868 if (stored_player[j].active)
1870 struct PlayerInfo *player = &stored_player[j];
1871 int jx = player->jx, jy = player->jy;
1873 player->active = FALSE;
1874 player->present = FALSE;
1876 StorePlayer[jx][jy] = 0;
1877 Feld[jx][jy] = EL_EMPTY;
1884 /* when recording the game, store which players take part in the game */
1887 for (i = 0; i < MAX_PLAYERS; i++)
1888 if (stored_player[i].active)
1889 tape.player_participates[i] = TRUE;
1894 for (i = 0; i < MAX_PLAYERS; i++)
1896 struct PlayerInfo *player = &stored_player[i];
1898 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1903 if (local_player == player)
1904 printf("Player %d is local player.\n", i+1);
1908 if (BorderElement == EL_EMPTY)
1911 SBX_Right = lev_fieldx - SCR_FIELDX;
1913 SBY_Lower = lev_fieldy - SCR_FIELDY;
1918 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1920 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1923 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1924 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1926 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1927 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1929 /* if local player not found, look for custom element that might create
1930 the player (make some assumptions about the right custom element) */
1931 if (!local_player->present)
1933 int start_x = 0, start_y = 0;
1934 int found_rating = 0;
1935 int found_element = EL_UNDEFINED;
1936 int player_nr = local_player->index_nr;
1938 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1940 int element = Feld[x][y];
1945 if (level.use_start_element[player_nr] &&
1946 level.start_element[player_nr] == element &&
1953 found_element = element;
1956 if (!IS_CUSTOM_ELEMENT(element))
1959 if (CAN_CHANGE(element))
1961 for (i = 0; i < element_info[element].num_change_pages; i++)
1963 /* check for player created from custom element as single target */
1964 content = element_info[element].change_page[i].target_element;
1965 is_player = ELEM_IS_PLAYER(content);
1967 if (is_player && (found_rating < 3 || element < found_element))
1973 found_element = element;
1978 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1980 /* check for player created from custom element as explosion content */
1981 content = element_info[element].content.e[xx][yy];
1982 is_player = ELEM_IS_PLAYER(content);
1984 if (is_player && (found_rating < 2 || element < found_element))
1986 start_x = x + xx - 1;
1987 start_y = y + yy - 1;
1990 found_element = element;
1993 if (!CAN_CHANGE(element))
1996 for (i = 0; i < element_info[element].num_change_pages; i++)
1998 /* check for player created from custom element as extended target */
2000 element_info[element].change_page[i].target_content.e[xx][yy];
2002 is_player = ELEM_IS_PLAYER(content);
2004 if (is_player && (found_rating < 1 || element < found_element))
2006 start_x = x + xx - 1;
2007 start_y = y + yy - 1;
2010 found_element = element;
2016 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2017 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2020 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2021 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2026 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2027 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2028 local_player->jx - MIDPOSX);
2030 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2031 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2032 local_player->jy - MIDPOSY);
2035 if (!game.restart_level)
2036 CloseDoor(DOOR_CLOSE_1);
2038 /* !!! FIX THIS (START) !!! */
2039 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2041 InitGameEngine_EM();
2048 /* after drawing the level, correct some elements */
2049 if (game.timegate_time_left == 0)
2050 CloseAllOpenTimegates();
2052 if (setup.soft_scrolling)
2053 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2055 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2058 /* !!! FIX THIS (END) !!! */
2060 if (!game.restart_level)
2062 /* copy default game door content to main double buffer */
2063 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2064 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2067 DrawGameDoorValues();
2069 if (!game.restart_level)
2073 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2074 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2075 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2079 /* copy actual game door content to door double buffer for OpenDoor() */
2080 BlitBitmap(drawto, bitmap_db_door,
2081 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2083 OpenDoor(DOOR_OPEN_ALL);
2085 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2087 if (setup.sound_music)
2090 KeyboardAutoRepeatOffUnlessAutoplay();
2094 for (i = 0; i < MAX_PLAYERS; i++)
2095 printf("Player %d %sactive.\n",
2096 i + 1, (stored_player[i].active ? "" : "not "));
2100 game.restart_level = FALSE;
2103 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2105 /* this is used for non-R'n'D game engines to update certain engine values */
2107 /* needed to determine if sounds are played within the visible screen area */
2108 scroll_x = actual_scroll_x;
2109 scroll_y = actual_scroll_y;
2112 void InitMovDir(int x, int y)
2114 int i, element = Feld[x][y];
2115 static int xy[4][2] =
2122 static int direction[3][4] =
2124 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2125 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2126 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2135 Feld[x][y] = EL_BUG;
2136 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2139 case EL_SPACESHIP_RIGHT:
2140 case EL_SPACESHIP_UP:
2141 case EL_SPACESHIP_LEFT:
2142 case EL_SPACESHIP_DOWN:
2143 Feld[x][y] = EL_SPACESHIP;
2144 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2147 case EL_BD_BUTTERFLY_RIGHT:
2148 case EL_BD_BUTTERFLY_UP:
2149 case EL_BD_BUTTERFLY_LEFT:
2150 case EL_BD_BUTTERFLY_DOWN:
2151 Feld[x][y] = EL_BD_BUTTERFLY;
2152 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2155 case EL_BD_FIREFLY_RIGHT:
2156 case EL_BD_FIREFLY_UP:
2157 case EL_BD_FIREFLY_LEFT:
2158 case EL_BD_FIREFLY_DOWN:
2159 Feld[x][y] = EL_BD_FIREFLY;
2160 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2163 case EL_PACMAN_RIGHT:
2165 case EL_PACMAN_LEFT:
2166 case EL_PACMAN_DOWN:
2167 Feld[x][y] = EL_PACMAN;
2168 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2171 case EL_SP_SNIKSNAK:
2172 MovDir[x][y] = MV_UP;
2175 case EL_SP_ELECTRON:
2176 MovDir[x][y] = MV_LEFT;
2183 Feld[x][y] = EL_MOLE;
2184 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2188 if (IS_CUSTOM_ELEMENT(element))
2190 struct ElementInfo *ei = &element_info[element];
2191 int move_direction_initial = ei->move_direction_initial;
2192 int move_pattern = ei->move_pattern;
2194 if (move_direction_initial == MV_START_PREVIOUS)
2196 if (MovDir[x][y] != MV_NONE)
2199 move_direction_initial = MV_START_AUTOMATIC;
2202 if (move_direction_initial == MV_START_RANDOM)
2203 MovDir[x][y] = 1 << RND(4);
2204 else if (move_direction_initial & MV_ANY_DIRECTION)
2205 MovDir[x][y] = move_direction_initial;
2206 else if (move_pattern == MV_ALL_DIRECTIONS ||
2207 move_pattern == MV_TURNING_LEFT ||
2208 move_pattern == MV_TURNING_RIGHT ||
2209 move_pattern == MV_TURNING_LEFT_RIGHT ||
2210 move_pattern == MV_TURNING_RIGHT_LEFT ||
2211 move_pattern == MV_TURNING_RANDOM)
2212 MovDir[x][y] = 1 << RND(4);
2213 else if (move_pattern == MV_HORIZONTAL)
2214 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2215 else if (move_pattern == MV_VERTICAL)
2216 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2217 else if (move_pattern & MV_ANY_DIRECTION)
2218 MovDir[x][y] = element_info[element].move_pattern;
2219 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2220 move_pattern == MV_ALONG_RIGHT_SIDE)
2222 /* use random direction as default start direction */
2223 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2224 MovDir[x][y] = 1 << RND(4);
2226 for (i = 0; i < NUM_DIRECTIONS; i++)
2228 int x1 = x + xy[i][0];
2229 int y1 = y + xy[i][1];
2231 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2233 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2234 MovDir[x][y] = direction[0][i];
2236 MovDir[x][y] = direction[1][i];
2245 MovDir[x][y] = 1 << RND(4);
2247 if (element != EL_BUG &&
2248 element != EL_SPACESHIP &&
2249 element != EL_BD_BUTTERFLY &&
2250 element != EL_BD_FIREFLY)
2253 for (i = 0; i < NUM_DIRECTIONS; i++)
2255 int x1 = x + xy[i][0];
2256 int y1 = y + xy[i][1];
2258 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2260 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2262 MovDir[x][y] = direction[0][i];
2265 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2266 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2268 MovDir[x][y] = direction[1][i];
2277 GfxDir[x][y] = MovDir[x][y];
2280 void InitAmoebaNr(int x, int y)
2283 int group_nr = AmoebeNachbarNr(x, y);
2287 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2289 if (AmoebaCnt[i] == 0)
2297 AmoebaNr[x][y] = group_nr;
2298 AmoebaCnt[group_nr]++;
2299 AmoebaCnt2[group_nr]++;
2305 boolean raise_level = FALSE;
2307 if (local_player->MovPos)
2310 if (tape.auto_play) /* tape might already be stopped here */
2311 tape.auto_play_level_solved = TRUE;
2313 local_player->LevelSolved = FALSE;
2315 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2319 if (!tape.playing && setup.sound_loops)
2320 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2321 SND_CTRL_PLAY_LOOP);
2323 while (TimeLeft > 0)
2325 if (!tape.playing && !setup.sound_loops)
2326 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2328 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2331 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2336 RaiseScore(level.score[SC_TIME_BONUS]);
2339 DrawGameValue_Time(TimeLeft);
2347 if (!tape.playing && setup.sound_loops)
2348 StopSound(SND_GAME_LEVELTIME_BONUS);
2350 else if (level.time == 0) /* level without time limit */
2352 if (!tape.playing && setup.sound_loops)
2353 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2354 SND_CTRL_PLAY_LOOP);
2356 while (TimePlayed < 999)
2358 if (!tape.playing && !setup.sound_loops)
2359 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2361 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2364 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2369 RaiseScore(level.score[SC_TIME_BONUS]);
2372 DrawGameValue_Time(TimePlayed);
2380 if (!tape.playing && setup.sound_loops)
2381 StopSound(SND_GAME_LEVELTIME_BONUS);
2384 /* close exit door after last player */
2385 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2386 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2387 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2389 int element = Feld[ExitX][ExitY];
2391 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2392 EL_SP_EXIT_CLOSING);
2394 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2397 /* player disappears */
2398 if (ExitX >= 0 && ExitY >= 0)
2399 DrawLevelField(ExitX, ExitY);
2406 CloseDoor(DOOR_CLOSE_1);
2411 SaveTape(tape.level_nr); /* Ask to save tape */
2414 if (level_nr == leveldir_current->handicap_level)
2416 leveldir_current->handicap_level++;
2417 SaveLevelSetup_SeriesInfo();
2420 if (level_editor_test_game)
2421 local_player->score = -1; /* no highscore when playing from editor */
2422 else if (level_nr < leveldir_current->last_level)
2423 raise_level = TRUE; /* advance to next level */
2425 if ((hi_pos = NewHiScore()) >= 0)
2427 game_status = GAME_MODE_SCORES;
2428 DrawHallOfFame(hi_pos);
2437 game_status = GAME_MODE_MAIN;
2454 LoadScore(level_nr);
2456 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2457 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2460 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2462 if (local_player->score > highscore[k].Score)
2464 /* player has made it to the hall of fame */
2466 if (k < MAX_SCORE_ENTRIES - 1)
2468 int m = MAX_SCORE_ENTRIES - 1;
2471 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2472 if (!strcmp(setup.player_name, highscore[l].Name))
2474 if (m == k) /* player's new highscore overwrites his old one */
2478 for (l = m; l > k; l--)
2480 strcpy(highscore[l].Name, highscore[l - 1].Name);
2481 highscore[l].Score = highscore[l - 1].Score;
2488 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2489 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2490 highscore[k].Score = local_player->score;
2496 else if (!strncmp(setup.player_name, highscore[k].Name,
2497 MAX_PLAYER_NAME_LEN))
2498 break; /* player already there with a higher score */
2504 SaveScore(level_nr);
2509 inline static int getElementMoveStepsize(int x, int y)
2511 int element = Feld[x][y];
2512 int direction = MovDir[x][y];
2513 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2514 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2515 int horiz_move = (dx != 0);
2516 int sign = (horiz_move ? dx : dy);
2517 int step = sign * element_info[element].move_stepsize;
2519 /* special values for move stepsize for spring and things on conveyor belt */
2523 if (element == EL_SPRING)
2524 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2525 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2526 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2527 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2529 if (CAN_FALL(element) &&
2530 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2531 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2532 else if (element == EL_SPRING)
2533 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2540 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2542 if (player->GfxAction != action || player->GfxDir != dir)
2545 printf("Player frame reset! (%d => %d, %d => %d)\n",
2546 player->GfxAction, action, player->GfxDir, dir);
2549 player->GfxAction = action;
2550 player->GfxDir = dir;
2552 player->StepFrame = 0;
2556 static void ResetRandomAnimationValue(int x, int y)
2558 GfxRandom[x][y] = INIT_GFX_RANDOM();
2561 static void ResetGfxAnimation(int x, int y)
2564 GfxAction[x][y] = ACTION_DEFAULT;
2565 GfxDir[x][y] = MovDir[x][y];
2568 void InitMovingField(int x, int y, int direction)
2570 int element = Feld[x][y];
2571 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2572 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2576 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2577 ResetGfxAnimation(x, y);
2579 MovDir[x][y] = direction;
2580 GfxDir[x][y] = direction;
2581 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2582 ACTION_FALLING : ACTION_MOVING);
2584 /* this is needed for CEs with property "can move" / "not moving" */
2586 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2588 if (Feld[newx][newy] == EL_EMPTY)
2589 Feld[newx][newy] = EL_BLOCKED;
2591 MovDir[newx][newy] = MovDir[x][y];
2593 #if USE_NEW_CUSTOM_VALUE
2594 CustomValue[newx][newy] = CustomValue[x][y];
2597 GfxFrame[newx][newy] = GfxFrame[x][y];
2598 GfxRandom[newx][newy] = GfxRandom[x][y];
2599 GfxAction[newx][newy] = GfxAction[x][y];
2600 GfxDir[newx][newy] = GfxDir[x][y];
2604 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2606 int direction = MovDir[x][y];
2607 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2608 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2614 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2616 int oldx = x, oldy = y;
2617 int direction = MovDir[x][y];
2619 if (direction == MV_LEFT)
2621 else if (direction == MV_RIGHT)
2623 else if (direction == MV_UP)
2625 else if (direction == MV_DOWN)
2628 *comes_from_x = oldx;
2629 *comes_from_y = oldy;
2632 int MovingOrBlocked2Element(int x, int y)
2634 int element = Feld[x][y];
2636 if (element == EL_BLOCKED)
2640 Blocked2Moving(x, y, &oldx, &oldy);
2641 return Feld[oldx][oldy];
2647 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2649 /* like MovingOrBlocked2Element(), but if element is moving
2650 and (x,y) is the field the moving element is just leaving,
2651 return EL_BLOCKED instead of the element value */
2652 int element = Feld[x][y];
2654 if (IS_MOVING(x, y))
2656 if (element == EL_BLOCKED)
2660 Blocked2Moving(x, y, &oldx, &oldy);
2661 return Feld[oldx][oldy];
2670 static void RemoveField(int x, int y)
2672 Feld[x][y] = EL_EMPTY;
2678 #if USE_NEW_CUSTOM_VALUE
2679 CustomValue[x][y] = 0;
2683 ChangeDelay[x][y] = 0;
2684 ChangePage[x][y] = -1;
2685 Pushed[x][y] = FALSE;
2688 ExplodeField[x][y] = EX_TYPE_NONE;
2691 GfxElement[x][y] = EL_UNDEFINED;
2692 GfxAction[x][y] = ACTION_DEFAULT;
2693 GfxDir[x][y] = MV_NONE;
2696 void RemoveMovingField(int x, int y)
2698 int oldx = x, oldy = y, newx = x, newy = y;
2699 int element = Feld[x][y];
2700 int next_element = EL_UNDEFINED;
2702 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2705 if (IS_MOVING(x, y))
2707 Moving2Blocked(x, y, &newx, &newy);
2709 if (Feld[newx][newy] != EL_BLOCKED)
2711 /* element is moving, but target field is not free (blocked), but
2712 already occupied by something different (example: acid pool);
2713 in this case, only remove the moving field, but not the target */
2715 RemoveField(oldx, oldy);
2717 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2719 DrawLevelField(oldx, oldy);
2724 else if (element == EL_BLOCKED)
2726 Blocked2Moving(x, y, &oldx, &oldy);
2727 if (!IS_MOVING(oldx, oldy))
2731 if (element == EL_BLOCKED &&
2732 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2733 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2734 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2735 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2736 next_element = get_next_element(Feld[oldx][oldy]);
2738 RemoveField(oldx, oldy);
2739 RemoveField(newx, newy);
2741 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2743 if (next_element != EL_UNDEFINED)
2744 Feld[oldx][oldy] = next_element;
2746 DrawLevelField(oldx, oldy);
2747 DrawLevelField(newx, newy);
2750 void DrawDynamite(int x, int y)
2752 int sx = SCREENX(x), sy = SCREENY(y);
2753 int graphic = el2img(Feld[x][y]);
2756 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2759 if (IS_WALKABLE_INSIDE(Back[x][y]))
2763 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2764 else if (Store[x][y])
2765 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2767 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2769 if (Back[x][y] || Store[x][y])
2770 DrawGraphicThruMask(sx, sy, graphic, frame);
2772 DrawGraphic(sx, sy, graphic, frame);
2775 void CheckDynamite(int x, int y)
2777 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2781 if (MovDelay[x][y] != 0)
2784 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2790 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2795 void DrawRelocatePlayer(struct PlayerInfo *player)
2797 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2798 boolean no_delay = (tape.warp_forward);
2799 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2800 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2801 int jx = player->jx;
2802 int jy = player->jy;
2804 if (level.instant_relocation)
2806 int offset = (setup.scroll_delay ? 3 : 0);
2808 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2810 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2811 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2812 local_player->jx - MIDPOSX);
2814 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2815 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2816 local_player->jy - MIDPOSY);
2820 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2821 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2822 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2824 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2825 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2826 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2828 /* don't scroll over playfield boundaries */
2829 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2830 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2832 /* don't scroll over playfield boundaries */
2833 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2834 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2837 RedrawPlayfield(TRUE, 0,0,0,0);
2841 int scroll_xx = -999, scroll_yy = -999;
2843 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2845 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2848 int fx = FX, fy = FY;
2850 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2851 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2852 local_player->jx - MIDPOSX);
2854 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2855 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2856 local_player->jy - MIDPOSY);
2858 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2859 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2861 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2867 fx += dx * TILEX / 2;
2868 fy += dy * TILEY / 2;
2870 ScrollLevel(dx, dy);
2873 /* scroll in two steps of half tile size to make things smoother */
2874 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2876 Delay(wait_delay_value);
2878 /* scroll second step to align at full tile size */
2880 Delay(wait_delay_value);
2885 Delay(wait_delay_value);
2889 void RelocatePlayer(int jx, int jy, int el_player_raw)
2891 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
2892 int player_nr = GET_PLAYER_NR(el_player);
2893 struct PlayerInfo *player = &stored_player[player_nr];
2894 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2895 boolean no_delay = (tape.warp_forward);
2896 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2897 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2898 int old_jx = player->jx;
2899 int old_jy = player->jy;
2900 int old_element = Feld[old_jx][old_jy];
2901 int element = Feld[jx][jy];
2902 boolean player_relocated = (old_jx != jx || old_jy != jy);
2904 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2905 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2906 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2907 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2908 int leave_side_horiz = move_dir_horiz;
2909 int leave_side_vert = move_dir_vert;
2910 int enter_side = enter_side_horiz | enter_side_vert;
2911 int leave_side = leave_side_horiz | leave_side_vert;
2913 if (player->GameOver) /* do not reanimate dead player */
2916 if (!player_relocated) /* no need to relocate the player */
2919 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2921 RemoveField(jx, jy); /* temporarily remove newly placed player */
2922 DrawLevelField(jx, jy);
2925 if (player->present)
2927 while (player->MovPos)
2929 ScrollPlayer(player, SCROLL_GO_ON);
2930 ScrollScreen(NULL, SCROLL_GO_ON);
2932 AdvanceFrameAndPlayerCounters(player->index_nr);
2937 Delay(wait_delay_value);
2940 DrawPlayer(player); /* needed here only to cleanup last field */
2941 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2943 player->is_moving = FALSE;
2946 if (IS_CUSTOM_ELEMENT(old_element))
2947 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
2949 player->index_bit, leave_side);
2951 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
2953 player->index_bit, leave_side);
2955 Feld[jx][jy] = el_player;
2956 InitPlayerField(jx, jy, el_player, TRUE);
2958 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
2960 Feld[jx][jy] = element;
2961 InitField(jx, jy, FALSE);
2964 if (player == local_player) /* only visually relocate local player */
2965 DrawRelocatePlayer(player);
2967 TestIfPlayerTouchesBadThing(jx, jy);
2968 TestIfPlayerTouchesCustomElement(jx, jy);
2970 if (IS_CUSTOM_ELEMENT(element))
2971 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
2972 player->index_bit, enter_side);
2974 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
2975 player->index_bit, enter_side);
2978 void Explode(int ex, int ey, int phase, int mode)
2984 /* !!! eliminate this variable !!! */
2985 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2987 if (game.explosions_delayed)
2989 ExplodeField[ex][ey] = mode;
2993 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2995 int center_element = Feld[ex][ey];
2998 /* --- This is only really needed (and now handled) in "Impact()". --- */
2999 /* do not explode moving elements that left the explode field in time */
3000 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3001 center_element == EL_EMPTY &&
3002 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3006 if (mode == EX_TYPE_NORMAL ||
3007 mode == EX_TYPE_CENTER ||
3008 mode == EX_TYPE_CROSS)
3009 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3011 /* remove things displayed in background while burning dynamite */
3012 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3015 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3017 /* put moving element to center field (and let it explode there) */
3018 center_element = MovingOrBlocked2Element(ex, ey);
3019 RemoveMovingField(ex, ey);
3020 Feld[ex][ey] = center_element;
3023 last_phase = element_info[center_element].explosion_delay + 1;
3025 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3027 int xx = x - ex + 1;
3028 int yy = y - ey + 1;
3031 if (!IN_LEV_FIELD(x, y) ||
3032 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3033 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3036 element = Feld[x][y];
3038 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3040 element = MovingOrBlocked2Element(x, y);
3042 if (!IS_EXPLOSION_PROOF(element))
3043 RemoveMovingField(x, y);
3046 /* indestructible elements can only explode in center (but not flames) */
3047 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3048 mode == EX_TYPE_BORDER)) ||
3049 element == EL_FLAMES)
3052 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3053 behaviour, for example when touching a yamyam that explodes to rocks
3054 with active deadly shield, a rock is created under the player !!! */
3055 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3057 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3058 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3059 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3061 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3064 if (IS_ACTIVE_BOMB(element))
3066 /* re-activate things under the bomb like gate or penguin */
3067 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3074 /* save walkable background elements while explosion on same tile */
3075 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3076 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3077 Back[x][y] = element;
3079 /* ignite explodable elements reached by other explosion */
3080 if (element == EL_EXPLOSION)
3081 element = Store2[x][y];
3083 if (AmoebaNr[x][y] &&
3084 (element == EL_AMOEBA_FULL ||
3085 element == EL_BD_AMOEBA ||
3086 element == EL_AMOEBA_GROWING))
3088 AmoebaCnt[AmoebaNr[x][y]]--;
3089 AmoebaCnt2[AmoebaNr[x][y]]--;
3094 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3096 switch(StorePlayer[ex][ey])
3099 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3102 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3105 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3109 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3113 if (PLAYERINFO(ex, ey)->use_murphy)
3114 Store[x][y] = EL_EMPTY;
3116 else if (center_element == EL_MOLE)
3117 Store[x][y] = EL_EMERALD_RED;
3118 else if (center_element == EL_PENGUIN)
3119 Store[x][y] = EL_EMERALD_PURPLE;
3120 else if (center_element == EL_BUG)
3121 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3122 else if (center_element == EL_BD_BUTTERFLY)
3123 Store[x][y] = EL_BD_DIAMOND;
3124 else if (center_element == EL_SP_ELECTRON)
3125 Store[x][y] = EL_SP_INFOTRON;
3126 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3127 Store[x][y] = level.amoeba_content;
3128 else if (center_element == EL_YAMYAM)
3129 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3130 else if (IS_CUSTOM_ELEMENT(center_element) &&
3131 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3132 Store[x][y] = element_info[center_element].content.e[xx][yy];
3133 else if (element == EL_WALL_EMERALD)
3134 Store[x][y] = EL_EMERALD;
3135 else if (element == EL_WALL_DIAMOND)
3136 Store[x][y] = EL_DIAMOND;
3137 else if (element == EL_WALL_BD_DIAMOND)
3138 Store[x][y] = EL_BD_DIAMOND;
3139 else if (element == EL_WALL_EMERALD_YELLOW)
3140 Store[x][y] = EL_EMERALD_YELLOW;
3141 else if (element == EL_WALL_EMERALD_RED)
3142 Store[x][y] = EL_EMERALD_RED;
3143 else if (element == EL_WALL_EMERALD_PURPLE)
3144 Store[x][y] = EL_EMERALD_PURPLE;
3145 else if (element == EL_WALL_PEARL)
3146 Store[x][y] = EL_PEARL;
3147 else if (element == EL_WALL_CRYSTAL)
3148 Store[x][y] = EL_CRYSTAL;
3149 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3150 Store[x][y] = element_info[element].content.e[1][1];
3152 Store[x][y] = EL_EMPTY;
3154 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3155 center_element == EL_AMOEBA_TO_DIAMOND)
3156 Store2[x][y] = element;
3158 Feld[x][y] = EL_EXPLOSION;
3159 GfxElement[x][y] = center_element;
3161 ExplodePhase[x][y] = 1;
3162 ExplodeDelay[x][y] = last_phase;
3167 if (center_element == EL_YAMYAM)
3168 game.yamyam_content_nr =
3169 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3181 GfxFrame[x][y] = 0; /* restart explosion animation */
3183 last_phase = ExplodeDelay[x][y];
3185 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3189 /* activate this even in non-DEBUG version until cause for crash in
3190 getGraphicAnimationFrame() (see below) is found and eliminated */
3195 if (GfxElement[x][y] == EL_UNDEFINED)
3198 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3199 printf("Explode(): This should never happen!\n");
3202 GfxElement[x][y] = EL_EMPTY;
3206 border_element = Store2[x][y];
3207 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3208 border_element = StorePlayer[x][y];
3210 if (phase == element_info[border_element].ignition_delay ||
3211 phase == last_phase)
3213 boolean border_explosion = FALSE;
3215 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3216 !PLAYER_EXPLOSION_PROTECTED(x, y))
3218 KillPlayerUnlessExplosionProtected(x, y);
3219 border_explosion = TRUE;
3221 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3223 Feld[x][y] = Store2[x][y];
3226 border_explosion = TRUE;
3228 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3230 AmoebeUmwandeln(x, y);
3232 border_explosion = TRUE;
3235 /* if an element just explodes due to another explosion (chain-reaction),
3236 do not immediately end the new explosion when it was the last frame of
3237 the explosion (as it would be done in the following "if"-statement!) */
3238 if (border_explosion && phase == last_phase)
3242 if (phase == last_phase)
3246 element = Feld[x][y] = Store[x][y];
3247 Store[x][y] = Store2[x][y] = 0;
3248 GfxElement[x][y] = EL_UNDEFINED;
3250 /* player can escape from explosions and might therefore be still alive */
3251 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3252 element <= EL_PLAYER_IS_EXPLODING_4)
3253 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3255 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3256 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3257 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3260 /* restore probably existing indestructible background element */
3261 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3262 element = Feld[x][y] = Back[x][y];
3265 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3266 GfxDir[x][y] = MV_NONE;
3267 ChangeDelay[x][y] = 0;
3268 ChangePage[x][y] = -1;
3270 #if USE_NEW_CUSTOM_VALUE
3271 CustomValue[x][y] = 0;
3274 InitField_WithBug2(x, y, FALSE);
3276 DrawLevelField(x, y);
3278 TestIfElementTouchesCustomElement(x, y);
3280 if (GFX_CRUMBLED(element))
3281 DrawLevelFieldCrumbledSandNeighbours(x, y);
3283 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3284 StorePlayer[x][y] = 0;
3286 if (ELEM_IS_PLAYER(element))
3287 RelocatePlayer(x, y, element);
3289 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3291 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3292 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3295 DrawLevelFieldCrumbledSand(x, y);
3297 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3299 DrawLevelElement(x, y, Back[x][y]);
3300 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3302 else if (IS_WALKABLE_UNDER(Back[x][y]))
3304 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3305 DrawLevelElementThruMask(x, y, Back[x][y]);
3307 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3308 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3312 void DynaExplode(int ex, int ey)
3315 int dynabomb_element = Feld[ex][ey];
3316 int dynabomb_size = 1;
3317 boolean dynabomb_xl = FALSE;
3318 struct PlayerInfo *player;
3319 static int xy[4][2] =
3327 if (IS_ACTIVE_BOMB(dynabomb_element))
3329 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3330 dynabomb_size = player->dynabomb_size;
3331 dynabomb_xl = player->dynabomb_xl;
3332 player->dynabombs_left++;
3335 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3337 for (i = 0; i < NUM_DIRECTIONS; i++)
3339 for (j = 1; j <= dynabomb_size; j++)
3341 int x = ex + j * xy[i][0];
3342 int y = ey + j * xy[i][1];
3345 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3348 element = Feld[x][y];
3350 /* do not restart explosions of fields with active bombs */
3351 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3354 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3356 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3357 !IS_DIGGABLE(element) && !dynabomb_xl)
3363 void Bang(int x, int y)
3365 int element = MovingOrBlocked2Element(x, y);
3367 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3369 struct PlayerInfo *player = PLAYERINFO(x, y);
3371 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3372 player->element_nr);
3379 case EL_BD_BUTTERFLY:
3382 case EL_DARK_YAMYAM:
3386 RaiseScoreElement(element);
3387 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3389 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3390 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3391 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3392 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3393 case EL_DYNABOMB_INCREASE_NUMBER:
3394 case EL_DYNABOMB_INCREASE_SIZE:
3395 case EL_DYNABOMB_INCREASE_POWER:
3400 case EL_LAMP_ACTIVE:
3401 case EL_AMOEBA_TO_DIAMOND:
3402 if (IS_PLAYER(x, y))
3403 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3405 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3408 if (element_info[element].explosion_type == EXPLODES_CROSS)
3409 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3410 else if (element_info[element].explosion_type == EXPLODES_1X1)
3411 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3413 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3417 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3420 void SplashAcid(int x, int y)
3422 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3423 (!IN_LEV_FIELD(x - 1, y - 2) ||
3424 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3425 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3427 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3428 (!IN_LEV_FIELD(x + 1, y - 2) ||
3429 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3430 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3432 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3435 static void InitBeltMovement()
3437 static int belt_base_element[4] =
3439 EL_CONVEYOR_BELT_1_LEFT,
3440 EL_CONVEYOR_BELT_2_LEFT,
3441 EL_CONVEYOR_BELT_3_LEFT,
3442 EL_CONVEYOR_BELT_4_LEFT
3444 static int belt_base_active_element[4] =
3446 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3447 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3448 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3449 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3454 /* set frame order for belt animation graphic according to belt direction */
3455 for (i = 0; i < NUM_BELTS; i++)
3459 for (j = 0; j < NUM_BELT_PARTS; j++)
3461 int element = belt_base_active_element[belt_nr] + j;
3462 int graphic = el2img(element);
3464 if (game.belt_dir[i] == MV_LEFT)
3465 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3467 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3471 for (y = 0; y < lev_fieldy; y++)
3473 for (x = 0; x < lev_fieldx; x++)
3475 int element = Feld[x][y];
3477 for (i = 0; i < NUM_BELTS; i++)
3479 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3481 int e_belt_nr = getBeltNrFromBeltElement(element);
3484 if (e_belt_nr == belt_nr)
3486 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3488 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3496 static void ToggleBeltSwitch(int x, int y)
3498 static int belt_base_element[4] =
3500 EL_CONVEYOR_BELT_1_LEFT,
3501 EL_CONVEYOR_BELT_2_LEFT,
3502 EL_CONVEYOR_BELT_3_LEFT,
3503 EL_CONVEYOR_BELT_4_LEFT
3505 static int belt_base_active_element[4] =
3507 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3508 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3509 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3510 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3512 static int belt_base_switch_element[4] =
3514 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3515 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3516 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3517 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3519 static int belt_move_dir[4] =
3527 int element = Feld[x][y];
3528 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3529 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3530 int belt_dir = belt_move_dir[belt_dir_nr];
3533 if (!IS_BELT_SWITCH(element))
3536 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3537 game.belt_dir[belt_nr] = belt_dir;
3539 if (belt_dir_nr == 3)
3542 /* set frame order for belt animation graphic according to belt direction */
3543 for (i = 0; i < NUM_BELT_PARTS; i++)
3545 int element = belt_base_active_element[belt_nr] + i;
3546 int graphic = el2img(element);
3548 if (belt_dir == MV_LEFT)
3549 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3551 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3554 for (yy = 0; yy < lev_fieldy; yy++)
3556 for (xx = 0; xx < lev_fieldx; xx++)
3558 int element = Feld[xx][yy];
3560 if (IS_BELT_SWITCH(element))
3562 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3564 if (e_belt_nr == belt_nr)
3566 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3567 DrawLevelField(xx, yy);
3570 else if (IS_BELT(element) && belt_dir != MV_NONE)
3572 int e_belt_nr = getBeltNrFromBeltElement(element);
3574 if (e_belt_nr == belt_nr)
3576 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3578 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3579 DrawLevelField(xx, yy);
3582 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3584 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3586 if (e_belt_nr == belt_nr)
3588 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3590 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3591 DrawLevelField(xx, yy);
3598 static void ToggleSwitchgateSwitch(int x, int y)
3602 game.switchgate_pos = !game.switchgate_pos;
3604 for (yy = 0; yy < lev_fieldy; yy++)
3606 for (xx = 0; xx < lev_fieldx; xx++)
3608 int element = Feld[xx][yy];
3610 if (element == EL_SWITCHGATE_SWITCH_UP ||
3611 element == EL_SWITCHGATE_SWITCH_DOWN)
3613 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3614 DrawLevelField(xx, yy);
3616 else if (element == EL_SWITCHGATE_OPEN ||
3617 element == EL_SWITCHGATE_OPENING)
3619 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3621 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3623 else if (element == EL_SWITCHGATE_CLOSED ||
3624 element == EL_SWITCHGATE_CLOSING)
3626 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3628 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3634 static int getInvisibleActiveFromInvisibleElement(int element)
3636 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3637 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3638 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3642 static int getInvisibleFromInvisibleActiveElement(int element)
3644 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3645 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3646 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3650 static void RedrawAllLightSwitchesAndInvisibleElements()
3654 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3656 int element = Feld[x][y];
3658 if (element == EL_LIGHT_SWITCH &&
3659 game.light_time_left > 0)
3661 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3662 DrawLevelField(x, y);
3664 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3665 game.light_time_left == 0)
3667 Feld[x][y] = EL_LIGHT_SWITCH;
3668 DrawLevelField(x, y);
3670 else if (element == EL_EMC_DRIPPER &&
3671 game.light_time_left > 0)
3673 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
3674 DrawLevelField(x, y);
3676 else if (element == EL_EMC_DRIPPER_ACTIVE &&
3677 game.light_time_left == 0)
3679 Feld[x][y] = EL_EMC_DRIPPER;
3680 DrawLevelField(x, y);
3682 else if (element == EL_INVISIBLE_STEELWALL ||
3683 element == EL_INVISIBLE_WALL ||
3684 element == EL_INVISIBLE_SAND)
3686 if (game.light_time_left > 0)
3687 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3689 DrawLevelField(x, y);
3691 /* uncrumble neighbour fields, if needed */
3692 if (element == EL_INVISIBLE_SAND)
3693 DrawLevelFieldCrumbledSandNeighbours(x, y);
3695 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3696 element == EL_INVISIBLE_WALL_ACTIVE ||
3697 element == EL_INVISIBLE_SAND_ACTIVE)
3699 if (game.light_time_left == 0)
3700 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3702 DrawLevelField(x, y);
3704 /* re-crumble neighbour fields, if needed */
3705 if (element == EL_INVISIBLE_SAND)
3706 DrawLevelFieldCrumbledSandNeighbours(x, y);
3711 static void RedrawAllInvisibleElementsForLenses()
3715 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3717 int element = Feld[x][y];
3719 if (element == EL_EMC_DRIPPER &&
3720 game.lenses_time_left > 0)
3722 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
3723 DrawLevelField(x, y);
3725 else if (element == EL_EMC_DRIPPER_ACTIVE &&
3726 game.lenses_time_left == 0)
3728 Feld[x][y] = EL_EMC_DRIPPER;
3729 DrawLevelField(x, y);
3731 else if (element == EL_INVISIBLE_STEELWALL ||
3732 element == EL_INVISIBLE_WALL ||
3733 element == EL_INVISIBLE_SAND)
3735 if (game.lenses_time_left > 0)
3736 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3738 DrawLevelField(x, y);
3740 /* uncrumble neighbour fields, if needed */
3741 if (element == EL_INVISIBLE_SAND)
3742 DrawLevelFieldCrumbledSandNeighbours(x, y);
3744 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3745 element == EL_INVISIBLE_WALL_ACTIVE ||
3746 element == EL_INVISIBLE_SAND_ACTIVE)
3748 if (game.lenses_time_left == 0)
3749 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3751 DrawLevelField(x, y);
3753 /* re-crumble neighbour fields, if needed */
3754 if (element == EL_INVISIBLE_SAND)
3755 DrawLevelFieldCrumbledSandNeighbours(x, y);
3760 static void RedrawAllInvisibleElementsForMagnifier()
3764 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3766 int element = Feld[x][y];
3768 if (element == EL_EMC_FAKE_GRASS &&
3769 game.magnify_time_left > 0)
3771 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
3772 DrawLevelField(x, y);
3774 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
3775 game.magnify_time_left == 0)
3777 Feld[x][y] = EL_EMC_FAKE_GRASS;
3778 DrawLevelField(x, y);
3780 else if (IS_GATE_GRAY(element) &&
3781 game.magnify_time_left > 0)
3783 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
3784 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
3785 IS_EM_GATE_GRAY(element) ?
3786 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
3787 IS_EMC_GATE_GRAY(element) ?
3788 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
3790 DrawLevelField(x, y);
3792 else if (IS_GATE_GRAY_ACTIVE(element) &&
3793 game.magnify_time_left == 0)
3795 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
3796 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
3797 IS_EM_GATE_GRAY_ACTIVE(element) ?
3798 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
3799 IS_EMC_GATE_GRAY_ACTIVE(element) ?
3800 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
3802 DrawLevelField(x, y);
3807 static void ToggleLightSwitch(int x, int y)
3809 int element = Feld[x][y];
3811 game.light_time_left =
3812 (element == EL_LIGHT_SWITCH ?
3813 level.time_light * FRAMES_PER_SECOND : 0);
3815 RedrawAllLightSwitchesAndInvisibleElements();
3818 static void ActivateTimegateSwitch(int x, int y)
3822 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3824 for (yy = 0; yy < lev_fieldy; yy++)
3826 for (xx = 0; xx < lev_fieldx; xx++)
3828 int element = Feld[xx][yy];
3830 if (element == EL_TIMEGATE_CLOSED ||
3831 element == EL_TIMEGATE_CLOSING)
3833 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3834 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3838 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3840 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3841 DrawLevelField(xx, yy);
3848 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3851 void Impact(int x, int y)
3853 boolean last_line = (y == lev_fieldy - 1);
3854 boolean object_hit = FALSE;
3855 boolean impact = (last_line || object_hit);
3856 int element = Feld[x][y];
3857 int smashed = EL_STEELWALL;
3859 if (!last_line) /* check if element below was hit */
3861 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3864 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3865 MovDir[x][y + 1] != MV_DOWN ||
3866 MovPos[x][y + 1] <= TILEY / 2));
3868 /* do not smash moving elements that left the smashed field in time */
3869 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3870 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3874 smashed = MovingOrBlocked2Element(x, y + 1);
3876 impact = (last_line || object_hit);
3879 if (!last_line && smashed == EL_ACID) /* element falls into acid */
3881 SplashAcid(x, y + 1);
3885 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
3886 /* only reset graphic animation if graphic really changes after impact */
3888 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3890 ResetGfxAnimation(x, y);
3891 DrawLevelField(x, y);
3894 if (impact && CAN_EXPLODE_IMPACT(element))
3899 else if (impact && element == EL_PEARL)
3901 ResetGfxAnimation(x, y);
3903 Feld[x][y] = EL_PEARL_BREAKING;
3904 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3907 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
3909 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3914 if (impact && element == EL_AMOEBA_DROP)
3916 if (object_hit && IS_PLAYER(x, y + 1))
3917 KillPlayerUnlessEnemyProtected(x, y + 1);
3918 else if (object_hit && smashed == EL_PENGUIN)
3922 Feld[x][y] = EL_AMOEBA_GROWING;
3923 Store[x][y] = EL_AMOEBA_WET;
3925 ResetRandomAnimationValue(x, y);
3930 if (object_hit) /* check which object was hit */
3932 if (CAN_PASS_MAGIC_WALL(element) &&
3933 (smashed == EL_MAGIC_WALL ||
3934 smashed == EL_BD_MAGIC_WALL))
3937 int activated_magic_wall =
3938 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3939 EL_BD_MAGIC_WALL_ACTIVE);
3941 /* activate magic wall / mill */
3942 for (yy = 0; yy < lev_fieldy; yy++)
3943 for (xx = 0; xx < lev_fieldx; xx++)
3944 if (Feld[xx][yy] == smashed)
3945 Feld[xx][yy] = activated_magic_wall;
3947 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3948 game.magic_wall_active = TRUE;
3950 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3951 SND_MAGIC_WALL_ACTIVATING :
3952 SND_BD_MAGIC_WALL_ACTIVATING));
3955 if (IS_PLAYER(x, y + 1))
3957 if (CAN_SMASH_PLAYER(element))
3959 KillPlayerUnlessEnemyProtected(x, y + 1);
3963 else if (smashed == EL_PENGUIN)
3965 if (CAN_SMASH_PLAYER(element))
3971 else if (element == EL_BD_DIAMOND)
3973 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3979 else if (((element == EL_SP_INFOTRON ||
3980 element == EL_SP_ZONK) &&
3981 (smashed == EL_SP_SNIKSNAK ||
3982 smashed == EL_SP_ELECTRON ||
3983 smashed == EL_SP_DISK_ORANGE)) ||
3984 (element == EL_SP_INFOTRON &&
3985 smashed == EL_SP_DISK_YELLOW))
3990 else if (CAN_SMASH_EVERYTHING(element))
3992 if (IS_CLASSIC_ENEMY(smashed) ||
3993 CAN_EXPLODE_SMASHED(smashed))
3998 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4000 if (smashed == EL_LAMP ||
4001 smashed == EL_LAMP_ACTIVE)
4006 else if (smashed == EL_NUT)
4008 Feld[x][y + 1] = EL_NUT_BREAKING;
4009 PlayLevelSound(x, y, SND_NUT_BREAKING);
4010 RaiseScoreElement(EL_NUT);
4013 else if (smashed == EL_PEARL)
4015 ResetGfxAnimation(x, y);
4017 Feld[x][y + 1] = EL_PEARL_BREAKING;
4018 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4021 else if (smashed == EL_DIAMOND)
4023 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4024 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4027 else if (IS_BELT_SWITCH(smashed))
4029 ToggleBeltSwitch(x, y + 1);
4031 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4032 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4034 ToggleSwitchgateSwitch(x, y + 1);
4036 else if (smashed == EL_LIGHT_SWITCH ||
4037 smashed == EL_LIGHT_SWITCH_ACTIVE)
4039 ToggleLightSwitch(x, y + 1);
4044 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4047 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4049 CheckElementChangeBySide(x, y + 1, smashed, element,
4050 CE_SWITCHED, CH_SIDE_TOP);
4051 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4057 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4062 /* play sound of magic wall / mill */
4064 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4065 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4067 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4068 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4069 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4070 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4075 /* play sound of object that hits the ground */
4076 if (last_line || object_hit)
4077 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4080 inline static void TurnRoundExt(int x, int y)
4092 { 0, 0 }, { 0, 0 }, { 0, 0 },
4097 int left, right, back;
4101 { MV_DOWN, MV_UP, MV_RIGHT },
4102 { MV_UP, MV_DOWN, MV_LEFT },
4104 { MV_LEFT, MV_RIGHT, MV_DOWN },
4108 { MV_RIGHT, MV_LEFT, MV_UP }
4111 int element = Feld[x][y];
4112 int move_pattern = element_info[element].move_pattern;
4114 int old_move_dir = MovDir[x][y];
4115 int left_dir = turn[old_move_dir].left;
4116 int right_dir = turn[old_move_dir].right;
4117 int back_dir = turn[old_move_dir].back;
4119 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4120 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4121 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4122 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4124 int left_x = x + left_dx, left_y = y + left_dy;
4125 int right_x = x + right_dx, right_y = y + right_dy;
4126 int move_x = x + move_dx, move_y = y + move_dy;
4130 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4132 TestIfBadThingTouchesOtherBadThing(x, y);
4134 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4135 MovDir[x][y] = right_dir;
4136 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4137 MovDir[x][y] = left_dir;
4139 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4141 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4144 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4146 TestIfBadThingTouchesOtherBadThing(x, y);
4148 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4149 MovDir[x][y] = left_dir;
4150 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4151 MovDir[x][y] = right_dir;
4153 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4155 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4158 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4160 TestIfBadThingTouchesOtherBadThing(x, y);
4162 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4163 MovDir[x][y] = left_dir;
4164 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4165 MovDir[x][y] = right_dir;
4167 if (MovDir[x][y] != old_move_dir)
4170 else if (element == EL_YAMYAM)
4172 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4173 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4175 if (can_turn_left && can_turn_right)
4176 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4177 else if (can_turn_left)
4178 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4179 else if (can_turn_right)
4180 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4182 MovDir[x][y] = back_dir;
4184 MovDelay[x][y] = 16 + 16 * RND(3);
4186 else if (element == EL_DARK_YAMYAM)
4188 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4190 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4193 if (can_turn_left && can_turn_right)
4194 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4195 else if (can_turn_left)
4196 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4197 else if (can_turn_right)
4198 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4200 MovDir[x][y] = back_dir;
4202 MovDelay[x][y] = 16 + 16 * RND(3);
4204 else if (element == EL_PACMAN)
4206 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4207 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4209 if (can_turn_left && can_turn_right)
4210 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4211 else if (can_turn_left)
4212 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4213 else if (can_turn_right)
4214 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4216 MovDir[x][y] = back_dir;
4218 MovDelay[x][y] = 6 + RND(40);
4220 else if (element == EL_PIG)
4222 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4223 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4224 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4225 boolean should_turn_left, should_turn_right, should_move_on;
4227 int rnd = RND(rnd_value);
4229 should_turn_left = (can_turn_left &&
4231 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4232 y + back_dy + left_dy)));
4233 should_turn_right = (can_turn_right &&
4235 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4236 y + back_dy + right_dy)));
4237 should_move_on = (can_move_on &&
4240 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4241 y + move_dy + left_dy) ||
4242 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4243 y + move_dy + right_dy)));
4245 if (should_turn_left || should_turn_right || should_move_on)
4247 if (should_turn_left && should_turn_right && should_move_on)
4248 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4249 rnd < 2 * rnd_value / 3 ? right_dir :
4251 else if (should_turn_left && should_turn_right)
4252 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4253 else if (should_turn_left && should_move_on)
4254 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4255 else if (should_turn_right && should_move_on)
4256 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4257 else if (should_turn_left)
4258 MovDir[x][y] = left_dir;
4259 else if (should_turn_right)
4260 MovDir[x][y] = right_dir;
4261 else if (should_move_on)
4262 MovDir[x][y] = old_move_dir;
4264 else if (can_move_on && rnd > rnd_value / 8)
4265 MovDir[x][y] = old_move_dir;
4266 else if (can_turn_left && can_turn_right)
4267 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4268 else if (can_turn_left && rnd > rnd_value / 8)
4269 MovDir[x][y] = left_dir;
4270 else if (can_turn_right && rnd > rnd_value/8)
4271 MovDir[x][y] = right_dir;
4273 MovDir[x][y] = back_dir;
4275 xx = x + move_xy[MovDir[x][y]].x;
4276 yy = y + move_xy[MovDir[x][y]].y;
4278 if (!IN_LEV_FIELD(xx, yy) ||
4279 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4280 MovDir[x][y] = old_move_dir;
4284 else if (element == EL_DRAGON)
4286 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4287 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4288 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4290 int rnd = RND(rnd_value);
4292 if (can_move_on && rnd > rnd_value / 8)
4293 MovDir[x][y] = old_move_dir;
4294 else if (can_turn_left && can_turn_right)
4295 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4296 else if (can_turn_left && rnd > rnd_value / 8)
4297 MovDir[x][y] = left_dir;
4298 else if (can_turn_right && rnd > rnd_value / 8)
4299 MovDir[x][y] = right_dir;
4301 MovDir[x][y] = back_dir;
4303 xx = x + move_xy[MovDir[x][y]].x;
4304 yy = y + move_xy[MovDir[x][y]].y;
4306 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4307 MovDir[x][y] = old_move_dir;
4311 else if (element == EL_MOLE)
4313 boolean can_move_on =
4314 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4315 IS_AMOEBOID(Feld[move_x][move_y]) ||
4316 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4319 boolean can_turn_left =
4320 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4321 IS_AMOEBOID(Feld[left_x][left_y])));
4323 boolean can_turn_right =
4324 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4325 IS_AMOEBOID(Feld[right_x][right_y])));
4327 if (can_turn_left && can_turn_right)
4328 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4329 else if (can_turn_left)
4330 MovDir[x][y] = left_dir;
4332 MovDir[x][y] = right_dir;
4335 if (MovDir[x][y] != old_move_dir)
4338 else if (element == EL_BALLOON)
4340 MovDir[x][y] = game.wind_direction;
4343 else if (element == EL_SPRING)
4345 if (MovDir[x][y] & MV_HORIZONTAL &&
4346 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4347 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4348 MovDir[x][y] = MV_NONE;
4352 else if (element == EL_ROBOT ||
4353 element == EL_SATELLITE ||
4354 element == EL_PENGUIN)
4356 int attr_x = -1, attr_y = -1;
4367 for (i = 0; i < MAX_PLAYERS; i++)
4369 struct PlayerInfo *player = &stored_player[i];
4370 int jx = player->jx, jy = player->jy;
4372 if (!player->active)
4376 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4384 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4385 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4386 game.engine_version < VERSION_IDENT(3,1,0,0)))
4392 if (element == EL_PENGUIN)
4395 static int xy[4][2] =
4403 for (i = 0; i < NUM_DIRECTIONS; i++)
4405 int ex = x + xy[i][0];
4406 int ey = y + xy[i][1];
4408 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4417 MovDir[x][y] = MV_NONE;
4419 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4420 else if (attr_x > x)
4421 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4423 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4424 else if (attr_y > y)
4425 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4427 if (element == EL_ROBOT)
4431 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4432 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4433 Moving2Blocked(x, y, &newx, &newy);
4435 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4436 MovDelay[x][y] = 8 + 8 * !RND(3);
4438 MovDelay[x][y] = 16;
4440 else if (element == EL_PENGUIN)
4446 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4448 boolean first_horiz = RND(2);
4449 int new_move_dir = MovDir[x][y];
4452 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4453 Moving2Blocked(x, y, &newx, &newy);
4455 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4459 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4460 Moving2Blocked(x, y, &newx, &newy);
4462 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4465 MovDir[x][y] = old_move_dir;
4469 else /* (element == EL_SATELLITE) */
4475 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4477 boolean first_horiz = RND(2);
4478 int new_move_dir = MovDir[x][y];
4481 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4482 Moving2Blocked(x, y, &newx, &newy);
4484 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4488 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4489 Moving2Blocked(x, y, &newx, &newy);
4491 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4494 MovDir[x][y] = old_move_dir;
4499 else if (move_pattern == MV_TURNING_LEFT ||
4500 move_pattern == MV_TURNING_RIGHT ||
4501 move_pattern == MV_TURNING_LEFT_RIGHT ||
4502 move_pattern == MV_TURNING_RIGHT_LEFT ||
4503 move_pattern == MV_TURNING_RANDOM ||
4504 move_pattern == MV_ALL_DIRECTIONS)
4506 boolean can_turn_left =
4507 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4508 boolean can_turn_right =
4509 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4511 if (element_info[element].move_stepsize == 0) /* "not moving" */
4514 if (move_pattern == MV_TURNING_LEFT)
4515 MovDir[x][y] = left_dir;
4516 else if (move_pattern == MV_TURNING_RIGHT)
4517 MovDir[x][y] = right_dir;
4518 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4519 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4520 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4521 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4522 else if (move_pattern == MV_TURNING_RANDOM)
4523 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4524 can_turn_right && !can_turn_left ? right_dir :
4525 RND(2) ? left_dir : right_dir);
4526 else if (can_turn_left && can_turn_right)
4527 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4528 else if (can_turn_left)
4529 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4530 else if (can_turn_right)
4531 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4533 MovDir[x][y] = back_dir;
4535 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4537 else if (move_pattern == MV_HORIZONTAL ||
4538 move_pattern == MV_VERTICAL)
4540 if (move_pattern & old_move_dir)
4541 MovDir[x][y] = back_dir;
4542 else if (move_pattern == MV_HORIZONTAL)
4543 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4544 else if (move_pattern == MV_VERTICAL)
4545 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4547 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4549 else if (move_pattern & MV_ANY_DIRECTION)
4551 MovDir[x][y] = move_pattern;
4552 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4554 else if (move_pattern & MV_WIND_DIRECTION)
4556 MovDir[x][y] = game.wind_direction;
4557 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4559 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4561 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4562 MovDir[x][y] = left_dir;
4563 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4564 MovDir[x][y] = right_dir;
4566 if (MovDir[x][y] != old_move_dir)
4567 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4569 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4571 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4572 MovDir[x][y] = right_dir;
4573 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4574 MovDir[x][y] = left_dir;
4576 if (MovDir[x][y] != old_move_dir)
4577 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4579 else if (move_pattern == MV_TOWARDS_PLAYER ||
4580 move_pattern == MV_AWAY_FROM_PLAYER)
4582 int attr_x = -1, attr_y = -1;
4584 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4595 for (i = 0; i < MAX_PLAYERS; i++)
4597 struct PlayerInfo *player = &stored_player[i];
4598 int jx = player->jx, jy = player->jy;
4600 if (!player->active)
4604 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4612 MovDir[x][y] = MV_NONE;
4614 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4615 else if (attr_x > x)
4616 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4618 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4619 else if (attr_y > y)
4620 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4622 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4624 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4626 boolean first_horiz = RND(2);
4627 int new_move_dir = MovDir[x][y];
4629 if (element_info[element].move_stepsize == 0) /* "not moving" */
4631 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
4632 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4638 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4639 Moving2Blocked(x, y, &newx, &newy);
4641 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4645 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4646 Moving2Blocked(x, y, &newx, &newy);
4648 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4651 MovDir[x][y] = old_move_dir;
4654 else if (move_pattern == MV_WHEN_PUSHED ||
4655 move_pattern == MV_WHEN_DROPPED)
4657 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4658 MovDir[x][y] = MV_NONE;
4662 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4664 static int test_xy[7][2] =
4674 static int test_dir[7] =
4684 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4685 int move_preference = -1000000; /* start with very low preference */
4686 int new_move_dir = MV_NONE;
4687 int start_test = RND(4);
4690 for (i = 0; i < NUM_DIRECTIONS; i++)
4692 int move_dir = test_dir[start_test + i];
4693 int move_dir_preference;
4695 xx = x + test_xy[start_test + i][0];
4696 yy = y + test_xy[start_test + i][1];
4698 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4699 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4701 new_move_dir = move_dir;
4706 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4709 move_dir_preference = -1 * RunnerVisit[xx][yy];
4710 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4711 move_dir_preference = PlayerVisit[xx][yy];
4713 if (move_dir_preference > move_preference)
4715 /* prefer field that has not been visited for the longest time */
4716 move_preference = move_dir_preference;
4717 new_move_dir = move_dir;
4719 else if (move_dir_preference == move_preference &&
4720 move_dir == old_move_dir)
4722 /* prefer last direction when all directions are preferred equally */
4723 move_preference = move_dir_preference;
4724 new_move_dir = move_dir;
4728 MovDir[x][y] = new_move_dir;
4729 if (old_move_dir != new_move_dir)
4730 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4734 static void TurnRound(int x, int y)
4736 int direction = MovDir[x][y];
4740 GfxDir[x][y] = MovDir[x][y];
4742 if (direction != MovDir[x][y])
4746 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4749 static boolean JustBeingPushed(int x, int y)
4753 for (i = 0; i < MAX_PLAYERS; i++)
4755 struct PlayerInfo *player = &stored_player[i];
4757 if (player->active && player->is_pushing && player->MovPos)
4759 int next_jx = player->jx + (player->jx - player->last_jx);
4760 int next_jy = player->jy + (player->jy - player->last_jy);
4762 if (x == next_jx && y == next_jy)
4770 void StartMoving(int x, int y)
4772 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4773 int element = Feld[x][y];
4778 if (MovDelay[x][y] == 0)
4779 GfxAction[x][y] = ACTION_DEFAULT;
4781 if (CAN_FALL(element) && y < lev_fieldy - 1)
4783 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4784 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4785 if (JustBeingPushed(x, y))
4788 if (element == EL_QUICKSAND_FULL)
4790 if (IS_FREE(x, y + 1))
4792 InitMovingField(x, y, MV_DOWN);
4793 started_moving = TRUE;
4795 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4796 Store[x][y] = EL_ROCK;
4798 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4800 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4802 if (!MovDelay[x][y])
4803 MovDelay[x][y] = TILEY + 1;
4812 Feld[x][y] = EL_QUICKSAND_EMPTY;
4813 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4814 Store[x][y + 1] = Store[x][y];
4817 PlayLevelSoundAction(x, y, ACTION_FILLING);
4820 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4821 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4823 InitMovingField(x, y, MV_DOWN);
4824 started_moving = TRUE;
4826 Feld[x][y] = EL_QUICKSAND_FILLING;
4827 Store[x][y] = element;
4829 PlayLevelSoundAction(x, y, ACTION_FILLING);
4831 else if (element == EL_MAGIC_WALL_FULL)
4833 if (IS_FREE(x, y + 1))
4835 InitMovingField(x, y, MV_DOWN);
4836 started_moving = TRUE;
4838 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4839 Store[x][y] = EL_CHANGED(Store[x][y]);
4841 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4843 if (!MovDelay[x][y])
4844 MovDelay[x][y] = TILEY/4 + 1;
4853 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4854 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4855 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4859 else if (element == EL_BD_MAGIC_WALL_FULL)
4861 if (IS_FREE(x, y + 1))
4863 InitMovingField(x, y, MV_DOWN);
4864 started_moving = TRUE;
4866 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4867 Store[x][y] = EL_CHANGED2(Store[x][y]);
4869 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4871 if (!MovDelay[x][y])
4872 MovDelay[x][y] = TILEY/4 + 1;
4881 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4882 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4883 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4887 else if (CAN_PASS_MAGIC_WALL(element) &&
4888 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4889 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4891 InitMovingField(x, y, MV_DOWN);
4892 started_moving = TRUE;
4895 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4896 EL_BD_MAGIC_WALL_FILLING);
4897 Store[x][y] = element;
4899 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4901 SplashAcid(x, y + 1);
4903 InitMovingField(x, y, MV_DOWN);
4904 started_moving = TRUE;
4906 Store[x][y] = EL_ACID;
4908 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4909 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
4911 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4912 CAN_FALL(element) && WasJustFalling[x][y] &&
4913 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
4915 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4916 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4917 (Feld[x][y + 1] == EL_BLOCKED)))
4919 /* this is needed for a special case not covered by calling "Impact()"
4920 from "ContinueMoving()": if an element moves to a tile directly below
4921 another element which was just falling on that tile (which was empty
4922 in the previous frame), the falling element above would just stop
4923 instead of smashing the element below (in previous version, the above
4924 element was just checked for "moving" instead of "falling", resulting
4925 in incorrect smashes caused by horizontal movement of the above
4926 element; also, the case of the player being the element to smash was
4927 simply not covered here... :-/ ) */
4929 CheckCollision[x][y] = 0;
4933 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4935 if (MovDir[x][y] == MV_NONE)
4937 InitMovingField(x, y, MV_DOWN);
4938 started_moving = TRUE;
4941 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4943 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4944 MovDir[x][y] = MV_DOWN;
4946 InitMovingField(x, y, MV_DOWN);
4947 started_moving = TRUE;
4949 else if (element == EL_AMOEBA_DROP)
4951 Feld[x][y] = EL_AMOEBA_GROWING;
4952 Store[x][y] = EL_AMOEBA_WET;
4954 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4955 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4956 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4957 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4959 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4960 (IS_FREE(x - 1, y + 1) ||
4961 Feld[x - 1][y + 1] == EL_ACID));
4962 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4963 (IS_FREE(x + 1, y + 1) ||
4964 Feld[x + 1][y + 1] == EL_ACID));
4965 boolean can_fall_any = (can_fall_left || can_fall_right);
4966 boolean can_fall_both = (can_fall_left && can_fall_right);
4967 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4969 #if USE_NEW_ALL_SLIPPERY
4970 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
4972 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4973 can_fall_right = FALSE;
4974 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4975 can_fall_left = FALSE;
4976 else if (slippery_type == SLIPPERY_ONLY_LEFT)
4977 can_fall_right = FALSE;
4978 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4979 can_fall_left = FALSE;
4981 can_fall_any = (can_fall_left || can_fall_right);
4982 can_fall_both = FALSE;
4985 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4987 if (slippery_type == SLIPPERY_ONLY_LEFT)
4988 can_fall_right = FALSE;
4989 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4990 can_fall_left = FALSE;
4991 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4992 can_fall_right = FALSE;
4993 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4994 can_fall_left = FALSE;
4996 can_fall_any = (can_fall_left || can_fall_right);
4997 can_fall_both = (can_fall_left && can_fall_right);
5001 #if USE_NEW_ALL_SLIPPERY
5003 #if USE_NEW_SP_SLIPPERY
5004 /* !!! better use the same properties as for custom elements here !!! */
5005 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5006 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5008 can_fall_right = FALSE; /* slip down on left side */
5009 can_fall_both = FALSE;
5014 #if USE_NEW_ALL_SLIPPERY
5017 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5018 can_fall_right = FALSE; /* slip down on left side */
5020 can_fall_left = !(can_fall_right = RND(2));
5022 can_fall_both = FALSE;
5027 if (game.emulation == EMU_BOULDERDASH ||
5028 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5029 can_fall_right = FALSE; /* slip down on left side */
5031 can_fall_left = !(can_fall_right = RND(2));
5033 can_fall_both = FALSE;
5039 /* if not determined otherwise, prefer left side for slipping down */
5040 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5041 started_moving = TRUE;
5045 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5047 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5050 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5051 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5052 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5053 int belt_dir = game.belt_dir[belt_nr];
5055 if ((belt_dir == MV_LEFT && left_is_free) ||
5056 (belt_dir == MV_RIGHT && right_is_free))
5058 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5060 InitMovingField(x, y, belt_dir);
5061 started_moving = TRUE;
5063 Pushed[x][y] = TRUE;
5064 Pushed[nextx][y] = TRUE;
5066 GfxAction[x][y] = ACTION_DEFAULT;
5070 MovDir[x][y] = 0; /* if element was moving, stop it */
5075 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5077 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5079 if (CAN_MOVE(element) && !started_moving)
5082 int move_pattern = element_info[element].move_pattern;
5087 if (MovDir[x][y] == MV_NONE)
5089 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5090 x, y, element, element_info[element].token_name);
5091 printf("StartMoving(): This should never happen!\n");
5096 Moving2Blocked(x, y, &newx, &newy);
5098 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5101 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5102 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5104 WasJustMoving[x][y] = 0;
5105 CheckCollision[x][y] = 0;
5107 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5109 if (Feld[x][y] != element) /* element has changed */
5113 if (!MovDelay[x][y]) /* start new movement phase */
5115 /* all objects that can change their move direction after each step
5116 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5118 if (element != EL_YAMYAM &&
5119 element != EL_DARK_YAMYAM &&
5120 element != EL_PACMAN &&
5121 !(move_pattern & MV_ANY_DIRECTION) &&
5122 move_pattern != MV_TURNING_LEFT &&
5123 move_pattern != MV_TURNING_RIGHT &&
5124 move_pattern != MV_TURNING_LEFT_RIGHT &&
5125 move_pattern != MV_TURNING_RIGHT_LEFT &&
5126 move_pattern != MV_TURNING_RANDOM)
5130 if (MovDelay[x][y] && (element == EL_BUG ||
5131 element == EL_SPACESHIP ||
5132 element == EL_SP_SNIKSNAK ||
5133 element == EL_SP_ELECTRON ||
5134 element == EL_MOLE))
5135 DrawLevelField(x, y);
5139 if (MovDelay[x][y]) /* wait some time before next movement */
5143 if (element == EL_ROBOT ||
5144 element == EL_YAMYAM ||
5145 element == EL_DARK_YAMYAM)
5147 DrawLevelElementAnimationIfNeeded(x, y, element);
5148 PlayLevelSoundAction(x, y, ACTION_WAITING);
5150 else if (element == EL_SP_ELECTRON)
5151 DrawLevelElementAnimationIfNeeded(x, y, element);
5152 else if (element == EL_DRAGON)
5155 int dir = MovDir[x][y];
5156 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5157 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5158 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5159 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5160 dir == MV_UP ? IMG_FLAMES_1_UP :
5161 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5162 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5164 GfxAction[x][y] = ACTION_ATTACKING;
5166 if (IS_PLAYER(x, y))
5167 DrawPlayerField(x, y);
5169 DrawLevelField(x, y);
5171 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5173 for (i = 1; i <= 3; i++)
5175 int xx = x + i * dx;
5176 int yy = y + i * dy;
5177 int sx = SCREENX(xx);
5178 int sy = SCREENY(yy);
5179 int flame_graphic = graphic + (i - 1);
5181 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5186 int flamed = MovingOrBlocked2Element(xx, yy);
5190 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5192 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5193 RemoveMovingField(xx, yy);
5195 RemoveField(xx, yy);
5197 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5200 RemoveMovingField(xx, yy);
5203 ChangeDelay[xx][yy] = 0;
5205 Feld[xx][yy] = EL_FLAMES;
5207 if (IN_SCR_FIELD(sx, sy))
5209 DrawLevelFieldCrumbledSand(xx, yy);
5210 DrawGraphic(sx, sy, flame_graphic, frame);
5215 if (Feld[xx][yy] == EL_FLAMES)
5216 Feld[xx][yy] = EL_EMPTY;
5217 DrawLevelField(xx, yy);
5222 if (MovDelay[x][y]) /* element still has to wait some time */
5224 PlayLevelSoundAction(x, y, ACTION_WAITING);
5230 /* now make next step */
5232 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5234 if (DONT_COLLIDE_WITH(element) &&
5235 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5236 !PLAYER_ENEMY_PROTECTED(newx, newy))
5238 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5243 else if (CAN_MOVE_INTO_ACID(element) &&
5244 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5245 (MovDir[x][y] == MV_DOWN ||
5246 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5248 SplashAcid(newx, newy);
5249 Store[x][y] = EL_ACID;
5251 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5253 if (Feld[newx][newy] == EL_EXIT_OPEN)
5256 DrawLevelField(x, y);
5258 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5259 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5260 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5262 local_player->friends_still_needed--;
5263 if (!local_player->friends_still_needed &&
5264 !local_player->GameOver && AllPlayersGone)
5265 local_player->LevelSolved = local_player->GameOver = TRUE;
5269 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5271 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5272 DrawLevelField(newx, newy);
5274 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5276 else if (!IS_FREE(newx, newy))
5278 GfxAction[x][y] = ACTION_WAITING;
5280 if (IS_PLAYER(x, y))
5281 DrawPlayerField(x, y);
5283 DrawLevelField(x, y);
5288 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5290 if (IS_FOOD_PIG(Feld[newx][newy]))
5292 if (IS_MOVING(newx, newy))
5293 RemoveMovingField(newx, newy);
5296 Feld[newx][newy] = EL_EMPTY;
5297 DrawLevelField(newx, newy);
5300 PlayLevelSound(x, y, SND_PIG_DIGGING);
5302 else if (!IS_FREE(newx, newy))
5304 if (IS_PLAYER(x, y))
5305 DrawPlayerField(x, y);
5307 DrawLevelField(x, y);
5312 else if (IS_CUSTOM_ELEMENT(element) &&
5313 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5315 int new_element = Feld[newx][newy];
5317 if (!IS_FREE(newx, newy))
5319 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5320 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5323 /* no element can dig solid indestructible elements */
5324 if (IS_INDESTRUCTIBLE(new_element) &&
5325 !IS_DIGGABLE(new_element) &&
5326 !IS_COLLECTIBLE(new_element))
5329 if (AmoebaNr[newx][newy] &&
5330 (new_element == EL_AMOEBA_FULL ||
5331 new_element == EL_BD_AMOEBA ||
5332 new_element == EL_AMOEBA_GROWING))
5334 AmoebaCnt[AmoebaNr[newx][newy]]--;
5335 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5338 if (IS_MOVING(newx, newy))
5339 RemoveMovingField(newx, newy);
5342 RemoveField(newx, newy);
5343 DrawLevelField(newx, newy);
5346 /* if digged element was about to explode, prevent the explosion */
5347 ExplodeField[newx][newy] = EX_TYPE_NONE;
5349 PlayLevelSoundAction(x, y, action);
5352 Store[newx][newy] = EL_EMPTY;
5353 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5355 int move_leave_element = element_info[element].move_leave_element;
5357 /* this makes it possible to leave the removed element again */
5358 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
5359 new_element : move_leave_element);
5362 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5364 RunnerVisit[x][y] = FrameCounter;
5365 PlayerVisit[x][y] /= 8; /* expire player visit path */
5368 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5370 if (!IS_FREE(newx, newy))
5372 if (IS_PLAYER(x, y))
5373 DrawPlayerField(x, y);
5375 DrawLevelField(x, y);
5381 boolean wanna_flame = !RND(10);
5382 int dx = newx - x, dy = newy - y;
5383 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5384 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5385 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5386 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5387 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5388 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5391 IS_CLASSIC_ENEMY(element1) ||
5392 IS_CLASSIC_ENEMY(element2)) &&
5393 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5394 element1 != EL_FLAMES && element2 != EL_FLAMES)
5396 ResetGfxAnimation(x, y);
5397 GfxAction[x][y] = ACTION_ATTACKING;
5399 if (IS_PLAYER(x, y))
5400 DrawPlayerField(x, y);
5402 DrawLevelField(x, y);
5404 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5406 MovDelay[x][y] = 50;
5410 RemoveField(newx, newy);
5412 Feld[newx][newy] = EL_FLAMES;
5413 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5416 RemoveField(newx1, newy1);
5418 Feld[newx1][newy1] = EL_FLAMES;
5420 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5423 RemoveField(newx2, newy2);
5425 Feld[newx2][newy2] = EL_FLAMES;
5432 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5433 Feld[newx][newy] == EL_DIAMOND)
5435 if (IS_MOVING(newx, newy))
5436 RemoveMovingField(newx, newy);
5439 Feld[newx][newy] = EL_EMPTY;
5440 DrawLevelField(newx, newy);
5443 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5445 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5446 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5448 if (AmoebaNr[newx][newy])
5450 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5451 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5452 Feld[newx][newy] == EL_BD_AMOEBA)
5453 AmoebaCnt[AmoebaNr[newx][newy]]--;
5458 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5460 RemoveMovingField(newx, newy);
5463 if (IS_MOVING(newx, newy))
5465 RemoveMovingField(newx, newy);
5470 Feld[newx][newy] = EL_EMPTY;
5471 DrawLevelField(newx, newy);
5474 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5476 else if ((element == EL_PACMAN || element == EL_MOLE)
5477 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5479 if (AmoebaNr[newx][newy])
5481 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5482 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5483 Feld[newx][newy] == EL_BD_AMOEBA)
5484 AmoebaCnt[AmoebaNr[newx][newy]]--;
5487 if (element == EL_MOLE)
5489 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5490 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5492 ResetGfxAnimation(x, y);
5493 GfxAction[x][y] = ACTION_DIGGING;
5494 DrawLevelField(x, y);
5496 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5498 return; /* wait for shrinking amoeba */
5500 else /* element == EL_PACMAN */
5502 Feld[newx][newy] = EL_EMPTY;
5503 DrawLevelField(newx, newy);
5504 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5507 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5508 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5509 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5511 /* wait for shrinking amoeba to completely disappear */
5514 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5516 /* object was running against a wall */
5521 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
5522 if (move_pattern & MV_ANY_DIRECTION &&
5523 move_pattern == MovDir[x][y])
5525 int blocking_element =
5526 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5528 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5531 element = Feld[x][y]; /* element might have changed */
5535 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5536 DrawLevelElementAnimation(x, y, element);
5538 if (DONT_TOUCH(element))
5539 TestIfBadThingTouchesPlayer(x, y);
5544 InitMovingField(x, y, MovDir[x][y]);
5546 PlayLevelSoundAction(x, y, ACTION_MOVING);
5550 ContinueMoving(x, y);
5553 void ContinueMoving(int x, int y)
5555 int element = Feld[x][y];
5556 int stored = Store[x][y];
5557 struct ElementInfo *ei = &element_info[element];
5558 int direction = MovDir[x][y];
5559 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5560 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5561 int newx = x + dx, newy = y + dy;
5562 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5563 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5564 boolean last_line = (newy == lev_fieldy - 1);
5566 MovPos[x][y] += getElementMoveStepsize(x, y);
5568 if (pushed_by_player) /* special case: moving object pushed by player */
5569 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5571 if (ABS(MovPos[x][y]) < TILEX)
5573 DrawLevelField(x, y);
5575 return; /* element is still moving */
5578 /* element reached destination field */
5580 Feld[x][y] = EL_EMPTY;
5581 Feld[newx][newy] = element;
5582 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5584 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
5586 element = Feld[newx][newy] = EL_ACID;
5588 else if (element == EL_MOLE)
5590 Feld[x][y] = EL_SAND;
5592 DrawLevelFieldCrumbledSandNeighbours(x, y);
5594 else if (element == EL_QUICKSAND_FILLING)
5596 element = Feld[newx][newy] = get_next_element(element);
5597 Store[newx][newy] = Store[x][y];
5599 else if (element == EL_QUICKSAND_EMPTYING)
5601 Feld[x][y] = get_next_element(element);
5602 element = Feld[newx][newy] = Store[x][y];
5604 else if (element == EL_MAGIC_WALL_FILLING)
5606 element = Feld[newx][newy] = get_next_element(element);
5607 if (!game.magic_wall_active)
5608 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5609 Store[newx][newy] = Store[x][y];
5611 else if (element == EL_MAGIC_WALL_EMPTYING)
5613 Feld[x][y] = get_next_element(element);
5614 if (!game.magic_wall_active)
5615 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5616 element = Feld[newx][newy] = Store[x][y];
5618 #if USE_NEW_CUSTOM_VALUE
5619 InitField(newx, newy, FALSE);
5622 else if (element == EL_BD_MAGIC_WALL_FILLING)
5624 element = Feld[newx][newy] = get_next_element(element);
5625 if (!game.magic_wall_active)
5626 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5627 Store[newx][newy] = Store[x][y];
5629 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5631 Feld[x][y] = get_next_element(element);
5632 if (!game.magic_wall_active)
5633 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5634 element = Feld[newx][newy] = Store[x][y];
5636 #if USE_NEW_CUSTOM_VALUE
5637 InitField(newx, newy, FALSE);
5640 else if (element == EL_AMOEBA_DROPPING)
5642 Feld[x][y] = get_next_element(element);
5643 element = Feld[newx][newy] = Store[x][y];
5645 else if (element == EL_SOKOBAN_OBJECT)
5648 Feld[x][y] = Back[x][y];
5650 if (Back[newx][newy])
5651 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5653 Back[x][y] = Back[newx][newy] = 0;
5656 Store[x][y] = EL_EMPTY;
5661 MovDelay[newx][newy] = 0;
5663 if (CAN_CHANGE(element))
5665 /* copy element change control values to new field */
5666 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5667 ChangePage[newx][newy] = ChangePage[x][y];
5668 Changed[newx][newy] = Changed[x][y];
5669 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5671 #if USE_NEW_CUSTOM_VALUE
5672 CustomValue[newx][newy] = CustomValue[x][y];
5676 ChangeDelay[x][y] = 0;
5677 ChangePage[x][y] = -1;
5678 Changed[x][y] = FALSE;
5679 ChangeEvent[x][y] = -1;
5681 #if USE_NEW_CUSTOM_VALUE
5682 CustomValue[x][y] = 0;
5685 /* copy animation control values to new field */
5686 GfxFrame[newx][newy] = GfxFrame[x][y];
5687 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5688 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5689 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5691 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5693 /* some elements can leave other elements behind after moving */
5694 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
5695 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
5696 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
5698 int move_leave_element = ei->move_leave_element;
5700 /* this makes it possible to leave the removed element again */
5701 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
5702 ei->move_leave_element == EL_TRIGGER_ELEMENT)
5703 move_leave_element = stored;
5705 Feld[x][y] = move_leave_element;
5707 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
5708 MovDir[x][y] = direction;
5710 InitField(x, y, FALSE);
5712 if (GFX_CRUMBLED(Feld[x][y]))
5713 DrawLevelFieldCrumbledSandNeighbours(x, y);
5715 if (ELEM_IS_PLAYER(move_leave_element))
5716 RelocatePlayer(x, y, move_leave_element);
5719 /* do this after checking for left-behind element */
5720 ResetGfxAnimation(x, y); /* reset animation values for old field */
5722 if (!CAN_MOVE(element) ||
5723 (CAN_FALL(element) && direction == MV_DOWN &&
5724 (element == EL_SPRING ||
5725 element_info[element].move_pattern == MV_WHEN_PUSHED ||
5726 element_info[element].move_pattern == MV_WHEN_DROPPED)))
5727 GfxDir[x][y] = MovDir[newx][newy] = 0;
5729 DrawLevelField(x, y);
5730 DrawLevelField(newx, newy);
5732 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5734 /* prevent pushed element from moving on in pushed direction */
5735 if (pushed_by_player && CAN_MOVE(element) &&
5736 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5737 !(element_info[element].move_pattern & direction))
5738 TurnRound(newx, newy);
5740 /* prevent elements on conveyor belt from moving on in last direction */
5741 if (pushed_by_conveyor && CAN_FALL(element) &&
5742 direction & MV_HORIZONTAL)
5743 MovDir[newx][newy] = 0;
5745 if (!pushed_by_player)
5747 int nextx = newx + dx, nexty = newy + dy;
5748 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
5750 WasJustMoving[newx][newy] = 3;
5752 if (CAN_FALL(element) && direction == MV_DOWN)
5753 WasJustFalling[newx][newy] = 3;
5755 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
5756 CheckCollision[newx][newy] = 2;
5759 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5761 TestIfBadThingTouchesPlayer(newx, newy);
5762 TestIfBadThingTouchesFriend(newx, newy);
5764 if (!IS_CUSTOM_ELEMENT(element))
5765 TestIfBadThingTouchesOtherBadThing(newx, newy);
5767 else if (element == EL_PENGUIN)
5768 TestIfFriendTouchesBadThing(newx, newy);
5770 /* give the player one last chance (one more frame) to move away */
5771 if (CAN_FALL(element) && direction == MV_DOWN &&
5772 (last_line || (!IS_FREE(x, newy + 1) &&
5773 (!IS_PLAYER(x, newy + 1) ||
5774 game.engine_version < VERSION_IDENT(3,1,1,0)))))
5777 if (pushed_by_player && !game.use_change_when_pushing_bug)
5779 int push_side = MV_DIR_OPPOSITE(direction);
5780 struct PlayerInfo *player = PLAYERINFO(x, y);
5782 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
5783 player->index_bit, push_side);
5784 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
5785 player->index_bit, push_side);
5788 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
5790 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5792 TestIfElementHitsCustomElement(newx, newy, direction);
5793 TestIfPlayerTouchesCustomElement(newx, newy);
5794 TestIfElementTouchesCustomElement(newx, newy);
5797 int AmoebeNachbarNr(int ax, int ay)
5800 int element = Feld[ax][ay];
5802 static int xy[4][2] =
5810 for (i = 0; i < NUM_DIRECTIONS; i++)
5812 int x = ax + xy[i][0];
5813 int y = ay + xy[i][1];
5815 if (!IN_LEV_FIELD(x, y))
5818 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5819 group_nr = AmoebaNr[x][y];
5825 void AmoebenVereinigen(int ax, int ay)
5827 int i, x, y, xx, yy;
5828 int new_group_nr = AmoebaNr[ax][ay];
5829 static int xy[4][2] =
5837 if (new_group_nr == 0)
5840 for (i = 0; i < NUM_DIRECTIONS; i++)
5845 if (!IN_LEV_FIELD(x, y))
5848 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5849 Feld[x][y] == EL_BD_AMOEBA ||
5850 Feld[x][y] == EL_AMOEBA_DEAD) &&
5851 AmoebaNr[x][y] != new_group_nr)
5853 int old_group_nr = AmoebaNr[x][y];
5855 if (old_group_nr == 0)
5858 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5859 AmoebaCnt[old_group_nr] = 0;
5860 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5861 AmoebaCnt2[old_group_nr] = 0;
5863 for (yy = 0; yy < lev_fieldy; yy++)
5865 for (xx = 0; xx < lev_fieldx; xx++)
5867 if (AmoebaNr[xx][yy] == old_group_nr)
5868 AmoebaNr[xx][yy] = new_group_nr;
5875 void AmoebeUmwandeln(int ax, int ay)
5879 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5881 int group_nr = AmoebaNr[ax][ay];
5886 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5887 printf("AmoebeUmwandeln(): This should never happen!\n");
5892 for (y = 0; y < lev_fieldy; y++)
5894 for (x = 0; x < lev_fieldx; x++)
5896 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5899 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5903 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5904 SND_AMOEBA_TURNING_TO_GEM :
5905 SND_AMOEBA_TURNING_TO_ROCK));
5910 static int xy[4][2] =
5918 for (i = 0; i < NUM_DIRECTIONS; i++)
5923 if (!IN_LEV_FIELD(x, y))
5926 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5928 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5929 SND_AMOEBA_TURNING_TO_GEM :
5930 SND_AMOEBA_TURNING_TO_ROCK));
5937 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5940 int group_nr = AmoebaNr[ax][ay];
5941 boolean done = FALSE;
5946 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5947 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5952 for (y = 0; y < lev_fieldy; y++)
5954 for (x = 0; x < lev_fieldx; x++)
5956 if (AmoebaNr[x][y] == group_nr &&
5957 (Feld[x][y] == EL_AMOEBA_DEAD ||
5958 Feld[x][y] == EL_BD_AMOEBA ||
5959 Feld[x][y] == EL_AMOEBA_GROWING))
5962 Feld[x][y] = new_element;
5963 InitField(x, y, FALSE);
5964 DrawLevelField(x, y);
5971 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5972 SND_BD_AMOEBA_TURNING_TO_ROCK :
5973 SND_BD_AMOEBA_TURNING_TO_GEM));
5976 void AmoebeWaechst(int x, int y)
5978 static unsigned long sound_delay = 0;
5979 static unsigned long sound_delay_value = 0;
5981 if (!MovDelay[x][y]) /* start new growing cycle */
5985 if (DelayReached(&sound_delay, sound_delay_value))
5987 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5988 sound_delay_value = 30;
5992 if (MovDelay[x][y]) /* wait some time before growing bigger */
5995 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5997 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5998 6 - MovDelay[x][y]);
6000 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6003 if (!MovDelay[x][y])
6005 Feld[x][y] = Store[x][y];
6007 DrawLevelField(x, y);
6012 void AmoebaDisappearing(int x, int y)
6014 static unsigned long sound_delay = 0;
6015 static unsigned long sound_delay_value = 0;
6017 if (!MovDelay[x][y]) /* start new shrinking cycle */
6021 if (DelayReached(&sound_delay, sound_delay_value))
6022 sound_delay_value = 30;
6025 if (MovDelay[x][y]) /* wait some time before shrinking */
6028 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6030 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6031 6 - MovDelay[x][y]);
6033 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6036 if (!MovDelay[x][y])
6038 Feld[x][y] = EL_EMPTY;
6039 DrawLevelField(x, y);
6041 /* don't let mole enter this field in this cycle;
6042 (give priority to objects falling to this field from above) */
6048 void AmoebeAbleger(int ax, int ay)
6051 int element = Feld[ax][ay];
6052 int graphic = el2img(element);
6053 int newax = ax, neway = ay;
6054 static int xy[4][2] =
6062 if (!level.amoeba_speed)
6064 Feld[ax][ay] = EL_AMOEBA_DEAD;
6065 DrawLevelField(ax, ay);
6069 if (IS_ANIMATED(graphic))
6070 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6072 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6073 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6075 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6078 if (MovDelay[ax][ay])
6082 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6085 int x = ax + xy[start][0];
6086 int y = ay + xy[start][1];
6088 if (!IN_LEV_FIELD(x, y))
6091 if (IS_FREE(x, y) ||
6092 CAN_GROW_INTO(Feld[x][y]) ||
6093 Feld[x][y] == EL_QUICKSAND_EMPTY)
6099 if (newax == ax && neway == ay)
6102 else /* normal or "filled" (BD style) amoeba */
6105 boolean waiting_for_player = FALSE;
6107 for (i = 0; i < NUM_DIRECTIONS; i++)
6109 int j = (start + i) % 4;
6110 int x = ax + xy[j][0];
6111 int y = ay + xy[j][1];
6113 if (!IN_LEV_FIELD(x, y))
6116 if (IS_FREE(x, y) ||
6117 CAN_GROW_INTO(Feld[x][y]) ||
6118 Feld[x][y] == EL_QUICKSAND_EMPTY)
6124 else if (IS_PLAYER(x, y))
6125 waiting_for_player = TRUE;
6128 if (newax == ax && neway == ay) /* amoeba cannot grow */
6130 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6132 Feld[ax][ay] = EL_AMOEBA_DEAD;
6133 DrawLevelField(ax, ay);
6134 AmoebaCnt[AmoebaNr[ax][ay]]--;
6136 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6138 if (element == EL_AMOEBA_FULL)
6139 AmoebeUmwandeln(ax, ay);
6140 else if (element == EL_BD_AMOEBA)
6141 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6146 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6148 /* amoeba gets larger by growing in some direction */
6150 int new_group_nr = AmoebaNr[ax][ay];
6153 if (new_group_nr == 0)
6155 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6156 printf("AmoebeAbleger(): This should never happen!\n");
6161 AmoebaNr[newax][neway] = new_group_nr;
6162 AmoebaCnt[new_group_nr]++;
6163 AmoebaCnt2[new_group_nr]++;
6165 /* if amoeba touches other amoeba(s) after growing, unify them */
6166 AmoebenVereinigen(newax, neway);
6168 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6170 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6176 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6177 (neway == lev_fieldy - 1 && newax != ax))
6179 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6180 Store[newax][neway] = element;
6182 else if (neway == ay)
6184 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6186 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6190 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6191 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6192 Store[ax][ay] = EL_AMOEBA_DROP;
6193 ContinueMoving(ax, ay);
6197 DrawLevelField(newax, neway);
6200 void Life(int ax, int ay)
6204 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6207 int element = Feld[ax][ay];
6208 int graphic = el2img(element);
6209 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6211 boolean changed = FALSE;
6213 if (IS_ANIMATED(graphic))
6214 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6219 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6220 MovDelay[ax][ay] = life_time;
6222 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6225 if (MovDelay[ax][ay])
6229 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6231 int xx = ax+x1, yy = ay+y1;
6234 if (!IN_LEV_FIELD(xx, yy))
6237 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6239 int x = xx+x2, y = yy+y2;
6241 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6244 if (((Feld[x][y] == element ||
6245 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6247 (IS_FREE(x, y) && Stop[x][y]))
6251 if (xx == ax && yy == ay) /* field in the middle */
6253 if (nachbarn < life_parameter[0] ||
6254 nachbarn > life_parameter[1])
6256 Feld[xx][yy] = EL_EMPTY;
6258 DrawLevelField(xx, yy);
6259 Stop[xx][yy] = TRUE;
6263 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6264 { /* free border field */
6265 if (nachbarn >= life_parameter[2] &&
6266 nachbarn <= life_parameter[3])
6268 Feld[xx][yy] = element;
6269 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6271 DrawLevelField(xx, yy);
6272 Stop[xx][yy] = TRUE;
6279 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6280 SND_GAME_OF_LIFE_GROWING);
6283 static void InitRobotWheel(int x, int y)
6285 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6288 static void RunRobotWheel(int x, int y)
6290 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6293 static void StopRobotWheel(int x, int y)
6295 if (ZX == x && ZY == y)
6299 static void InitTimegateWheel(int x, int y)
6301 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6304 static void RunTimegateWheel(int x, int y)
6306 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6309 void CheckExit(int x, int y)
6311 if (local_player->gems_still_needed > 0 ||
6312 local_player->sokobanfields_still_needed > 0 ||
6313 local_player->lights_still_needed > 0)
6315 int element = Feld[x][y];
6316 int graphic = el2img(element);
6318 if (IS_ANIMATED(graphic))
6319 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6324 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6327 Feld[x][y] = EL_EXIT_OPENING;
6329 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6332 void CheckExitSP(int x, int y)
6334 if (local_player->gems_still_needed > 0)
6336 int element = Feld[x][y];
6337 int graphic = el2img(element);
6339 if (IS_ANIMATED(graphic))
6340 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6345 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6348 Feld[x][y] = EL_SP_EXIT_OPENING;
6350 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6353 static void CloseAllOpenTimegates()
6357 for (y = 0; y < lev_fieldy; y++)
6359 for (x = 0; x < lev_fieldx; x++)
6361 int element = Feld[x][y];
6363 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6365 Feld[x][y] = EL_TIMEGATE_CLOSING;
6367 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6373 void EdelsteinFunkeln(int x, int y)
6375 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6378 if (Feld[x][y] == EL_BD_DIAMOND)
6381 if (MovDelay[x][y] == 0) /* next animation frame */
6382 MovDelay[x][y] = 11 * !SimpleRND(500);
6384 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6388 if (setup.direct_draw && MovDelay[x][y])
6389 SetDrawtoField(DRAW_BUFFERED);
6391 DrawLevelElementAnimation(x, y, Feld[x][y]);
6393 if (MovDelay[x][y] != 0)
6395 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6396 10 - MovDelay[x][y]);
6398 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6400 if (setup.direct_draw)
6404 dest_x = FX + SCREENX(x) * TILEX;
6405 dest_y = FY + SCREENY(y) * TILEY;
6407 BlitBitmap(drawto_field, window,
6408 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6409 SetDrawtoField(DRAW_DIRECT);
6415 void MauerWaechst(int x, int y)
6419 if (!MovDelay[x][y]) /* next animation frame */
6420 MovDelay[x][y] = 3 * delay;
6422 if (MovDelay[x][y]) /* wait some time before next frame */
6426 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6428 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6429 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6431 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6434 if (!MovDelay[x][y])
6436 if (MovDir[x][y] == MV_LEFT)
6438 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6439 DrawLevelField(x - 1, y);
6441 else if (MovDir[x][y] == MV_RIGHT)
6443 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6444 DrawLevelField(x + 1, y);
6446 else if (MovDir[x][y] == MV_UP)
6448 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6449 DrawLevelField(x, y - 1);
6453 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6454 DrawLevelField(x, y + 1);
6457 Feld[x][y] = Store[x][y];
6459 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6460 DrawLevelField(x, y);
6465 void MauerAbleger(int ax, int ay)
6467 int element = Feld[ax][ay];
6468 int graphic = el2img(element);
6469 boolean oben_frei = FALSE, unten_frei = FALSE;
6470 boolean links_frei = FALSE, rechts_frei = FALSE;
6471 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6472 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6473 boolean new_wall = FALSE;
6475 if (IS_ANIMATED(graphic))
6476 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6478 if (!MovDelay[ax][ay]) /* start building new wall */
6479 MovDelay[ax][ay] = 6;
6481 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6484 if (MovDelay[ax][ay])
6488 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6490 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6492 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6494 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6497 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6498 element == EL_EXPANDABLE_WALL_ANY)
6502 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6503 Store[ax][ay-1] = element;
6504 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6505 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6506 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6507 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6512 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6513 Store[ax][ay+1] = element;
6514 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6515 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6516 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6517 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6522 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6523 element == EL_EXPANDABLE_WALL_ANY ||
6524 element == EL_EXPANDABLE_WALL)
6528 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6529 Store[ax-1][ay] = element;
6530 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6531 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6532 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6533 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6539 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6540 Store[ax+1][ay] = element;
6541 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6542 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6543 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6544 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6549 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6550 DrawLevelField(ax, ay);
6552 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6554 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6555 unten_massiv = TRUE;
6556 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6557 links_massiv = TRUE;
6558 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6559 rechts_massiv = TRUE;
6561 if (((oben_massiv && unten_massiv) ||
6562 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6563 element == EL_EXPANDABLE_WALL) &&
6564 ((links_massiv && rechts_massiv) ||
6565 element == EL_EXPANDABLE_WALL_VERTICAL))
6566 Feld[ax][ay] = EL_WALL;
6569 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6572 void CheckForDragon(int x, int y)
6575 boolean dragon_found = FALSE;
6576 static int xy[4][2] =
6584 for (i = 0; i < NUM_DIRECTIONS; i++)
6586 for (j = 0; j < 4; j++)
6588 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6590 if (IN_LEV_FIELD(xx, yy) &&
6591 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6593 if (Feld[xx][yy] == EL_DRAGON)
6594 dragon_found = TRUE;
6603 for (i = 0; i < NUM_DIRECTIONS; i++)
6605 for (j = 0; j < 3; j++)
6607 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6609 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6611 Feld[xx][yy] = EL_EMPTY;
6612 DrawLevelField(xx, yy);
6621 static void InitBuggyBase(int x, int y)
6623 int element = Feld[x][y];
6624 int activating_delay = FRAMES_PER_SECOND / 4;
6627 (element == EL_SP_BUGGY_BASE ?
6628 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6629 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6631 element == EL_SP_BUGGY_BASE_ACTIVE ?
6632 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6635 static void WarnBuggyBase(int x, int y)
6638 static int xy[4][2] =
6646 for (i = 0; i < NUM_DIRECTIONS; i++)
6648 int xx = x + xy[i][0], yy = y + xy[i][1];
6650 if (IS_PLAYER(xx, yy))
6652 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6659 static void InitTrap(int x, int y)
6661 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6664 static void ActivateTrap(int x, int y)
6666 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6669 static void ChangeActiveTrap(int x, int y)
6671 int graphic = IMG_TRAP_ACTIVE;
6673 /* if new animation frame was drawn, correct crumbled sand border */
6674 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6675 DrawLevelFieldCrumbledSand(x, y);
6678 static int getSpecialActionElement(int element, int number, int base_element)
6680 return (element != EL_EMPTY ? element :
6681 number != -1 ? base_element + number - 1 :
6685 static int getModifiedActionNumber(int value_old, int operator, int operand,
6686 int value_min, int value_max)
6688 int value_new = (operator == CA_MODE_SET ? operand :
6689 operator == CA_MODE_ADD ? value_old + operand :
6690 operator == CA_MODE_SUBTRACT ? value_old - operand :
6691 operator == CA_MODE_MULTIPLY ? value_old * operand :
6692 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
6693 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
6696 return (value_new < value_min ? value_min :
6697 value_new > value_max ? value_max :
6701 static void ExecuteCustomElementAction(int x, int y, int element, int page)
6703 struct ElementInfo *ei = &element_info[element];
6704 struct ElementChangeInfo *change = &ei->change_page[page];
6705 int action_type = change->action_type;
6706 int action_mode = change->action_mode;
6707 int action_arg = change->action_arg;
6710 if (!change->has_action)
6713 /* ---------- determine action paramater values -------------------------- */
6715 int level_time_value =
6716 (level.time > 0 ? TimeLeft :
6719 int action_arg_element =
6720 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
6721 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
6722 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
6725 int action_arg_direction =
6726 (action_arg >= CA_ARG_DIRECTION_LEFT &&
6727 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
6728 action_arg == CA_ARG_DIRECTION_TRIGGER ?
6729 change->actual_trigger_side :
6730 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
6731 MV_DIR_OPPOSITE(change->actual_trigger_side) :
6734 int action_arg_number_min =
6735 (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MIN :
6738 int action_arg_number_max =
6739 (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MAX :
6740 action_type == CA_SET_LEVEL_GEMS ? 999 :
6741 action_type == CA_SET_LEVEL_TIME ? 9999 :
6742 action_type == CA_SET_LEVEL_SCORE ? 99999 :
6743 action_type == CA_SET_CE_SCORE ? 9999 :
6744 action_type == CA_SET_CE_VALUE ? 9999 :
6747 int action_arg_number_reset =
6748 (action_type == CA_SET_PLAYER_SPEED ? TILEX/game.initial_move_delay_value :
6749 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
6750 action_type == CA_SET_LEVEL_TIME ? level.time :
6751 action_type == CA_SET_LEVEL_SCORE ? 0 :
6752 action_type == CA_SET_CE_SCORE ? 0 :
6754 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
6756 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
6760 int action_arg_number =
6761 (action_arg <= CA_ARG_MAX ? action_arg :
6762 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
6763 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
6764 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
6765 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
6766 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
6767 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
6768 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
6769 #if USE_NEW_CUSTOM_VALUE
6770 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
6772 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
6774 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) :
6775 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
6776 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
6777 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
6778 action_arg == CA_ARG_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
6779 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value :
6782 int action_arg_number_old =
6783 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
6784 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
6785 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
6786 action_type == CA_SET_CE_SCORE ? ei->collect_score :
6787 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
6790 int action_arg_number_new =
6791 getModifiedActionNumber(action_arg_number_old,
6792 action_mode, action_arg_number,
6793 action_arg_number_min, action_arg_number_max);
6795 int trigger_player_bits =
6796 (change->actual_trigger_player >= EL_PLAYER_1 &&
6797 change->actual_trigger_player <= EL_PLAYER_4 ?
6798 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
6801 int action_arg_player_bits =
6802 (action_arg >= CA_ARG_PLAYER_1 &&
6803 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
6804 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
6807 /* ---------- execute action -------------------------------------------- */
6816 /* ---------- level actions ------------------------------------------- */
6818 case CA_RESTART_LEVEL:
6820 game.restart_level = TRUE;
6825 case CA_SHOW_ENVELOPE:
6827 int element = getSpecialActionElement(action_arg_element,
6828 action_arg_number, EL_ENVELOPE_1);
6830 if (IS_ENVELOPE(element))
6831 local_player->show_envelope = element;
6836 case CA_SET_LEVEL_TIME:
6838 if (level.time > 0) /* only modify limited time value */
6840 TimeLeft = action_arg_number_new;
6842 DrawGameValue_Time(TimeLeft);
6844 if (!TimeLeft && setup.time_limit)
6845 for (i = 0; i < MAX_PLAYERS; i++)
6846 KillPlayer(&stored_player[i]);
6852 case CA_SET_LEVEL_SCORE:
6854 local_player->score = action_arg_number_new;
6856 DrawGameValue_Score(local_player->score);
6861 case CA_SET_LEVEL_GEMS:
6863 local_player->gems_still_needed = action_arg_number_new;
6865 DrawGameValue_Emeralds(local_player->gems_still_needed);
6870 case CA_SET_LEVEL_GRAVITY:
6872 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
6873 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
6874 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
6879 case CA_SET_LEVEL_WIND:
6881 game.wind_direction = action_arg_direction;
6886 /* ---------- player actions ------------------------------------------ */
6888 case CA_MOVE_PLAYER:
6890 /* automatically move to the next field in specified direction */
6891 for (i = 0; i < MAX_PLAYERS; i++)
6892 if (trigger_player_bits & (1 << i))
6893 stored_player[i].programmed_action = action_arg_direction;
6898 case CA_EXIT_PLAYER:
6900 for (i = 0; i < MAX_PLAYERS; i++)
6901 if (action_arg_player_bits & (1 << i))
6902 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
6907 case CA_KILL_PLAYER:
6909 for (i = 0; i < MAX_PLAYERS; i++)
6910 if (action_arg_player_bits & (1 << i))
6911 KillPlayer(&stored_player[i]);
6916 case CA_SET_PLAYER_KEYS:
6918 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
6919 int element = getSpecialActionElement(action_arg_element,
6920 action_arg_number, EL_KEY_1);
6922 if (IS_KEY(element))
6924 for (i = 0; i < MAX_PLAYERS; i++)
6926 if (trigger_player_bits & (1 << i))
6928 stored_player[i].key[KEY_NR(element)] = key_state;
6930 DrawGameValue_Keys(stored_player[i].key);
6932 redraw_mask |= REDRAW_DOOR_1;
6940 case CA_SET_PLAYER_SPEED:
6942 for (i = 0; i < MAX_PLAYERS; i++)
6944 if (trigger_player_bits & (1 << i))
6946 int move_stepsize = TILEX / stored_player[i].move_delay_value;
6948 if (action_arg == CA_ARG_SPEED_SLOWER ||
6949 action_arg == CA_ARG_SPEED_FASTER)
6951 action_arg_number = 2;
6952 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
6957 getModifiedActionNumber(move_stepsize,
6960 action_arg_number_min,
6961 action_arg_number_max);
6963 /* make sure that value is power of 2 */
6964 move_stepsize = (1 << log_2(move_stepsize));
6966 /* do no immediately change -- the player might just be moving */
6967 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
6969 stored_player[i].cannot_move =
6970 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
6977 case CA_SET_PLAYER_SHIELD:
6979 for (i = 0; i < MAX_PLAYERS; i++)
6981 if (trigger_player_bits & (1 << i))
6983 if (action_arg == CA_ARG_SHIELD_OFF)
6985 stored_player[i].shield_normal_time_left = 0;
6986 stored_player[i].shield_deadly_time_left = 0;
6988 else if (action_arg == CA_ARG_SHIELD_NORMAL)
6990 stored_player[i].shield_normal_time_left = 999999;
6992 else if (action_arg == CA_ARG_SHIELD_DEADLY)
6994 stored_player[i].shield_normal_time_left = 999999;
6995 stored_player[i].shield_deadly_time_left = 999999;
7003 case CA_SET_PLAYER_ARTWORK:
7005 for (i = 0; i < MAX_PLAYERS; i++)
7007 if (trigger_player_bits & (1 << i))
7009 int artwork_element = action_arg_element;
7011 if (action_arg == CA_ARG_ELEMENT_RESET)
7012 artwork_element = stored_player[i].element_nr;
7014 stored_player[i].artwork_element = artwork_element;
7016 SetPlayerWaiting(&stored_player[i], FALSE);
7018 /* set number of special actions for bored and sleeping animation */
7019 stored_player[i].num_special_action_bored =
7020 get_num_special_action(artwork_element,
7021 ACTION_BORING_1, ACTION_BORING_LAST);
7022 stored_player[i].num_special_action_sleeping =
7023 get_num_special_action(artwork_element,
7024 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7031 /* ---------- CE actions ---------------------------------------------- */
7033 case CA_SET_CE_SCORE:
7035 ei->collect_score = action_arg_number_new;
7040 case CA_SET_CE_VALUE:
7042 #if USE_NEW_CUSTOM_VALUE
7043 int last_custom_value = CustomValue[x][y];
7045 CustomValue[x][y] = action_arg_number_new;
7048 printf("::: Count == %d\n", CustomValue[x][y]);
7051 if (CustomValue[x][y] == 0 && last_custom_value > 0)
7054 printf("::: CE_VALUE_GETS_ZERO\n");
7057 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7058 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7070 static void ChangeElementNowExt(struct ElementChangeInfo *change,
7071 int x, int y, int target_element)
7073 int previous_move_direction = MovDir[x][y];
7074 #if USE_NEW_CUSTOM_VALUE
7075 int last_ce_value = CustomValue[x][y];
7077 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7078 IS_WALKABLE(Feld[x][y]));
7080 /* check if element under player changes from accessible to unaccessible
7081 (needed for special case of dropping element which then changes) */
7082 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7083 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7091 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7092 RemoveMovingField(x, y);
7096 Feld[x][y] = target_element;
7098 ResetGfxAnimation(x, y);
7099 ResetRandomAnimationValue(x, y);
7101 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7102 MovDir[x][y] = previous_move_direction;
7104 #if USE_NEW_CUSTOM_VALUE
7105 if (element_info[Feld[x][y]].use_last_ce_value)
7106 CustomValue[x][y] = last_ce_value;
7109 InitField_WithBug1(x, y, FALSE);
7111 DrawLevelField(x, y);
7113 if (GFX_CRUMBLED(Feld[x][y]))
7114 DrawLevelFieldCrumbledSandNeighbours(x, y);
7117 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7118 if (ELEM_IS_PLAYER(target_element))
7119 RelocatePlayer(x, y, target_element);
7122 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7124 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7127 TestIfBadThingTouchesPlayer(x, y);
7128 TestIfPlayerTouchesCustomElement(x, y);
7129 TestIfElementTouchesCustomElement(x, y);
7132 static boolean ChangeElementNow(int x, int y, int element, int page)
7134 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7136 int old_element = Feld[x][y];
7138 /* always use default change event to prevent running into a loop */
7139 if (ChangeEvent[x][y] == -1)
7140 ChangeEvent[x][y] = CE_DELAY;
7142 if (ChangeEvent[x][y] == CE_DELAY)
7144 /* reset actual trigger element, trigger player and action element */
7145 change->actual_trigger_element = EL_EMPTY;
7146 change->actual_trigger_player = EL_PLAYER_1;
7147 change->actual_trigger_side = CH_SIDE_NONE;
7148 change->actual_trigger_ce_value = 0;
7152 /* do not change any elements that have already changed in this frame */
7156 /* do not change already changed elements with same change event */
7157 if (Changed[x][y] & ChangeEvent[x][y])
7162 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7164 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7167 if (change->explode)
7174 if (change->use_target_content)
7176 boolean complete_replace = TRUE;
7177 boolean can_replace[3][3];
7180 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7183 boolean is_walkable;
7184 boolean is_diggable;
7185 boolean is_collectible;
7186 boolean is_removable;
7187 boolean is_destructible;
7188 int ex = x + xx - 1;
7189 int ey = y + yy - 1;
7190 int content_element = change->target_content.e[xx][yy];
7193 can_replace[xx][yy] = TRUE;
7195 if (ex == x && ey == y) /* do not check changing element itself */
7198 if (content_element == EL_EMPTY_SPACE)
7200 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7205 if (!IN_LEV_FIELD(ex, ey))
7207 can_replace[xx][yy] = FALSE;
7208 complete_replace = FALSE;
7215 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7216 e = MovingOrBlocked2Element(ex, ey);
7218 is_empty = (IS_FREE(ex, ey) ||
7219 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7221 is_walkable = (is_empty || IS_WALKABLE(e));
7222 is_diggable = (is_empty || IS_DIGGABLE(e));
7223 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7224 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7225 is_removable = (is_diggable || is_collectible);
7227 can_replace[xx][yy] =
7228 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7229 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7230 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7231 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7232 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7233 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7234 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7236 if (!can_replace[xx][yy])
7237 complete_replace = FALSE;
7240 if (!change->only_if_complete || complete_replace)
7242 boolean something_has_changed = FALSE;
7244 if (change->only_if_complete && change->use_random_replace &&
7245 RND(100) < change->random_percentage)
7248 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7250 int ex = x + xx - 1;
7251 int ey = y + yy - 1;
7252 int content_element;
7254 if (can_replace[xx][yy] && (!change->use_random_replace ||
7255 RND(100) < change->random_percentage))
7257 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7258 RemoveMovingField(ex, ey);
7260 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7262 content_element = change->target_content.e[xx][yy];
7263 target_element = GET_TARGET_ELEMENT(content_element, change);
7265 ChangeElementNowExt(change, ex, ey, target_element);
7267 something_has_changed = TRUE;
7269 /* for symmetry reasons, freeze newly created border elements */
7270 if (ex != x || ey != y)
7271 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7275 if (something_has_changed)
7277 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7278 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7284 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7286 ChangeElementNowExt(change, x, y, target_element);
7288 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7289 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7292 /* this uses direct change before indirect change */
7293 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
7298 #if USE_NEW_DELAYED_ACTION
7300 static void ChangeElement(int x, int y, int page)
7302 int element = MovingOrBlocked2Element(x, y);
7303 struct ElementInfo *ei = &element_info[element];
7304 struct ElementChangeInfo *change = &ei->change_page[page];
7307 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
7308 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
7311 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7312 x, y, element, element_info[element].token_name);
7313 printf("ChangeElement(): This should never happen!\n");
7318 /* this can happen with classic bombs on walkable, changing elements */
7319 if (!CAN_CHANGE_OR_HAS_ACTION(element))
7322 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7323 ChangeDelay[x][y] = 0;
7329 if (ChangeDelay[x][y] == 0) /* initialize element change */
7331 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7333 if (change->can_change)
7335 ResetGfxAnimation(x, y);
7336 ResetRandomAnimationValue(x, y);
7338 if (change->pre_change_function)
7339 change->pre_change_function(x, y);
7343 ChangeDelay[x][y]--;
7345 if (ChangeDelay[x][y] != 0) /* continue element change */
7347 if (change->can_change)
7349 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7351 if (IS_ANIMATED(graphic))
7352 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7354 if (change->change_function)
7355 change->change_function(x, y);
7358 else /* finish element change */
7360 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7362 page = ChangePage[x][y];
7363 ChangePage[x][y] = -1;
7365 change = &ei->change_page[page];
7368 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7370 ChangeDelay[x][y] = 1; /* try change after next move step */
7371 ChangePage[x][y] = page; /* remember page to use for change */
7376 if (change->can_change)
7378 if (ChangeElementNow(x, y, element, page))
7380 if (change->post_change_function)
7381 change->post_change_function(x, y);
7385 if (change->has_action)
7386 ExecuteCustomElementAction(x, y, element, page);
7392 static void ChangeElement(int x, int y, int page)
7394 int element = MovingOrBlocked2Element(x, y);
7395 struct ElementInfo *ei = &element_info[element];
7396 struct ElementChangeInfo *change = &ei->change_page[page];
7399 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7402 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7403 x, y, element, element_info[element].token_name);
7404 printf("ChangeElement(): This should never happen!\n");
7409 /* this can happen with classic bombs on walkable, changing elements */
7410 if (!CAN_CHANGE(element))
7413 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7414 ChangeDelay[x][y] = 0;
7420 if (ChangeDelay[x][y] == 0) /* initialize element change */
7422 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7424 ResetGfxAnimation(x, y);
7425 ResetRandomAnimationValue(x, y);
7427 if (change->pre_change_function)
7428 change->pre_change_function(x, y);
7431 ChangeDelay[x][y]--;
7433 if (ChangeDelay[x][y] != 0) /* continue element change */
7435 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7437 if (IS_ANIMATED(graphic))
7438 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7440 if (change->change_function)
7441 change->change_function(x, y);
7443 else /* finish element change */
7445 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7447 page = ChangePage[x][y];
7448 ChangePage[x][y] = -1;
7450 change = &ei->change_page[page];
7453 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7455 ChangeDelay[x][y] = 1; /* try change after next move step */
7456 ChangePage[x][y] = page; /* remember page to use for change */
7461 if (ChangeElementNow(x, y, element, page))
7463 if (change->post_change_function)
7464 change->post_change_function(x, y);
7471 static boolean CheckTriggeredElementChangeExt(int x, int y,
7472 int trigger_element,
7478 boolean change_done_any = FALSE;
7479 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7482 if (!(trigger_events[trigger_element][trigger_event]))
7485 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7487 int element = EL_CUSTOM_START + i;
7488 boolean change_done = FALSE;
7491 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7492 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7495 for (p = 0; p < element_info[element].num_change_pages; p++)
7497 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7499 if (change->can_change_or_has_action &&
7500 change->has_event[trigger_event] &&
7501 change->trigger_side & trigger_side &&
7502 change->trigger_player & trigger_player &&
7503 change->trigger_page & trigger_page_bits &&
7504 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7506 change->actual_trigger_element = trigger_element;
7507 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7508 change->actual_trigger_side = trigger_side;
7509 change->actual_trigger_ce_value = CustomValue[x][y];
7511 if ((change->can_change && !change_done) || change->has_action)
7515 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7517 if (Feld[x][y] == element)
7519 if (change->can_change && !change_done)
7521 ChangeDelay[x][y] = 1;
7522 ChangeEvent[x][y] = trigger_event;
7523 ChangeElement(x, y, p);
7525 #if USE_NEW_DELAYED_ACTION
7526 else if (change->has_action)
7528 ExecuteCustomElementAction(x, y, element, p);
7529 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7532 if (change->has_action)
7534 ExecuteCustomElementAction(x, y, element, p);
7535 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7541 if (change->can_change)
7544 change_done_any = TRUE;
7551 return change_done_any;
7554 static boolean CheckElementChangeExt(int x, int y,
7556 int trigger_element,
7561 boolean change_done = FALSE;
7564 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7565 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7568 if (Feld[x][y] == EL_BLOCKED)
7570 Blocked2Moving(x, y, &x, &y);
7571 element = Feld[x][y];
7574 if (Feld[x][y] != element) /* check if element has already changed */
7577 for (p = 0; p < element_info[element].num_change_pages; p++)
7579 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7581 boolean check_trigger_element =
7582 (trigger_event == CE_TOUCHING_X ||
7583 trigger_event == CE_HITTING_X ||
7584 trigger_event == CE_HIT_BY_X);
7586 if (change->can_change_or_has_action &&
7587 change->has_event[trigger_event] &&
7588 change->trigger_side & trigger_side &&
7589 change->trigger_player & trigger_player &&
7590 (!check_trigger_element ||
7591 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
7593 change->actual_trigger_element = trigger_element;
7594 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7595 change->actual_trigger_side = trigger_side;
7596 change->actual_trigger_ce_value = CustomValue[x][y];
7598 if (change->can_change && !change_done)
7600 ChangeDelay[x][y] = 1;
7601 ChangeEvent[x][y] = trigger_event;
7602 ChangeElement(x, y, p);
7606 #if USE_NEW_DELAYED_ACTION
7607 else if (change->has_action)
7609 ExecuteCustomElementAction(x, y, element, p);
7610 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7613 if (change->has_action)
7615 ExecuteCustomElementAction(x, y, element, p);
7616 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7625 static void PlayPlayerSound(struct PlayerInfo *player)
7627 int jx = player->jx, jy = player->jy;
7628 int sound_element = player->artwork_element;
7629 int last_action = player->last_action_waiting;
7630 int action = player->action_waiting;
7632 if (player->is_waiting)
7634 if (action != last_action)
7635 PlayLevelSoundElementAction(jx, jy, sound_element, action);
7637 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
7641 if (action != last_action)
7642 StopSound(element_info[sound_element].sound[last_action]);
7644 if (last_action == ACTION_SLEEPING)
7645 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
7649 static void PlayAllPlayersSound()
7653 for (i = 0; i < MAX_PLAYERS; i++)
7654 if (stored_player[i].active)
7655 PlayPlayerSound(&stored_player[i]);
7658 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7660 boolean last_waiting = player->is_waiting;
7661 int move_dir = player->MovDir;
7663 player->last_action_waiting = player->action_waiting;
7667 if (!last_waiting) /* not waiting -> waiting */
7669 player->is_waiting = TRUE;
7671 player->frame_counter_bored =
7673 game.player_boring_delay_fixed +
7674 SimpleRND(game.player_boring_delay_random);
7675 player->frame_counter_sleeping =
7677 game.player_sleeping_delay_fixed +
7678 SimpleRND(game.player_sleeping_delay_random);
7680 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7683 if (game.player_sleeping_delay_fixed +
7684 game.player_sleeping_delay_random > 0 &&
7685 player->anim_delay_counter == 0 &&
7686 player->post_delay_counter == 0 &&
7687 FrameCounter >= player->frame_counter_sleeping)
7688 player->is_sleeping = TRUE;
7689 else if (game.player_boring_delay_fixed +
7690 game.player_boring_delay_random > 0 &&
7691 FrameCounter >= player->frame_counter_bored)
7692 player->is_bored = TRUE;
7694 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7695 player->is_bored ? ACTION_BORING :
7698 if (player->is_sleeping)
7700 if (player->num_special_action_sleeping > 0)
7702 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7704 int last_special_action = player->special_action_sleeping;
7705 int num_special_action = player->num_special_action_sleeping;
7706 int special_action =
7707 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7708 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7709 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7710 last_special_action + 1 : ACTION_SLEEPING);
7711 int special_graphic =
7712 el_act_dir2img(player->artwork_element, special_action, move_dir);
7714 player->anim_delay_counter =
7715 graphic_info[special_graphic].anim_delay_fixed +
7716 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7717 player->post_delay_counter =
7718 graphic_info[special_graphic].post_delay_fixed +
7719 SimpleRND(graphic_info[special_graphic].post_delay_random);
7721 player->special_action_sleeping = special_action;
7724 if (player->anim_delay_counter > 0)
7726 player->action_waiting = player->special_action_sleeping;
7727 player->anim_delay_counter--;
7729 else if (player->post_delay_counter > 0)
7731 player->post_delay_counter--;
7735 else if (player->is_bored)
7737 if (player->num_special_action_bored > 0)
7739 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7741 int special_action =
7742 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7743 int special_graphic =
7744 el_act_dir2img(player->artwork_element, special_action, move_dir);
7746 player->anim_delay_counter =
7747 graphic_info[special_graphic].anim_delay_fixed +
7748 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7749 player->post_delay_counter =
7750 graphic_info[special_graphic].post_delay_fixed +
7751 SimpleRND(graphic_info[special_graphic].post_delay_random);
7753 player->special_action_bored = special_action;
7756 if (player->anim_delay_counter > 0)
7758 player->action_waiting = player->special_action_bored;
7759 player->anim_delay_counter--;
7761 else if (player->post_delay_counter > 0)
7763 player->post_delay_counter--;
7768 else if (last_waiting) /* waiting -> not waiting */
7770 player->is_waiting = FALSE;
7771 player->is_bored = FALSE;
7772 player->is_sleeping = FALSE;
7774 player->frame_counter_bored = -1;
7775 player->frame_counter_sleeping = -1;
7777 player->anim_delay_counter = 0;
7778 player->post_delay_counter = 0;
7780 player->action_waiting = ACTION_DEFAULT;
7782 player->special_action_bored = ACTION_DEFAULT;
7783 player->special_action_sleeping = ACTION_DEFAULT;
7787 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7789 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7790 int left = player_action & JOY_LEFT;
7791 int right = player_action & JOY_RIGHT;
7792 int up = player_action & JOY_UP;
7793 int down = player_action & JOY_DOWN;
7794 int button1 = player_action & JOY_BUTTON_1;
7795 int button2 = player_action & JOY_BUTTON_2;
7796 int dx = (left ? -1 : right ? 1 : 0);
7797 int dy = (up ? -1 : down ? 1 : 0);
7799 if (!player->active || tape.pausing)
7805 snapped = SnapField(player, dx, dy);
7809 dropped = DropElement(player);
7811 moved = MovePlayer(player, dx, dy);
7814 if (tape.single_step && tape.recording && !tape.pausing)
7816 if (button1 || (dropped && !moved))
7818 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7819 SnapField(player, 0, 0); /* stop snapping */
7823 SetPlayerWaiting(player, FALSE);
7825 return player_action;
7829 /* no actions for this player (no input at player's configured device) */
7831 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7832 SnapField(player, 0, 0);
7833 CheckGravityMovementWhenNotMoving(player);
7835 if (player->MovPos == 0)
7836 SetPlayerWaiting(player, TRUE);
7838 if (player->MovPos == 0) /* needed for tape.playing */
7839 player->is_moving = FALSE;
7841 player->is_dropping = FALSE;
7847 void AdvanceFrameAndPlayerCounters(int player_nr)
7851 /* advance frame counters (global frame counter and time frame counter) */
7855 /* advance player counters (counters for move delay, move animation etc.) */
7856 for (i = 0; i < MAX_PLAYERS; i++)
7858 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
7859 int move_delay_value = stored_player[i].move_delay_value;
7860 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
7862 if (!advance_player_counters) /* not all players may be affected */
7865 #if USE_NEW_PLAYER_ANIM
7866 if (move_frames == 0) /* less than one move per game frame */
7868 int stepsize = TILEX / move_delay_value;
7869 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
7870 int count = (stored_player[i].is_moving ?
7871 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
7873 if (count % delay == 0)
7878 stored_player[i].Frame += move_frames;
7880 if (stored_player[i].MovPos != 0)
7881 stored_player[i].StepFrame += move_frames;
7883 if (stored_player[i].move_delay > 0)
7884 stored_player[i].move_delay--;
7886 /* due to bugs in previous versions, counter must count up, not down */
7887 if (stored_player[i].push_delay != -1)
7888 stored_player[i].push_delay++;
7890 if (stored_player[i].drop_delay > 0)
7891 stored_player[i].drop_delay--;
7897 static unsigned long game_frame_delay = 0;
7898 unsigned long game_frame_delay_value;
7899 int magic_wall_x = 0, magic_wall_y = 0;
7900 int i, x, y, element, graphic;
7901 byte *recorded_player_action;
7902 byte summarized_player_action = 0;
7903 byte tape_action[MAX_PLAYERS];
7905 if (game_status != GAME_MODE_PLAYING)
7908 game_frame_delay_value =
7909 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7911 if (tape.playing && tape.warp_forward && !tape.pausing)
7912 game_frame_delay_value = 0;
7914 /* ---------- main game synchronization point ---------- */
7916 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
7918 if (network_playing && !network_player_action_received)
7920 /* try to get network player actions in time */
7922 #if defined(NETWORK_AVALIABLE)
7923 /* last chance to get network player actions without main loop delay */
7927 /* game was quit by network peer */
7928 if (game_status != GAME_MODE_PLAYING)
7931 if (!network_player_action_received)
7932 return; /* failed to get network player actions in time */
7938 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7941 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
7942 if (recorded_player_action == NULL && tape.pausing)
7946 for (i = 0; i < MAX_PLAYERS; i++)
7948 summarized_player_action |= stored_player[i].action;
7950 if (!network_playing)
7951 stored_player[i].effective_action = stored_player[i].action;
7954 #if defined(NETWORK_AVALIABLE)
7955 if (network_playing)
7956 SendToServer_MovePlayer(summarized_player_action);
7959 if (!options.network && !setup.team_mode)
7960 local_player->effective_action = summarized_player_action;
7962 if (recorded_player_action != NULL)
7963 for (i = 0; i < MAX_PLAYERS; i++)
7964 stored_player[i].effective_action = recorded_player_action[i];
7966 for (i = 0; i < MAX_PLAYERS; i++)
7968 tape_action[i] = stored_player[i].effective_action;
7970 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7971 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7974 /* only save actions from input devices, but not programmed actions */
7976 TapeRecordAction(tape_action);
7978 for (i = 0; i < MAX_PLAYERS; i++)
7980 int actual_player_action = stored_player[i].effective_action;
7983 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
7984 - rnd_equinox_tetrachloride 048
7985 - rnd_equinox_tetrachloride_ii 096
7986 - rnd_emanuel_schmieg 002
7987 - doctor_sloan_ww 001, 020
7989 if (stored_player[i].MovPos == 0)
7990 CheckGravityMovement(&stored_player[i]);
7993 /* overwrite programmed action with tape action */
7994 if (stored_player[i].programmed_action)
7995 actual_player_action = stored_player[i].programmed_action;
7998 PlayerActions(&stored_player[i], actual_player_action);
8000 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8002 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8003 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8006 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8009 network_player_action_received = FALSE;
8011 ScrollScreen(NULL, SCROLL_GO_ON);
8013 /* for backwards compatibility, the following code emulates a fixed bug that
8014 occured when pushing elements (causing elements that just made their last
8015 pushing step to already (if possible) make their first falling step in the
8016 same game frame, which is bad); this code is also needed to use the famous
8017 "spring push bug" which is used in older levels and might be wanted to be
8018 used also in newer levels, but in this case the buggy pushing code is only
8019 affecting the "spring" element and no other elements */
8021 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8023 for (i = 0; i < MAX_PLAYERS; i++)
8025 struct PlayerInfo *player = &stored_player[i];
8029 if (player->active && player->is_pushing && player->is_moving &&
8031 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8032 Feld[x][y] == EL_SPRING))
8034 ContinueMoving(x, y);
8036 /* continue moving after pushing (this is actually a bug) */
8037 if (!IS_MOVING(x, y))
8045 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8047 Changed[x][y] = FALSE;
8048 ChangeEvent[x][y] = -1;
8050 /* this must be handled before main playfield loop */
8051 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8054 if (MovDelay[x][y] <= 0)
8058 #if USE_NEW_SNAP_DELAY
8059 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
8062 if (MovDelay[x][y] <= 0)
8065 DrawLevelField(x, y);
8067 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8073 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8075 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8076 printf("GameActions(): This should never happen!\n");
8078 ChangePage[x][y] = -1;
8083 if (WasJustMoving[x][y] > 0)
8084 WasJustMoving[x][y]--;
8085 if (WasJustFalling[x][y] > 0)
8086 WasJustFalling[x][y]--;
8087 if (CheckCollision[x][y] > 0)
8088 CheckCollision[x][y]--;
8092 /* reset finished pushing action (not done in ContinueMoving() to allow
8093 continous pushing animation for elements with zero push delay) */
8094 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8096 ResetGfxAnimation(x, y);
8097 DrawLevelField(x, y);
8101 if (IS_BLOCKED(x, y))
8105 Blocked2Moving(x, y, &oldx, &oldy);
8106 if (!IS_MOVING(oldx, oldy))
8108 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8109 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8110 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8111 printf("GameActions(): This should never happen!\n");
8117 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8119 element = Feld[x][y];
8120 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8122 if (graphic_info[graphic].anim_global_sync)
8123 GfxFrame[x][y] = FrameCounter;
8125 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8126 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8127 ResetRandomAnimationValue(x, y);
8129 SetRandomAnimationValue(x, y);
8131 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8133 if (IS_INACTIVE(element))
8135 if (IS_ANIMATED(graphic))
8136 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8141 /* this may take place after moving, so 'element' may have changed */
8142 if (IS_CHANGING(x, y) &&
8143 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8145 int page = element_info[element].event_page_nr[CE_DELAY];
8147 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
8151 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8155 ChangeElement(x, y, page);
8157 if (CAN_CHANGE(element))
8158 ChangeElement(x, y, page);
8160 if (HAS_ACTION(element))
8161 ExecuteCustomElementAction(x, y, element, page);
8166 element = Feld[x][y];
8167 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8170 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8174 element = Feld[x][y];
8175 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8177 if (IS_ANIMATED(graphic) &&
8180 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8182 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8183 EdelsteinFunkeln(x, y);
8185 else if ((element == EL_ACID ||
8186 element == EL_EXIT_OPEN ||
8187 element == EL_SP_EXIT_OPEN ||
8188 element == EL_SP_TERMINAL ||
8189 element == EL_SP_TERMINAL_ACTIVE ||
8190 element == EL_EXTRA_TIME ||
8191 element == EL_SHIELD_NORMAL ||
8192 element == EL_SHIELD_DEADLY) &&
8193 IS_ANIMATED(graphic))
8194 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8195 else if (IS_MOVING(x, y))
8196 ContinueMoving(x, y);
8197 else if (IS_ACTIVE_BOMB(element))
8198 CheckDynamite(x, y);
8199 else if (element == EL_AMOEBA_GROWING)
8200 AmoebeWaechst(x, y);
8201 else if (element == EL_AMOEBA_SHRINKING)
8202 AmoebaDisappearing(x, y);
8204 #if !USE_NEW_AMOEBA_CODE
8205 else if (IS_AMOEBALIVE(element))
8206 AmoebeAbleger(x, y);
8209 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8211 else if (element == EL_EXIT_CLOSED)
8213 else if (element == EL_SP_EXIT_CLOSED)
8215 else if (element == EL_EXPANDABLE_WALL_GROWING)
8217 else if (element == EL_EXPANDABLE_WALL ||
8218 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8219 element == EL_EXPANDABLE_WALL_VERTICAL ||
8220 element == EL_EXPANDABLE_WALL_ANY)
8222 else if (element == EL_FLAMES)
8223 CheckForDragon(x, y);
8224 else if (element == EL_EXPLOSION)
8225 ; /* drawing of correct explosion animation is handled separately */
8226 else if (element == EL_ELEMENT_SNAPPING)
8229 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
8231 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8234 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8235 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8237 if (IS_BELT_ACTIVE(element))
8238 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8240 if (game.magic_wall_active)
8242 int jx = local_player->jx, jy = local_player->jy;
8244 /* play the element sound at the position nearest to the player */
8245 if ((element == EL_MAGIC_WALL_FULL ||
8246 element == EL_MAGIC_WALL_ACTIVE ||
8247 element == EL_MAGIC_WALL_EMPTYING ||
8248 element == EL_BD_MAGIC_WALL_FULL ||
8249 element == EL_BD_MAGIC_WALL_ACTIVE ||
8250 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8251 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8259 #if USE_NEW_AMOEBA_CODE
8260 /* new experimental amoeba growth stuff */
8261 if (!(FrameCounter % 8))
8263 static unsigned long random = 1684108901;
8265 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8267 x = RND(lev_fieldx);
8268 y = RND(lev_fieldy);
8269 element = Feld[x][y];
8271 if (!IS_PLAYER(x,y) &&
8272 (element == EL_EMPTY ||
8273 CAN_GROW_INTO(element) ||
8274 element == EL_QUICKSAND_EMPTY ||
8275 element == EL_ACID_SPLASH_LEFT ||
8276 element == EL_ACID_SPLASH_RIGHT))
8278 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8279 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8280 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8281 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8282 Feld[x][y] = EL_AMOEBA_DROP;
8285 random = random * 129 + 1;
8291 if (game.explosions_delayed)
8294 game.explosions_delayed = FALSE;
8296 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8298 element = Feld[x][y];
8300 if (ExplodeField[x][y])
8301 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8302 else if (element == EL_EXPLOSION)
8303 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8305 ExplodeField[x][y] = EX_TYPE_NONE;
8308 game.explosions_delayed = TRUE;
8311 if (game.magic_wall_active)
8313 if (!(game.magic_wall_time_left % 4))
8315 int element = Feld[magic_wall_x][magic_wall_y];
8317 if (element == EL_BD_MAGIC_WALL_FULL ||
8318 element == EL_BD_MAGIC_WALL_ACTIVE ||
8319 element == EL_BD_MAGIC_WALL_EMPTYING)
8320 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8322 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8325 if (game.magic_wall_time_left > 0)
8327 game.magic_wall_time_left--;
8328 if (!game.magic_wall_time_left)
8330 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8332 element = Feld[x][y];
8334 if (element == EL_MAGIC_WALL_ACTIVE ||
8335 element == EL_MAGIC_WALL_FULL)
8337 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8338 DrawLevelField(x, y);
8340 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8341 element == EL_BD_MAGIC_WALL_FULL)
8343 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8344 DrawLevelField(x, y);
8348 game.magic_wall_active = FALSE;
8353 if (game.light_time_left > 0)
8355 game.light_time_left--;
8357 if (game.light_time_left == 0)
8358 RedrawAllLightSwitchesAndInvisibleElements();
8361 if (game.timegate_time_left > 0)
8363 game.timegate_time_left--;
8365 if (game.timegate_time_left == 0)
8366 CloseAllOpenTimegates();
8369 if (game.lenses_time_left > 0)
8371 game.lenses_time_left--;
8373 if (game.lenses_time_left == 0)
8374 RedrawAllInvisibleElementsForLenses();
8377 if (game.magnify_time_left > 0)
8379 game.magnify_time_left--;
8381 if (game.magnify_time_left == 0)
8382 RedrawAllInvisibleElementsForMagnifier();
8385 for (i = 0; i < MAX_PLAYERS; i++)
8387 struct PlayerInfo *player = &stored_player[i];
8389 if (SHIELD_ON(player))
8391 if (player->shield_deadly_time_left)
8392 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8393 else if (player->shield_normal_time_left)
8394 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8398 if (TimeFrames >= FRAMES_PER_SECOND)
8403 for (i = 0; i < MAX_PLAYERS; i++)
8405 struct PlayerInfo *player = &stored_player[i];
8407 if (SHIELD_ON(player))
8409 player->shield_normal_time_left--;
8411 if (player->shield_deadly_time_left > 0)
8412 player->shield_deadly_time_left--;
8416 if (!level.use_step_counter)
8424 if (TimeLeft <= 10 && setup.time_limit)
8425 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8427 DrawGameValue_Time(TimeLeft);
8429 if (!TimeLeft && setup.time_limit)
8430 for (i = 0; i < MAX_PLAYERS; i++)
8431 KillPlayer(&stored_player[i]);
8433 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8434 DrawGameValue_Time(TimePlayed);
8437 if (tape.recording || tape.playing)
8438 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8442 PlayAllPlayersSound();
8444 if (options.debug) /* calculate frames per second */
8446 static unsigned long fps_counter = 0;
8447 static int fps_frames = 0;
8448 unsigned long fps_delay_ms = Counter() - fps_counter;
8452 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8454 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8457 fps_counter = Counter();
8460 redraw_mask |= REDRAW_FPS;
8463 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
8465 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8467 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8469 local_player->show_envelope = 0;
8472 /* use random number generator in every frame to make it less predictable */
8473 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8477 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8479 int min_x = x, min_y = y, max_x = x, max_y = y;
8482 for (i = 0; i < MAX_PLAYERS; i++)
8484 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8486 if (!stored_player[i].active || &stored_player[i] == player)
8489 min_x = MIN(min_x, jx);
8490 min_y = MIN(min_y, jy);
8491 max_x = MAX(max_x, jx);
8492 max_y = MAX(max_y, jy);
8495 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8498 static boolean AllPlayersInVisibleScreen()
8502 for (i = 0; i < MAX_PLAYERS; i++)
8504 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8506 if (!stored_player[i].active)
8509 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8516 void ScrollLevel(int dx, int dy)
8518 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8521 BlitBitmap(drawto_field, drawto_field,
8522 FX + TILEX * (dx == -1) - softscroll_offset,
8523 FY + TILEY * (dy == -1) - softscroll_offset,
8524 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8525 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8526 FX + TILEX * (dx == 1) - softscroll_offset,
8527 FY + TILEY * (dy == 1) - softscroll_offset);
8531 x = (dx == 1 ? BX1 : BX2);
8532 for (y = BY1; y <= BY2; y++)
8533 DrawScreenField(x, y);
8538 y = (dy == 1 ? BY1 : BY2);
8539 for (x = BX1; x <= BX2; x++)
8540 DrawScreenField(x, y);
8543 redraw_mask |= REDRAW_FIELD;
8546 static boolean canFallDown(struct PlayerInfo *player)
8548 int jx = player->jx, jy = player->jy;
8550 return (IN_LEV_FIELD(jx, jy + 1) &&
8551 (IS_FREE(jx, jy + 1) ||
8552 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8553 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8554 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8557 static boolean canPassField(int x, int y, int move_dir)
8559 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8560 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8561 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8564 int element = Feld[x][y];
8566 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8567 !CAN_MOVE(element) &&
8568 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8569 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8570 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8573 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8575 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8576 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8577 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8581 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8582 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
8583 (IS_DIGGABLE(Feld[newx][newy]) ||
8584 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8585 canPassField(newx, newy, move_dir)));
8588 static void CheckGravityMovement(struct PlayerInfo *player)
8590 if (game.gravity && !player->programmed_action)
8592 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8593 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8594 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8595 int jx = player->jx, jy = player->jy;
8596 boolean player_is_moving_to_valid_field =
8597 (!player_is_snapping &&
8598 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8599 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8600 boolean player_can_fall_down = canFallDown(player);
8602 if (player_can_fall_down &&
8603 !player_is_moving_to_valid_field)
8604 player->programmed_action = MV_DOWN;
8608 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8610 return CheckGravityMovement(player);
8612 if (game.gravity && !player->programmed_action)
8614 int jx = player->jx, jy = player->jy;
8615 boolean field_under_player_is_free =
8616 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8617 boolean player_is_standing_on_valid_field =
8618 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8619 (IS_WALKABLE(Feld[jx][jy]) &&
8620 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8622 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8623 player->programmed_action = MV_DOWN;
8629 -----------------------------------------------------------------------------
8630 dx, dy: direction (non-diagonal) to try to move the player to
8631 real_dx, real_dy: direction as read from input device (can be diagonal)
8634 boolean MovePlayerOneStep(struct PlayerInfo *player,
8635 int dx, int dy, int real_dx, int real_dy)
8637 int jx = player->jx, jy = player->jy;
8638 int new_jx = jx + dx, new_jy = jy + dy;
8642 if (!player->active || (!dx && !dy))
8643 return MF_NO_ACTION;
8645 player->MovDir = (dx < 0 ? MV_LEFT :
8648 dy > 0 ? MV_DOWN : MV_NONE);
8650 if (!IN_LEV_FIELD(new_jx, new_jy))
8651 return MF_NO_ACTION;
8653 if (player->cannot_move)
8655 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8656 SnapField(player, 0, 0);
8658 return MF_NO_ACTION;
8661 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8662 return MF_NO_ACTION;
8664 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8666 if (DONT_RUN_INTO(element))
8668 if (element == EL_ACID && dx == 0 && dy == 1)
8670 SplashAcid(new_jx, new_jy);
8671 Feld[jx][jy] = EL_PLAYER_1;
8672 InitMovingField(jx, jy, MV_DOWN);
8673 Store[jx][jy] = EL_ACID;
8674 ContinueMoving(jx, jy);
8678 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
8683 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8684 if (can_move != MF_MOVING)
8687 /* check if DigField() has caused relocation of the player */
8688 if (player->jx != jx || player->jy != jy)
8689 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
8691 StorePlayer[jx][jy] = 0;
8692 player->last_jx = jx;
8693 player->last_jy = jy;
8694 player->jx = new_jx;
8695 player->jy = new_jy;
8696 StorePlayer[new_jx][new_jy] = player->element_nr;
8698 if (player->move_delay_value_next != -1)
8700 player->move_delay_value = player->move_delay_value_next;
8701 player->move_delay_value_next = -1;
8705 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8707 player->step_counter++;
8709 PlayerVisit[jx][jy] = FrameCounter;
8711 ScrollPlayer(player, SCROLL_INIT);
8716 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8718 int jx = player->jx, jy = player->jy;
8719 int old_jx = jx, old_jy = jy;
8720 int moved = MF_NO_ACTION;
8722 if (!player->active)
8727 if (player->MovPos == 0)
8729 player->is_moving = FALSE;
8730 player->is_digging = FALSE;
8731 player->is_collecting = FALSE;
8732 player->is_snapping = FALSE;
8733 player->is_pushing = FALSE;
8739 if (player->move_delay > 0)
8742 player->move_delay = -1; /* set to "uninitialized" value */
8744 /* store if player is automatically moved to next field */
8745 player->is_auto_moving = (player->programmed_action != MV_NONE);
8747 /* remove the last programmed player action */
8748 player->programmed_action = 0;
8752 /* should only happen if pre-1.2 tape recordings are played */
8753 /* this is only for backward compatibility */
8755 int original_move_delay_value = player->move_delay_value;
8758 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8762 /* scroll remaining steps with finest movement resolution */
8763 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8765 while (player->MovPos)
8767 ScrollPlayer(player, SCROLL_GO_ON);
8768 ScrollScreen(NULL, SCROLL_GO_ON);
8770 AdvanceFrameAndPlayerCounters(player->index_nr);
8776 player->move_delay_value = original_move_delay_value;
8779 if (player->last_move_dir & MV_HORIZONTAL)
8781 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8782 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8786 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8787 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8793 if (moved & MF_MOVING && !ScreenMovPos &&
8794 (player == local_player || !options.network))
8796 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8797 int offset = (setup.scroll_delay ? 3 : 0);
8799 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8801 /* actual player has left the screen -- scroll in that direction */
8802 if (jx != old_jx) /* player has moved horizontally */
8803 scroll_x += (jx - old_jx);
8804 else /* player has moved vertically */
8805 scroll_y += (jy - old_jy);
8809 if (jx != old_jx) /* player has moved horizontally */
8811 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8812 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8813 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8815 /* don't scroll over playfield boundaries */
8816 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8817 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8819 /* don't scroll more than one field at a time */
8820 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8822 /* don't scroll against the player's moving direction */
8823 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8824 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8825 scroll_x = old_scroll_x;
8827 else /* player has moved vertically */
8829 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8830 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8831 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8833 /* don't scroll over playfield boundaries */
8834 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8835 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8837 /* don't scroll more than one field at a time */
8838 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8840 /* don't scroll against the player's moving direction */
8841 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8842 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8843 scroll_y = old_scroll_y;
8847 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8849 if (!options.network && !AllPlayersInVisibleScreen())
8851 scroll_x = old_scroll_x;
8852 scroll_y = old_scroll_y;
8856 ScrollScreen(player, SCROLL_INIT);
8857 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8862 player->StepFrame = 0;
8864 if (moved & MF_MOVING)
8866 if (old_jx != jx && old_jy == jy)
8867 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8868 else if (old_jx == jx && old_jy != jy)
8869 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8871 DrawLevelField(jx, jy); /* for "crumbled sand" */
8873 player->last_move_dir = player->MovDir;
8874 player->is_moving = TRUE;
8875 player->is_snapping = FALSE;
8876 player->is_switching = FALSE;
8877 player->is_dropping = FALSE;
8881 CheckGravityMovementWhenNotMoving(player);
8883 player->is_moving = FALSE;
8885 /* at this point, the player is allowed to move, but cannot move right now
8886 (e.g. because of something blocking the way) -- ensure that the player
8887 is also allowed to move in the next frame (in old versions before 3.1.1,
8888 the player was forced to wait again for eight frames before next try) */
8890 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8891 player->move_delay = 0; /* allow direct movement in the next frame */
8894 if (player->move_delay == -1) /* not yet initialized by DigField() */
8895 player->move_delay = player->move_delay_value;
8897 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8899 TestIfPlayerTouchesBadThing(jx, jy);
8900 TestIfPlayerTouchesCustomElement(jx, jy);
8903 if (!player->active)
8904 RemovePlayer(player);
8909 void ScrollPlayer(struct PlayerInfo *player, int mode)
8911 int jx = player->jx, jy = player->jy;
8912 int last_jx = player->last_jx, last_jy = player->last_jy;
8913 int move_stepsize = TILEX / player->move_delay_value;
8915 #if USE_NEW_PLAYER_SPEED
8916 if (!player->active)
8919 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
8922 if (!player->active || player->MovPos == 0)
8926 if (mode == SCROLL_INIT)
8928 player->actual_frame_counter = FrameCounter;
8929 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8931 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
8932 Feld[last_jx][last_jy] == EL_EMPTY)
8934 int last_field_block_delay = 0; /* start with no blocking at all */
8935 int block_delay_adjustment = player->block_delay_adjustment;
8937 /* if player blocks last field, add delay for exactly one move */
8938 if (player->block_last_field)
8940 last_field_block_delay += player->move_delay_value;
8942 /* when blocking enabled, prevent moving up despite gravity */
8943 if (game.gravity && player->MovDir == MV_UP)
8944 block_delay_adjustment = -1;
8947 /* add block delay adjustment (also possible when not blocking) */
8948 last_field_block_delay += block_delay_adjustment;
8950 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8951 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
8954 #if USE_NEW_PLAYER_SPEED
8955 if (player->MovPos != 0) /* player has not yet reached destination */
8961 else if (!FrameReached(&player->actual_frame_counter, 1))
8965 printf("::: player->MovPos: %d -> %d\n",
8967 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
8970 #if USE_NEW_PLAYER_SPEED
8971 if (player->MovPos != 0)
8973 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8974 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8976 /* before DrawPlayer() to draw correct player graphic for this case */
8977 if (player->MovPos == 0)
8978 CheckGravityMovement(player);
8981 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8982 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8984 /* before DrawPlayer() to draw correct player graphic for this case */
8985 if (player->MovPos == 0)
8986 CheckGravityMovement(player);
8989 if (player->MovPos == 0) /* player reached destination field */
8992 printf("::: player reached destination field\n");
8995 if (player->move_delay_reset_counter > 0)
8997 player->move_delay_reset_counter--;
8999 if (player->move_delay_reset_counter == 0)
9001 /* continue with normal speed after quickly moving through gate */
9002 HALVE_PLAYER_SPEED(player);
9004 /* be able to make the next move without delay */
9005 player->move_delay = 0;
9009 player->last_jx = jx;
9010 player->last_jy = jy;
9012 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9013 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9014 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9016 DrawPlayer(player); /* needed here only to cleanup last field */
9017 RemovePlayer(player);
9019 if (local_player->friends_still_needed == 0 ||
9020 IS_SP_ELEMENT(Feld[jx][jy]))
9021 player->LevelSolved = player->GameOver = TRUE;
9024 /* this breaks one level: "machine", level 000 */
9026 int move_direction = player->MovDir;
9027 int enter_side = MV_DIR_OPPOSITE(move_direction);
9028 int leave_side = move_direction;
9029 int old_jx = last_jx;
9030 int old_jy = last_jy;
9031 int old_element = Feld[old_jx][old_jy];
9032 int new_element = Feld[jx][jy];
9034 if (IS_CUSTOM_ELEMENT(old_element))
9035 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9037 player->index_bit, leave_side);
9039 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9041 player->index_bit, leave_side);
9043 if (IS_CUSTOM_ELEMENT(new_element))
9044 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9045 player->index_bit, enter_side);
9047 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9049 player->index_bit, enter_side);
9051 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
9052 CE_MOVE_OF_X, move_direction);
9055 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9057 TestIfPlayerTouchesBadThing(jx, jy);
9058 TestIfPlayerTouchesCustomElement(jx, jy);
9060 /* needed because pushed element has not yet reached its destination,
9061 so it would trigger a change event at its previous field location */
9062 if (!player->is_pushing)
9063 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9065 if (!player->active)
9066 RemovePlayer(player);
9069 if (level.use_step_counter)
9079 if (TimeLeft <= 10 && setup.time_limit)
9080 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9082 DrawGameValue_Time(TimeLeft);
9084 if (!TimeLeft && setup.time_limit)
9085 for (i = 0; i < MAX_PLAYERS; i++)
9086 KillPlayer(&stored_player[i]);
9088 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9089 DrawGameValue_Time(TimePlayed);
9092 if (tape.single_step && tape.recording && !tape.pausing &&
9093 !player->programmed_action)
9094 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9098 void ScrollScreen(struct PlayerInfo *player, int mode)
9100 static unsigned long screen_frame_counter = 0;
9102 if (mode == SCROLL_INIT)
9104 /* set scrolling step size according to actual player's moving speed */
9105 ScrollStepSize = TILEX / player->move_delay_value;
9107 screen_frame_counter = FrameCounter;
9108 ScreenMovDir = player->MovDir;
9109 ScreenMovPos = player->MovPos;
9110 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9113 else if (!FrameReached(&screen_frame_counter, 1))
9118 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9119 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9120 redraw_mask |= REDRAW_FIELD;
9123 ScreenMovDir = MV_NONE;
9126 void TestIfPlayerTouchesCustomElement(int x, int y)
9128 static int xy[4][2] =
9135 static int trigger_sides[4][2] =
9137 /* center side border side */
9138 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9139 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9140 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9141 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9143 static int touch_dir[4] =
9150 int center_element = Feld[x][y]; /* should always be non-moving! */
9153 for (i = 0; i < NUM_DIRECTIONS; i++)
9155 int xx = x + xy[i][0];
9156 int yy = y + xy[i][1];
9157 int center_side = trigger_sides[i][0];
9158 int border_side = trigger_sides[i][1];
9161 if (!IN_LEV_FIELD(xx, yy))
9164 if (IS_PLAYER(x, y))
9166 struct PlayerInfo *player = PLAYERINFO(x, y);
9168 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9169 border_element = Feld[xx][yy]; /* may be moving! */
9170 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9171 border_element = Feld[xx][yy];
9172 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9173 border_element = MovingOrBlocked2Element(xx, yy);
9175 continue; /* center and border element do not touch */
9177 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9178 player->index_bit, border_side);
9179 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9180 CE_PLAYER_TOUCHES_X,
9181 player->index_bit, border_side);
9183 else if (IS_PLAYER(xx, yy))
9185 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9187 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9189 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9190 continue; /* center and border element do not touch */
9193 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9194 player->index_bit, center_side);
9195 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9196 CE_PLAYER_TOUCHES_X,
9197 player->index_bit, center_side);
9203 void TestIfElementTouchesCustomElement(int x, int y)
9205 static int xy[4][2] =
9212 static int trigger_sides[4][2] =
9214 /* center side border side */
9215 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9216 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9217 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9218 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9220 static int touch_dir[4] =
9227 boolean change_center_element = FALSE;
9228 int center_element = Feld[x][y]; /* should always be non-moving! */
9231 for (i = 0; i < NUM_DIRECTIONS; i++)
9233 int xx = x + xy[i][0];
9234 int yy = y + xy[i][1];
9235 int center_side = trigger_sides[i][0];
9236 int border_side = trigger_sides[i][1];
9239 if (!IN_LEV_FIELD(xx, yy))
9242 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9243 border_element = Feld[xx][yy]; /* may be moving! */
9244 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9245 border_element = Feld[xx][yy];
9246 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9247 border_element = MovingOrBlocked2Element(xx, yy);
9249 continue; /* center and border element do not touch */
9251 /* check for change of center element (but change it only once) */
9252 if (!change_center_element)
9253 change_center_element =
9254 CheckElementChangeBySide(x, y, center_element, border_element,
9255 CE_TOUCHING_X, border_side);
9257 /* check for change of border element */
9258 CheckElementChangeBySide(xx, yy, border_element, center_element,
9259 CE_TOUCHING_X, center_side);
9263 void TestIfElementHitsCustomElement(int x, int y, int direction)
9265 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9266 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9267 int hitx = x + dx, hity = y + dy;
9268 int hitting_element = Feld[x][y];
9269 int touched_element;
9271 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9274 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9275 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9277 if (IN_LEV_FIELD(hitx, hity))
9279 int opposite_direction = MV_DIR_OPPOSITE(direction);
9280 int hitting_side = direction;
9281 int touched_side = opposite_direction;
9282 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9283 MovDir[hitx][hity] != direction ||
9284 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9290 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9291 CE_HITTING_X, touched_side);
9293 CheckElementChangeBySide(hitx, hity, touched_element,
9294 hitting_element, CE_HIT_BY_X, hitting_side);
9296 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9297 CE_HIT_BY_SOMETHING, opposite_direction);
9301 /* "hitting something" is also true when hitting the playfield border */
9302 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9303 CE_HITTING_SOMETHING, direction);
9307 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9309 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9310 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9311 int hitx = x + dx, hity = y + dy;
9312 int hitting_element = Feld[x][y];
9313 int touched_element;
9315 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9316 !IS_FREE(hitx, hity) &&
9317 (!IS_MOVING(hitx, hity) ||
9318 MovDir[hitx][hity] != direction ||
9319 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9322 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9326 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9330 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9331 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9333 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9334 EP_CAN_SMASH_EVERYTHING, direction);
9336 if (IN_LEV_FIELD(hitx, hity))
9338 int opposite_direction = MV_DIR_OPPOSITE(direction);
9339 int hitting_side = direction;
9340 int touched_side = opposite_direction;
9342 int touched_element = MovingOrBlocked2Element(hitx, hity);
9345 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9346 MovDir[hitx][hity] != direction ||
9347 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9356 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9357 CE_SMASHED_BY_SOMETHING, opposite_direction);
9359 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9360 CE_OTHER_IS_SMASHING, touched_side);
9362 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9363 CE_OTHER_GETS_SMASHED, hitting_side);
9369 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9371 int i, kill_x = -1, kill_y = -1;
9372 int bad_element = -1;
9373 static int test_xy[4][2] =
9380 static int test_dir[4] =
9388 for (i = 0; i < NUM_DIRECTIONS; i++)
9390 int test_x, test_y, test_move_dir, test_element;
9392 test_x = good_x + test_xy[i][0];
9393 test_y = good_y + test_xy[i][1];
9395 if (!IN_LEV_FIELD(test_x, test_y))
9399 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9401 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9403 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9404 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9406 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9407 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9411 bad_element = test_element;
9417 if (kill_x != -1 || kill_y != -1)
9419 if (IS_PLAYER(good_x, good_y))
9421 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9423 if (player->shield_deadly_time_left > 0 &&
9424 !IS_INDESTRUCTIBLE(bad_element))
9425 Bang(kill_x, kill_y);
9426 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9430 Bang(good_x, good_y);
9434 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9436 int i, kill_x = -1, kill_y = -1;
9437 int bad_element = Feld[bad_x][bad_y];
9438 static int test_xy[4][2] =
9445 static int touch_dir[4] =
9452 static int test_dir[4] =
9460 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9463 for (i = 0; i < NUM_DIRECTIONS; i++)
9465 int test_x, test_y, test_move_dir, test_element;
9467 test_x = bad_x + test_xy[i][0];
9468 test_y = bad_y + test_xy[i][1];
9469 if (!IN_LEV_FIELD(test_x, test_y))
9473 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9475 test_element = Feld[test_x][test_y];
9477 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9478 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9480 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9481 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9483 /* good thing is player or penguin that does not move away */
9484 if (IS_PLAYER(test_x, test_y))
9486 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9488 if (bad_element == EL_ROBOT && player->is_moving)
9489 continue; /* robot does not kill player if he is moving */
9491 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9493 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9494 continue; /* center and border element do not touch */
9501 else if (test_element == EL_PENGUIN)
9510 if (kill_x != -1 || kill_y != -1)
9512 if (IS_PLAYER(kill_x, kill_y))
9514 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9516 if (player->shield_deadly_time_left > 0 &&
9517 !IS_INDESTRUCTIBLE(bad_element))
9519 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9523 Bang(kill_x, kill_y);
9527 void TestIfPlayerTouchesBadThing(int x, int y)
9529 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9532 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
9534 TestIfGoodThingHitsBadThing(x, y, move_dir);
9537 void TestIfBadThingTouchesPlayer(int x, int y)
9539 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9542 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
9544 TestIfBadThingHitsGoodThing(x, y, move_dir);
9547 void TestIfFriendTouchesBadThing(int x, int y)
9549 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9552 void TestIfBadThingTouchesFriend(int x, int y)
9554 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9557 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9559 int i, kill_x = bad_x, kill_y = bad_y;
9560 static int xy[4][2] =
9568 for (i = 0; i < NUM_DIRECTIONS; i++)
9572 x = bad_x + xy[i][0];
9573 y = bad_y + xy[i][1];
9574 if (!IN_LEV_FIELD(x, y))
9577 element = Feld[x][y];
9578 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9579 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9587 if (kill_x != bad_x || kill_y != bad_y)
9591 void KillPlayer(struct PlayerInfo *player)
9593 int jx = player->jx, jy = player->jy;
9595 if (!player->active)
9598 /* remove accessible field at the player's position */
9599 Feld[jx][jy] = EL_EMPTY;
9601 /* deactivate shield (else Bang()/Explode() would not work right) */
9602 player->shield_normal_time_left = 0;
9603 player->shield_deadly_time_left = 0;
9609 static void KillPlayerUnlessEnemyProtected(int x, int y)
9611 if (!PLAYER_ENEMY_PROTECTED(x, y))
9612 KillPlayer(PLAYERINFO(x, y));
9615 static void KillPlayerUnlessExplosionProtected(int x, int y)
9617 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9618 KillPlayer(PLAYERINFO(x, y));
9621 void BuryPlayer(struct PlayerInfo *player)
9623 int jx = player->jx, jy = player->jy;
9625 if (!player->active)
9628 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
9629 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9631 player->GameOver = TRUE;
9632 RemovePlayer(player);
9635 void RemovePlayer(struct PlayerInfo *player)
9637 int jx = player->jx, jy = player->jy;
9638 int i, found = FALSE;
9640 player->present = FALSE;
9641 player->active = FALSE;
9643 if (!ExplodeField[jx][jy])
9644 StorePlayer[jx][jy] = 0;
9646 if (player->is_moving)
9647 DrawLevelField(player->last_jx, player->last_jy);
9649 for (i = 0; i < MAX_PLAYERS; i++)
9650 if (stored_player[i].active)
9654 AllPlayersGone = TRUE;
9660 #if USE_NEW_SNAP_DELAY
9661 static void setFieldForSnapping(int x, int y, int element, int direction)
9663 struct ElementInfo *ei = &element_info[element];
9664 int direction_bit = MV_DIR_BIT(direction);
9665 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
9666 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
9667 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
9669 Feld[x][y] = EL_ELEMENT_SNAPPING;
9670 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
9672 ResetGfxAnimation(x, y);
9674 GfxElement[x][y] = element;
9675 GfxAction[x][y] = action;
9676 GfxDir[x][y] = direction;
9677 GfxFrame[x][y] = -1;
9682 =============================================================================
9683 checkDiagonalPushing()
9684 -----------------------------------------------------------------------------
9685 check if diagonal input device direction results in pushing of object
9686 (by checking if the alternative direction is walkable, diggable, ...)
9687 =============================================================================
9690 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9691 int x, int y, int real_dx, int real_dy)
9693 int jx, jy, dx, dy, xx, yy;
9695 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9698 /* diagonal direction: check alternative direction */
9703 xx = jx + (dx == 0 ? real_dx : 0);
9704 yy = jy + (dy == 0 ? real_dy : 0);
9706 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9710 =============================================================================
9712 -----------------------------------------------------------------------------
9713 x, y: field next to player (non-diagonal) to try to dig to
9714 real_dx, real_dy: direction as read from input device (can be diagonal)
9715 =============================================================================
9718 int DigField(struct PlayerInfo *player,
9719 int oldx, int oldy, int x, int y,
9720 int real_dx, int real_dy, int mode)
9722 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
9723 boolean player_was_pushing = player->is_pushing;
9724 int jx = oldx, jy = oldy;
9725 int dx = x - jx, dy = y - jy;
9726 int nextx = x + dx, nexty = y + dy;
9727 int move_direction = (dx == -1 ? MV_LEFT :
9728 dx == +1 ? MV_RIGHT :
9730 dy == +1 ? MV_DOWN : MV_NONE);
9731 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9732 int dig_side = MV_DIR_OPPOSITE(move_direction);
9733 int old_element = Feld[jx][jy];
9737 if (is_player) /* function can also be called by EL_PENGUIN */
9739 if (player->MovPos == 0)
9741 player->is_digging = FALSE;
9742 player->is_collecting = FALSE;
9745 if (player->MovPos == 0) /* last pushing move finished */
9746 player->is_pushing = FALSE;
9748 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9750 player->is_switching = FALSE;
9751 player->push_delay = -1;
9753 return MF_NO_ACTION;
9757 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9758 return MF_NO_ACTION;
9760 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9761 old_element = Back[jx][jy];
9763 /* in case of element dropped at player position, check background */
9764 else if (Back[jx][jy] != EL_EMPTY &&
9765 game.engine_version >= VERSION_IDENT(2,2,0,0))
9766 old_element = Back[jx][jy];
9768 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
9769 return MF_NO_ACTION; /* field has no opening in this direction */
9771 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
9772 return MF_NO_ACTION; /* field has no opening in this direction */
9774 element = Feld[x][y];
9775 #if USE_NEW_CUSTOM_VALUE
9778 collect_count = element_info[element].collect_count_initial;
9780 collect_count = CustomValue[x][y];
9784 collect_count = element_info[element].collect_count_initial;
9788 if (element != EL_BLOCKED &&
9789 CustomValue[x][y] != element_info[element].collect_count_initial)
9790 printf("::: %d: %d != %d\n",
9793 element_info[element].collect_count_initial);
9796 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
9797 return MF_NO_ACTION;
9799 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9800 game.engine_version >= VERSION_IDENT(2,2,0,0))
9802 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
9803 player->index_bit, dig_side);
9804 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
9805 player->index_bit, dig_side);
9807 if (Feld[x][y] != element) /* field changed by snapping */
9810 return MF_NO_ACTION;
9813 if (game.gravity && is_player && !player->is_auto_moving &&
9814 canFallDown(player) && move_direction != MV_DOWN &&
9815 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
9816 return MF_NO_ACTION; /* player cannot walk here due to gravity */
9818 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
9820 int sound_element = SND_ELEMENT(element);
9821 int sound_action = ACTION_WALKING;
9823 if (IS_RND_GATE(element))
9825 if (!player->key[RND_GATE_NR(element)])
9826 return MF_NO_ACTION;
9828 else if (IS_RND_GATE_GRAY(element))
9830 if (!player->key[RND_GATE_GRAY_NR(element)])
9831 return MF_NO_ACTION;
9833 else if (IS_RND_GATE_GRAY_ACTIVE(element))
9835 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
9836 return MF_NO_ACTION;
9838 else if (element == EL_EXIT_OPEN ||
9839 element == EL_SP_EXIT_OPEN ||
9840 element == EL_SP_EXIT_OPENING)
9842 sound_action = ACTION_PASSING; /* player is passing exit */
9844 else if (element == EL_EMPTY)
9846 sound_action = ACTION_MOVING; /* nothing to walk on */
9849 /* play sound from background or player, whatever is available */
9850 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
9851 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
9853 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
9855 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
9857 if (!ACCESS_FROM(element, opposite_direction))
9858 return MF_NO_ACTION; /* field not accessible from this direction */
9860 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9861 return MF_NO_ACTION;
9863 if (IS_EM_GATE(element))
9865 if (!player->key[EM_GATE_NR(element)])
9866 return MF_NO_ACTION;
9868 else if (IS_EM_GATE_GRAY(element))
9870 if (!player->key[EM_GATE_GRAY_NR(element)])
9871 return MF_NO_ACTION;
9873 else if (IS_EM_GATE_GRAY_ACTIVE(element))
9875 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
9876 return MF_NO_ACTION;
9878 else if (IS_SP_PORT(element))
9880 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9881 element == EL_SP_GRAVITY_PORT_RIGHT ||
9882 element == EL_SP_GRAVITY_PORT_UP ||
9883 element == EL_SP_GRAVITY_PORT_DOWN)
9884 game.gravity = !game.gravity;
9885 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
9886 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
9887 element == EL_SP_GRAVITY_ON_PORT_UP ||
9888 element == EL_SP_GRAVITY_ON_PORT_DOWN)
9889 game.gravity = TRUE;
9890 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
9891 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
9892 element == EL_SP_GRAVITY_OFF_PORT_UP ||
9893 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
9894 game.gravity = FALSE;
9897 /* automatically move to the next field with double speed */
9898 player->programmed_action = move_direction;
9900 if (player->move_delay_reset_counter == 0)
9902 player->move_delay_reset_counter = 2; /* two double speed steps */
9904 DOUBLE_PLAYER_SPEED(player);
9907 PlayLevelSoundAction(x, y, ACTION_PASSING);
9909 else if (IS_DIGGABLE(element))
9913 if (mode != DF_SNAP)
9915 GfxElement[x][y] = GFX_ELEMENT(element);
9916 player->is_digging = TRUE;
9919 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9921 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
9922 player->index_bit, dig_side);
9924 if (mode == DF_SNAP)
9926 #if USE_NEW_SNAP_DELAY
9927 if (level.block_snap_field)
9928 setFieldForSnapping(x, y, element, move_direction);
9930 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9932 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9935 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
9936 player->index_bit, dig_side);
9939 else if (IS_COLLECTIBLE(element))
9943 if (is_player && mode != DF_SNAP)
9945 GfxElement[x][y] = element;
9946 player->is_collecting = TRUE;
9949 if (element == EL_SPEED_PILL)
9951 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9953 else if (element == EL_EXTRA_TIME && level.time > 0)
9955 TimeLeft += level.extra_time;
9956 DrawGameValue_Time(TimeLeft);
9958 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9960 player->shield_normal_time_left += level.shield_normal_time;
9961 if (element == EL_SHIELD_DEADLY)
9962 player->shield_deadly_time_left += level.shield_deadly_time;
9964 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9966 if (player->inventory_size < MAX_INVENTORY_SIZE)
9967 player->inventory_element[player->inventory_size++] = element;
9969 DrawGameValue_Dynamite(local_player->inventory_size);
9971 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9973 player->dynabomb_count++;
9974 player->dynabombs_left++;
9976 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9978 player->dynabomb_size++;
9980 else if (element == EL_DYNABOMB_INCREASE_POWER)
9982 player->dynabomb_xl = TRUE;
9984 else if (IS_KEY(element))
9986 player->key[KEY_NR(element)] = TRUE;
9988 DrawGameValue_Keys(player->key);
9990 redraw_mask |= REDRAW_DOOR_1;
9992 else if (IS_ENVELOPE(element))
9994 player->show_envelope = element;
9996 else if (element == EL_EMC_LENSES)
9998 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
10000 RedrawAllInvisibleElementsForLenses();
10002 else if (element == EL_EMC_MAGNIFIER)
10004 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
10006 RedrawAllInvisibleElementsForMagnifier();
10008 else if (IS_DROPPABLE(element) ||
10009 IS_THROWABLE(element)) /* can be collected and dropped */
10013 if (collect_count == 0)
10014 player->inventory_infinite_element = element;
10016 for (i = 0; i < collect_count; i++)
10017 if (player->inventory_size < MAX_INVENTORY_SIZE)
10018 player->inventory_element[player->inventory_size++] = element;
10020 DrawGameValue_Dynamite(local_player->inventory_size);
10022 else if (collect_count > 0)
10024 local_player->gems_still_needed -= collect_count;
10025 if (local_player->gems_still_needed < 0)
10026 local_player->gems_still_needed = 0;
10028 DrawGameValue_Emeralds(local_player->gems_still_needed);
10031 RaiseScoreElement(element);
10032 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10035 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
10036 player->index_bit, dig_side);
10038 if (mode == DF_SNAP)
10040 #if USE_NEW_SNAP_DELAY
10041 if (level.block_snap_field)
10042 setFieldForSnapping(x, y, element, move_direction);
10044 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10046 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10049 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10050 player->index_bit, dig_side);
10053 else if (IS_PUSHABLE(element))
10055 if (mode == DF_SNAP && element != EL_BD_ROCK)
10056 return MF_NO_ACTION;
10058 if (CAN_FALL(element) && dy)
10059 return MF_NO_ACTION;
10061 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10062 !(element == EL_SPRING && level.use_spring_bug))
10063 return MF_NO_ACTION;
10065 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10066 ((move_direction & MV_VERTICAL &&
10067 ((element_info[element].move_pattern & MV_LEFT &&
10068 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10069 (element_info[element].move_pattern & MV_RIGHT &&
10070 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10071 (move_direction & MV_HORIZONTAL &&
10072 ((element_info[element].move_pattern & MV_UP &&
10073 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10074 (element_info[element].move_pattern & MV_DOWN &&
10075 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10076 return MF_NO_ACTION;
10078 /* do not push elements already moving away faster than player */
10079 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10080 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10081 return MF_NO_ACTION;
10083 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10085 if (player->push_delay_value == -1 || !player_was_pushing)
10086 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10088 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10090 if (player->push_delay_value == -1)
10091 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10093 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10095 if (!player->is_pushing)
10096 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10099 player->is_pushing = TRUE;
10101 if (!(IN_LEV_FIELD(nextx, nexty) &&
10102 (IS_FREE(nextx, nexty) ||
10103 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10104 IS_SB_ELEMENT(element)))))
10105 return MF_NO_ACTION;
10107 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10108 return MF_NO_ACTION;
10110 if (player->push_delay == -1) /* new pushing; restart delay */
10111 player->push_delay = 0;
10113 if (player->push_delay < player->push_delay_value &&
10114 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10115 element != EL_SPRING && element != EL_BALLOON)
10117 /* make sure that there is no move delay before next try to push */
10118 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10119 player->move_delay = 0;
10121 return MF_NO_ACTION;
10124 if (IS_SB_ELEMENT(element))
10126 if (element == EL_SOKOBAN_FIELD_FULL)
10128 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10129 local_player->sokobanfields_still_needed++;
10132 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10134 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10135 local_player->sokobanfields_still_needed--;
10138 Feld[x][y] = EL_SOKOBAN_OBJECT;
10140 if (Back[x][y] == Back[nextx][nexty])
10141 PlayLevelSoundAction(x, y, ACTION_PUSHING);
10142 else if (Back[x][y] != 0)
10143 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10146 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10149 if (local_player->sokobanfields_still_needed == 0 &&
10150 game.emulation == EMU_SOKOBAN)
10152 player->LevelSolved = player->GameOver = TRUE;
10153 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10157 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10159 InitMovingField(x, y, move_direction);
10160 GfxAction[x][y] = ACTION_PUSHING;
10162 if (mode == DF_SNAP)
10163 ContinueMoving(x, y);
10165 MovPos[x][y] = (dx != 0 ? dx : dy);
10167 Pushed[x][y] = TRUE;
10168 Pushed[nextx][nexty] = TRUE;
10170 if (game.engine_version < VERSION_IDENT(2,2,0,7))
10171 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10173 player->push_delay_value = -1; /* get new value later */
10175 /* check for element change _after_ element has been pushed */
10176 if (game.use_change_when_pushing_bug)
10178 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10179 player->index_bit, dig_side);
10180 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
10181 player->index_bit, dig_side);
10184 else if (IS_SWITCHABLE(element))
10186 if (PLAYER_SWITCHING(player, x, y))
10188 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10189 player->index_bit, dig_side);
10194 player->is_switching = TRUE;
10195 player->switch_x = x;
10196 player->switch_y = y;
10198 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10200 if (element == EL_ROBOT_WHEEL)
10202 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10206 DrawLevelField(x, y);
10208 else if (element == EL_SP_TERMINAL)
10212 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
10214 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10216 else if (Feld[xx][yy] == EL_SP_TERMINAL)
10217 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10220 else if (IS_BELT_SWITCH(element))
10222 ToggleBeltSwitch(x, y);
10224 else if (element == EL_SWITCHGATE_SWITCH_UP ||
10225 element == EL_SWITCHGATE_SWITCH_DOWN)
10227 ToggleSwitchgateSwitch(x, y);
10229 else if (element == EL_LIGHT_SWITCH ||
10230 element == EL_LIGHT_SWITCH_ACTIVE)
10232 ToggleLightSwitch(x, y);
10234 else if (element == EL_TIMEGATE_SWITCH)
10236 ActivateTimegateSwitch(x, y);
10238 else if (element == EL_BALLOON_SWITCH_LEFT ||
10239 element == EL_BALLOON_SWITCH_RIGHT ||
10240 element == EL_BALLOON_SWITCH_UP ||
10241 element == EL_BALLOON_SWITCH_DOWN ||
10242 element == EL_BALLOON_SWITCH_NONE ||
10243 element == EL_BALLOON_SWITCH_ANY)
10245 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10246 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10247 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10248 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10249 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
10252 else if (element == EL_LAMP)
10254 Feld[x][y] = EL_LAMP_ACTIVE;
10255 local_player->lights_still_needed--;
10257 ResetGfxAnimation(x, y);
10258 DrawLevelField(x, y);
10260 else if (element == EL_TIME_ORB_FULL)
10262 Feld[x][y] = EL_TIME_ORB_EMPTY;
10264 if (level.time > 0 || level.use_time_orb_bug)
10266 TimeLeft += level.time_orb_time;
10267 DrawGameValue_Time(TimeLeft);
10270 ResetGfxAnimation(x, y);
10271 DrawLevelField(x, y);
10274 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10275 player->index_bit, dig_side);
10277 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
10278 player->index_bit, dig_side);
10280 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10281 player->index_bit, dig_side);
10287 if (!PLAYER_SWITCHING(player, x, y))
10289 player->is_switching = TRUE;
10290 player->switch_x = x;
10291 player->switch_y = y;
10293 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
10294 player->index_bit, dig_side);
10295 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10296 player->index_bit, dig_side);
10298 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
10299 player->index_bit, dig_side);
10300 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
10301 player->index_bit, dig_side);
10304 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10305 player->index_bit, dig_side);
10306 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10307 player->index_bit, dig_side);
10309 return MF_NO_ACTION;
10312 player->push_delay = -1;
10314 if (is_player) /* function can also be called by EL_PENGUIN */
10316 if (Feld[x][y] != element) /* really digged/collected something */
10317 player->is_collecting = !player->is_digging;
10323 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10325 int jx = player->jx, jy = player->jy;
10326 int x = jx + dx, y = jy + dy;
10327 int snap_direction = (dx == -1 ? MV_LEFT :
10328 dx == +1 ? MV_RIGHT :
10330 dy == +1 ? MV_DOWN : MV_NONE);
10332 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
10335 if (!player->active || !IN_LEV_FIELD(x, y))
10343 if (player->MovPos == 0)
10344 player->is_pushing = FALSE;
10346 player->is_snapping = FALSE;
10348 if (player->MovPos == 0)
10350 player->is_moving = FALSE;
10351 player->is_digging = FALSE;
10352 player->is_collecting = FALSE;
10358 if (player->is_snapping)
10361 player->MovDir = snap_direction;
10363 if (player->MovPos == 0)
10365 player->is_moving = FALSE;
10366 player->is_digging = FALSE;
10367 player->is_collecting = FALSE;
10370 player->is_dropping = FALSE;
10372 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10375 player->is_snapping = TRUE;
10377 if (player->MovPos == 0)
10379 player->is_moving = FALSE;
10380 player->is_digging = FALSE;
10381 player->is_collecting = FALSE;
10384 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
10385 DrawLevelField(player->last_jx, player->last_jy);
10387 DrawLevelField(x, y);
10392 boolean DropElement(struct PlayerInfo *player)
10394 int old_element, new_element;
10395 int dropx = player->jx, dropy = player->jy;
10396 int drop_direction = player->MovDir;
10397 int drop_side = drop_direction;
10398 int drop_element = (player->inventory_size > 0 ?
10399 player->inventory_element[player->inventory_size - 1] :
10400 player->inventory_infinite_element != EL_UNDEFINED ?
10401 player->inventory_infinite_element :
10402 player->dynabombs_left > 0 ?
10403 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10406 /* do not drop an element on top of another element; when holding drop key
10407 pressed without moving, dropped element must move away before the next
10408 element can be dropped (this is especially important if the next element
10409 is dynamite, which can be placed on background for historical reasons) */
10410 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
10413 if (IS_THROWABLE(drop_element))
10415 dropx += GET_DX_FROM_DIR(drop_direction);
10416 dropy += GET_DY_FROM_DIR(drop_direction);
10418 if (!IN_LEV_FIELD(dropx, dropy))
10422 old_element = Feld[dropx][dropy]; /* old element at dropping position */
10423 new_element = drop_element; /* default: no change when dropping */
10425 /* check if player is active, not moving and ready to drop */
10426 if (!player->active || player->MovPos || player->drop_delay > 0)
10429 /* check if player has anything that can be dropped */
10430 if (new_element == EL_UNDEFINED)
10433 /* check if anything can be dropped at the current position */
10434 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10437 /* collected custom elements can only be dropped on empty fields */
10438 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10441 if (old_element != EL_EMPTY)
10442 Back[dropx][dropy] = old_element; /* store old element on this field */
10444 ResetGfxAnimation(dropx, dropy);
10445 ResetRandomAnimationValue(dropx, dropy);
10447 if (player->inventory_size > 0 ||
10448 player->inventory_infinite_element != EL_UNDEFINED)
10450 if (player->inventory_size > 0)
10452 player->inventory_size--;
10454 DrawGameValue_Dynamite(local_player->inventory_size);
10456 if (new_element == EL_DYNAMITE)
10457 new_element = EL_DYNAMITE_ACTIVE;
10458 else if (new_element == EL_SP_DISK_RED)
10459 new_element = EL_SP_DISK_RED_ACTIVE;
10462 Feld[dropx][dropy] = new_element;
10464 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10465 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10466 el2img(Feld[dropx][dropy]), 0);
10468 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10470 /* needed if previous element just changed to "empty" in the last frame */
10471 Changed[dropx][dropy] = FALSE; /* allow another change */
10473 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
10474 player->index_bit, drop_side);
10475 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
10477 player->index_bit, drop_side);
10479 TestIfElementTouchesCustomElement(dropx, dropy);
10481 else /* player is dropping a dyna bomb */
10483 player->dynabombs_left--;
10485 Feld[dropx][dropy] = new_element;
10487 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10488 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10489 el2img(Feld[dropx][dropy]), 0);
10491 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10494 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
10495 InitField_WithBug1(dropx, dropy, FALSE);
10497 new_element = Feld[dropx][dropy]; /* element might have changed */
10499 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10500 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10502 int move_direction, nextx, nexty;
10504 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10505 MovDir[dropx][dropy] = drop_direction;
10507 move_direction = MovDir[dropx][dropy];
10508 nextx = dropx + GET_DX_FROM_DIR(move_direction);
10509 nexty = dropy + GET_DY_FROM_DIR(move_direction);
10511 Changed[dropx][dropy] = FALSE; /* allow another change */
10512 CheckCollision[dropx][dropy] = 2;
10515 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
10516 player->is_dropping = TRUE;
10518 player->drop_x = dropx;
10519 player->drop_y = dropy;
10524 /* ------------------------------------------------------------------------- */
10525 /* game sound playing functions */
10526 /* ------------------------------------------------------------------------- */
10528 static int *loop_sound_frame = NULL;
10529 static int *loop_sound_volume = NULL;
10531 void InitPlayLevelSound()
10533 int num_sounds = getSoundListSize();
10535 checked_free(loop_sound_frame);
10536 checked_free(loop_sound_volume);
10538 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10539 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10542 static void PlayLevelSound(int x, int y, int nr)
10544 int sx = SCREENX(x), sy = SCREENY(y);
10545 int volume, stereo_position;
10546 int max_distance = 8;
10547 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10549 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10550 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10553 if (!IN_LEV_FIELD(x, y) ||
10554 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10555 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10558 volume = SOUND_MAX_VOLUME;
10560 if (!IN_SCR_FIELD(sx, sy))
10562 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10563 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10565 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10568 stereo_position = (SOUND_MAX_LEFT +
10569 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10570 (SCR_FIELDX + 2 * max_distance));
10572 if (IS_LOOP_SOUND(nr))
10574 /* This assures that quieter loop sounds do not overwrite louder ones,
10575 while restarting sound volume comparison with each new game frame. */
10577 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10580 loop_sound_volume[nr] = volume;
10581 loop_sound_frame[nr] = FrameCounter;
10584 PlaySoundExt(nr, volume, stereo_position, type);
10587 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10589 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10590 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10591 y < LEVELY(BY1) ? LEVELY(BY1) :
10592 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10596 static void PlayLevelSoundAction(int x, int y, int action)
10598 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10601 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10603 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10605 if (sound_effect != SND_UNDEFINED)
10606 PlayLevelSound(x, y, sound_effect);
10609 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10612 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10614 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10615 PlayLevelSound(x, y, sound_effect);
10618 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10620 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10622 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10623 PlayLevelSound(x, y, sound_effect);
10626 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10628 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10630 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10631 StopSound(sound_effect);
10634 static void PlayLevelMusic()
10636 if (levelset.music[level_nr] != MUS_UNDEFINED)
10637 PlayMusic(levelset.music[level_nr]); /* from config file */
10639 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10642 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
10644 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
10649 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
10653 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10657 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10661 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10665 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10669 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10673 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10676 case SAMPLE_android_clone:
10677 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10680 case SAMPLE_android_move:
10681 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10684 case SAMPLE_spring:
10685 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10689 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
10693 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
10696 case SAMPLE_eater_eat:
10697 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10701 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10704 case SAMPLE_collect:
10705 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10708 case SAMPLE_diamond:
10709 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10712 case SAMPLE_squash:
10713 /* !!! CHECK THIS !!! */
10715 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10717 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
10721 case SAMPLE_wonderfall:
10722 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
10726 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10730 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10734 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10738 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
10742 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10746 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
10749 case SAMPLE_wonder:
10750 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10754 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10757 case SAMPLE_exit_open:
10758 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
10761 case SAMPLE_exit_leave:
10762 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10765 case SAMPLE_dynamite:
10766 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10770 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10774 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10778 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10782 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
10786 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
10790 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10794 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
10799 void RaiseScore(int value)
10801 local_player->score += value;
10803 DrawGameValue_Score(local_player->score);
10806 void RaiseScoreElement(int element)
10811 case EL_BD_DIAMOND:
10812 case EL_EMERALD_YELLOW:
10813 case EL_EMERALD_RED:
10814 case EL_EMERALD_PURPLE:
10815 case EL_SP_INFOTRON:
10816 RaiseScore(level.score[SC_EMERALD]);
10819 RaiseScore(level.score[SC_DIAMOND]);
10822 RaiseScore(level.score[SC_CRYSTAL]);
10825 RaiseScore(level.score[SC_PEARL]);
10828 case EL_BD_BUTTERFLY:
10829 case EL_SP_ELECTRON:
10830 RaiseScore(level.score[SC_BUG]);
10833 case EL_BD_FIREFLY:
10834 case EL_SP_SNIKSNAK:
10835 RaiseScore(level.score[SC_SPACESHIP]);
10838 case EL_DARK_YAMYAM:
10839 RaiseScore(level.score[SC_YAMYAM]);
10842 RaiseScore(level.score[SC_ROBOT]);
10845 RaiseScore(level.score[SC_PACMAN]);
10848 RaiseScore(level.score[SC_NUT]);
10851 case EL_SP_DISK_RED:
10852 case EL_DYNABOMB_INCREASE_NUMBER:
10853 case EL_DYNABOMB_INCREASE_SIZE:
10854 case EL_DYNABOMB_INCREASE_POWER:
10855 RaiseScore(level.score[SC_DYNAMITE]);
10857 case EL_SHIELD_NORMAL:
10858 case EL_SHIELD_DEADLY:
10859 RaiseScore(level.score[SC_SHIELD]);
10861 case EL_EXTRA_TIME:
10862 RaiseScore(level.extra_time_score);
10876 RaiseScore(level.score[SC_KEY]);
10879 RaiseScore(element_info[element].collect_score);
10884 void RequestQuitGame(boolean ask_if_really_quit)
10886 if (AllPlayersGone ||
10887 !ask_if_really_quit ||
10888 level_editor_test_game ||
10889 Request("Do you really want to quit the game ?",
10890 REQ_ASK | REQ_STAY_CLOSED))
10892 #if defined(NETWORK_AVALIABLE)
10893 if (options.network)
10894 SendToServer_StopPlaying();
10898 game_status = GAME_MODE_MAIN;
10904 if (tape.playing && tape.deactivate_display)
10905 TapeDeactivateDisplayOff(TRUE);
10907 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10909 if (tape.playing && tape.deactivate_display)
10910 TapeDeactivateDisplayOn();
10915 /* ---------- new game button stuff ---------------------------------------- */
10917 /* graphic position values for game buttons */
10918 #define GAME_BUTTON_XSIZE 30
10919 #define GAME_BUTTON_YSIZE 30
10920 #define GAME_BUTTON_XPOS 5
10921 #define GAME_BUTTON_YPOS 215
10922 #define SOUND_BUTTON_XPOS 5
10923 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10925 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10926 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10927 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10928 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10929 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10930 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10937 } gamebutton_info[NUM_GAME_BUTTONS] =
10940 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10945 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10946 GAME_CTRL_ID_PAUSE,
10950 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10955 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10956 SOUND_CTRL_ID_MUSIC,
10957 "background music on/off"
10960 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10961 SOUND_CTRL_ID_LOOPS,
10962 "sound loops on/off"
10965 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10966 SOUND_CTRL_ID_SIMPLE,
10967 "normal sounds on/off"
10971 void CreateGameButtons()
10975 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10977 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10978 struct GadgetInfo *gi;
10981 unsigned long event_mask;
10982 int gd_xoffset, gd_yoffset;
10983 int gd_x1, gd_x2, gd_y1, gd_y2;
10986 gd_xoffset = gamebutton_info[i].x;
10987 gd_yoffset = gamebutton_info[i].y;
10988 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10989 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10991 if (id == GAME_CTRL_ID_STOP ||
10992 id == GAME_CTRL_ID_PAUSE ||
10993 id == GAME_CTRL_ID_PLAY)
10995 button_type = GD_TYPE_NORMAL_BUTTON;
10997 event_mask = GD_EVENT_RELEASED;
10998 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10999 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11003 button_type = GD_TYPE_CHECK_BUTTON;
11005 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11006 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11007 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11008 event_mask = GD_EVENT_PRESSED;
11009 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11010 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11013 gi = CreateGadget(GDI_CUSTOM_ID, id,
11014 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11015 GDI_X, DX + gd_xoffset,
11016 GDI_Y, DY + gd_yoffset,
11017 GDI_WIDTH, GAME_BUTTON_XSIZE,
11018 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11019 GDI_TYPE, button_type,
11020 GDI_STATE, GD_BUTTON_UNPRESSED,
11021 GDI_CHECKED, checked,
11022 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11023 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11024 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11025 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11026 GDI_EVENT_MASK, event_mask,
11027 GDI_CALLBACK_ACTION, HandleGameButtons,
11031 Error(ERR_EXIT, "cannot create gadget");
11033 game_gadget[id] = gi;
11037 void FreeGameButtons()
11041 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11042 FreeGadget(game_gadget[i]);
11045 static void MapGameButtons()
11049 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11050 MapGadget(game_gadget[i]);
11053 void UnmapGameButtons()
11057 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11058 UnmapGadget(game_gadget[i]);
11061 static void HandleGameButtons(struct GadgetInfo *gi)
11063 int id = gi->custom_id;
11065 if (game_status != GAME_MODE_PLAYING)
11070 case GAME_CTRL_ID_STOP:
11071 RequestQuitGame(TRUE);
11074 case GAME_CTRL_ID_PAUSE:
11075 if (options.network)
11077 #if defined(NETWORK_AVALIABLE)
11079 SendToServer_ContinuePlaying();
11081 SendToServer_PausePlaying();
11085 TapeTogglePause(TAPE_TOGGLE_MANUAL);
11088 case GAME_CTRL_ID_PLAY:
11091 #if defined(NETWORK_AVALIABLE)
11092 if (options.network)
11093 SendToServer_ContinuePlaying();
11097 tape.pausing = FALSE;
11098 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11103 case SOUND_CTRL_ID_MUSIC:
11104 if (setup.sound_music)
11106 setup.sound_music = FALSE;
11109 else if (audio.music_available)
11111 setup.sound = setup.sound_music = TRUE;
11113 SetAudioMode(setup.sound);
11119 case SOUND_CTRL_ID_LOOPS:
11120 if (setup.sound_loops)
11121 setup.sound_loops = FALSE;
11122 else if (audio.loops_available)
11124 setup.sound = setup.sound_loops = TRUE;
11125 SetAudioMode(setup.sound);
11129 case SOUND_CTRL_ID_SIMPLE:
11130 if (setup.sound_simple)
11131 setup.sound_simple = FALSE;
11132 else if (audio.sound_available)
11134 setup.sound = setup.sound_simple = TRUE;
11135 SetAudioMode(setup.sound);